From 12ea3824e00cd1c4ea9b0b2c52dc5d8997761c3f Mon Sep 17 00:00:00 2001 From: Simon Browne Date: Tue, 19 Nov 2024 14:01:39 +0100 Subject: [PATCH] added declarations --- content/2024/declarations-pen-pals-EN.md | 3 + content/2024/declarations-pen-pals-NL.md | 9 +- venv/bin/icalendar | 8 + venv/bin/markdown-it | 8 + venv/bin/markdown_py | 8 + venv/bin/pelican | 8 + venv/bin/pelican-import | 8 + venv/bin/pelican-plugins | 8 + venv/bin/pelican-quickstart | 8 + venv/bin/pelican-themes | 8 + venv/bin/pygmentize | 8 + venv/bin/rst2html.py | 23 + venv/bin/rst2html4.py | 26 + venv/bin/rst2html5.py | 35 + venv/bin/rst2latex.py | 26 + venv/bin/rst2man.py | 26 + venv/bin/rst2odt.py | 30 + venv/bin/rst2odt_prepstyles.py | 67 + venv/bin/rst2pseudoxml.py | 23 + venv/bin/rst2s5.py | 24 + venv/bin/rst2xetex.py | 27 + venv/bin/rst2xml.py | 23 + venv/bin/rstpep2html.py | 25 + venv/bin/unidecode | 8 + venv/bin/watchfiles | 8 + .../Jinja2-2.11.3.dist-info/INSTALLER | 1 + .../Jinja2-2.11.3.dist-info/LICENSE.rst | 28 + .../Jinja2-2.11.3.dist-info/METADATA | 106 + .../Jinja2-2.11.3.dist-info/RECORD | 61 + .../Jinja2-2.11.3.dist-info/WHEEL | 6 + .../Jinja2-2.11.3.dist-info/entry_points.txt | 3 + .../Jinja2-2.11.3.dist-info/top_level.txt | 1 + .../Markdown-3.3.3.dist-info/INSTALLER | 1 + .../Markdown-3.3.3.dist-info/LICENSE.md | 29 + .../Markdown-3.3.3.dist-info/METADATA | 98 + .../Markdown-3.3.3.dist-info/RECORD | 77 + .../Markdown-3.3.3.dist-info/REQUESTED | 0 .../Markdown-3.3.3.dist-info/WHEEL | 5 + .../Markdown-3.3.3.dist-info/entry_points.txt | 23 + .../Markdown-3.3.3.dist-info/top_level.txt | 1 + .../MarkupSafe-2.0.1.egg-info/PKG-INFO | 98 + .../MarkupSafe-2.0.1.egg-info/SOURCES.txt | 34 + .../dependency_links.txt | 1 + .../installed-files.txt | 12 + .../MarkupSafe-2.0.1.egg-info/top_level.txt | 1 + .../site-packages/PIL/BdfFontFile.py | 110 + .../site-packages/PIL/BlpImagePlugin.py | 422 + .../site-packages/PIL/BmpImagePlugin.py | 380 + .../site-packages/PIL/BufrStubImagePlugin.py | 73 + .../site-packages/PIL/ContainerIO.py | 120 + .../site-packages/PIL/CurImagePlugin.py | 75 + .../site-packages/PIL/DcxImagePlugin.py | 89 + .../site-packages/PIL/DdsImagePlugin.py | 190 + .../site-packages/PIL/EpsImagePlugin.py | 419 + .../python3.11/site-packages/PIL/ExifTags.py | 318 + .../site-packages/PIL/FitsStubImagePlugin.py | 76 + .../site-packages/PIL/FliImagePlugin.py | 171 + .../python3.11/site-packages/PIL/FontFile.py | 111 + .../site-packages/PIL/FpxImagePlugin.py | 242 + .../site-packages/PIL/FtexImagePlugin.py | 106 + .../site-packages/PIL/GbrImagePlugin.py | 100 + .../site-packages/PIL/GdImageFile.py | 90 + .../site-packages/PIL/GifImagePlugin.py | 888 ++ .../site-packages/PIL/GimpGradientFile.py | 140 + .../site-packages/PIL/GimpPaletteFile.py | 56 + .../site-packages/PIL/GribStubImagePlugin.py | 73 + .../site-packages/PIL/Hdf5StubImagePlugin.py | 73 + .../site-packages/PIL/IcnsImagePlugin.py | 383 + .../site-packages/PIL/IcoImagePlugin.py | 328 + .../site-packages/PIL/ImImagePlugin.py | 376 + .../lib/python3.11/site-packages/PIL/Image.py | 3491 +++++++ .../site-packages/PIL/ImageChops.py | 328 + .../python3.11/site-packages/PIL/ImageCms.py | 999 ++ .../site-packages/PIL/ImageColor.py | 300 + .../python3.11/site-packages/PIL/ImageDraw.py | 898 ++ .../site-packages/PIL/ImageDraw2.py | 179 + .../site-packages/PIL/ImageEnhance.py | 103 + .../python3.11/site-packages/PIL/ImageFile.py | 697 ++ .../site-packages/PIL/ImageFilter.py | 534 + .../python3.11/site-packages/PIL/ImageFont.py | 1057 ++ .../python3.11/site-packages/PIL/ImageGrab.py | 120 + .../python3.11/site-packages/PIL/ImageMath.py | 253 + .../python3.11/site-packages/PIL/ImageMode.py | 64 + .../site-packages/PIL/ImageMorph.py | 245 + .../python3.11/site-packages/PIL/ImageOps.py | 558 ++ .../site-packages/PIL/ImagePalette.py | 221 + .../python3.11/site-packages/PIL/ImagePath.py | 19 + .../python3.11/site-packages/PIL/ImageQt.py | 202 + .../site-packages/PIL/ImageSequence.py | 75 + .../python3.11/site-packages/PIL/ImageShow.py | 236 + .../python3.11/site-packages/PIL/ImageStat.py | 147 + .../python3.11/site-packages/PIL/ImageTk.py | 300 + .../site-packages/PIL/ImageTransform.py | 102 + .../python3.11/site-packages/PIL/ImageWin.py | 230 + .../site-packages/PIL/ImtImagePlugin.py | 93 + .../site-packages/PIL/IptcImagePlugin.py | 230 + .../site-packages/PIL/Jpeg2KImagePlugin.py | 314 + .../site-packages/PIL/JpegImagePlugin.py | 805 ++ .../site-packages/PIL/JpegPresets.py | 248 + .../site-packages/PIL/McIdasImagePlugin.py | 75 + .../site-packages/PIL/MicImagePlugin.py | 107 + .../site-packages/PIL/MpegImagePlugin.py | 83 + .../site-packages/PIL/MpoImagePlugin.py | 134 + .../site-packages/PIL/MspImagePlugin.py | 194 + .../python3.11/site-packages/PIL/PSDraw.py | 235 + .../site-packages/PIL/PaletteFile.py | 53 + .../site-packages/PIL/PalmImagePlugin.py | 227 + .../site-packages/PIL/PcdImagePlugin.py | 63 + .../site-packages/PIL/PcfFontFile.py | 248 + .../site-packages/PIL/PcxImagePlugin.py | 213 + .../site-packages/PIL/PdfImagePlugin.py | 246 + .../python3.11/site-packages/PIL/PdfParser.py | 994 ++ .../site-packages/PIL/PixarImagePlugin.py | 70 + .../site-packages/PIL/PngImagePlugin.py | 1395 +++ .../site-packages/PIL/PpmImagePlugin.py | 164 + .../site-packages/PIL/PsdImagePlugin.py | 313 + .../python3.11/site-packages/PIL/PyAccess.py | 352 + .../site-packages/PIL/SgiImagePlugin.py | 229 + .../site-packages/PIL/SpiderImagePlugin.py | 324 + .../site-packages/PIL/SunImagePlugin.py | 136 + .../lib/python3.11/site-packages/PIL/TarIO.py | 65 + .../site-packages/PIL/TgaImagePlugin.py | 248 + .../site-packages/PIL/TiffImagePlugin.py | 1924 ++++ .../python3.11/site-packages/PIL/TiffTags.py | 499 + .../site-packages/PIL/WalImageFile.py | 126 + .../site-packages/PIL/WebPImagePlugin.py | 351 + .../site-packages/PIL/WmfImagePlugin.py | 178 + .../site-packages/PIL/XVThumbImagePlugin.py | 78 + .../site-packages/PIL/XbmImagePlugin.py | 94 + .../site-packages/PIL/XpmImagePlugin.py | 130 + .../python3.11/site-packages/PIL/__init__.py | 139 + .../python3.11/site-packages/PIL/__main__.py | 3 + .../python3.11/site-packages/PIL/_binary.py | 92 + .../_imaging.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 1735600 bytes ..._imagingft.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 111672 bytes ...magingmath.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 60328 bytes ...agingmorph.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 34040 bytes ..._imagingtk.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 39968 bytes .../site-packages/PIL/_tkinter_finder.py | 9 + .../lib/python3.11/site-packages/PIL/_util.py | 19 + .../python3.11/site-packages/PIL/_version.py | 2 + .../PIL/_webp.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 79224 bytes .../python3.11/site-packages/PIL/features.py | 313 + .../Pillow-8.1.0.egg-info/PKG-INFO | 135 + .../Pillow-8.1.0.egg-info/SOURCES.txt | 1321 +++ .../dependency_links.txt | 1 + .../Pillow-8.1.0.egg-info/installed-files.txt | 195 + .../Pillow-8.1.0.egg-info/top_level.txt | 1 + .../Pillow-8.1.0.egg-info/zip-safe | 1 + .../Pygments-2.6.1.dist-info/AUTHORS | 231 + .../Pygments-2.6.1.dist-info/INSTALLER | 1 + .../Pygments-2.6.1.dist-info/LICENSE | 25 + .../Pygments-2.6.1.dist-info/METADATA | 48 + .../Pygments-2.6.1.dist-info/RECORD | 463 + .../Pygments-2.6.1.dist-info/WHEEL | 5 + .../Pygments-2.6.1.dist-info/entry_points.txt | 3 + .../Pygments-2.6.1.dist-info/top_level.txt | 1 + .../Unidecode-1.3.8.dist-info/INSTALLER | 1 + .../Unidecode-1.3.8.dist-info/LICENSE | 339 + .../Unidecode-1.3.8.dist-info/METADATA | 313 + .../Unidecode-1.3.8.dist-info/RECORD | 395 + .../Unidecode-1.3.8.dist-info/WHEEL | 5 + .../entry_points.txt | 3 + .../Unidecode-1.3.8.dist-info/top_level.txt | 1 + .../anyio-4.6.2.post1.dist-info/INSTALLER | 1 + .../anyio-4.6.2.post1.dist-info/LICENSE | 20 + .../anyio-4.6.2.post1.dist-info/METADATA | 105 + .../anyio-4.6.2.post1.dist-info/RECORD | 82 + .../anyio-4.6.2.post1.dist-info/WHEEL | 5 + .../entry_points.txt | 2 + .../anyio-4.6.2.post1.dist-info/top_level.txt | 1 + .../site-packages/anyio/__init__.py | 74 + .../site-packages/anyio/_backends/__init__.py | 0 .../site-packages/anyio/_backends/_asyncio.py | 2771 ++++++ .../site-packages/anyio/_backends/_trio.py | 1330 +++ .../site-packages/anyio/_core/__init__.py | 0 .../site-packages/anyio/_core/_eventloop.py | 166 + .../site-packages/anyio/_core/_exceptions.py | 89 + .../site-packages/anyio/_core/_fileio.py | 674 ++ .../site-packages/anyio/_core/_resources.py | 18 + .../site-packages/anyio/_core/_signals.py | 27 + .../site-packages/anyio/_core/_sockets.py | 718 ++ .../site-packages/anyio/_core/_streams.py | 52 + .../anyio/_core/_subprocesses.py | 196 + .../anyio/_core/_synchronization.py | 724 ++ .../site-packages/anyio/_core/_tasks.py | 158 + .../site-packages/anyio/_core/_testing.py | 78 + .../site-packages/anyio/_core/_typedattr.py | 81 + .../site-packages/anyio/abc/__init__.py | 57 + .../site-packages/anyio/abc/_eventloop.py | 374 + .../site-packages/anyio/abc/_resources.py | 33 + .../site-packages/anyio/abc/_sockets.py | 194 + .../site-packages/anyio/abc/_streams.py | 203 + .../site-packages/anyio/abc/_subprocesses.py | 79 + .../site-packages/anyio/abc/_tasks.py | 95 + .../site-packages/anyio/abc/_testing.py | 65 + .../site-packages/anyio/from_thread.py | 527 + .../site-packages/anyio/lowlevel.py | 161 + .../python3.11/site-packages/anyio/py.typed | 0 .../site-packages/anyio/pytest_plugin.py | 191 + .../site-packages/anyio/streams/__init__.py | 0 .../site-packages/anyio/streams/buffered.py | 119 + .../site-packages/anyio/streams/file.py | 148 + .../site-packages/anyio/streams/memory.py | 317 + .../site-packages/anyio/streams/stapled.py | 141 + .../site-packages/anyio/streams/text.py | 147 + .../site-packages/anyio/streams/tls.py | 337 + .../site-packages/anyio/to_process.py | 258 + .../site-packages/anyio/to_thread.py | 69 + .../beautifulsoup4-4.9.3.dist-info/AUTHORS | 49 + .../COPYING.txt | 27 + .../beautifulsoup4-4.9.3.dist-info/INSTALLER | 1 + .../beautifulsoup4-4.9.3.dist-info/LICENSE | 30 + .../beautifulsoup4-4.9.3.dist-info/METADATA | 132 + .../beautifulsoup4-4.9.3.dist-info/RECORD | 45 + .../beautifulsoup4-4.9.3.dist-info/REQUESTED | 0 .../beautifulsoup4-4.9.3.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../blinker-1.9.0.dist-info/INSTALLER | 1 + .../blinker-1.9.0.dist-info/LICENSE.txt | 20 + .../blinker-1.9.0.dist-info/METADATA | 60 + .../blinker-1.9.0.dist-info/RECORD | 12 + .../blinker-1.9.0.dist-info/WHEEL | 4 + .../site-packages/blinker/__init__.py | 17 + .../site-packages/blinker/_utilities.py | 64 + .../python3.11/site-packages/blinker/base.py | 512 + .../python3.11/site-packages/blinker/py.typed | 0 .../python3.11/site-packages/bs4/__init__.py | 791 ++ .../site-packages/bs4/builder/__init__.py | 519 + .../site-packages/bs4/builder/_html5lib.py | 467 + .../site-packages/bs4/builder/_htmlparser.py | 477 + .../site-packages/bs4/builder/_lxml.py | 332 + .../python3.11/site-packages/bs4/dammit.py | 939 ++ .../python3.11/site-packages/bs4/diagnose.py | 242 + .../python3.11/site-packages/bs4/element.py | 2175 +++++ .../python3.11/site-packages/bs4/formatter.py | 152 + .../python3.11/site-packages/bs4/testing.py | 1101 +++ .../site-packages/bs4/tests/__init__.py | 1 + .../bs4/tests/test_builder_registry.py | 147 + .../site-packages/bs4/tests/test_docs.py | 36 + .../site-packages/bs4/tests/test_html5lib.py | 190 + .../bs4/tests/test_htmlparser.py | 97 + .../site-packages/bs4/tests/test_lxml.py | 115 + .../site-packages/bs4/tests/test_soup.py | 728 ++ .../site-packages/bs4/tests/test_tree.py | 2333 +++++ .../site-packages/dateutil/__init__.py | 24 + .../site-packages/dateutil/_common.py | 43 + .../site-packages/dateutil/_version.py | 4 + .../site-packages/dateutil/easter.py | 89 + .../site-packages/dateutil/parser/__init__.py | 61 + .../site-packages/dateutil/parser/_parser.py | 1613 +++ .../dateutil/parser/isoparser.py | 416 + .../site-packages/dateutil/relativedelta.py | 599 ++ .../site-packages/dateutil/rrule.py | 1737 ++++ .../site-packages/dateutil/tz/__init__.py | 12 + .../site-packages/dateutil/tz/_common.py | 419 + .../site-packages/dateutil/tz/_factories.py | 80 + .../site-packages/dateutil/tz/tz.py | 1849 ++++ .../site-packages/dateutil/tz/win.py | 370 + .../site-packages/dateutil/tzwin.py | 2 + .../site-packages/dateutil/utils.py | 71 + .../dateutil/zoneinfo/__init__.py | 167 + .../zoneinfo/dateutil-zoneinfo.tar.gz | Bin 0 -> 156400 bytes .../dateutil/zoneinfo/rebuild.py | 75 + .../docutils-0.16.dist-info/COPYING.txt | 137 + .../docutils-0.16.dist-info/INSTALLER | 1 + .../docutils-0.16.dist-info/METADATA | 64 + .../docutils-0.16.dist-info/RECORD | 332 + .../docutils-0.16.dist-info/WHEEL | 6 + .../docutils-0.16.dist-info/top_level.txt | 1 + .../site-packages/docutils/__init__.py | 233 + .../python3.11/site-packages/docutils/core.py | 666 ++ .../site-packages/docutils/examples.py | 97 + .../site-packages/docutils/frontend.py | 863 ++ .../python3.11/site-packages/docutils/io.py | 481 + .../docutils/languages/__init__.py | 47 + .../site-packages/docutils/languages/af.py | 58 + .../site-packages/docutils/languages/ca.py | 60 + .../site-packages/docutils/languages/cs.py | 60 + .../site-packages/docutils/languages/da.py | 62 + .../site-packages/docutils/languages/de.py | 58 + .../site-packages/docutils/languages/en.py | 60 + .../site-packages/docutils/languages/eo.py | 61 + .../site-packages/docutils/languages/es.py | 59 + .../site-packages/docutils/languages/fa.py | 61 + .../site-packages/docutils/languages/fi.py | 60 + .../site-packages/docutils/languages/fr.py | 58 + .../site-packages/docutils/languages/gl.py | 63 + .../site-packages/docutils/languages/he.py | 60 + .../site-packages/docutils/languages/it.py | 58 + .../site-packages/docutils/languages/ja.py | 61 + .../site-packages/docutils/languages/ko.py | 61 + .../site-packages/docutils/languages/lt.py | 61 + .../site-packages/docutils/languages/lv.py | 60 + .../site-packages/docutils/languages/nl.py | 60 + .../site-packages/docutils/languages/pl.py | 62 + .../site-packages/docutils/languages/pt_br.py | 60 + .../site-packages/docutils/languages/ru.py | 59 + .../site-packages/docutils/languages/sk.py | 58 + .../site-packages/docutils/languages/sv.py | 60 + .../site-packages/docutils/languages/zh_cn.py | 67 + .../site-packages/docutils/languages/zh_tw.py | 66 + .../site-packages/docutils/nodes.py | 2317 +++++ .../docutils/parsers/__init__.py | 51 + .../site-packages/docutils/parsers/null.py | 20 + .../docutils/parsers/rst/__init__.py | 416 + .../parsers/rst/directives/__init__.py | 419 + .../parsers/rst/directives/admonitions.py | 99 + .../docutils/parsers/rst/directives/body.py | 288 + .../docutils/parsers/rst/directives/html.py | 88 + .../docutils/parsers/rst/directives/images.py | 171 + .../docutils/parsers/rst/directives/misc.py | 556 ++ .../docutils/parsers/rst/directives/parts.py | 126 + .../parsers/rst/directives/references.py | 29 + .../docutils/parsers/rst/directives/tables.py | 513 + .../docutils/parsers/rst/include/README.txt | 17 + .../docutils/parsers/rst/include/isoamsa.txt | 162 + .../docutils/parsers/rst/include/isoamsb.txt | 126 + .../docutils/parsers/rst/include/isoamsc.txt | 29 + .../docutils/parsers/rst/include/isoamsn.txt | 96 + .../docutils/parsers/rst/include/isoamso.txt | 62 + .../docutils/parsers/rst/include/isoamsr.txt | 191 + .../docutils/parsers/rst/include/isobox.txt | 46 + .../docutils/parsers/rst/include/isocyr1.txt | 73 + .../docutils/parsers/rst/include/isocyr2.txt | 32 + .../docutils/parsers/rst/include/isodia.txt | 20 + .../docutils/parsers/rst/include/isogrk1.txt | 55 + .../docutils/parsers/rst/include/isogrk2.txt | 26 + .../docutils/parsers/rst/include/isogrk3.txt | 52 + .../parsers/rst/include/isogrk4-wide.txt | 49 + .../docutils/parsers/rst/include/isogrk4.txt | 8 + .../docutils/parsers/rst/include/isolat1.txt | 68 + .../docutils/parsers/rst/include/isolat2.txt | 128 + .../parsers/rst/include/isomfrk-wide.txt | 58 + .../docutils/parsers/rst/include/isomfrk.txt | 11 + .../parsers/rst/include/isomopf-wide.txt | 32 + .../docutils/parsers/rst/include/isomopf.txt | 13 + .../parsers/rst/include/isomscr-wide.txt | 58 + .../docutils/parsers/rst/include/isomscr.txt | 17 + .../docutils/parsers/rst/include/isonum.txt | 82 + .../docutils/parsers/rst/include/isopub.txt | 90 + .../docutils/parsers/rst/include/isotech.txt | 168 + .../docutils/parsers/rst/include/mmlalias.txt | 554 ++ .../parsers/rst/include/mmlextra-wide.txt | 113 + .../docutils/parsers/rst/include/mmlextra.txt | 87 + .../docutils/parsers/rst/include/s5defs.txt | 68 + .../parsers/rst/include/xhtml1-lat1.txt | 102 + .../parsers/rst/include/xhtml1-special.txt | 37 + .../parsers/rst/include/xhtml1-symbol.txt | 130 + .../parsers/rst/languages/__init__.py | 36 + .../docutils/parsers/rst/languages/af.py | 106 + .../docutils/parsers/rst/languages/ca.py | 125 + .../docutils/parsers/rst/languages/cs.py | 108 + .../docutils/parsers/rst/languages/da.py | 113 + .../docutils/parsers/rst/languages/de.py | 105 + .../docutils/parsers/rst/languages/en.py | 110 + .../docutils/parsers/rst/languages/eo.py | 118 + .../docutils/parsers/rst/languages/es.py | 125 + .../docutils/parsers/rst/languages/fa.py | 102 + .../docutils/parsers/rst/languages/fi.py | 98 + .../docutils/parsers/rst/languages/fr.py | 103 + .../docutils/parsers/rst/languages/gl.py | 111 + .../docutils/parsers/rst/languages/he.py | 108 + .../docutils/parsers/rst/languages/it.py | 97 + .../docutils/parsers/rst/languages/ja.py | 119 + .../docutils/parsers/rst/languages/ko.py | 111 + .../docutils/parsers/rst/languages/lt.py | 109 + .../docutils/parsers/rst/languages/lv.py | 108 + .../docutils/parsers/rst/languages/nl.py | 112 + .../docutils/parsers/rst/languages/pl.py | 102 + .../docutils/parsers/rst/languages/pt_br.py | 108 + .../docutils/parsers/rst/languages/ru.py | 89 + .../docutils/parsers/rst/languages/sk.py | 95 + .../docutils/parsers/rst/languages/sv.py | 95 + .../docutils/parsers/rst/languages/zh_cn.py | 104 + .../docutils/parsers/rst/languages/zh_tw.py | 109 + .../docutils/parsers/rst/roles.py | 399 + .../docutils/parsers/rst/states.py | 3116 ++++++ .../docutils/parsers/rst/tableparser.py | 542 + .../docutils/readers/__init__.py | 112 + .../site-packages/docutils/readers/doctree.py | 46 + .../site-packages/docutils/readers/pep.py | 48 + .../docutils/readers/standalone.py | 66 + .../site-packages/docutils/statemachine.py | 1539 +++ .../docutils/transforms/__init__.py | 172 + .../docutils/transforms/components.py | 52 + .../docutils/transforms/frontmatter.py | 548 ++ .../site-packages/docutils/transforms/misc.py | 144 + .../docutils/transforms/parts.py | 180 + .../site-packages/docutils/transforms/peps.py | 305 + .../docutils/transforms/references.py | 911 ++ .../docutils/transforms/universal.py | 320 + .../docutils/transforms/writer_aux.py | 88 + .../site-packages/docutils/utils/__init__.py | 797 ++ .../docutils/utils/code_analyzer.py | 142 + .../docutils/utils/error_reporting.py | 229 + .../docutils/utils/math/__init__.py | 48 + .../docutils/utils/math/latex2mathml.py | 568 ++ .../docutils/utils/math/math2html.py | 5383 ++++++++++ .../docutils/utils/math/tex2mathml_extern.py | 148 + .../docutils/utils/math/tex2unichar.py | 662 ++ .../docutils/utils/math/unichar2tex.py | 788 ++ .../docutils/utils/punctuation_chars.py | 122 + .../site-packages/docutils/utils/roman.py | 82 + .../docutils/utils/smartquotes.py | 1014 ++ .../docutils/utils/urischemes.py | 136 + .../docutils/writers/__init__.py | 143 + .../docutils/writers/_html_base.py | 1670 ++++ .../docutils/writers/docutils_xml.py | 199 + .../docutils/writers/html4css1/__init__.py | 824 ++ .../docutils/writers/html4css1/html4css1.css | 349 + .../docutils/writers/html4css1/template.txt | 8 + .../writers/html5_polyglot/__init__.py | 219 + .../docutils/writers/html5_polyglot/math.css | 276 + .../writers/html5_polyglot/minimal.css | 284 + .../docutils/writers/html5_polyglot/plain.css | 298 + .../writers/html5_polyglot/template.txt | 8 + .../docutils/writers/latex2e/__init__.py | 3216 ++++++ .../docutils/writers/latex2e/default.tex | 14 + .../docutils/writers/latex2e/titlepage.tex | 20 + .../docutils/writers/latex2e/xelatex.tex | 21 + .../site-packages/docutils/writers/manpage.py | 1178 +++ .../site-packages/docutils/writers/null.py | 21 + .../docutils/writers/odf_odt/__init__.py | 3534 +++++++ .../writers/odf_odt/pygmentsformatter.py | 109 + .../docutils/writers/odf_odt/styles.odt | Bin 0 -> 16500 bytes .../docutils/writers/pep_html/__init__.py | 105 + .../docutils/writers/pep_html/pep.css | 344 + .../docutils/writers/pep_html/template.txt | 29 + .../docutils/writers/pseudoxml.py | 31 + .../docutils/writers/s5_html/__init__.py | 353 + .../writers/s5_html/themes/README.txt | 6 + .../writers/s5_html/themes/big-black/__base__ | 2 + .../s5_html/themes/big-black/framing.css | 25 + .../s5_html/themes/big-black/pretty.css | 109 + .../s5_html/themes/big-white/framing.css | 24 + .../s5_html/themes/big-white/pretty.css | 107 + .../writers/s5_html/themes/default/blank.gif | Bin 0 -> 49 bytes .../s5_html/themes/default/framing.css | 25 + .../s5_html/themes/default/iepngfix.htc | 42 + .../writers/s5_html/themes/default/opera.css | 8 + .../s5_html/themes/default/outline.css | 16 + .../writers/s5_html/themes/default/pretty.css | 120 + .../writers/s5_html/themes/default/print.css | 24 + .../s5_html/themes/default/s5-core.css | 11 + .../writers/s5_html/themes/default/slides.css | 10 + .../writers/s5_html/themes/default/slides.js | 558 ++ .../s5_html/themes/medium-black/__base__ | 2 + .../s5_html/themes/medium-black/pretty.css | 115 + .../s5_html/themes/medium-white/framing.css | 24 + .../s5_html/themes/medium-white/pretty.css | 113 + .../s5_html/themes/small-black/__base__ | 2 + .../s5_html/themes/small-black/pretty.css | 116 + .../s5_html/themes/small-white/framing.css | 24 + .../s5_html/themes/small-white/pretty.css | 114 + .../docutils/writers/xetex/__init__.py | 150 + .../feedgenerator-1.9.2.dist-info/INSTALLER | 1 + .../feedgenerator-1.9.2.dist-info/LICENSE | 28 + .../feedgenerator-1.9.2.dist-info/METADATA | 49 + .../feedgenerator-1.9.2.dist-info/RECORD | 26 + .../feedgenerator-1.9.2.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../site-packages/feedgenerator/__init__.py | 24 + .../feedgenerator/django/__init__.py | 0 .../feedgenerator/django/utils/__init__.py | 0 .../django/utils/datetime_safe.py | 89 + .../feedgenerator/django/utils/encoding.py | 256 + .../django/utils/feedgenerator.py | 408 + .../feedgenerator/django/utils/functional.py | 370 + .../feedgenerator/django/utils/six.py | 370 + .../feedgenerator/django/utils/timezone.py | 292 + .../feedgenerator/django/utils/xmlutils.py | 23 + .../html5lib-1.1.dist-info/AUTHORS.rst | 66 + .../html5lib-1.1.dist-info/INSTALLER | 1 + .../html5lib-1.1.dist-info/LICENSE | 20 + .../html5lib-1.1.dist-info/METADATA | 552 ++ .../html5lib-1.1.dist-info/RECORD | 74 + .../html5lib-1.1.dist-info/REQUESTED | 0 .../html5lib-1.1.dist-info/WHEEL | 6 + .../html5lib-1.1.dist-info/top_level.txt | 1 + .../site-packages/html5lib/__init__.py | 35 + .../site-packages/html5lib/_ihatexml.py | 289 + .../site-packages/html5lib/_inputstream.py | 918 ++ .../site-packages/html5lib/_tokenizer.py | 1735 ++++ .../site-packages/html5lib/_trie/__init__.py | 5 + .../site-packages/html5lib/_trie/_base.py | 40 + .../site-packages/html5lib/_trie/py.py | 67 + .../site-packages/html5lib/_utils.py | 159 + .../site-packages/html5lib/constants.py | 2946 ++++++ .../html5lib/filters/__init__.py | 0 .../filters/alphabeticalattributes.py | 29 + .../site-packages/html5lib/filters/base.py | 12 + .../html5lib/filters/inject_meta_charset.py | 73 + .../site-packages/html5lib/filters/lint.py | 93 + .../html5lib/filters/optionaltags.py | 207 + .../html5lib/filters/sanitizer.py | 916 ++ .../html5lib/filters/whitespace.py | 38 + .../site-packages/html5lib/html5parser.py | 2795 ++++++ .../site-packages/html5lib/serializer.py | 409 + .../html5lib/treeadapters/__init__.py | 30 + .../html5lib/treeadapters/genshi.py | 54 + .../html5lib/treeadapters/sax.py | 50 + .../html5lib/treebuilders/__init__.py | 88 + .../html5lib/treebuilders/base.py | 417 + .../html5lib/treebuilders/dom.py | 239 + .../html5lib/treebuilders/etree.py | 343 + .../html5lib/treebuilders/etree_lxml.py | 392 + .../html5lib/treewalkers/__init__.py | 154 + .../html5lib/treewalkers/base.py | 252 + .../site-packages/html5lib/treewalkers/dom.py | 43 + .../html5lib/treewalkers/etree.py | 131 + .../html5lib/treewalkers/etree_lxml.py | 215 + .../html5lib/treewalkers/genshi.py | 69 + .../icalendar-4.0.7.dist-info/INSTALLER | 1 + .../icalendar-4.0.7.dist-info/LICENSE.rst | 26 + .../icalendar-4.0.7.dist-info/METADATA | 849 ++ .../icalendar-4.0.7.dist-info/RECORD | 74 + .../icalendar-4.0.7.dist-info/REQUESTED | 0 .../icalendar-4.0.7.dist-info/WHEEL | 6 + .../entry_points.txt | 3 + .../icalendar-4.0.7.dist-info/top_level.txt | 1 + .../site-packages/icalendar/__init__.py | 49 + .../python3.11/site-packages/icalendar/cal.py | 699 ++ .../site-packages/icalendar/caselessdict.py | 107 + .../python3.11/site-packages/icalendar/cli.py | 111 + .../site-packages/icalendar/compat.py | 12 + .../site-packages/icalendar/parser.py | 399 + .../site-packages/icalendar/parser_tools.py | 34 + .../site-packages/icalendar/prop.py | 1043 ++ .../site-packages/icalendar/tests/__init__.py | 0 .../icalendar/tests/america_new_york.ics | 61 + .../icalendar/tests/encoding.ics | 16 + .../tests/hypothesis/test_fuzzing.py | 38 + .../issue_112_missing_tzinfo_on_exdate.ics | 48 + .../tests/issue_53_parsing_failure.ics | 78 + .../icalendar/tests/multiple.ics | 80 + .../icalendar/tests/pacific_fiji.ics | 52 + .../icalendar/tests/recurrence.ics | 24 + .../icalendar/tests/test_encoding.py | 93 + .../icalendar/tests/test_fixed_issues.py | 480 + .../icalendar/tests/test_icalendar.py | 323 + .../icalendar/tests/test_multiple.py | 28 + .../icalendar/tests/test_property_params.py | 218 + .../icalendar/tests/test_recurrence.py | 62 + .../icalendar/tests/test_time.py | 30 + .../icalendar/tests/test_timezoned.py | 375 + .../icalendar/tests/test_unit_cal.py | 460 + .../icalendar/tests/test_unit_caselessdict.py | 100 + .../icalendar/tests/test_unit_parser_tools.py | 31 + .../icalendar/tests/test_unit_prop.py | 522 + .../icalendar/tests/test_unit_tools.py | 29 + .../site-packages/icalendar/tests/time.ics | 3 + .../icalendar/tests/timezone_rdate.ics | 55 + .../icalendar/tests/timezone_same_start.ics | 27 + .../tests/timezone_same_start_and_offset.ics | 23 + .../icalendar/tests/timezoned.ics | 36 + .../icalendar/tests/x_location.ics | 48 + .../site-packages/icalendar/timezone_cache.py | 3 + .../site-packages/icalendar/tools.py | 34 + .../icalendar/windows_to_olson.py | 119 + .../idna-3.10.dist-info/INSTALLER | 1 + .../idna-3.10.dist-info/LICENSE.md | 31 + .../idna-3.10.dist-info/METADATA | 250 + .../site-packages/idna-3.10.dist-info/RECORD | 22 + .../site-packages/idna-3.10.dist-info/WHEEL | 4 + .../python3.11/site-packages/idna/__init__.py | 45 + .../python3.11/site-packages/idna/codec.py | 122 + .../python3.11/site-packages/idna/compat.py | 15 + .../lib/python3.11/site-packages/idna/core.py | 437 + .../python3.11/site-packages/idna/idnadata.py | 4243 ++++++++ .../site-packages/idna/intranges.py | 57 + .../site-packages/idna/package_data.py | 1 + .../python3.11/site-packages/idna/py.typed | 0 .../site-packages/idna/uts46data.py | 8681 +++++++++++++++++ .../site-packages/jinja2/__init__.py | 44 + .../site-packages/jinja2/_compat.py | 132 + .../site-packages/jinja2/_identifier.py | 6 + .../site-packages/jinja2/asyncfilters.py | 158 + .../site-packages/jinja2/asyncsupport.py | 264 + .../site-packages/jinja2/bccache.py | 350 + .../site-packages/jinja2/compiler.py | 1843 ++++ .../site-packages/jinja2/constants.py | 21 + .../python3.11/site-packages/jinja2/debug.py | 268 + .../site-packages/jinja2/defaults.py | 44 + .../site-packages/jinja2/environment.py | 1362 +++ .../site-packages/jinja2/exceptions.py | 177 + .../python3.11/site-packages/jinja2/ext.py | 704 ++ .../site-packages/jinja2/filters.py | 1382 +++ .../site-packages/jinja2/idtracking.py | 290 + .../python3.11/site-packages/jinja2/lexer.py | 848 ++ .../site-packages/jinja2/loaders.py | 504 + .../python3.11/site-packages/jinja2/meta.py | 101 + .../site-packages/jinja2/nativetypes.py | 94 + .../python3.11/site-packages/jinja2/nodes.py | 1088 +++ .../site-packages/jinja2/optimizer.py | 41 + .../python3.11/site-packages/jinja2/parser.py | 939 ++ .../site-packages/jinja2/runtime.py | 1011 ++ .../site-packages/jinja2/sandbox.py | 510 + .../python3.11/site-packages/jinja2/tests.py | 215 + .../python3.11/site-packages/jinja2/utils.py | 737 ++ .../site-packages/jinja2/visitor.py | 81 + .../site-packages/markdown/__init__.py | 61 + .../site-packages/markdown/__main__.py | 151 + .../site-packages/markdown/__meta__.py | 49 + .../site-packages/markdown/blockparser.py | 125 + .../site-packages/markdown/blockprocessors.py | 621 ++ .../python3.11/site-packages/markdown/core.py | 407 + .../markdown/extensions/__init__.py | 107 + .../site-packages/markdown/extensions/abbr.py | 99 + .../markdown/extensions/admonition.py | 164 + .../markdown/extensions/attr_list.py | 166 + .../markdown/extensions/codehilite.py | 307 + .../markdown/extensions/def_list.py | 111 + .../markdown/extensions/extra.py | 58 + .../markdown/extensions/fenced_code.py | 179 + .../markdown/extensions/footnotes.py | 402 + .../markdown/extensions/legacy_attrs.py | 67 + .../markdown/extensions/legacy_em.py | 49 + .../markdown/extensions/md_in_html.py | 345 + .../site-packages/markdown/extensions/meta.py | 79 + .../markdown/extensions/nl2br.py | 33 + .../markdown/extensions/sane_lists.py | 54 + .../markdown/extensions/smarty.py | 263 + .../markdown/extensions/tables.py | 223 + .../site-packages/markdown/extensions/toc.py | 373 + .../markdown/extensions/wikilinks.py | 87 + .../site-packages/markdown/htmlparser.py | 285 + .../site-packages/markdown/inlinepatterns.py | 892 ++ .../site-packages/markdown/pep562.py | 245 + .../site-packages/markdown/postprocessors.py | 120 + .../site-packages/markdown/preprocessors.py | 82 + .../site-packages/markdown/serializers.py | 189 + .../site-packages/markdown/test_tools.py | 210 + .../site-packages/markdown/treeprocessors.py | 435 + .../python3.11/site-packages/markdown/util.py | 482 + .../site-packages/markdown_it/__init__.py | 5 + .../site-packages/markdown_it/_compat.py | 11 + .../site-packages/markdown_it/_punycode.py | 67 + .../site-packages/markdown_it/cli/__init__.py | 0 .../site-packages/markdown_it/cli/parse.py | 109 + .../markdown_it/common/__init__.py | 0 .../markdown_it/common/entities.py | 4 + .../markdown_it/common/html_blocks.py | 68 + .../markdown_it/common/html_re.py | 40 + .../markdown_it/common/normalize_url.py | 81 + .../site-packages/markdown_it/common/utils.py | 318 + .../markdown_it/helpers/__init__.py | 6 + .../helpers/parse_link_destination.py | 86 + .../markdown_it/helpers/parse_link_label.py | 43 + .../markdown_it/helpers/parse_link_title.py | 60 + .../site-packages/markdown_it/main.py | 355 + .../site-packages/markdown_it/parser_block.py | 111 + .../site-packages/markdown_it/parser_core.py | 45 + .../markdown_it/parser_inline.py | 147 + .../site-packages/markdown_it/port.yaml | 48 + .../markdown_it/presets/__init__.py | 28 + .../markdown_it/presets/commonmark.py | 74 + .../markdown_it/presets/default.py | 35 + .../site-packages/markdown_it/presets/zero.py | 43 + .../site-packages/markdown_it/py.typed | 1 + .../site-packages/markdown_it/renderer.py | 336 + .../site-packages/markdown_it/ruler.py | 276 + .../markdown_it/rules_block/__init__.py | 27 + .../markdown_it/rules_block/blockquote.py | 299 + .../markdown_it/rules_block/code.py | 35 + .../markdown_it/rules_block/fence.py | 101 + .../markdown_it/rules_block/heading.py | 68 + .../markdown_it/rules_block/hr.py | 55 + .../markdown_it/rules_block/html_block.py | 90 + .../markdown_it/rules_block/lheading.py | 86 + .../markdown_it/rules_block/list.py | 345 + .../markdown_it/rules_block/paragraph.py | 65 + .../markdown_it/rules_block/reference.py | 215 + .../markdown_it/rules_block/state_block.py | 261 + .../markdown_it/rules_block/table.py | 236 + .../markdown_it/rules_core/__init__.py | 19 + .../markdown_it/rules_core/block.py | 13 + .../markdown_it/rules_core/inline.py | 10 + .../markdown_it/rules_core/linkify.py | 149 + .../markdown_it/rules_core/normalize.py | 18 + .../markdown_it/rules_core/replacements.py | 126 + .../markdown_it/rules_core/smartquotes.py | 202 + .../markdown_it/rules_core/state_core.py | 25 + .../markdown_it/rules_core/text_join.py | 34 + .../markdown_it/rules_inline/__init__.py | 31 + .../markdown_it/rules_inline/autolink.py | 77 + .../markdown_it/rules_inline/backticks.py | 72 + .../markdown_it/rules_inline/balance_pairs.py | 137 + .../markdown_it/rules_inline/emphasis.py | 102 + .../markdown_it/rules_inline/entity.py | 53 + .../markdown_it/rules_inline/escape.py | 92 + .../rules_inline/fragments_join.py | 43 + .../markdown_it/rules_inline/html_inline.py | 43 + .../markdown_it/rules_inline/image.py | 148 + .../markdown_it/rules_inline/link.py | 151 + .../markdown_it/rules_inline/linkify.py | 61 + .../markdown_it/rules_inline/newline.py | 43 + .../markdown_it/rules_inline/state_inline.py | 166 + .../markdown_it/rules_inline/strikethrough.py | 127 + .../markdown_it/rules_inline/text.py | 53 + .../site-packages/markdown_it/token.py | 180 + .../site-packages/markdown_it/tree.py | 345 + .../site-packages/markdown_it/utils.py | 176 + .../markdown_it_py-3.0.0.dist-info/INSTALLER | 1 + .../markdown_it_py-3.0.0.dist-info/LICENSE | 21 + .../LICENSE.markdown-it | 22 + .../markdown_it_py-3.0.0.dist-info/METADATA | 205 + .../markdown_it_py-3.0.0.dist-info/RECORD | 142 + .../markdown_it_py-3.0.0.dist-info/WHEEL | 4 + .../entry_points.txt | 3 + .../site-packages/markupsafe/__init__.py | 288 + .../site-packages/markupsafe/_native.py | 75 + .../site-packages/markupsafe/_speedups.c | 339 + .../_speedups.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 42360 bytes .../site-packages/markupsafe/_speedups.pyi | 9 + .../site-packages/markupsafe/py.typed | 0 .../mdurl-0.1.2.dist-info/INSTALLER | 1 + .../mdurl-0.1.2.dist-info/LICENSE | 46 + .../mdurl-0.1.2.dist-info/METADATA | 32 + .../mdurl-0.1.2.dist-info/RECORD | 18 + .../site-packages/mdurl-0.1.2.dist-info/WHEEL | 4 + .../site-packages/mdurl/__init__.py | 18 + .../python3.11/site-packages/mdurl/_decode.py | 104 + .../python3.11/site-packages/mdurl/_encode.py | 85 + .../python3.11/site-packages/mdurl/_format.py | 27 + .../python3.11/site-packages/mdurl/_parse.py | 304 + .../python3.11/site-packages/mdurl/_url.py | 14 + .../python3.11/site-packages/mdurl/py.typed | 1 + .../ordered_set-4.1.0.dist-info/INSTALLER | 1 + .../ordered_set-4.1.0.dist-info/METADATA | 158 + .../ordered_set-4.1.0.dist-info/RECORD | 7 + .../ordered_set-4.1.0.dist-info/WHEEL | 4 + .../site-packages/ordered_set/__init__.py | 536 + .../site-packages/ordered_set/py.typed | 0 .../pelican-4.5.4.dist-info/INSTALLER | 1 + .../pelican-4.5.4.dist-info/LICENSE | 661 ++ .../pelican-4.5.4.dist-info/METADATA | 111 + .../pelican-4.5.4.dist-info/RECORD | 499 + .../pelican-4.5.4.dist-info/REQUESTED | 0 .../pelican-4.5.4.dist-info/WHEEL | 4 + .../pelican-4.5.4.dist-info/entry_points.txt | 7 + .../site-packages/pelican/__init__.py | 536 + .../site-packages/pelican/__main__.py | 9 + .../python3.11/site-packages/pelican/cache.py | 134 + .../site-packages/pelican/contents.py | 625 ++ .../site-packages/pelican/generators.py | 934 ++ .../python3.11/site-packages/pelican/log.py | 228 + .../site-packages/pelican/paginator.py | 158 + .../site-packages/pelican/plugins/_utils.py | 125 + .../site-packages/pelican/plugins/signals.py | 49 + .../site-packages/pelican/readers.py | 747 ++ .../site-packages/pelican/rstdirectives.py | 90 + .../site-packages/pelican/server.py | 138 + .../site-packages/pelican/settings.py | 683 ++ .../site-packages/pelican/signals.py | 4 + .../pelican/tests/TestPages/bad_page.rst | 8 + .../pelican/tests/TestPages/draft_page.rst | 8 + .../tests/TestPages/draft_page_markdown.md | 12 + .../TestPages/draft_page_with_template.rst | 11 + .../pelican/tests/TestPages/hidden_page.rst | 8 + .../tests/TestPages/hidden_page_markdown.md | 12 + .../TestPages/hidden_page_with_template.rst | 11 + .../pelican/tests/TestPages/page.rst | 4 + .../pelican/tests/TestPages/page_markdown.md | 9 + .../TestPages/page_used_for_sorting_test.rst | 6 + .../page_with_category_and_tag_links.md | 7 + .../tests/TestPages/page_with_static_links.md | 7 + .../tests/TestPages/page_with_template.rst | 8 + .../site-packages/pelican/tests/__init__.py | 15 + ...2012-11-29_rst_w_filename_meta#foo-bar.rst | 6 + .../2012-11-30_md_w_filename_meta#foo-bar.md | 6 + .../TestCategory/article_with_category.rst | 7 + .../TestCategory/article_without_category.rst | 4 + .../pelican/tests/content/article.rst | 6 + ...h_attributes_containing_double_quotes.html | 11 + .../article_with_capitalized_metadata.rst | 16 + .../tests/content/article_with_code_block.rst | 15 + .../tests/content/article_with_comments.html | 8 + .../article_with_duplicate_tags_authors.md | 15 + .../content/article_with_inline_svg.html | 17 + .../tests/content/article_with_keywords.html | 6 + .../article_with_markdown_and_footnote.md | 15 + ...ticle_with_markdown_and_nested_metadata.md | 5 + ...icle_with_markdown_and_nonascii_summary.md | 19 + ...ith_markdown_and_summary_metadata_multi.md | 10 + ...th_markdown_and_summary_metadata_single.md | 5 + .../article_with_markdown_extension.markdown | 10 + ...article_with_markdown_markup_extensions.md | 8 + .../content/article_with_md_extension.md | 14 + .../article_with_mdown_extension.mdown | 10 + .../tests/content/article_with_metadata.html | 15 + .../tests/content/article_with_metadata.rst | 16 + .../article_with_metadata.unknownextension | 12 + .../article_with_metadata_and_contents.html | 15 + ...adata_explicit_date_implicit_modified.html | 15 + .../article_with_metadata_explicit_dates.html | 16 + ...adata_implicit_date_explicit_modified.html | 15 + .../article_with_metadata_implicit_dates.html | 14 + .../content/article_with_mkd_extension.mkd | 10 + .../article_with_multiple_authors.html | 6 + .../content/article_with_multiple_authors.rst | 6 + .../article_with_multiple_authors_list.rst | 10 + ...rticle_with_multiple_authors_semicolon.rst | 6 + .../article_with_multiple_metadata_tags.html | 11 + .../article_with_nonconformant_meta_tags.html | 12 + .../content/article_with_null_attributes.html | 8 + .../tests/content/article_with_template.rst | 8 + .../content/article_with_typogrify_dashes.md | 3 + .../content/article_with_typogrify_dashes.rst | 4 + .../article_with_uppercase_metadata.html | 6 + .../article_with_uppercase_metadata.rst | 6 + .../content/article_without_category.rst | 6 + .../pelican/tests/content/bad_extension.mmd | 3 + .../pelican/tests/content/bloggerexport.xml | 1067 ++ .../pelican/tests/content/empty.md | 0 .../pelican/tests/content/empty_with_bom.md | 1 + .../tests/content/wordpress_content_decoded | 48 + .../tests/content/wordpress_content_encoded | 55 + .../pelican/tests/content/wordpressexport.xml | 957 ++ .../cyclic_intersite_links/first-article.rst | 7 + .../cyclic_intersite_links/second-article.rst | 7 + .../cyclic_intersite_links/third-article.rst | 7 + .../pelican/tests/default_conf.py | 44 + .../pelican/plugins/ns_plugin/__init__.py | 5 + .../normal_plugin/normal_plugin/__init__.py | 5 + .../normal_plugin/normal_plugin/submodule.py | 2 + .../normal_submodule_plugin/__init__.py | 0 .../subpackage/__init__.py | 0 .../subpackage/subpackage.py | 2 + .../normal_submodule_plugin/subplugin.py | 2 + .../tests/mixed_content/fake_image.jpg | 0 .../pelican/tests/mixed_content/short_page.md | 3 + .../subdir/subdir_fake_image.jpg | 0 .../tests/nested_content/maindir/maindir.md | 3 + .../nested_content/maindir/subdir/subdir.md | 3 + .../basic/a-markdown-powered-article.html | 67 + .../pelican/tests/output/basic/archives.html | 69 + .../pelican/tests/output/basic/article-1.html | 66 + .../pelican/tests/output/basic/article-2.html | 66 + .../pelican/tests/output/basic/article-3.html | 66 + .../output/basic/author/alexis-metaireau.html | 111 + .../pelican/tests/output/basic/authors.html | 51 + .../tests/output/basic/categories.html | 50 + .../tests/output/basic/category/bar.html | 67 + .../tests/output/basic/category/cat1.html | 124 + .../tests/output/basic/category/misc.html | 135 + .../tests/output/basic/category/yeah.html | 75 + .../drafts/a-draft-article-without-date.html | 67 + .../output/basic/drafts/a-draft-article.html | 67 + .../basic/feeds/alexis-metaireau.atom.xml | 22 + .../basic/feeds/alexis-metaireau.rss.xml | 10 + .../tests/output/basic/feeds/all-en.atom.xml | 74 + .../tests/output/basic/feeds/all-fr.atom.xml | 4 + .../tests/output/basic/feeds/all.atom.xml | 76 + .../tests/output/basic/feeds/bar.atom.xml | 8 + .../tests/output/basic/feeds/cat1.atom.xml | 7 + .../tests/output/basic/feeds/misc.atom.xml | 49 + .../tests/output/basic/feeds/yeah.atom.xml | 16 + .../basic/filename_metadata-example.html | 66 + .../pelican/tests/output/basic/index.html | 274 + .../tests/output/basic/oh-yeah-fr.html | 70 + .../pelican/tests/output/basic/oh-yeah.html | 78 + .../tests/output/basic/override/index.html | 50 + .../pages/this-is-a-test-hidden-page.html | 50 + .../basic/pages/this-is-a-test-page.html | 51 + .../tests/output/basic/pictures/Fat_Cat.jpg | Bin 0 -> 62675 bytes .../tests/output/basic/pictures/Sushi.jpg | Bin 0 -> 28992 bytes .../output/basic/pictures/Sushi_Macro.jpg | Bin 0 -> 38594 bytes .../tests/output/basic/second-article-fr.html | 70 + .../tests/output/basic/second-article.html | 70 + .../pelican/tests/output/basic/tag/bar.html | 123 + .../pelican/tests/output/basic/tag/baz.html | 66 + .../pelican/tests/output/basic/tag/foo.html | 93 + .../tests/output/basic/tag/foobar.html | 75 + .../pelican/tests/output/basic/tag/oh.html | 49 + .../pelican/tests/output/basic/tag/yeah.html | 67 + .../pelican/tests/output/basic/tags.html | 56 + .../tests/output/basic/theme/css/fonts.css | 12 + .../tests/output/basic/theme/css/main.css | 466 + .../tests/output/basic/theme/css/pygment.css | 205 + .../tests/output/basic/theme/css/reset.css | 52 + .../output/basic/theme/css/typogrify.css | 3 + .../tests/output/basic/theme/css/wide.css | 48 + .../theme/fonts/Yanone_Kaffeesatz_400.eot | Bin 0 -> 20932 bytes .../theme/fonts/Yanone_Kaffeesatz_400.svg | 407 + .../theme/fonts/Yanone_Kaffeesatz_400.ttf | Bin 0 -> 39168 bytes .../theme/fonts/Yanone_Kaffeesatz_400.woff | Bin 0 -> 22256 bytes .../theme/fonts/Yanone_Kaffeesatz_400.woff2 | Bin 0 -> 18320 bytes .../tests/output/basic/theme/fonts/font.css | 12 + .../basic/theme/images/icons/aboutme.png | Bin 0 -> 411 bytes .../basic/theme/images/icons/bitbucket.png | Bin 0 -> 3178 bytes .../basic/theme/images/icons/delicious.png | Bin 0 -> 827 bytes .../basic/theme/images/icons/facebook.png | Bin 0 -> 150 bytes .../basic/theme/images/icons/github.png | Bin 0 -> 606 bytes .../basic/theme/images/icons/gitorious.png | Bin 0 -> 223 bytes .../basic/theme/images/icons/gittip.png | Bin 0 -> 402 bytes .../theme/images/icons/google-groups.png | Bin 0 -> 420 bytes .../basic/theme/images/icons/google-plus.png | Bin 0 -> 511 bytes .../basic/theme/images/icons/hackernews.png | Bin 0 -> 2771 bytes .../basic/theme/images/icons/lastfm.png | Bin 0 -> 840 bytes .../basic/theme/images/icons/linkedin.png | Bin 0 -> 625 bytes .../basic/theme/images/icons/reddit.png | Bin 0 -> 458 bytes .../output/basic/theme/images/icons/rss.png | Bin 0 -> 751 bytes .../basic/theme/images/icons/slideshare.png | Bin 0 -> 435 bytes .../basic/theme/images/icons/speakerdeck.png | Bin 0 -> 580 bytes .../theme/images/icons/stackoverflow.png | Bin 0 -> 414 bytes .../basic/theme/images/icons/twitter.png | Bin 0 -> 416 bytes .../output/basic/theme/images/icons/vimeo.png | Bin 0 -> 349 bytes .../basic/theme/images/icons/youtube.png | Bin 0 -> 316 bytes .../output/basic/this-is-a-super-article.html | 84 + .../tests/output/basic/unbelievable.html | 98 + .../custom/a-markdown-powered-article.html | 113 + .../pelican/tests/output/custom/archives.html | 97 + .../tests/output/custom/article-1.html | 112 + .../tests/output/custom/article-2.html | 112 + .../tests/output/custom/article-3.html | 112 + .../custom/author/alexis-metaireau.html | 171 + .../custom/author/alexis-metaireau2.html | 186 + .../custom/author/alexis-metaireau3.html | 136 + .../pelican/tests/output/custom/authors.html | 79 + .../tests/output/custom/categories.html | 78 + .../tests/output/custom/category/bar.html | 95 + .../tests/output/custom/category/cat1.html | 164 + .../tests/output/custom/category/misc.html | 175 + .../tests/output/custom/category/yeah.html | 103 + .../drafts/a-draft-article-without-date.html | 98 + .../output/custom/drafts/a-draft-article.html | 98 + .../custom/feeds/alexis-metaireau.atom.xml | 74 + .../custom/feeds/alexis-metaireau.rss.xml | 29 + .../tests/output/custom/feeds/all-en.atom.xml | 74 + .../tests/output/custom/feeds/all-fr.atom.xml | 4 + .../tests/output/custom/feeds/all.atom.xml | 76 + .../tests/output/custom/feeds/all.rss.xml | 31 + .../tests/output/custom/feeds/bar.atom.xml | 8 + .../tests/output/custom/feeds/bar.rss.xml | 8 + .../tests/output/custom/feeds/cat1.atom.xml | 7 + .../tests/output/custom/feeds/cat1.rss.xml | 7 + .../tests/output/custom/feeds/misc.atom.xml | 49 + .../tests/output/custom/feeds/misc.rss.xml | 16 + .../tests/output/custom/feeds/yeah.atom.xml | 16 + .../tests/output/custom/feeds/yeah.rss.xml | 4 + .../custom/filename_metadata-example.html | 112 + .../pelican/tests/output/custom/index.html | 171 + .../pelican/tests/output/custom/index2.html | 186 + .../pelican/tests/output/custom/index3.html | 136 + .../tests/output/custom/jinja2_template.html | 74 + .../tests/output/custom/oh-yeah-fr.html | 116 + .../pelican/tests/output/custom/oh-yeah.html | 121 + .../tests/output/custom/override/index.html | 78 + .../pages/this-is-a-test-hidden-page.html | 78 + .../custom/pages/this-is-a-test-page.html | 79 + .../tests/output/custom/pictures/Fat_Cat.jpg | Bin 0 -> 62675 bytes .../tests/output/custom/pictures/Sushi.jpg | Bin 0 -> 28992 bytes .../output/custom/pictures/Sushi_Macro.jpg | Bin 0 -> 38594 bytes .../pelican/tests/output/custom/robots.txt | 2 + .../output/custom/second-article-fr.html | 116 + .../tests/output/custom/second-article.html | 116 + .../pelican/tests/output/custom/tag/bar.html | 154 + .../pelican/tests/output/custom/tag/baz.html | 112 + .../pelican/tests/output/custom/tag/foo.html | 124 + .../tests/output/custom/tag/foobar.html | 103 + .../pelican/tests/output/custom/tag/oh.html | 77 + .../pelican/tests/output/custom/tag/yeah.html | 95 + .../pelican/tests/output/custom/tags.html | 84 + .../tests/output/custom/theme/css/fonts.css | 12 + .../tests/output/custom/theme/css/main.css | 466 + .../tests/output/custom/theme/css/pygment.css | 205 + .../tests/output/custom/theme/css/reset.css | 52 + .../output/custom/theme/css/typogrify.css | 3 + .../tests/output/custom/theme/css/wide.css | 48 + .../theme/fonts/Yanone_Kaffeesatz_400.eot | Bin 0 -> 20932 bytes .../theme/fonts/Yanone_Kaffeesatz_400.svg | 407 + .../theme/fonts/Yanone_Kaffeesatz_400.ttf | Bin 0 -> 39168 bytes .../theme/fonts/Yanone_Kaffeesatz_400.woff | Bin 0 -> 22256 bytes .../theme/fonts/Yanone_Kaffeesatz_400.woff2 | Bin 0 -> 18320 bytes .../tests/output/custom/theme/fonts/font.css | 12 + .../custom/theme/images/icons/aboutme.png | Bin 0 -> 411 bytes .../custom/theme/images/icons/bitbucket.png | Bin 0 -> 3178 bytes .../custom/theme/images/icons/delicious.png | Bin 0 -> 827 bytes .../custom/theme/images/icons/facebook.png | Bin 0 -> 150 bytes .../custom/theme/images/icons/github.png | Bin 0 -> 606 bytes .../custom/theme/images/icons/gitorious.png | Bin 0 -> 223 bytes .../custom/theme/images/icons/gittip.png | Bin 0 -> 402 bytes .../theme/images/icons/google-groups.png | Bin 0 -> 420 bytes .../custom/theme/images/icons/google-plus.png | Bin 0 -> 511 bytes .../custom/theme/images/icons/hackernews.png | Bin 0 -> 2771 bytes .../custom/theme/images/icons/lastfm.png | Bin 0 -> 840 bytes .../custom/theme/images/icons/linkedin.png | Bin 0 -> 625 bytes .../custom/theme/images/icons/reddit.png | Bin 0 -> 458 bytes .../output/custom/theme/images/icons/rss.png | Bin 0 -> 751 bytes .../custom/theme/images/icons/slideshare.png | Bin 0 -> 435 bytes .../custom/theme/images/icons/speakerdeck.png | Bin 0 -> 580 bytes .../theme/images/icons/stackoverflow.png | Bin 0 -> 414 bytes .../custom/theme/images/icons/twitter.png | Bin 0 -> 416 bytes .../custom/theme/images/icons/vimeo.png | Bin 0 -> 349 bytes .../custom/theme/images/icons/youtube.png | Bin 0 -> 316 bytes .../custom/this-is-a-super-article.html | 127 + .../tests/output/custom/unbelievable.html | 144 + .../tests/output/custom_locale/archives.html | 97 + .../author/alexis-metaireau.html | 171 + .../author/alexis-metaireau2.html | 186 + .../author/alexis-metaireau3.html | 136 + .../tests/output/custom_locale/authors.html | 79 + .../output/custom_locale/categories.html | 78 + .../output/custom_locale/category/bar.html | 95 + .../output/custom_locale/category/cat1.html | 164 + .../output/custom_locale/category/misc.html | 175 + .../output/custom_locale/category/yeah.html | 103 + .../drafts/a-draft-article-without-date.html | 98 + .../custom_locale/drafts/a-draft-article.html | 98 + .../feeds/alexis-metaireau.atom.xml | 74 + .../feeds/alexis-metaireau.rss.xml | 29 + .../custom_locale/feeds/all-en.atom.xml | 74 + .../custom_locale/feeds/all-fr.atom.xml | 4 + .../output/custom_locale/feeds/all.atom.xml | 76 + .../output/custom_locale/feeds/all.rss.xml | 31 + .../output/custom_locale/feeds/bar.atom.xml | 8 + .../output/custom_locale/feeds/bar.rss.xml | 8 + .../output/custom_locale/feeds/cat1.atom.xml | 7 + .../output/custom_locale/feeds/cat1.rss.xml | 7 + .../output/custom_locale/feeds/misc.atom.xml | 49 + .../output/custom_locale/feeds/misc.rss.xml | 16 + .../output/custom_locale/feeds/yeah.atom.xml | 16 + .../output/custom_locale/feeds/yeah.rss.xml | 4 + .../tests/output/custom_locale/index.html | 171 + .../tests/output/custom_locale/index2.html | 186 + .../tests/output/custom_locale/index3.html | 136 + .../output/custom_locale/jinja2_template.html | 74 + .../output/custom_locale/oh-yeah-fr.html | 116 + .../output/custom_locale/override/index.html | 78 + .../pages/this-is-a-test-hidden-page.html | 78 + .../pages/this-is-a-test-page.html | 79 + .../output/custom_locale/pictures/Fat_Cat.jpg | Bin 0 -> 62675 bytes .../output/custom_locale/pictures/Sushi.jpg | Bin 0 -> 28992 bytes .../custom_locale/pictures/Sushi_Macro.jpg | Bin 0 -> 38594 bytes .../02/this-is-a-super-article/index.html | 127 + .../2010/octobre/15/unbelievable/index.html | 144 + .../posts/2010/octobre/20/oh-yeah/index.html | 121 + .../20/a-markdown-powered-article/index.html | 113 + .../2011/février/17/article-1/index.html | 112 + .../2011/février/17/article-2/index.html | 112 + .../2011/février/17/article-3/index.html | 112 + .../2012/février/29/second-article/index.html | 116 + .../30/filename_metadata-example/index.html | 112 + .../tests/output/custom_locale/robots.txt | 2 + .../custom_locale/second-article-fr.html | 116 + .../tests/output/custom_locale/tag/bar.html | 154 + .../tests/output/custom_locale/tag/baz.html | 112 + .../tests/output/custom_locale/tag/foo.html | 124 + .../output/custom_locale/tag/foobar.html | 103 + .../tests/output/custom_locale/tag/oh.html | 77 + .../tests/output/custom_locale/tag/yeah.html | 95 + .../tests/output/custom_locale/tags.html | 84 + .../output/custom_locale/theme/css/fonts.css | 12 + .../output/custom_locale/theme/css/main.css | 466 + .../custom_locale/theme/css/pygment.css | 205 + .../output/custom_locale/theme/css/reset.css | 52 + .../custom_locale/theme/css/typogrify.css | 3 + .../output/custom_locale/theme/css/wide.css | 48 + .../theme/fonts/Yanone_Kaffeesatz_400.eot | Bin 0 -> 20932 bytes .../theme/fonts/Yanone_Kaffeesatz_400.svg | 407 + .../theme/fonts/Yanone_Kaffeesatz_400.ttf | Bin 0 -> 39168 bytes .../theme/fonts/Yanone_Kaffeesatz_400.woff | Bin 0 -> 22256 bytes .../theme/fonts/Yanone_Kaffeesatz_400.woff2 | Bin 0 -> 18320 bytes .../output/custom_locale/theme/fonts/font.css | 12 + .../theme/images/icons/aboutme.png | Bin 0 -> 411 bytes .../theme/images/icons/bitbucket.png | Bin 0 -> 3178 bytes .../theme/images/icons/delicious.png | Bin 0 -> 827 bytes .../theme/images/icons/facebook.png | Bin 0 -> 150 bytes .../theme/images/icons/github.png | Bin 0 -> 606 bytes .../theme/images/icons/gitorious.png | Bin 0 -> 223 bytes .../theme/images/icons/gittip.png | Bin 0 -> 402 bytes .../theme/images/icons/google-groups.png | Bin 0 -> 420 bytes .../theme/images/icons/google-plus.png | Bin 0 -> 511 bytes .../theme/images/icons/hackernews.png | Bin 0 -> 2771 bytes .../theme/images/icons/lastfm.png | Bin 0 -> 840 bytes .../theme/images/icons/linkedin.png | Bin 0 -> 625 bytes .../theme/images/icons/reddit.png | Bin 0 -> 458 bytes .../custom_locale/theme/images/icons/rss.png | Bin 0 -> 751 bytes .../theme/images/icons/slideshare.png | Bin 0 -> 435 bytes .../theme/images/icons/speakerdeck.png | Bin 0 -> 580 bytes .../theme/images/icons/stackoverflow.png | Bin 0 -> 414 bytes .../theme/images/icons/twitter.png | Bin 0 -> 416 bytes .../theme/images/icons/vimeo.png | Bin 0 -> 349 bytes .../theme/images/icons/youtube.png | Bin 0 -> 316 bytes .../pelican/tests/parse_error/parse_error.rst | 4 + .../site-packages/pelican/tests/support.py | 238 + .../site-packages/pelican/tests/test_cache.py | 279 + .../pelican/tests/test_contents.py | 1038 ++ .../pelican/tests/test_generators.py | 1227 +++ .../pelican/tests/test_importer.py | 453 + .../site-packages/pelican/tests/test_log.py | 132 + .../pelican/tests/test_paginator.py | 78 + .../pelican/tests/test_pelican.py | 267 + .../pelican/tests/test_plugins.py | 191 + .../pelican/tests/test_readers.py | 902 ++ .../pelican/tests/test_rstdirectives.py | 31 + .../pelican/tests/test_server.py | 56 + .../pelican/tests/test_settings.py | 321 + .../pelican/tests/test_testsuite.py | 10 + .../pelican/tests/test_urlwrappers.py | 85 + .../site-packages/pelican/tests/test_utils.py | 833 ++ .../tests/theme_overrides/level1/article.html | 4 + .../tests/theme_overrides/level2/article.html | 4 + .../tests/theme_overrides/level2/authors.html | 4 + .../themes/notmyidea/static/css/fonts.css | 12 + .../themes/notmyidea/static/css/main.css | 466 + .../themes/notmyidea/static/css/pygment.css | 205 + .../themes/notmyidea/static/css/reset.css | 52 + .../themes/notmyidea/static/css/typogrify.css | 3 + .../themes/notmyidea/static/css/wide.css | 48 + .../static/fonts/Yanone_Kaffeesatz_400.eot | Bin 0 -> 20932 bytes .../static/fonts/Yanone_Kaffeesatz_400.svg | 407 + .../static/fonts/Yanone_Kaffeesatz_400.ttf | Bin 0 -> 39168 bytes .../static/fonts/Yanone_Kaffeesatz_400.woff | Bin 0 -> 22256 bytes .../static/fonts/Yanone_Kaffeesatz_400.woff2 | Bin 0 -> 18320 bytes .../themes/notmyidea/static/fonts/font.css | 12 + .../notmyidea/static/images/icons/aboutme.png | Bin 0 -> 411 bytes .../static/images/icons/bitbucket.png | Bin 0 -> 3178 bytes .../static/images/icons/delicious.png | Bin 0 -> 827 bytes .../static/images/icons/facebook.png | Bin 0 -> 150 bytes .../notmyidea/static/images/icons/github.png | Bin 0 -> 606 bytes .../static/images/icons/gitorious.png | Bin 0 -> 223 bytes .../notmyidea/static/images/icons/gittip.png | Bin 0 -> 402 bytes .../static/images/icons/google-groups.png | Bin 0 -> 420 bytes .../static/images/icons/google-plus.png | Bin 0 -> 511 bytes .../static/images/icons/hackernews.png | Bin 0 -> 2771 bytes .../notmyidea/static/images/icons/lastfm.png | Bin 0 -> 840 bytes .../static/images/icons/linkedin.png | Bin 0 -> 625 bytes .../notmyidea/static/images/icons/reddit.png | Bin 0 -> 458 bytes .../notmyidea/static/images/icons/rss.png | Bin 0 -> 751 bytes .../static/images/icons/slideshare.png | Bin 0 -> 435 bytes .../static/images/icons/speakerdeck.png | Bin 0 -> 580 bytes .../static/images/icons/stackoverflow.png | Bin 0 -> 414 bytes .../notmyidea/static/images/icons/twitter.png | Bin 0 -> 416 bytes .../notmyidea/static/images/icons/vimeo.png | Bin 0 -> 349 bytes .../notmyidea/static/images/icons/youtube.png | Bin 0 -> 316 bytes .../themes/notmyidea/templates/analytics.html | 26 + .../themes/notmyidea/templates/archives.html | 13 + .../themes/notmyidea/templates/article.html | 53 + .../notmyidea/templates/article_infos.html | 23 + .../themes/notmyidea/templates/author.html | 2 + .../themes/notmyidea/templates/authors.html | 16 + .../themes/notmyidea/templates/base.html | 82 + .../themes/notmyidea/templates/category.html | 2 + .../themes/notmyidea/templates/comments.html | 1 + .../notmyidea/templates/disqus_script.html | 11 + .../themes/notmyidea/templates/github.html | 9 + .../themes/notmyidea/templates/index.html | 65 + .../themes/notmyidea/templates/page.html | 19 + .../notmyidea/templates/period_archives.html | 13 + .../themes/notmyidea/templates/tag.html | 2 + .../themes/notmyidea/templates/taglist.html | 1 + .../themes/notmyidea/templates/tags.html | 16 + .../notmyidea/templates/translations.html | 16 + .../themes/notmyidea/templates/twitter.html | 3 + .../themes/simple/templates/archives.html | 14 + .../themes/simple/templates/article.html | 67 + .../themes/simple/templates/author.html | 8 + .../themes/simple/templates/authors.html | 12 + .../pelican/themes/simple/templates/base.html | 63 + .../themes/simple/templates/categories.html | 12 + .../themes/simple/templates/category.html | 8 + .../themes/simple/templates/gosquared.html | 14 + .../themes/simple/templates/index.html | 28 + .../pelican/themes/simple/templates/page.html | 27 + .../themes/simple/templates/pagination.html | 15 + .../simple/templates/period_archives.html | 14 + .../pelican/themes/simple/templates/tag.html | 7 + .../pelican/themes/simple/templates/tags.html | 12 + .../themes/simple/templates/translations.html | 16 + .../site-packages/pelican/tools/__init__.py | 0 .../pelican/tools/pelican_import.py | 1027 ++ .../pelican/tools/pelican_quickstart.py | 354 + .../pelican/tools/pelican_themes.py | 259 + .../pelican/tools/templates/Makefile.jinja2 | 161 + .../tools/templates/pelicanconf.py.jinja2 | 34 + .../tools/templates/publishconf.py.jinja2 | 24 + .../pelican/tools/templates/tasks.py.jinja2 | 156 + .../site-packages/pelican/urlwrappers.py | 138 + .../python3.11/site-packages/pelican/utils.py | 968 ++ .../site-packages/pelican/writers.py | 270 + .../site-packages/pygments/__init__.py | 85 + .../site-packages/pygments/__main__.py | 18 + .../site-packages/pygments/cmdline.py | 575 ++ .../site-packages/pygments/console.py | 71 + .../site-packages/pygments/filter.py | 74 + .../pygments/filters/__init__.py | 351 + .../site-packages/pygments/formatter.py | 95 + .../pygments/formatters/__init__.py | 154 + .../pygments/formatters/_mapping.py | 83 + .../pygments/formatters/bbcode.py | 109 + .../site-packages/pygments/formatters/html.py | 880 ++ .../site-packages/pygments/formatters/img.py | 614 ++ .../site-packages/pygments/formatters/irc.py | 182 + .../pygments/formatters/latex.py | 481 + .../pygments/formatters/other.py | 164 + .../site-packages/pygments/formatters/rtf.py | 147 + .../site-packages/pygments/formatters/svg.py | 187 + .../pygments/formatters/terminal.py | 130 + .../pygments/formatters/terminal256.py | 319 + .../site-packages/pygments/lexer.py | 877 ++ .../site-packages/pygments/lexers/__init__.py | 343 + .../pygments/lexers/_asy_builtins.py | 1645 ++++ .../pygments/lexers/_cl_builtins.py | 232 + .../pygments/lexers/_cocoa_builtins.py | 71 + .../pygments/lexers/_csound_builtins.py | 1694 ++++ .../pygments/lexers/_lasso_builtins.py | 5327 ++++++++++ .../pygments/lexers/_lua_builtins.py | 293 + .../site-packages/pygments/lexers/_mapping.py | 538 + .../pygments/lexers/_mql_builtins.py | 1172 +++ .../pygments/lexers/_openedge_builtins.py | 2547 +++++ .../pygments/lexers/_php_builtins.py | 4754 +++++++++ .../pygments/lexers/_postgres_builtins.py | 621 ++ .../pygments/lexers/_scilab_builtins.py | 3094 ++++++ .../pygments/lexers/_sourcemod_builtins.py | 1161 +++ .../pygments/lexers/_stan_builtins.py | 558 ++ .../pygments/lexers/_stata_builtins.py | 421 + .../pygments/lexers/_tsql_builtins.py | 1004 ++ .../pygments/lexers/_usd_builtins.py | 107 + .../pygments/lexers/_vbscript_builtins.py | 280 + .../pygments/lexers/_vim_builtins.py | 1939 ++++ .../pygments/lexers/actionscript.py | 240 + .../site-packages/pygments/lexers/agile.py | 24 + .../site-packages/pygments/lexers/algebra.py | 221 + .../site-packages/pygments/lexers/ambient.py | 76 + .../site-packages/pygments/lexers/ampl.py | 87 + .../site-packages/pygments/lexers/apl.py | 101 + .../pygments/lexers/archetype.py | 318 + .../site-packages/pygments/lexers/asm.py | 993 ++ .../pygments/lexers/automation.py | 374 + .../site-packages/pygments/lexers/basic.py | 659 ++ .../site-packages/pygments/lexers/bibtex.py | 160 + .../site-packages/pygments/lexers/boa.py | 102 + .../site-packages/pygments/lexers/business.py | 612 ++ .../site-packages/pygments/lexers/c_cpp.py | 253 + .../site-packages/pygments/lexers/c_like.py | 571 ++ .../pygments/lexers/capnproto.py | 78 + .../site-packages/pygments/lexers/chapel.py | 112 + .../site-packages/pygments/lexers/clean.py | 178 + .../site-packages/pygments/lexers/compiled.py | 34 + .../site-packages/pygments/lexers/configs.py | 939 ++ .../site-packages/pygments/lexers/console.py | 114 + .../site-packages/pygments/lexers/crystal.py | 393 + .../site-packages/pygments/lexers/csound.py | 465 + .../site-packages/pygments/lexers/css.py | 691 ++ .../site-packages/pygments/lexers/d.py | 256 + .../site-packages/pygments/lexers/dalvik.py | 125 + .../site-packages/pygments/lexers/data.py | 561 ++ .../site-packages/pygments/lexers/diff.py | 165 + .../site-packages/pygments/lexers/dotnet.py | 688 ++ .../site-packages/pygments/lexers/dsls.py | 960 ++ .../site-packages/pygments/lexers/dylan.py | 289 + .../site-packages/pygments/lexers/ecl.py | 125 + .../site-packages/pygments/lexers/eiffel.py | 65 + .../site-packages/pygments/lexers/elm.py | 121 + .../site-packages/pygments/lexers/email.py | 154 + .../site-packages/pygments/lexers/erlang.py | 533 + .../site-packages/pygments/lexers/esoteric.py | 277 + .../site-packages/pygments/lexers/ezhil.py | 69 + .../site-packages/pygments/lexers/factor.py | 344 + .../site-packages/pygments/lexers/fantom.py | 250 + .../site-packages/pygments/lexers/felix.py | 273 + .../pygments/lexers/floscript.py | 83 + .../site-packages/pygments/lexers/forth.py | 179 + .../site-packages/pygments/lexers/fortran.py | 206 + .../site-packages/pygments/lexers/foxpro.py | 428 + .../site-packages/pygments/lexers/freefem.py | 898 ++ .../pygments/lexers/functional.py | 21 + .../site-packages/pygments/lexers/go.py | 101 + .../pygments/lexers/grammar_notation.py | 270 + .../site-packages/pygments/lexers/graph.py | 85 + .../site-packages/pygments/lexers/graphics.py | 781 ++ .../site-packages/pygments/lexers/haskell.py | 871 ++ .../site-packages/pygments/lexers/haxe.py | 936 ++ .../site-packages/pygments/lexers/hdl.py | 376 + .../site-packages/pygments/lexers/hexdump.py | 103 + .../site-packages/pygments/lexers/html.py | 602 ++ .../site-packages/pygments/lexers/idl.py | 270 + .../site-packages/pygments/lexers/igor.py | 425 + .../site-packages/pygments/lexers/inferno.py | 96 + .../pygments/lexers/installers.py | 322 + .../pygments/lexers/int_fiction.py | 1343 +++ .../site-packages/pygments/lexers/iolang.py | 63 + .../site-packages/pygments/lexers/j.py | 146 + .../pygments/lexers/javascript.py | 1533 +++ .../site-packages/pygments/lexers/julia.py | 335 + .../site-packages/pygments/lexers/jvm.py | 1666 ++++ .../site-packages/pygments/lexers/lisp.py | 2694 +++++ .../site-packages/pygments/lexers/make.py | 202 + .../site-packages/pygments/lexers/markup.py | 598 ++ .../site-packages/pygments/lexers/math.py | 21 + .../site-packages/pygments/lexers/matlab.py | 692 ++ .../site-packages/pygments/lexers/mime.py | 226 + .../site-packages/pygments/lexers/ml.py | 859 ++ .../site-packages/pygments/lexers/modeling.py | 366 + .../site-packages/pygments/lexers/modula2.py | 1561 +++ .../site-packages/pygments/lexers/monte.py | 204 + .../site-packages/pygments/lexers/mosel.py | 448 + .../site-packages/pygments/lexers/ncl.py | 894 ++ .../site-packages/pygments/lexers/nimrod.py | 159 + .../site-packages/pygments/lexers/nit.py | 64 + .../site-packages/pygments/lexers/nix.py | 136 + .../site-packages/pygments/lexers/oberon.py | 105 + .../pygments/lexers/objective.py | 504 + .../site-packages/pygments/lexers/ooc.py | 85 + .../site-packages/pygments/lexers/other.py | 41 + .../site-packages/pygments/lexers/parasail.py | 79 + .../site-packages/pygments/lexers/parsers.py | 835 ++ .../site-packages/pygments/lexers/pascal.py | 644 ++ .../site-packages/pygments/lexers/pawn.py | 199 + .../site-packages/pygments/lexers/perl.py | 718 ++ .../site-packages/pygments/lexers/php.py | 270 + .../site-packages/pygments/lexers/pony.py | 94 + .../site-packages/pygments/lexers/praat.py | 302 + .../site-packages/pygments/lexers/prolog.py | 306 + .../site-packages/pygments/lexers/python.py | 1153 +++ .../site-packages/pygments/lexers/qvt.py | 152 + .../site-packages/pygments/lexers/r.py | 193 + .../site-packages/pygments/lexers/rdf.py | 423 + .../site-packages/pygments/lexers/rebol.py | 431 + .../site-packages/pygments/lexers/resource.py | 85 + .../site-packages/pygments/lexers/ride.py | 139 + .../site-packages/pygments/lexers/rnc.py | 67 + .../site-packages/pygments/lexers/roboconf.py | 82 + .../pygments/lexers/robotframework.py | 559 ++ .../site-packages/pygments/lexers/ruby.py | 519 + .../site-packages/pygments/lexers/rust.py | 211 + .../site-packages/pygments/lexers/sas.py | 228 + .../site-packages/pygments/lexers/scdoc.py | 70 + .../pygments/lexers/scripting.py | 1275 +++ .../site-packages/pygments/lexers/sgf.py | 61 + .../site-packages/pygments/lexers/shell.py | 849 ++ .../site-packages/pygments/lexers/sieve.py | 69 + .../site-packages/pygments/lexers/slash.py | 185 + .../pygments/lexers/smalltalk.py | 195 + .../site-packages/pygments/lexers/smv.py | 79 + .../site-packages/pygments/lexers/snobol.py | 83 + .../site-packages/pygments/lexers/solidity.py | 93 + .../site-packages/pygments/lexers/special.py | 105 + .../site-packages/pygments/lexers/sql.py | 744 ++ .../site-packages/pygments/lexers/stata.py | 171 + .../pygments/lexers/supercollider.py | 90 + .../site-packages/pygments/lexers/tcl.py | 145 + .../pygments/lexers/templates.py | 2283 +++++ .../site-packages/pygments/lexers/teraterm.py | 158 + .../site-packages/pygments/lexers/testing.py | 207 + .../site-packages/pygments/lexers/text.py | 26 + .../site-packages/pygments/lexers/textedit.py | 169 + .../site-packages/pygments/lexers/textfmts.py | 431 + .../site-packages/pygments/lexers/theorem.py | 456 + .../pygments/lexers/trafficscript.py | 54 + .../pygments/lexers/typoscript.py | 219 + .../site-packages/pygments/lexers/unicon.py | 390 + .../site-packages/pygments/lexers/urbi.py | 133 + .../site-packages/pygments/lexers/usd.py | 90 + .../site-packages/pygments/lexers/varnish.py | 190 + .../pygments/lexers/verification.py | 114 + .../site-packages/pygments/lexers/web.py | 24 + .../site-packages/pygments/lexers/webidl.py | 297 + .../site-packages/pygments/lexers/webmisc.py | 992 ++ .../site-packages/pygments/lexers/whiley.py | 116 + .../site-packages/pygments/lexers/x10.py | 69 + .../site-packages/pygments/lexers/xorg.py | 37 + .../site-packages/pygments/lexers/zig.py | 129 + .../site-packages/pygments/modeline.py | 44 + .../site-packages/pygments/plugin.py | 70 + .../site-packages/pygments/regexopt.py | 92 + .../site-packages/pygments/scanner.py | 105 + .../site-packages/pygments/sphinxext.py | 156 + .../site-packages/pygments/style.py | 180 + .../site-packages/pygments/styles/__init__.py | 87 + .../site-packages/pygments/styles/abap.py | 29 + .../site-packages/pygments/styles/algol.py | 63 + .../site-packages/pygments/styles/algol_nu.py | 63 + .../site-packages/pygments/styles/arduino.py | 98 + .../site-packages/pygments/styles/autumn.py | 65 + .../site-packages/pygments/styles/borland.py | 51 + .../site-packages/pygments/styles/bw.py | 49 + .../site-packages/pygments/styles/colorful.py | 81 + .../site-packages/pygments/styles/default.py | 73 + .../site-packages/pygments/styles/emacs.py | 72 + .../site-packages/pygments/styles/friendly.py | 72 + .../site-packages/pygments/styles/fruity.py | 42 + .../site-packages/pygments/styles/igor.py | 29 + .../site-packages/pygments/styles/inkpot.py | 67 + .../site-packages/pygments/styles/lovelace.py | 97 + .../site-packages/pygments/styles/manni.py | 75 + .../site-packages/pygments/styles/monokai.py | 107 + .../site-packages/pygments/styles/murphy.py | 80 + .../site-packages/pygments/styles/native.py | 65 + .../pygments/styles/paraiso_dark.py | 125 + .../pygments/styles/paraiso_light.py | 125 + .../site-packages/pygments/styles/pastie.py | 75 + .../site-packages/pygments/styles/perldoc.py | 69 + .../pygments/styles/rainbow_dash.py | 89 + .../site-packages/pygments/styles/rrt.py | 33 + .../site-packages/pygments/styles/sas.py | 44 + .../pygments/styles/solarized.py | 130 + .../pygments/styles/stata_dark.py | 41 + .../pygments/styles/stata_light.py | 39 + .../site-packages/pygments/styles/tango.py | 141 + .../site-packages/pygments/styles/trac.py | 63 + .../site-packages/pygments/styles/vim.py | 63 + .../site-packages/pygments/styles/vs.py | 38 + .../site-packages/pygments/styles/xcode.py | 51 + .../site-packages/pygments/token.py | 213 + .../site-packages/pygments/unistring.py | 217 + .../python3.11/site-packages/pygments/util.py | 351 + .../INSTALLER | 1 + .../LICENSE | 54 + .../METADATA | 204 + .../RECORD | 44 + .../WHEEL | 6 + .../top_level.txt | 1 + .../zip-safe | 1 + .../pytz-2020.1.dist-info/DESCRIPTION.rst | 598 ++ .../pytz-2020.1.dist-info/INSTALLER | 1 + .../pytz-2020.1.dist-info/LICENSE.txt | 19 + .../pytz-2020.1.dist-info/METADATA | 632 ++ .../pytz-2020.1.dist-info/RECORD | 622 ++ .../pytz-2020.1.dist-info/REQUESTED | 0 .../site-packages/pytz-2020.1.dist-info/WHEEL | 6 + .../pytz-2020.1.dist-info/metadata.json | 1 + .../pytz-2020.1.dist-info/top_level.txt | 1 + .../pytz-2020.1.dist-info/zip-safe | 1 + .../python3.11/site-packages/pytz/__init__.py | 1552 +++ .../site-packages/pytz/exceptions.py | 59 + .../lib/python3.11/site-packages/pytz/lazy.py | 172 + .../site-packages/pytz/reference.py | 140 + .../python3.11/site-packages/pytz/tzfile.py | 134 + .../python3.11/site-packages/pytz/tzinfo.py | 577 ++ .../pytz/zoneinfo/Africa/Abidjan | Bin 0 -> 148 bytes .../site-packages/pytz/zoneinfo/Africa/Accra | Bin 0 -> 816 bytes .../pytz/zoneinfo/Africa/Addis_Ababa | Bin 0 -> 251 bytes .../pytz/zoneinfo/Africa/Algiers | Bin 0 -> 735 bytes .../site-packages/pytz/zoneinfo/Africa/Asmara | Bin 0 -> 251 bytes .../site-packages/pytz/zoneinfo/Africa/Asmera | Bin 0 -> 251 bytes .../site-packages/pytz/zoneinfo/Africa/Bamako | Bin 0 -> 148 bytes .../site-packages/pytz/zoneinfo/Africa/Bangui | Bin 0 -> 149 bytes .../site-packages/pytz/zoneinfo/Africa/Banjul | Bin 0 -> 148 bytes .../site-packages/pytz/zoneinfo/Africa/Bissau | Bin 0 -> 194 bytes .../pytz/zoneinfo/Africa/Blantyre | Bin 0 -> 149 bytes .../pytz/zoneinfo/Africa/Brazzaville | Bin 0 -> 149 bytes .../pytz/zoneinfo/Africa/Bujumbura | Bin 0 -> 149 bytes .../site-packages/pytz/zoneinfo/Africa/Cairo | Bin 0 -> 1955 bytes .../pytz/zoneinfo/Africa/Casablanca | Bin 0 -> 2429 bytes .../site-packages/pytz/zoneinfo/Africa/Ceuta | Bin 0 -> 2036 bytes .../pytz/zoneinfo/Africa/Conakry | Bin 0 -> 148 bytes .../site-packages/pytz/zoneinfo/Africa/Dakar | Bin 0 -> 148 bytes .../pytz/zoneinfo/Africa/Dar_es_Salaam | Bin 0 -> 251 bytes .../pytz/zoneinfo/Africa/Djibouti | Bin 0 -> 251 bytes .../site-packages/pytz/zoneinfo/Africa/Douala | Bin 0 -> 149 bytes .../pytz/zoneinfo/Africa/El_Aaiun | Bin 0 -> 2295 bytes .../pytz/zoneinfo/Africa/Freetown | Bin 0 -> 148 bytes .../pytz/zoneinfo/Africa/Gaborone | Bin 0 -> 149 bytes .../site-packages/pytz/zoneinfo/Africa/Harare | Bin 0 -> 149 bytes .../pytz/zoneinfo/Africa/Johannesburg | Bin 0 -> 246 bytes .../site-packages/pytz/zoneinfo/Africa/Juba | Bin 0 -> 653 bytes .../pytz/zoneinfo/Africa/Kampala | Bin 0 -> 251 bytes .../pytz/zoneinfo/Africa/Khartoum | Bin 0 -> 679 bytes .../site-packages/pytz/zoneinfo/Africa/Kigali | Bin 0 -> 149 bytes .../pytz/zoneinfo/Africa/Kinshasa | Bin 0 -> 149 bytes .../site-packages/pytz/zoneinfo/Africa/Lagos | Bin 0 -> 149 bytes .../pytz/zoneinfo/Africa/Libreville | Bin 0 -> 149 bytes .../site-packages/pytz/zoneinfo/Africa/Lome | Bin 0 -> 148 bytes .../site-packages/pytz/zoneinfo/Africa/Luanda | Bin 0 -> 149 bytes .../pytz/zoneinfo/Africa/Lubumbashi | Bin 0 -> 149 bytes .../site-packages/pytz/zoneinfo/Africa/Lusaka | Bin 0 -> 149 bytes .../site-packages/pytz/zoneinfo/Africa/Malabo | Bin 0 -> 149 bytes .../site-packages/pytz/zoneinfo/Africa/Maputo | Bin 0 -> 149 bytes .../site-packages/pytz/zoneinfo/Africa/Maseru | Bin 0 -> 246 bytes .../pytz/zoneinfo/Africa/Mbabane | Bin 0 -> 246 bytes .../pytz/zoneinfo/Africa/Mogadishu | Bin 0 -> 251 bytes .../pytz/zoneinfo/Africa/Monrovia | Bin 0 -> 208 bytes .../pytz/zoneinfo/Africa/Nairobi | Bin 0 -> 251 bytes .../pytz/zoneinfo/Africa/Ndjamena | Bin 0 -> 199 bytes .../site-packages/pytz/zoneinfo/Africa/Niamey | Bin 0 -> 149 bytes .../pytz/zoneinfo/Africa/Nouakchott | Bin 0 -> 148 bytes .../pytz/zoneinfo/Africa/Ouagadougou | Bin 0 -> 148 bytes .../pytz/zoneinfo/Africa/Porto-Novo | Bin 0 -> 149 bytes .../pytz/zoneinfo/Africa/Sao_Tome | Bin 0 -> 254 bytes .../pytz/zoneinfo/Africa/Timbuktu | Bin 0 -> 148 bytes .../pytz/zoneinfo/Africa/Tripoli | Bin 0 -> 625 bytes .../site-packages/pytz/zoneinfo/Africa/Tunis | Bin 0 -> 689 bytes .../pytz/zoneinfo/Africa/Windhoek | Bin 0 -> 955 bytes .../site-packages/pytz/zoneinfo/America/Adak | Bin 0 -> 2356 bytes .../pytz/zoneinfo/America/Anchorage | Bin 0 -> 2371 bytes .../pytz/zoneinfo/America/Anguilla | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/Antigua | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/Araguaina | Bin 0 -> 884 bytes .../zoneinfo/America/Argentina/Buenos_Aires | Bin 0 -> 1076 bytes .../pytz/zoneinfo/America/Argentina/Catamarca | Bin 0 -> 1076 bytes .../zoneinfo/America/Argentina/ComodRivadavia | Bin 0 -> 1076 bytes .../pytz/zoneinfo/America/Argentina/Cordoba | Bin 0 -> 1076 bytes .../pytz/zoneinfo/America/Argentina/Jujuy | Bin 0 -> 1048 bytes .../pytz/zoneinfo/America/Argentina/La_Rioja | Bin 0 -> 1090 bytes .../pytz/zoneinfo/America/Argentina/Mendoza | Bin 0 -> 1076 bytes .../zoneinfo/America/Argentina/Rio_Gallegos | Bin 0 -> 1076 bytes .../pytz/zoneinfo/America/Argentina/Salta | Bin 0 -> 1048 bytes .../pytz/zoneinfo/America/Argentina/San_Juan | Bin 0 -> 1090 bytes .../pytz/zoneinfo/America/Argentina/San_Luis | Bin 0 -> 1102 bytes .../pytz/zoneinfo/America/Argentina/Tucuman | Bin 0 -> 1104 bytes .../pytz/zoneinfo/America/Argentina/Ushuaia | Bin 0 -> 1076 bytes .../site-packages/pytz/zoneinfo/America/Aruba | Bin 0 -> 186 bytes .../pytz/zoneinfo/America/Asuncion | Bin 0 -> 2044 bytes .../pytz/zoneinfo/America/Atikokan | Bin 0 -> 336 bytes .../site-packages/pytz/zoneinfo/America/Atka | Bin 0 -> 2356 bytes .../site-packages/pytz/zoneinfo/America/Bahia | Bin 0 -> 1024 bytes .../pytz/zoneinfo/America/Bahia_Banderas | Bin 0 -> 1546 bytes .../pytz/zoneinfo/America/Barbados | Bin 0 -> 314 bytes .../site-packages/pytz/zoneinfo/America/Belem | Bin 0 -> 576 bytes .../pytz/zoneinfo/America/Belize | Bin 0 -> 948 bytes .../pytz/zoneinfo/America/Blanc-Sablon | Bin 0 -> 298 bytes .../pytz/zoneinfo/America/Boa_Vista | Bin 0 -> 632 bytes .../pytz/zoneinfo/America/Bogota | Bin 0 -> 246 bytes .../site-packages/pytz/zoneinfo/America/Boise | Bin 0 -> 2394 bytes .../pytz/zoneinfo/America/Buenos_Aires | Bin 0 -> 1076 bytes .../pytz/zoneinfo/America/Cambridge_Bay | Bin 0 -> 2084 bytes .../pytz/zoneinfo/America/Campo_Grande | Bin 0 -> 1444 bytes .../pytz/zoneinfo/America/Cancun | Bin 0 -> 782 bytes .../pytz/zoneinfo/America/Caracas | Bin 0 -> 264 bytes .../pytz/zoneinfo/America/Catamarca | Bin 0 -> 1076 bytes .../pytz/zoneinfo/America/Cayenne | Bin 0 -> 198 bytes .../pytz/zoneinfo/America/Cayman | Bin 0 -> 182 bytes .../pytz/zoneinfo/America/Chicago | Bin 0 -> 3576 bytes .../pytz/zoneinfo/America/Chihuahua | Bin 0 -> 1484 bytes .../pytz/zoneinfo/America/Coral_Harbour | Bin 0 -> 336 bytes .../pytz/zoneinfo/America/Cordoba | Bin 0 -> 1076 bytes .../pytz/zoneinfo/America/Costa_Rica | Bin 0 -> 316 bytes .../pytz/zoneinfo/America/Creston | Bin 0 -> 208 bytes .../pytz/zoneinfo/America/Cuiaba | Bin 0 -> 1416 bytes .../pytz/zoneinfo/America/Curacao | Bin 0 -> 186 bytes .../pytz/zoneinfo/America/Danmarkshavn | Bin 0 -> 698 bytes .../pytz/zoneinfo/America/Dawson | Bin 0 -> 1600 bytes .../pytz/zoneinfo/America/Dawson_Creek | Bin 0 -> 1050 bytes .../pytz/zoneinfo/America/Denver | Bin 0 -> 2444 bytes .../pytz/zoneinfo/America/Detroit | Bin 0 -> 2230 bytes .../pytz/zoneinfo/America/Dominica | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/Edmonton | Bin 0 -> 2332 bytes .../pytz/zoneinfo/America/Eirunepe | Bin 0 -> 656 bytes .../pytz/zoneinfo/America/El_Salvador | Bin 0 -> 224 bytes .../pytz/zoneinfo/America/Ensenada | Bin 0 -> 2342 bytes .../pytz/zoneinfo/America/Fort_Nelson | Bin 0 -> 2240 bytes .../pytz/zoneinfo/America/Fort_Wayne | Bin 0 -> 1666 bytes .../pytz/zoneinfo/America/Fortaleza | Bin 0 -> 716 bytes .../pytz/zoneinfo/America/Glace_Bay | Bin 0 -> 2192 bytes .../pytz/zoneinfo/America/Godthab | Bin 0 -> 1878 bytes .../pytz/zoneinfo/America/Goose_Bay | Bin 0 -> 3210 bytes .../pytz/zoneinfo/America/Grand_Turk | Bin 0 -> 1848 bytes .../pytz/zoneinfo/America/Grenada | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/Guadeloupe | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/Guatemala | Bin 0 -> 280 bytes .../pytz/zoneinfo/America/Guayaquil | Bin 0 -> 246 bytes .../pytz/zoneinfo/America/Guyana | Bin 0 -> 236 bytes .../pytz/zoneinfo/America/Halifax | Bin 0 -> 3424 bytes .../pytz/zoneinfo/America/Havana | Bin 0 -> 2416 bytes .../pytz/zoneinfo/America/Hermosillo | Bin 0 -> 416 bytes .../zoneinfo/America/Indiana/Indianapolis | Bin 0 -> 1666 bytes .../pytz/zoneinfo/America/Indiana/Knox | Bin 0 -> 2428 bytes .../pytz/zoneinfo/America/Indiana/Marengo | Bin 0 -> 1722 bytes .../pytz/zoneinfo/America/Indiana/Petersburg | Bin 0 -> 1904 bytes .../pytz/zoneinfo/America/Indiana/Tell_City | Bin 0 -> 1684 bytes .../pytz/zoneinfo/America/Indiana/Vevay | Bin 0 -> 1414 bytes .../pytz/zoneinfo/America/Indiana/Vincennes | Bin 0 -> 1694 bytes .../pytz/zoneinfo/America/Indiana/Winamac | Bin 0 -> 1778 bytes .../pytz/zoneinfo/America/Indianapolis | Bin 0 -> 1666 bytes .../pytz/zoneinfo/America/Inuvik | Bin 0 -> 1894 bytes .../pytz/zoneinfo/America/Iqaluit | Bin 0 -> 2032 bytes .../pytz/zoneinfo/America/Jamaica | Bin 0 -> 482 bytes .../site-packages/pytz/zoneinfo/America/Jujuy | Bin 0 -> 1048 bytes .../pytz/zoneinfo/America/Juneau | Bin 0 -> 2353 bytes .../pytz/zoneinfo/America/Kentucky/Louisville | Bin 0 -> 2772 bytes .../pytz/zoneinfo/America/Kentucky/Monticello | Bin 0 -> 2352 bytes .../pytz/zoneinfo/America/Knox_IN | Bin 0 -> 2428 bytes .../pytz/zoneinfo/America/Kralendijk | Bin 0 -> 186 bytes .../pytz/zoneinfo/America/La_Paz | Bin 0 -> 232 bytes .../site-packages/pytz/zoneinfo/America/Lima | Bin 0 -> 406 bytes .../pytz/zoneinfo/America/Los_Angeles | Bin 0 -> 2836 bytes .../pytz/zoneinfo/America/Louisville | Bin 0 -> 2772 bytes .../pytz/zoneinfo/America/Lower_Princes | Bin 0 -> 186 bytes .../pytz/zoneinfo/America/Maceio | Bin 0 -> 744 bytes .../pytz/zoneinfo/America/Managua | Bin 0 -> 430 bytes .../pytz/zoneinfo/America/Manaus | Bin 0 -> 604 bytes .../pytz/zoneinfo/America/Marigot | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/Martinique | Bin 0 -> 232 bytes .../pytz/zoneinfo/America/Matamoros | Bin 0 -> 1390 bytes .../pytz/zoneinfo/America/Mazatlan | Bin 0 -> 1526 bytes .../pytz/zoneinfo/America/Mendoza | Bin 0 -> 1076 bytes .../pytz/zoneinfo/America/Menominee | Bin 0 -> 2274 bytes .../pytz/zoneinfo/America/Merida | Bin 0 -> 1422 bytes .../pytz/zoneinfo/America/Metlakatla | Bin 0 -> 1423 bytes .../pytz/zoneinfo/America/Mexico_City | Bin 0 -> 1584 bytes .../pytz/zoneinfo/America/Miquelon | Bin 0 -> 1666 bytes .../pytz/zoneinfo/America/Moncton | Bin 0 -> 3154 bytes .../pytz/zoneinfo/America/Monterrey | Bin 0 -> 1390 bytes .../pytz/zoneinfo/America/Montevideo | Bin 0 -> 1510 bytes .../pytz/zoneinfo/America/Montreal | Bin 0 -> 3494 bytes .../pytz/zoneinfo/America/Montserrat | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/Nassau | Bin 0 -> 2258 bytes .../pytz/zoneinfo/America/New_York | Bin 0 -> 3536 bytes .../pytz/zoneinfo/America/Nipigon | Bin 0 -> 2122 bytes .../site-packages/pytz/zoneinfo/America/Nome | Bin 0 -> 2367 bytes .../pytz/zoneinfo/America/Noronha | Bin 0 -> 716 bytes .../pytz/zoneinfo/America/North_Dakota/Beulah | Bin 0 -> 2380 bytes .../pytz/zoneinfo/America/North_Dakota/Center | Bin 0 -> 2380 bytes .../zoneinfo/America/North_Dakota/New_Salem | Bin 0 -> 2380 bytes .../site-packages/pytz/zoneinfo/America/Nuuk | Bin 0 -> 1878 bytes .../pytz/zoneinfo/America/Ojinaga | Bin 0 -> 1484 bytes .../pytz/zoneinfo/America/Panama | Bin 0 -> 182 bytes .../pytz/zoneinfo/America/Pangnirtung | Bin 0 -> 2094 bytes .../pytz/zoneinfo/America/Paramaribo | Bin 0 -> 262 bytes .../pytz/zoneinfo/America/Phoenix | Bin 0 -> 328 bytes .../pytz/zoneinfo/America/Port-au-Prince | Bin 0 -> 1434 bytes .../pytz/zoneinfo/America/Port_of_Spain | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/Porto_Acre | Bin 0 -> 628 bytes .../pytz/zoneinfo/America/Porto_Velho | Bin 0 -> 576 bytes .../pytz/zoneinfo/America/Puerto_Rico | Bin 0 -> 246 bytes .../pytz/zoneinfo/America/Punta_Arenas | Bin 0 -> 1902 bytes .../pytz/zoneinfo/America/Rainy_River | Bin 0 -> 2122 bytes .../pytz/zoneinfo/America/Rankin_Inlet | Bin 0 -> 1892 bytes .../pytz/zoneinfo/America/Recife | Bin 0 -> 716 bytes .../pytz/zoneinfo/America/Regina | Bin 0 -> 980 bytes .../pytz/zoneinfo/America/Resolute | Bin 0 -> 1892 bytes .../pytz/zoneinfo/America/Rio_Branco | Bin 0 -> 628 bytes .../pytz/zoneinfo/America/Rosario | Bin 0 -> 1076 bytes .../pytz/zoneinfo/America/Santa_Isabel | Bin 0 -> 2342 bytes .../pytz/zoneinfo/America/Santarem | Bin 0 -> 602 bytes .../pytz/zoneinfo/America/Santiago | Bin 0 -> 2529 bytes .../pytz/zoneinfo/America/Santo_Domingo | Bin 0 -> 458 bytes .../pytz/zoneinfo/America/Sao_Paulo | Bin 0 -> 1444 bytes .../pytz/zoneinfo/America/Scoresbysund | Bin 0 -> 1916 bytes .../pytz/zoneinfo/America/Shiprock | Bin 0 -> 2444 bytes .../site-packages/pytz/zoneinfo/America/Sitka | Bin 0 -> 2329 bytes .../pytz/zoneinfo/America/St_Barthelemy | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/St_Johns | Bin 0 -> 3655 bytes .../pytz/zoneinfo/America/St_Kitts | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/St_Lucia | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/St_Thomas | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/St_Vincent | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/Swift_Current | Bin 0 -> 560 bytes .../pytz/zoneinfo/America/Tegucigalpa | Bin 0 -> 252 bytes .../site-packages/pytz/zoneinfo/America/Thule | Bin 0 -> 1502 bytes .../pytz/zoneinfo/America/Thunder_Bay | Bin 0 -> 2202 bytes .../pytz/zoneinfo/America/Tijuana | Bin 0 -> 2342 bytes .../pytz/zoneinfo/America/Toronto | Bin 0 -> 3494 bytes .../pytz/zoneinfo/America/Tortola | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/Vancouver | Bin 0 -> 2892 bytes .../pytz/zoneinfo/America/Virgin | Bin 0 -> 148 bytes .../pytz/zoneinfo/America/Whitehorse | Bin 0 -> 1600 bytes .../pytz/zoneinfo/America/Winnipeg | Bin 0 -> 2868 bytes .../pytz/zoneinfo/America/Yakutat | Bin 0 -> 2305 bytes .../pytz/zoneinfo/America/Yellowknife | Bin 0 -> 1966 bytes .../pytz/zoneinfo/Antarctica/Casey | Bin 0 -> 297 bytes .../pytz/zoneinfo/Antarctica/Davis | Bin 0 -> 297 bytes .../pytz/zoneinfo/Antarctica/DumontDUrville | Bin 0 -> 194 bytes .../pytz/zoneinfo/Antarctica/Macquarie | Bin 0 -> 1520 bytes .../pytz/zoneinfo/Antarctica/Mawson | Bin 0 -> 199 bytes .../pytz/zoneinfo/Antarctica/McMurdo | Bin 0 -> 2437 bytes .../pytz/zoneinfo/Antarctica/Palmer | Bin 0 -> 1418 bytes .../pytz/zoneinfo/Antarctica/Rothera | Bin 0 -> 164 bytes .../pytz/zoneinfo/Antarctica/South_Pole | Bin 0 -> 2437 bytes .../pytz/zoneinfo/Antarctica/Syowa | Bin 0 -> 165 bytes .../pytz/zoneinfo/Antarctica/Troll | Bin 0 -> 1162 bytes .../pytz/zoneinfo/Antarctica/Vostok | Bin 0 -> 165 bytes .../pytz/zoneinfo/Arctic/Longyearbyen | Bin 0 -> 2228 bytes .../site-packages/pytz/zoneinfo/Asia/Aden | Bin 0 -> 165 bytes .../site-packages/pytz/zoneinfo/Asia/Almaty | Bin 0 -> 997 bytes .../site-packages/pytz/zoneinfo/Asia/Amman | Bin 0 -> 1853 bytes .../site-packages/pytz/zoneinfo/Asia/Anadyr | Bin 0 -> 1188 bytes .../site-packages/pytz/zoneinfo/Asia/Aqtau | Bin 0 -> 983 bytes .../site-packages/pytz/zoneinfo/Asia/Aqtobe | Bin 0 -> 1011 bytes .../site-packages/pytz/zoneinfo/Asia/Ashgabat | Bin 0 -> 619 bytes .../pytz/zoneinfo/Asia/Ashkhabad | Bin 0 -> 619 bytes .../site-packages/pytz/zoneinfo/Asia/Atyrau | Bin 0 -> 991 bytes .../site-packages/pytz/zoneinfo/Asia/Baghdad | Bin 0 -> 983 bytes .../site-packages/pytz/zoneinfo/Asia/Bahrain | Bin 0 -> 199 bytes .../site-packages/pytz/zoneinfo/Asia/Baku | Bin 0 -> 1227 bytes .../site-packages/pytz/zoneinfo/Asia/Bangkok | Bin 0 -> 199 bytes .../site-packages/pytz/zoneinfo/Asia/Barnaul | Bin 0 -> 1221 bytes .../site-packages/pytz/zoneinfo/Asia/Beirut | Bin 0 -> 2154 bytes .../site-packages/pytz/zoneinfo/Asia/Bishkek | Bin 0 -> 983 bytes .../site-packages/pytz/zoneinfo/Asia/Brunei | Bin 0 -> 203 bytes .../site-packages/pytz/zoneinfo/Asia/Calcutta | Bin 0 -> 285 bytes .../site-packages/pytz/zoneinfo/Asia/Chita | Bin 0 -> 1221 bytes .../pytz/zoneinfo/Asia/Choibalsan | Bin 0 -> 949 bytes .../pytz/zoneinfo/Asia/Chongqing | Bin 0 -> 561 bytes .../pytz/zoneinfo/Asia/Chungking | Bin 0 -> 561 bytes .../site-packages/pytz/zoneinfo/Asia/Colombo | Bin 0 -> 372 bytes .../site-packages/pytz/zoneinfo/Asia/Dacca | Bin 0 -> 337 bytes .../site-packages/pytz/zoneinfo/Asia/Damascus | Bin 0 -> 2294 bytes .../site-packages/pytz/zoneinfo/Asia/Dhaka | Bin 0 -> 337 bytes .../site-packages/pytz/zoneinfo/Asia/Dili | Bin 0 -> 227 bytes .../site-packages/pytz/zoneinfo/Asia/Dubai | Bin 0 -> 165 bytes .../site-packages/pytz/zoneinfo/Asia/Dushanbe | Bin 0 -> 591 bytes .../pytz/zoneinfo/Asia/Famagusta | Bin 0 -> 2028 bytes .../site-packages/pytz/zoneinfo/Asia/Gaza | Bin 0 -> 2316 bytes .../site-packages/pytz/zoneinfo/Asia/Harbin | Bin 0 -> 561 bytes .../site-packages/pytz/zoneinfo/Asia/Hebron | Bin 0 -> 2344 bytes .../pytz/zoneinfo/Asia/Ho_Chi_Minh | Bin 0 -> 351 bytes .../pytz/zoneinfo/Asia/Hong_Kong | Bin 0 -> 1203 bytes .../site-packages/pytz/zoneinfo/Asia/Hovd | Bin 0 -> 891 bytes .../site-packages/pytz/zoneinfo/Asia/Irkutsk | Bin 0 -> 1243 bytes .../site-packages/pytz/zoneinfo/Asia/Istanbul | Bin 0 -> 1947 bytes .../site-packages/pytz/zoneinfo/Asia/Jakarta | Bin 0 -> 355 bytes .../site-packages/pytz/zoneinfo/Asia/Jayapura | Bin 0 -> 221 bytes .../pytz/zoneinfo/Asia/Jerusalem | Bin 0 -> 2288 bytes .../site-packages/pytz/zoneinfo/Asia/Kabul | Bin 0 -> 208 bytes .../pytz/zoneinfo/Asia/Kamchatka | Bin 0 -> 1166 bytes .../site-packages/pytz/zoneinfo/Asia/Karachi | Bin 0 -> 379 bytes .../site-packages/pytz/zoneinfo/Asia/Kashgar | Bin 0 -> 165 bytes .../pytz/zoneinfo/Asia/Kathmandu | Bin 0 -> 212 bytes .../site-packages/pytz/zoneinfo/Asia/Katmandu | Bin 0 -> 212 bytes .../site-packages/pytz/zoneinfo/Asia/Khandyga | Bin 0 -> 1271 bytes .../site-packages/pytz/zoneinfo/Asia/Kolkata | Bin 0 -> 285 bytes .../pytz/zoneinfo/Asia/Krasnoyarsk | Bin 0 -> 1207 bytes .../pytz/zoneinfo/Asia/Kuala_Lumpur | Bin 0 -> 383 bytes .../site-packages/pytz/zoneinfo/Asia/Kuching | Bin 0 -> 483 bytes .../site-packages/pytz/zoneinfo/Asia/Kuwait | Bin 0 -> 165 bytes .../site-packages/pytz/zoneinfo/Asia/Macao | Bin 0 -> 1227 bytes .../site-packages/pytz/zoneinfo/Asia/Macau | Bin 0 -> 1227 bytes .../site-packages/pytz/zoneinfo/Asia/Magadan | Bin 0 -> 1222 bytes .../site-packages/pytz/zoneinfo/Asia/Makassar | Bin 0 -> 254 bytes .../site-packages/pytz/zoneinfo/Asia/Manila | Bin 0 -> 328 bytes .../site-packages/pytz/zoneinfo/Asia/Muscat | Bin 0 -> 165 bytes .../site-packages/pytz/zoneinfo/Asia/Nicosia | Bin 0 -> 2002 bytes .../pytz/zoneinfo/Asia/Novokuznetsk | Bin 0 -> 1165 bytes .../pytz/zoneinfo/Asia/Novosibirsk | Bin 0 -> 1221 bytes .../site-packages/pytz/zoneinfo/Asia/Omsk | Bin 0 -> 1207 bytes .../site-packages/pytz/zoneinfo/Asia/Oral | Bin 0 -> 1005 bytes .../pytz/zoneinfo/Asia/Phnom_Penh | Bin 0 -> 199 bytes .../pytz/zoneinfo/Asia/Pontianak | Bin 0 -> 353 bytes .../pytz/zoneinfo/Asia/Pyongyang | Bin 0 -> 237 bytes .../site-packages/pytz/zoneinfo/Asia/Qatar | Bin 0 -> 199 bytes .../site-packages/pytz/zoneinfo/Asia/Qostanay | Bin 0 -> 1011 bytes .../pytz/zoneinfo/Asia/Qyzylorda | Bin 0 -> 1025 bytes .../site-packages/pytz/zoneinfo/Asia/Rangoon | Bin 0 -> 268 bytes .../site-packages/pytz/zoneinfo/Asia/Riyadh | Bin 0 -> 165 bytes .../site-packages/pytz/zoneinfo/Asia/Saigon | Bin 0 -> 351 bytes .../site-packages/pytz/zoneinfo/Asia/Sakhalin | Bin 0 -> 1202 bytes .../pytz/zoneinfo/Asia/Samarkand | Bin 0 -> 577 bytes .../site-packages/pytz/zoneinfo/Asia/Seoul | Bin 0 -> 617 bytes .../site-packages/pytz/zoneinfo/Asia/Shanghai | Bin 0 -> 561 bytes .../pytz/zoneinfo/Asia/Singapore | Bin 0 -> 383 bytes .../pytz/zoneinfo/Asia/Srednekolymsk | Bin 0 -> 1208 bytes .../site-packages/pytz/zoneinfo/Asia/Taipei | Bin 0 -> 761 bytes .../site-packages/pytz/zoneinfo/Asia/Tashkent | Bin 0 -> 591 bytes .../site-packages/pytz/zoneinfo/Asia/Tbilisi | Bin 0 -> 1035 bytes .../site-packages/pytz/zoneinfo/Asia/Tehran | Bin 0 -> 2582 bytes .../site-packages/pytz/zoneinfo/Asia/Tel_Aviv | Bin 0 -> 2288 bytes .../site-packages/pytz/zoneinfo/Asia/Thimbu | Bin 0 -> 203 bytes .../site-packages/pytz/zoneinfo/Asia/Thimphu | Bin 0 -> 203 bytes .../site-packages/pytz/zoneinfo/Asia/Tokyo | Bin 0 -> 309 bytes .../site-packages/pytz/zoneinfo/Asia/Tomsk | Bin 0 -> 1221 bytes .../pytz/zoneinfo/Asia/Ujung_Pandang | Bin 0 -> 254 bytes .../pytz/zoneinfo/Asia/Ulaanbaatar | Bin 0 -> 891 bytes .../pytz/zoneinfo/Asia/Ulan_Bator | Bin 0 -> 891 bytes .../site-packages/pytz/zoneinfo/Asia/Urumqi | Bin 0 -> 165 bytes .../site-packages/pytz/zoneinfo/Asia/Ust-Nera | Bin 0 -> 1252 bytes .../pytz/zoneinfo/Asia/Vientiane | Bin 0 -> 199 bytes .../pytz/zoneinfo/Asia/Vladivostok | Bin 0 -> 1208 bytes .../site-packages/pytz/zoneinfo/Asia/Yakutsk | Bin 0 -> 1207 bytes .../site-packages/pytz/zoneinfo/Asia/Yangon | Bin 0 -> 268 bytes .../pytz/zoneinfo/Asia/Yekaterinburg | Bin 0 -> 1243 bytes .../site-packages/pytz/zoneinfo/Asia/Yerevan | Bin 0 -> 1151 bytes .../pytz/zoneinfo/Atlantic/Azores | Bin 0 -> 3484 bytes .../pytz/zoneinfo/Atlantic/Bermuda | Bin 0 -> 1978 bytes .../pytz/zoneinfo/Atlantic/Canary | Bin 0 -> 1897 bytes .../pytz/zoneinfo/Atlantic/Cape_Verde | Bin 0 -> 270 bytes .../pytz/zoneinfo/Atlantic/Faeroe | Bin 0 -> 1815 bytes .../pytz/zoneinfo/Atlantic/Faroe | Bin 0 -> 1815 bytes .../pytz/zoneinfo/Atlantic/Jan_Mayen | Bin 0 -> 2228 bytes .../pytz/zoneinfo/Atlantic/Madeira | Bin 0 -> 3475 bytes .../pytz/zoneinfo/Atlantic/Reykjavik | Bin 0 -> 1162 bytes .../pytz/zoneinfo/Atlantic/South_Georgia | Bin 0 -> 164 bytes .../pytz/zoneinfo/Atlantic/St_Helena | Bin 0 -> 148 bytes .../pytz/zoneinfo/Atlantic/Stanley | Bin 0 -> 1214 bytes .../site-packages/pytz/zoneinfo/Australia/ACT | Bin 0 -> 2204 bytes .../pytz/zoneinfo/Australia/Adelaide | Bin 0 -> 2222 bytes .../pytz/zoneinfo/Australia/Brisbane | Bin 0 -> 433 bytes .../pytz/zoneinfo/Australia/Broken_Hill | Bin 0 -> 2243 bytes .../pytz/zoneinfo/Australia/Canberra | Bin 0 -> 2204 bytes .../pytz/zoneinfo/Australia/Currie | Bin 0 -> 2204 bytes .../pytz/zoneinfo/Australia/Darwin | Bin 0 -> 304 bytes .../pytz/zoneinfo/Australia/Eucla | Bin 0 -> 484 bytes .../pytz/zoneinfo/Australia/Hobart | Bin 0 -> 2316 bytes .../site-packages/pytz/zoneinfo/Australia/LHI | Bin 0 -> 1860 bytes .../pytz/zoneinfo/Australia/Lindeman | Bin 0 -> 489 bytes .../pytz/zoneinfo/Australia/Lord_Howe | Bin 0 -> 1860 bytes .../pytz/zoneinfo/Australia/Melbourne | Bin 0 -> 2204 bytes .../site-packages/pytz/zoneinfo/Australia/NSW | Bin 0 -> 2204 bytes .../pytz/zoneinfo/Australia/North | Bin 0 -> 304 bytes .../pytz/zoneinfo/Australia/Perth | Bin 0 -> 460 bytes .../pytz/zoneinfo/Australia/Queensland | Bin 0 -> 433 bytes .../pytz/zoneinfo/Australia/South | Bin 0 -> 2222 bytes .../pytz/zoneinfo/Australia/Sydney | Bin 0 -> 2204 bytes .../pytz/zoneinfo/Australia/Tasmania | Bin 0 -> 2316 bytes .../pytz/zoneinfo/Australia/Victoria | Bin 0 -> 2204 bytes .../pytz/zoneinfo/Australia/West | Bin 0 -> 460 bytes .../pytz/zoneinfo/Australia/Yancowinna | Bin 0 -> 2243 bytes .../site-packages/pytz/zoneinfo/Brazil/Acre | Bin 0 -> 628 bytes .../pytz/zoneinfo/Brazil/DeNoronha | Bin 0 -> 716 bytes .../site-packages/pytz/zoneinfo/Brazil/East | Bin 0 -> 1444 bytes .../site-packages/pytz/zoneinfo/Brazil/West | Bin 0 -> 604 bytes .../site-packages/pytz/zoneinfo/CET | Bin 0 -> 2094 bytes .../site-packages/pytz/zoneinfo/CST6CDT | Bin 0 -> 2310 bytes .../pytz/zoneinfo/Canada/Atlantic | Bin 0 -> 3424 bytes .../pytz/zoneinfo/Canada/Central | Bin 0 -> 2868 bytes .../pytz/zoneinfo/Canada/Eastern | Bin 0 -> 3494 bytes .../pytz/zoneinfo/Canada/Mountain | Bin 0 -> 2332 bytes .../pytz/zoneinfo/Canada/Newfoundland | Bin 0 -> 3655 bytes .../pytz/zoneinfo/Canada/Pacific | Bin 0 -> 2892 bytes .../pytz/zoneinfo/Canada/Saskatchewan | Bin 0 -> 980 bytes .../site-packages/pytz/zoneinfo/Canada/Yukon | Bin 0 -> 1600 bytes .../pytz/zoneinfo/Chile/Continental | Bin 0 -> 2529 bytes .../pytz/zoneinfo/Chile/EasterIsland | Bin 0 -> 2233 bytes .../site-packages/pytz/zoneinfo/Cuba | Bin 0 -> 2416 bytes .../site-packages/pytz/zoneinfo/EET | Bin 0 -> 1908 bytes .../site-packages/pytz/zoneinfo/EST | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/EST5EDT | Bin 0 -> 2310 bytes .../site-packages/pytz/zoneinfo/Egypt | Bin 0 -> 1955 bytes .../site-packages/pytz/zoneinfo/Eire | Bin 0 -> 3492 bytes .../site-packages/pytz/zoneinfo/Etc/GMT | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/Etc/GMT+0 | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/Etc/GMT+1 | Bin 0 -> 116 bytes .../site-packages/pytz/zoneinfo/Etc/GMT+10 | Bin 0 -> 117 bytes .../site-packages/pytz/zoneinfo/Etc/GMT+11 | Bin 0 -> 117 bytes .../site-packages/pytz/zoneinfo/Etc/GMT+12 | Bin 0 -> 117 bytes .../site-packages/pytz/zoneinfo/Etc/GMT+2 | Bin 0 -> 116 bytes .../site-packages/pytz/zoneinfo/Etc/GMT+3 | Bin 0 -> 116 bytes .../site-packages/pytz/zoneinfo/Etc/GMT+4 | Bin 0 -> 116 bytes .../site-packages/pytz/zoneinfo/Etc/GMT+5 | Bin 0 -> 116 bytes .../site-packages/pytz/zoneinfo/Etc/GMT+6 | Bin 0 -> 116 bytes .../site-packages/pytz/zoneinfo/Etc/GMT+7 | Bin 0 -> 116 bytes .../site-packages/pytz/zoneinfo/Etc/GMT+8 | Bin 0 -> 116 bytes .../site-packages/pytz/zoneinfo/Etc/GMT+9 | Bin 0 -> 116 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-0 | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-1 | Bin 0 -> 117 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-10 | Bin 0 -> 118 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-11 | Bin 0 -> 118 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-12 | Bin 0 -> 118 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-13 | Bin 0 -> 118 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-14 | Bin 0 -> 118 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-2 | Bin 0 -> 117 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-3 | Bin 0 -> 117 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-4 | Bin 0 -> 117 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-5 | Bin 0 -> 117 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-6 | Bin 0 -> 117 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-7 | Bin 0 -> 117 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-8 | Bin 0 -> 117 bytes .../site-packages/pytz/zoneinfo/Etc/GMT-9 | Bin 0 -> 117 bytes .../site-packages/pytz/zoneinfo/Etc/GMT0 | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/Etc/Greenwich | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/Etc/UCT | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/Etc/UTC | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/Etc/Universal | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/Etc/Zulu | Bin 0 -> 114 bytes .../pytz/zoneinfo/Europe/Amsterdam | Bin 0 -> 2910 bytes .../pytz/zoneinfo/Europe/Andorra | Bin 0 -> 1742 bytes .../pytz/zoneinfo/Europe/Astrakhan | Bin 0 -> 1165 bytes .../site-packages/pytz/zoneinfo/Europe/Athens | Bin 0 -> 2262 bytes .../pytz/zoneinfo/Europe/Belfast | Bin 0 -> 3648 bytes .../pytz/zoneinfo/Europe/Belgrade | Bin 0 -> 1920 bytes .../site-packages/pytz/zoneinfo/Europe/Berlin | Bin 0 -> 2298 bytes .../pytz/zoneinfo/Europe/Bratislava | Bin 0 -> 2301 bytes .../pytz/zoneinfo/Europe/Brussels | Bin 0 -> 2933 bytes .../pytz/zoneinfo/Europe/Bucharest | Bin 0 -> 2184 bytes .../pytz/zoneinfo/Europe/Budapest | Bin 0 -> 2368 bytes .../pytz/zoneinfo/Europe/Busingen | Bin 0 -> 1909 bytes .../pytz/zoneinfo/Europe/Chisinau | Bin 0 -> 2390 bytes .../pytz/zoneinfo/Europe/Copenhagen | Bin 0 -> 2137 bytes .../site-packages/pytz/zoneinfo/Europe/Dublin | Bin 0 -> 3492 bytes .../pytz/zoneinfo/Europe/Gibraltar | Bin 0 -> 3052 bytes .../pytz/zoneinfo/Europe/Guernsey | Bin 0 -> 3648 bytes .../pytz/zoneinfo/Europe/Helsinki | Bin 0 -> 1900 bytes .../pytz/zoneinfo/Europe/Isle_of_Man | Bin 0 -> 3648 bytes .../pytz/zoneinfo/Europe/Istanbul | Bin 0 -> 1947 bytes .../site-packages/pytz/zoneinfo/Europe/Jersey | Bin 0 -> 3648 bytes .../pytz/zoneinfo/Europe/Kaliningrad | Bin 0 -> 1493 bytes .../site-packages/pytz/zoneinfo/Europe/Kiev | Bin 0 -> 2088 bytes .../site-packages/pytz/zoneinfo/Europe/Kirov | Bin 0 -> 1153 bytes .../site-packages/pytz/zoneinfo/Europe/Lisbon | Bin 0 -> 3469 bytes .../pytz/zoneinfo/Europe/Ljubljana | Bin 0 -> 1920 bytes .../site-packages/pytz/zoneinfo/Europe/London | Bin 0 -> 3648 bytes .../pytz/zoneinfo/Europe/Luxembourg | Bin 0 -> 2946 bytes .../site-packages/pytz/zoneinfo/Europe/Madrid | Bin 0 -> 2614 bytes .../site-packages/pytz/zoneinfo/Europe/Malta | Bin 0 -> 2620 bytes .../pytz/zoneinfo/Europe/Mariehamn | Bin 0 -> 1900 bytes .../site-packages/pytz/zoneinfo/Europe/Minsk | Bin 0 -> 1321 bytes .../site-packages/pytz/zoneinfo/Europe/Monaco | Bin 0 -> 2944 bytes .../site-packages/pytz/zoneinfo/Europe/Moscow | Bin 0 -> 1535 bytes .../pytz/zoneinfo/Europe/Nicosia | Bin 0 -> 2002 bytes .../site-packages/pytz/zoneinfo/Europe/Oslo | Bin 0 -> 2228 bytes .../site-packages/pytz/zoneinfo/Europe/Paris | Bin 0 -> 2962 bytes .../pytz/zoneinfo/Europe/Podgorica | Bin 0 -> 1920 bytes .../site-packages/pytz/zoneinfo/Europe/Prague | Bin 0 -> 2301 bytes .../site-packages/pytz/zoneinfo/Europe/Riga | Bin 0 -> 2198 bytes .../site-packages/pytz/zoneinfo/Europe/Rome | Bin 0 -> 2641 bytes .../site-packages/pytz/zoneinfo/Europe/Samara | Bin 0 -> 1215 bytes .../pytz/zoneinfo/Europe/San_Marino | Bin 0 -> 2641 bytes .../pytz/zoneinfo/Europe/Sarajevo | Bin 0 -> 1920 bytes .../pytz/zoneinfo/Europe/Saratov | Bin 0 -> 1183 bytes .../pytz/zoneinfo/Europe/Simferopol | Bin 0 -> 1453 bytes .../site-packages/pytz/zoneinfo/Europe/Skopje | Bin 0 -> 1920 bytes .../site-packages/pytz/zoneinfo/Europe/Sofia | Bin 0 -> 2077 bytes .../pytz/zoneinfo/Europe/Stockholm | Bin 0 -> 1909 bytes .../pytz/zoneinfo/Europe/Tallinn | Bin 0 -> 2148 bytes .../site-packages/pytz/zoneinfo/Europe/Tirane | Bin 0 -> 2084 bytes .../pytz/zoneinfo/Europe/Tiraspol | Bin 0 -> 2390 bytes .../pytz/zoneinfo/Europe/Ulyanovsk | Bin 0 -> 1267 bytes .../pytz/zoneinfo/Europe/Uzhgorod | Bin 0 -> 2050 bytes .../site-packages/pytz/zoneinfo/Europe/Vaduz | Bin 0 -> 1909 bytes .../pytz/zoneinfo/Europe/Vatican | Bin 0 -> 2641 bytes .../site-packages/pytz/zoneinfo/Europe/Vienna | Bin 0 -> 2200 bytes .../pytz/zoneinfo/Europe/Vilnius | Bin 0 -> 2162 bytes .../pytz/zoneinfo/Europe/Volgograd | Bin 0 -> 1165 bytes .../site-packages/pytz/zoneinfo/Europe/Warsaw | Bin 0 -> 2654 bytes .../site-packages/pytz/zoneinfo/Europe/Zagreb | Bin 0 -> 1920 bytes .../pytz/zoneinfo/Europe/Zaporozhye | Bin 0 -> 2106 bytes .../site-packages/pytz/zoneinfo/Europe/Zurich | Bin 0 -> 1909 bytes .../site-packages/pytz/zoneinfo/Factory | Bin 0 -> 116 bytes .../python3.11/site-packages/pytz/zoneinfo/GB | Bin 0 -> 3648 bytes .../site-packages/pytz/zoneinfo/GB-Eire | Bin 0 -> 3648 bytes .../site-packages/pytz/zoneinfo/GMT | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/GMT+0 | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/GMT-0 | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/GMT0 | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/Greenwich | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/HST | Bin 0 -> 115 bytes .../site-packages/pytz/zoneinfo/Hongkong | Bin 0 -> 1203 bytes .../site-packages/pytz/zoneinfo/Iceland | Bin 0 -> 1162 bytes .../pytz/zoneinfo/Indian/Antananarivo | Bin 0 -> 251 bytes .../site-packages/pytz/zoneinfo/Indian/Chagos | Bin 0 -> 199 bytes .../pytz/zoneinfo/Indian/Christmas | Bin 0 -> 165 bytes .../site-packages/pytz/zoneinfo/Indian/Cocos | Bin 0 -> 174 bytes .../site-packages/pytz/zoneinfo/Indian/Comoro | Bin 0 -> 251 bytes .../pytz/zoneinfo/Indian/Kerguelen | Bin 0 -> 165 bytes .../site-packages/pytz/zoneinfo/Indian/Mahe | Bin 0 -> 165 bytes .../pytz/zoneinfo/Indian/Maldives | Bin 0 -> 199 bytes .../pytz/zoneinfo/Indian/Mauritius | Bin 0 -> 241 bytes .../pytz/zoneinfo/Indian/Mayotte | Bin 0 -> 251 bytes .../pytz/zoneinfo/Indian/Reunion | Bin 0 -> 165 bytes .../site-packages/pytz/zoneinfo/Iran | Bin 0 -> 2582 bytes .../site-packages/pytz/zoneinfo/Israel | Bin 0 -> 2288 bytes .../site-packages/pytz/zoneinfo/Jamaica | Bin 0 -> 482 bytes .../site-packages/pytz/zoneinfo/Japan | Bin 0 -> 309 bytes .../site-packages/pytz/zoneinfo/Kwajalein | Bin 0 -> 316 bytes .../site-packages/pytz/zoneinfo/Libya | Bin 0 -> 625 bytes .../site-packages/pytz/zoneinfo/MET | Bin 0 -> 2094 bytes .../site-packages/pytz/zoneinfo/MST | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/MST7MDT | Bin 0 -> 2310 bytes .../pytz/zoneinfo/Mexico/BajaNorte | Bin 0 -> 2342 bytes .../pytz/zoneinfo/Mexico/BajaSur | Bin 0 -> 1526 bytes .../pytz/zoneinfo/Mexico/General | Bin 0 -> 1584 bytes .../python3.11/site-packages/pytz/zoneinfo/NZ | Bin 0 -> 2437 bytes .../site-packages/pytz/zoneinfo/NZ-CHAT | Bin 0 -> 2068 bytes .../site-packages/pytz/zoneinfo/Navajo | Bin 0 -> 2444 bytes .../site-packages/pytz/zoneinfo/PRC | Bin 0 -> 561 bytes .../site-packages/pytz/zoneinfo/PST8PDT | Bin 0 -> 2310 bytes .../site-packages/pytz/zoneinfo/Pacific/Apia | Bin 0 -> 1097 bytes .../pytz/zoneinfo/Pacific/Auckland | Bin 0 -> 2437 bytes .../pytz/zoneinfo/Pacific/Bougainville | Bin 0 -> 268 bytes .../pytz/zoneinfo/Pacific/Chatham | Bin 0 -> 2068 bytes .../site-packages/pytz/zoneinfo/Pacific/Chuuk | Bin 0 -> 269 bytes .../pytz/zoneinfo/Pacific/Easter | Bin 0 -> 2233 bytes .../site-packages/pytz/zoneinfo/Pacific/Efate | Bin 0 -> 466 bytes .../pytz/zoneinfo/Pacific/Enderbury | Bin 0 -> 234 bytes .../pytz/zoneinfo/Pacific/Fakaofo | Bin 0 -> 200 bytes .../site-packages/pytz/zoneinfo/Pacific/Fiji | Bin 0 -> 1077 bytes .../pytz/zoneinfo/Pacific/Funafuti | Bin 0 -> 166 bytes .../pytz/zoneinfo/Pacific/Galapagos | Bin 0 -> 238 bytes .../pytz/zoneinfo/Pacific/Gambier | Bin 0 -> 164 bytes .../pytz/zoneinfo/Pacific/Guadalcanal | Bin 0 -> 166 bytes .../site-packages/pytz/zoneinfo/Pacific/Guam | Bin 0 -> 494 bytes .../pytz/zoneinfo/Pacific/Honolulu | Bin 0 -> 329 bytes .../pytz/zoneinfo/Pacific/Johnston | Bin 0 -> 329 bytes .../pytz/zoneinfo/Pacific/Kiritimati | Bin 0 -> 238 bytes .../pytz/zoneinfo/Pacific/Kosrae | Bin 0 -> 351 bytes .../pytz/zoneinfo/Pacific/Kwajalein | Bin 0 -> 316 bytes .../pytz/zoneinfo/Pacific/Majuro | Bin 0 -> 310 bytes .../pytz/zoneinfo/Pacific/Marquesas | Bin 0 -> 173 bytes .../pytz/zoneinfo/Pacific/Midway | Bin 0 -> 175 bytes .../site-packages/pytz/zoneinfo/Pacific/Nauru | Bin 0 -> 252 bytes .../site-packages/pytz/zoneinfo/Pacific/Niue | Bin 0 -> 241 bytes .../pytz/zoneinfo/Pacific/Norfolk | Bin 0 -> 880 bytes .../pytz/zoneinfo/Pacific/Noumea | Bin 0 -> 304 bytes .../pytz/zoneinfo/Pacific/Pago_Pago | Bin 0 -> 175 bytes .../site-packages/pytz/zoneinfo/Pacific/Palau | Bin 0 -> 180 bytes .../pytz/zoneinfo/Pacific/Pitcairn | Bin 0 -> 202 bytes .../pytz/zoneinfo/Pacific/Pohnpei | Bin 0 -> 303 bytes .../pytz/zoneinfo/Pacific/Ponape | Bin 0 -> 303 bytes .../pytz/zoneinfo/Pacific/Port_Moresby | Bin 0 -> 186 bytes .../pytz/zoneinfo/Pacific/Rarotonga | Bin 0 -> 577 bytes .../pytz/zoneinfo/Pacific/Saipan | Bin 0 -> 494 bytes .../site-packages/pytz/zoneinfo/Pacific/Samoa | Bin 0 -> 175 bytes .../pytz/zoneinfo/Pacific/Tahiti | Bin 0 -> 165 bytes .../pytz/zoneinfo/Pacific/Tarawa | Bin 0 -> 166 bytes .../pytz/zoneinfo/Pacific/Tongatapu | Bin 0 -> 372 bytes .../site-packages/pytz/zoneinfo/Pacific/Truk | Bin 0 -> 269 bytes .../site-packages/pytz/zoneinfo/Pacific/Wake | Bin 0 -> 166 bytes .../pytz/zoneinfo/Pacific/Wallis | Bin 0 -> 166 bytes .../site-packages/pytz/zoneinfo/Pacific/Yap | Bin 0 -> 269 bytes .../site-packages/pytz/zoneinfo/Poland | Bin 0 -> 2654 bytes .../site-packages/pytz/zoneinfo/Portugal | Bin 0 -> 3469 bytes .../site-packages/pytz/zoneinfo/ROC | Bin 0 -> 761 bytes .../site-packages/pytz/zoneinfo/ROK | Bin 0 -> 617 bytes .../site-packages/pytz/zoneinfo/Singapore | Bin 0 -> 383 bytes .../site-packages/pytz/zoneinfo/Turkey | Bin 0 -> 1947 bytes .../site-packages/pytz/zoneinfo/UCT | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/US/Alaska | Bin 0 -> 2371 bytes .../site-packages/pytz/zoneinfo/US/Aleutian | Bin 0 -> 2356 bytes .../site-packages/pytz/zoneinfo/US/Arizona | Bin 0 -> 328 bytes .../site-packages/pytz/zoneinfo/US/Central | Bin 0 -> 3576 bytes .../pytz/zoneinfo/US/East-Indiana | Bin 0 -> 1666 bytes .../site-packages/pytz/zoneinfo/US/Eastern | Bin 0 -> 3536 bytes .../site-packages/pytz/zoneinfo/US/Hawaii | Bin 0 -> 329 bytes .../pytz/zoneinfo/US/Indiana-Starke | Bin 0 -> 2428 bytes .../site-packages/pytz/zoneinfo/US/Michigan | Bin 0 -> 2230 bytes .../site-packages/pytz/zoneinfo/US/Mountain | Bin 0 -> 2444 bytes .../site-packages/pytz/zoneinfo/US/Pacific | Bin 0 -> 2836 bytes .../site-packages/pytz/zoneinfo/US/Samoa | Bin 0 -> 175 bytes .../site-packages/pytz/zoneinfo/UTC | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/Universal | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/W-SU | Bin 0 -> 1535 bytes .../site-packages/pytz/zoneinfo/WET | Bin 0 -> 1905 bytes .../site-packages/pytz/zoneinfo/Zulu | Bin 0 -> 114 bytes .../site-packages/pytz/zoneinfo/iso3166.tab | 274 + .../site-packages/pytz/zoneinfo/leapseconds | 78 + .../site-packages/pytz/zoneinfo/posixrules | Bin 0 -> 3536 bytes .../site-packages/pytz/zoneinfo/tzdata.zi | 4410 +++++++++ .../site-packages/pytz/zoneinfo/zone.tab | 452 + .../site-packages/pytz/zoneinfo/zone1970.tab | 384 + .../rich-13.9.4.dist-info/INSTALLER | 1 + .../rich-13.9.4.dist-info/LICENSE | 19 + .../rich-13.9.4.dist-info/METADATA | 473 + .../rich-13.9.4.dist-info/RECORD | 162 + .../site-packages/rich-13.9.4.dist-info/WHEEL | 4 + .../python3.11/site-packages/rich/__init__.py | 177 + .../python3.11/site-packages/rich/__main__.py | 273 + .../site-packages/rich/_cell_widths.py | 454 + .../site-packages/rich/_emoji_codes.py | 3610 +++++++ .../site-packages/rich/_emoji_replace.py | 32 + .../site-packages/rich/_export_format.py | 76 + .../site-packages/rich/_extension.py | 10 + .../python3.11/site-packages/rich/_fileno.py | 24 + .../python3.11/site-packages/rich/_inspect.py | 268 + .../site-packages/rich/_log_render.py | 94 + .../python3.11/site-packages/rich/_loop.py | 43 + .../site-packages/rich/_null_file.py | 69 + .../site-packages/rich/_palettes.py | 309 + .../python3.11/site-packages/rich/_pick.py | 17 + .../python3.11/site-packages/rich/_ratio.py | 159 + .../site-packages/rich/_spinners.py | 482 + .../python3.11/site-packages/rich/_stack.py | 16 + .../python3.11/site-packages/rich/_timer.py | 19 + .../site-packages/rich/_win32_console.py | 661 ++ .../python3.11/site-packages/rich/_windows.py | 71 + .../site-packages/rich/_windows_renderer.py | 56 + .../python3.11/site-packages/rich/_wrap.py | 93 + venv/lib/python3.11/site-packages/rich/abc.py | 33 + .../python3.11/site-packages/rich/align.py | 312 + .../lib/python3.11/site-packages/rich/ansi.py | 241 + venv/lib/python3.11/site-packages/rich/bar.py | 93 + venv/lib/python3.11/site-packages/rich/box.py | 480 + .../python3.11/site-packages/rich/cells.py | 174 + .../python3.11/site-packages/rich/color.py | 621 ++ .../site-packages/rich/color_triplet.py | 38 + .../python3.11/site-packages/rich/columns.py | 187 + .../python3.11/site-packages/rich/console.py | 2661 +++++ .../site-packages/rich/constrain.py | 37 + .../site-packages/rich/containers.py | 167 + .../python3.11/site-packages/rich/control.py | 225 + .../site-packages/rich/default_styles.py | 191 + .../python3.11/site-packages/rich/diagnose.py | 37 + .../python3.11/site-packages/rich/emoji.py | 96 + .../python3.11/site-packages/rich/errors.py | 34 + .../site-packages/rich/file_proxy.py | 57 + .../python3.11/site-packages/rich/filesize.py | 88 + .../site-packages/rich/highlighter.py | 232 + .../lib/python3.11/site-packages/rich/json.py | 139 + .../python3.11/site-packages/rich/jupyter.py | 101 + .../python3.11/site-packages/rich/layout.py | 442 + .../lib/python3.11/site-packages/rich/live.py | 375 + .../site-packages/rich/live_render.py | 112 + .../python3.11/site-packages/rich/logging.py | 297 + .../python3.11/site-packages/rich/markdown.py | 784 ++ .../python3.11/site-packages/rich/markup.py | 251 + .../python3.11/site-packages/rich/measure.py | 151 + .../python3.11/site-packages/rich/padding.py | 141 + .../python3.11/site-packages/rich/pager.py | 34 + .../python3.11/site-packages/rich/palette.py | 100 + .../python3.11/site-packages/rich/panel.py | 318 + .../python3.11/site-packages/rich/pretty.py | 1016 ++ .../python3.11/site-packages/rich/progress.py | 1715 ++++ .../site-packages/rich/progress_bar.py | 223 + .../python3.11/site-packages/rich/prompt.py | 400 + .../python3.11/site-packages/rich/protocol.py | 42 + .../python3.11/site-packages/rich/py.typed | 0 .../python3.11/site-packages/rich/region.py | 10 + .../lib/python3.11/site-packages/rich/repr.py | 149 + .../lib/python3.11/site-packages/rich/rule.py | 130 + .../python3.11/site-packages/rich/scope.py | 86 + .../python3.11/site-packages/rich/screen.py | 54 + .../python3.11/site-packages/rich/segment.py | 752 ++ .../python3.11/site-packages/rich/spinner.py | 138 + .../python3.11/site-packages/rich/status.py | 131 + .../python3.11/site-packages/rich/style.py | 796 ++ .../python3.11/site-packages/rich/styled.py | 42 + .../python3.11/site-packages/rich/syntax.py | 966 ++ .../python3.11/site-packages/rich/table.py | 1007 ++ .../site-packages/rich/terminal_theme.py | 153 + .../lib/python3.11/site-packages/rich/text.py | 1361 +++ .../python3.11/site-packages/rich/theme.py | 115 + .../python3.11/site-packages/rich/themes.py | 5 + .../site-packages/rich/traceback.py | 797 ++ .../lib/python3.11/site-packages/rich/tree.py | 257 + .../six-1.16.0.dist-info/INSTALLER | 1 + .../six-1.16.0.dist-info/LICENSE | 18 + .../six-1.16.0.dist-info/METADATA | 49 + .../site-packages/six-1.16.0.dist-info/RECORD | 8 + .../site-packages/six-1.16.0.dist-info/WHEEL | 6 + .../six-1.16.0.dist-info/top_level.txt | 1 + venv/lib/python3.11/site-packages/six.py | 998 ++ .../sniffio-1.3.1.dist-info/INSTALLER | 1 + .../sniffio-1.3.1.dist-info/LICENSE | 3 + .../sniffio-1.3.1.dist-info/LICENSE.APACHE2 | 202 + .../sniffio-1.3.1.dist-info/LICENSE.MIT | 20 + .../sniffio-1.3.1.dist-info/METADATA | 104 + .../sniffio-1.3.1.dist-info/RECORD | 19 + .../sniffio-1.3.1.dist-info/WHEEL | 5 + .../sniffio-1.3.1.dist-info/top_level.txt | 1 + .../site-packages/sniffio/__init__.py | 17 + .../python3.11/site-packages/sniffio/_impl.py | 95 + .../site-packages/sniffio/_tests/__init__.py | 0 .../sniffio/_tests/test_sniffio.py | 84 + .../site-packages/sniffio/_version.py | 3 + .../python3.11/site-packages/sniffio/py.typed | 0 .../soupsieve-2.6.dist-info/INSTALLER | 1 + .../soupsieve-2.6.dist-info/METADATA | 114 + .../soupsieve-2.6.dist-info/RECORD | 20 + .../soupsieve-2.6.dist-info/WHEEL | 4 + .../licenses/LICENSE.md | 21 + .../site-packages/soupsieve/__init__.py | 168 + .../site-packages/soupsieve/__meta__.py | 197 + .../site-packages/soupsieve/css_match.py | 1582 +++ .../site-packages/soupsieve/css_parser.py | 1289 +++ .../site-packages/soupsieve/css_types.py | 407 + .../site-packages/soupsieve/pretty.py | 139 + .../site-packages/soupsieve/py.typed | 0 .../site-packages/soupsieve/util.py | 117 + .../site-packages/unidecode/__init__.py | 138 + .../site-packages/unidecode/__main__.py | 3 + .../site-packages/unidecode/py.typed | 0 .../site-packages/unidecode/util.py | 51 + .../site-packages/unidecode/x000.py | 165 + .../site-packages/unidecode/x001.py | 258 + .../site-packages/unidecode/x002.py | 257 + .../site-packages/unidecode/x003.py | 257 + .../site-packages/unidecode/x004.py | 257 + .../site-packages/unidecode/x005.py | 257 + .../site-packages/unidecode/x006.py | 257 + .../site-packages/unidecode/x007.py | 257 + .../site-packages/unidecode/x009.py | 257 + .../site-packages/unidecode/x00a.py | 257 + .../site-packages/unidecode/x00b.py | 257 + .../site-packages/unidecode/x00c.py | 257 + .../site-packages/unidecode/x00d.py | 257 + .../site-packages/unidecode/x00e.py | 257 + .../site-packages/unidecode/x00f.py | 257 + .../site-packages/unidecode/x010.py | 257 + .../site-packages/unidecode/x011.py | 257 + .../site-packages/unidecode/x012.py | 258 + .../site-packages/unidecode/x013.py | 257 + .../site-packages/unidecode/x014.py | 258 + .../site-packages/unidecode/x015.py | 258 + .../site-packages/unidecode/x016.py | 257 + .../site-packages/unidecode/x017.py | 257 + .../site-packages/unidecode/x018.py | 257 + .../site-packages/unidecode/x01d.py | 257 + .../site-packages/unidecode/x01e.py | 257 + .../site-packages/unidecode/x01f.py | 257 + .../site-packages/unidecode/x020.py | 261 + .../site-packages/unidecode/x021.py | 257 + .../site-packages/unidecode/x022.py | 257 + .../site-packages/unidecode/x023.py | 257 + .../site-packages/unidecode/x024.py | 258 + .../site-packages/unidecode/x025.py | 257 + .../site-packages/unidecode/x026.py | 257 + .../site-packages/unidecode/x027.py | 257 + .../site-packages/unidecode/x028.py | 258 + .../site-packages/unidecode/x029.py | 257 + .../site-packages/unidecode/x02a.py | 257 + .../site-packages/unidecode/x02c.py | 257 + .../site-packages/unidecode/x02e.py | 257 + .../site-packages/unidecode/x02f.py | 257 + .../site-packages/unidecode/x030.py | 257 + .../site-packages/unidecode/x031.py | 257 + .../site-packages/unidecode/x032.py | 257 + .../site-packages/unidecode/x033.py | 258 + .../site-packages/unidecode/x04d.py | 257 + .../site-packages/unidecode/x04e.py | 258 + .../site-packages/unidecode/x04f.py | 258 + .../site-packages/unidecode/x050.py | 258 + .../site-packages/unidecode/x051.py | 258 + .../site-packages/unidecode/x052.py | 258 + .../site-packages/unidecode/x053.py | 258 + .../site-packages/unidecode/x054.py | 258 + .../site-packages/unidecode/x055.py | 258 + .../site-packages/unidecode/x056.py | 258 + .../site-packages/unidecode/x057.py | 258 + .../site-packages/unidecode/x058.py | 258 + .../site-packages/unidecode/x059.py | 258 + .../site-packages/unidecode/x05a.py | 258 + .../site-packages/unidecode/x05b.py | 258 + .../site-packages/unidecode/x05c.py | 258 + .../site-packages/unidecode/x05d.py | 258 + .../site-packages/unidecode/x05e.py | 258 + .../site-packages/unidecode/x05f.py | 258 + .../site-packages/unidecode/x060.py | 258 + .../site-packages/unidecode/x061.py | 258 + .../site-packages/unidecode/x062.py | 258 + .../site-packages/unidecode/x063.py | 258 + .../site-packages/unidecode/x064.py | 258 + .../site-packages/unidecode/x065.py | 258 + .../site-packages/unidecode/x066.py | 258 + .../site-packages/unidecode/x067.py | 258 + .../site-packages/unidecode/x068.py | 258 + .../site-packages/unidecode/x069.py | 258 + .../site-packages/unidecode/x06a.py | 258 + .../site-packages/unidecode/x06b.py | 258 + .../site-packages/unidecode/x06c.py | 258 + .../site-packages/unidecode/x06d.py | 258 + .../site-packages/unidecode/x06e.py | 258 + .../site-packages/unidecode/x06f.py | 258 + .../site-packages/unidecode/x070.py | 258 + .../site-packages/unidecode/x071.py | 258 + .../site-packages/unidecode/x072.py | 258 + .../site-packages/unidecode/x073.py | 258 + .../site-packages/unidecode/x074.py | 258 + .../site-packages/unidecode/x075.py | 258 + .../site-packages/unidecode/x076.py | 258 + .../site-packages/unidecode/x077.py | 258 + .../site-packages/unidecode/x078.py | 258 + .../site-packages/unidecode/x079.py | 258 + .../site-packages/unidecode/x07a.py | 258 + .../site-packages/unidecode/x07b.py | 258 + .../site-packages/unidecode/x07c.py | 258 + .../site-packages/unidecode/x07d.py | 258 + .../site-packages/unidecode/x07e.py | 258 + .../site-packages/unidecode/x07f.py | 258 + .../site-packages/unidecode/x080.py | 258 + .../site-packages/unidecode/x081.py | 258 + .../site-packages/unidecode/x082.py | 258 + .../site-packages/unidecode/x083.py | 258 + .../site-packages/unidecode/x084.py | 258 + .../site-packages/unidecode/x085.py | 258 + .../site-packages/unidecode/x086.py | 258 + .../site-packages/unidecode/x087.py | 258 + .../site-packages/unidecode/x088.py | 258 + .../site-packages/unidecode/x089.py | 258 + .../site-packages/unidecode/x08a.py | 258 + .../site-packages/unidecode/x08b.py | 258 + .../site-packages/unidecode/x08c.py | 258 + .../site-packages/unidecode/x08d.py | 258 + .../site-packages/unidecode/x08e.py | 258 + .../site-packages/unidecode/x08f.py | 258 + .../site-packages/unidecode/x090.py | 258 + .../site-packages/unidecode/x091.py | 258 + .../site-packages/unidecode/x092.py | 258 + .../site-packages/unidecode/x093.py | 258 + .../site-packages/unidecode/x094.py | 258 + .../site-packages/unidecode/x095.py | 258 + .../site-packages/unidecode/x096.py | 258 + .../site-packages/unidecode/x097.py | 258 + .../site-packages/unidecode/x098.py | 258 + .../site-packages/unidecode/x099.py | 258 + .../site-packages/unidecode/x09a.py | 258 + .../site-packages/unidecode/x09b.py | 258 + .../site-packages/unidecode/x09c.py | 258 + .../site-packages/unidecode/x09d.py | 258 + .../site-packages/unidecode/x09e.py | 258 + .../site-packages/unidecode/x09f.py | 257 + .../site-packages/unidecode/x0a0.py | 258 + .../site-packages/unidecode/x0a1.py | 258 + .../site-packages/unidecode/x0a2.py | 258 + .../site-packages/unidecode/x0a3.py | 258 + .../site-packages/unidecode/x0a4.py | 257 + .../site-packages/unidecode/x0ac.py | 258 + .../site-packages/unidecode/x0ad.py | 258 + .../site-packages/unidecode/x0ae.py | 258 + .../site-packages/unidecode/x0af.py | 258 + .../site-packages/unidecode/x0b0.py | 258 + .../site-packages/unidecode/x0b1.py | 258 + .../site-packages/unidecode/x0b2.py | 258 + .../site-packages/unidecode/x0b3.py | 258 + .../site-packages/unidecode/x0b4.py | 258 + .../site-packages/unidecode/x0b5.py | 258 + .../site-packages/unidecode/x0b6.py | 258 + .../site-packages/unidecode/x0b7.py | 258 + .../site-packages/unidecode/x0b8.py | 258 + .../site-packages/unidecode/x0b9.py | 258 + .../site-packages/unidecode/x0ba.py | 258 + .../site-packages/unidecode/x0bb.py | 258 + .../site-packages/unidecode/x0bc.py | 258 + .../site-packages/unidecode/x0bd.py | 258 + .../site-packages/unidecode/x0be.py | 258 + .../site-packages/unidecode/x0bf.py | 258 + .../site-packages/unidecode/x0c0.py | 258 + .../site-packages/unidecode/x0c1.py | 258 + .../site-packages/unidecode/x0c2.py | 258 + .../site-packages/unidecode/x0c3.py | 258 + .../site-packages/unidecode/x0c4.py | 258 + .../site-packages/unidecode/x0c5.py | 258 + .../site-packages/unidecode/x0c6.py | 258 + .../site-packages/unidecode/x0c7.py | 258 + .../site-packages/unidecode/x0c8.py | 258 + .../site-packages/unidecode/x0c9.py | 258 + .../site-packages/unidecode/x0ca.py | 258 + .../site-packages/unidecode/x0cb.py | 258 + .../site-packages/unidecode/x0cc.py | 258 + .../site-packages/unidecode/x0cd.py | 258 + .../site-packages/unidecode/x0ce.py | 258 + .../site-packages/unidecode/x0cf.py | 258 + .../site-packages/unidecode/x0d0.py | 258 + .../site-packages/unidecode/x0d1.py | 258 + .../site-packages/unidecode/x0d2.py | 258 + .../site-packages/unidecode/x0d3.py | 258 + .../site-packages/unidecode/x0d4.py | 258 + .../site-packages/unidecode/x0d5.py | 258 + .../site-packages/unidecode/x0d6.py | 258 + .../site-packages/unidecode/x0d7.py | 257 + .../site-packages/unidecode/x0f9.py | 258 + .../site-packages/unidecode/x0fa.py | 257 + .../site-packages/unidecode/x0fb.py | 258 + .../site-packages/unidecode/x0fc.py | 258 + .../site-packages/unidecode/x0fd.py | 257 + .../site-packages/unidecode/x0fe.py | 258 + .../site-packages/unidecode/x0ff.py | 258 + .../site-packages/unidecode/x1d4.py | 258 + .../site-packages/unidecode/x1d5.py | 258 + .../site-packages/unidecode/x1d6.py | 258 + .../site-packages/unidecode/x1d7.py | 258 + .../site-packages/unidecode/x1f1.py | 258 + .../site-packages/unidecode/x1f6.py | 258 + .../watchfiles-0.24.0.dist-info/INSTALLER | 1 + .../watchfiles-0.24.0.dist-info/METADATA | 152 + .../watchfiles-0.24.0.dist-info/RECORD | 24 + .../watchfiles-0.24.0.dist-info/WHEEL | 4 + .../entry_points.txt | 2 + .../licenses/LICENSE | 21 + .../site-packages/watchfiles/__init__.py | 17 + .../site-packages/watchfiles/__main__.py | 4 + ...ust_notify.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 1012160 bytes .../site-packages/watchfiles/_rust_notify.pyi | 111 + .../site-packages/watchfiles/cli.py | 224 + .../site-packages/watchfiles/filters.py | 149 + .../site-packages/watchfiles/main.py | 353 + .../site-packages/watchfiles/py.typed | 1 + .../site-packages/watchfiles/run.py | 438 + .../site-packages/watchfiles/version.py | 5 + .../DESCRIPTION.rst | 27 + .../webencodings-0.5.1.dist-info/INSTALLER | 1 + .../webencodings-0.5.1.dist-info/METADATA | 52 + .../webencodings-0.5.1.dist-info/RECORD | 17 + .../webencodings-0.5.1.dist-info/WHEEL | 6 + .../metadata.json | 1 + .../top_level.txt | 1 + .../site-packages/webencodings/__init__.py | 342 + .../site-packages/webencodings/labels.py | 231 + .../site-packages/webencodings/mklabels.py | 59 + .../site-packages/webencodings/tests.py | 153 + .../webencodings/x_user_defined.py | 325 + 2372 files changed, 401266 insertions(+), 3 deletions(-) create mode 100755 venv/bin/icalendar create mode 100755 venv/bin/markdown-it create mode 100755 venv/bin/markdown_py create mode 100755 venv/bin/pelican create mode 100755 venv/bin/pelican-import create mode 100755 venv/bin/pelican-plugins create mode 100755 venv/bin/pelican-quickstart create mode 100755 venv/bin/pelican-themes create mode 100755 venv/bin/pygmentize create mode 100755 venv/bin/rst2html.py create mode 100755 venv/bin/rst2html4.py create mode 100755 venv/bin/rst2html5.py create mode 100755 venv/bin/rst2latex.py create mode 100755 venv/bin/rst2man.py create mode 100755 venv/bin/rst2odt.py create mode 100755 venv/bin/rst2odt_prepstyles.py create mode 100755 venv/bin/rst2pseudoxml.py create mode 100755 venv/bin/rst2s5.py create mode 100755 venv/bin/rst2xetex.py create mode 100755 venv/bin/rst2xml.py create mode 100755 venv/bin/rstpep2html.py create mode 100755 venv/bin/unidecode create mode 100755 venv/bin/watchfiles create mode 100644 venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/LICENSE.rst create mode 100644 venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/entry_points.txt create mode 100644 venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/LICENSE.md create mode 100644 venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/REQUESTED create mode 100644 venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/entry_points.txt create mode 100644 venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/PKG-INFO create mode 100644 venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/SOURCES.txt create mode 100644 venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/dependency_links.txt create mode 100644 venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/installed-files.txt create mode 100644 venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/PIL/BdfFontFile.py create mode 100644 venv/lib/python3.11/site-packages/PIL/BlpImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/BmpImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/BufrStubImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ContainerIO.py create mode 100644 venv/lib/python3.11/site-packages/PIL/CurImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/DcxImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/DdsImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/EpsImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ExifTags.py create mode 100644 venv/lib/python3.11/site-packages/PIL/FitsStubImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/FliImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/FontFile.py create mode 100644 venv/lib/python3.11/site-packages/PIL/FpxImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/FtexImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/GbrImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/GdImageFile.py create mode 100644 venv/lib/python3.11/site-packages/PIL/GifImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/GimpGradientFile.py create mode 100644 venv/lib/python3.11/site-packages/PIL/GimpPaletteFile.py create mode 100644 venv/lib/python3.11/site-packages/PIL/GribStubImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/Hdf5StubImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/IcnsImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/IcoImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/Image.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageChops.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageCms.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageColor.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageDraw.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageDraw2.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageEnhance.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageFile.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageFilter.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageFont.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageGrab.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageMath.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageMode.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageMorph.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageOps.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImagePalette.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImagePath.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageQt.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageSequence.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageShow.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageStat.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageTk.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageTransform.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImageWin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/ImtImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/IptcImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/Jpeg2KImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/JpegImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/JpegPresets.py create mode 100644 venv/lib/python3.11/site-packages/PIL/McIdasImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/MicImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/MpegImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/MpoImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/MspImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/PSDraw.py create mode 100644 venv/lib/python3.11/site-packages/PIL/PaletteFile.py create mode 100644 venv/lib/python3.11/site-packages/PIL/PalmImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/PcdImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/PcfFontFile.py create mode 100644 venv/lib/python3.11/site-packages/PIL/PcxImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/PdfImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/PdfParser.py create mode 100644 venv/lib/python3.11/site-packages/PIL/PixarImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/PngImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/PpmImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/PsdImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/PyAccess.py create mode 100644 venv/lib/python3.11/site-packages/PIL/SgiImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/SpiderImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/SunImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/TarIO.py create mode 100644 venv/lib/python3.11/site-packages/PIL/TgaImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/TiffImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/TiffTags.py create mode 100644 venv/lib/python3.11/site-packages/PIL/WalImageFile.py create mode 100644 venv/lib/python3.11/site-packages/PIL/WebPImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/WmfImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/XVThumbImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/XbmImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/XpmImagePlugin.py create mode 100644 venv/lib/python3.11/site-packages/PIL/__init__.py create mode 100644 venv/lib/python3.11/site-packages/PIL/__main__.py create mode 100644 venv/lib/python3.11/site-packages/PIL/_binary.py create mode 100755 venv/lib/python3.11/site-packages/PIL/_imaging.cpython-311-x86_64-linux-gnu.so create mode 100755 venv/lib/python3.11/site-packages/PIL/_imagingft.cpython-311-x86_64-linux-gnu.so create mode 100755 venv/lib/python3.11/site-packages/PIL/_imagingmath.cpython-311-x86_64-linux-gnu.so create mode 100755 venv/lib/python3.11/site-packages/PIL/_imagingmorph.cpython-311-x86_64-linux-gnu.so create mode 100755 venv/lib/python3.11/site-packages/PIL/_imagingtk.cpython-311-x86_64-linux-gnu.so create mode 100644 venv/lib/python3.11/site-packages/PIL/_tkinter_finder.py create mode 100644 venv/lib/python3.11/site-packages/PIL/_util.py create mode 100644 venv/lib/python3.11/site-packages/PIL/_version.py create mode 100755 venv/lib/python3.11/site-packages/PIL/_webp.cpython-311-x86_64-linux-gnu.so create mode 100644 venv/lib/python3.11/site-packages/PIL/features.py create mode 100644 venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/PKG-INFO create mode 100644 venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/SOURCES.txt create mode 100644 venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/dependency_links.txt create mode 100644 venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/installed-files.txt create mode 100644 venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/zip-safe create mode 100644 venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/AUTHORS create mode 100644 venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/LICENSE create mode 100644 venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/entry_points.txt create mode 100644 venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/LICENSE create mode 100644 venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/entry_points.txt create mode 100644 venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/LICENSE create mode 100644 venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/entry_points.txt create mode 100644 venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/anyio/__init__.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_backends/__init__.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_backends/_asyncio.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_backends/_trio.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_core/__init__.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_core/_eventloop.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_core/_exceptions.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_core/_fileio.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_core/_resources.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_core/_signals.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_core/_sockets.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_core/_streams.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_core/_subprocesses.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_core/_synchronization.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_core/_tasks.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_core/_testing.py create mode 100644 venv/lib/python3.11/site-packages/anyio/_core/_typedattr.py create mode 100644 venv/lib/python3.11/site-packages/anyio/abc/__init__.py create mode 100644 venv/lib/python3.11/site-packages/anyio/abc/_eventloop.py create mode 100644 venv/lib/python3.11/site-packages/anyio/abc/_resources.py create mode 100644 venv/lib/python3.11/site-packages/anyio/abc/_sockets.py create mode 100644 venv/lib/python3.11/site-packages/anyio/abc/_streams.py create mode 100644 venv/lib/python3.11/site-packages/anyio/abc/_subprocesses.py create mode 100644 venv/lib/python3.11/site-packages/anyio/abc/_tasks.py create mode 100644 venv/lib/python3.11/site-packages/anyio/abc/_testing.py create mode 100644 venv/lib/python3.11/site-packages/anyio/from_thread.py create mode 100644 venv/lib/python3.11/site-packages/anyio/lowlevel.py create mode 100644 venv/lib/python3.11/site-packages/anyio/py.typed create mode 100644 venv/lib/python3.11/site-packages/anyio/pytest_plugin.py create mode 100644 venv/lib/python3.11/site-packages/anyio/streams/__init__.py create mode 100644 venv/lib/python3.11/site-packages/anyio/streams/buffered.py create mode 100644 venv/lib/python3.11/site-packages/anyio/streams/file.py create mode 100644 venv/lib/python3.11/site-packages/anyio/streams/memory.py create mode 100644 venv/lib/python3.11/site-packages/anyio/streams/stapled.py create mode 100644 venv/lib/python3.11/site-packages/anyio/streams/text.py create mode 100644 venv/lib/python3.11/site-packages/anyio/streams/tls.py create mode 100644 venv/lib/python3.11/site-packages/anyio/to_process.py create mode 100644 venv/lib/python3.11/site-packages/anyio/to_thread.py create mode 100644 venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/AUTHORS create mode 100644 venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/COPYING.txt create mode 100644 venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/LICENSE create mode 100644 venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/REQUESTED create mode 100644 venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/LICENSE.txt create mode 100644 venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/blinker/__init__.py create mode 100644 venv/lib/python3.11/site-packages/blinker/_utilities.py create mode 100644 venv/lib/python3.11/site-packages/blinker/base.py create mode 100644 venv/lib/python3.11/site-packages/blinker/py.typed create mode 100644 venv/lib/python3.11/site-packages/bs4/__init__.py create mode 100644 venv/lib/python3.11/site-packages/bs4/builder/__init__.py create mode 100644 venv/lib/python3.11/site-packages/bs4/builder/_html5lib.py create mode 100644 venv/lib/python3.11/site-packages/bs4/builder/_htmlparser.py create mode 100644 venv/lib/python3.11/site-packages/bs4/builder/_lxml.py create mode 100644 venv/lib/python3.11/site-packages/bs4/dammit.py create mode 100644 venv/lib/python3.11/site-packages/bs4/diagnose.py create mode 100644 venv/lib/python3.11/site-packages/bs4/element.py create mode 100644 venv/lib/python3.11/site-packages/bs4/formatter.py create mode 100644 venv/lib/python3.11/site-packages/bs4/testing.py create mode 100644 venv/lib/python3.11/site-packages/bs4/tests/__init__.py create mode 100644 venv/lib/python3.11/site-packages/bs4/tests/test_builder_registry.py create mode 100644 venv/lib/python3.11/site-packages/bs4/tests/test_docs.py create mode 100644 venv/lib/python3.11/site-packages/bs4/tests/test_html5lib.py create mode 100644 venv/lib/python3.11/site-packages/bs4/tests/test_htmlparser.py create mode 100644 venv/lib/python3.11/site-packages/bs4/tests/test_lxml.py create mode 100644 venv/lib/python3.11/site-packages/bs4/tests/test_soup.py create mode 100644 venv/lib/python3.11/site-packages/bs4/tests/test_tree.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/__init__.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/_common.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/_version.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/easter.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/parser/__init__.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/parser/_parser.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/parser/isoparser.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/relativedelta.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/rrule.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/tz/__init__.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/tz/_common.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/tz/_factories.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/tz/tz.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/tz/win.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/tzwin.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/utils.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/zoneinfo/__init__.py create mode 100644 venv/lib/python3.11/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz create mode 100644 venv/lib/python3.11/site-packages/dateutil/zoneinfo/rebuild.py create mode 100644 venv/lib/python3.11/site-packages/docutils-0.16.dist-info/COPYING.txt create mode 100644 venv/lib/python3.11/site-packages/docutils-0.16.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/docutils-0.16.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/docutils-0.16.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/docutils-0.16.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/docutils-0.16.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/core.py create mode 100644 venv/lib/python3.11/site-packages/docutils/examples.py create mode 100644 venv/lib/python3.11/site-packages/docutils/frontend.py create mode 100644 venv/lib/python3.11/site-packages/docutils/io.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/af.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/ca.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/cs.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/da.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/de.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/en.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/eo.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/es.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/fa.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/fi.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/fr.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/gl.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/he.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/it.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/ja.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/ko.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/lt.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/lv.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/nl.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/pl.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/pt_br.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/ru.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/sk.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/sv.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/zh_cn.py create mode 100644 venv/lib/python3.11/site-packages/docutils/languages/zh_tw.py create mode 100644 venv/lib/python3.11/site-packages/docutils/nodes.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/null.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/admonitions.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/body.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/html.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/images.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/misc.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/parts.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/references.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/tables.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/README.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isoamsa.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isoamsb.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isoamsc.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isoamsn.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isoamso.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isoamsr.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isobox.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isocyr1.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isocyr2.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isodia.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isogrk1.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isogrk2.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isogrk3.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isogrk4-wide.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isogrk4.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isolat1.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isolat2.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isomfrk-wide.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isomfrk.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isomopf-wide.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isomopf.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isomscr-wide.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isomscr.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isonum.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isopub.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isotech.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/mmlalias.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/mmlextra-wide.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/mmlextra.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/s5defs.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/xhtml1-lat1.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/xhtml1-special.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/include/xhtml1-symbol.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/af.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/ca.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/cs.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/da.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/de.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/en.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/eo.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/es.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/fa.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/fi.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/fr.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/gl.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/he.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/it.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/ja.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/ko.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/lt.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/lv.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/nl.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/pl.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/pt_br.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/ru.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/sk.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/sv.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/zh_cn.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/zh_tw.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/roles.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/states.py create mode 100644 venv/lib/python3.11/site-packages/docutils/parsers/rst/tableparser.py create mode 100644 venv/lib/python3.11/site-packages/docutils/readers/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/readers/doctree.py create mode 100644 venv/lib/python3.11/site-packages/docutils/readers/pep.py create mode 100644 venv/lib/python3.11/site-packages/docutils/readers/standalone.py create mode 100644 venv/lib/python3.11/site-packages/docutils/statemachine.py create mode 100644 venv/lib/python3.11/site-packages/docutils/transforms/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/transforms/components.py create mode 100644 venv/lib/python3.11/site-packages/docutils/transforms/frontmatter.py create mode 100644 venv/lib/python3.11/site-packages/docutils/transforms/misc.py create mode 100644 venv/lib/python3.11/site-packages/docutils/transforms/parts.py create mode 100644 venv/lib/python3.11/site-packages/docutils/transforms/peps.py create mode 100644 venv/lib/python3.11/site-packages/docutils/transforms/references.py create mode 100644 venv/lib/python3.11/site-packages/docutils/transforms/universal.py create mode 100644 venv/lib/python3.11/site-packages/docutils/transforms/writer_aux.py create mode 100644 venv/lib/python3.11/site-packages/docutils/utils/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/utils/code_analyzer.py create mode 100644 venv/lib/python3.11/site-packages/docutils/utils/error_reporting.py create mode 100644 venv/lib/python3.11/site-packages/docutils/utils/math/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/utils/math/latex2mathml.py create mode 100644 venv/lib/python3.11/site-packages/docutils/utils/math/math2html.py create mode 100644 venv/lib/python3.11/site-packages/docutils/utils/math/tex2mathml_extern.py create mode 100644 venv/lib/python3.11/site-packages/docutils/utils/math/tex2unichar.py create mode 100644 venv/lib/python3.11/site-packages/docutils/utils/math/unichar2tex.py create mode 100644 venv/lib/python3.11/site-packages/docutils/utils/punctuation_chars.py create mode 100644 venv/lib/python3.11/site-packages/docutils/utils/roman.py create mode 100644 venv/lib/python3.11/site-packages/docutils/utils/smartquotes.py create mode 100644 venv/lib/python3.11/site-packages/docutils/utils/urischemes.py create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/_html_base.py create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/docutils_xml.py create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/html4css1/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/html4css1/html4css1.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/html4css1/template.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/html5_polyglot/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/html5_polyglot/math.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/html5_polyglot/minimal.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/html5_polyglot/plain.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/html5_polyglot/template.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/latex2e/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/latex2e/default.tex create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/latex2e/titlepage.tex create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/latex2e/xelatex.tex create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/manpage.py create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/null.py create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/odf_odt/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/odf_odt/pygmentsformatter.py create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/odf_odt/styles.odt create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/pep_html/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/pep_html/pep.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/pep_html/template.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/pseudoxml.py create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/__init__.py create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/README.txt create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/big-black/__base__ create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/big-black/framing.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/big-black/pretty.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/big-white/framing.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/big-white/pretty.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/blank.gif create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/framing.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/iepngfix.htc create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/opera.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/outline.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/pretty.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/print.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/s5-core.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/slides.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/slides.js create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/medium-black/__base__ create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/medium-black/pretty.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/medium-white/framing.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/medium-white/pretty.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/small-black/__base__ create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/small-black/pretty.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/small-white/framing.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/small-white/pretty.css create mode 100644 venv/lib/python3.11/site-packages/docutils/writers/xetex/__init__.py create mode 100644 venv/lib/python3.11/site-packages/feedgenerator-1.9.2.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/feedgenerator-1.9.2.dist-info/LICENSE create mode 100644 venv/lib/python3.11/site-packages/feedgenerator-1.9.2.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/feedgenerator-1.9.2.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/feedgenerator-1.9.2.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/feedgenerator-1.9.2.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/feedgenerator/__init__.py create mode 100644 venv/lib/python3.11/site-packages/feedgenerator/django/__init__.py create mode 100644 venv/lib/python3.11/site-packages/feedgenerator/django/utils/__init__.py create mode 100644 venv/lib/python3.11/site-packages/feedgenerator/django/utils/datetime_safe.py create mode 100644 venv/lib/python3.11/site-packages/feedgenerator/django/utils/encoding.py create mode 100644 venv/lib/python3.11/site-packages/feedgenerator/django/utils/feedgenerator.py create mode 100644 venv/lib/python3.11/site-packages/feedgenerator/django/utils/functional.py create mode 100644 venv/lib/python3.11/site-packages/feedgenerator/django/utils/six.py create mode 100644 venv/lib/python3.11/site-packages/feedgenerator/django/utils/timezone.py create mode 100644 venv/lib/python3.11/site-packages/feedgenerator/django/utils/xmlutils.py create mode 100644 venv/lib/python3.11/site-packages/html5lib-1.1.dist-info/AUTHORS.rst create mode 100644 venv/lib/python3.11/site-packages/html5lib-1.1.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/html5lib-1.1.dist-info/LICENSE create mode 100644 venv/lib/python3.11/site-packages/html5lib-1.1.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/html5lib-1.1.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/html5lib-1.1.dist-info/REQUESTED create mode 100644 venv/lib/python3.11/site-packages/html5lib-1.1.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/html5lib-1.1.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/html5lib/__init__.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/_ihatexml.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/_inputstream.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/_tokenizer.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/_trie/__init__.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/_trie/_base.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/_trie/py.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/_utils.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/constants.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/filters/__init__.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/filters/alphabeticalattributes.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/filters/base.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/filters/inject_meta_charset.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/filters/lint.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/filters/optionaltags.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/filters/sanitizer.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/filters/whitespace.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/html5parser.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/serializer.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/treeadapters/__init__.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/treeadapters/genshi.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/treeadapters/sax.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/treebuilders/__init__.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/treebuilders/base.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/treebuilders/dom.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/treebuilders/etree.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/treebuilders/etree_lxml.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/treewalkers/__init__.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/treewalkers/base.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/treewalkers/dom.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/treewalkers/etree.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/treewalkers/etree_lxml.py create mode 100644 venv/lib/python3.11/site-packages/html5lib/treewalkers/genshi.py create mode 100644 venv/lib/python3.11/site-packages/icalendar-4.0.7.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/icalendar-4.0.7.dist-info/LICENSE.rst create mode 100644 venv/lib/python3.11/site-packages/icalendar-4.0.7.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/icalendar-4.0.7.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/icalendar-4.0.7.dist-info/REQUESTED create mode 100644 venv/lib/python3.11/site-packages/icalendar-4.0.7.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/icalendar-4.0.7.dist-info/entry_points.txt create mode 100644 venv/lib/python3.11/site-packages/icalendar-4.0.7.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/icalendar/__init__.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/cal.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/caselessdict.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/cli.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/compat.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/parser.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/parser_tools.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/prop.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/__init__.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/america_new_york.ics create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/encoding.ics create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/hypothesis/test_fuzzing.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/issue_112_missing_tzinfo_on_exdate.ics create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/issue_53_parsing_failure.ics create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/multiple.ics create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/pacific_fiji.ics create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/recurrence.ics create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/test_encoding.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/test_fixed_issues.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/test_icalendar.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/test_multiple.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/test_property_params.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/test_recurrence.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/test_time.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/test_timezoned.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/test_unit_cal.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/test_unit_caselessdict.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/test_unit_parser_tools.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/test_unit_prop.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/test_unit_tools.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/time.ics create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/timezone_rdate.ics create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/timezone_same_start.ics create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/timezone_same_start_and_offset.ics create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/timezoned.ics create mode 100644 venv/lib/python3.11/site-packages/icalendar/tests/x_location.ics create mode 100644 venv/lib/python3.11/site-packages/icalendar/timezone_cache.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/tools.py create mode 100644 venv/lib/python3.11/site-packages/icalendar/windows_to_olson.py create mode 100644 venv/lib/python3.11/site-packages/idna-3.10.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/idna-3.10.dist-info/LICENSE.md create mode 100644 venv/lib/python3.11/site-packages/idna-3.10.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/idna-3.10.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/idna-3.10.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/idna/__init__.py create mode 100644 venv/lib/python3.11/site-packages/idna/codec.py create mode 100644 venv/lib/python3.11/site-packages/idna/compat.py create mode 100644 venv/lib/python3.11/site-packages/idna/core.py create mode 100644 venv/lib/python3.11/site-packages/idna/idnadata.py create mode 100644 venv/lib/python3.11/site-packages/idna/intranges.py create mode 100644 venv/lib/python3.11/site-packages/idna/package_data.py create mode 100644 venv/lib/python3.11/site-packages/idna/py.typed create mode 100644 venv/lib/python3.11/site-packages/idna/uts46data.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/__init__.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/_compat.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/_identifier.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/asyncfilters.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/asyncsupport.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/bccache.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/compiler.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/constants.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/debug.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/defaults.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/environment.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/exceptions.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/ext.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/filters.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/idtracking.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/lexer.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/loaders.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/meta.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/nativetypes.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/nodes.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/optimizer.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/parser.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/runtime.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/sandbox.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/tests.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/utils.py create mode 100644 venv/lib/python3.11/site-packages/jinja2/visitor.py create mode 100644 venv/lib/python3.11/site-packages/markdown/__init__.py create mode 100644 venv/lib/python3.11/site-packages/markdown/__main__.py create mode 100644 venv/lib/python3.11/site-packages/markdown/__meta__.py create mode 100644 venv/lib/python3.11/site-packages/markdown/blockparser.py create mode 100644 venv/lib/python3.11/site-packages/markdown/blockprocessors.py create mode 100644 venv/lib/python3.11/site-packages/markdown/core.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/__init__.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/abbr.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/admonition.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/attr_list.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/codehilite.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/def_list.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/extra.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/fenced_code.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/footnotes.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/legacy_attrs.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/legacy_em.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/md_in_html.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/meta.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/nl2br.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/sane_lists.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/smarty.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/tables.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/toc.py create mode 100644 venv/lib/python3.11/site-packages/markdown/extensions/wikilinks.py create mode 100644 venv/lib/python3.11/site-packages/markdown/htmlparser.py create mode 100644 venv/lib/python3.11/site-packages/markdown/inlinepatterns.py create mode 100644 venv/lib/python3.11/site-packages/markdown/pep562.py create mode 100644 venv/lib/python3.11/site-packages/markdown/postprocessors.py create mode 100644 venv/lib/python3.11/site-packages/markdown/preprocessors.py create mode 100644 venv/lib/python3.11/site-packages/markdown/serializers.py create mode 100644 venv/lib/python3.11/site-packages/markdown/test_tools.py create mode 100644 venv/lib/python3.11/site-packages/markdown/treeprocessors.py create mode 100644 venv/lib/python3.11/site-packages/markdown/util.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/__init__.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/_compat.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/_punycode.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/cli/__init__.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/cli/parse.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/common/__init__.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/common/entities.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/common/html_blocks.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/common/html_re.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/common/normalize_url.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/common/utils.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/helpers/__init__.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/helpers/parse_link_destination.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/helpers/parse_link_label.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/helpers/parse_link_title.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/main.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/parser_block.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/parser_core.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/parser_inline.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/port.yaml create mode 100644 venv/lib/python3.11/site-packages/markdown_it/presets/__init__.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/presets/commonmark.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/presets/default.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/presets/zero.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/py.typed create mode 100644 venv/lib/python3.11/site-packages/markdown_it/renderer.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/ruler.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_block/__init__.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_block/blockquote.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_block/code.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_block/fence.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_block/heading.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_block/hr.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_block/html_block.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_block/lheading.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_block/list.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_block/paragraph.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_block/reference.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_block/state_block.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_block/table.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_core/__init__.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_core/block.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_core/inline.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_core/linkify.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_core/normalize.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_core/replacements.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_core/smartquotes.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_core/state_core.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_core/text_join.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/__init__.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/autolink.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/backticks.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/balance_pairs.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/emphasis.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/entity.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/escape.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/fragments_join.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/html_inline.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/image.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/link.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/linkify.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/newline.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/state_inline.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/strikethrough.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/rules_inline/text.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/token.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/tree.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it/utils.py create mode 100644 venv/lib/python3.11/site-packages/markdown_it_py-3.0.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/markdown_it_py-3.0.0.dist-info/LICENSE create mode 100644 venv/lib/python3.11/site-packages/markdown_it_py-3.0.0.dist-info/LICENSE.markdown-it create mode 100644 venv/lib/python3.11/site-packages/markdown_it_py-3.0.0.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/markdown_it_py-3.0.0.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/markdown_it_py-3.0.0.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/markdown_it_py-3.0.0.dist-info/entry_points.txt create mode 100644 venv/lib/python3.11/site-packages/markupsafe/__init__.py create mode 100644 venv/lib/python3.11/site-packages/markupsafe/_native.py create mode 100644 venv/lib/python3.11/site-packages/markupsafe/_speedups.c create mode 100755 venv/lib/python3.11/site-packages/markupsafe/_speedups.cpython-311-x86_64-linux-gnu.so create mode 100644 venv/lib/python3.11/site-packages/markupsafe/_speedups.pyi create mode 100644 venv/lib/python3.11/site-packages/markupsafe/py.typed create mode 100644 venv/lib/python3.11/site-packages/mdurl-0.1.2.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/mdurl-0.1.2.dist-info/LICENSE create mode 100644 venv/lib/python3.11/site-packages/mdurl-0.1.2.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/mdurl-0.1.2.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/mdurl-0.1.2.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/mdurl/__init__.py create mode 100644 venv/lib/python3.11/site-packages/mdurl/_decode.py create mode 100644 venv/lib/python3.11/site-packages/mdurl/_encode.py create mode 100644 venv/lib/python3.11/site-packages/mdurl/_format.py create mode 100644 venv/lib/python3.11/site-packages/mdurl/_parse.py create mode 100644 venv/lib/python3.11/site-packages/mdurl/_url.py create mode 100644 venv/lib/python3.11/site-packages/mdurl/py.typed create mode 100644 venv/lib/python3.11/site-packages/ordered_set-4.1.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/ordered_set-4.1.0.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/ordered_set-4.1.0.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/ordered_set-4.1.0.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/ordered_set/__init__.py create mode 100644 venv/lib/python3.11/site-packages/ordered_set/py.typed create mode 100644 venv/lib/python3.11/site-packages/pelican-4.5.4.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/pelican-4.5.4.dist-info/LICENSE create mode 100644 venv/lib/python3.11/site-packages/pelican-4.5.4.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/pelican-4.5.4.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/pelican-4.5.4.dist-info/REQUESTED create mode 100644 venv/lib/python3.11/site-packages/pelican-4.5.4.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/pelican-4.5.4.dist-info/entry_points.txt create mode 100644 venv/lib/python3.11/site-packages/pelican/__init__.py create mode 100644 venv/lib/python3.11/site-packages/pelican/__main__.py create mode 100644 venv/lib/python3.11/site-packages/pelican/cache.py create mode 100644 venv/lib/python3.11/site-packages/pelican/contents.py create mode 100644 venv/lib/python3.11/site-packages/pelican/generators.py create mode 100644 venv/lib/python3.11/site-packages/pelican/log.py create mode 100644 venv/lib/python3.11/site-packages/pelican/paginator.py create mode 100644 venv/lib/python3.11/site-packages/pelican/plugins/_utils.py create mode 100644 venv/lib/python3.11/site-packages/pelican/plugins/signals.py create mode 100644 venv/lib/python3.11/site-packages/pelican/readers.py create mode 100644 venv/lib/python3.11/site-packages/pelican/rstdirectives.py create mode 100644 venv/lib/python3.11/site-packages/pelican/server.py create mode 100644 venv/lib/python3.11/site-packages/pelican/settings.py create mode 100644 venv/lib/python3.11/site-packages/pelican/signals.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/TestPages/bad_page.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/TestPages/draft_page.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/TestPages/draft_page_markdown.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/TestPages/draft_page_with_template.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/TestPages/hidden_page.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/TestPages/hidden_page_markdown.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/TestPages/hidden_page_with_template.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/TestPages/page.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/TestPages/page_markdown.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/TestPages/page_used_for_sorting_test.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/TestPages/page_with_category_and_tag_links.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/TestPages/page_with_static_links.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/TestPages/page_with_template.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/__init__.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/2012-11-29_rst_w_filename_meta#foo-bar.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/2012-11-30_md_w_filename_meta#foo-bar.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/TestCategory/article_with_category.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/TestCategory/article_without_category.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_attributes_containing_double_quotes.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_capitalized_metadata.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_code_block.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_comments.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_duplicate_tags_authors.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_inline_svg.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_keywords.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_markdown_and_footnote.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_markdown_and_nested_metadata.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_markdown_and_nonascii_summary.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_markdown_and_summary_metadata_multi.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_markdown_and_summary_metadata_single.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_markdown_extension.markdown create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_markdown_markup_extensions.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_md_extension.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_mdown_extension.mdown create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_metadata.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_metadata.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_metadata.unknownextension create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_metadata_and_contents.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_metadata_explicit_date_implicit_modified.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_metadata_explicit_dates.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_metadata_implicit_date_explicit_modified.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_metadata_implicit_dates.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_mkd_extension.mkd create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_multiple_authors.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_multiple_authors.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_multiple_authors_list.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_multiple_authors_semicolon.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_multiple_metadata_tags.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_nonconformant_meta_tags.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_null_attributes.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_template.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_typogrify_dashes.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_typogrify_dashes.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_uppercase_metadata.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_with_uppercase_metadata.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/article_without_category.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/bad_extension.mmd create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/bloggerexport.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/empty.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/empty_with_bom.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/wordpress_content_decoded create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/wordpress_content_encoded create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/content/wordpressexport.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/cyclic_intersite_links/first-article.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/cyclic_intersite_links/second-article.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/cyclic_intersite_links/third-article.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/default_conf.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/dummy_plugins/namespace_plugin/pelican/plugins/ns_plugin/__init__.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/dummy_plugins/normal_plugin/normal_plugin/__init__.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/dummy_plugins/normal_plugin/normal_plugin/submodule.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/__init__.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subpackage/__init__.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subpackage/subpackage.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subplugin.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/mixed_content/fake_image.jpg create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/mixed_content/short_page.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/mixed_content/subdir/subdir_fake_image.jpg create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/nested_content/maindir/maindir.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/nested_content/maindir/subdir/subdir.md create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/a-markdown-powered-article.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/archives.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/article-1.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/article-2.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/article-3.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/author/alexis-metaireau.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/authors.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/categories.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/category/bar.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/category/cat1.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/category/misc.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/category/yeah.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/drafts/a-draft-article-without-date.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/drafts/a-draft-article.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/feeds/alexis-metaireau.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/feeds/alexis-metaireau.rss.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/feeds/all-en.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/feeds/all-fr.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/feeds/all.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/feeds/bar.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/feeds/cat1.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/feeds/misc.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/feeds/yeah.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/filename_metadata-example.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/oh-yeah-fr.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/oh-yeah.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/override/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/pages/this-is-a-test-page.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/pictures/Fat_Cat.jpg create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/pictures/Sushi.jpg create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/pictures/Sushi_Macro.jpg create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/second-article-fr.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/second-article.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/tag/bar.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/tag/baz.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/tag/foo.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/tag/foobar.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/tag/oh.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/tag/yeah.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/tags.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/css/fonts.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/css/main.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/css/pygment.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/css/reset.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/css/typogrify.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/css/wide.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/fonts/Yanone_Kaffeesatz_400.eot create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/fonts/Yanone_Kaffeesatz_400.svg create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/fonts/Yanone_Kaffeesatz_400.ttf create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/fonts/Yanone_Kaffeesatz_400.woff create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/fonts/Yanone_Kaffeesatz_400.woff2 create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/fonts/font.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/aboutme.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/bitbucket.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/delicious.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/facebook.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/github.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/gitorious.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/gittip.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/google-groups.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/google-plus.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/hackernews.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/lastfm.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/linkedin.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/reddit.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/rss.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/slideshare.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/speakerdeck.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/stackoverflow.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/twitter.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/vimeo.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/theme/images/icons/youtube.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/this-is-a-super-article.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/basic/unbelievable.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/a-markdown-powered-article.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/archives.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/article-1.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/article-2.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/article-3.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/author/alexis-metaireau.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/author/alexis-metaireau2.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/author/alexis-metaireau3.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/authors.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/categories.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/category/bar.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/category/cat1.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/category/misc.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/category/yeah.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/drafts/a-draft-article-without-date.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/drafts/a-draft-article.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/feeds/all-en.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/feeds/all-fr.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/feeds/all.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/feeds/all.rss.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/feeds/bar.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/feeds/bar.rss.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/feeds/cat1.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/feeds/cat1.rss.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/feeds/misc.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/feeds/misc.rss.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/feeds/yeah.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/feeds/yeah.rss.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/filename_metadata-example.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/index2.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/index3.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/jinja2_template.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/oh-yeah-fr.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/oh-yeah.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/override/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/pages/this-is-a-test-page.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/pictures/Fat_Cat.jpg create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/pictures/Sushi.jpg create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/pictures/Sushi_Macro.jpg create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/robots.txt create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/second-article-fr.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/second-article.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/tag/bar.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/tag/baz.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/tag/foo.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/tag/foobar.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/tag/oh.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/tag/yeah.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/tags.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/css/fonts.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/css/main.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/css/pygment.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/css/reset.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/css/typogrify.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/css/wide.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/fonts/Yanone_Kaffeesatz_400.eot create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/fonts/Yanone_Kaffeesatz_400.svg create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/fonts/Yanone_Kaffeesatz_400.ttf create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/fonts/Yanone_Kaffeesatz_400.woff create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/fonts/Yanone_Kaffeesatz_400.woff2 create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/fonts/font.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/aboutme.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/bitbucket.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/delicious.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/facebook.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/github.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/gitorious.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/gittip.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/google-groups.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/google-plus.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/hackernews.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/lastfm.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/linkedin.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/reddit.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/rss.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/slideshare.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/speakerdeck.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/stackoverflow.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/twitter.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/vimeo.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/theme/images/icons/youtube.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/this-is-a-super-article.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom/unbelievable.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/archives.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/author/alexis-metaireau.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/author/alexis-metaireau2.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/author/alexis-metaireau3.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/authors.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/categories.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/category/bar.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/category/cat1.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/category/misc.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/category/yeah.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/drafts/a-draft-article-without-date.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/drafts/a-draft-article.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/feeds/all-en.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/feeds/all-fr.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/feeds/all.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/feeds/all.rss.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/feeds/bar.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/feeds/bar.rss.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/feeds/cat1.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/feeds/cat1.rss.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/feeds/misc.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/feeds/misc.rss.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/feeds/yeah.atom.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/feeds/yeah.rss.xml create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/index2.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/index3.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/jinja2_template.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/oh-yeah-fr.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/override/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/pictures/Fat_Cat.jpg create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/pictures/Sushi.jpg create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/pictures/Sushi_Macro.jpg create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/robots.txt create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/second-article-fr.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/tag/bar.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/tag/baz.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/tag/foo.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/tag/foobar.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/tag/oh.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/tag/yeah.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/tags.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/css/fonts.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/css/main.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/css/pygment.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/css/reset.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/css/typogrify.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/css/wide.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/fonts/Yanone_Kaffeesatz_400.eot create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/fonts/Yanone_Kaffeesatz_400.svg create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/fonts/Yanone_Kaffeesatz_400.ttf create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/fonts/Yanone_Kaffeesatz_400.woff create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/fonts/Yanone_Kaffeesatz_400.woff2 create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/fonts/font.css create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/aboutme.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/bitbucket.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/delicious.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/facebook.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/github.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/gitorious.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/gittip.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/google-groups.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/google-plus.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/hackernews.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/lastfm.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/linkedin.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/reddit.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/rss.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/slideshare.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/speakerdeck.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/stackoverflow.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/twitter.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/vimeo.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/output/custom_locale/theme/images/icons/youtube.png create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/parse_error/parse_error.rst create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/support.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_cache.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_contents.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_generators.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_importer.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_log.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_paginator.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_pelican.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_plugins.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_readers.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_rstdirectives.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_server.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_settings.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_testsuite.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_urlwrappers.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/test_utils.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/theme_overrides/level1/article.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/theme_overrides/level2/article.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tests/theme_overrides/level2/authors.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/css/fonts.css create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/css/main.css create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/css/pygment.css create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/css/reset.css create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/css/typogrify.css create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/css/wide.css create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/fonts/Yanone_Kaffeesatz_400.eot create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/fonts/Yanone_Kaffeesatz_400.svg create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/fonts/Yanone_Kaffeesatz_400.ttf create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/fonts/Yanone_Kaffeesatz_400.woff create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/fonts/Yanone_Kaffeesatz_400.woff2 create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/fonts/font.css create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/aboutme.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/bitbucket.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/delicious.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/facebook.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/github.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/gitorious.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/gittip.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/google-groups.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/google-plus.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/hackernews.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/lastfm.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/linkedin.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/reddit.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/rss.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/slideshare.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/speakerdeck.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/stackoverflow.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/twitter.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/vimeo.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/static/images/icons/youtube.png create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/analytics.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/archives.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/article.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/article_infos.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/author.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/authors.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/base.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/category.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/comments.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/disqus_script.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/github.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/page.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/period_archives.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/tag.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/taglist.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/tags.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/translations.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/notmyidea/templates/twitter.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/archives.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/article.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/author.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/authors.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/base.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/categories.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/category.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/gosquared.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/index.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/page.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/pagination.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/period_archives.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/tag.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/tags.html create mode 100644 venv/lib/python3.11/site-packages/pelican/themes/simple/templates/translations.html create mode 100644 venv/lib/python3.11/site-packages/pelican/tools/__init__.py create mode 100755 venv/lib/python3.11/site-packages/pelican/tools/pelican_import.py create mode 100755 venv/lib/python3.11/site-packages/pelican/tools/pelican_quickstart.py create mode 100755 venv/lib/python3.11/site-packages/pelican/tools/pelican_themes.py create mode 100644 venv/lib/python3.11/site-packages/pelican/tools/templates/Makefile.jinja2 create mode 100644 venv/lib/python3.11/site-packages/pelican/tools/templates/pelicanconf.py.jinja2 create mode 100755 venv/lib/python3.11/site-packages/pelican/tools/templates/publishconf.py.jinja2 create mode 100644 venv/lib/python3.11/site-packages/pelican/tools/templates/tasks.py.jinja2 create mode 100644 venv/lib/python3.11/site-packages/pelican/urlwrappers.py create mode 100644 venv/lib/python3.11/site-packages/pelican/utils.py create mode 100644 venv/lib/python3.11/site-packages/pelican/writers.py create mode 100644 venv/lib/python3.11/site-packages/pygments/__init__.py create mode 100644 venv/lib/python3.11/site-packages/pygments/__main__.py create mode 100644 venv/lib/python3.11/site-packages/pygments/cmdline.py create mode 100644 venv/lib/python3.11/site-packages/pygments/console.py create mode 100644 venv/lib/python3.11/site-packages/pygments/filter.py create mode 100644 venv/lib/python3.11/site-packages/pygments/filters/__init__.py create mode 100644 venv/lib/python3.11/site-packages/pygments/formatter.py create mode 100644 venv/lib/python3.11/site-packages/pygments/formatters/__init__.py create mode 100644 venv/lib/python3.11/site-packages/pygments/formatters/_mapping.py create mode 100644 venv/lib/python3.11/site-packages/pygments/formatters/bbcode.py create mode 100644 venv/lib/python3.11/site-packages/pygments/formatters/html.py create mode 100644 venv/lib/python3.11/site-packages/pygments/formatters/img.py create mode 100644 venv/lib/python3.11/site-packages/pygments/formatters/irc.py create mode 100644 venv/lib/python3.11/site-packages/pygments/formatters/latex.py create mode 100644 venv/lib/python3.11/site-packages/pygments/formatters/other.py create mode 100644 venv/lib/python3.11/site-packages/pygments/formatters/rtf.py create mode 100644 venv/lib/python3.11/site-packages/pygments/formatters/svg.py create mode 100644 venv/lib/python3.11/site-packages/pygments/formatters/terminal.py create mode 100644 venv/lib/python3.11/site-packages/pygments/formatters/terminal256.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexer.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/__init__.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_asy_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_cl_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_cocoa_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_csound_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_lasso_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_lua_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_mapping.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_mql_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_openedge_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_php_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_postgres_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_scilab_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_sourcemod_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_stan_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_stata_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_tsql_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_usd_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_vbscript_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/_vim_builtins.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/actionscript.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/agile.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/algebra.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/ambient.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/ampl.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/apl.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/archetype.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/asm.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/automation.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/basic.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/bibtex.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/boa.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/business.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/c_cpp.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/c_like.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/capnproto.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/chapel.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/clean.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/compiled.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/configs.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/console.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/crystal.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/csound.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/css.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/d.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/dalvik.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/data.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/diff.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/dotnet.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/dsls.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/dylan.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/ecl.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/eiffel.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/elm.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/email.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/erlang.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/esoteric.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/ezhil.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/factor.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/fantom.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/felix.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/floscript.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/forth.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/fortran.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/foxpro.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/freefem.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/functional.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/go.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/grammar_notation.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/graph.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/graphics.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/haskell.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/haxe.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/hdl.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/hexdump.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/html.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/idl.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/igor.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/inferno.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/installers.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/int_fiction.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/iolang.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/j.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/javascript.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/julia.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/jvm.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/lisp.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/make.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/markup.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/math.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/matlab.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/mime.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/ml.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/modeling.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/modula2.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/monte.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/mosel.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/ncl.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/nimrod.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/nit.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/nix.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/oberon.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/objective.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/ooc.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/other.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/parasail.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/parsers.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/pascal.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/pawn.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/perl.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/php.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/pony.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/praat.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/prolog.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/python.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/qvt.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/r.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/rdf.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/rebol.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/resource.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/ride.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/rnc.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/roboconf.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/robotframework.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/ruby.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/rust.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/sas.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/scdoc.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/scripting.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/sgf.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/shell.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/sieve.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/slash.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/smalltalk.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/smv.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/snobol.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/solidity.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/special.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/sql.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/stata.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/supercollider.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/tcl.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/templates.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/teraterm.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/testing.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/text.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/textedit.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/textfmts.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/theorem.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/trafficscript.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/typoscript.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/unicon.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/urbi.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/usd.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/varnish.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/verification.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/web.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/webidl.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/webmisc.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/whiley.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/x10.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/xorg.py create mode 100644 venv/lib/python3.11/site-packages/pygments/lexers/zig.py create mode 100644 venv/lib/python3.11/site-packages/pygments/modeline.py create mode 100644 venv/lib/python3.11/site-packages/pygments/plugin.py create mode 100644 venv/lib/python3.11/site-packages/pygments/regexopt.py create mode 100644 venv/lib/python3.11/site-packages/pygments/scanner.py create mode 100644 venv/lib/python3.11/site-packages/pygments/sphinxext.py create mode 100644 venv/lib/python3.11/site-packages/pygments/style.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/__init__.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/abap.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/algol.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/algol_nu.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/arduino.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/autumn.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/borland.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/bw.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/colorful.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/default.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/emacs.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/friendly.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/fruity.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/igor.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/inkpot.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/lovelace.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/manni.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/monokai.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/murphy.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/native.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/paraiso_dark.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/paraiso_light.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/pastie.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/perldoc.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/rainbow_dash.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/rrt.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/sas.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/solarized.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/stata_dark.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/stata_light.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/tango.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/trac.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/vim.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/vs.py create mode 100644 venv/lib/python3.11/site-packages/pygments/styles/xcode.py create mode 100644 venv/lib/python3.11/site-packages/pygments/token.py create mode 100644 venv/lib/python3.11/site-packages/pygments/unistring.py create mode 100644 venv/lib/python3.11/site-packages/pygments/util.py create mode 100644 venv/lib/python3.11/site-packages/python_dateutil-2.9.0.post0.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/python_dateutil-2.9.0.post0.dist-info/LICENSE create mode 100644 venv/lib/python3.11/site-packages/python_dateutil-2.9.0.post0.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/python_dateutil-2.9.0.post0.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/python_dateutil-2.9.0.post0.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/python_dateutil-2.9.0.post0.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/python_dateutil-2.9.0.post0.dist-info/zip-safe create mode 100644 venv/lib/python3.11/site-packages/pytz-2020.1.dist-info/DESCRIPTION.rst create mode 100644 venv/lib/python3.11/site-packages/pytz-2020.1.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/pytz-2020.1.dist-info/LICENSE.txt create mode 100644 venv/lib/python3.11/site-packages/pytz-2020.1.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/pytz-2020.1.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/pytz-2020.1.dist-info/REQUESTED create mode 100644 venv/lib/python3.11/site-packages/pytz-2020.1.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/pytz-2020.1.dist-info/metadata.json create mode 100644 venv/lib/python3.11/site-packages/pytz-2020.1.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/pytz-2020.1.dist-info/zip-safe create mode 100644 venv/lib/python3.11/site-packages/pytz/__init__.py create mode 100644 venv/lib/python3.11/site-packages/pytz/exceptions.py create mode 100644 venv/lib/python3.11/site-packages/pytz/lazy.py create mode 100644 venv/lib/python3.11/site-packages/pytz/reference.py create mode 100644 venv/lib/python3.11/site-packages/pytz/tzfile.py create mode 100644 venv/lib/python3.11/site-packages/pytz/tzinfo.py create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Abidjan create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Accra create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Addis_Ababa create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Algiers create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Asmara create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Asmera create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Bamako create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Bangui create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Banjul create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Bissau create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Blantyre create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Brazzaville create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Bujumbura create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Cairo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Casablanca create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Ceuta create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Conakry create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Dakar create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Dar_es_Salaam create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Djibouti create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Douala create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/El_Aaiun create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Freetown create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Gaborone create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Harare create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Johannesburg create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Juba create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Kampala create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Khartoum create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Kigali create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Kinshasa create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Lagos create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Libreville create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Lome create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Luanda create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Lubumbashi create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Lusaka create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Malabo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Maputo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Maseru create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Mbabane create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Mogadishu create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Monrovia create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Nairobi create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Ndjamena create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Niamey create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Nouakchott create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Ouagadougou create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Porto-Novo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Sao_Tome create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Timbuktu create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Tripoli create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Tunis create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Africa/Windhoek create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Adak create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Anchorage create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Anguilla create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Antigua create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Araguaina create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Argentina/Buenos_Aires create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Argentina/Catamarca create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Argentina/ComodRivadavia create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Argentina/Cordoba create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Argentina/Jujuy create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Argentina/La_Rioja create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Argentina/Mendoza create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Argentina/Rio_Gallegos create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Argentina/Salta create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Argentina/San_Juan create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Argentina/San_Luis create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Argentina/Tucuman create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Argentina/Ushuaia create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Aruba create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Asuncion create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Atikokan create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Atka create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Bahia create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Bahia_Banderas create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Barbados create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Belem create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Belize create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Blanc-Sablon create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Boa_Vista create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Bogota create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Boise create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Buenos_Aires create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Cambridge_Bay create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Campo_Grande create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Cancun create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Caracas create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Catamarca create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Cayenne create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Cayman create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Chicago create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Chihuahua create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Coral_Harbour create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Cordoba create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Costa_Rica create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Creston create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Cuiaba create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Curacao create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Danmarkshavn create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Dawson create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Dawson_Creek create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Denver create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Detroit create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Dominica create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Edmonton create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Eirunepe create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/El_Salvador create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Ensenada create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Fort_Nelson create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Fort_Wayne create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Fortaleza create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Glace_Bay create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Godthab create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Goose_Bay create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Grand_Turk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Grenada create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Guadeloupe create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Guatemala create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Guayaquil create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Guyana create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Halifax create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Havana create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Hermosillo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Indiana/Indianapolis create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Indiana/Knox create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Indiana/Marengo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Indiana/Petersburg create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Indiana/Tell_City create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Indiana/Vevay create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Indiana/Vincennes create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Indiana/Winamac create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Indianapolis create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Inuvik create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Iqaluit create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Jamaica create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Jujuy create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Juneau create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Kentucky/Louisville create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Kentucky/Monticello create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Knox_IN create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Kralendijk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/La_Paz create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Lima create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Los_Angeles create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Louisville create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Lower_Princes create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Maceio create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Managua create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Manaus create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Marigot create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Martinique create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Matamoros create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Mazatlan create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Mendoza create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Menominee create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Merida create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Metlakatla create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Mexico_City create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Miquelon create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Moncton create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Monterrey create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Montevideo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Montreal create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Montserrat create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Nassau create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/New_York create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Nipigon create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Nome create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Noronha create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/North_Dakota/Beulah create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/North_Dakota/Center create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/North_Dakota/New_Salem create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Nuuk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Ojinaga create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Panama create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Pangnirtung create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Paramaribo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Phoenix create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Port-au-Prince create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Port_of_Spain create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Porto_Acre create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Porto_Velho create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Puerto_Rico create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Punta_Arenas create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Rainy_River create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Rankin_Inlet create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Recife create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Regina create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Resolute create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Rio_Branco create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Rosario create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Santa_Isabel create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Santarem create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Santiago create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Santo_Domingo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Sao_Paulo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Scoresbysund create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Shiprock create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Sitka create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/St_Barthelemy create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/St_Johns create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/St_Kitts create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/St_Lucia create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/St_Thomas create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/St_Vincent create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Swift_Current create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Tegucigalpa create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Thule create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Thunder_Bay create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Tijuana create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Toronto create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Tortola create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Vancouver create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Virgin create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Whitehorse create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Winnipeg create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Yakutat create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/America/Yellowknife create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Antarctica/Casey create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Antarctica/Davis create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Antarctica/DumontDUrville create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Antarctica/Macquarie create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Antarctica/Mawson create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Antarctica/McMurdo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Antarctica/Palmer create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Antarctica/Rothera create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Antarctica/South_Pole create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Antarctica/Syowa create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Antarctica/Troll create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Antarctica/Vostok create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Arctic/Longyearbyen create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Aden create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Almaty create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Amman create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Anadyr create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Aqtau create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Aqtobe create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Ashgabat create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Ashkhabad create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Atyrau create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Baghdad create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Bahrain create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Baku create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Bangkok create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Barnaul create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Beirut create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Bishkek create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Brunei create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Calcutta create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Chita create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Choibalsan create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Chongqing create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Chungking create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Colombo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Dacca create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Damascus create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Dhaka create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Dili create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Dubai create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Dushanbe create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Famagusta create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Gaza create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Harbin create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Hebron create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Ho_Chi_Minh create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Hong_Kong create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Hovd create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Irkutsk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Istanbul create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Jakarta create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Jayapura create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Jerusalem create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Kabul create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Kamchatka create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Karachi create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Kashgar create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Kathmandu create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Katmandu create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Khandyga create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Kolkata create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Krasnoyarsk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Kuala_Lumpur create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Kuching create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Kuwait create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Macao create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Macau create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Magadan create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Makassar create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Manila create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Muscat create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Nicosia create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Novokuznetsk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Novosibirsk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Omsk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Oral create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Phnom_Penh create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Pontianak create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Pyongyang create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Qatar create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Qostanay create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Qyzylorda create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Rangoon create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Riyadh create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Saigon create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Sakhalin create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Samarkand create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Seoul create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Shanghai create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Singapore create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Srednekolymsk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Taipei create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Tashkent create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Tbilisi create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Tehran create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Tel_Aviv create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Thimbu create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Thimphu create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Tokyo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Tomsk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Ujung_Pandang create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Ulaanbaatar create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Ulan_Bator create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Urumqi create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Ust-Nera create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Vientiane create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Vladivostok create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Yakutsk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Yangon create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Yekaterinburg create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Asia/Yerevan create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Atlantic/Azores create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Atlantic/Bermuda create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Atlantic/Canary create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Atlantic/Cape_Verde create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Atlantic/Faeroe create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Atlantic/Faroe create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Atlantic/Jan_Mayen create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Atlantic/Madeira create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Atlantic/Reykjavik create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Atlantic/South_Georgia create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Atlantic/St_Helena create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Atlantic/Stanley create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/ACT create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Adelaide create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Brisbane create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Broken_Hill create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Canberra create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Currie create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Darwin create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Eucla create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Hobart create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/LHI create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Lindeman create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Lord_Howe create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Melbourne create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/NSW create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/North create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Perth create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Queensland create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/South create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Sydney create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Tasmania create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Victoria create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/West create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Australia/Yancowinna create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Brazil/Acre create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Brazil/DeNoronha create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Brazil/East create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Brazil/West create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/CET create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/CST6CDT create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Canada/Atlantic create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Canada/Central create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Canada/Eastern create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Canada/Mountain create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Canada/Newfoundland create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Canada/Pacific create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Canada/Saskatchewan create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Canada/Yukon create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Chile/Continental create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Chile/EasterIsland create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Cuba create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/EET create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/EST create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/EST5EDT create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Egypt create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Eire create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT+0 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT+1 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT+10 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT+11 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT+12 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT+2 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT+3 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT+4 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT+5 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT+6 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT+7 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT+8 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT+9 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-0 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-1 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-10 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-11 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-12 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-13 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-14 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-2 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-3 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-4 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-5 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-6 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-7 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-8 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT-9 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/GMT0 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/Greenwich create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/UCT create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/UTC create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/Universal create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Etc/Zulu create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Amsterdam create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Andorra create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Astrakhan create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Athens create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Belfast create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Belgrade create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Berlin create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Bratislava create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Brussels create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Bucharest create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Budapest create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Busingen create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Chisinau create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Copenhagen create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Dublin create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Gibraltar create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Guernsey create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Helsinki create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Isle_of_Man create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Istanbul create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Jersey create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Kaliningrad create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Kiev create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Kirov create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Lisbon create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Ljubljana create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/London create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Luxembourg create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Madrid create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Malta create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Mariehamn create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Minsk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Monaco create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Moscow create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Nicosia create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Oslo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Paris create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Podgorica create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Prague create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Riga create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Rome create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Samara create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/San_Marino create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Sarajevo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Saratov create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Simferopol create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Skopje create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Sofia create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Stockholm create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Tallinn create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Tirane create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Tiraspol create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Ulyanovsk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Uzhgorod create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Vaduz create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Vatican create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Vienna create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Vilnius create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Volgograd create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Warsaw create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Zagreb create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Zaporozhye create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Europe/Zurich create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Factory create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/GB create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/GB-Eire create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/GMT create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/GMT+0 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/GMT-0 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/GMT0 create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Greenwich create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/HST create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Hongkong create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Iceland create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Indian/Antananarivo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Indian/Chagos create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Indian/Christmas create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Indian/Cocos create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Indian/Comoro create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Indian/Kerguelen create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Indian/Mahe create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Indian/Maldives create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Indian/Mauritius create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Indian/Mayotte create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Indian/Reunion create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Iran create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Israel create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Jamaica create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Japan create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Kwajalein create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Libya create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/MET create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/MST create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/MST7MDT create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Mexico/BajaNorte create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Mexico/BajaSur create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Mexico/General create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/NZ create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/NZ-CHAT create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Navajo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/PRC create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/PST8PDT create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Apia create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Auckland create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Bougainville create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Chatham create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Chuuk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Easter create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Efate create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Enderbury create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Fakaofo create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Fiji create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Funafuti create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Galapagos create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Gambier create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Guadalcanal create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Guam create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Honolulu create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Johnston create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Kiritimati create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Kosrae create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Kwajalein create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Majuro create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Marquesas create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Midway create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Nauru create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Niue create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Norfolk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Noumea create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Pago_Pago create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Palau create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Pitcairn create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Pohnpei create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Ponape create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Port_Moresby create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Rarotonga create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Saipan create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Samoa create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Tahiti create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Tarawa create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Tongatapu create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Truk create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Wake create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Wallis create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Pacific/Yap create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Poland create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Portugal create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/ROC create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/ROK create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Singapore create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Turkey create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/UCT create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/US/Alaska create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/US/Aleutian create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/US/Arizona create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/US/Central create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/US/East-Indiana create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/US/Eastern create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/US/Hawaii create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/US/Indiana-Starke create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/US/Michigan create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/US/Mountain create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/US/Pacific create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/US/Samoa create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/UTC create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Universal create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/W-SU create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/WET create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/Zulu create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/iso3166.tab create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/leapseconds create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/posixrules create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/tzdata.zi create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/zone.tab create mode 100644 venv/lib/python3.11/site-packages/pytz/zoneinfo/zone1970.tab create mode 100644 venv/lib/python3.11/site-packages/rich-13.9.4.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/rich-13.9.4.dist-info/LICENSE create mode 100644 venv/lib/python3.11/site-packages/rich-13.9.4.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/rich-13.9.4.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/rich-13.9.4.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/rich/__init__.py create mode 100644 venv/lib/python3.11/site-packages/rich/__main__.py create mode 100644 venv/lib/python3.11/site-packages/rich/_cell_widths.py create mode 100644 venv/lib/python3.11/site-packages/rich/_emoji_codes.py create mode 100644 venv/lib/python3.11/site-packages/rich/_emoji_replace.py create mode 100644 venv/lib/python3.11/site-packages/rich/_export_format.py create mode 100644 venv/lib/python3.11/site-packages/rich/_extension.py create mode 100644 venv/lib/python3.11/site-packages/rich/_fileno.py create mode 100644 venv/lib/python3.11/site-packages/rich/_inspect.py create mode 100644 venv/lib/python3.11/site-packages/rich/_log_render.py create mode 100644 venv/lib/python3.11/site-packages/rich/_loop.py create mode 100644 venv/lib/python3.11/site-packages/rich/_null_file.py create mode 100644 venv/lib/python3.11/site-packages/rich/_palettes.py create mode 100644 venv/lib/python3.11/site-packages/rich/_pick.py create mode 100644 venv/lib/python3.11/site-packages/rich/_ratio.py create mode 100644 venv/lib/python3.11/site-packages/rich/_spinners.py create mode 100644 venv/lib/python3.11/site-packages/rich/_stack.py create mode 100644 venv/lib/python3.11/site-packages/rich/_timer.py create mode 100644 venv/lib/python3.11/site-packages/rich/_win32_console.py create mode 100644 venv/lib/python3.11/site-packages/rich/_windows.py create mode 100644 venv/lib/python3.11/site-packages/rich/_windows_renderer.py create mode 100644 venv/lib/python3.11/site-packages/rich/_wrap.py create mode 100644 venv/lib/python3.11/site-packages/rich/abc.py create mode 100644 venv/lib/python3.11/site-packages/rich/align.py create mode 100644 venv/lib/python3.11/site-packages/rich/ansi.py create mode 100644 venv/lib/python3.11/site-packages/rich/bar.py create mode 100644 venv/lib/python3.11/site-packages/rich/box.py create mode 100644 venv/lib/python3.11/site-packages/rich/cells.py create mode 100644 venv/lib/python3.11/site-packages/rich/color.py create mode 100644 venv/lib/python3.11/site-packages/rich/color_triplet.py create mode 100644 venv/lib/python3.11/site-packages/rich/columns.py create mode 100644 venv/lib/python3.11/site-packages/rich/console.py create mode 100644 venv/lib/python3.11/site-packages/rich/constrain.py create mode 100644 venv/lib/python3.11/site-packages/rich/containers.py create mode 100644 venv/lib/python3.11/site-packages/rich/control.py create mode 100644 venv/lib/python3.11/site-packages/rich/default_styles.py create mode 100644 venv/lib/python3.11/site-packages/rich/diagnose.py create mode 100644 venv/lib/python3.11/site-packages/rich/emoji.py create mode 100644 venv/lib/python3.11/site-packages/rich/errors.py create mode 100644 venv/lib/python3.11/site-packages/rich/file_proxy.py create mode 100644 venv/lib/python3.11/site-packages/rich/filesize.py create mode 100644 venv/lib/python3.11/site-packages/rich/highlighter.py create mode 100644 venv/lib/python3.11/site-packages/rich/json.py create mode 100644 venv/lib/python3.11/site-packages/rich/jupyter.py create mode 100644 venv/lib/python3.11/site-packages/rich/layout.py create mode 100644 venv/lib/python3.11/site-packages/rich/live.py create mode 100644 venv/lib/python3.11/site-packages/rich/live_render.py create mode 100644 venv/lib/python3.11/site-packages/rich/logging.py create mode 100644 venv/lib/python3.11/site-packages/rich/markdown.py create mode 100644 venv/lib/python3.11/site-packages/rich/markup.py create mode 100644 venv/lib/python3.11/site-packages/rich/measure.py create mode 100644 venv/lib/python3.11/site-packages/rich/padding.py create mode 100644 venv/lib/python3.11/site-packages/rich/pager.py create mode 100644 venv/lib/python3.11/site-packages/rich/palette.py create mode 100644 venv/lib/python3.11/site-packages/rich/panel.py create mode 100644 venv/lib/python3.11/site-packages/rich/pretty.py create mode 100644 venv/lib/python3.11/site-packages/rich/progress.py create mode 100644 venv/lib/python3.11/site-packages/rich/progress_bar.py create mode 100644 venv/lib/python3.11/site-packages/rich/prompt.py create mode 100644 venv/lib/python3.11/site-packages/rich/protocol.py create mode 100644 venv/lib/python3.11/site-packages/rich/py.typed create mode 100644 venv/lib/python3.11/site-packages/rich/region.py create mode 100644 venv/lib/python3.11/site-packages/rich/repr.py create mode 100644 venv/lib/python3.11/site-packages/rich/rule.py create mode 100644 venv/lib/python3.11/site-packages/rich/scope.py create mode 100644 venv/lib/python3.11/site-packages/rich/screen.py create mode 100644 venv/lib/python3.11/site-packages/rich/segment.py create mode 100644 venv/lib/python3.11/site-packages/rich/spinner.py create mode 100644 venv/lib/python3.11/site-packages/rich/status.py create mode 100644 venv/lib/python3.11/site-packages/rich/style.py create mode 100644 venv/lib/python3.11/site-packages/rich/styled.py create mode 100644 venv/lib/python3.11/site-packages/rich/syntax.py create mode 100644 venv/lib/python3.11/site-packages/rich/table.py create mode 100644 venv/lib/python3.11/site-packages/rich/terminal_theme.py create mode 100644 venv/lib/python3.11/site-packages/rich/text.py create mode 100644 venv/lib/python3.11/site-packages/rich/theme.py create mode 100644 venv/lib/python3.11/site-packages/rich/themes.py create mode 100644 venv/lib/python3.11/site-packages/rich/traceback.py create mode 100644 venv/lib/python3.11/site-packages/rich/tree.py create mode 100644 venv/lib/python3.11/site-packages/six-1.16.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/six-1.16.0.dist-info/LICENSE create mode 100644 venv/lib/python3.11/site-packages/six-1.16.0.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/six-1.16.0.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/six-1.16.0.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/six-1.16.0.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/six.py create mode 100644 venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/LICENSE create mode 100644 venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/LICENSE.APACHE2 create mode 100644 venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT create mode 100644 venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/sniffio/__init__.py create mode 100644 venv/lib/python3.11/site-packages/sniffio/_impl.py create mode 100644 venv/lib/python3.11/site-packages/sniffio/_tests/__init__.py create mode 100644 venv/lib/python3.11/site-packages/sniffio/_tests/test_sniffio.py create mode 100644 venv/lib/python3.11/site-packages/sniffio/_version.py create mode 100644 venv/lib/python3.11/site-packages/sniffio/py.typed create mode 100644 venv/lib/python3.11/site-packages/soupsieve-2.6.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/soupsieve-2.6.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/soupsieve-2.6.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/soupsieve-2.6.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/soupsieve-2.6.dist-info/licenses/LICENSE.md create mode 100644 venv/lib/python3.11/site-packages/soupsieve/__init__.py create mode 100644 venv/lib/python3.11/site-packages/soupsieve/__meta__.py create mode 100644 venv/lib/python3.11/site-packages/soupsieve/css_match.py create mode 100644 venv/lib/python3.11/site-packages/soupsieve/css_parser.py create mode 100644 venv/lib/python3.11/site-packages/soupsieve/css_types.py create mode 100644 venv/lib/python3.11/site-packages/soupsieve/pretty.py create mode 100644 venv/lib/python3.11/site-packages/soupsieve/py.typed create mode 100644 venv/lib/python3.11/site-packages/soupsieve/util.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/__init__.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/__main__.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/py.typed create mode 100644 venv/lib/python3.11/site-packages/unidecode/util.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x000.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x001.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x002.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x003.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x004.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x005.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x006.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x007.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x009.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x00a.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x00b.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x00c.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x00d.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x00e.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x00f.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x010.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x011.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x012.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x013.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x014.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x015.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x016.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x017.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x018.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x01d.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x01e.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x01f.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x020.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x021.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x022.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x023.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x024.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x025.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x026.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x027.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x028.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x029.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x02a.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x02c.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x02e.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x02f.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x030.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x031.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x032.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x033.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x04d.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x04e.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x04f.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x050.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x051.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x052.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x053.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x054.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x055.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x056.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x057.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x058.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x059.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x05a.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x05b.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x05c.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x05d.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x05e.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x05f.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x060.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x061.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x062.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x063.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x064.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x065.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x066.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x067.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x068.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x069.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x06a.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x06b.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x06c.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x06d.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x06e.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x06f.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x070.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x071.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x072.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x073.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x074.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x075.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x076.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x077.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x078.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x079.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x07a.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x07b.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x07c.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x07d.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x07e.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x07f.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x080.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x081.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x082.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x083.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x084.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x085.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x086.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x087.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x088.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x089.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x08a.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x08b.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x08c.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x08d.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x08e.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x08f.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x090.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x091.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x092.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x093.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x094.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x095.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x096.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x097.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x098.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x099.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x09a.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x09b.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x09c.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x09d.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x09e.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x09f.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0a0.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0a1.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0a2.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0a3.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0a4.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0ac.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0ad.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0ae.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0af.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0b0.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0b1.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0b2.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0b3.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0b4.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0b5.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0b6.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0b7.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0b8.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0b9.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0ba.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0bb.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0bc.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0bd.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0be.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0bf.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0c0.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0c1.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0c2.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0c3.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0c4.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0c5.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0c6.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0c7.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0c8.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0c9.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0ca.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0cb.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0cc.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0cd.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0ce.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0cf.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0d0.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0d1.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0d2.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0d3.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0d4.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0d5.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0d6.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0d7.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0f9.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0fa.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0fb.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0fc.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0fd.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0fe.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x0ff.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x1d4.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x1d5.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x1d6.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x1d7.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x1f1.py create mode 100644 venv/lib/python3.11/site-packages/unidecode/x1f6.py create mode 100644 venv/lib/python3.11/site-packages/watchfiles-0.24.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/watchfiles-0.24.0.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/watchfiles-0.24.0.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/watchfiles-0.24.0.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/watchfiles-0.24.0.dist-info/entry_points.txt create mode 100644 venv/lib/python3.11/site-packages/watchfiles-0.24.0.dist-info/licenses/LICENSE create mode 100644 venv/lib/python3.11/site-packages/watchfiles/__init__.py create mode 100644 venv/lib/python3.11/site-packages/watchfiles/__main__.py create mode 100755 venv/lib/python3.11/site-packages/watchfiles/_rust_notify.cpython-311-x86_64-linux-gnu.so create mode 100644 venv/lib/python3.11/site-packages/watchfiles/_rust_notify.pyi create mode 100644 venv/lib/python3.11/site-packages/watchfiles/cli.py create mode 100644 venv/lib/python3.11/site-packages/watchfiles/filters.py create mode 100644 venv/lib/python3.11/site-packages/watchfiles/main.py create mode 100644 venv/lib/python3.11/site-packages/watchfiles/py.typed create mode 100644 venv/lib/python3.11/site-packages/watchfiles/run.py create mode 100644 venv/lib/python3.11/site-packages/watchfiles/version.py create mode 100644 venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/DESCRIPTION.rst create mode 100644 venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/INSTALLER create mode 100644 venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/METADATA create mode 100644 venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/RECORD create mode 100644 venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/WHEEL create mode 100644 venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/metadata.json create mode 100644 venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/top_level.txt create mode 100644 venv/lib/python3.11/site-packages/webencodings/__init__.py create mode 100644 venv/lib/python3.11/site-packages/webencodings/labels.py create mode 100644 venv/lib/python3.11/site-packages/webencodings/mklabels.py create mode 100644 venv/lib/python3.11/site-packages/webencodings/tests.py create mode 100644 venv/lib/python3.11/site-packages/webencodings/x_user_defined.py diff --git a/content/2024/declarations-pen-pals-EN.md b/content/2024/declarations-pen-pals-EN.md index 00e7df57..3e57c851 100644 --- a/content/2024/declarations-pen-pals-EN.md +++ b/content/2024/declarations-pen-pals-EN.md @@ -15,8 +15,11 @@ For that occasion we decided to focus two days on CSS inside emails. --- **Dates:** Friday 6th and Saturday 7th of December, 2024 + **Time:** 10:00-17:00 + **Location:** Varia (Gouwstraat 3) + **Cost:** Free Dear CSS writers, web-artisans and declarative artists, Declarations is organising its first meetup at Varia in Rotterdam. diff --git a/content/2024/declarations-pen-pals-NL.md b/content/2024/declarations-pen-pals-NL.md index 6089ac4a..618168c0 100644 --- a/content/2024/declarations-pen-pals-NL.md +++ b/content/2024/declarations-pen-pals-NL.md @@ -13,9 +13,12 @@ Voor deze gelegenheid hebben we besloten om twee dagen te focussen op CSS in e-m --- -**Data:** Vrijdag, 6 and Zaterdag, 7 December 2024\ -**Tijd:** 10:00-17:00\ -**Locatie:** Varia (Gouwstraat 3)\ +**Data:** Vrijdag, 6 and Zaterdag, 7 December 2024 + +**Tijd:** 10:00-17:00 + +**Locatie:** Varia (Gouwstraat 3) + **Entree:** Gratis Beste CSS-schrijvers, webkunstenaars en declaratieve kunstenaars, Declarations organiseert zijn eerste meetup bij Varia in Rotterdam. diff --git a/venv/bin/icalendar b/venv/bin/icalendar new file mode 100755 index 00000000..258622f5 --- /dev/null +++ b/venv/bin/icalendar @@ -0,0 +1,8 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from icalendar.cli import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/markdown-it b/venv/bin/markdown-it new file mode 100755 index 00000000..7bdafff6 --- /dev/null +++ b/venv/bin/markdown-it @@ -0,0 +1,8 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from markdown_it.cli.parse import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/markdown_py b/venv/bin/markdown_py new file mode 100755 index 00000000..236e63ff --- /dev/null +++ b/venv/bin/markdown_py @@ -0,0 +1,8 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from markdown.__main__ import run +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(run()) diff --git a/venv/bin/pelican b/venv/bin/pelican new file mode 100755 index 00000000..7af946ac --- /dev/null +++ b/venv/bin/pelican @@ -0,0 +1,8 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pelican.__main__ import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pelican-import b/venv/bin/pelican-import new file mode 100755 index 00000000..620fbb86 --- /dev/null +++ b/venv/bin/pelican-import @@ -0,0 +1,8 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pelican.tools.pelican_import import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pelican-plugins b/venv/bin/pelican-plugins new file mode 100755 index 00000000..99666768 --- /dev/null +++ b/venv/bin/pelican-plugins @@ -0,0 +1,8 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pelican.plugins._utils import list_plugins +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(list_plugins()) diff --git a/venv/bin/pelican-quickstart b/venv/bin/pelican-quickstart new file mode 100755 index 00000000..0ff60bae --- /dev/null +++ b/venv/bin/pelican-quickstart @@ -0,0 +1,8 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pelican.tools.pelican_quickstart import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pelican-themes b/venv/bin/pelican-themes new file mode 100755 index 00000000..698ac355 --- /dev/null +++ b/venv/bin/pelican-themes @@ -0,0 +1,8 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pelican.tools.pelican_themes import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pygmentize b/venv/bin/pygmentize new file mode 100755 index 00000000..54f81f1e --- /dev/null +++ b/venv/bin/pygmentize @@ -0,0 +1,8 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pygments.cmdline import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/rst2html.py b/venv/bin/rst2html.py new file mode 100755 index 00000000..148f8e5d --- /dev/null +++ b/venv/bin/rst2html.py @@ -0,0 +1,23 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 + +# $Id: rst2html.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML documents from standalone reStructuredText ' + 'sources. ' + default_description) + +publish_cmdline(writer_name='html', description=description) diff --git a/venv/bin/rst2html4.py b/venv/bin/rst2html4.py new file mode 100755 index 00000000..a5e618a1 --- /dev/null +++ b/venv/bin/rst2html4.py @@ -0,0 +1,26 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 + +# $Id: rst2html4.py 7994 2016-12-10 17:41:45Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing (X)HTML. + +The output conforms to XHTML 1.0 transitional +and almost to HTML 4.01 transitional (except for closing empty tags). +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML documents from standalone reStructuredText ' + 'sources. ' + default_description) + +publish_cmdline(writer_name='html4', description=description) diff --git a/venv/bin/rst2html5.py b/venv/bin/rst2html5.py new file mode 100755 index 00000000..5b9cacef --- /dev/null +++ b/venv/bin/rst2html5.py @@ -0,0 +1,35 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 +# -*- coding: utf8 -*- +# :Copyright: © 2015 Günter Milde. +# :License: Released under the terms of the `2-Clause BSD license`_, in short: +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. +# This file is offered as-is, without any warranty. +# +# .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause +# +# Revision: $Revision: 8410 $ +# Date: $Date: 2019-11-04 22:14:43 +0100 (Mo, 04. Nov 2019) $ + +""" +A minimal front end to the Docutils Publisher, producing HTML 5 documents. + +The output also conforms to XHTML 1.0 transitional +(except for the doctype declaration). +""" + +try: + import locale # module missing in Jython + locale.setlocale(locale.LC_ALL, '') +except locale.Error: + pass + +from docutils.core import publish_cmdline, default_description + +description = (u'Generates HTML 5 documents from standalone ' + u'reStructuredText sources ' + + default_description) + +publish_cmdline(writer_name='html5', description=description) diff --git a/venv/bin/rst2latex.py b/venv/bin/rst2latex.py new file mode 100755 index 00000000..b414040c --- /dev/null +++ b/venv/bin/rst2latex.py @@ -0,0 +1,26 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 + +# $Id: rst2latex.py 5905 2009-04-16 12:04:49Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing LaTeX. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline + +description = ('Generates LaTeX documents from standalone reStructuredText ' + 'sources. ' + 'Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +publish_cmdline(writer_name='latex', description=description) diff --git a/venv/bin/rst2man.py b/venv/bin/rst2man.py new file mode 100755 index 00000000..3fb7125b --- /dev/null +++ b/venv/bin/rst2man.py @@ -0,0 +1,26 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 + +# Author: +# Contact: grubert@users.sf.net +# Copyright: This module has been placed in the public domain. + +""" +man.py +====== + +This module provides a simple command line interface that uses the +man page writer to output from ReStructuredText source. +""" + +import locale +try: + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description +from docutils.writers import manpage + +description = ("Generates plain unix manual documents. " + default_description) + +publish_cmdline(writer=manpage.Writer(), description=description) diff --git a/venv/bin/rst2odt.py b/venv/bin/rst2odt.py new file mode 100755 index 00000000..4e3f9107 --- /dev/null +++ b/venv/bin/rst2odt.py @@ -0,0 +1,30 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 + +# $Id: rst2odt.py 5839 2009-01-07 19:09:28Z dkuhlman $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" +A front end to the Docutils Publisher, producing OpenOffice documents. +""" + +import sys +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline_to_binary, default_description +from docutils.writers.odf_odt import Writer, Reader + + +description = ('Generates OpenDocument/OpenOffice/ODF documents from ' + 'standalone reStructuredText sources. ' + default_description) + + +writer = Writer() +reader = Reader() +output = publish_cmdline_to_binary(reader=reader, writer=writer, + description=description) + diff --git a/venv/bin/rst2odt_prepstyles.py b/venv/bin/rst2odt_prepstyles.py new file mode 100755 index 00000000..0e108efe --- /dev/null +++ b/venv/bin/rst2odt_prepstyles.py @@ -0,0 +1,67 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 + +# $Id: rst2odt_prepstyles.py 8346 2019-08-26 12:11:32Z milde $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" +Fix a word-processor-generated styles.odt for odtwriter use: Drop page size +specifications from styles.xml in STYLE_FILE.odt. +""" + +# Author: Michael Schutte + +from __future__ import print_function + +from lxml import etree +import sys +import zipfile +from tempfile import mkstemp +import shutil +import os + +NAMESPACES = { + "style": "urn:oasis:names:tc:opendocument:xmlns:style:1.0", + "fo": "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" +} + + +def prepstyle(filename): + + zin = zipfile.ZipFile(filename) + styles = zin.read("styles.xml") + + root = etree.fromstring(styles) + for el in root.xpath("//style:page-layout-properties", + namespaces=NAMESPACES): + for attr in el.attrib: + if attr.startswith("{%s}" % NAMESPACES["fo"]): + del el.attrib[attr] + + tempname = mkstemp() + zout = zipfile.ZipFile(os.fdopen(tempname[0], "w"), "w", + zipfile.ZIP_DEFLATED) + + for item in zin.infolist(): + if item.filename == "styles.xml": + zout.writestr(item, etree.tostring(root)) + else: + zout.writestr(item, zin.read(item.filename)) + + zout.close() + zin.close() + shutil.move(tempname[1], filename) + + +def main(): + args = sys.argv[1:] + if len(args) != 1: + print(__doc__, file=sys.stderr) + print("Usage: %s STYLE_FILE.odt\n" % sys.argv[0], file=sys.stderr) + sys.exit(1) + filename = args[0] + prepstyle(filename) + + +if __name__ == '__main__': + main() diff --git a/venv/bin/rst2pseudoxml.py b/venv/bin/rst2pseudoxml.py new file mode 100755 index 00000000..6dfd7ab2 --- /dev/null +++ b/venv/bin/rst2pseudoxml.py @@ -0,0 +1,23 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 + +# $Id: rst2pseudoxml.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing pseudo-XML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates pseudo-XML from standalone reStructuredText ' + 'sources (for testing purposes). ' + default_description) + +publish_cmdline(description=description) diff --git a/venv/bin/rst2s5.py b/venv/bin/rst2s5.py new file mode 100755 index 00000000..34ef5384 --- /dev/null +++ b/venv/bin/rst2s5.py @@ -0,0 +1,24 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 + +# $Id: rst2s5.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Chris Liechti +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML slides using +the S5 template system. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates S5 (X)HTML slideshow documents from standalone ' + 'reStructuredText sources. ' + default_description) + +publish_cmdline(writer_name='s5', description=description) diff --git a/venv/bin/rst2xetex.py b/venv/bin/rst2xetex.py new file mode 100755 index 00000000..326d9136 --- /dev/null +++ b/venv/bin/rst2xetex.py @@ -0,0 +1,27 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 + +# $Id: rst2xetex.py 7847 2015-03-17 17:30:47Z milde $ +# Author: Guenter Milde +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing Lua/XeLaTeX code. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline + +description = ('Generates LaTeX documents from standalone reStructuredText ' + 'sources for compilation with the Unicode-aware TeX variants ' + 'XeLaTeX or LuaLaTeX. ' + 'Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +publish_cmdline(writer_name='xetex', description=description) diff --git a/venv/bin/rst2xml.py b/venv/bin/rst2xml.py new file mode 100755 index 00000000..409e8671 --- /dev/null +++ b/venv/bin/rst2xml.py @@ -0,0 +1,23 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 + +# $Id: rst2xml.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing Docutils XML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates Docutils-native XML from standalone ' + 'reStructuredText sources. ' + default_description) + +publish_cmdline(writer_name='xml', description=description) diff --git a/venv/bin/rstpep2html.py b/venv/bin/rstpep2html.py new file mode 100755 index 00000000..63c9a4ea --- /dev/null +++ b/venv/bin/rstpep2html.py @@ -0,0 +1,25 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 + +# $Id: rstpep2html.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML from PEP +(Python Enhancement Proposal) documents. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML from reStructuredText-format PEP files. ' + + default_description) + +publish_cmdline(reader_name='pep', writer_name='pep_html', + description=description) diff --git a/venv/bin/unidecode b/venv/bin/unidecode new file mode 100755 index 00000000..a591b8cf --- /dev/null +++ b/venv/bin/unidecode @@ -0,0 +1,8 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from unidecode.util import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/watchfiles b/venv/bin/watchfiles new file mode 100755 index 00000000..f37226ee --- /dev/null +++ b/venv/bin/watchfiles @@ -0,0 +1,8 @@ +#!/home/simon/Documents/Work/Varia/varia.website/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from watchfiles.cli import cli +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(cli()) diff --git a/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/INSTALLER b/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/LICENSE.rst b/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/LICENSE.rst new file mode 100644 index 00000000..c37cae49 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/METADATA b/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/METADATA new file mode 100644 index 00000000..1af8df0f --- /dev/null +++ b/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/METADATA @@ -0,0 +1,106 @@ +Metadata-Version: 2.1 +Name: Jinja2 +Version: 2.11.3 +Summary: A very fast and expressive template engine. +Home-page: https://palletsprojects.com/p/jinja/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Code, https://github.com/pallets/jinja +Project-URL: Issue tracker, https://github.com/pallets/jinja/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +Description-Content-Type: text/x-rst +Requires-Dist: MarkupSafe (>=0.23) +Provides-Extra: i18n +Requires-Dist: Babel (>=0.8) ; extra == 'i18n' + +Jinja +===== + +Jinja is a fast, expressive, extensible templating engine. Special +placeholders in the template allow writing code similar to Python +syntax. Then the template is passed data to render the final document. + +It includes: + +- Template inheritance and inclusion. +- Define and import macros within templates. +- HTML templates can use autoescaping to prevent XSS from untrusted + user input. +- A sandboxed environment can safely render untrusted templates. +- AsyncIO support for generating templates and calling async + functions. +- I18N support with Babel. +- Templates are compiled to optimized Python code just-in-time and + cached, or can be compiled ahead-of-time. +- Exceptions point to the correct line in templates to make debugging + easier. +- Extensible filters, tests, functions, and even syntax. + +Jinja's philosophy is that while application logic belongs in Python if +possible, it shouldn't make the template designer's job difficult by +restricting functionality too much. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Jinja2 + +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + +In A Nutshell +------------- + +.. code-block:: jinja + + {% extends "base.html" %} + {% block title %}Members{% endblock %} + {% block content %} + + {% endblock %} + + +Links +----- + +- Website: https://palletsprojects.com/p/jinja/ +- Documentation: https://jinja.palletsprojects.com/ +- Releases: https://pypi.org/project/Jinja2/ +- Code: https://github.com/pallets/jinja +- Issue tracker: https://github.com/pallets/jinja/issues +- Test status: https://dev.azure.com/pallets/jinja/_build +- Official chat: https://discord.gg/t6rrQZH + + diff --git a/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/RECORD b/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/RECORD new file mode 100644 index 00000000..299821a7 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/RECORD @@ -0,0 +1,61 @@ +Jinja2-2.11.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Jinja2-2.11.3.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Jinja2-2.11.3.dist-info/METADATA,sha256=PscpJ1C3RSp8xcjV3fAuTz13rKbGxmzJXnMQFH-WKhs,3535 +Jinja2-2.11.3.dist-info/RECORD,, +Jinja2-2.11.3.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 +Jinja2-2.11.3.dist-info/entry_points.txt,sha256=Qy_DkVo6Xj_zzOtmErrATe8lHZhOqdjpt3e4JJAGyi8,61 +Jinja2-2.11.3.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7 +jinja2/__init__.py,sha256=LZUXmxJc2GIchfSAeMWsxCWiQYO-w1-736f2Q3I8ms8,1549 +jinja2/__pycache__/__init__.cpython-311.pyc,, +jinja2/__pycache__/_compat.cpython-311.pyc,, +jinja2/__pycache__/_identifier.cpython-311.pyc,, +jinja2/__pycache__/asyncfilters.cpython-311.pyc,, +jinja2/__pycache__/asyncsupport.cpython-311.pyc,, +jinja2/__pycache__/bccache.cpython-311.pyc,, +jinja2/__pycache__/compiler.cpython-311.pyc,, +jinja2/__pycache__/constants.cpython-311.pyc,, +jinja2/__pycache__/debug.cpython-311.pyc,, +jinja2/__pycache__/defaults.cpython-311.pyc,, +jinja2/__pycache__/environment.cpython-311.pyc,, +jinja2/__pycache__/exceptions.cpython-311.pyc,, +jinja2/__pycache__/ext.cpython-311.pyc,, +jinja2/__pycache__/filters.cpython-311.pyc,, +jinja2/__pycache__/idtracking.cpython-311.pyc,, +jinja2/__pycache__/lexer.cpython-311.pyc,, +jinja2/__pycache__/loaders.cpython-311.pyc,, +jinja2/__pycache__/meta.cpython-311.pyc,, +jinja2/__pycache__/nativetypes.cpython-311.pyc,, +jinja2/__pycache__/nodes.cpython-311.pyc,, +jinja2/__pycache__/optimizer.cpython-311.pyc,, +jinja2/__pycache__/parser.cpython-311.pyc,, +jinja2/__pycache__/runtime.cpython-311.pyc,, +jinja2/__pycache__/sandbox.cpython-311.pyc,, +jinja2/__pycache__/tests.cpython-311.pyc,, +jinja2/__pycache__/utils.cpython-311.pyc,, +jinja2/__pycache__/visitor.cpython-311.pyc,, +jinja2/_compat.py,sha256=B6Se8HjnXVpzz9-vfHejn-DV2NjaVK-Iewupc5kKlu8,3191 +jinja2/_identifier.py,sha256=EdgGJKi7O1yvr4yFlvqPNEqV6M1qHyQr8Gt8GmVTKVM,1775 +jinja2/asyncfilters.py,sha256=XJtYXTxFvcJ5xwk6SaDL4S0oNnT0wPYvXBCSzc482fI,4250 +jinja2/asyncsupport.py,sha256=ZBFsDLuq3Gtji3Ia87lcyuDbqaHZJRdtShZcqwpFnSQ,7209 +jinja2/bccache.py,sha256=3Pmp4jo65M9FQuIxdxoDBbEDFwe4acDMQf77nEJfrHA,12139 +jinja2/compiler.py,sha256=Ta9W1Lit542wItAHXlDcg0sEOsFDMirCdlFPHAurg4o,66284 +jinja2/constants.py,sha256=RR1sTzNzUmKco6aZicw4JpQpJGCuPuqm1h1YmCNUEFY,1458 +jinja2/debug.py,sha256=neR7GIGGjZH3_ILJGVUYy3eLQCCaWJMXOb7o0kGInWc,8529 +jinja2/defaults.py,sha256=85B6YUUCyWPSdrSeVhcqFVuu_bHUAQXeey--FIwSeVQ,1126 +jinja2/environment.py,sha256=XDSLKc4SqNLMOwTSq3TbWEyA5WyXfuLuVD0wAVjEFwM,50629 +jinja2/exceptions.py,sha256=VjNLawcmf2ODffqVMCQK1cRmvFaUfQWF4u8ouP3QPcE,5425 +jinja2/ext.py,sha256=AtwL5O5enT_L3HR9-oBvhGyUTdGoyaqG_ICtnR_EVd4,26441 +jinja2/filters.py,sha256=9ORilsZrUoydSI9upz8_qGy7gozDWLYoFmlIBFSVRnQ,41439 +jinja2/idtracking.py,sha256=J3O4VHsrbf3wzwiBc7Cro26kHb6_5kbULeIOzocchIU,9211 +jinja2/lexer.py,sha256=nUFLRKhhKmmEWkLI65nQePgcQs7qsRdjVYZETMt_v0g,30331 +jinja2/loaders.py,sha256=C-fST_dmFjgWkp0ZuCkrgICAoOsoSIF28wfAFink0oU,17666 +jinja2/meta.py,sha256=QjyYhfNRD3QCXjBJpiPl9KgkEkGXJbAkCUq4-Ur10EQ,4131 +jinja2/nativetypes.py,sha256=Ul__gtVw4xH-0qvUvnCNHedQeNDwmEuyLJztzzSPeRg,2753 +jinja2/nodes.py,sha256=Mk1oJPVgIjnQw9WOqILvcu3rLepcFZ0ahxQm2mbwDwc,31095 +jinja2/optimizer.py,sha256=gQLlMYzvQhluhzmAIFA1tXS0cwgWYOjprN-gTRcHVsc,1457 +jinja2/parser.py,sha256=fcfdqePNTNyvosIvczbytVA332qpsURvYnCGcjDHSkA,35660 +jinja2/runtime.py,sha256=0y-BRyIEZ9ltByL2Id6GpHe1oDRQAwNeQvI0SKobNMw,30618 +jinja2/sandbox.py,sha256=knayyUvXsZ-F0mk15mO2-ehK9gsw04UhB8td-iUOtLc,17127 +jinja2/tests.py,sha256=iO_Y-9Vo60zrVe1lMpSl5sKHqAxe2leZHC08OoZ8K24,4799 +jinja2/utils.py,sha256=Wy4yC3IByqUWwnKln6SdaixdzgK74P6F5nf-gQZrYnU,22436 +jinja2/visitor.py,sha256=DUHupl0a4PGp7nxRtZFttUzAi1ccxzqc2hzetPYUz8U,3240 diff --git a/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/WHEEL b/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/WHEEL new file mode 100644 index 00000000..01b8fc7d --- /dev/null +++ b/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/entry_points.txt b/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/entry_points.txt new file mode 100644 index 00000000..3619483f --- /dev/null +++ b/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[babel.extractors] +jinja2 = jinja2.ext:babel_extract [i18n] + diff --git a/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/top_level.txt b/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/top_level.txt new file mode 100644 index 00000000..7f7afbf3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Jinja2-2.11.3.dist-info/top_level.txt @@ -0,0 +1 @@ +jinja2 diff --git a/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/INSTALLER b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/LICENSE.md b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/LICENSE.md new file mode 100644 index 00000000..2652d97a --- /dev/null +++ b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/LICENSE.md @@ -0,0 +1,29 @@ +Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of the Python Markdown Project nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE PYTHON MARKDOWN PROJECT ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTORS TO THE PYTHON MARKDOWN PROJECT +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/METADATA b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/METADATA new file mode 100644 index 00000000..39895cc3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/METADATA @@ -0,0 +1,98 @@ +Metadata-Version: 2.1 +Name: Markdown +Version: 3.3.3 +Summary: Python implementation of Markdown. +Home-page: https://Python-Markdown.github.io/ +Author: Manfred Stienstra, Yuri takhteyev and Waylan limberg +Author-email: waylan.limberg@icloud.com +Maintainer: Waylan Limberg +Maintainer-email: waylan.limberg@icloud.com +License: BSD License +Download-URL: http://pypi.python.org/packages/source/M/Markdown/Markdown-3.3.3-py2.py3-none-any.whl +Project-URL: Documentation, https://Python-Markdown.github.io/ +Project-URL: GitHub Project, https://github.com/Python-Markdown/markdown +Project-URL: Issue Tracker, https://github.com/Python-Markdown/markdown/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Communications :: Email :: Filters +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries +Classifier: Topic :: Internet :: WWW/HTTP :: Site Management +Classifier: Topic :: Software Development :: Documentation +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Filters +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Topic :: Text Processing :: Markup :: Markdown +Requires-Python: >=3.6 +Description-Content-Type: text/markdown +Requires-Dist: importlib-metadata ; python_version < "3.8" +Provides-Extra: testing +Requires-Dist: coverage ; extra == 'testing' +Requires-Dist: pyyaml ; extra == 'testing' + +[Python-Markdown][] +=================== + +[![Build Status][build-button]][build] +[![Coverage Status][codecov-button]][codecov] +[![Latest Version][mdversion-button]][md-pypi] +[![Python Versions][pyversion-button]][md-pypi] +[![BSD License][bsdlicense-button]][bsdlicense] +[![Code of Conduct][codeofconduct-button]][Code of Conduct] + +[build-button]: https://github.com/Python-Markdown/markdown/workflows/CI/badge.svg?event=push +[build]: https://github.com/Python-Markdown/markdown/actions?query=workflow%3ACI+event%3Apush +[codecov-button]: https://codecov.io/gh/Python-Markdown/markdown/branch/master/graph/badge.svg +[codecov]: https://codecov.io/gh/Python-Markdown/markdown +[mdversion-button]: https://img.shields.io/pypi/v/Markdown.svg +[md-pypi]: https://pypi.org/project/Markdown/ +[pyversion-button]: https://img.shields.io/pypi/pyversions/Markdown.svg +[bsdlicense-button]: https://img.shields.io/badge/license-BSD-yellow.svg +[bsdlicense]: https://opensource.org/licenses/BSD-3-Clause +[codeofconduct-button]: https://img.shields.io/badge/code%20of%20conduct-contributor%20covenant-green.svg?style=flat-square +[Code of Conduct]: https://github.com/Python-Markdown/markdown/blob/master/CODE_OF_CONDUCT.md + +This is a Python implementation of John Gruber's [Markdown][]. +It is almost completely compliant with the reference implementation, +though there are a few known issues. See [Features][] for information +on what exactly is supported and what is not. Additional features are +supported by the [Available Extensions][]. + +[Python-Markdown]: https://Python-Markdown.github.io/ +[Markdown]: https://daringfireball.net/projects/markdown/ +[Features]: https://Python-Markdown.github.io#Features +[Available Extensions]: https://Python-Markdown.github.io/extensions + +Documentation +------------- + +Installation and usage documentation is available in the `docs/` directory +of the distribution and on the project website at +. + +See the change log at . + +Support +------- + +You may report bugs, ask for help, and discuss various other issues on the [bug tracker][]. + +[bug tracker]: https://github.com/Python-Markdown/markdown/issues + +Code of Conduct +--------------- + +Everyone interacting in the Python-Markdown project's codebases, issue trackers, +and mailing lists is expected to follow the [Code of Conduct]. + + diff --git a/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/RECORD b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/RECORD new file mode 100644 index 00000000..6b1fd1ea --- /dev/null +++ b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/RECORD @@ -0,0 +1,77 @@ +../../../bin/markdown_py,sha256=icHwfLiy95guxpA9KEZf6hKWeFf7XDbdpNSFvUNX8VA,261 +Markdown-3.3.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Markdown-3.3.3.dist-info/LICENSE.md,sha256=bxGTy2NHGOZcOlN9biXr1hSCDsDvaTz8EiSBEmONZNo,1645 +Markdown-3.3.3.dist-info/METADATA,sha256=WHalEShUS2T2w0axy9BTbQBrS03UkpFE3vE0Ew4g_dU,4416 +Markdown-3.3.3.dist-info/RECORD,, +Markdown-3.3.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +Markdown-3.3.3.dist-info/WHEEL,sha256=EVRjI69F5qVjm_YgqcTXPnTAv3BfSUr0WVAHuSP3Xoo,92 +Markdown-3.3.3.dist-info/entry_points.txt,sha256=j4jiKg-iwZGImvi8OzotZePWoFbJJ4GrfzDqH03u3SQ,1103 +Markdown-3.3.3.dist-info/top_level.txt,sha256=IAxs8x618RXoH1uCqeLLxXsDefJvE_mIibr_M4sOlyk,9 +markdown/__init__.py,sha256=002-LuHviYzROW2rg_gBGai81nMouUNO9UFj5nSsTSk,2065 +markdown/__main__.py,sha256=MpVK3zlwQ-4AzDzZmIScPB90PpunMGVgS5KBmJuHYTw,5802 +markdown/__meta__.py,sha256=7JKFgvYGgDfauq4ZK8nEMnIAbVKOg64uR7D-xt14fM0,1630 +markdown/__pycache__/__init__.cpython-311.pyc,, +markdown/__pycache__/__main__.cpython-311.pyc,, +markdown/__pycache__/__meta__.cpython-311.pyc,, +markdown/__pycache__/blockparser.cpython-311.pyc,, +markdown/__pycache__/blockprocessors.cpython-311.pyc,, +markdown/__pycache__/core.cpython-311.pyc,, +markdown/__pycache__/htmlparser.cpython-311.pyc,, +markdown/__pycache__/inlinepatterns.cpython-311.pyc,, +markdown/__pycache__/pep562.cpython-311.pyc,, +markdown/__pycache__/postprocessors.cpython-311.pyc,, +markdown/__pycache__/preprocessors.cpython-311.pyc,, +markdown/__pycache__/serializers.cpython-311.pyc,, +markdown/__pycache__/test_tools.cpython-311.pyc,, +markdown/__pycache__/treeprocessors.cpython-311.pyc,, +markdown/__pycache__/util.cpython-311.pyc,, +markdown/blockparser.py,sha256=JpBhOokOoBUGCXolftOc5m1hPcR2y9s9hVd9WSuhHzo,4285 +markdown/blockprocessors.py,sha256=U7IjfwJ4WWldfWW4Ia1epEWJKk_7g1B6GqJzB8tN4MU,24821 +markdown/core.py,sha256=4ASA3Wvs4rgk6HBiT3ScKnIyx3oVz86_-5kGe5Vs5nU,15390 +markdown/extensions/__init__.py,sha256=nw2VtafIf5zHjAcUuykQbaNY6taOmNn7ARn11-Pe080,3661 +markdown/extensions/__pycache__/__init__.cpython-311.pyc,, +markdown/extensions/__pycache__/abbr.cpython-311.pyc,, +markdown/extensions/__pycache__/admonition.cpython-311.pyc,, +markdown/extensions/__pycache__/attr_list.cpython-311.pyc,, +markdown/extensions/__pycache__/codehilite.cpython-311.pyc,, +markdown/extensions/__pycache__/def_list.cpython-311.pyc,, +markdown/extensions/__pycache__/extra.cpython-311.pyc,, +markdown/extensions/__pycache__/fenced_code.cpython-311.pyc,, +markdown/extensions/__pycache__/footnotes.cpython-311.pyc,, +markdown/extensions/__pycache__/legacy_attrs.cpython-311.pyc,, +markdown/extensions/__pycache__/legacy_em.cpython-311.pyc,, +markdown/extensions/__pycache__/md_in_html.cpython-311.pyc,, +markdown/extensions/__pycache__/meta.cpython-311.pyc,, +markdown/extensions/__pycache__/nl2br.cpython-311.pyc,, +markdown/extensions/__pycache__/sane_lists.cpython-311.pyc,, +markdown/extensions/__pycache__/smarty.cpython-311.pyc,, +markdown/extensions/__pycache__/tables.cpython-311.pyc,, +markdown/extensions/__pycache__/toc.cpython-311.pyc,, +markdown/extensions/__pycache__/wikilinks.cpython-311.pyc,, +markdown/extensions/abbr.py,sha256=5TNU5ml6-H1n-fztEkgUphSTvp5yKCXaiPZMrVuRFvo,3186 +markdown/extensions/admonition.py,sha256=Zn53VydoDM4HtCZdVKT2Zf0Kp3Dbt6E6FJiyU-tCRvc,5534 +markdown/extensions/attr_list.py,sha256=nhKFY_u6BVyKW2oMUeC4wEjqFNGpDSnNXqaohuF6M7I,5988 +markdown/extensions/codehilite.py,sha256=AVbP0Ze37v0FRmySXcsJWmGzaulKdL8w_g-HS1gr4Ag,11628 +markdown/extensions/def_list.py,sha256=p-JT64hKqMkfxlmhETMVRPxjrdnBIPDW8k3S05S-qNM,3634 +markdown/extensions/extra.py,sha256=udRN8OvSWcq3UwkPygvsFl1RlCVtCJ-ARVg2IwVH6VY,1831 +markdown/extensions/fenced_code.py,sha256=rrqPzFvxeVJpitoIXy0qMkNe53gJ0PWHoLZS44yIfeM,7305 +markdown/extensions/footnotes.py,sha256=xvT6etWuTWTHLNHXYQWQGV-35RHTCvH9kBp2xJA6Jdg,15481 +markdown/extensions/legacy_attrs.py,sha256=2EaVQkxQoNnP8_lMPvGRBdNda8L4weUQroiyEuVdS-w,2547 +markdown/extensions/legacy_em.py,sha256=18j4L6zdScy9k18y-U2zaIhYsKVTxCaPurjqLFZmWkI,1582 +markdown/extensions/md_in_html.py,sha256=XD8Mui_u2c5qUyAtkSMyiH9nSB4-t4ACrf67x7HR6Mk,14521 +markdown/extensions/meta.py,sha256=EUfkzM7l7UpH__Or9K3pl8ldVddwndlCZWA3d712RAE,2331 +markdown/extensions/nl2br.py,sha256=wAqTNOuf2L1NzlEvEqoID70n9y-aiYaGLkuyQk3CD0w,783 +markdown/extensions/sane_lists.py,sha256=ZQmCf-247KBexVG0fc62nDvokGkV6W1uavYbieNKSG4,1505 +markdown/extensions/smarty.py,sha256=0padzkVCNACainKw-Xj1S5UfT0125VCTfNejmrCZItA,10238 +markdown/extensions/tables.py,sha256=bicFx_wqhnEx6Y_8MJqA56rh71pt5fOe94oiWbvcobY,7685 +markdown/extensions/toc.py,sha256=9f0QS5K2BE-Rn0RqZ8xZO8s_xX0U8fgKajx6Jl7goiE,13681 +markdown/extensions/wikilinks.py,sha256=GkgT9BY7b1-qW--dIwFAhC9V20RoeF13b7CFdw_V21Q,2812 +markdown/htmlparser.py,sha256=gRk_Ai7w7U46Oo2xbhkLdQCjRqFp5Y8wouIJBzvo0Ts,11276 +markdown/inlinepatterns.py,sha256=cZZdzEWZhVMerELC6KGlgKUi1AEZosOmw_uJNnPscRw,29762 +markdown/pep562.py,sha256=5UkqT7sb-cQufgbOl_jF-RYUVVHS7VThzlMzR9vrd3I,8917 +markdown/postprocessors.py,sha256=o5WpNIImALhyRB40k6TQeVao9eRN_8jOz4Oe7s6nYqA,3844 +markdown/preprocessors.py,sha256=-s8QGHGlX7JAIJTfCivuc-CVwTLWs0IyEU94YUT2IvQ,2742 +markdown/serializers.py,sha256=_wQl-iJrPSUEQ4Q1owWYqN9qceVh6TOlAOH_i44BKAQ,6540 +markdown/test_tools.py,sha256=ZnDwyELUmiyYa0oQgQ31phQrWI1-X6KyRuPkPbnXan4,7750 +markdown/treeprocessors.py,sha256=gSFoKa_ec-9iPzczsOvHxlkPBvhih5AcA4Q45ZrVJeQ,15407 +markdown/util.py,sha256=CfRNpS13QDxokl7SAqmkAih6IdPoK2OcPOY8_vgUZHI,16063 diff --git a/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/REQUESTED b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/WHEEL b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/WHEEL new file mode 100644 index 00000000..83ff02e9 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.35.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/entry_points.txt b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/entry_points.txt new file mode 100644 index 00000000..f49693d4 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/entry_points.txt @@ -0,0 +1,23 @@ +[console_scripts] +markdown_py = markdown.__main__:run + +[markdown.extensions] +abbr = markdown.extensions.abbr:AbbrExtension +admonition = markdown.extensions.admonition:AdmonitionExtension +attr_list = markdown.extensions.attr_list:AttrListExtension +codehilite = markdown.extensions.codehilite:CodeHiliteExtension +def_list = markdown.extensions.def_list:DefListExtension +extra = markdown.extensions.extra:ExtraExtension +fenced_code = markdown.extensions.fenced_code:FencedCodeExtension +footnotes = markdown.extensions.footnotes:FootnoteExtension +legacy_attrs = markdown.extensions.legacy_attrs:LegacyAttrExtension +legacy_em = markdown.extensions.legacy_em:LegacyEmExtension +md_in_html = markdown.extensions.md_in_html:MarkdownInHtmlExtension +meta = markdown.extensions.meta:MetaExtension +nl2br = markdown.extensions.nl2br:Nl2BrExtension +sane_lists = markdown.extensions.sane_lists:SaneListExtension +smarty = markdown.extensions.smarty:SmartyExtension +tables = markdown.extensions.tables:TableExtension +toc = markdown.extensions.toc:TocExtension +wikilinks = markdown.extensions.wikilinks:WikiLinkExtension + diff --git a/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/top_level.txt b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/top_level.txt new file mode 100644 index 00000000..0918c976 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Markdown-3.3.3.dist-info/top_level.txt @@ -0,0 +1 @@ +markdown diff --git a/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/PKG-INFO b/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/PKG-INFO new file mode 100644 index 00000000..2d9f37c5 --- /dev/null +++ b/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/PKG-INFO @@ -0,0 +1,98 @@ +Metadata-Version: 2.1 +Name: MarkupSafe +Version: 2.0.1 +Summary: Safely add untrusted strings to HTML/XML markup. +Home-page: https://palletsprojects.com/p/markupsafe/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://markupsafe.palletsprojects.com/ +Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/markupsafe/ +Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst + +MarkupSafe +========== + +MarkupSafe implements a text object that escapes characters so it is +safe to use in HTML and XML. Characters that have special meanings are +replaced so that they display as the actual characters. This mitigates +injection attacks, meaning untrusted user input can safely be displayed +on a page. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U MarkupSafe + +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + +Examples +-------- + +.. code-block:: pycon + + >>> from markupsafe import Markup, escape + + >>> # escape replaces special characters and wraps in Markup + >>> escape("") + Markup('<script>alert(document.cookie);</script>') + + >>> # wrap in Markup to mark text "safe" and prevent escaping + >>> Markup("Hello") + Markup('hello') + + >>> escape(Markup("Hello")) + Markup('hello') + + >>> # Markup is a str subclass + >>> # methods and operators escape their arguments + >>> template = Markup("Hello {name}") + >>> template.format(name='"World"') + Markup('Hello "World"') + + +Donate +------ + +The Pallets organization develops and supports MarkupSafe and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +`please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://markupsafe.palletsprojects.com/ +- Changes: https://markupsafe.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/MarkupSafe/ +- Source Code: https://github.com/pallets/markupsafe/ +- Issue Tracker: https://github.com/pallets/markupsafe/issues/ +- Website: https://palletsprojects.com/p/markupsafe/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets diff --git a/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/SOURCES.txt b/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/SOURCES.txt new file mode 100644 index 00000000..3f8c1a14 --- /dev/null +++ b/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/SOURCES.txt @@ -0,0 +1,34 @@ +CHANGES.rst +LICENSE.rst +MANIFEST.in +README.rst +setup.cfg +setup.py +tox.ini +docs/Makefile +docs/changes.rst +docs/conf.py +docs/escaping.rst +docs/formatting.rst +docs/html.rst +docs/index.rst +docs/license.rst +docs/make.bat +requirements/dev.txt +requirements/docs.txt +requirements/tests.txt +requirements/typing.txt +src/MarkupSafe.egg-info/PKG-INFO +src/MarkupSafe.egg-info/SOURCES.txt +src/MarkupSafe.egg-info/dependency_links.txt +src/MarkupSafe.egg-info/top_level.txt +src/markupsafe/__init__.py +src/markupsafe/_native.py +src/markupsafe/_speedups.c +src/markupsafe/_speedups.pyi +src/markupsafe/py.typed +tests/conftest.py +tests/test_escape.py +tests/test_exception_custom_html.py +tests/test_leak.py +tests/test_markupsafe.py \ No newline at end of file diff --git a/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/dependency_links.txt b/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/installed-files.txt b/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/installed-files.txt new file mode 100644 index 00000000..1cceae1e --- /dev/null +++ b/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/installed-files.txt @@ -0,0 +1,12 @@ +../markupsafe/__init__.py +../markupsafe/__pycache__/__init__.cpython-311.pyc +../markupsafe/__pycache__/_native.cpython-311.pyc +../markupsafe/_native.py +../markupsafe/_speedups.c +../markupsafe/_speedups.cpython-311-x86_64-linux-gnu.so +../markupsafe/_speedups.pyi +../markupsafe/py.typed +PKG-INFO +SOURCES.txt +dependency_links.txt +top_level.txt diff --git a/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/top_level.txt b/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/top_level.txt new file mode 100644 index 00000000..75bf7292 --- /dev/null +++ b/venv/lib/python3.11/site-packages/MarkupSafe-2.0.1.egg-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/venv/lib/python3.11/site-packages/PIL/BdfFontFile.py b/venv/lib/python3.11/site-packages/PIL/BdfFontFile.py new file mode 100644 index 00000000..102b72e1 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/BdfFontFile.py @@ -0,0 +1,110 @@ +# +# The Python Imaging Library +# $Id$ +# +# bitmap distribution font (bdf) file parser +# +# history: +# 1996-05-16 fl created (as bdf2pil) +# 1997-08-25 fl converted to FontFile driver +# 2001-05-25 fl removed bogus __init__ call +# 2002-11-20 fl robustification (from Kevin Cazabon, Dmitry Vasiliev) +# 2003-04-22 fl more robustification (from Graham Dumpleton) +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1997-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +""" +Parse X Bitmap Distribution Format (BDF) +""" + + +from . import FontFile, Image + +bdf_slant = { + "R": "Roman", + "I": "Italic", + "O": "Oblique", + "RI": "Reverse Italic", + "RO": "Reverse Oblique", + "OT": "Other", +} + +bdf_spacing = {"P": "Proportional", "M": "Monospaced", "C": "Cell"} + + +def bdf_char(f): + # skip to STARTCHAR + while True: + s = f.readline() + if not s: + return None + if s[:9] == b"STARTCHAR": + break + id = s[9:].strip().decode("ascii") + + # load symbol properties + props = {} + while True: + s = f.readline() + if not s or s[:6] == b"BITMAP": + break + i = s.find(b" ") + props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii") + + # load bitmap + bitmap = [] + while True: + s = f.readline() + if not s or s[:7] == b"ENDCHAR": + break + bitmap.append(s[:-1]) + bitmap = b"".join(bitmap) + + [x, y, l, d] = [int(p) for p in props["BBX"].split()] + [dx, dy] = [int(p) for p in props["DWIDTH"].split()] + + bbox = (dx, dy), (l, -d - y, x + l, -d), (0, 0, x, y) + + try: + im = Image.frombytes("1", (x, y), bitmap, "hex", "1") + except ValueError: + # deal with zero-width characters + im = Image.new("1", (x, y)) + + return id, int(props["ENCODING"]), bbox, im + + +class BdfFontFile(FontFile.FontFile): + """Font file plugin for the X11 BDF format.""" + + def __init__(self, fp): + super().__init__() + + s = fp.readline() + if s[:13] != b"STARTFONT 2.1": + raise SyntaxError("not a valid BDF file") + + props = {} + comments = [] + + while True: + s = fp.readline() + if not s or s[:13] == b"ENDPROPERTIES": + break + i = s.find(b" ") + props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii") + if s[:i] in [b"COMMENT", b"COPYRIGHT"]: + if s.find(b"LogicalFontDescription") < 0: + comments.append(s[i + 1 : -1].decode("ascii")) + + while True: + c = bdf_char(fp) + if not c: + break + id, ch, (xy, dst, src), im = c + if 0 <= ch < len(self.glyph): + self.glyph[ch] = xy, dst, src, im diff --git a/venv/lib/python3.11/site-packages/PIL/BlpImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/BlpImagePlugin.py new file mode 100644 index 00000000..d5d7c0e0 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/BlpImagePlugin.py @@ -0,0 +1,422 @@ +""" +Blizzard Mipmap Format (.blp) +Jerome Leclanche + +The contents of this file are hereby released in the public domain (CC0) +Full text of the CC0 license: + https://creativecommons.org/publicdomain/zero/1.0/ + +BLP1 files, used mostly in Warcraft III, are not fully supported. +All types of BLP2 files used in World of Warcraft are supported. + +The BLP file structure consists of a header, up to 16 mipmaps of the +texture + +Texture sizes must be powers of two, though the two dimensions do +not have to be equal; 512x256 is valid, but 512x200 is not. +The first mipmap (mipmap #0) is the full size image; each subsequent +mipmap halves both dimensions. The final mipmap should be 1x1. + +BLP files come in many different flavours: +* JPEG-compressed (type == 0) - only supported for BLP1. +* RAW images (type == 1, encoding == 1). Each mipmap is stored as an + array of 8-bit values, one per pixel, left to right, top to bottom. + Each value is an index to the palette. +* DXT-compressed (type == 1, encoding == 2): +- DXT1 compression is used if alpha_encoding == 0. + - An additional alpha bit is used if alpha_depth == 1. + - DXT3 compression is used if alpha_encoding == 1. + - DXT5 compression is used if alpha_encoding == 7. +""" + +import struct +from io import BytesIO + +from . import Image, ImageFile + +BLP_FORMAT_JPEG = 0 + +BLP_ENCODING_UNCOMPRESSED = 1 +BLP_ENCODING_DXT = 2 +BLP_ENCODING_UNCOMPRESSED_RAW_BGRA = 3 + +BLP_ALPHA_ENCODING_DXT1 = 0 +BLP_ALPHA_ENCODING_DXT3 = 1 +BLP_ALPHA_ENCODING_DXT5 = 7 + + +def unpack_565(i): + return (((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3) + + +def decode_dxt1(data, alpha=False): + """ + input: one "row" of data (i.e. will produce 4*width pixels) + """ + + blocks = len(data) // 8 # number of blocks in row + ret = (bytearray(), bytearray(), bytearray(), bytearray()) + + for block in range(blocks): + # Decode next 8-byte block. + idx = block * 8 + color0, color1, bits = struct.unpack_from("> 2 + + a = 0xFF + if control == 0: + r, g, b = r0, g0, b0 + elif control == 1: + r, g, b = r1, g1, b1 + elif control == 2: + if color0 > color1: + r = (2 * r0 + r1) // 3 + g = (2 * g0 + g1) // 3 + b = (2 * b0 + b1) // 3 + else: + r = (r0 + r1) // 2 + g = (g0 + g1) // 2 + b = (b0 + b1) // 2 + elif control == 3: + if color0 > color1: + r = (2 * r1 + r0) // 3 + g = (2 * g1 + g0) // 3 + b = (2 * b1 + b0) // 3 + else: + r, g, b, a = 0, 0, 0, 0 + + if alpha: + ret[j].extend([r, g, b, a]) + else: + ret[j].extend([r, g, b]) + + return ret + + +def decode_dxt3(data): + """ + input: one "row" of data (i.e. will produce 4*width pixels) + """ + + blocks = len(data) // 16 # number of blocks in row + ret = (bytearray(), bytearray(), bytearray(), bytearray()) + + for block in range(blocks): + idx = block * 16 + block = data[idx : idx + 16] + # Decode next 16-byte block. + bits = struct.unpack_from("<8B", block) + color0, color1 = struct.unpack_from(">= 4 + else: + high = True + a &= 0xF + a *= 17 # We get a value between 0 and 15 + + color_code = (code >> 2 * (4 * j + i)) & 0x03 + + if color_code == 0: + r, g, b = r0, g0, b0 + elif color_code == 1: + r, g, b = r1, g1, b1 + elif color_code == 2: + r = (2 * r0 + r1) // 3 + g = (2 * g0 + g1) // 3 + b = (2 * b0 + b1) // 3 + elif color_code == 3: + r = (2 * r1 + r0) // 3 + g = (2 * g1 + g0) // 3 + b = (2 * b1 + b0) // 3 + + ret[j].extend([r, g, b, a]) + + return ret + + +def decode_dxt5(data): + """ + input: one "row" of data (i.e. will produce 4 * width pixels) + """ + + blocks = len(data) // 16 # number of blocks in row + ret = (bytearray(), bytearray(), bytearray(), bytearray()) + + for block in range(blocks): + idx = block * 16 + block = data[idx : idx + 16] + # Decode next 16-byte block. + a0, a1 = struct.unpack_from("> alphacode_index) & 0x07 + elif alphacode_index == 15: + alphacode = (alphacode2 >> 15) | ((alphacode1 << 1) & 0x06) + else: # alphacode_index >= 18 and alphacode_index <= 45 + alphacode = (alphacode1 >> (alphacode_index - 16)) & 0x07 + + if alphacode == 0: + a = a0 + elif alphacode == 1: + a = a1 + elif a0 > a1: + a = ((8 - alphacode) * a0 + (alphacode - 1) * a1) // 7 + elif alphacode == 6: + a = 0 + elif alphacode == 7: + a = 255 + else: + a = ((6 - alphacode) * a0 + (alphacode - 1) * a1) // 5 + + color_code = (code >> 2 * (4 * j + i)) & 0x03 + + if color_code == 0: + r, g, b = r0, g0, b0 + elif color_code == 1: + r, g, b = r1, g1, b1 + elif color_code == 2: + r = (2 * r0 + r1) // 3 + g = (2 * g0 + g1) // 3 + b = (2 * b0 + b1) // 3 + elif color_code == 3: + r = (2 * r1 + r0) // 3 + g = (2 * g1 + g0) // 3 + b = (2 * b1 + b0) // 3 + + ret[j].extend([r, g, b, a]) + + return ret + + +class BLPFormatError(NotImplementedError): + pass + + +class BlpImageFile(ImageFile.ImageFile): + """ + Blizzard Mipmap Format + """ + + format = "BLP" + format_description = "Blizzard Mipmap Format" + + def _open(self): + self.magic = self.fp.read(4) + self._read_blp_header() + + if self.magic == b"BLP1": + decoder = "BLP1" + self.mode = "RGB" + elif self.magic == b"BLP2": + decoder = "BLP2" + self.mode = "RGBA" if self._blp_alpha_depth else "RGB" + else: + raise BLPFormatError(f"Bad BLP magic {repr(self.magic)}") + + self.tile = [(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))] + + def _read_blp_header(self): + (self._blp_compression,) = struct.unpack(" mode, rawmode + 1: ("P", "P;1"), + 4: ("P", "P;4"), + 8: ("P", "P"), + 16: ("RGB", "BGR;15"), + 24: ("RGB", "BGR"), + 32: ("RGB", "BGRX"), +} + + +def _accept(prefix): + return prefix[:2] == b"BM" + + +def _dib_accept(prefix): + return i32(prefix) in [12, 40, 64, 108, 124] + + +# ============================================================================= +# Image plugin for the Windows BMP format. +# ============================================================================= +class BmpImageFile(ImageFile.ImageFile): + """ Image plugin for the Windows Bitmap format (BMP) """ + + # ------------------------------------------------------------- Description + format_description = "Windows Bitmap" + format = "BMP" + + # -------------------------------------------------- BMP Compression values + COMPRESSIONS = {"RAW": 0, "RLE8": 1, "RLE4": 2, "BITFIELDS": 3, "JPEG": 4, "PNG": 5} + for k, v in COMPRESSIONS.items(): + vars()[k] = v + + def _bitmap(self, header=0, offset=0): + """ Read relevant info about the BMP """ + read, seek = self.fp.read, self.fp.seek + if header: + seek(header) + file_info = {} + # read bmp header size @offset 14 (this is part of the header size) + file_info["header_size"] = i32(read(4)) + file_info["direction"] = -1 + + # -------------------- If requested, read header at a specific position + # read the rest of the bmp header, without its size + header_data = ImageFile._safe_read(self.fp, file_info["header_size"] - 4) + + # -------------------------------------------------- IBM OS/2 Bitmap v1 + # ----- This format has different offsets because of width/height types + if file_info["header_size"] == 12: + file_info["width"] = i16(header_data, 0) + file_info["height"] = i16(header_data, 2) + file_info["planes"] = i16(header_data, 4) + file_info["bits"] = i16(header_data, 6) + file_info["compression"] = self.RAW + file_info["palette_padding"] = 3 + + # --------------------------------------------- Windows Bitmap v2 to v5 + # v3, OS/2 v2, v4, v5 + elif file_info["header_size"] in (40, 64, 108, 124): + file_info["y_flip"] = header_data[7] == 0xFF + file_info["direction"] = 1 if file_info["y_flip"] else -1 + file_info["width"] = i32(header_data, 0) + file_info["height"] = ( + i32(header_data, 4) + if not file_info["y_flip"] + else 2 ** 32 - i32(header_data, 4) + ) + file_info["planes"] = i16(header_data, 8) + file_info["bits"] = i16(header_data, 10) + file_info["compression"] = i32(header_data, 12) + # byte size of pixel data + file_info["data_size"] = i32(header_data, 16) + file_info["pixels_per_meter"] = ( + i32(header_data, 20), + i32(header_data, 24), + ) + file_info["colors"] = i32(header_data, 28) + file_info["palette_padding"] = 4 + self.info["dpi"] = tuple( + int(x / 39.3701 + 0.5) for x in file_info["pixels_per_meter"] + ) + if file_info["compression"] == self.BITFIELDS: + if len(header_data) >= 52: + for idx, mask in enumerate( + ["r_mask", "g_mask", "b_mask", "a_mask"] + ): + file_info[mask] = i32(header_data, 36 + idx * 4) + else: + # 40 byte headers only have the three components in the + # bitfields masks, ref: + # https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx + # See also + # https://github.com/python-pillow/Pillow/issues/1293 + # There is a 4th component in the RGBQuad, in the alpha + # location, but it is listed as a reserved component, + # and it is not generally an alpha channel + file_info["a_mask"] = 0x0 + for mask in ["r_mask", "g_mask", "b_mask"]: + file_info[mask] = i32(read(4)) + file_info["rgb_mask"] = ( + file_info["r_mask"], + file_info["g_mask"], + file_info["b_mask"], + ) + file_info["rgba_mask"] = ( + file_info["r_mask"], + file_info["g_mask"], + file_info["b_mask"], + file_info["a_mask"], + ) + else: + raise OSError(f"Unsupported BMP header type ({file_info['header_size']})") + + # ------------------ Special case : header is reported 40, which + # ---------------------- is shorter than real size for bpp >= 16 + self._size = file_info["width"], file_info["height"] + + # ------- If color count was not found in the header, compute from bits + file_info["colors"] = ( + file_info["colors"] + if file_info.get("colors", 0) + else (1 << file_info["bits"]) + ) + + # ---------------------- Check bit depth for unusual unsupported values + self.mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None)) + if self.mode is None: + raise OSError(f"Unsupported BMP pixel depth ({file_info['bits']})") + + # ---------------- Process BMP with Bitfields compression (not palette) + if file_info["compression"] == self.BITFIELDS: + SUPPORTED = { + 32: [ + (0xFF0000, 0xFF00, 0xFF, 0x0), + (0xFF0000, 0xFF00, 0xFF, 0xFF000000), + (0xFF, 0xFF00, 0xFF0000, 0xFF000000), + (0x0, 0x0, 0x0, 0x0), + (0xFF000000, 0xFF0000, 0xFF00, 0x0), + ], + 24: [(0xFF0000, 0xFF00, 0xFF)], + 16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)], + } + MASK_MODES = { + (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX", + (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR", + (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA", + (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA", + (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", + (24, (0xFF0000, 0xFF00, 0xFF)): "BGR", + (16, (0xF800, 0x7E0, 0x1F)): "BGR;16", + (16, (0x7C00, 0x3E0, 0x1F)): "BGR;15", + } + if file_info["bits"] in SUPPORTED: + if ( + file_info["bits"] == 32 + and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]] + ): + raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])] + self.mode = "RGBA" if "A" in raw_mode else self.mode + elif ( + file_info["bits"] in (24, 16) + and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]] + ): + raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])] + else: + raise OSError("Unsupported BMP bitfields layout") + else: + raise OSError("Unsupported BMP bitfields layout") + elif file_info["compression"] == self.RAW: + if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset + raw_mode, self.mode = "BGRA", "RGBA" + else: + raise OSError(f"Unsupported BMP compression ({file_info['compression']})") + + # --------------- Once the header is processed, process the palette/LUT + if self.mode == "P": # Paletted for 1, 4 and 8 bit images + + # ---------------------------------------------------- 1-bit images + if not (0 < file_info["colors"] <= 65536): + raise OSError(f"Unsupported BMP Palette size ({file_info['colors']})") + else: + padding = file_info["palette_padding"] + palette = read(padding * file_info["colors"]) + greyscale = True + indices = ( + (0, 255) + if file_info["colors"] == 2 + else list(range(file_info["colors"])) + ) + + # ----------------- Check if greyscale and ignore palette if so + for ind, val in enumerate(indices): + rgb = palette[ind * padding : ind * padding + 3] + if rgb != o8(val) * 3: + greyscale = False + + # ------- If all colors are grey, white or black, ditch palette + if greyscale: + self.mode = "1" if file_info["colors"] == 2 else "L" + raw_mode = self.mode + else: + self.mode = "P" + self.palette = ImagePalette.raw( + "BGRX" if padding == 4 else "BGR", palette + ) + + # ---------------------------- Finally set the tile data for the plugin + self.info["compression"] = file_info["compression"] + self.tile = [ + ( + "raw", + (0, 0, file_info["width"], file_info["height"]), + offset or self.fp.tell(), + ( + raw_mode, + ((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3), + file_info["direction"], + ), + ) + ] + + def _open(self): + """ Open file, check magic number and read header """ + # read 14 bytes: magic number, filesize, reserved, header final offset + head_data = self.fp.read(14) + # choke if the file does not have the required magic bytes + if not _accept(head_data): + raise SyntaxError("Not a BMP file") + # read the start position of the BMP image data (u32) + offset = i32(head_data, 10) + # load bitmap information (offset=raster info) + self._bitmap(offset=offset) + + +# ============================================================================= +# Image plugin for the DIB format (BMP alias) +# ============================================================================= +class DibImageFile(BmpImageFile): + + format = "DIB" + format_description = "Windows Bitmap" + + def _open(self): + self._bitmap() + + +# +# -------------------------------------------------------------------- +# Write BMP file + + +SAVE = { + "1": ("1", 1, 2), + "L": ("L", 8, 256), + "P": ("P", 8, 256), + "RGB": ("BGR", 24, 0), + "RGBA": ("BGRA", 32, 0), +} + + +def _dib_save(im, fp, filename): + _save(im, fp, filename, False) + + +def _save(im, fp, filename, bitmap_header=True): + try: + rawmode, bits, colors = SAVE[im.mode] + except KeyError as e: + raise OSError(f"cannot write mode {im.mode} as BMP") from e + + info = im.encoderinfo + + dpi = info.get("dpi", (96, 96)) + + # 1 meter == 39.3701 inches + ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi)) + + stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3) + header = 40 # or 64 for OS/2 version 2 + image = stride * im.size[1] + + # bitmap header + if bitmap_header: + offset = 14 + header + colors * 4 + file_size = offset + image + if file_size > 2 ** 32 - 1: + raise ValueError("File size is too large for the BMP format") + fp.write( + b"BM" # file type (magic) + + o32(file_size) # file size + + o32(0) # reserved + + o32(offset) # image data offset + ) + + # bitmap info header + fp.write( + o32(header) # info header size + + o32(im.size[0]) # width + + o32(im.size[1]) # height + + o16(1) # planes + + o16(bits) # depth + + o32(0) # compression (0=uncompressed) + + o32(image) # size of bitmap + + o32(ppm[0]) # resolution + + o32(ppm[1]) # resolution + + o32(colors) # colors used + + o32(colors) # colors important + ) + + fp.write(b"\0" * (header - 40)) # padding (for OS/2 format) + + if im.mode == "1": + for i in (0, 255): + fp.write(o8(i) * 4) + elif im.mode == "L": + for i in range(256): + fp.write(o8(i) * 4) + elif im.mode == "P": + fp.write(im.im.getpalette("RGB", "BGRX")) + + ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, stride, -1))]) + + +# +# -------------------------------------------------------------------- +# Registry + + +Image.register_open(BmpImageFile.format, BmpImageFile, _accept) +Image.register_save(BmpImageFile.format, _save) + +Image.register_extension(BmpImageFile.format, ".bmp") + +Image.register_mime(BmpImageFile.format, "image/bmp") + +Image.register_open(DibImageFile.format, DibImageFile, _dib_accept) +Image.register_save(DibImageFile.format, _dib_save) + +Image.register_extension(DibImageFile.format, ".dib") + +Image.register_mime(DibImageFile.format, "image/bmp") diff --git a/venv/lib/python3.11/site-packages/PIL/BufrStubImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/BufrStubImagePlugin.py new file mode 100644 index 00000000..48f21e1b --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/BufrStubImagePlugin.py @@ -0,0 +1,73 @@ +# +# The Python Imaging Library +# $Id$ +# +# BUFR stub adapter +# +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from . import Image, ImageFile + +_handler = None + + +def register_handler(handler): + """ + Install application-specific BUFR image handler. + + :param handler: Handler object. + """ + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + + +def _accept(prefix): + return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC" + + +class BufrStubImageFile(ImageFile.StubImageFile): + + format = "BUFR" + format_description = "BUFR" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(4)): + raise SyntaxError("Not a BUFR file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self._size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise OSError("BUFR save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(BufrStubImageFile.format, BufrStubImageFile, _accept) +Image.register_save(BufrStubImageFile.format, _save) + +Image.register_extension(BufrStubImageFile.format, ".bufr") diff --git a/venv/lib/python3.11/site-packages/PIL/ContainerIO.py b/venv/lib/python3.11/site-packages/PIL/ContainerIO.py new file mode 100644 index 00000000..45e80b39 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ContainerIO.py @@ -0,0 +1,120 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a class to read from a container file +# +# History: +# 1995-06-18 fl Created +# 1995-09-07 fl Added readline(), readlines() +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1995 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +import io + + +class ContainerIO: + """ + A file object that provides read access to a part of an existing + file (for example a TAR file). + """ + + def __init__(self, file, offset, length): + """ + Create file object. + + :param file: Existing file. + :param offset: Start of region, in bytes. + :param length: Size of region, in bytes. + """ + self.fh = file + self.pos = 0 + self.offset = offset + self.length = length + self.fh.seek(offset) + + ## + # Always false. + + def isatty(self): + return False + + def seek(self, offset, mode=io.SEEK_SET): + """ + Move file pointer. + + :param offset: Offset in bytes. + :param mode: Starting position. Use 0 for beginning of region, 1 + for current offset, and 2 for end of region. You cannot move + the pointer outside the defined region. + """ + if mode == 1: + self.pos = self.pos + offset + elif mode == 2: + self.pos = self.length + offset + else: + self.pos = offset + # clamp + self.pos = max(0, min(self.pos, self.length)) + self.fh.seek(self.offset + self.pos) + + def tell(self): + """ + Get current file pointer. + + :returns: Offset from start of region, in bytes. + """ + return self.pos + + def read(self, n=0): + """ + Read data. + + :param n: Number of bytes to read. If omitted or zero, + read until end of region. + :returns: An 8-bit string. + """ + if n: + n = min(n, self.length - self.pos) + else: + n = self.length - self.pos + if not n: # EOF + return b"" if "b" in self.fh.mode else "" + self.pos = self.pos + n + return self.fh.read(n) + + def readline(self): + """ + Read a line of text. + + :returns: An 8-bit string. + """ + s = b"" if "b" in self.fh.mode else "" + newline_character = b"\n" if "b" in self.fh.mode else "\n" + while True: + c = self.read(1) + if not c: + break + s = s + c + if c == newline_character: + break + return s + + def readlines(self): + """ + Read multiple lines of text. + + :returns: A list of 8-bit strings. + """ + lines = [] + while True: + s = self.readline() + if not s: + break + lines.append(s) + return lines diff --git a/venv/lib/python3.11/site-packages/PIL/CurImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/CurImagePlugin.py new file mode 100644 index 00000000..42af5caf --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/CurImagePlugin.py @@ -0,0 +1,75 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Windows Cursor support for PIL +# +# notes: +# uses BmpImagePlugin.py to read the bitmap data. +# +# history: +# 96-05-27 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# +from . import BmpImagePlugin, Image +from ._binary import i16le as i16 +from ._binary import i32le as i32 + +# +# -------------------------------------------------------------------- + + +def _accept(prefix): + return prefix[:4] == b"\0\0\2\0" + + +## +# Image plugin for Windows Cursor files. + + +class CurImageFile(BmpImagePlugin.BmpImageFile): + + format = "CUR" + format_description = "Windows Cursor" + + def _open(self): + + offset = self.fp.tell() + + # check magic + s = self.fp.read(6) + if not _accept(s): + raise SyntaxError("not a CUR file") + + # pick the largest cursor in the file + m = b"" + for i in range(i16(s, 4)): + s = self.fp.read(16) + if not m: + m = s + elif s[0] > m[0] and s[1] > m[1]: + m = s + if not m: + raise TypeError("No cursors were found") + + # load as bitmap + self._bitmap(i32(m, 12) + offset) + + # patch up the bitmap height + self._size = self.size[0], self.size[1] // 2 + d, e, o, a = self.tile[0] + self.tile[0] = d, (0, 0) + self.size, o, a + + return + + +# +# -------------------------------------------------------------------- + +Image.register_open(CurImageFile.format, CurImageFile, _accept) + +Image.register_extension(CurImageFile.format, ".cur") diff --git a/venv/lib/python3.11/site-packages/PIL/DcxImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/DcxImagePlugin.py new file mode 100644 index 00000000..de21db8f --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/DcxImagePlugin.py @@ -0,0 +1,89 @@ +# +# The Python Imaging Library. +# $Id$ +# +# DCX file handling +# +# DCX is a container file format defined by Intel, commonly used +# for fax applications. Each DCX file consists of a directory +# (a list of file offsets) followed by a set of (usually 1-bit) +# PCX files. +# +# History: +# 1995-09-09 fl Created +# 1996-03-20 fl Properly derived from PcxImageFile. +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 2002-07-30 fl Fixed file handling +# +# Copyright (c) 1997-98 by Secret Labs AB. +# Copyright (c) 1995-96 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +from . import Image +from ._binary import i32le as i32 +from .PcxImagePlugin import PcxImageFile + +MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? + + +def _accept(prefix): + return len(prefix) >= 4 and i32(prefix) == MAGIC + + +## +# Image plugin for the Intel DCX format. + + +class DcxImageFile(PcxImageFile): + + format = "DCX" + format_description = "Intel DCX" + _close_exclusive_fp_after_loading = False + + def _open(self): + + # Header + s = self.fp.read(4) + if not _accept(s): + raise SyntaxError("not a DCX file") + + # Component directory + self._offset = [] + for i in range(1024): + offset = i32(self.fp.read(4)) + if not offset: + break + self._offset.append(offset) + + self.__fp = self.fp + self.frame = None + self.n_frames = len(self._offset) + self.is_animated = self.n_frames > 1 + self.seek(0) + + def seek(self, frame): + if not self._seek_check(frame): + return + self.frame = frame + self.fp = self.__fp + self.fp.seek(self._offset[frame]) + PcxImageFile._open(self) + + def tell(self): + return self.frame + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +Image.register_open(DcxImageFile.format, DcxImageFile, _accept) + +Image.register_extension(DcxImageFile.format, ".dcx") diff --git a/venv/lib/python3.11/site-packages/PIL/DdsImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/DdsImagePlugin.py new file mode 100644 index 00000000..df2d0060 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/DdsImagePlugin.py @@ -0,0 +1,190 @@ +""" +A Pillow loader for .dds files (S3TC-compressed aka DXTC) +Jerome Leclanche + +Documentation: + https://web.archive.org/web/20170802060935/http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_compression_s3tc.txt + +The contents of this file are hereby released in the public domain (CC0) +Full text of the CC0 license: + https://creativecommons.org/publicdomain/zero/1.0/ +""" + +import struct +from io import BytesIO + +from . import Image, ImageFile + +# Magic ("DDS ") +DDS_MAGIC = 0x20534444 + +# DDS flags +DDSD_CAPS = 0x1 +DDSD_HEIGHT = 0x2 +DDSD_WIDTH = 0x4 +DDSD_PITCH = 0x8 +DDSD_PIXELFORMAT = 0x1000 +DDSD_MIPMAPCOUNT = 0x20000 +DDSD_LINEARSIZE = 0x80000 +DDSD_DEPTH = 0x800000 + +# DDS caps +DDSCAPS_COMPLEX = 0x8 +DDSCAPS_TEXTURE = 0x1000 +DDSCAPS_MIPMAP = 0x400000 + +DDSCAPS2_CUBEMAP = 0x200 +DDSCAPS2_CUBEMAP_POSITIVEX = 0x400 +DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800 +DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000 +DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000 +DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000 +DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000 +DDSCAPS2_VOLUME = 0x200000 + +# Pixel Format +DDPF_ALPHAPIXELS = 0x1 +DDPF_ALPHA = 0x2 +DDPF_FOURCC = 0x4 +DDPF_PALETTEINDEXED8 = 0x20 +DDPF_RGB = 0x40 +DDPF_LUMINANCE = 0x20000 + + +# dds.h + +DDS_FOURCC = DDPF_FOURCC +DDS_RGB = DDPF_RGB +DDS_RGBA = DDPF_RGB | DDPF_ALPHAPIXELS +DDS_LUMINANCE = DDPF_LUMINANCE +DDS_LUMINANCEA = DDPF_LUMINANCE | DDPF_ALPHAPIXELS +DDS_ALPHA = DDPF_ALPHA +DDS_PAL8 = DDPF_PALETTEINDEXED8 + +DDS_HEADER_FLAGS_TEXTURE = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT +DDS_HEADER_FLAGS_MIPMAP = DDSD_MIPMAPCOUNT +DDS_HEADER_FLAGS_VOLUME = DDSD_DEPTH +DDS_HEADER_FLAGS_PITCH = DDSD_PITCH +DDS_HEADER_FLAGS_LINEARSIZE = DDSD_LINEARSIZE + +DDS_HEIGHT = DDSD_HEIGHT +DDS_WIDTH = DDSD_WIDTH + +DDS_SURFACE_FLAGS_TEXTURE = DDSCAPS_TEXTURE +DDS_SURFACE_FLAGS_MIPMAP = DDSCAPS_COMPLEX | DDSCAPS_MIPMAP +DDS_SURFACE_FLAGS_CUBEMAP = DDSCAPS_COMPLEX + +DDS_CUBEMAP_POSITIVEX = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX +DDS_CUBEMAP_NEGATIVEX = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX +DDS_CUBEMAP_POSITIVEY = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY +DDS_CUBEMAP_NEGATIVEY = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY +DDS_CUBEMAP_POSITIVEZ = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ +DDS_CUBEMAP_NEGATIVEZ = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ + + +# DXT1 +DXT1_FOURCC = 0x31545844 + +# DXT3 +DXT3_FOURCC = 0x33545844 + +# DXT5 +DXT5_FOURCC = 0x35545844 + + +# dxgiformat.h + +DXGI_FORMAT_R8G8B8A8_TYPELESS = 27 +DXGI_FORMAT_R8G8B8A8_UNORM = 28 +DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29 +DXGI_FORMAT_BC7_TYPELESS = 97 +DXGI_FORMAT_BC7_UNORM = 98 +DXGI_FORMAT_BC7_UNORM_SRGB = 99 + + +class DdsImageFile(ImageFile.ImageFile): + format = "DDS" + format_description = "DirectDraw Surface" + + def _open(self): + magic, header_size = struct.unpack(" 0: + s = fp.read(min(lengthfile, 100 * 1024)) + if not s: + break + lengthfile -= len(s) + f.write(s) + + # Build Ghostscript command + command = [ + "gs", + "-q", # quiet mode + "-g%dx%d" % size, # set output geometry (pixels) + "-r%fx%f" % res, # set input DPI (dots per inch) + "-dBATCH", # exit after processing + "-dNOPAUSE", # don't pause between pages + "-dSAFER", # safe mode + "-sDEVICE=ppmraw", # ppm driver + f"-sOutputFile={outfile}", # output file + # adjust for image origin + "-c", + f"{-bbox[0]} {-bbox[1]} translate", + "-f", + infile, # input file + # showpage (see https://bugs.ghostscript.com/show_bug.cgi?id=698272) + "-c", + "showpage", + ] + + if gs_windows_binary is not None: + if not gs_windows_binary: + raise OSError("Unable to locate Ghostscript on paths") + command[0] = gs_windows_binary + + # push data through Ghostscript + try: + startupinfo = None + if sys.platform.startswith("win"): + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + subprocess.check_call(command, startupinfo=startupinfo) + out_im = Image.open(outfile) + out_im.load() + finally: + try: + os.unlink(outfile) + if infile_temp: + os.unlink(infile_temp) + except OSError: + pass + + im = out_im.im.copy() + out_im.close() + return im + + +class PSFile: + """ + Wrapper for bytesio object that treats either CR or LF as end of line. + """ + + def __init__(self, fp): + self.fp = fp + self.char = None + + def seek(self, offset, whence=io.SEEK_SET): + self.char = None + self.fp.seek(offset, whence) + + def readline(self): + s = self.char or b"" + self.char = None + + c = self.fp.read(1) + while c not in b"\r\n": + s = s + c + c = self.fp.read(1) + + self.char = self.fp.read(1) + # line endings can be 1 or 2 of \r \n, in either order + if self.char in b"\r\n": + self.char = None + + return s.decode("latin-1") + + +def _accept(prefix): + return prefix[:4] == b"%!PS" or (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5) + + +## +# Image plugin for Encapsulated PostScript. This plugin supports only +# a few variants of this format. + + +class EpsImageFile(ImageFile.ImageFile): + """EPS File Parser for the Python Imaging Library""" + + format = "EPS" + format_description = "Encapsulated Postscript" + + mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"} + + def _open(self): + (length, offset) = self._find_offset(self.fp) + + # Rewrap the open file pointer in something that will + # convert line endings and decode to latin-1. + fp = PSFile(self.fp) + + # go to offset - start of "%!PS" + fp.seek(offset) + + box = None + + self.mode = "RGB" + self._size = 1, 1 # FIXME: huh? + + # + # Load EPS header + + s_raw = fp.readline() + s = s_raw.strip("\r\n") + + while s_raw: + if s: + if len(s) > 255: + raise SyntaxError("not an EPS file") + + try: + m = split.match(s) + except re.error as e: + raise SyntaxError("not an EPS file") from e + + if m: + k, v = m.group(1, 2) + self.info[k] = v + if k == "BoundingBox": + try: + # Note: The DSC spec says that BoundingBox + # fields should be integers, but some drivers + # put floating point values there anyway. + box = [int(float(i)) for i in v.split()] + self._size = box[2] - box[0], box[3] - box[1] + self.tile = [ + ("eps", (0, 0) + self.size, offset, (length, box)) + ] + except Exception: + pass + + else: + m = field.match(s) + if m: + k = m.group(1) + + if k == "EndComments": + break + if k[:8] == "PS-Adobe": + self.info[k[:8]] = k[9:] + else: + self.info[k] = "" + elif s[0] == "%": + # handle non-DSC PostScript comments that some + # tools mistakenly put in the Comments section + pass + else: + raise OSError("bad EPS header") + + s_raw = fp.readline() + s = s_raw.strip("\r\n") + + if s and s[:1] != "%": + break + + # + # Scan for an "ImageData" descriptor + + while s[:1] == "%": + + if len(s) > 255: + raise SyntaxError("not an EPS file") + + if s[:11] == "%ImageData:": + # Encoded bitmapped image. + x, y, bi, mo = s[11:].split(None, 7)[:4] + + if int(bi) != 8: + break + try: + self.mode = self.mode_map[int(mo)] + except ValueError: + break + + self._size = int(x), int(y) + return + + s = fp.readline().strip("\r\n") + if not s: + break + + if not box: + raise OSError("cannot determine EPS bounding box") + + def _find_offset(self, fp): + + s = fp.read(160) + + if s[:4] == b"%!PS": + # for HEAD without binary preview + fp.seek(0, io.SEEK_END) + length = fp.tell() + offset = 0 + elif i32(s, 0) == 0xC6D3D0C5: + # FIX for: Some EPS file not handled correctly / issue #302 + # EPS can contain binary data + # or start directly with latin coding + # more info see: + # https://web.archive.org/web/20160528181353/http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf + offset = i32(s, 4) + length = i32(s, 8) + else: + raise SyntaxError("not an EPS file") + + return (length, offset) + + def load(self, scale=1): + # Load EPS via Ghostscript + if not self.tile: + return + self.im = Ghostscript(self.tile, self.size, self.fp, scale) + self.mode = self.im.mode + self._size = self.im.size + self.tile = [] + + def load_seek(self, *args, **kwargs): + # we can't incrementally load, so force ImageFile.parser to + # use our custom load method by defining this method. + pass + + +# +# -------------------------------------------------------------------- + + +def _save(im, fp, filename, eps=1): + """EPS Writer for the Python Imaging Library.""" + + # + # make sure image data is available + im.load() + + # + # determine PostScript image mode + if im.mode == "L": + operator = (8, 1, "image") + elif im.mode == "RGB": + operator = (8, 3, "false 3 colorimage") + elif im.mode == "CMYK": + operator = (8, 4, "false 4 colorimage") + else: + raise ValueError("image mode is not supported") + + base_fp = fp + wrapped_fp = False + if fp != sys.stdout: + fp = io.TextIOWrapper(fp, encoding="latin-1") + wrapped_fp = True + + try: + if eps: + # + # write EPS header + fp.write("%!PS-Adobe-3.0 EPSF-3.0\n") + fp.write("%%Creator: PIL 0.1 EpsEncode\n") + # fp.write("%%CreationDate: %s"...) + fp.write("%%%%BoundingBox: 0 0 %d %d\n" % im.size) + fp.write("%%Pages: 1\n") + fp.write("%%EndComments\n") + fp.write("%%Page: 1 1\n") + fp.write("%%ImageData: %d %d " % im.size) + fp.write('%d %d 0 1 1 "%s"\n' % operator) + + # + # image header + fp.write("gsave\n") + fp.write("10 dict begin\n") + fp.write(f"/buf {im.size[0] * operator[1]} string def\n") + fp.write("%d %d scale\n" % im.size) + fp.write("%d %d 8\n" % im.size) # <= bits + fp.write(f"[{im.size[0]} 0 0 -{im.size[1]} 0 {im.size[1]}]\n") + fp.write("{ currentfile buf readhexstring pop } bind\n") + fp.write(operator[2] + "\n") + if hasattr(fp, "flush"): + fp.flush() + + ImageFile._save(im, base_fp, [("eps", (0, 0) + im.size, 0, None)]) + + fp.write("\n%%%%EndBinary\n") + fp.write("grestore end\n") + if hasattr(fp, "flush"): + fp.flush() + finally: + if wrapped_fp: + fp.detach() + + +# +# -------------------------------------------------------------------- + + +Image.register_open(EpsImageFile.format, EpsImageFile, _accept) + +Image.register_save(EpsImageFile.format, _save) + +Image.register_extensions(EpsImageFile.format, [".ps", ".eps"]) + +Image.register_mime(EpsImageFile.format, "application/postscript") diff --git a/venv/lib/python3.11/site-packages/PIL/ExifTags.py b/venv/lib/python3.11/site-packages/PIL/ExifTags.py new file mode 100644 index 00000000..f1c037e5 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ExifTags.py @@ -0,0 +1,318 @@ +# +# The Python Imaging Library. +# $Id$ +# +# EXIF tags +# +# Copyright (c) 2003 by Secret Labs AB +# +# See the README file for information on usage and redistribution. +# + +""" +This module provides constants and clear-text names for various +well-known EXIF tags. +""" + + +TAGS = { + # possibly incomplete + 0x000B: "ProcessingSoftware", + 0x00FE: "NewSubfileType", + 0x00FF: "SubfileType", + 0x0100: "ImageWidth", + 0x0101: "ImageLength", + 0x0102: "BitsPerSample", + 0x0103: "Compression", + 0x0106: "PhotometricInterpretation", + 0x0107: "Thresholding", + 0x0108: "CellWidth", + 0x0109: "CellLength", + 0x010A: "FillOrder", + 0x010D: "DocumentName", + 0x010E: "ImageDescription", + 0x010F: "Make", + 0x0110: "Model", + 0x0111: "StripOffsets", + 0x0112: "Orientation", + 0x0115: "SamplesPerPixel", + 0x0116: "RowsPerStrip", + 0x0117: "StripByteCounts", + 0x0118: "MinSampleValue", + 0x0119: "MaxSampleValue", + 0x011A: "XResolution", + 0x011B: "YResolution", + 0x011C: "PlanarConfiguration", + 0x011D: "PageName", + 0x0120: "FreeOffsets", + 0x0121: "FreeByteCounts", + 0x0122: "GrayResponseUnit", + 0x0123: "GrayResponseCurve", + 0x0124: "T4Options", + 0x0125: "T6Options", + 0x0128: "ResolutionUnit", + 0x0129: "PageNumber", + 0x012D: "TransferFunction", + 0x0131: "Software", + 0x0132: "DateTime", + 0x013B: "Artist", + 0x013C: "HostComputer", + 0x013D: "Predictor", + 0x013E: "WhitePoint", + 0x013F: "PrimaryChromaticities", + 0x0140: "ColorMap", + 0x0141: "HalftoneHints", + 0x0142: "TileWidth", + 0x0143: "TileLength", + 0x0144: "TileOffsets", + 0x0145: "TileByteCounts", + 0x014A: "SubIFDs", + 0x014C: "InkSet", + 0x014D: "InkNames", + 0x014E: "NumberOfInks", + 0x0150: "DotRange", + 0x0151: "TargetPrinter", + 0x0152: "ExtraSamples", + 0x0153: "SampleFormat", + 0x0154: "SMinSampleValue", + 0x0155: "SMaxSampleValue", + 0x0156: "TransferRange", + 0x0157: "ClipPath", + 0x0158: "XClipPathUnits", + 0x0159: "YClipPathUnits", + 0x015A: "Indexed", + 0x015B: "JPEGTables", + 0x015F: "OPIProxy", + 0x0200: "JPEGProc", + 0x0201: "JpegIFOffset", + 0x0202: "JpegIFByteCount", + 0x0203: "JpegRestartInterval", + 0x0205: "JpegLosslessPredictors", + 0x0206: "JpegPointTransforms", + 0x0207: "JpegQTables", + 0x0208: "JpegDCTables", + 0x0209: "JpegACTables", + 0x0211: "YCbCrCoefficients", + 0x0212: "YCbCrSubSampling", + 0x0213: "YCbCrPositioning", + 0x0214: "ReferenceBlackWhite", + 0x02BC: "XMLPacket", + 0x1000: "RelatedImageFileFormat", + 0x1001: "RelatedImageWidth", + 0x1002: "RelatedImageLength", + 0x4746: "Rating", + 0x4749: "RatingPercent", + 0x800D: "ImageID", + 0x828D: "CFARepeatPatternDim", + 0x828E: "CFAPattern", + 0x828F: "BatteryLevel", + 0x8298: "Copyright", + 0x829A: "ExposureTime", + 0x829D: "FNumber", + 0x83BB: "IPTCNAA", + 0x8649: "ImageResources", + 0x8769: "ExifOffset", + 0x8773: "InterColorProfile", + 0x8822: "ExposureProgram", + 0x8824: "SpectralSensitivity", + 0x8825: "GPSInfo", + 0x8827: "ISOSpeedRatings", + 0x8828: "OECF", + 0x8829: "Interlace", + 0x882A: "TimeZoneOffset", + 0x882B: "SelfTimerMode", + 0x9000: "ExifVersion", + 0x9003: "DateTimeOriginal", + 0x9004: "DateTimeDigitized", + 0x9101: "ComponentsConfiguration", + 0x9102: "CompressedBitsPerPixel", + 0x9201: "ShutterSpeedValue", + 0x9202: "ApertureValue", + 0x9203: "BrightnessValue", + 0x9204: "ExposureBiasValue", + 0x9205: "MaxApertureValue", + 0x9206: "SubjectDistance", + 0x9207: "MeteringMode", + 0x9208: "LightSource", + 0x9209: "Flash", + 0x920A: "FocalLength", + 0x920B: "FlashEnergy", + 0x920C: "SpatialFrequencyResponse", + 0x920D: "Noise", + 0x9211: "ImageNumber", + 0x9212: "SecurityClassification", + 0x9213: "ImageHistory", + 0x9214: "SubjectLocation", + 0x9215: "ExposureIndex", + 0x9216: "TIFF/EPStandardID", + 0x927C: "MakerNote", + 0x9286: "UserComment", + 0x9290: "SubsecTime", + 0x9291: "SubsecTimeOriginal", + 0x9292: "SubsecTimeDigitized", + 0x9400: "AmbientTemperature", + 0x9401: "Humidity", + 0x9402: "Pressure", + 0x9403: "WaterDepth", + 0x9404: "Acceleration", + 0x9405: "CameraElevationAngle", + 0x9C9B: "XPTitle", + 0x9C9C: "XPComment", + 0x9C9D: "XPAuthor", + 0x9C9E: "XPKeywords", + 0x9C9F: "XPSubject", + 0xA000: "FlashPixVersion", + 0xA001: "ColorSpace", + 0xA002: "ExifImageWidth", + 0xA003: "ExifImageHeight", + 0xA004: "RelatedSoundFile", + 0xA005: "ExifInteroperabilityOffset", + 0xA20B: "FlashEnergy", + 0xA20C: "SpatialFrequencyResponse", + 0xA20E: "FocalPlaneXResolution", + 0xA20F: "FocalPlaneYResolution", + 0xA210: "FocalPlaneResolutionUnit", + 0xA214: "SubjectLocation", + 0xA215: "ExposureIndex", + 0xA217: "SensingMethod", + 0xA300: "FileSource", + 0xA301: "SceneType", + 0xA302: "CFAPattern", + 0xA401: "CustomRendered", + 0xA402: "ExposureMode", + 0xA403: "WhiteBalance", + 0xA404: "DigitalZoomRatio", + 0xA405: "FocalLengthIn35mmFilm", + 0xA406: "SceneCaptureType", + 0xA407: "GainControl", + 0xA408: "Contrast", + 0xA409: "Saturation", + 0xA40A: "Sharpness", + 0xA40B: "DeviceSettingDescription", + 0xA40C: "SubjectDistanceRange", + 0xA420: "ImageUniqueID", + 0xA430: "CameraOwnerName", + 0xA431: "BodySerialNumber", + 0xA432: "LensSpecification", + 0xA433: "LensMake", + 0xA434: "LensModel", + 0xA435: "LensSerialNumber", + 0xA500: "Gamma", + 0xC4A5: "PrintImageMatching", + 0xC612: "DNGVersion", + 0xC613: "DNGBackwardVersion", + 0xC614: "UniqueCameraModel", + 0xC615: "LocalizedCameraModel", + 0xC616: "CFAPlaneColor", + 0xC617: "CFALayout", + 0xC618: "LinearizationTable", + 0xC619: "BlackLevelRepeatDim", + 0xC61A: "BlackLevel", + 0xC61B: "BlackLevelDeltaH", + 0xC61C: "BlackLevelDeltaV", + 0xC61D: "WhiteLevel", + 0xC61E: "DefaultScale", + 0xC61F: "DefaultCropOrigin", + 0xC620: "DefaultCropSize", + 0xC621: "ColorMatrix1", + 0xC622: "ColorMatrix2", + 0xC623: "CameraCalibration1", + 0xC624: "CameraCalibration2", + 0xC625: "ReductionMatrix1", + 0xC626: "ReductionMatrix2", + 0xC627: "AnalogBalance", + 0xC628: "AsShotNeutral", + 0xC629: "AsShotWhiteXY", + 0xC62A: "BaselineExposure", + 0xC62B: "BaselineNoise", + 0xC62C: "BaselineSharpness", + 0xC62D: "BayerGreenSplit", + 0xC62E: "LinearResponseLimit", + 0xC62F: "CameraSerialNumber", + 0xC630: "LensInfo", + 0xC631: "ChromaBlurRadius", + 0xC632: "AntiAliasStrength", + 0xC633: "ShadowScale", + 0xC634: "DNGPrivateData", + 0xC635: "MakerNoteSafety", + 0xC65A: "CalibrationIlluminant1", + 0xC65B: "CalibrationIlluminant2", + 0xC65C: "BestQualityScale", + 0xC65D: "RawDataUniqueID", + 0xC68B: "OriginalRawFileName", + 0xC68C: "OriginalRawFileData", + 0xC68D: "ActiveArea", + 0xC68E: "MaskedAreas", + 0xC68F: "AsShotICCProfile", + 0xC690: "AsShotPreProfileMatrix", + 0xC691: "CurrentICCProfile", + 0xC692: "CurrentPreProfileMatrix", + 0xC6BF: "ColorimetricReference", + 0xC6F3: "CameraCalibrationSignature", + 0xC6F4: "ProfileCalibrationSignature", + 0xC6F6: "AsShotProfileName", + 0xC6F7: "NoiseReductionApplied", + 0xC6F8: "ProfileName", + 0xC6F9: "ProfileHueSatMapDims", + 0xC6FA: "ProfileHueSatMapData1", + 0xC6FB: "ProfileHueSatMapData2", + 0xC6FC: "ProfileToneCurve", + 0xC6FD: "ProfileEmbedPolicy", + 0xC6FE: "ProfileCopyright", + 0xC714: "ForwardMatrix1", + 0xC715: "ForwardMatrix2", + 0xC716: "PreviewApplicationName", + 0xC717: "PreviewApplicationVersion", + 0xC718: "PreviewSettingsName", + 0xC719: "PreviewSettingsDigest", + 0xC71A: "PreviewColorSpace", + 0xC71B: "PreviewDateTime", + 0xC71C: "RawImageDigest", + 0xC71D: "OriginalRawFileDigest", + 0xC71E: "SubTileBlockSize", + 0xC71F: "RowInterleaveFactor", + 0xC725: "ProfileLookTableDims", + 0xC726: "ProfileLookTableData", + 0xC740: "OpcodeList1", + 0xC741: "OpcodeList2", + 0xC74E: "OpcodeList3", + 0xC761: "NoiseProfile", +} +"""Maps EXIF tags to tag names.""" + + +GPSTAGS = { + 0: "GPSVersionID", + 1: "GPSLatitudeRef", + 2: "GPSLatitude", + 3: "GPSLongitudeRef", + 4: "GPSLongitude", + 5: "GPSAltitudeRef", + 6: "GPSAltitude", + 7: "GPSTimeStamp", + 8: "GPSSatellites", + 9: "GPSStatus", + 10: "GPSMeasureMode", + 11: "GPSDOP", + 12: "GPSSpeedRef", + 13: "GPSSpeed", + 14: "GPSTrackRef", + 15: "GPSTrack", + 16: "GPSImgDirectionRef", + 17: "GPSImgDirection", + 18: "GPSMapDatum", + 19: "GPSDestLatitudeRef", + 20: "GPSDestLatitude", + 21: "GPSDestLongitudeRef", + 22: "GPSDestLongitude", + 23: "GPSDestBearingRef", + 24: "GPSDestBearing", + 25: "GPSDestDistanceRef", + 26: "GPSDestDistance", + 27: "GPSProcessingMethod", + 28: "GPSAreaInformation", + 29: "GPSDateStamp", + 30: "GPSDifferential", + 31: "GPSHPositioningError", +} +"""Maps EXIF GPS tags to tag names.""" diff --git a/venv/lib/python3.11/site-packages/PIL/FitsStubImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/FitsStubImagePlugin.py new file mode 100644 index 00000000..c2ce8651 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/FitsStubImagePlugin.py @@ -0,0 +1,76 @@ +# +# The Python Imaging Library +# $Id$ +# +# FITS stub adapter +# +# Copyright (c) 1998-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from . import Image, ImageFile + +_handler = None + + +def register_handler(handler): + """ + Install application-specific FITS image handler. + + :param handler: Handler object. + """ + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + + +def _accept(prefix): + return prefix[:6] == b"SIMPLE" + + +class FITSStubImageFile(ImageFile.StubImageFile): + + format = "FITS" + format_description = "FITS" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(6)): + raise SyntaxError("Not a FITS file") + + # FIXME: add more sanity checks here; mandatory header items + # include SIMPLE, BITPIX, NAXIS, etc. + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self._size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise OSError("FITS save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(FITSStubImageFile.format, FITSStubImageFile, _accept) +Image.register_save(FITSStubImageFile.format, _save) + +Image.register_extensions(FITSStubImageFile.format, [".fit", ".fits"]) diff --git a/venv/lib/python3.11/site-packages/PIL/FliImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/FliImagePlugin.py new file mode 100644 index 00000000..f2d4857f --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/FliImagePlugin.py @@ -0,0 +1,171 @@ +# +# The Python Imaging Library. +# $Id$ +# +# FLI/FLC file handling. +# +# History: +# 95-09-01 fl Created +# 97-01-03 fl Fixed parser, setup decoder tile +# 98-07-15 fl Renamed offset attribute to avoid name clash +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1995-97. +# +# See the README file for information on usage and redistribution. +# + + +from . import Image, ImageFile, ImagePalette +from ._binary import i16le as i16 +from ._binary import i32le as i32 +from ._binary import o8 + +# +# decoder + + +def _accept(prefix): + return len(prefix) >= 6 and i16(prefix, 4) in [0xAF11, 0xAF12] + + +## +# Image plugin for the FLI/FLC animation format. Use the seek +# method to load individual frames. + + +class FliImageFile(ImageFile.ImageFile): + + format = "FLI" + format_description = "Autodesk FLI/FLC Animation" + _close_exclusive_fp_after_loading = False + + def _open(self): + + # HEAD + s = self.fp.read(128) + if not ( + _accept(s) + and i16(s, 14) in [0, 3] # flags + and s[20:22] == b"\x00\x00" # reserved + ): + raise SyntaxError("not an FLI/FLC file") + + # frames + self.n_frames = i16(s, 6) + self.is_animated = self.n_frames > 1 + + # image characteristics + self.mode = "P" + self._size = i16(s, 8), i16(s, 10) + + # animation speed + duration = i32(s, 16) + magic = i16(s, 4) + if magic == 0xAF11: + duration = (duration * 1000) // 70 + self.info["duration"] = duration + + # look for palette + palette = [(a, a, a) for a in range(256)] + + s = self.fp.read(16) + + self.__offset = 128 + + if i16(s, 4) == 0xF100: + # prefix chunk; ignore it + self.__offset = self.__offset + i32(s) + s = self.fp.read(16) + + if i16(s, 4) == 0xF1FA: + # look for palette chunk + s = self.fp.read(6) + if i16(s, 4) == 11: + self._palette(palette, 2) + elif i16(s, 4) == 4: + self._palette(palette, 0) + + palette = [o8(r) + o8(g) + o8(b) for (r, g, b) in palette] + self.palette = ImagePalette.raw("RGB", b"".join(palette)) + + # set things up to decode first frame + self.__frame = -1 + self.__fp = self.fp + self.__rewind = self.fp.tell() + self.seek(0) + + def _palette(self, palette, shift): + # load palette + + i = 0 + for e in range(i16(self.fp.read(2))): + s = self.fp.read(2) + i = i + s[0] + n = s[1] + if n == 0: + n = 256 + s = self.fp.read(n * 3) + for n in range(0, len(s), 3): + r = s[n] << shift + g = s[n + 1] << shift + b = s[n + 2] << shift + palette[i] = (r, g, b) + i += 1 + + def seek(self, frame): + if not self._seek_check(frame): + return + if frame < self.__frame: + self._seek(0) + + for f in range(self.__frame + 1, frame + 1): + self._seek(f) + + def _seek(self, frame): + if frame == 0: + self.__frame = -1 + self.__fp.seek(self.__rewind) + self.__offset = 128 + else: + # ensure that the previous frame was loaded + self.load() + + if frame != self.__frame + 1: + raise ValueError(f"cannot seek to frame {frame}") + self.__frame = frame + + # move to next frame + self.fp = self.__fp + self.fp.seek(self.__offset) + + s = self.fp.read(4) + if not s: + raise EOFError + + framesize = i32(s) + + self.decodermaxblock = framesize + self.tile = [("fli", (0, 0) + self.size, self.__offset, None)] + + self.__offset += framesize + + def tell(self): + return self.__frame + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +# +# registry + +Image.register_open(FliImageFile.format, FliImageFile, _accept) + +Image.register_extensions(FliImageFile.format, [".fli", ".flc"]) diff --git a/venv/lib/python3.11/site-packages/PIL/FontFile.py b/venv/lib/python3.11/site-packages/PIL/FontFile.py new file mode 100644 index 00000000..c5fc80b3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/FontFile.py @@ -0,0 +1,111 @@ +# +# The Python Imaging Library +# $Id$ +# +# base class for raster font file parsers +# +# history: +# 1997-06-05 fl created +# 1997-08-19 fl restrict image width +# +# Copyright (c) 1997-1998 by Secret Labs AB +# Copyright (c) 1997-1998 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +import os + +from . import Image, _binary + +WIDTH = 800 + + +def puti16(fp, values): + """Write network order (big-endian) 16-bit sequence""" + for v in values: + if v < 0: + v += 65536 + fp.write(_binary.o16be(v)) + + +class FontFile: + """Base class for raster font file handlers.""" + + bitmap = None + + def __init__(self): + + self.info = {} + self.glyph = [None] * 256 + + def __getitem__(self, ix): + return self.glyph[ix] + + def compile(self): + """Create metrics and bitmap""" + + if self.bitmap: + return + + # create bitmap large enough to hold all data + h = w = maxwidth = 0 + lines = 1 + for glyph in self: + if glyph: + d, dst, src, im = glyph + h = max(h, src[3] - src[1]) + w = w + (src[2] - src[0]) + if w > WIDTH: + lines += 1 + w = src[2] - src[0] + maxwidth = max(maxwidth, w) + + xsize = maxwidth + ysize = lines * h + + if xsize == 0 and ysize == 0: + return "" + + self.ysize = h + + # paste glyphs into bitmap + self.bitmap = Image.new("1", (xsize, ysize)) + self.metrics = [None] * 256 + x = y = 0 + for i in range(256): + glyph = self[i] + if glyph: + d, dst, src, im = glyph + xx = src[2] - src[0] + # yy = src[3] - src[1] + x0, y0 = x, y + x = x + xx + if x > WIDTH: + x, y = 0, y + h + x0, y0 = x, y + x = xx + s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0 + self.bitmap.paste(im.crop(src), s) + self.metrics[i] = d, dst, s + + def save(self, filename): + """Save font""" + + self.compile() + + # font data + self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG") + + # font metrics + with open(os.path.splitext(filename)[0] + ".pil", "wb") as fp: + fp.write(b"PILfont\n") + fp.write(f";;;;;;{self.ysize};\n".encode("ascii")) # HACK!!! + fp.write(b"DATA\n") + for id in range(256): + m = self.metrics[id] + if not m: + puti16(fp, [0] * 10) + else: + puti16(fp, m[0] + m[1] + m[2]) diff --git a/venv/lib/python3.11/site-packages/PIL/FpxImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/FpxImagePlugin.py new file mode 100644 index 00000000..5e385469 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/FpxImagePlugin.py @@ -0,0 +1,242 @@ +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library. +# $Id$ +# +# FlashPix support for PIL +# +# History: +# 97-01-25 fl Created (reads uncompressed RGB images only) +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# +import olefile + +from . import Image, ImageFile +from ._binary import i32le as i32 + +# we map from colour field tuples to (mode, rawmode) descriptors +MODES = { + # opacity + (0x00007FFE): ("A", "L"), + # monochrome + (0x00010000,): ("L", "L"), + (0x00018000, 0x00017FFE): ("RGBA", "LA"), + # photo YCC + (0x00020000, 0x00020001, 0x00020002): ("RGB", "YCC;P"), + (0x00028000, 0x00028001, 0x00028002, 0x00027FFE): ("RGBA", "YCCA;P"), + # standard RGB (NIFRGB) + (0x00030000, 0x00030001, 0x00030002): ("RGB", "RGB"), + (0x00038000, 0x00038001, 0x00038002, 0x00037FFE): ("RGBA", "RGBA"), +} + + +# +# -------------------------------------------------------------------- + + +def _accept(prefix): + return prefix[:8] == olefile.MAGIC + + +## +# Image plugin for the FlashPix images. + + +class FpxImageFile(ImageFile.ImageFile): + + format = "FPX" + format_description = "FlashPix" + + def _open(self): + # + # read the OLE directory and see if this is a likely + # to be a FlashPix file + + try: + self.ole = olefile.OleFileIO(self.fp) + except OSError as e: + raise SyntaxError("not an FPX file; invalid OLE file") from e + + if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B": + raise SyntaxError("not an FPX file; bad root CLSID") + + self._open_index(1) + + def _open_index(self, index=1): + # + # get the Image Contents Property Set + + prop = self.ole.getproperties( + [f"Data Object Store {index:06d}", "\005Image Contents"] + ) + + # size (highest resolution) + + self._size = prop[0x1000002], prop[0x1000003] + + size = max(self.size) + i = 1 + while size > 64: + size = size / 2 + i += 1 + self.maxid = i - 1 + + # mode. instead of using a single field for this, flashpix + # requires you to specify the mode for each channel in each + # resolution subimage, and leaves it to the decoder to make + # sure that they all match. for now, we'll cheat and assume + # that this is always the case. + + id = self.maxid << 16 + + s = prop[0x2000002 | id] + + colors = [] + bands = i32(s, 4) + if bands > 4: + raise OSError("Invalid number of bands") + for i in range(bands): + # note: for now, we ignore the "uncalibrated" flag + colors.append(i32(s, 8 + i * 4) & 0x7FFFFFFF) + + self.mode, self.rawmode = MODES[tuple(colors)] + + # load JPEG tables, if any + self.jpeg = {} + for i in range(256): + id = 0x3000001 | (i << 16) + if id in prop: + self.jpeg[i] = prop[id] + + self._open_subimage(1, self.maxid) + + def _open_subimage(self, index=1, subimage=0): + # + # setup tile descriptors for a given subimage + + stream = [ + f"Data Object Store {index:06d}", + f"Resolution {subimage:04d}", + "Subimage 0000 Header", + ] + + fp = self.ole.openstream(stream) + + # skip prefix + fp.read(28) + + # header stream + s = fp.read(36) + + size = i32(s, 4), i32(s, 8) + # tilecount = i32(s, 12) + tilesize = i32(s, 16), i32(s, 20) + # channels = i32(s, 24) + offset = i32(s, 28) + length = i32(s, 32) + + if size != self.size: + raise OSError("subimage mismatch") + + # get tile descriptors + fp.seek(28 + offset) + s = fp.read(i32(s, 12) * length) + + x = y = 0 + xsize, ysize = size + xtile, ytile = tilesize + self.tile = [] + + for i in range(0, len(s), length): + + compression = i32(s, i + 8) + + if compression == 0: + self.tile.append( + ( + "raw", + (x, y, x + xtile, y + ytile), + i32(s, i) + 28, + (self.rawmode), + ) + ) + + elif compression == 1: + + # FIXME: the fill decoder is not implemented + self.tile.append( + ( + "fill", + (x, y, x + xtile, y + ytile), + i32(s, i) + 28, + (self.rawmode, s[12:16]), + ) + ) + + elif compression == 2: + + internal_color_conversion = s[14] + jpeg_tables = s[15] + rawmode = self.rawmode + + if internal_color_conversion: + # The image is stored as usual (usually YCbCr). + if rawmode == "RGBA": + # For "RGBA", data is stored as YCbCrA based on + # negative RGB. The following trick works around + # this problem : + jpegmode, rawmode = "YCbCrK", "CMYK" + else: + jpegmode = None # let the decoder decide + + else: + # The image is stored as defined by rawmode + jpegmode = rawmode + + self.tile.append( + ( + "jpeg", + (x, y, x + xtile, y + ytile), + i32(s, i) + 28, + (rawmode, jpegmode), + ) + ) + + # FIXME: jpeg tables are tile dependent; the prefix + # data must be placed in the tile descriptor itself! + + if jpeg_tables: + self.tile_prefix = self.jpeg[jpeg_tables] + + else: + raise OSError("unknown/invalid compression") + + x = x + xtile + if x >= xsize: + x, y = 0, y + ytile + if y >= ysize: + break # isn't really required + + self.stream = stream + self.fp = None + + def load(self): + + if not self.fp: + self.fp = self.ole.openstream(self.stream[:2] + ["Subimage 0000 Data"]) + + return ImageFile.ImageFile.load(self) + + +# +# -------------------------------------------------------------------- + + +Image.register_open(FpxImageFile.format, FpxImageFile, _accept) + +Image.register_extension(FpxImageFile.format, ".fpx") diff --git a/venv/lib/python3.11/site-packages/PIL/FtexImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/FtexImagePlugin.py new file mode 100644 index 00000000..90066123 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/FtexImagePlugin.py @@ -0,0 +1,106 @@ +""" +A Pillow loader for .ftc and .ftu files (FTEX) +Jerome Leclanche + +The contents of this file are hereby released in the public domain (CC0) +Full text of the CC0 license: + https://creativecommons.org/publicdomain/zero/1.0/ + +Independence War 2: Edge Of Chaos - Texture File Format - 16 October 2001 + +The textures used for 3D objects in Independence War 2: Edge Of Chaos are in a +packed custom format called FTEX. This file format uses file extensions FTC +and FTU. +* FTC files are compressed textures (using standard texture compression). +* FTU files are not compressed. +Texture File Format +The FTC and FTU texture files both use the same format. This +has the following structure: +{header} +{format_directory} +{data} +Where: +{header} = { + u32:magic, + u32:version, + u32:width, + u32:height, + u32:mipmap_count, + u32:format_count +} + +* The "magic" number is "FTEX". +* "width" and "height" are the dimensions of the texture. +* "mipmap_count" is the number of mipmaps in the texture. +* "format_count" is the number of texture formats (different versions of the +same texture) in this file. + +{format_directory} = format_count * { u32:format, u32:where } + +The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB +uncompressed textures. +The texture data for a format starts at the position "where" in the file. + +Each set of texture data in the file has the following structure: +{data} = format_count * { u32:mipmap_size, mipmap_size * { u8 } } +* "mipmap_size" is the number of bytes in that mip level. For compressed +textures this is the size of the texture data compressed with DXT1. For 24 bit +uncompressed textures, this is 3 * width * height. Following this are the image +bytes for that mipmap level. + +Note: All data is stored in little-Endian (Intel) byte order. +""" + +import struct +from io import BytesIO + +from . import Image, ImageFile + +MAGIC = b"FTEX" +FORMAT_DXT1 = 0 +FORMAT_UNCOMPRESSED = 1 + + +class FtexImageFile(ImageFile.ImageFile): + format = "FTEX" + format_description = "Texture File Format (IW2:EOC)" + + def _open(self): + struct.unpack("= 8 and i32(prefix, 0) >= 20 and i32(prefix, 4) in (1, 2) + + +## +# Image plugin for the GIMP brush format. + + +class GbrImageFile(ImageFile.ImageFile): + + format = "GBR" + format_description = "GIMP brush file" + + def _open(self): + header_size = i32(self.fp.read(4)) + version = i32(self.fp.read(4)) + if header_size < 20: + raise SyntaxError("not a GIMP brush") + if version not in (1, 2): + raise SyntaxError(f"Unsupported GIMP brush version: {version}") + + width = i32(self.fp.read(4)) + height = i32(self.fp.read(4)) + color_depth = i32(self.fp.read(4)) + if width <= 0 or height <= 0: + raise SyntaxError("not a GIMP brush") + if color_depth not in (1, 4): + raise SyntaxError(f"Unsupported GIMP brush color depth: {color_depth}") + + if version == 1: + comment_length = header_size - 20 + else: + comment_length = header_size - 28 + magic_number = self.fp.read(4) + if magic_number != b"GIMP": + raise SyntaxError("not a GIMP brush, bad magic number") + self.info["spacing"] = i32(self.fp.read(4)) + + comment = self.fp.read(comment_length)[:-1] + + if color_depth == 1: + self.mode = "L" + else: + self.mode = "RGBA" + + self._size = width, height + + self.info["comment"] = comment + + # Image might not be small + Image._decompression_bomb_check(self.size) + + # Data is an uncompressed block of w * h * bytes/pixel + self._data_size = width * height * color_depth + + def load(self): + if self.im: + # Already loaded + return + + self.im = Image.core.new(self.mode, self.size) + self.frombytes(self.fp.read(self._data_size)) + + +# +# registry + + +Image.register_open(GbrImageFile.format, GbrImageFile, _accept) +Image.register_extension(GbrImageFile.format, ".gbr") diff --git a/venv/lib/python3.11/site-packages/PIL/GdImageFile.py b/venv/lib/python3.11/site-packages/PIL/GdImageFile.py new file mode 100644 index 00000000..9c34adaa --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/GdImageFile.py @@ -0,0 +1,90 @@ +# +# The Python Imaging Library. +# $Id$ +# +# GD file handling +# +# History: +# 1996-04-12 fl Created +# +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +""" +.. note:: + This format cannot be automatically recognized, so the + class is not registered for use with :py:func:`PIL.Image.open()`. To open a + gd file, use the :py:func:`PIL.GdImageFile.open()` function instead. + +.. warning:: + THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This + implementation is provided for convenience and demonstrational + purposes only. +""" + + +from . import ImageFile, ImagePalette, UnidentifiedImageError +from ._binary import i16be as i16 +from ._binary import i32be as i32 + + +class GdImageFile(ImageFile.ImageFile): + """ + Image plugin for the GD uncompressed format. Note that this format + is not supported by the standard :py:func:`PIL.Image.open()` function. To use + this plugin, you have to import the :py:mod:`PIL.GdImageFile` module and + use the :py:func:`PIL.GdImageFile.open()` function. + """ + + format = "GD" + format_description = "GD uncompressed images" + + def _open(self): + + # Header + s = self.fp.read(1037) + + if not i16(s) in [65534, 65535]: + raise SyntaxError("Not a valid GD 2.x .gd file") + + self.mode = "L" # FIXME: "P" + self._size = i16(s, 2), i16(s, 4) + + trueColor = s[6] + trueColorOffset = 2 if trueColor else 0 + + # transparency index + tindex = i32(s, 7 + trueColorOffset) + if tindex < 256: + self.info["transparency"] = tindex + + self.palette = ImagePalette.raw( + "XBGR", s[7 + trueColorOffset + 4 : 7 + trueColorOffset + 4 + 256 * 4] + ) + + self.tile = [ + ("raw", (0, 0) + self.size, 7 + trueColorOffset + 4 + 256 * 4, ("L", 0, 1)) + ] + + +def open(fp, mode="r"): + """ + Load texture from a GD image file. + + :param filename: GD file name, or an opened file handle. + :param mode: Optional mode. In this version, if the mode argument + is given, it must be "r". + :returns: An image instance. + :raises OSError: If the image could not be read. + """ + if mode != "r": + raise ValueError("bad mode") + + try: + return GdImageFile(fp) + except SyntaxError as e: + raise UnidentifiedImageError("cannot identify this image file") from e diff --git a/venv/lib/python3.11/site-packages/PIL/GifImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/GifImagePlugin.py new file mode 100644 index 00000000..7c083bd8 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/GifImagePlugin.py @@ -0,0 +1,888 @@ +# +# The Python Imaging Library. +# $Id$ +# +# GIF file handling +# +# History: +# 1995-09-01 fl Created +# 1996-12-14 fl Added interlace support +# 1996-12-30 fl Added animation support +# 1997-01-05 fl Added write support, fixed local colour map bug +# 1997-02-23 fl Make sure to load raster data in getdata() +# 1997-07-05 fl Support external decoder (0.4) +# 1998-07-09 fl Handle all modes when saving (0.5) +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 2001-04-16 fl Added rewind support (seek to frame 0) (0.6) +# 2001-04-17 fl Added palette optimization (0.7) +# 2002-06-06 fl Added transparency support for save (0.8) +# 2004-02-24 fl Disable interlacing for small images +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1995-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import itertools +import math +import os +import subprocess + +from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence +from ._binary import i16le as i16 +from ._binary import o8 +from ._binary import o16le as o16 + +# -------------------------------------------------------------------- +# Identify/read GIF files + + +def _accept(prefix): + return prefix[:6] in [b"GIF87a", b"GIF89a"] + + +## +# Image plugin for GIF images. This plugin supports both GIF87 and +# GIF89 images. + + +class GifImageFile(ImageFile.ImageFile): + + format = "GIF" + format_description = "Compuserve GIF" + _close_exclusive_fp_after_loading = False + + global_palette = None + + def data(self): + s = self.fp.read(1) + if s and s[0]: + return self.fp.read(s[0]) + return None + + def _open(self): + + # Screen + s = self.fp.read(13) + if not _accept(s): + raise SyntaxError("not a GIF file") + + self.info["version"] = s[:6] + self._size = i16(s, 6), i16(s, 8) + self.tile = [] + flags = s[10] + bits = (flags & 7) + 1 + + if flags & 128: + # get global palette + self.info["background"] = s[11] + # check if palette contains colour indices + p = self.fp.read(3 << bits) + for i in range(0, len(p), 3): + if not (i // 3 == p[i] == p[i + 1] == p[i + 2]): + p = ImagePalette.raw("RGB", p) + self.global_palette = self.palette = p + break + + self.__fp = self.fp # FIXME: hack + self.__rewind = self.fp.tell() + self._n_frames = None + self._is_animated = None + self._seek(0) # get ready to read first frame + + @property + def n_frames(self): + if self._n_frames is None: + current = self.tell() + try: + while True: + self.seek(self.tell() + 1) + except EOFError: + self._n_frames = self.tell() + 1 + self.seek(current) + return self._n_frames + + @property + def is_animated(self): + if self._is_animated is None: + if self._n_frames is not None: + self._is_animated = self._n_frames != 1 + else: + current = self.tell() + + try: + self.seek(1) + self._is_animated = True + except EOFError: + self._is_animated = False + + self.seek(current) + return self._is_animated + + def seek(self, frame): + if not self._seek_check(frame): + return + if frame < self.__frame: + if frame != 0: + self.im = None + self._seek(0) + + last_frame = self.__frame + for f in range(self.__frame + 1, frame + 1): + try: + self._seek(f) + except EOFError as e: + self.seek(last_frame) + raise EOFError("no more images in GIF file") from e + + def _seek(self, frame): + + if frame == 0: + # rewind + self.__offset = 0 + self.dispose = None + self.dispose_extent = [0, 0, 0, 0] # x0, y0, x1, y1 + self.__frame = -1 + self.__fp.seek(self.__rewind) + self._prev_im = None + self.disposal_method = 0 + else: + # ensure that the previous frame was loaded + if not self.im: + self.load() + + if frame != self.__frame + 1: + raise ValueError(f"cannot seek to frame {frame}") + self.__frame = frame + + self.tile = [] + + self.fp = self.__fp + if self.__offset: + # backup to last frame + self.fp.seek(self.__offset) + while self.data(): + pass + self.__offset = 0 + + if self.dispose: + self.im.paste(self.dispose, self.dispose_extent) + + from copy import copy + + self.palette = copy(self.global_palette) + + info = {} + while True: + + s = self.fp.read(1) + if not s or s == b";": + break + + elif s == b"!": + # + # extensions + # + s = self.fp.read(1) + block = self.data() + if s[0] == 249: + # + # graphic control extension + # + flags = block[0] + if flags & 1: + info["transparency"] = block[3] + info["duration"] = i16(block, 1) * 10 + + # disposal method - find the value of bits 4 - 6 + dispose_bits = 0b00011100 & flags + dispose_bits = dispose_bits >> 2 + if dispose_bits: + # only set the dispose if it is not + # unspecified. I'm not sure if this is + # correct, but it seems to prevent the last + # frame from looking odd for some animations + self.disposal_method = dispose_bits + elif s[0] == 254: + # + # comment extension + # + while block: + if "comment" in info: + info["comment"] += block + else: + info["comment"] = block + block = self.data() + continue + elif s[0] == 255: + # + # application extension + # + info["extension"] = block, self.fp.tell() + if block[:11] == b"NETSCAPE2.0": + block = self.data() + if len(block) >= 3 and block[0] == 1: + info["loop"] = i16(block, 1) + while self.data(): + pass + + elif s == b",": + # + # local image + # + s = self.fp.read(9) + + # extent + x0, y0 = i16(s, 0), i16(s, 2) + x1, y1 = x0 + i16(s, 4), y0 + i16(s, 6) + if x1 > self.size[0] or y1 > self.size[1]: + self._size = max(x1, self.size[0]), max(y1, self.size[1]) + self.dispose_extent = x0, y0, x1, y1 + flags = s[8] + + interlace = (flags & 64) != 0 + + if flags & 128: + bits = (flags & 7) + 1 + self.palette = ImagePalette.raw("RGB", self.fp.read(3 << bits)) + + # image data + bits = self.fp.read(1)[0] + self.__offset = self.fp.tell() + self.tile = [ + ("gif", (x0, y0, x1, y1), self.__offset, (bits, interlace)) + ] + break + + else: + pass + # raise OSError, "illegal GIF tag `%x`" % s[0] + + try: + if self.disposal_method < 2: + # do not dispose or none specified + self.dispose = None + elif self.disposal_method == 2: + # replace with background colour + Image._decompression_bomb_check(self.size) + self.dispose = Image.core.fill("P", self.size, self.info["background"]) + else: + # replace with previous contents + if self.im: + self.dispose = self.im.copy() + + # only dispose the extent in this frame + if self.dispose: + self.dispose = self._crop(self.dispose, self.dispose_extent) + except (AttributeError, KeyError): + pass + + if not self.tile: + # self.__fp = None + raise EOFError + + for k in ["transparency", "duration", "comment", "extension", "loop"]: + if k in info: + self.info[k] = info[k] + elif k in self.info: + del self.info[k] + + self.mode = "L" + if self.palette: + self.mode = "P" + + def tell(self): + return self.__frame + + def load_end(self): + ImageFile.ImageFile.load_end(self) + + # if the disposal method is 'do not dispose', transparent + # pixels should show the content of the previous frame + if self._prev_im and self._prev_disposal_method == 1: + # we do this by pasting the updated area onto the previous + # frame which we then use as the current image content + updated = self._crop(self.im, self.dispose_extent) + self._prev_im.paste(updated, self.dispose_extent, updated.convert("RGBA")) + self.im = self._prev_im + self._prev_im = self.im.copy() + self._prev_disposal_method = self.disposal_method + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +# -------------------------------------------------------------------- +# Write GIF files + + +RAWMODE = {"1": "L", "L": "L", "P": "P"} + + +def _normalize_mode(im, initial_call=False): + """ + Takes an image (or frame), returns an image in a mode that is appropriate + for saving in a Gif. + + It may return the original image, or it may return an image converted to + palette or 'L' mode. + + UNDONE: What is the point of mucking with the initial call palette, for + an image that shouldn't have a palette, or it would be a mode 'P' and + get returned in the RAWMODE clause. + + :param im: Image object + :param initial_call: Default false, set to true for a single frame. + :returns: Image object + """ + if im.mode in RAWMODE: + im.load() + return im + if Image.getmodebase(im.mode) == "RGB": + if initial_call: + palette_size = 256 + if im.palette: + palette_size = len(im.palette.getdata()[1]) // 3 + return im.convert("P", palette=Image.ADAPTIVE, colors=palette_size) + else: + return im.convert("P") + return im.convert("L") + + +def _normalize_palette(im, palette, info): + """ + Normalizes the palette for image. + - Sets the palette to the incoming palette, if provided. + - Ensures that there's a palette for L mode images + - Optimizes the palette if necessary/desired. + + :param im: Image object + :param palette: bytes object containing the source palette, or .... + :param info: encoderinfo + :returns: Image object + """ + source_palette = None + if palette: + # a bytes palette + if isinstance(palette, (bytes, bytearray, list)): + source_palette = bytearray(palette[:768]) + if isinstance(palette, ImagePalette.ImagePalette): + source_palette = bytearray( + itertools.chain.from_iterable( + zip( + palette.palette[:256], + palette.palette[256:512], + palette.palette[512:768], + ) + ) + ) + + if im.mode == "P": + if not source_palette: + source_palette = im.im.getpalette("RGB")[:768] + else: # L-mode + if not source_palette: + source_palette = bytearray(i // 3 for i in range(768)) + im.palette = ImagePalette.ImagePalette("RGB", palette=source_palette) + + used_palette_colors = _get_optimize(im, info) + if used_palette_colors is not None: + return im.remap_palette(used_palette_colors, source_palette) + + im.palette.palette = source_palette + return im + + +def _write_single_frame(im, fp, palette): + im_out = _normalize_mode(im, True) + for k, v in im_out.info.items(): + im.encoderinfo.setdefault(k, v) + im_out = _normalize_palette(im_out, palette, im.encoderinfo) + + for s in _get_global_header(im_out, im.encoderinfo): + fp.write(s) + + # local image header + flags = 0 + if get_interlace(im): + flags = flags | 64 + _write_local_header(fp, im, (0, 0), flags) + + im_out.encoderconfig = (8, get_interlace(im)) + ImageFile._save(im_out, fp, [("gif", (0, 0) + im.size, 0, RAWMODE[im_out.mode])]) + + fp.write(b"\0") # end of image data + + +def _write_multiple_frames(im, fp, palette): + + duration = im.encoderinfo.get("duration", im.info.get("duration")) + disposal = im.encoderinfo.get("disposal", im.info.get("disposal")) + + im_frames = [] + frame_count = 0 + background_im = None + for imSequence in itertools.chain([im], im.encoderinfo.get("append_images", [])): + for im_frame in ImageSequence.Iterator(imSequence): + # a copy is required here since seek can still mutate the image + im_frame = _normalize_mode(im_frame.copy()) + if frame_count == 0: + for k, v in im_frame.info.items(): + im.encoderinfo.setdefault(k, v) + im_frame = _normalize_palette(im_frame, palette, im.encoderinfo) + + encoderinfo = im.encoderinfo.copy() + if isinstance(duration, (list, tuple)): + encoderinfo["duration"] = duration[frame_count] + if isinstance(disposal, (list, tuple)): + encoderinfo["disposal"] = disposal[frame_count] + frame_count += 1 + + if im_frames: + # delta frame + previous = im_frames[-1] + if encoderinfo.get("disposal") == 2: + if background_im is None: + background = _get_background( + im, + im.encoderinfo.get("background", im.info.get("background")), + ) + background_im = Image.new("P", im_frame.size, background) + background_im.putpalette(im_frames[0]["im"].palette) + base_im = background_im + else: + base_im = previous["im"] + if _get_palette_bytes(im_frame) == _get_palette_bytes(base_im): + delta = ImageChops.subtract_modulo(im_frame, base_im) + else: + delta = ImageChops.subtract_modulo( + im_frame.convert("RGB"), base_im.convert("RGB") + ) + bbox = delta.getbbox() + if not bbox: + # This frame is identical to the previous frame + if duration: + previous["encoderinfo"]["duration"] += encoderinfo["duration"] + continue + else: + bbox = None + im_frames.append({"im": im_frame, "bbox": bbox, "encoderinfo": encoderinfo}) + + if len(im_frames) > 1: + for frame_data in im_frames: + im_frame = frame_data["im"] + if not frame_data["bbox"]: + # global header + for s in _get_global_header(im_frame, frame_data["encoderinfo"]): + fp.write(s) + offset = (0, 0) + else: + # compress difference + frame_data["encoderinfo"]["include_color_table"] = True + + im_frame = im_frame.crop(frame_data["bbox"]) + offset = frame_data["bbox"][:2] + _write_frame_data(fp, im_frame, offset, frame_data["encoderinfo"]) + return True + elif "duration" in im.encoderinfo and isinstance( + im.encoderinfo["duration"], (list, tuple) + ): + # Since multiple frames will not be written, add together the frame durations + im.encoderinfo["duration"] = sum(im.encoderinfo["duration"]) + + +def _save_all(im, fp, filename): + _save(im, fp, filename, save_all=True) + + +def _save(im, fp, filename, save_all=False): + # header + if "palette" in im.encoderinfo or "palette" in im.info: + palette = im.encoderinfo.get("palette", im.info.get("palette")) + else: + palette = None + im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True) + + if not save_all or not _write_multiple_frames(im, fp, palette): + _write_single_frame(im, fp, palette) + + fp.write(b";") # end of file + + if hasattr(fp, "flush"): + fp.flush() + + +def get_interlace(im): + interlace = im.encoderinfo.get("interlace", 1) + + # workaround for @PIL153 + if min(im.size) < 16: + interlace = 0 + + return interlace + + +def _write_local_header(fp, im, offset, flags): + transparent_color_exists = False + try: + transparency = im.encoderinfo["transparency"] + except KeyError: + pass + else: + transparency = int(transparency) + # optimize the block away if transparent color is not used + transparent_color_exists = True + + used_palette_colors = _get_optimize(im, im.encoderinfo) + if used_palette_colors is not None: + # adjust the transparency index after optimize + try: + transparency = used_palette_colors.index(transparency) + except ValueError: + transparent_color_exists = False + + if "duration" in im.encoderinfo: + duration = int(im.encoderinfo["duration"] / 10) + else: + duration = 0 + + disposal = int(im.encoderinfo.get("disposal", 0)) + + if transparent_color_exists or duration != 0 or disposal: + packed_flag = 1 if transparent_color_exists else 0 + packed_flag |= disposal << 2 + if not transparent_color_exists: + transparency = 0 + + fp.write( + b"!" + + o8(249) # extension intro + + o8(4) # length + + o8(packed_flag) # packed fields + + o16(duration) # duration + + o8(transparency) # transparency index + + o8(0) + ) + + if "comment" in im.encoderinfo and 1 <= len(im.encoderinfo["comment"]): + fp.write(b"!" + o8(254)) # extension intro + comment = im.encoderinfo["comment"] + if isinstance(comment, str): + comment = comment.encode() + for i in range(0, len(comment), 255): + subblock = comment[i : i + 255] + fp.write(o8(len(subblock)) + subblock) + fp.write(o8(0)) + if "loop" in im.encoderinfo: + number_of_loops = im.encoderinfo["loop"] + fp.write( + b"!" + + o8(255) # extension intro + + o8(11) + + b"NETSCAPE2.0" + + o8(3) + + o8(1) + + o16(number_of_loops) # number of loops + + o8(0) + ) + include_color_table = im.encoderinfo.get("include_color_table") + if include_color_table: + palette_bytes = _get_palette_bytes(im) + color_table_size = _get_color_table_size(palette_bytes) + if color_table_size: + flags = flags | 128 # local color table flag + flags = flags | color_table_size + + fp.write( + b"," + + o16(offset[0]) # offset + + o16(offset[1]) + + o16(im.size[0]) # size + + o16(im.size[1]) + + o8(flags) # flags + ) + if include_color_table and color_table_size: + fp.write(_get_header_palette(palette_bytes)) + fp.write(o8(8)) # bits + + +def _save_netpbm(im, fp, filename): + + # Unused by default. + # To use, uncomment the register_save call at the end of the file. + # + # If you need real GIF compression and/or RGB quantization, you + # can use the external NETPBM/PBMPLUS utilities. See comments + # below for information on how to enable this. + tempfile = im._dump() + + try: + with open(filename, "wb") as f: + if im.mode != "RGB": + subprocess.check_call( + ["ppmtogif", tempfile], stdout=f, stderr=subprocess.DEVNULL + ) + else: + # Pipe ppmquant output into ppmtogif + # "ppmquant 256 %s | ppmtogif > %s" % (tempfile, filename) + quant_cmd = ["ppmquant", "256", tempfile] + togif_cmd = ["ppmtogif"] + quant_proc = subprocess.Popen( + quant_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL + ) + togif_proc = subprocess.Popen( + togif_cmd, + stdin=quant_proc.stdout, + stdout=f, + stderr=subprocess.DEVNULL, + ) + + # Allow ppmquant to receive SIGPIPE if ppmtogif exits + quant_proc.stdout.close() + + retcode = quant_proc.wait() + if retcode: + raise subprocess.CalledProcessError(retcode, quant_cmd) + + retcode = togif_proc.wait() + if retcode: + raise subprocess.CalledProcessError(retcode, togif_cmd) + finally: + try: + os.unlink(tempfile) + except OSError: + pass + + +# Force optimization so that we can test performance against +# cases where it took lots of memory and time previously. +_FORCE_OPTIMIZE = False + + +def _get_optimize(im, info): + """ + Palette optimization is a potentially expensive operation. + + This function determines if the palette should be optimized using + some heuristics, then returns the list of palette entries in use. + + :param im: Image object + :param info: encoderinfo + :returns: list of indexes of palette entries in use, or None + """ + if im.mode in ("P", "L") and info and info.get("optimize", 0): + # Potentially expensive operation. + + # The palette saves 3 bytes per color not used, but palette + # lengths are restricted to 3*(2**N) bytes. Max saving would + # be 768 -> 6 bytes if we went all the way down to 2 colors. + # * If we're over 128 colors, we can't save any space. + # * If there aren't any holes, it's not worth collapsing. + # * If we have a 'large' image, the palette is in the noise. + + # create the new palette if not every color is used + optimise = _FORCE_OPTIMIZE or im.mode == "L" + if optimise or im.width * im.height < 512 * 512: + # check which colors are used + used_palette_colors = [] + for i, count in enumerate(im.histogram()): + if count: + used_palette_colors.append(i) + + if optimise or ( + len(used_palette_colors) <= 128 + and max(used_palette_colors) > len(used_palette_colors) + ): + return used_palette_colors + + +def _get_color_table_size(palette_bytes): + # calculate the palette size for the header + if not palette_bytes: + return 0 + elif len(palette_bytes) < 9: + return 1 + else: + return math.ceil(math.log(len(palette_bytes) // 3, 2)) - 1 + + +def _get_header_palette(palette_bytes): + """ + Returns the palette, null padded to the next power of 2 (*3) bytes + suitable for direct inclusion in the GIF header + + :param palette_bytes: Unpadded palette bytes, in RGBRGB form + :returns: Null padded palette + """ + color_table_size = _get_color_table_size(palette_bytes) + + # add the missing amount of bytes + # the palette has to be 2< 0: + palette_bytes += o8(0) * 3 * actual_target_size_diff + return palette_bytes + + +def _get_palette_bytes(im): + """ + Gets the palette for inclusion in the gif header + + :param im: Image object + :returns: Bytes, len<=768 suitable for inclusion in gif header + """ + return im.palette.palette + + +def _get_background(im, infoBackground): + background = 0 + if infoBackground: + background = infoBackground + if isinstance(background, tuple): + # WebPImagePlugin stores an RGBA value in info["background"] + # So it must be converted to the same format as GifImagePlugin's + # info["background"] - a global color table index + background = im.palette.getcolor(background) + return background + + +def _get_global_header(im, info): + """Return a list of strings representing a GIF header""" + + # Header Block + # http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp + + version = b"87a" + for extensionKey in ["transparency", "duration", "loop", "comment"]: + if info and extensionKey in info: + if (extensionKey == "duration" and info[extensionKey] == 0) or ( + extensionKey == "comment" and not (1 <= len(info[extensionKey]) <= 255) + ): + continue + version = b"89a" + break + else: + if im.info.get("version") == b"89a": + version = b"89a" + + background = _get_background(im, info.get("background")) + + palette_bytes = _get_palette_bytes(im) + color_table_size = _get_color_table_size(palette_bytes) + + return [ + b"GIF" # signature + + version # version + + o16(im.size[0]) # canvas width + + o16(im.size[1]), # canvas height + # Logical Screen Descriptor + # size of global color table + global color table flag + o8(color_table_size + 128), # packed fields + # background + reserved/aspect + o8(background) + o8(0), + # Global Color Table + _get_header_palette(palette_bytes), + ] + + +def _write_frame_data(fp, im_frame, offset, params): + try: + im_frame.encoderinfo = params + + # local image header + _write_local_header(fp, im_frame, offset, 0) + + ImageFile._save( + im_frame, fp, [("gif", (0, 0) + im_frame.size, 0, RAWMODE[im_frame.mode])] + ) + + fp.write(b"\0") # end of image data + finally: + del im_frame.encoderinfo + + +# -------------------------------------------------------------------- +# Legacy GIF utilities + + +def getheader(im, palette=None, info=None): + """ + Legacy Method to get Gif data from image. + + Warning:: May modify image data. + + :param im: Image object + :param palette: bytes object containing the source palette, or .... + :param info: encoderinfo + :returns: tuple of(list of header items, optimized palette) + + """ + used_palette_colors = _get_optimize(im, info) + + if info is None: + info = {} + + if "background" not in info and "background" in im.info: + info["background"] = im.info["background"] + + im_mod = _normalize_palette(im, palette, info) + im.palette = im_mod.palette + im.im = im_mod.im + header = _get_global_header(im, info) + + return header, used_palette_colors + + +# To specify duration, add the time in milliseconds to getdata(), +# e.g. getdata(im_frame, duration=1000) +def getdata(im, offset=(0, 0), **params): + """ + Legacy Method + + Return a list of strings representing this image. + The first string is a local image header, the rest contains + encoded image data. + + :param im: Image object + :param offset: Tuple of (x, y) pixels. Defaults to (0,0) + :param \\**params: E.g. duration or other encoder info parameters + :returns: List of Bytes containing gif encoded frame data + + """ + + class Collector: + data = [] + + def write(self, data): + self.data.append(data) + + im.load() # make sure raster data is available + + fp = Collector() + + _write_frame_data(fp, im, offset, params) + + return fp.data + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(GifImageFile.format, GifImageFile, _accept) +Image.register_save(GifImageFile.format, _save) +Image.register_save_all(GifImageFile.format, _save_all) +Image.register_extension(GifImageFile.format, ".gif") +Image.register_mime(GifImageFile.format, "image/gif") + +# +# Uncomment the following line if you wish to use NETPBM/PBMPLUS +# instead of the built-in "uncompressed" GIF encoder + +# Image.register_save(GifImageFile.format, _save_netpbm) diff --git a/venv/lib/python3.11/site-packages/PIL/GimpGradientFile.py b/venv/lib/python3.11/site-packages/PIL/GimpGradientFile.py new file mode 100644 index 00000000..7ab7f999 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/GimpGradientFile.py @@ -0,0 +1,140 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read (and render) GIMP gradient files +# +# History: +# 97-08-23 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +""" +Stuff to translate curve segments to palette values (derived from +the corresponding code in GIMP, written by Federico Mena Quintero. +See the GIMP distribution for more information.) +""" + + +from math import log, pi, sin, sqrt + +from ._binary import o8 + +EPSILON = 1e-10 +"""""" # Enable auto-doc for data member + + +def linear(middle, pos): + if pos <= middle: + if middle < EPSILON: + return 0.0 + else: + return 0.5 * pos / middle + else: + pos = pos - middle + middle = 1.0 - middle + if middle < EPSILON: + return 1.0 + else: + return 0.5 + 0.5 * pos / middle + + +def curved(middle, pos): + return pos ** (log(0.5) / log(max(middle, EPSILON))) + + +def sine(middle, pos): + return (sin((-pi / 2.0) + pi * linear(middle, pos)) + 1.0) / 2.0 + + +def sphere_increasing(middle, pos): + return sqrt(1.0 - (linear(middle, pos) - 1.0) ** 2) + + +def sphere_decreasing(middle, pos): + return 1.0 - sqrt(1.0 - linear(middle, pos) ** 2) + + +SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing] +"""""" # Enable auto-doc for data member + + +class GradientFile: + + gradient = None + + def getpalette(self, entries=256): + + palette = [] + + ix = 0 + x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] + + for i in range(entries): + + x = i / (entries - 1) + + while x1 < x: + ix += 1 + x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] + + w = x1 - x0 + + if w < EPSILON: + scale = segment(0.5, 0.5) + else: + scale = segment((xm - x0) / w, (x - x0) / w) + + # expand to RGBA + r = o8(int(255 * ((rgb1[0] - rgb0[0]) * scale + rgb0[0]) + 0.5)) + g = o8(int(255 * ((rgb1[1] - rgb0[1]) * scale + rgb0[1]) + 0.5)) + b = o8(int(255 * ((rgb1[2] - rgb0[2]) * scale + rgb0[2]) + 0.5)) + a = o8(int(255 * ((rgb1[3] - rgb0[3]) * scale + rgb0[3]) + 0.5)) + + # add to palette + palette.append(r + g + b + a) + + return b"".join(palette), "RGBA" + + +class GimpGradientFile(GradientFile): + """File handler for GIMP's gradient format.""" + + def __init__(self, fp): + + if fp.readline()[:13] != b"GIMP Gradient": + raise SyntaxError("not a GIMP gradient file") + + line = fp.readline() + + # GIMP 1.2 gradient files don't contain a name, but GIMP 1.3 files do + if line.startswith(b"Name: "): + line = fp.readline().strip() + + count = int(line) + + gradient = [] + + for i in range(count): + + s = fp.readline().split() + w = [float(x) for x in s[:11]] + + x0, x1 = w[0], w[2] + xm = w[1] + rgb0 = w[3:7] + rgb1 = w[7:11] + + segment = SEGMENTS[int(s[11])] + cspace = int(s[12]) + + if cspace != 0: + raise OSError("cannot handle HSV colour space") + + gradient.append((x0, x1, xm, rgb0, rgb1, segment)) + + self.gradient = gradient diff --git a/venv/lib/python3.11/site-packages/PIL/GimpPaletteFile.py b/venv/lib/python3.11/site-packages/PIL/GimpPaletteFile.py new file mode 100644 index 00000000..10fd3ad8 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/GimpPaletteFile.py @@ -0,0 +1,56 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read GIMP palette files +# +# History: +# 1997-08-23 fl Created +# 2004-09-07 fl Support GIMP 2.0 palette files. +# +# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. +# Copyright (c) Fredrik Lundh 1997-2004. +# +# See the README file for information on usage and redistribution. +# + +import re + +from ._binary import o8 + + +class GimpPaletteFile: + """File handler for GIMP's palette format.""" + + rawmode = "RGB" + + def __init__(self, fp): + + self.palette = [o8(i) * 3 for i in range(256)] + + if fp.readline()[:12] != b"GIMP Palette": + raise SyntaxError("not a GIMP palette file") + + for i in range(256): + + s = fp.readline() + if not s: + break + + # skip fields and comment lines + if re.match(br"\w+:|#", s): + continue + if len(s) > 100: + raise SyntaxError("bad palette file") + + v = tuple(map(int, s.split()[:3])) + if len(v) != 3: + raise ValueError("bad palette entry") + + self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2]) + + self.palette = b"".join(self.palette) + + def getpalette(self): + + return self.palette, self.rawmode diff --git a/venv/lib/python3.11/site-packages/PIL/GribStubImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/GribStubImagePlugin.py new file mode 100644 index 00000000..b9bdd16e --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/GribStubImagePlugin.py @@ -0,0 +1,73 @@ +# +# The Python Imaging Library +# $Id$ +# +# GRIB stub adapter +# +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from . import Image, ImageFile + +_handler = None + + +def register_handler(handler): + """ + Install application-specific GRIB image handler. + + :param handler: Handler object. + """ + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + + +def _accept(prefix): + return prefix[0:4] == b"GRIB" and prefix[7] == 1 + + +class GribStubImageFile(ImageFile.StubImageFile): + + format = "GRIB" + format_description = "GRIB" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(8)): + raise SyntaxError("Not a GRIB file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self._size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise OSError("GRIB save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(GribStubImageFile.format, GribStubImageFile, _accept) +Image.register_save(GribStubImageFile.format, _save) + +Image.register_extension(GribStubImageFile.format, ".grib") diff --git a/venv/lib/python3.11/site-packages/PIL/Hdf5StubImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/Hdf5StubImagePlugin.py new file mode 100644 index 00000000..362f2d39 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/Hdf5StubImagePlugin.py @@ -0,0 +1,73 @@ +# +# The Python Imaging Library +# $Id$ +# +# HDF5 stub adapter +# +# Copyright (c) 2000-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from . import Image, ImageFile + +_handler = None + + +def register_handler(handler): + """ + Install application-specific HDF5 image handler. + + :param handler: Handler object. + """ + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + + +def _accept(prefix): + return prefix[:8] == b"\x89HDF\r\n\x1a\n" + + +class HDF5StubImageFile(ImageFile.StubImageFile): + + format = "HDF5" + format_description = "HDF5" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(8)): + raise SyntaxError("Not an HDF file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self._size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise OSError("HDF5 save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(HDF5StubImageFile.format, HDF5StubImageFile, _accept) +Image.register_save(HDF5StubImageFile.format, _save) + +Image.register_extensions(HDF5StubImageFile.format, [".h5", ".hdf"]) diff --git a/venv/lib/python3.11/site-packages/PIL/IcnsImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/IcnsImagePlugin.py new file mode 100644 index 00000000..2a63d75c --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/IcnsImagePlugin.py @@ -0,0 +1,383 @@ +# +# The Python Imaging Library. +# $Id$ +# +# macOS icns file decoder, based on icns.py by Bob Ippolito. +# +# history: +# 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies. +# +# Copyright (c) 2004 by Bob Ippolito. +# Copyright (c) 2004 by Secret Labs. +# Copyright (c) 2004 by Fredrik Lundh. +# Copyright (c) 2014 by Alastair Houghton. +# +# See the README file for information on usage and redistribution. +# + +import io +import os +import shutil +import struct +import subprocess +import sys +import tempfile + +from PIL import Image, ImageFile, PngImagePlugin, features + +enable_jpeg2k = features.check_codec("jpg_2000") +if enable_jpeg2k: + from PIL import Jpeg2KImagePlugin + +HEADERSIZE = 8 + + +def nextheader(fobj): + return struct.unpack(">4sI", fobj.read(HEADERSIZE)) + + +def read_32t(fobj, start_length, size): + # The 128x128 icon seems to have an extra header for some reason. + (start, length) = start_length + fobj.seek(start) + sig = fobj.read(4) + if sig != b"\x00\x00\x00\x00": + raise SyntaxError("Unknown signature, expecting 0x00000000") + return read_32(fobj, (start + 4, length - 4), size) + + +def read_32(fobj, start_length, size): + """ + Read a 32bit RGB icon resource. Seems to be either uncompressed or + an RLE packbits-like scheme. + """ + (start, length) = start_length + fobj.seek(start) + pixel_size = (size[0] * size[2], size[1] * size[2]) + sizesq = pixel_size[0] * pixel_size[1] + if length == sizesq * 3: + # uncompressed ("RGBRGBGB") + indata = fobj.read(length) + im = Image.frombuffer("RGB", pixel_size, indata, "raw", "RGB", 0, 1) + else: + # decode image + im = Image.new("RGB", pixel_size, None) + for band_ix in range(3): + data = [] + bytesleft = sizesq + while bytesleft > 0: + byte = fobj.read(1) + if not byte: + break + byte = byte[0] + if byte & 0x80: + blocksize = byte - 125 + byte = fobj.read(1) + for i in range(blocksize): + data.append(byte) + else: + blocksize = byte + 1 + data.append(fobj.read(blocksize)) + bytesleft -= blocksize + if bytesleft <= 0: + break + if bytesleft != 0: + raise SyntaxError(f"Error reading channel [{repr(bytesleft)} left]") + band = Image.frombuffer("L", pixel_size, b"".join(data), "raw", "L", 0, 1) + im.im.putband(band.im, band_ix) + return {"RGB": im} + + +def read_mk(fobj, start_length, size): + # Alpha masks seem to be uncompressed + start = start_length[0] + fobj.seek(start) + pixel_size = (size[0] * size[2], size[1] * size[2]) + sizesq = pixel_size[0] * pixel_size[1] + band = Image.frombuffer("L", pixel_size, fobj.read(sizesq), "raw", "L", 0, 1) + return {"A": band} + + +def read_png_or_jpeg2000(fobj, start_length, size): + (start, length) = start_length + fobj.seek(start) + sig = fobj.read(12) + if sig[:8] == b"\x89PNG\x0d\x0a\x1a\x0a": + fobj.seek(start) + im = PngImagePlugin.PngImageFile(fobj) + return {"RGBA": im} + elif ( + sig[:4] == b"\xff\x4f\xff\x51" + or sig[:4] == b"\x0d\x0a\x87\x0a" + or sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a" + ): + if not enable_jpeg2k: + raise ValueError( + "Unsupported icon subimage format (rebuild PIL " + "with JPEG 2000 support to fix this)" + ) + # j2k, jpc or j2c + fobj.seek(start) + jp2kstream = fobj.read(length) + f = io.BytesIO(jp2kstream) + im = Jpeg2KImagePlugin.Jpeg2KImageFile(f) + if im.mode != "RGBA": + im = im.convert("RGBA") + return {"RGBA": im} + else: + raise ValueError("Unsupported icon subimage format") + + +class IcnsFile: + + SIZES = { + (512, 512, 2): [(b"ic10", read_png_or_jpeg2000)], + (512, 512, 1): [(b"ic09", read_png_or_jpeg2000)], + (256, 256, 2): [(b"ic14", read_png_or_jpeg2000)], + (256, 256, 1): [(b"ic08", read_png_or_jpeg2000)], + (128, 128, 2): [(b"ic13", read_png_or_jpeg2000)], + (128, 128, 1): [ + (b"ic07", read_png_or_jpeg2000), + (b"it32", read_32t), + (b"t8mk", read_mk), + ], + (64, 64, 1): [(b"icp6", read_png_or_jpeg2000)], + (32, 32, 2): [(b"ic12", read_png_or_jpeg2000)], + (48, 48, 1): [(b"ih32", read_32), (b"h8mk", read_mk)], + (32, 32, 1): [ + (b"icp5", read_png_or_jpeg2000), + (b"il32", read_32), + (b"l8mk", read_mk), + ], + (16, 16, 2): [(b"ic11", read_png_or_jpeg2000)], + (16, 16, 1): [ + (b"icp4", read_png_or_jpeg2000), + (b"is32", read_32), + (b"s8mk", read_mk), + ], + } + + def __init__(self, fobj): + """ + fobj is a file-like object as an icns resource + """ + # signature : (start, length) + self.dct = dct = {} + self.fobj = fobj + sig, filesize = nextheader(fobj) + if sig != b"icns": + raise SyntaxError("not an icns file") + i = HEADERSIZE + while i < filesize: + sig, blocksize = nextheader(fobj) + if blocksize <= 0: + raise SyntaxError("invalid block header") + i += HEADERSIZE + blocksize -= HEADERSIZE + dct[sig] = (i, blocksize) + fobj.seek(blocksize, io.SEEK_CUR) + i += blocksize + + def itersizes(self): + sizes = [] + for size, fmts in self.SIZES.items(): + for (fmt, reader) in fmts: + if fmt in self.dct: + sizes.append(size) + break + return sizes + + def bestsize(self): + sizes = self.itersizes() + if not sizes: + raise SyntaxError("No 32bit icon resources found") + return max(sizes) + + def dataforsize(self, size): + """ + Get an icon resource as {channel: array}. Note that + the arrays are bottom-up like windows bitmaps and will likely + need to be flipped or transposed in some way. + """ + dct = {} + for code, reader in self.SIZES[size]: + desc = self.dct.get(code) + if desc is not None: + dct.update(reader(self.fobj, desc, size)) + return dct + + def getimage(self, size=None): + if size is None: + size = self.bestsize() + if len(size) == 2: + size = (size[0], size[1], 1) + channels = self.dataforsize(size) + + im = channels.get("RGBA", None) + if im: + return im + + im = channels.get("RGB").copy() + try: + im.putalpha(channels["A"]) + except KeyError: + pass + return im + + +## +# Image plugin for Mac OS icons. + + +class IcnsImageFile(ImageFile.ImageFile): + """ + PIL image support for Mac OS .icns files. + Chooses the best resolution, but will possibly load + a different size image if you mutate the size attribute + before calling 'load'. + + The info dictionary has a key 'sizes' that is a list + of sizes that the icns file has. + """ + + format = "ICNS" + format_description = "Mac OS icns resource" + + def _open(self): + self.icns = IcnsFile(self.fp) + self.mode = "RGBA" + self.info["sizes"] = self.icns.itersizes() + self.best_size = self.icns.bestsize() + self.size = ( + self.best_size[0] * self.best_size[2], + self.best_size[1] * self.best_size[2], + ) + + @property + def size(self): + return self._size + + @size.setter + def size(self, value): + info_size = value + if info_size not in self.info["sizes"] and len(info_size) == 2: + info_size = (info_size[0], info_size[1], 1) + if ( + info_size not in self.info["sizes"] + and len(info_size) == 3 + and info_size[2] == 1 + ): + simple_sizes = [ + (size[0] * size[2], size[1] * size[2]) for size in self.info["sizes"] + ] + if value in simple_sizes: + info_size = self.info["sizes"][simple_sizes.index(value)] + if info_size not in self.info["sizes"]: + raise ValueError("This is not one of the allowed sizes of this image") + self._size = value + + def load(self): + if len(self.size) == 3: + self.best_size = self.size + self.size = ( + self.best_size[0] * self.best_size[2], + self.best_size[1] * self.best_size[2], + ) + + Image.Image.load(self) + if self.im and self.im.size == self.size: + # Already loaded + return + self.load_prepare() + # This is likely NOT the best way to do it, but whatever. + im = self.icns.getimage(self.best_size) + + # If this is a PNG or JPEG 2000, it won't be loaded yet + im.load() + + self.im = im.im + self.mode = im.mode + self.size = im.size + self.load_end() + + +def _save(im, fp, filename): + """ + Saves the image as a series of PNG files, + that are then converted to a .icns file + using the macOS command line utility 'iconutil'. + + macOS only. + """ + if hasattr(fp, "flush"): + fp.flush() + + # create the temporary set of pngs + with tempfile.TemporaryDirectory(".iconset") as iconset: + provided_images = { + im.width: im for im in im.encoderinfo.get("append_images", []) + } + last_w = None + second_path = None + for w in [16, 32, 128, 256, 512]: + prefix = f"icon_{w}x{w}" + + first_path = os.path.join(iconset, prefix + ".png") + if last_w == w: + shutil.copyfile(second_path, first_path) + else: + im_w = provided_images.get(w, im.resize((w, w), Image.LANCZOS)) + im_w.save(first_path) + + second_path = os.path.join(iconset, prefix + "@2x.png") + im_w2 = provided_images.get(w * 2, im.resize((w * 2, w * 2), Image.LANCZOS)) + im_w2.save(second_path) + last_w = w * 2 + + # iconutil -c icns -o {} {} + + fp_only = not filename + if fp_only: + f, filename = tempfile.mkstemp(".icns") + os.close(f) + convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset] + convert_proc = subprocess.Popen( + convert_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL + ) + + convert_proc.stdout.close() + + retcode = convert_proc.wait() + + if retcode: + raise subprocess.CalledProcessError(retcode, convert_cmd) + + if fp_only: + with open(filename, "rb") as f: + fp.write(f.read()) + + +Image.register_open(IcnsImageFile.format, IcnsImageFile, lambda x: x[:4] == b"icns") +Image.register_extension(IcnsImageFile.format, ".icns") + +if sys.platform == "darwin": + Image.register_save(IcnsImageFile.format, _save) + + Image.register_mime(IcnsImageFile.format, "image/icns") + + +if __name__ == "__main__": + + if len(sys.argv) < 2: + print("Syntax: python IcnsImagePlugin.py [file]") + sys.exit() + + with open(sys.argv[1], "rb") as fp: + imf = IcnsImageFile(fp) + for size in imf.info["sizes"]: + imf.size = size + imf.save("out-%s-%s-%s.png" % size) + with Image.open(sys.argv[1]) as im: + im.save("out.png") + if sys.platform == "windows": + os.startfile("out.png") diff --git a/venv/lib/python3.11/site-packages/PIL/IcoImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/IcoImagePlugin.py new file mode 100644 index 00000000..e1bfa7a5 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/IcoImagePlugin.py @@ -0,0 +1,328 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Windows Icon support for PIL +# +# History: +# 96-05-27 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis +# . +# https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki +# +# Icon format references: +# * https://en.wikipedia.org/wiki/ICO_(file_format) +# * https://msdn.microsoft.com/en-us/library/ms997538.aspx + + +import struct +import warnings +from io import BytesIO +from math import ceil, log + +from . import BmpImagePlugin, Image, ImageFile, PngImagePlugin +from ._binary import i16le as i16 +from ._binary import i32le as i32 + +# +# -------------------------------------------------------------------- + +_MAGIC = b"\0\0\1\0" + + +def _save(im, fp, filename): + fp.write(_MAGIC) # (2+2) + sizes = im.encoderinfo.get( + "sizes", + [(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)], + ) + width, height = im.size + sizes = filter( + lambda x: False + if (x[0] > width or x[1] > height or x[0] > 256 or x[1] > 256) + else True, + sizes, + ) + sizes = list(sizes) + fp.write(struct.pack("=8bpp) + "reserved": s[3], + "planes": i16(s, 4), + "bpp": i16(s, 6), + "size": i32(s, 8), + "offset": i32(s, 12), + } + + # See Wikipedia + for j in ("width", "height"): + if not icon_header[j]: + icon_header[j] = 256 + + # See Wikipedia notes about color depth. + # We need this just to differ images with equal sizes + icon_header["color_depth"] = ( + icon_header["bpp"] + or ( + icon_header["nb_color"] != 0 + and ceil(log(icon_header["nb_color"], 2)) + ) + or 256 + ) + + icon_header["dim"] = (icon_header["width"], icon_header["height"]) + icon_header["square"] = icon_header["width"] * icon_header["height"] + + self.entry.append(icon_header) + + self.entry = sorted(self.entry, key=lambda x: x["color_depth"]) + # ICO images are usually squares + # self.entry = sorted(self.entry, key=lambda x: x['width']) + self.entry = sorted(self.entry, key=lambda x: x["square"]) + self.entry.reverse() + + def sizes(self): + """ + Get a list of all available icon sizes and color depths. + """ + return {(h["width"], h["height"]) for h in self.entry} + + def getentryindex(self, size, bpp=False): + for (i, h) in enumerate(self.entry): + if size == h["dim"] and (bpp is False or bpp == h["color_depth"]): + return i + return 0 + + def getimage(self, size, bpp=False): + """ + Get an image from the icon + """ + return self.frame(self.getentryindex(size, bpp)) + + def frame(self, idx): + """ + Get an image from frame idx + """ + + header = self.entry[idx] + + self.buf.seek(header["offset"]) + data = self.buf.read(8) + self.buf.seek(header["offset"]) + + if data[:8] == PngImagePlugin._MAGIC: + # png frame + im = PngImagePlugin.PngImageFile(self.buf) + else: + # XOR + AND mask bmp frame + im = BmpImagePlugin.DibImageFile(self.buf) + Image._decompression_bomb_check(im.size) + + # change tile dimension to only encompass XOR image + im._size = (im.size[0], int(im.size[1] / 2)) + d, e, o, a = im.tile[0] + im.tile[0] = d, (0, 0) + im.size, o, a + + # figure out where AND mask image starts + mode = a[0] + bpp = 8 + for k, v in BmpImagePlugin.BIT2MODE.items(): + if mode == v[1]: + bpp = k + break + + if 32 == bpp: + # 32-bit color depth icon image allows semitransparent areas + # PIL's DIB format ignores transparency bits, recover them. + # The DIB is packed in BGRX byte order where X is the alpha + # channel. + + # Back up to start of bmp data + self.buf.seek(o) + # extract every 4th byte (eg. 3,7,11,15,...) + alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4] + + # convert to an 8bpp grayscale image + mask = Image.frombuffer( + "L", # 8bpp + im.size, # (w, h) + alpha_bytes, # source chars + "raw", # raw decoder + ("L", 0, -1), # 8bpp inverted, unpadded, reversed + ) + else: + # get AND image from end of bitmap + w = im.size[0] + if (w % 32) > 0: + # bitmap row data is aligned to word boundaries + w += 32 - (im.size[0] % 32) + + # the total mask data is + # padded row size * height / bits per char + + and_mask_offset = o + int(im.size[0] * im.size[1] * (bpp / 8.0)) + total_bytes = int((w * im.size[1]) / 8) + + self.buf.seek(and_mask_offset) + mask_data = self.buf.read(total_bytes) + + # convert raw data to image + mask = Image.frombuffer( + "1", # 1 bpp + im.size, # (w, h) + mask_data, # source chars + "raw", # raw decoder + ("1;I", int(w / 8), -1), # 1bpp inverted, padded, reversed + ) + + # now we have two images, im is XOR image and mask is AND image + + # apply mask image as alpha channel + im = im.convert("RGBA") + im.putalpha(mask) + + return im + + +## +# Image plugin for Windows Icon files. + + +class IcoImageFile(ImageFile.ImageFile): + """ + PIL read-only image support for Microsoft Windows .ico files. + + By default the largest resolution image in the file will be loaded. This + can be changed by altering the 'size' attribute before calling 'load'. + + The info dictionary has a key 'sizes' that is a list of the sizes available + in the icon file. + + Handles classic, XP and Vista icon formats. + + When saving, PNG compression is used. Support for this was only added in + Windows Vista. + + This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis + . + https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki + """ + + format = "ICO" + format_description = "Windows Icon" + + def _open(self): + self.ico = IcoFile(self.fp) + self.info["sizes"] = self.ico.sizes() + self.size = self.ico.entry[0]["dim"] + self.load() + + @property + def size(self): + return self._size + + @size.setter + def size(self, value): + if value not in self.info["sizes"]: + raise ValueError("This is not one of the allowed sizes of this image") + self._size = value + + def load(self): + if self.im and self.im.size == self.size: + # Already loaded + return + im = self.ico.getimage(self.size) + # if tile is PNG, it won't really be loaded yet + im.load() + self.im = im.im + self.mode = im.mode + if im.size != self.size: + warnings.warn("Image was not the expected size") + + index = self.ico.getentryindex(self.size) + sizes = list(self.info["sizes"]) + sizes[index] = im.size + self.info["sizes"] = set(sizes) + + self.size = im.size + + def load_seek(self): + # Flag the ImageFile.Parser so that it + # just does all the decode at the end. + pass + + +# +# -------------------------------------------------------------------- + + +Image.register_open(IcoImageFile.format, IcoImageFile, _accept) +Image.register_save(IcoImageFile.format, _save) +Image.register_extension(IcoImageFile.format, ".ico") + +Image.register_mime(IcoImageFile.format, "image/x-icon") diff --git a/venv/lib/python3.11/site-packages/PIL/ImImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/ImImagePlugin.py new file mode 100644 index 00000000..1dfc808c --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImImagePlugin.py @@ -0,0 +1,376 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IFUNC IM file handling for PIL +# +# history: +# 1995-09-01 fl Created. +# 1997-01-03 fl Save palette images +# 1997-01-08 fl Added sequence support +# 1997-01-23 fl Added P and RGB save support +# 1997-05-31 fl Read floating point images +# 1997-06-22 fl Save floating point images +# 1997-08-27 fl Read and save 1-bit images +# 1998-06-25 fl Added support for RGB+LUT images +# 1998-07-02 fl Added support for YCC images +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 1998-12-29 fl Added I;16 support +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) +# 2003-09-26 fl Added LA/PA support +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2001 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +import os +import re + +from . import Image, ImageFile, ImagePalette + +# -------------------------------------------------------------------- +# Standard tags + +COMMENT = "Comment" +DATE = "Date" +EQUIPMENT = "Digitalization equipment" +FRAMES = "File size (no of images)" +LUT = "Lut" +NAME = "Name" +SCALE = "Scale (x,y)" +SIZE = "Image size (x*y)" +MODE = "Image type" + +TAGS = { + COMMENT: 0, + DATE: 0, + EQUIPMENT: 0, + FRAMES: 0, + LUT: 0, + NAME: 0, + SCALE: 0, + SIZE: 0, + MODE: 0, +} + +OPEN = { + # ifunc93/p3cfunc formats + "0 1 image": ("1", "1"), + "L 1 image": ("1", "1"), + "Greyscale image": ("L", "L"), + "Grayscale image": ("L", "L"), + "RGB image": ("RGB", "RGB;L"), + "RLB image": ("RGB", "RLB"), + "RYB image": ("RGB", "RLB"), + "B1 image": ("1", "1"), + "B2 image": ("P", "P;2"), + "B4 image": ("P", "P;4"), + "X 24 image": ("RGB", "RGB"), + "L 32 S image": ("I", "I;32"), + "L 32 F image": ("F", "F;32"), + # old p3cfunc formats + "RGB3 image": ("RGB", "RGB;T"), + "RYB3 image": ("RGB", "RYB;T"), + # extensions + "LA image": ("LA", "LA;L"), + "PA image": ("LA", "PA;L"), + "RGBA image": ("RGBA", "RGBA;L"), + "RGBX image": ("RGBX", "RGBX;L"), + "CMYK image": ("CMYK", "CMYK;L"), + "YCC image": ("YCbCr", "YCbCr;L"), +} + +# ifunc95 extensions +for i in ["8", "8S", "16", "16S", "32", "32F"]: + OPEN[f"L {i} image"] = ("F", f"F;{i}") + OPEN[f"L*{i} image"] = ("F", f"F;{i}") +for i in ["16", "16L", "16B"]: + OPEN[f"L {i} image"] = (f"I;{i}", f"I;{i}") + OPEN[f"L*{i} image"] = (f"I;{i}", f"I;{i}") +for i in ["32S"]: + OPEN[f"L {i} image"] = ("I", f"I;{i}") + OPEN[f"L*{i} image"] = ("I", f"I;{i}") +for i in range(2, 33): + OPEN[f"L*{i} image"] = ("F", f"F;{i}") + + +# -------------------------------------------------------------------- +# Read IM directory + +split = re.compile(br"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") + + +def number(s): + try: + return int(s) + except ValueError: + return float(s) + + +## +# Image plugin for the IFUNC IM file format. + + +class ImImageFile(ImageFile.ImageFile): + + format = "IM" + format_description = "IFUNC Image Memory" + _close_exclusive_fp_after_loading = False + + def _open(self): + + # Quick rejection: if there's not an LF among the first + # 100 bytes, this is (probably) not a text header. + + if b"\n" not in self.fp.read(100): + raise SyntaxError("not an IM file") + self.fp.seek(0) + + n = 0 + + # Default values + self.info[MODE] = "L" + self.info[SIZE] = (512, 512) + self.info[FRAMES] = 1 + + self.rawmode = "L" + + while True: + + s = self.fp.read(1) + + # Some versions of IFUNC uses \n\r instead of \r\n... + if s == b"\r": + continue + + if not s or s == b"\0" or s == b"\x1A": + break + + # FIXME: this may read whole file if not a text file + s = s + self.fp.readline() + + if len(s) > 100: + raise SyntaxError("not an IM file") + + if s[-2:] == b"\r\n": + s = s[:-2] + elif s[-1:] == b"\n": + s = s[:-1] + + try: + m = split.match(s) + except re.error as e: + raise SyntaxError("not an IM file") from e + + if m: + + k, v = m.group(1, 2) + + # Don't know if this is the correct encoding, + # but a decent guess (I guess) + k = k.decode("latin-1", "replace") + v = v.decode("latin-1", "replace") + + # Convert value as appropriate + if k in [FRAMES, SCALE, SIZE]: + v = v.replace("*", ",") + v = tuple(map(number, v.split(","))) + if len(v) == 1: + v = v[0] + elif k == MODE and v in OPEN: + v, self.rawmode = OPEN[v] + + # Add to dictionary. Note that COMMENT tags are + # combined into a list of strings. + if k == COMMENT: + if k in self.info: + self.info[k].append(v) + else: + self.info[k] = [v] + else: + self.info[k] = v + + if k in TAGS: + n += 1 + + else: + + raise SyntaxError( + "Syntax error in IM header: " + s.decode("ascii", "replace") + ) + + if not n: + raise SyntaxError("Not an IM file") + + # Basic attributes + self._size = self.info[SIZE] + self.mode = self.info[MODE] + + # Skip forward to start of image data + while s and s[0:1] != b"\x1A": + s = self.fp.read(1) + if not s: + raise SyntaxError("File truncated") + + if LUT in self.info: + # convert lookup table to palette or lut attribute + palette = self.fp.read(768) + greyscale = 1 # greyscale palette + linear = 1 # linear greyscale palette + for i in range(256): + if palette[i] == palette[i + 256] == palette[i + 512]: + if palette[i] != i: + linear = 0 + else: + greyscale = 0 + if self.mode in ["L", "LA", "P", "PA"]: + if greyscale: + if not linear: + self.lut = list(palette[:256]) + else: + if self.mode in ["L", "P"]: + self.mode = self.rawmode = "P" + elif self.mode in ["LA", "PA"]: + self.mode = "PA" + self.rawmode = "PA;L" + self.palette = ImagePalette.raw("RGB;L", palette) + elif self.mode == "RGB": + if not greyscale or not linear: + self.lut = list(palette) + + self.frame = 0 + + self.__offset = offs = self.fp.tell() + + self.__fp = self.fp # FIXME: hack + + if self.rawmode[:2] == "F;": + + # ifunc95 formats + try: + # use bit decoder (if necessary) + bits = int(self.rawmode[2:]) + if bits not in [8, 16, 32]: + self.tile = [("bit", (0, 0) + self.size, offs, (bits, 8, 3, 0, -1))] + return + except ValueError: + pass + + if self.rawmode in ["RGB;T", "RYB;T"]: + # Old LabEye/3PC files. Would be very surprised if anyone + # ever stumbled upon such a file ;-) + size = self.size[0] * self.size[1] + self.tile = [ + ("raw", (0, 0) + self.size, offs, ("G", 0, -1)), + ("raw", (0, 0) + self.size, offs + size, ("R", 0, -1)), + ("raw", (0, 0) + self.size, offs + 2 * size, ("B", 0, -1)), + ] + else: + # LabEye/IFUNC files + self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))] + + @property + def n_frames(self): + return self.info[FRAMES] + + @property + def is_animated(self): + return self.info[FRAMES] > 1 + + def seek(self, frame): + if not self._seek_check(frame): + return + + self.frame = frame + + if self.mode == "1": + bits = 1 + else: + bits = 8 * len(self.mode) + + size = ((self.size[0] * bits + 7) // 8) * self.size[1] + offs = self.__offset + frame * size + + self.fp = self.__fp + + self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))] + + def tell(self): + return self.frame + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +# +# -------------------------------------------------------------------- +# Save IM files + + +SAVE = { + # mode: (im type, raw mode) + "1": ("0 1", "1"), + "L": ("Greyscale", "L"), + "LA": ("LA", "LA;L"), + "P": ("Greyscale", "P"), + "PA": ("LA", "PA;L"), + "I": ("L 32S", "I;32S"), + "I;16": ("L 16", "I;16"), + "I;16L": ("L 16L", "I;16L"), + "I;16B": ("L 16B", "I;16B"), + "F": ("L 32F", "F;32F"), + "RGB": ("RGB", "RGB;L"), + "RGBA": ("RGBA", "RGBA;L"), + "RGBX": ("RGBX", "RGBX;L"), + "CMYK": ("CMYK", "CMYK;L"), + "YCbCr": ("YCC", "YCbCr;L"), +} + + +def _save(im, fp, filename): + + try: + image_type, rawmode = SAVE[im.mode] + except KeyError as e: + raise ValueError(f"Cannot save {im.mode} images as IM") from e + + frames = im.encoderinfo.get("frames", 1) + + fp.write(f"Image type: {image_type} image\r\n".encode("ascii")) + if filename: + # Each line must be 100 characters or less, + # or: SyntaxError("not an IM file") + # 8 characters are used for "Name: " and "\r\n" + # Keep just the filename, ditch the potentially overlong path + name, ext = os.path.splitext(os.path.basename(filename)) + name = "".join([name[: 92 - len(ext)], ext]) + + fp.write(f"Name: {name}\r\n".encode("ascii")) + fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode("ascii")) + fp.write(f"File size (no of images): {frames}\r\n".encode("ascii")) + if im.mode in ["P", "PA"]: + fp.write(b"Lut: 1\r\n") + fp.write(b"\000" * (511 - fp.tell()) + b"\032") + if im.mode in ["P", "PA"]: + fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes + ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, -1))]) + + +# +# -------------------------------------------------------------------- +# Registry + + +Image.register_open(ImImageFile.format, ImImageFile) +Image.register_save(ImImageFile.format, _save) + +Image.register_extension(ImImageFile.format, ".im") diff --git a/venv/lib/python3.11/site-packages/PIL/Image.py b/venv/lib/python3.11/site-packages/PIL/Image.py new file mode 100644 index 00000000..e2540a2b --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/Image.py @@ -0,0 +1,3491 @@ +# +# The Python Imaging Library. +# $Id$ +# +# the Image class wrapper +# +# partial release history: +# 1995-09-09 fl Created +# 1996-03-11 fl PIL release 0.0 (proof of concept) +# 1996-04-30 fl PIL release 0.1b1 +# 1999-07-28 fl PIL release 1.0 final +# 2000-06-07 fl PIL release 1.1 +# 2000-10-20 fl PIL release 1.1.1 +# 2001-05-07 fl PIL release 1.1.2 +# 2002-03-15 fl PIL release 1.1.3 +# 2003-05-10 fl PIL release 1.1.4 +# 2005-03-28 fl PIL release 1.1.5 +# 2006-12-02 fl PIL release 1.1.6 +# 2009-11-15 fl PIL release 1.1.7 +# +# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-2009 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +import atexit +import builtins +import io +import logging +import math +import numbers +import os +import struct +import sys +import tempfile +import warnings +import xml.etree.ElementTree +from collections.abc import Callable, MutableMapping +from pathlib import Path + +# VERSION was removed in Pillow 6.0.0. +# PILLOW_VERSION is deprecated and will be removed in a future release. +# Use __version__ instead. +from . import ( + ImageMode, + TiffTags, + UnidentifiedImageError, + __version__, + _plugins, + _raise_version_warning, +) +from ._binary import i32le +from ._util import deferred_error, isPath + +if sys.version_info >= (3, 7): + + def __getattr__(name): + if name == "PILLOW_VERSION": + _raise_version_warning() + return __version__ + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") + + +else: + + from . import PILLOW_VERSION + + # Silence warning + assert PILLOW_VERSION + + +logger = logging.getLogger(__name__) + + +class DecompressionBombWarning(RuntimeWarning): + pass + + +class DecompressionBombError(Exception): + pass + + +# Limit to around a quarter gigabyte for a 24-bit (3 bpp) image +MAX_IMAGE_PIXELS = int(1024 * 1024 * 1024 // 4 // 3) + + +try: + # If the _imaging C module is not present, Pillow will not load. + # Note that other modules should not refer to _imaging directly; + # import Image and use the Image.core variable instead. + # Also note that Image.core is not a publicly documented interface, + # and should be considered private and subject to change. + from . import _imaging as core + + if __version__ != getattr(core, "PILLOW_VERSION", None): + raise ImportError( + "The _imaging extension was built for another version of Pillow or PIL:\n" + f"Core version: {getattr(core, 'PILLOW_VERSION', None)}\n" + f"Pillow version: {__version__}" + ) + +except ImportError as v: + core = deferred_error(ImportError("The _imaging C module is not installed.")) + # Explanations for ways that we know we might have an import error + if str(v).startswith("Module use of python"): + # The _imaging C module is present, but not compiled for + # the right version (windows only). Print a warning, if + # possible. + warnings.warn( + "The _imaging extension was built for another version of Python.", + RuntimeWarning, + ) + elif str(v).startswith("The _imaging extension"): + warnings.warn(str(v), RuntimeWarning) + # Fail here anyway. Don't let people run with a mostly broken Pillow. + # see docs/porting.rst + raise + + +# works everywhere, win for pypy, not cpython +USE_CFFI_ACCESS = hasattr(sys, "pypy_version_info") +try: + import cffi +except ImportError: + cffi = None + + +def isImageType(t): + """ + Checks if an object is an image object. + + .. warning:: + + This function is for internal use only. + + :param t: object to check if it's an image + :returns: True if the object is an image + """ + return hasattr(t, "im") + + +# +# Constants + +NONE = 0 + +# transpose +FLIP_LEFT_RIGHT = 0 +FLIP_TOP_BOTTOM = 1 +ROTATE_90 = 2 +ROTATE_180 = 3 +ROTATE_270 = 4 +TRANSPOSE = 5 +TRANSVERSE = 6 + +# transforms (also defined in Imaging.h) +AFFINE = 0 +EXTENT = 1 +PERSPECTIVE = 2 +QUAD = 3 +MESH = 4 + +# resampling filters (also defined in Imaging.h) +NEAREST = NONE = 0 +BOX = 4 +BILINEAR = LINEAR = 2 +HAMMING = 5 +BICUBIC = CUBIC = 3 +LANCZOS = ANTIALIAS = 1 + +_filters_support = {BOX: 0.5, BILINEAR: 1.0, HAMMING: 1.0, BICUBIC: 2.0, LANCZOS: 3.0} + + +# dithers +NEAREST = NONE = 0 +ORDERED = 1 # Not yet implemented +RASTERIZE = 2 # Not yet implemented +FLOYDSTEINBERG = 3 # default + +# palettes/quantizers +WEB = 0 +ADAPTIVE = 1 + +MEDIANCUT = 0 +MAXCOVERAGE = 1 +FASTOCTREE = 2 +LIBIMAGEQUANT = 3 + +# categories +NORMAL = 0 +SEQUENCE = 1 +CONTAINER = 2 + +if hasattr(core, "DEFAULT_STRATEGY"): + DEFAULT_STRATEGY = core.DEFAULT_STRATEGY + FILTERED = core.FILTERED + HUFFMAN_ONLY = core.HUFFMAN_ONLY + RLE = core.RLE + FIXED = core.FIXED + + +# -------------------------------------------------------------------- +# Registries + +ID = [] +OPEN = {} +MIME = {} +SAVE = {} +SAVE_ALL = {} +EXTENSION = {} +DECODERS = {} +ENCODERS = {} + +# -------------------------------------------------------------------- +# Modes supported by this version + +_MODEINFO = { + # NOTE: this table will be removed in future versions. use + # getmode* functions or ImageMode descriptors instead. + # official modes + "1": ("L", "L", ("1",)), + "L": ("L", "L", ("L",)), + "I": ("L", "I", ("I",)), + "F": ("L", "F", ("F",)), + "P": ("P", "L", ("P",)), + "RGB": ("RGB", "L", ("R", "G", "B")), + "RGBX": ("RGB", "L", ("R", "G", "B", "X")), + "RGBA": ("RGB", "L", ("R", "G", "B", "A")), + "CMYK": ("RGB", "L", ("C", "M", "Y", "K")), + "YCbCr": ("RGB", "L", ("Y", "Cb", "Cr")), + "LAB": ("RGB", "L", ("L", "A", "B")), + "HSV": ("RGB", "L", ("H", "S", "V")), + # Experimental modes include I;16, I;16L, I;16B, RGBa, BGR;15, and + # BGR;24. Use these modes only if you know exactly what you're + # doing... +} + +if sys.byteorder == "little": + _ENDIAN = "<" +else: + _ENDIAN = ">" + +_MODE_CONV = { + # official modes + "1": ("|b1", None), # Bits need to be extended to bytes + "L": ("|u1", None), + "LA": ("|u1", 2), + "I": (_ENDIAN + "i4", None), + "F": (_ENDIAN + "f4", None), + "P": ("|u1", None), + "RGB": ("|u1", 3), + "RGBX": ("|u1", 4), + "RGBA": ("|u1", 4), + "CMYK": ("|u1", 4), + "YCbCr": ("|u1", 3), + "LAB": ("|u1", 3), # UNDONE - unsigned |u1i1i1 + "HSV": ("|u1", 3), + # I;16 == I;16L, and I;32 == I;32L + "I;16": ("u2", None), + "I;16L": ("i2", None), + "I;16LS": ("u4", None), + "I;32L": ("i4", None), + "I;32LS": ("= 1: + return + + try: + from . import BmpImagePlugin + + assert BmpImagePlugin + except ImportError: + pass + try: + from . import GifImagePlugin + + assert GifImagePlugin + except ImportError: + pass + try: + from . import JpegImagePlugin + + assert JpegImagePlugin + except ImportError: + pass + try: + from . import PpmImagePlugin + + assert PpmImagePlugin + except ImportError: + pass + try: + from . import PngImagePlugin + + assert PngImagePlugin + except ImportError: + pass + # try: + # import TiffImagePlugin + # assert TiffImagePlugin + # except ImportError: + # pass + + _initialized = 1 + + +def init(): + """ + Explicitly initializes the Python Imaging Library. This function + loads all available file format drivers. + """ + + global _initialized + if _initialized >= 2: + return 0 + + for plugin in _plugins: + try: + logger.debug("Importing %s", plugin) + __import__(f"PIL.{plugin}", globals(), locals(), []) + except ImportError as e: + logger.debug("Image: failed to import %s: %s", plugin, e) + + if OPEN or SAVE: + _initialized = 2 + return 1 + + +# -------------------------------------------------------------------- +# Codec factories (used by tobytes/frombytes and ImageFile.load) + + +def _getdecoder(mode, decoder_name, args, extra=()): + + # tweak arguments + if args is None: + args = () + elif not isinstance(args, tuple): + args = (args,) + + try: + decoder = DECODERS[decoder_name] + except KeyError: + pass + else: + return decoder(mode, *args + extra) + + try: + # get decoder + decoder = getattr(core, decoder_name + "_decoder") + except AttributeError as e: + raise OSError(f"decoder {decoder_name} not available") from e + return decoder(mode, *args + extra) + + +def _getencoder(mode, encoder_name, args, extra=()): + + # tweak arguments + if args is None: + args = () + elif not isinstance(args, tuple): + args = (args,) + + try: + encoder = ENCODERS[encoder_name] + except KeyError: + pass + else: + return encoder(mode, *args + extra) + + try: + # get encoder + encoder = getattr(core, encoder_name + "_encoder") + except AttributeError as e: + raise OSError(f"encoder {encoder_name} not available") from e + return encoder(mode, *args + extra) + + +# -------------------------------------------------------------------- +# Simple expression analyzer + + +def coerce_e(value): + return value if isinstance(value, _E) else _E(value) + + +class _E: + def __init__(self, data): + self.data = data + + def __add__(self, other): + return _E((self.data, "__add__", coerce_e(other).data)) + + def __mul__(self, other): + return _E((self.data, "__mul__", coerce_e(other).data)) + + +def _getscaleoffset(expr): + stub = ["stub"] + data = expr(_E(stub)).data + try: + (a, b, c) = data # simplified syntax + if a is stub and b == "__mul__" and isinstance(c, numbers.Number): + return c, 0.0 + if a is stub and b == "__add__" and isinstance(c, numbers.Number): + return 1.0, c + except TypeError: + pass + try: + ((a, b, c), d, e) = data # full syntax + if ( + a is stub + and b == "__mul__" + and isinstance(c, numbers.Number) + and d == "__add__" + and isinstance(e, numbers.Number) + ): + return c, e + except TypeError: + pass + raise ValueError("illegal expression") + + +# -------------------------------------------------------------------- +# Implementation wrapper + + +class Image: + """ + This class represents an image object. To create + :py:class:`~PIL.Image.Image` objects, use the appropriate factory + functions. There's hardly ever any reason to call the Image constructor + directly. + + * :py:func:`~PIL.Image.open` + * :py:func:`~PIL.Image.new` + * :py:func:`~PIL.Image.frombytes` + """ + + format = None + format_description = None + _close_exclusive_fp_after_loading = True + + def __init__(self): + # FIXME: take "new" parameters / other image? + # FIXME: turn mode and size into delegating properties? + self.im = None + self.mode = "" + self._size = (0, 0) + self.palette = None + self.info = {} + self.category = NORMAL + self.readonly = 0 + self.pyaccess = None + self._exif = None + + @property + def width(self): + return self.size[0] + + @property + def height(self): + return self.size[1] + + @property + def size(self): + return self._size + + def _new(self, im): + new = Image() + new.im = im + new.mode = im.mode + new._size = im.size + if im.mode in ("P", "PA"): + if self.palette: + new.palette = self.palette.copy() + else: + from . import ImagePalette + + new.palette = ImagePalette.ImagePalette() + new.info = self.info.copy() + return new + + # Context manager support + def __enter__(self): + return self + + def __exit__(self, *args): + if hasattr(self, "fp") and getattr(self, "_exclusive_fp", False): + if hasattr(self, "_close__fp"): + self._close__fp() + if self.fp: + self.fp.close() + self.fp = None + + def close(self): + """ + Closes the file pointer, if possible. + + This operation will destroy the image core and release its memory. + The image data will be unusable afterward. + + This function is only required to close images that have not + had their file read and closed by the + :py:meth:`~PIL.Image.Image.load` method. See + :ref:`file-handling` for more information. + """ + try: + if hasattr(self, "_close__fp"): + self._close__fp() + if self.fp: + self.fp.close() + self.fp = None + except Exception as msg: + logger.debug("Error closing: %s", msg) + + if getattr(self, "map", None): + self.map = None + + # Instead of simply setting to None, we're setting up a + # deferred error that will better explain that the core image + # object is gone. + self.im = deferred_error(ValueError("Operation on closed image")) + + def _copy(self): + self.load() + self.im = self.im.copy() + self.pyaccess = None + self.readonly = 0 + + def _ensure_mutable(self): + if self.readonly: + self._copy() + else: + self.load() + + def _dump(self, file=None, format=None, **options): + suffix = "" + if format: + suffix = "." + format + + if not file: + f, filename = tempfile.mkstemp(suffix) + os.close(f) + else: + filename = file + if not filename.endswith(suffix): + filename = filename + suffix + + self.load() + + if not format or format == "PPM": + self.im.save_ppm(filename) + else: + self.save(filename, format, **options) + + return filename + + def __eq__(self, other): + return ( + self.__class__ is other.__class__ + and self.mode == other.mode + and self.size == other.size + and self.info == other.info + and self.category == other.category + and self.readonly == other.readonly + and self.getpalette() == other.getpalette() + and self.tobytes() == other.tobytes() + ) + + def __repr__(self): + return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % ( + self.__class__.__module__, + self.__class__.__name__, + self.mode, + self.size[0], + self.size[1], + id(self), + ) + + def _repr_png_(self): + """iPython display hook support + + :returns: png version of the image as bytes + """ + b = io.BytesIO() + try: + self.save(b, "PNG") + except Exception as e: + raise ValueError("Could not save to PNG for display") from e + return b.getvalue() + + @property + def __array_interface__(self): + # numpy array interface support + new = {} + shape, typestr = _conv_type_shape(self) + new["shape"] = shape + new["typestr"] = typestr + new["version"] = 3 + if self.mode == "1": + # Binary images need to be extended from bits to bytes + # See: https://github.com/python-pillow/Pillow/issues/350 + new["data"] = self.tobytes("raw", "L") + else: + new["data"] = self.tobytes() + return new + + def __getstate__(self): + return [self.info, self.mode, self.size, self.getpalette(), self.tobytes()] + + def __setstate__(self, state): + Image.__init__(self) + self.tile = [] + info, mode, size, palette, data = state + self.info = info + self.mode = mode + self._size = size + self.im = core.new(mode, size) + if mode in ("L", "LA", "P", "PA") and palette: + self.putpalette(palette) + self.frombytes(data) + + def tobytes(self, encoder_name="raw", *args): + """ + Return image as a bytes object. + + .. warning:: + + This method returns the raw image data from the internal + storage. For compressed image data (e.g. PNG, JPEG) use + :meth:`~.save`, with a BytesIO parameter for in-memory + data. + + :param encoder_name: What encoder to use. The default is to + use the standard "raw" encoder. + :param args: Extra arguments to the encoder. + :returns: A :py:class:`bytes` object. + """ + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + if encoder_name == "raw" and args == (): + args = self.mode + + self.load() + + # unpack data + e = _getencoder(self.mode, encoder_name, args) + e.setimage(self.im) + + bufsize = max(65536, self.size[0] * 4) # see RawEncode.c + + data = [] + while True: + l, s, d = e.encode(bufsize) + data.append(d) + if s: + break + if s < 0: + raise RuntimeError(f"encoder error {s} in tobytes") + + return b"".join(data) + + def tobitmap(self, name="image"): + """ + Returns the image converted to an X11 bitmap. + + .. note:: This method only works for mode "1" images. + + :param name: The name prefix to use for the bitmap variables. + :returns: A string containing an X11 bitmap. + :raises ValueError: If the mode is not "1" + """ + + self.load() + if self.mode != "1": + raise ValueError("not a bitmap") + data = self.tobytes("xbm") + return b"".join( + [ + f"#define {name}_width {self.size[0]}\n".encode("ascii"), + f"#define {name}_height {self.size[1]}\n".encode("ascii"), + f"static char {name}_bits[] = {{\n".encode("ascii"), + data, + b"};", + ] + ) + + def frombytes(self, data, decoder_name="raw", *args): + """ + Loads this image with pixel data from a bytes object. + + This method is similar to the :py:func:`~PIL.Image.frombytes` function, + but loads data into this image instead of creating a new image object. + """ + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + # default format + if decoder_name == "raw" and args == (): + args = self.mode + + # unpack data + d = _getdecoder(self.mode, decoder_name, args) + d.setimage(self.im) + s = d.decode(data) + + if s[0] >= 0: + raise ValueError("not enough image data") + if s[1] != 0: + raise ValueError("cannot decode image data") + + def load(self): + """ + Allocates storage for the image and loads the pixel data. In + normal cases, you don't need to call this method, since the + Image class automatically loads an opened image when it is + accessed for the first time. + + If the file associated with the image was opened by Pillow, then this + method will close it. The exception to this is if the image has + multiple frames, in which case the file will be left open for seek + operations. See :ref:`file-handling` for more information. + + :returns: An image access object. + :rtype: :ref:`PixelAccess` or :py:class:`PIL.PyAccess` + """ + if self.im and self.palette and self.palette.dirty: + # realize palette + mode, arr = self.palette.getdata() + if mode == "RGBA": + mode = "RGB" + self.info["transparency"] = arr[3::4] + arr = bytes( + value for (index, value) in enumerate(arr) if index % 4 != 3 + ) + self.im.putpalette(mode, arr) + self.palette.dirty = 0 + self.palette.rawmode = None + if "transparency" in self.info: + if isinstance(self.info["transparency"], int): + self.im.putpalettealpha(self.info["transparency"], 0) + else: + self.im.putpalettealphas(self.info["transparency"]) + self.palette.mode = "RGBA" + else: + self.palette.mode = "RGB" + + if self.im: + if cffi and USE_CFFI_ACCESS: + if self.pyaccess: + return self.pyaccess + from . import PyAccess + + self.pyaccess = PyAccess.new(self, self.readonly) + if self.pyaccess: + return self.pyaccess + return self.im.pixel_access(self.readonly) + + def verify(self): + """ + Verifies the contents of a file. For data read from a file, this + method attempts to determine if the file is broken, without + actually decoding the image data. If this method finds any + problems, it raises suitable exceptions. If you need to load + the image after using this method, you must reopen the image + file. + """ + pass + + def convert(self, mode=None, matrix=None, dither=None, palette=WEB, colors=256): + """ + Returns a converted copy of this image. For the "P" mode, this + method translates pixels through the palette. If mode is + omitted, a mode is chosen so that all information in the image + and the palette can be represented without a palette. + + The current version supports all possible conversions between + "L", "RGB" and "CMYK." The ``matrix`` argument only supports "L" + and "RGB". + + When translating a color image to greyscale (mode "L"), + the library uses the ITU-R 601-2 luma transform:: + + L = R * 299/1000 + G * 587/1000 + B * 114/1000 + + The default method of converting a greyscale ("L") or "RGB" + image into a bilevel (mode "1") image uses Floyd-Steinberg + dither to approximate the original image luminosity levels. If + dither is :data:`NONE`, all values larger than 128 are set to 255 (white), + all other values to 0 (black). To use other thresholds, use the + :py:meth:`~PIL.Image.Image.point` method. + + When converting from "RGBA" to "P" without a ``matrix`` argument, + this passes the operation to :py:meth:`~PIL.Image.Image.quantize`, + and ``dither`` and ``palette`` are ignored. + + :param mode: The requested mode. See: :ref:`concept-modes`. + :param matrix: An optional conversion matrix. If given, this + should be 4- or 12-tuple containing floating point values. + :param dither: Dithering method, used when converting from + mode "RGB" to "P" or from "RGB" or "L" to "1". + Available methods are :data:`NONE` or :data:`FLOYDSTEINBERG` (default). + Note that this is not used when ``matrix`` is supplied. + :param palette: Palette to use when converting from mode "RGB" + to "P". Available palettes are :data:`WEB` or :data:`ADAPTIVE`. + :param colors: Number of colors to use for the :data:`ADAPTIVE` palette. + Defaults to 256. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + self.load() + + if not mode and self.mode == "P": + # determine default mode + if self.palette: + mode = self.palette.mode + else: + mode = "RGB" + if not mode or (mode == self.mode and not matrix): + return self.copy() + + has_transparency = self.info.get("transparency") is not None + if matrix: + # matrix conversion + if mode not in ("L", "RGB"): + raise ValueError("illegal conversion") + im = self.im.convert_matrix(mode, matrix) + new = self._new(im) + if has_transparency and self.im.bands == 3: + transparency = new.info["transparency"] + + def convert_transparency(m, v): + v = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * 0.5 + return max(0, min(255, int(v))) + + if mode == "L": + transparency = convert_transparency(matrix, transparency) + elif len(mode) == 3: + transparency = tuple( + [ + convert_transparency( + matrix[i * 4 : i * 4 + 4], transparency + ) + for i in range(0, len(transparency)) + ] + ) + new.info["transparency"] = transparency + return new + + if mode == "P" and self.mode == "RGBA": + return self.quantize(colors) + + trns = None + delete_trns = False + # transparency handling + if has_transparency: + if self.mode in ("1", "L", "I", "RGB") and mode == "RGBA": + # Use transparent conversion to promote from transparent + # color to an alpha channel. + new_im = self._new( + self.im.convert_transparent(mode, self.info["transparency"]) + ) + del new_im.info["transparency"] + return new_im + elif self.mode in ("L", "RGB", "P") and mode in ("L", "RGB", "P"): + t = self.info["transparency"] + if isinstance(t, bytes): + # Dragons. This can't be represented by a single color + warnings.warn( + "Palette images with Transparency expressed in bytes should be " + "converted to RGBA images" + ) + delete_trns = True + else: + # get the new transparency color. + # use existing conversions + trns_im = Image()._new(core.new(self.mode, (1, 1))) + if self.mode == "P": + trns_im.putpalette(self.palette) + if isinstance(t, tuple): + try: + t = trns_im.palette.getcolor(t) + except Exception as e: + raise ValueError( + "Couldn't allocate a palette color for transparency" + ) from e + trns_im.putpixel((0, 0), t) + + if mode in ("L", "RGB"): + trns_im = trns_im.convert(mode) + else: + # can't just retrieve the palette number, got to do it + # after quantization. + trns_im = trns_im.convert("RGB") + trns = trns_im.getpixel((0, 0)) + + elif self.mode == "P" and mode == "RGBA": + t = self.info["transparency"] + delete_trns = True + + if isinstance(t, bytes): + self.im.putpalettealphas(t) + elif isinstance(t, int): + self.im.putpalettealpha(t, 0) + else: + raise ValueError("Transparency for P mode should be bytes or int") + + if mode == "P" and palette == ADAPTIVE: + im = self.im.quantize(colors) + new = self._new(im) + from . import ImagePalette + + new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB")) + if delete_trns: + # This could possibly happen if we requantize to fewer colors. + # The transparency would be totally off in that case. + del new.info["transparency"] + if trns is not None: + try: + new.info["transparency"] = new.palette.getcolor(trns) + except Exception: + # if we can't make a transparent color, don't leave the old + # transparency hanging around to mess us up. + del new.info["transparency"] + warnings.warn("Couldn't allocate palette entry for transparency") + return new + + # colorspace conversion + if dither is None: + dither = FLOYDSTEINBERG + + try: + im = self.im.convert(mode, dither) + except ValueError: + try: + # normalize source image and try again + im = self.im.convert(getmodebase(self.mode)) + im = im.convert(mode, dither) + except KeyError as e: + raise ValueError("illegal conversion") from e + + new_im = self._new(im) + if delete_trns: + # crash fail if we leave a bytes transparency in an rgb/l mode. + del new_im.info["transparency"] + if trns is not None: + if new_im.mode == "P": + try: + new_im.info["transparency"] = new_im.palette.getcolor(trns) + except Exception: + del new_im.info["transparency"] + warnings.warn("Couldn't allocate palette entry for transparency") + else: + new_im.info["transparency"] = trns + return new_im + + def quantize(self, colors=256, method=None, kmeans=0, palette=None, dither=1): + """ + Convert the image to 'P' mode with the specified number + of colors. + + :param colors: The desired number of colors, <= 256 + :param method: :data:`MEDIANCUT` (median cut), + :data:`MAXCOVERAGE` (maximum coverage), + :data:`FASTOCTREE` (fast octree), + :data:`LIBIMAGEQUANT` (libimagequant; check support using + :py:func:`PIL.features.check_feature` + with ``feature="libimagequant"``). + :param kmeans: Integer + :param palette: Quantize to the palette of given + :py:class:`PIL.Image.Image`. + :param dither: Dithering method, used when converting from + mode "RGB" to "P" or from "RGB" or "L" to "1". + Available methods are :data:`NONE` or :data:`FLOYDSTEINBERG` (default). + Default: 1 (legacy setting) + :returns: A new image + + """ + + self.load() + + if method is None: + # defaults: + method = 0 + if self.mode == "RGBA": + method = 2 + + if self.mode == "RGBA" and method not in (2, 3): + # Caller specified an invalid mode. + raise ValueError( + "Fast Octree (method == 2) and libimagequant (method == 3) " + "are the only valid methods for quantizing RGBA images" + ) + + if palette: + # use palette from reference image + palette.load() + if palette.mode != "P": + raise ValueError("bad mode for palette image") + if self.mode != "RGB" and self.mode != "L": + raise ValueError( + "only RGB or L mode images can be quantized to a palette" + ) + im = self.im.convert("P", dither, palette.im) + return self._new(im) + + im = self._new(self.im.quantize(colors, method, kmeans)) + + from . import ImagePalette + + mode = im.im.getpalettemode() + im.palette = ImagePalette.ImagePalette(mode, im.im.getpalette(mode, mode)) + + return im + + def copy(self): + """ + Copies this image. Use this method if you wish to paste things + into an image, but still retain the original. + + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ + self.load() + return self._new(self.im.copy()) + + __copy__ = copy + + def crop(self, box=None): + """ + Returns a rectangular region from this image. The box is a + 4-tuple defining the left, upper, right, and lower pixel + coordinate. See :ref:`coordinate-system`. + + Note: Prior to Pillow 3.4.0, this was a lazy operation. + + :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if box is None: + return self.copy() + + self.load() + return self._new(self._crop(self.im, box)) + + def _crop(self, im, box): + """ + Returns a rectangular region from the core image object im. + + This is equivalent to calling im.crop((x0, y0, x1, y1)), but + includes additional sanity checks. + + :param im: a core image object + :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. + :returns: A core image object. + """ + + x0, y0, x1, y1 = map(int, map(round, box)) + + absolute_values = (abs(x1 - x0), abs(y1 - y0)) + + _decompression_bomb_check(absolute_values) + + return im.crop((x0, y0, x1, y1)) + + def draft(self, mode, size): + """ + Configures the image file loader so it returns a version of the + image that as closely as possible matches the given mode and + size. For example, you can use this method to convert a color + JPEG to greyscale while loading it. + + If any changes are made, returns a tuple with the chosen ``mode`` and + ``box`` with coordinates of the original image within the altered one. + + Note that this method modifies the :py:class:`~PIL.Image.Image` object + in place. If the image has already been loaded, this method has no + effect. + + Note: This method is not implemented for most images. It is + currently implemented only for JPEG and MPO images. + + :param mode: The requested mode. + :param size: The requested size. + """ + pass + + def _expand(self, xmargin, ymargin=None): + if ymargin is None: + ymargin = xmargin + self.load() + return self._new(self.im.expand(xmargin, ymargin, 0)) + + def filter(self, filter): + """ + Filters this image using the given filter. For a list of + available filters, see the :py:mod:`~PIL.ImageFilter` module. + + :param filter: Filter kernel. + :returns: An :py:class:`~PIL.Image.Image` object.""" + + from . import ImageFilter + + self.load() + + if isinstance(filter, Callable): + filter = filter() + if not hasattr(filter, "filter"): + raise TypeError( + "filter argument should be ImageFilter.Filter instance or class" + ) + + multiband = isinstance(filter, ImageFilter.MultibandFilter) + if self.im.bands == 1 or multiband: + return self._new(filter.filter(self.im)) + + ims = [] + for c in range(self.im.bands): + ims.append(self._new(filter.filter(self.im.getband(c)))) + return merge(self.mode, ims) + + def getbands(self): + """ + Returns a tuple containing the name of each band in this image. + For example, ``getbands`` on an RGB image returns ("R", "G", "B"). + + :returns: A tuple containing band names. + :rtype: tuple + """ + return ImageMode.getmode(self.mode).bands + + def getbbox(self): + """ + Calculates the bounding box of the non-zero regions in the + image. + + :returns: The bounding box is returned as a 4-tuple defining the + left, upper, right, and lower pixel coordinate. See + :ref:`coordinate-system`. If the image is completely empty, this + method returns None. + + """ + + self.load() + return self.im.getbbox() + + def getcolors(self, maxcolors=256): + """ + Returns a list of colors used in this image. + + :param maxcolors: Maximum number of colors. If this number is + exceeded, this method returns None. The default limit is + 256 colors. + :returns: An unsorted list of (count, pixel) values. + """ + + self.load() + if self.mode in ("1", "L", "P"): + h = self.im.histogram() + out = [] + for i in range(256): + if h[i]: + out.append((h[i], i)) + if len(out) > maxcolors: + return None + return out + return self.im.getcolors(maxcolors) + + def getdata(self, band=None): + """ + Returns the contents of this image as a sequence object + containing pixel values. The sequence object is flattened, so + that values for line one follow directly after the values of + line zero, and so on. + + Note that the sequence object returned by this method is an + internal PIL data type, which only supports certain sequence + operations. To convert it to an ordinary sequence (e.g. for + printing), use ``list(im.getdata())``. + + :param band: What band to return. The default is to return + all bands. To return a single band, pass in the index + value (e.g. 0 to get the "R" band from an "RGB" image). + :returns: A sequence-like object. + """ + + self.load() + if band is not None: + return self.im.getband(band) + return self.im # could be abused + + def getextrema(self): + """ + Gets the the minimum and maximum pixel values for each band in + the image. + + :returns: For a single-band image, a 2-tuple containing the + minimum and maximum pixel value. For a multi-band image, + a tuple containing one 2-tuple for each band. + """ + + self.load() + if self.im.bands > 1: + extrema = [] + for i in range(self.im.bands): + extrema.append(self.im.getband(i).getextrema()) + return tuple(extrema) + return self.im.getextrema() + + def getexif(self): + if self._exif is None: + self._exif = Exif() + + exif_info = self.info.get("exif") + if exif_info is None and "Raw profile type exif" in self.info: + exif_info = bytes.fromhex( + "".join(self.info["Raw profile type exif"].split("\n")[3:]) + ) + self._exif.load(exif_info) + + # XMP tags + if 0x0112 not in self._exif: + xmp_tags = self.info.get("XML:com.adobe.xmp") + if xmp_tags: + root = xml.etree.ElementTree.fromstring(xmp_tags) + for elem in root.iter(): + if elem.tag.endswith("}Description"): + orientation = elem.attrib.get( + "{http://ns.adobe.com/tiff/1.0/}Orientation" + ) + if orientation: + self._exif[0x0112] = int(orientation) + break + + return self._exif + + def getim(self): + """ + Returns a capsule that points to the internal image memory. + + :returns: A capsule object. + """ + + self.load() + return self.im.ptr + + def getpalette(self): + """ + Returns the image palette as a list. + + :returns: A list of color values [r, g, b, ...], or None if the + image has no palette. + """ + + self.load() + try: + return list(self.im.getpalette()) + except ValueError: + return None # no palette + + def getpixel(self, xy): + """ + Returns the pixel value at a given position. + + :param xy: The coordinate, given as (x, y). See + :ref:`coordinate-system`. + :returns: The pixel value. If the image is a multi-layer image, + this method returns a tuple. + """ + + self.load() + if self.pyaccess: + return self.pyaccess.getpixel(xy) + return self.im.getpixel(xy) + + def getprojection(self): + """ + Get projection to x and y axes + + :returns: Two sequences, indicating where there are non-zero + pixels along the X-axis and the Y-axis, respectively. + """ + + self.load() + x, y = self.im.getprojection() + return list(x), list(y) + + def histogram(self, mask=None, extrema=None): + """ + Returns a histogram for the image. The histogram is returned as + a list of pixel counts, one for each pixel value in the source + image. If the image has more than one band, the histograms for + all bands are concatenated (for example, the histogram for an + "RGB" image contains 768 values). + + A bilevel image (mode "1") is treated as a greyscale ("L") image + by this method. + + If a mask is provided, the method returns a histogram for those + parts of the image where the mask image is non-zero. The mask + image must have the same size as the image, and be either a + bi-level image (mode "1") or a greyscale image ("L"). + + :param mask: An optional mask. + :param extrema: An optional tuple of manually-specified extrema. + :returns: A list containing pixel counts. + """ + self.load() + if mask: + mask.load() + return self.im.histogram((0, 0), mask.im) + if self.mode in ("I", "F"): + if extrema is None: + extrema = self.getextrema() + return self.im.histogram(extrema) + return self.im.histogram() + + def entropy(self, mask=None, extrema=None): + """ + Calculates and returns the entropy for the image. + + A bilevel image (mode "1") is treated as a greyscale ("L") + image by this method. + + If a mask is provided, the method employs the histogram for + those parts of the image where the mask image is non-zero. + The mask image must have the same size as the image, and be + either a bi-level image (mode "1") or a greyscale image ("L"). + + :param mask: An optional mask. + :param extrema: An optional tuple of manually-specified extrema. + :returns: A float value representing the image entropy + """ + self.load() + if mask: + mask.load() + return self.im.entropy((0, 0), mask.im) + if self.mode in ("I", "F"): + if extrema is None: + extrema = self.getextrema() + return self.im.entropy(extrema) + return self.im.entropy() + + def paste(self, im, box=None, mask=None): + """ + Pastes another image into this image. The box argument is either + a 2-tuple giving the upper left corner, a 4-tuple defining the + left, upper, right, and lower pixel coordinate, or None (same as + (0, 0)). See :ref:`coordinate-system`. If a 4-tuple is given, the size + of the pasted image must match the size of the region. + + If the modes don't match, the pasted image is converted to the mode of + this image (see the :py:meth:`~PIL.Image.Image.convert` method for + details). + + Instead of an image, the source can be a integer or tuple + containing pixel values. The method then fills the region + with the given color. When creating RGB images, you can + also use color strings as supported by the ImageColor module. + + If a mask is given, this method updates only the regions + indicated by the mask. You can use either "1", "L" or "RGBA" + images (in the latter case, the alpha band is used as mask). + Where the mask is 255, the given image is copied as is. Where + the mask is 0, the current value is preserved. Intermediate + values will mix the two images together, including their alpha + channels if they have them. + + See :py:meth:`~PIL.Image.Image.alpha_composite` if you want to + combine images with respect to their alpha channels. + + :param im: Source image or pixel value (integer or tuple). + :param box: An optional 4-tuple giving the region to paste into. + If a 2-tuple is used instead, it's treated as the upper left + corner. If omitted or None, the source is pasted into the + upper left corner. + + If an image is given as the second argument and there is no + third, the box defaults to (0, 0), and the second argument + is interpreted as a mask image. + :param mask: An optional mask image. + """ + + if isImageType(box) and mask is None: + # abbreviated paste(im, mask) syntax + mask = box + box = None + + if box is None: + box = (0, 0) + + if len(box) == 2: + # upper left corner given; get size from image or mask + if isImageType(im): + size = im.size + elif isImageType(mask): + size = mask.size + else: + # FIXME: use self.size here? + raise ValueError("cannot determine region size; use 4-item box") + box += (box[0] + size[0], box[1] + size[1]) + + if isinstance(im, str): + from . import ImageColor + + im = ImageColor.getcolor(im, self.mode) + + elif isImageType(im): + im.load() + if self.mode != im.mode: + if self.mode != "RGB" or im.mode not in ("RGBA", "RGBa"): + # should use an adapter for this! + im = im.convert(self.mode) + im = im.im + + self._ensure_mutable() + + if mask: + mask.load() + self.im.paste(im, box, mask.im) + else: + self.im.paste(im, box) + + def alpha_composite(self, im, dest=(0, 0), source=(0, 0)): + """'In-place' analog of Image.alpha_composite. Composites an image + onto this image. + + :param im: image to composite over this one + :param dest: Optional 2 tuple (left, top) specifying the upper + left corner in this (destination) image. + :param source: Optional 2 (left, top) tuple for the upper left + corner in the overlay source image, or 4 tuple (left, top, right, + bottom) for the bounds of the source rectangle + + Performance Note: Not currently implemented in-place in the core layer. + """ + + if not isinstance(source, (list, tuple)): + raise ValueError("Source must be a tuple") + if not isinstance(dest, (list, tuple)): + raise ValueError("Destination must be a tuple") + if not len(source) in (2, 4): + raise ValueError("Source must be a 2 or 4-tuple") + if not len(dest) == 2: + raise ValueError("Destination must be a 2-tuple") + if min(source) < 0: + raise ValueError("Source must be non-negative") + if min(dest) < 0: + raise ValueError("Destination must be non-negative") + + if len(source) == 2: + source = source + im.size + + # over image, crop if it's not the whole thing. + if source == (0, 0) + im.size: + overlay = im + else: + overlay = im.crop(source) + + # target for the paste + box = dest + (dest[0] + overlay.width, dest[1] + overlay.height) + + # destination image. don't copy if we're using the whole image. + if box == (0, 0) + self.size: + background = self + else: + background = self.crop(box) + + result = alpha_composite(background, overlay) + self.paste(result, box) + + def point(self, lut, mode=None): + """ + Maps this image through a lookup table or function. + + :param lut: A lookup table, containing 256 (or 65536 if + self.mode=="I" and mode == "L") values per band in the + image. A function can be used instead, it should take a + single argument. The function is called once for each + possible pixel value, and the resulting table is applied to + all bands of the image. + + It may also be an :py:class:`~PIL.Image.ImagePointHandler` + object:: + + class Example(Image.ImagePointHandler): + def point(self, data): + # Return result + :param mode: Output mode (default is same as input). In the + current version, this can only be used if the source image + has mode "L" or "P", and the output has mode "1" or the + source image mode is "I" and the output mode is "L". + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + self.load() + + if isinstance(lut, ImagePointHandler): + return lut.point(self) + + if callable(lut): + # if it isn't a list, it should be a function + if self.mode in ("I", "I;16", "F"): + # check if the function can be used with point_transform + # UNDONE wiredfool -- I think this prevents us from ever doing + # a gamma function point transform on > 8bit images. + scale, offset = _getscaleoffset(lut) + return self._new(self.im.point_transform(scale, offset)) + # for other modes, convert the function to a table + lut = [lut(i) for i in range(256)] * self.im.bands + + if self.mode == "F": + # FIXME: _imaging returns a confusing error message for this case + raise ValueError("point operation not supported for this mode") + + return self._new(self.im.point(lut, mode)) + + def putalpha(self, alpha): + """ + Adds or replaces the alpha layer in this image. If the image + does not have an alpha layer, it's converted to "LA" or "RGBA". + The new layer must be either "L" or "1". + + :param alpha: The new alpha layer. This can either be an "L" or "1" + image having the same size as this image, or an integer or + other color value. + """ + + self._ensure_mutable() + + if self.mode not in ("LA", "PA", "RGBA"): + # attempt to promote self to a matching alpha mode + try: + mode = getmodebase(self.mode) + "A" + try: + self.im.setmode(mode) + except (AttributeError, ValueError) as e: + # do things the hard way + im = self.im.convert(mode) + if im.mode not in ("LA", "PA", "RGBA"): + raise ValueError from e # sanity check + self.im = im + self.pyaccess = None + self.mode = self.im.mode + except KeyError as e: + raise ValueError("illegal image mode") from e + + if self.mode in ("LA", "PA"): + band = 1 + else: + band = 3 + + if isImageType(alpha): + # alpha layer + if alpha.mode not in ("1", "L"): + raise ValueError("illegal image mode") + alpha.load() + if alpha.mode == "1": + alpha = alpha.convert("L") + else: + # constant alpha + try: + self.im.fillband(band, alpha) + except (AttributeError, ValueError): + # do things the hard way + alpha = new("L", self.size, alpha) + else: + return + + self.im.putband(alpha.im, band) + + def putdata(self, data, scale=1.0, offset=0.0): + """ + Copies pixel data to this image. This method copies data from a + sequence object into the image, starting at the upper left + corner (0, 0), and continuing until either the image or the + sequence ends. The scale and offset values are used to adjust + the sequence values: **pixel = value*scale + offset**. + + :param data: A sequence object. + :param scale: An optional scale value. The default is 1.0. + :param offset: An optional offset value. The default is 0.0. + """ + + self._ensure_mutable() + + self.im.putdata(data, scale, offset) + + def putpalette(self, data, rawmode="RGB"): + """ + Attaches a palette to this image. The image must be a "P", "PA", "L" + or "LA" image. + + The palette sequence must contain either 768 integer values, or 1024 + integer values if alpha is included. Each group of values represents + the red, green, blue (and alpha if included) values for the + corresponding pixel index. Instead of an integer sequence, you can use + an 8-bit string. + + :param data: A palette sequence (either a list or a string). + :param rawmode: The raw mode of the palette. + """ + from . import ImagePalette + + if self.mode not in ("L", "LA", "P", "PA"): + raise ValueError("illegal image mode") + self.load() + if isinstance(data, ImagePalette.ImagePalette): + palette = ImagePalette.raw(data.rawmode, data.palette) + else: + if not isinstance(data, bytes): + data = bytes(data) + palette = ImagePalette.raw(rawmode, data) + self.mode = "PA" if "A" in self.mode else "P" + self.palette = palette + self.palette.mode = "RGB" + self.load() # install new palette + + def putpixel(self, xy, value): + """ + Modifies the pixel at the given position. The color is given as + a single numerical value for single-band images, and a tuple for + multi-band images. In addition to this, RGB and RGBA tuples are + accepted for P images. + + Note that this method is relatively slow. For more extensive changes, + use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw` + module instead. + + See: + + * :py:meth:`~PIL.Image.Image.paste` + * :py:meth:`~PIL.Image.Image.putdata` + * :py:mod:`~PIL.ImageDraw` + + :param xy: The pixel coordinate, given as (x, y). See + :ref:`coordinate-system`. + :param value: The pixel value. + """ + + if self.readonly: + self._copy() + self.load() + + if self.pyaccess: + return self.pyaccess.putpixel(xy, value) + + if ( + self.mode == "P" + and isinstance(value, (list, tuple)) + and len(value) in [3, 4] + ): + # RGB or RGBA value for a P image + value = self.palette.getcolor(value) + return self.im.putpixel(xy, value) + + def remap_palette(self, dest_map, source_palette=None): + """ + Rewrites the image to reorder the palette. + + :param dest_map: A list of indexes into the original palette. + e.g. ``[1,0]`` would swap a two item palette, and ``list(range(256))`` + is the identity transform. + :param source_palette: Bytes or None. + :returns: An :py:class:`~PIL.Image.Image` object. + + """ + from . import ImagePalette + + if self.mode not in ("L", "P"): + raise ValueError("illegal image mode") + + if source_palette is None: + if self.mode == "P": + real_source_palette = self.im.getpalette("RGB")[:768] + else: # L-mode + real_source_palette = bytearray(i // 3 for i in range(768)) + else: + real_source_palette = source_palette + + palette_bytes = b"" + new_positions = [0] * 256 + + # pick only the used colors from the palette + for i, oldPosition in enumerate(dest_map): + palette_bytes += real_source_palette[oldPosition * 3 : oldPosition * 3 + 3] + new_positions[oldPosition] = i + + # replace the palette color id of all pixel with the new id + + # Palette images are [0..255], mapped through a 1 or 3 + # byte/color map. We need to remap the whole image + # from palette 1 to palette 2. New_positions is + # an array of indexes into palette 1. Palette 2 is + # palette 1 with any holes removed. + + # We're going to leverage the convert mechanism to use the + # C code to remap the image from palette 1 to palette 2, + # by forcing the source image into 'L' mode and adding a + # mapping 'L' mode palette, then converting back to 'L' + # sans palette thus converting the image bytes, then + # assigning the optimized RGB palette. + + # perf reference, 9500x4000 gif, w/~135 colors + # 14 sec prepatch, 1 sec postpatch with optimization forced. + + mapping_palette = bytearray(new_positions) + + m_im = self.copy() + m_im.mode = "P" + + m_im.palette = ImagePalette.ImagePalette( + "RGB", palette=mapping_palette * 3, size=768 + ) + # possibly set palette dirty, then + # m_im.putpalette(mapping_palette, 'L') # converts to 'P' + # or just force it. + # UNDONE -- this is part of the general issue with palettes + m_im.im.putpalette(*m_im.palette.getdata()) + + m_im = m_im.convert("L") + + # Internally, we require 768 bytes for a palette. + new_palette_bytes = palette_bytes + (768 - len(palette_bytes)) * b"\x00" + m_im.putpalette(new_palette_bytes) + m_im.palette = ImagePalette.ImagePalette( + "RGB", palette=palette_bytes, size=len(palette_bytes) + ) + + return m_im + + def _get_safe_box(self, size, resample, box): + """Expands the box so it includes adjacent pixels + that may be used by resampling with the given resampling filter. + """ + filter_support = _filters_support[resample] - 0.5 + scale_x = (box[2] - box[0]) / size[0] + scale_y = (box[3] - box[1]) / size[1] + support_x = filter_support * scale_x + support_y = filter_support * scale_y + + return ( + max(0, int(box[0] - support_x)), + max(0, int(box[1] - support_y)), + min(self.size[0], math.ceil(box[2] + support_x)), + min(self.size[1], math.ceil(box[3] + support_y)), + ) + + def resize(self, size, resample=BICUBIC, box=None, reducing_gap=None): + """ + Returns a resized copy of this image. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param resample: An optional resampling filter. This can be + one of :py:data:`PIL.Image.NEAREST`, :py:data:`PIL.Image.BOX`, + :py:data:`PIL.Image.BILINEAR`, :py:data:`PIL.Image.HAMMING`, + :py:data:`PIL.Image.BICUBIC` or :py:data:`PIL.Image.LANCZOS`. + Default filter is :py:data:`PIL.Image.BICUBIC`. + If the image has mode "1" or "P", it is + always set to :py:data:`PIL.Image.NEAREST`. + See: :ref:`concept-filters`. + :param box: An optional 4-tuple of floats providing + the source image region to be scaled. + The values must be within (0, 0, width, height) rectangle. + If omitted or None, the entire source is used. + :param reducing_gap: Apply optimization by resizing the image + in two steps. First, reducing the image by integer times + using :py:meth:`~PIL.Image.Image.reduce`. + Second, resizing using regular resampling. The last step + changes size no less than by ``reducing_gap`` times. + ``reducing_gap`` may be None (no first step is performed) + or should be greater than 1.0. The bigger ``reducing_gap``, + the closer the result to the fair resampling. + The smaller ``reducing_gap``, the faster resizing. + With ``reducing_gap`` greater or equal to 3.0, the result is + indistinguishable from fair resampling in most cases. + The default value is None (no optimization). + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if resample not in (NEAREST, BILINEAR, BICUBIC, LANCZOS, BOX, HAMMING): + message = f"Unknown resampling filter ({resample})." + + filters = [ + "{} ({})".format(filter[1], filter[0]) + for filter in ( + (NEAREST, "Image.NEAREST"), + (LANCZOS, "Image.LANCZOS"), + (BILINEAR, "Image.BILINEAR"), + (BICUBIC, "Image.BICUBIC"), + (BOX, "Image.BOX"), + (HAMMING, "Image.HAMMING"), + ) + ] + raise ValueError( + message + " Use " + ", ".join(filters[:-1]) + " or " + filters[-1] + ) + + if reducing_gap is not None and reducing_gap < 1.0: + raise ValueError("reducing_gap must be 1.0 or greater") + + size = tuple(size) + + if box is None: + box = (0, 0) + self.size + else: + box = tuple(box) + + if self.size == size and box == (0, 0) + self.size: + return self.copy() + + if self.mode in ("1", "P"): + resample = NEAREST + + if self.mode in ["LA", "RGBA"]: + im = self.convert(self.mode[:-1] + "a") + im = im.resize(size, resample, box) + return im.convert(self.mode) + + self.load() + + if reducing_gap is not None and resample != NEAREST: + factor_x = int((box[2] - box[0]) / size[0] / reducing_gap) or 1 + factor_y = int((box[3] - box[1]) / size[1] / reducing_gap) or 1 + if factor_x > 1 or factor_y > 1: + reduce_box = self._get_safe_box(size, resample, box) + factor = (factor_x, factor_y) + if callable(self.reduce): + self = self.reduce(factor, box=reduce_box) + else: + self = Image.reduce(self, factor, box=reduce_box) + box = ( + (box[0] - reduce_box[0]) / factor_x, + (box[1] - reduce_box[1]) / factor_y, + (box[2] - reduce_box[0]) / factor_x, + (box[3] - reduce_box[1]) / factor_y, + ) + + return self._new(self.im.resize(size, resample, box)) + + def reduce(self, factor, box=None): + """ + Returns a copy of the image reduced ``factor`` times. + If the size of the image is not dividable by ``factor``, + the resulting size will be rounded up. + + :param factor: A greater than 0 integer or tuple of two integers + for width and height separately. + :param box: An optional 4-tuple of ints providing + the source image region to be reduced. + The values must be within ``(0, 0, width, height)`` rectangle. + If omitted or ``None``, the entire source is used. + """ + if not isinstance(factor, (list, tuple)): + factor = (factor, factor) + + if box is None: + box = (0, 0) + self.size + else: + box = tuple(box) + + if factor == (1, 1) and box == (0, 0) + self.size: + return self.copy() + + if self.mode in ["LA", "RGBA"]: + im = self.convert(self.mode[:-1] + "a") + im = im.reduce(factor, box) + return im.convert(self.mode) + + self.load() + + return self._new(self.im.reduce(factor, box)) + + def rotate( + self, + angle, + resample=NEAREST, + expand=0, + center=None, + translate=None, + fillcolor=None, + ): + """ + Returns a rotated copy of this image. This method returns a + copy of this image, rotated the given number of degrees counter + clockwise around its centre. + + :param angle: In degrees counter clockwise. + :param resample: An optional resampling filter. This can be + one of :py:data:`PIL.Image.NEAREST` (use nearest neighbour), + :py:data:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 + environment), or :py:data:`PIL.Image.BICUBIC` + (cubic spline interpolation in a 4x4 environment). + If omitted, or if the image has mode "1" or "P", it is + set to :py:data:`PIL.Image.NEAREST`. See :ref:`concept-filters`. + :param expand: Optional expansion flag. If true, expands the output + image to make it large enough to hold the entire rotated image. + If false or omitted, make the output image the same size as the + input image. Note that the expand flag assumes rotation around + the center and no translation. + :param center: Optional center of rotation (a 2-tuple). Origin is + the upper left corner. Default is the center of the image. + :param translate: An optional post-rotate translation (a 2-tuple). + :param fillcolor: An optional color for area outside the rotated image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + angle = angle % 360.0 + + # Fast paths regardless of filter, as long as we're not + # translating or changing the center. + if not (center or translate): + if angle == 0: + return self.copy() + if angle == 180: + return self.transpose(ROTATE_180) + if angle == 90 and expand: + return self.transpose(ROTATE_90) + if angle == 270 and expand: + return self.transpose(ROTATE_270) + + # Calculate the affine matrix. Note that this is the reverse + # transformation (from destination image to source) because we + # want to interpolate the (discrete) destination pixel from + # the local area around the (floating) source pixel. + + # The matrix we actually want (note that it operates from the right): + # (1, 0, tx) (1, 0, cx) ( cos a, sin a, 0) (1, 0, -cx) + # (0, 1, ty) * (0, 1, cy) * (-sin a, cos a, 0) * (0, 1, -cy) + # (0, 0, 1) (0, 0, 1) ( 0, 0, 1) (0, 0, 1) + + # The reverse matrix is thus: + # (1, 0, cx) ( cos -a, sin -a, 0) (1, 0, -cx) (1, 0, -tx) + # (0, 1, cy) * (-sin -a, cos -a, 0) * (0, 1, -cy) * (0, 1, -ty) + # (0, 0, 1) ( 0, 0, 1) (0, 0, 1) (0, 0, 1) + + # In any case, the final translation may be updated at the end to + # compensate for the expand flag. + + w, h = self.size + + if translate is None: + post_trans = (0, 0) + else: + post_trans = translate + if center is None: + # FIXME These should be rounded to ints? + rotn_center = (w / 2.0, h / 2.0) + else: + rotn_center = center + + angle = -math.radians(angle) + matrix = [ + round(math.cos(angle), 15), + round(math.sin(angle), 15), + 0.0, + round(-math.sin(angle), 15), + round(math.cos(angle), 15), + 0.0, + ] + + def transform(x, y, matrix): + (a, b, c, d, e, f) = matrix + return a * x + b * y + c, d * x + e * y + f + + matrix[2], matrix[5] = transform( + -rotn_center[0] - post_trans[0], -rotn_center[1] - post_trans[1], matrix + ) + matrix[2] += rotn_center[0] + matrix[5] += rotn_center[1] + + if expand: + # calculate output size + xx = [] + yy = [] + for x, y in ((0, 0), (w, 0), (w, h), (0, h)): + x, y = transform(x, y, matrix) + xx.append(x) + yy.append(y) + nw = math.ceil(max(xx)) - math.floor(min(xx)) + nh = math.ceil(max(yy)) - math.floor(min(yy)) + + # We multiply a translation matrix from the right. Because of its + # special form, this is the same as taking the image of the + # translation vector as new translation vector. + matrix[2], matrix[5] = transform(-(nw - w) / 2.0, -(nh - h) / 2.0, matrix) + w, h = nw, nh + + return self.transform((w, h), AFFINE, matrix, resample, fillcolor=fillcolor) + + def save(self, fp, format=None, **params): + """ + Saves this image under the given filename. If no format is + specified, the format to use is determined from the filename + extension, if possible. + + Keyword options can be used to provide additional instructions + to the writer. If a writer doesn't recognise an option, it is + silently ignored. The available options are described in the + :doc:`image format documentation + <../handbook/image-file-formats>` for each writer. + + You can use a file object instead of a filename. In this case, + you must always specify the format. The file object must + implement the ``seek``, ``tell``, and ``write`` + methods, and be opened in binary mode. + + :param fp: A filename (string), pathlib.Path object or file object. + :param format: Optional format override. If omitted, the + format to use is determined from the filename extension. + If a file object was used instead of a filename, this + parameter should always be used. + :param params: Extra parameters to the image writer. + :returns: None + :exception ValueError: If the output format could not be determined + from the file name. Use the format option to solve this. + :exception OSError: If the file could not be written. The file + may have been created, and may contain partial data. + """ + + filename = "" + open_fp = False + if isPath(fp): + filename = fp + open_fp = True + elif isinstance(fp, Path): + filename = str(fp) + open_fp = True + if not filename and hasattr(fp, "name") and isPath(fp.name): + # only set the name for metadata purposes + filename = fp.name + + # may mutate self! + self._ensure_mutable() + + save_all = params.pop("save_all", False) + self.encoderinfo = params + self.encoderconfig = () + + preinit() + + ext = os.path.splitext(filename)[1].lower() + + if not format: + if ext not in EXTENSION: + init() + try: + format = EXTENSION[ext] + except KeyError as e: + raise ValueError(f"unknown file extension: {ext}") from e + + if format.upper() not in SAVE: + init() + if save_all: + save_handler = SAVE_ALL[format.upper()] + else: + save_handler = SAVE[format.upper()] + + if open_fp: + if params.get("append", False): + # Open also for reading ("+"), because TIFF save_all + # writer needs to go back and edit the written data. + fp = builtins.open(filename, "r+b") + else: + fp = builtins.open(filename, "w+b") + + try: + save_handler(self, fp, filename) + finally: + # do what we can to clean up + if open_fp: + fp.close() + + def seek(self, frame): + """ + Seeks to the given frame in this sequence file. If you seek + beyond the end of the sequence, the method raises an + ``EOFError`` exception. When a sequence file is opened, the + library automatically seeks to frame 0. + + See :py:meth:`~PIL.Image.Image.tell`. + + If defined, :attr:`~PIL.Image.Image.n_frames` refers to the + number of available frames. + + :param frame: Frame number, starting at 0. + :exception EOFError: If the call attempts to seek beyond the end + of the sequence. + """ + + # overridden by file handlers + if frame != 0: + raise EOFError + + def show(self, title=None, command=None): + """ + Displays this image. This method is mainly intended for debugging purposes. + + This method calls :py:func:`PIL.ImageShow.show` internally. You can use + :py:func:`PIL.ImageShow.register` to override its default behaviour. + + The image is first saved to a temporary file. By default, it will be in + PNG format. + + On Unix, the image is then opened using the **display**, **eog** or + **xv** utility, depending on which one can be found. + + On macOS, the image is opened with the native Preview application. + + On Windows, the image is opened with the standard PNG display utility. + + :param title: Optional title to use for the image window, where possible. + """ + + if command is not None: + warnings.warn( + "The command parameter is deprecated and will be removed in Pillow 9 " + "(2022-01-02). Use a subclass of ImageShow.Viewer instead.", + DeprecationWarning, + ) + + _show(self, title=title, command=command) + + def split(self): + """ + Split this image into individual bands. This method returns a + tuple of individual image bands from an image. For example, + splitting an "RGB" image creates three new images each + containing a copy of one of the original bands (red, green, + blue). + + If you need only one band, :py:meth:`~PIL.Image.Image.getchannel` + method can be more convenient and faster. + + :returns: A tuple containing bands. + """ + + self.load() + if self.im.bands == 1: + ims = [self.copy()] + else: + ims = map(self._new, self.im.split()) + return tuple(ims) + + def getchannel(self, channel): + """ + Returns an image containing a single channel of the source image. + + :param channel: What channel to return. Could be index + (0 for "R" channel of "RGB") or channel name + ("A" for alpha channel of "RGBA"). + :returns: An image in "L" mode. + + .. versionadded:: 4.3.0 + """ + self.load() + + if isinstance(channel, str): + try: + channel = self.getbands().index(channel) + except ValueError as e: + raise ValueError(f'The image has no channel "{channel}"') from e + + return self._new(self.im.getband(channel)) + + def tell(self): + """ + Returns the current frame number. See :py:meth:`~PIL.Image.Image.seek`. + + If defined, :attr:`~PIL.Image.Image.n_frames` refers to the + number of available frames. + + :returns: Frame number, starting with 0. + """ + return 0 + + def thumbnail(self, size, resample=BICUBIC, reducing_gap=2.0): + """ + Make this image into a thumbnail. This method modifies the + image to contain a thumbnail version of itself, no larger than + the given size. This method calculates an appropriate thumbnail + size to preserve the aspect of the image, calls the + :py:meth:`~PIL.Image.Image.draft` method to configure the file reader + (where applicable), and finally resizes the image. + + Note that this function modifies the :py:class:`~PIL.Image.Image` + object in place. If you need to use the full resolution image as well, + apply this method to a :py:meth:`~PIL.Image.Image.copy` of the original + image. + + :param size: Requested size. + :param resample: Optional resampling filter. This can be one + of :py:data:`PIL.Image.NEAREST`, :py:data:`PIL.Image.BOX`, + :py:data:`PIL.Image.BILINEAR`, :py:data:`PIL.Image.HAMMING`, + :py:data:`PIL.Image.BICUBIC` or :py:data:`PIL.Image.LANCZOS`. + If omitted, it defaults to :py:data:`PIL.Image.BICUBIC`. + (was :py:data:`PIL.Image.NEAREST` prior to version 2.5.0). + See: :ref:`concept-filters`. + :param reducing_gap: Apply optimization by resizing the image + in two steps. First, reducing the image by integer times + using :py:meth:`~PIL.Image.Image.reduce` or + :py:meth:`~PIL.Image.Image.draft` for JPEG images. + Second, resizing using regular resampling. The last step + changes size no less than by ``reducing_gap`` times. + ``reducing_gap`` may be None (no first step is performed) + or should be greater than 1.0. The bigger ``reducing_gap``, + the closer the result to the fair resampling. + The smaller ``reducing_gap``, the faster resizing. + With ``reducing_gap`` greater or equal to 3.0, the result is + indistinguishable from fair resampling in most cases. + The default value is 2.0 (very close to fair resampling + while still being faster in many cases). + :returns: None + """ + + x, y = map(math.floor, size) + if x >= self.width and y >= self.height: + return + + def round_aspect(number, key): + return max(min(math.floor(number), math.ceil(number), key=key), 1) + + # preserve aspect ratio + aspect = self.width / self.height + if x / y >= aspect: + x = round_aspect(y * aspect, key=lambda n: abs(aspect - n / y)) + else: + y = round_aspect( + x / aspect, key=lambda n: 0 if n == 0 else abs(aspect - x / n) + ) + size = (x, y) + + box = None + if reducing_gap is not None: + res = self.draft(None, (size[0] * reducing_gap, size[1] * reducing_gap)) + if res is not None: + box = res[1] + + if self.size != size: + im = self.resize(size, resample, box=box, reducing_gap=reducing_gap) + + self.im = im.im + self._size = size + self.mode = self.im.mode + + self.readonly = 0 + self.pyaccess = None + + # FIXME: the different transform methods need further explanation + # instead of bloating the method docs, add a separate chapter. + def transform( + self, size, method, data=None, resample=NEAREST, fill=1, fillcolor=None + ): + """ + Transforms this image. This method creates a new image with the + given size, and the same mode as the original, and copies data + to the new image using the given transform. + + :param size: The output size. + :param method: The transformation method. This is one of + :py:data:`PIL.Image.EXTENT` (cut out a rectangular subregion), + :py:data:`PIL.Image.AFFINE` (affine transform), + :py:data:`PIL.Image.PERSPECTIVE` (perspective transform), + :py:data:`PIL.Image.QUAD` (map a quadrilateral to a rectangle), or + :py:data:`PIL.Image.MESH` (map a number of source quadrilaterals + in one operation). + + It may also be an :py:class:`~PIL.Image.ImageTransformHandler` + object:: + + class Example(Image.ImageTransformHandler): + def transform(self, size, data, resample, fill=1): + # Return result + + It may also be an object with a ``method.getdata`` method + that returns a tuple supplying new ``method`` and ``data`` values:: + + class Example: + def getdata(self): + method = Image.EXTENT + data = (0, 0, 100, 100) + return method, data + :param data: Extra data to the transformation method. + :param resample: Optional resampling filter. It can be one of + :py:data:`PIL.Image.NEAREST` (use nearest neighbour), + :py:data:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 + environment), or :py:data:`PIL.Image.BICUBIC` (cubic spline + interpolation in a 4x4 environment). If omitted, or if the image + has mode "1" or "P", it is set to :py:data:`PIL.Image.NEAREST`. + See: :ref:`concept-filters`. + :param fill: If ``method`` is an + :py:class:`~PIL.Image.ImageTransformHandler` object, this is one of + the arguments passed to it. Otherwise, it is unused. + :param fillcolor: Optional fill color for the area outside the + transform in the output image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if self.mode == "LA": + return ( + self.convert("La") + .transform(size, method, data, resample, fill, fillcolor) + .convert("LA") + ) + + if self.mode == "RGBA": + return ( + self.convert("RGBa") + .transform(size, method, data, resample, fill, fillcolor) + .convert("RGBA") + ) + + if isinstance(method, ImageTransformHandler): + return method.transform(size, self, resample=resample, fill=fill) + + if hasattr(method, "getdata"): + # compatibility w. old-style transform objects + method, data = method.getdata() + + if data is None: + raise ValueError("missing method data") + + im = new(self.mode, size, fillcolor) + im.info = self.info.copy() + if method == MESH: + # list of quads + for box, quad in data: + im.__transformer(box, self, QUAD, quad, resample, fillcolor is None) + else: + im.__transformer( + (0, 0) + size, self, method, data, resample, fillcolor is None + ) + + return im + + def __transformer(self, box, image, method, data, resample=NEAREST, fill=1): + w = box[2] - box[0] + h = box[3] - box[1] + + if method == AFFINE: + data = data[0:6] + + elif method == EXTENT: + # convert extent to an affine transform + x0, y0, x1, y1 = data + xs = (x1 - x0) / w + ys = (y1 - y0) / h + method = AFFINE + data = (xs, 0, x0, 0, ys, y0) + + elif method == PERSPECTIVE: + data = data[0:8] + + elif method == QUAD: + # quadrilateral warp. data specifies the four corners + # given as NW, SW, SE, and NE. + nw = data[0:2] + sw = data[2:4] + se = data[4:6] + ne = data[6:8] + x0, y0 = nw + As = 1.0 / w + At = 1.0 / h + data = ( + x0, + (ne[0] - x0) * As, + (sw[0] - x0) * At, + (se[0] - sw[0] - ne[0] + x0) * As * At, + y0, + (ne[1] - y0) * As, + (sw[1] - y0) * At, + (se[1] - sw[1] - ne[1] + y0) * As * At, + ) + + else: + raise ValueError("unknown transformation method") + + if resample not in (NEAREST, BILINEAR, BICUBIC): + if resample in (BOX, HAMMING, LANCZOS): + message = { + BOX: "Image.BOX", + HAMMING: "Image.HAMMING", + LANCZOS: "Image.LANCZOS/Image.ANTIALIAS", + }[resample] + f" ({resample}) cannot be used." + else: + message = f"Unknown resampling filter ({resample})." + + filters = [ + "{} ({})".format(filter[1], filter[0]) + for filter in ( + (NEAREST, "Image.NEAREST"), + (BILINEAR, "Image.BILINEAR"), + (BICUBIC, "Image.BICUBIC"), + ) + ] + raise ValueError( + message + " Use " + ", ".join(filters[:-1]) + " or " + filters[-1] + ) + + image.load() + + self.load() + + if image.mode in ("1", "P"): + resample = NEAREST + + self.im.transform2(box, image.im, method, data, resample, fill) + + def transpose(self, method): + """ + Transpose image (flip or rotate in 90 degree steps) + + :param method: One of :py:data:`PIL.Image.FLIP_LEFT_RIGHT`, + :py:data:`PIL.Image.FLIP_TOP_BOTTOM`, :py:data:`PIL.Image.ROTATE_90`, + :py:data:`PIL.Image.ROTATE_180`, :py:data:`PIL.Image.ROTATE_270`, + :py:data:`PIL.Image.TRANSPOSE` or :py:data:`PIL.Image.TRANSVERSE`. + :returns: Returns a flipped or rotated copy of this image. + """ + + self.load() + return self._new(self.im.transpose(method)) + + def effect_spread(self, distance): + """ + Randomly spread pixels in an image. + + :param distance: Distance to spread pixels. + """ + self.load() + return self._new(self.im.effect_spread(distance)) + + def toqimage(self): + """Returns a QImage copy of this image""" + from . import ImageQt + + if not ImageQt.qt_is_installed: + raise ImportError("Qt bindings are not installed") + return ImageQt.toqimage(self) + + def toqpixmap(self): + """Returns a QPixmap copy of this image""" + from . import ImageQt + + if not ImageQt.qt_is_installed: + raise ImportError("Qt bindings are not installed") + return ImageQt.toqpixmap(self) + + +# -------------------------------------------------------------------- +# Abstract handlers. + + +class ImagePointHandler: + """ + Used as a mixin by point transforms + (for use with :py:meth:`~PIL.Image.Image.point`) + """ + + pass + + +class ImageTransformHandler: + """ + Used as a mixin by geometry transforms + (for use with :py:meth:`~PIL.Image.Image.transform`) + """ + + pass + + +# -------------------------------------------------------------------- +# Factories + +# +# Debugging + + +def _wedge(): + """Create greyscale wedge (for debugging only)""" + + return Image()._new(core.wedge("L")) + + +def _check_size(size): + """ + Common check to enforce type and sanity check on size tuples + + :param size: Should be a 2 tuple of (width, height) + :returns: True, or raises a ValueError + """ + + if not isinstance(size, (list, tuple)): + raise ValueError("Size must be a tuple") + if len(size) != 2: + raise ValueError("Size must be a tuple of length 2") + if size[0] < 0 or size[1] < 0: + raise ValueError("Width and height must be >= 0") + + return True + + +def new(mode, size, color=0): + """ + Creates a new image with the given mode and size. + + :param mode: The mode to use for the new image. See: + :ref:`concept-modes`. + :param size: A 2-tuple, containing (width, height) in pixels. + :param color: What color to use for the image. Default is black. + If given, this should be a single integer or floating point value + for single-band modes, and a tuple for multi-band modes (one value + per band). When creating RGB images, you can also use color + strings as supported by the ImageColor module. If the color is + None, the image is not initialised. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + _check_size(size) + + if color is None: + # don't initialize + return Image()._new(core.new(mode, size)) + + if isinstance(color, str): + # css3-style specifier + + from . import ImageColor + + color = ImageColor.getcolor(color, mode) + + im = Image() + if mode == "P" and isinstance(color, (list, tuple)) and len(color) in [3, 4]: + # RGB or RGBA value for a P image + from . import ImagePalette + + im.palette = ImagePalette.ImagePalette() + color = im.palette.getcolor(color) + return im._new(core.fill(mode, size, color)) + + +def frombytes(mode, size, data, decoder_name="raw", *args): + """ + Creates a copy of an image memory from pixel data in a buffer. + + In its simplest form, this function takes three arguments + (mode, size, and unpacked pixel data). + + You can also use any pixel decoder supported by PIL. For more + information on available decoders, see the section + :ref:`Writing Your Own File Decoder `. + + Note that this function decodes pixel data only, not entire images. + If you have an entire image in a string, wrap it in a + :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load + it. + + :param mode: The image mode. See: :ref:`concept-modes`. + :param size: The image size. + :param data: A byte buffer containing raw data for the given mode. + :param decoder_name: What decoder to use. + :param args: Additional parameters for the given decoder. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + _check_size(size) + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + if decoder_name == "raw" and args == (): + args = mode + + im = new(mode, size) + im.frombytes(data, decoder_name, args) + return im + + +def frombuffer(mode, size, data, decoder_name="raw", *args): + """ + Creates an image memory referencing pixel data in a byte buffer. + + This function is similar to :py:func:`~PIL.Image.frombytes`, but uses data + in the byte buffer, where possible. This means that changes to the + original buffer object are reflected in this image). Not all modes can + share memory; supported modes include "L", "RGBX", "RGBA", and "CMYK". + + Note that this function decodes pixel data only, not entire images. + If you have an entire image file in a string, wrap it in a + :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load it. + + In the current version, the default parameters used for the "raw" decoder + differs from that used for :py:func:`~PIL.Image.frombytes`. This is a + bug, and will probably be fixed in a future release. The current release + issues a warning if you do this; to disable the warning, you should provide + the full set of parameters. See below for details. + + :param mode: The image mode. See: :ref:`concept-modes`. + :param size: The image size. + :param data: A bytes or other buffer object containing raw + data for the given mode. + :param decoder_name: What decoder to use. + :param args: Additional parameters for the given decoder. For the + default encoder ("raw"), it's recommended that you provide the + full set of parameters:: + + frombuffer(mode, size, data, "raw", mode, 0, 1) + + :returns: An :py:class:`~PIL.Image.Image` object. + + .. versionadded:: 1.1.4 + """ + + _check_size(size) + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + if decoder_name == "raw": + if args == (): + args = mode, 0, 1 + if args[0] in _MAPMODES: + im = new(mode, (1, 1)) + im = im._new(core.map_buffer(data, size, decoder_name, 0, args)) + im.readonly = 1 + return im + + return frombytes(mode, size, data, decoder_name, args) + + +def fromarray(obj, mode=None): + """ + Creates an image memory from an object exporting the array interface + (using the buffer protocol). + + If ``obj`` is not contiguous, then the ``tobytes`` method is called + and :py:func:`~PIL.Image.frombuffer` is used. + + If you have an image in NumPy:: + + from PIL import Image + import numpy as np + im = Image.open('hopper.jpg') + a = np.asarray(im) + + Then this can be used to convert it to a Pillow image:: + + im = Image.fromarray(a) + + :param obj: Object with array interface + :param mode: Mode to use (will be determined from type if None) + See: :ref:`concept-modes`. + :returns: An image object. + + .. versionadded:: 1.1.6 + """ + arr = obj.__array_interface__ + shape = arr["shape"] + ndim = len(shape) + strides = arr.get("strides", None) + if mode is None: + try: + typekey = (1, 1) + shape[2:], arr["typestr"] + except KeyError as e: + raise TypeError("Cannot handle this data type") from e + try: + mode, rawmode = _fromarray_typemap[typekey] + except KeyError as e: + raise TypeError("Cannot handle this data type: %s, %s" % typekey) from e + else: + rawmode = mode + if mode in ["1", "L", "I", "P", "F"]: + ndmax = 2 + elif mode == "RGB": + ndmax = 3 + else: + ndmax = 4 + if ndim > ndmax: + raise ValueError(f"Too many dimensions: {ndim} > {ndmax}.") + + size = 1 if ndim == 1 else shape[1], shape[0] + if strides is not None: + if hasattr(obj, "tobytes"): + obj = obj.tobytes() + else: + obj = obj.tostring() + + return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) + + +def fromqimage(im): + """Creates an image instance from a QImage image""" + from . import ImageQt + + if not ImageQt.qt_is_installed: + raise ImportError("Qt bindings are not installed") + return ImageQt.fromqimage(im) + + +def fromqpixmap(im): + """Creates an image instance from a QPixmap image""" + from . import ImageQt + + if not ImageQt.qt_is_installed: + raise ImportError("Qt bindings are not installed") + return ImageQt.fromqpixmap(im) + + +_fromarray_typemap = { + # (shape, typestr) => mode, rawmode + # first two members of shape are set to one + ((1, 1), "|b1"): ("1", "1;8"), + ((1, 1), "|u1"): ("L", "L"), + ((1, 1), "|i1"): ("I", "I;8"), + ((1, 1), "u2"): ("I", "I;16B"), + ((1, 1), "i2"): ("I", "I;16BS"), + ((1, 1), "u4"): ("I", "I;32B"), + ((1, 1), "i4"): ("I", "I;32BS"), + ((1, 1), "f4"): ("F", "F;32BF"), + ((1, 1), "f8"): ("F", "F;64BF"), + ((1, 1, 2), "|u1"): ("LA", "LA"), + ((1, 1, 3), "|u1"): ("RGB", "RGB"), + ((1, 1, 4), "|u1"): ("RGBA", "RGBA"), +} + +# shortcuts +_fromarray_typemap[((1, 1), _ENDIAN + "i4")] = ("I", "I") +_fromarray_typemap[((1, 1), _ENDIAN + "f4")] = ("F", "F") + + +def _decompression_bomb_check(size): + if MAX_IMAGE_PIXELS is None: + return + + pixels = size[0] * size[1] + + if pixels > 2 * MAX_IMAGE_PIXELS: + raise DecompressionBombError( + f"Image size ({pixels} pixels) exceeds limit of {2 * MAX_IMAGE_PIXELS} " + "pixels, could be decompression bomb DOS attack." + ) + + if pixels > MAX_IMAGE_PIXELS: + warnings.warn( + f"Image size ({pixels} pixels) exceeds limit of {MAX_IMAGE_PIXELS} pixels, " + "could be decompression bomb DOS attack.", + DecompressionBombWarning, + ) + + +def open(fp, mode="r", formats=None): + """ + Opens and identifies the given image file. + + This is a lazy operation; this function identifies the file, but + the file remains open and the actual image data is not read from + the file until you try to process the data (or call the + :py:meth:`~PIL.Image.Image.load` method). See + :py:func:`~PIL.Image.new`. See :ref:`file-handling`. + + :param fp: A filename (string), pathlib.Path object or a file object. + The file object must implement ``file.read``, + ``file.seek``, and ``file.tell`` methods, + and be opened in binary mode. + :param mode: The mode. If given, this argument must be "r". + :param formats: A list or tuple of formats to attempt to load the file in. + This can be used to restrict the set of formats checked. + Pass ``None`` to try all supported formats. You can print the set of + available formats by running ``python -m PIL`` or using + the :py:func:`PIL.features.pilinfo` function. + :returns: An :py:class:`~PIL.Image.Image` object. + :exception FileNotFoundError: If the file cannot be found. + :exception PIL.UnidentifiedImageError: If the image cannot be opened and + identified. + :exception ValueError: If the ``mode`` is not "r", or if a ``StringIO`` + instance is used for ``fp``. + :exception TypeError: If ``formats`` is not ``None``, a list or a tuple. + """ + + if mode != "r": + raise ValueError(f"bad mode {repr(mode)}") + elif isinstance(fp, io.StringIO): + raise ValueError( + "StringIO cannot be used to open an image. " + "Binary data must be used instead." + ) + + if formats is None: + formats = ID + elif not isinstance(formats, (list, tuple)): + raise TypeError("formats must be a list or tuple") + + exclusive_fp = False + filename = "" + if isinstance(fp, Path): + filename = str(fp.resolve()) + elif isPath(fp): + filename = fp + + if filename: + fp = builtins.open(filename, "rb") + exclusive_fp = True + + try: + fp.seek(0) + except (AttributeError, io.UnsupportedOperation): + fp = io.BytesIO(fp.read()) + exclusive_fp = True + + prefix = fp.read(16) + + preinit() + + accept_warnings = [] + + def _open_core(fp, filename, prefix, formats): + for i in formats: + if i not in OPEN: + init() + try: + factory, accept = OPEN[i] + result = not accept or accept(prefix) + if type(result) in [str, bytes]: + accept_warnings.append(result) + elif result: + fp.seek(0) + im = factory(fp, filename) + _decompression_bomb_check(im.size) + return im + except (SyntaxError, IndexError, TypeError, struct.error): + # Leave disabled by default, spams the logs with image + # opening failures that are entirely expected. + # logger.debug("", exc_info=True) + continue + except BaseException: + if exclusive_fp: + fp.close() + raise + return None + + im = _open_core(fp, filename, prefix, formats) + + if im is None: + if init(): + im = _open_core(fp, filename, prefix, formats) + + if im: + im._exclusive_fp = exclusive_fp + return im + + if exclusive_fp: + fp.close() + for message in accept_warnings: + warnings.warn(message) + raise UnidentifiedImageError( + "cannot identify image file %r" % (filename if filename else fp) + ) + + +# +# Image processing. + + +def alpha_composite(im1, im2): + """ + Alpha composite im2 over im1. + + :param im1: The first image. Must have mode RGBA. + :param im2: The second image. Must have mode RGBA, and the same size as + the first image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + im1.load() + im2.load() + return im1._new(core.alpha_composite(im1.im, im2.im)) + + +def blend(im1, im2, alpha): + """ + Creates a new image by interpolating between two input images, using + a constant alpha.:: + + out = image1 * (1.0 - alpha) + image2 * alpha + + :param im1: The first image. + :param im2: The second image. Must have the same mode and size as + the first image. + :param alpha: The interpolation alpha factor. If alpha is 0.0, a + copy of the first image is returned. If alpha is 1.0, a copy of + the second image is returned. There are no restrictions on the + alpha value. If necessary, the result is clipped to fit into + the allowed output range. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + im1.load() + im2.load() + return im1._new(core.blend(im1.im, im2.im, alpha)) + + +def composite(image1, image2, mask): + """ + Create composite image by blending images using a transparency mask. + + :param image1: The first image. + :param image2: The second image. Must have the same mode and + size as the first image. + :param mask: A mask image. This image can have mode + "1", "L", or "RGBA", and must have the same size as the + other two images. + """ + + image = image2.copy() + image.paste(image1, None, mask) + return image + + +def eval(image, *args): + """ + Applies the function (which should take one argument) to each pixel + in the given image. If the image has more than one band, the same + function is applied to each band. Note that the function is + evaluated once for each possible pixel value, so you cannot use + random components or other generators. + + :param image: The input image. + :param function: A function object, taking one integer argument. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + return image.point(args[0]) + + +def merge(mode, bands): + """ + Merge a set of single band images into a new multiband image. + + :param mode: The mode to use for the output image. See: + :ref:`concept-modes`. + :param bands: A sequence containing one single-band image for + each band in the output image. All bands must have the + same size. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if getmodebands(mode) != len(bands) or "*" in mode: + raise ValueError("wrong number of bands") + for band in bands[1:]: + if band.mode != getmodetype(mode): + raise ValueError("mode mismatch") + if band.size != bands[0].size: + raise ValueError("size mismatch") + for band in bands: + band.load() + return bands[0]._new(core.merge(mode, *[b.im for b in bands])) + + +# -------------------------------------------------------------------- +# Plugin registry + + +def register_open(id, factory, accept=None): + """ + Register an image file plugin. This function should not be used + in application code. + + :param id: An image format identifier. + :param factory: An image file factory method. + :param accept: An optional function that can be used to quickly + reject images having another format. + """ + id = id.upper() + ID.append(id) + OPEN[id] = factory, accept + + +def register_mime(id, mimetype): + """ + Registers an image MIME type. This function should not be used + in application code. + + :param id: An image format identifier. + :param mimetype: The image MIME type for this format. + """ + MIME[id.upper()] = mimetype + + +def register_save(id, driver): + """ + Registers an image save function. This function should not be + used in application code. + + :param id: An image format identifier. + :param driver: A function to save images in this format. + """ + SAVE[id.upper()] = driver + + +def register_save_all(id, driver): + """ + Registers an image function to save all the frames + of a multiframe format. This function should not be + used in application code. + + :param id: An image format identifier. + :param driver: A function to save images in this format. + """ + SAVE_ALL[id.upper()] = driver + + +def register_extension(id, extension): + """ + Registers an image extension. This function should not be + used in application code. + + :param id: An image format identifier. + :param extension: An extension used for this format. + """ + EXTENSION[extension.lower()] = id.upper() + + +def register_extensions(id, extensions): + """ + Registers image extensions. This function should not be + used in application code. + + :param id: An image format identifier. + :param extensions: A list of extensions used for this format. + """ + for extension in extensions: + register_extension(id, extension) + + +def registered_extensions(): + """ + Returns a dictionary containing all file extensions belonging + to registered plugins + """ + if not EXTENSION: + init() + return EXTENSION + + +def register_decoder(name, decoder): + """ + Registers an image decoder. This function should not be + used in application code. + + :param name: The name of the decoder + :param decoder: A callable(mode, args) that returns an + ImageFile.PyDecoder object + + .. versionadded:: 4.1.0 + """ + DECODERS[name] = decoder + + +def register_encoder(name, encoder): + """ + Registers an image encoder. This function should not be + used in application code. + + :param name: The name of the encoder + :param encoder: A callable(mode, args) that returns an + ImageFile.PyEncoder object + + .. versionadded:: 4.1.0 + """ + ENCODERS[name] = encoder + + +# -------------------------------------------------------------------- +# Simple display support. + + +def _show(image, **options): + options["_internal_pillow"] = True + _showxv(image, **options) + + +def _showxv(image, title=None, **options): + from . import ImageShow + + if "_internal_pillow" in options: + del options["_internal_pillow"] + else: + warnings.warn( + "_showxv is deprecated and will be removed in Pillow 9 (2022-01-02). " + "Use Image.show instead.", + DeprecationWarning, + ) + ImageShow.show(image, title, **options) + + +# -------------------------------------------------------------------- +# Effects + + +def effect_mandelbrot(size, extent, quality): + """ + Generate a Mandelbrot set covering the given extent. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param extent: The extent to cover, as a 4-tuple: + (x0, y0, x1, y2). + :param quality: Quality. + """ + return Image()._new(core.effect_mandelbrot(size, extent, quality)) + + +def effect_noise(size, sigma): + """ + Generate Gaussian noise centered around 128. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param sigma: Standard deviation of noise. + """ + return Image()._new(core.effect_noise(size, sigma)) + + +def linear_gradient(mode): + """ + Generate 256x256 linear gradient from black to white, top to bottom. + + :param mode: Input mode. + """ + return Image()._new(core.linear_gradient(mode)) + + +def radial_gradient(mode): + """ + Generate 256x256 radial gradient from black to white, centre to edge. + + :param mode: Input mode. + """ + return Image()._new(core.radial_gradient(mode)) + + +# -------------------------------------------------------------------- +# Resources + + +def _apply_env_variables(env=None): + if env is None: + env = os.environ + + for var_name, setter in [ + ("PILLOW_ALIGNMENT", core.set_alignment), + ("PILLOW_BLOCK_SIZE", core.set_block_size), + ("PILLOW_BLOCKS_MAX", core.set_blocks_max), + ]: + if var_name not in env: + continue + + var = env[var_name].lower() + + units = 1 + for postfix, mul in [("k", 1024), ("m", 1024 * 1024)]: + if var.endswith(postfix): + units = mul + var = var[: -len(postfix)] + + try: + var = int(var) * units + except ValueError: + warnings.warn(f"{var_name} is not int") + continue + + try: + setter(var) + except ValueError as e: + warnings.warn(f"{var_name}: {e}") + + +_apply_env_variables() +atexit.register(core.clear_cache) + + +class Exif(MutableMapping): + endian = "<" + + def __init__(self): + self._data = {} + self._ifds = {} + self._info = None + self._loaded_exif = None + + def _fixup(self, value): + try: + if len(value) == 1 and isinstance(value, tuple): + return value[0] + except Exception: + pass + return value + + def _fixup_dict(self, src_dict): + # Helper function + # returns a dict with any single item tuples/lists as individual values + return {k: self._fixup(v) for k, v in src_dict.items()} + + def _get_ifd_dict(self, tag): + try: + # an offset pointer to the location of the nested embedded IFD. + # It should be a long, but may be corrupted. + self.fp.seek(self[tag]) + except (KeyError, TypeError): + pass + else: + from . import TiffImagePlugin + + info = TiffImagePlugin.ImageFileDirectory_v2(self.head) + info.load(self.fp) + return self._fixup_dict(info) + + def load(self, data): + # Extract EXIF information. This is highly experimental, + # and is likely to be replaced with something better in a future + # version. + + # The EXIF record consists of a TIFF file embedded in a JPEG + # application marker (!). + if data == self._loaded_exif: + return + self._loaded_exif = data + self._data.clear() + self._ifds.clear() + self._info = None + if not data: + return + + if data.startswith(b"Exif\x00\x00"): + data = data[6:] + self.fp = io.BytesIO(data) + self.head = self.fp.read(8) + # process dictionary + from . import TiffImagePlugin + + self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head) + self.endian = self._info._endian + self.fp.seek(self._info.next) + self._info.load(self.fp) + + # get EXIF extension + ifd = self._get_ifd_dict(0x8769) + if ifd: + self._data.update(ifd) + self._ifds[0x8769] = ifd + + def tobytes(self, offset=8): + from . import TiffImagePlugin + + if self.endian == "<": + head = b"II\x2A\x00\x08\x00\x00\x00" + else: + head = b"MM\x00\x2A\x00\x00\x00\x08" + ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head) + for tag, value in self.items(): + ifd[tag] = value + return b"Exif\x00\x00" + head + ifd.tobytes(offset) + + def get_ifd(self, tag): + if tag not in self._ifds and tag in self: + if tag in [0x8825, 0xA005]: + # gpsinfo, interop + self._ifds[tag] = self._get_ifd_dict(tag) + elif tag == 0x927C: # makernote + from .TiffImagePlugin import ImageFileDirectory_v2 + + if self[0x927C][:8] == b"FUJIFILM": + exif_data = self[0x927C] + ifd_offset = i32le(exif_data, 8) + ifd_data = exif_data[ifd_offset:] + + makernote = {} + for i in range(0, struct.unpack(" 4: + (offset,) = struct.unpack("H", ifd_data[:2])[0]): + ifd_tag, typ, count, data = struct.unpack( + ">HHL4s", ifd_data[i * 12 + 2 : (i + 1) * 12 + 2] + ) + if ifd_tag == 0x1101: + # CameraInfo + (offset,) = struct.unpack(">L", data) + self.fp.seek(offset) + + camerainfo = {"ModelID": self.fp.read(4)} + + self.fp.read(4) + # Seconds since 2000 + camerainfo["TimeStamp"] = i32le(self.fp.read(12)) + + self.fp.read(4) + camerainfo["InternalSerialNumber"] = self.fp.read(4) + + self.fp.read(12) + parallax = self.fp.read(4) + handler = ImageFileDirectory_v2._load_dispatch[ + TiffTags.FLOAT + ][1] + camerainfo["Parallax"] = handler( + ImageFileDirectory_v2(), parallax, False + ) + + self.fp.read(4) + camerainfo["Category"] = self.fp.read(2) + + makernote = {0x1101: dict(self._fixup_dict(camerainfo))} + self._ifds[0x927C] = makernote + return self._ifds.get(tag, {}) + + def __str__(self): + if self._info is not None: + # Load all keys into self._data + for tag in self._info.keys(): + self[tag] + + return str(self._data) + + def __len__(self): + keys = set(self._data) + if self._info is not None: + keys.update(self._info) + return len(keys) + + def __getitem__(self, tag): + if self._info is not None and tag not in self._data and tag in self._info: + self._data[tag] = self._fixup(self._info[tag]) + if tag == 0x8825: + self._data[tag] = self.get_ifd(tag) + del self._info[tag] + return self._data[tag] + + def __contains__(self, tag): + return tag in self._data or (self._info is not None and tag in self._info) + + def __setitem__(self, tag, value): + if self._info is not None and tag in self._info: + del self._info[tag] + self._data[tag] = value + + def __delitem__(self, tag): + if self._info is not None and tag in self._info: + del self._info[tag] + else: + del self._data[tag] + + def __iter__(self): + keys = set(self._data) + if self._info is not None: + keys.update(self._info) + return iter(keys) diff --git a/venv/lib/python3.11/site-packages/PIL/ImageChops.py b/venv/lib/python3.11/site-packages/PIL/ImageChops.py new file mode 100644 index 00000000..61d3a295 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageChops.py @@ -0,0 +1,328 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard channel operations +# +# History: +# 1996-03-24 fl Created +# 1996-08-13 fl Added logical operations (for "1" images) +# 2000-10-12 fl Added offset method (from Image.py) +# +# Copyright (c) 1997-2000 by Secret Labs AB +# Copyright (c) 1996-2000 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from . import Image + + +def constant(image, value): + """Fill a channel with a given grey level. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.new("L", image.size, value) + + +def duplicate(image): + """Copy a channel. Alias for :py:meth:`PIL.Image.Image.copy`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return image.copy() + + +def invert(image): + """ + Invert an image (channel). + + .. code-block:: python + + out = MAX - image + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image.load() + return image._new(image.im.chop_invert()) + + +def lighter(image1, image2): + """ + Compares the two images, pixel by pixel, and returns a new image containing + the lighter values. + + .. code-block:: python + + out = max(image1, image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_lighter(image2.im)) + + +def darker(image1, image2): + """ + Compares the two images, pixel by pixel, and returns a new image containing + the darker values. + + .. code-block:: python + + out = min(image1, image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_darker(image2.im)) + + +def difference(image1, image2): + """ + Returns the absolute value of the pixel-by-pixel difference between the two + images. + + .. code-block:: python + + out = abs(image1 - image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_difference(image2.im)) + + +def multiply(image1, image2): + """ + Superimposes two images on top of each other. + + If you multiply an image with a solid black image, the result is black. If + you multiply with a solid white image, the image is unaffected. + + .. code-block:: python + + out = image1 * image2 / MAX + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_multiply(image2.im)) + + +def screen(image1, image2): + """ + Superimposes two inverted images on top of each other. + + .. code-block:: python + + out = MAX - ((MAX - image1) * (MAX - image2) / MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_screen(image2.im)) + + +def soft_light(image1, image2): + """ + Superimposes two images on top of each other using the Soft Light algorithm + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_soft_light(image2.im)) + + +def hard_light(image1, image2): + """ + Superimposes two images on top of each other using the Hard Light algorithm + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_hard_light(image2.im)) + + +def overlay(image1, image2): + """ + Superimposes two images on top of each other using the Overlay algorithm + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_overlay(image2.im)) + + +def add(image1, image2, scale=1.0, offset=0): + """ + Adds two images, dividing the result by scale and adding the + offset. If omitted, scale defaults to 1.0, and offset to 0.0. + + .. code-block:: python + + out = ((image1 + image2) / scale + offset) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_add(image2.im, scale, offset)) + + +def subtract(image1, image2, scale=1.0, offset=0): + """ + Subtracts two images, dividing the result by scale and adding the offset. + If omitted, scale defaults to 1.0, and offset to 0.0. + + .. code-block:: python + + out = ((image1 - image2) / scale + offset) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_subtract(image2.im, scale, offset)) + + +def add_modulo(image1, image2): + """Add two images, without clipping the result. + + .. code-block:: python + + out = ((image1 + image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_add_modulo(image2.im)) + + +def subtract_modulo(image1, image2): + """Subtract two images, without clipping the result. + + .. code-block:: python + + out = ((image1 - image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_subtract_modulo(image2.im)) + + +def logical_and(image1, image2): + """Logical AND between two images. + + Both of the images must have mode "1". If you would like to perform a + logical AND on an image with a mode other than "1", try + :py:meth:`~PIL.ImageChops.multiply` instead, using a black-and-white mask + as the second image. + + .. code-block:: python + + out = ((image1 and image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_and(image2.im)) + + +def logical_or(image1, image2): + """Logical OR between two images. + + Both of the images must have mode "1". + + .. code-block:: python + + out = ((image1 or image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_or(image2.im)) + + +def logical_xor(image1, image2): + """Logical XOR between two images. + + Both of the images must have mode "1". + + .. code-block:: python + + out = ((bool(image1) != bool(image2)) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_xor(image2.im)) + + +def blend(image1, image2, alpha): + """Blend images using constant transparency weight. Alias for + :py:func:`PIL.Image.blend`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.blend(image1, image2, alpha) + + +def composite(image1, image2, mask): + """Create composite using transparency mask. Alias for + :py:func:`PIL.Image.composite`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.composite(image1, image2, mask) + + +def offset(image, xoffset, yoffset=None): + """Returns a copy of the image where data has been offset by the given + distances. Data wraps around the edges. If ``yoffset`` is omitted, it + is assumed to be equal to ``xoffset``. + + :param xoffset: The horizontal distance. + :param yoffset: The vertical distance. If omitted, both + distances are set to the same value. + :rtype: :py:class:`~PIL.Image.Image` + """ + + if yoffset is None: + yoffset = xoffset + image.load() + return image._new(image.im.offset(xoffset, yoffset)) diff --git a/venv/lib/python3.11/site-packages/PIL/ImageCms.py b/venv/lib/python3.11/site-packages/PIL/ImageCms.py new file mode 100644 index 00000000..8c4740dd --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageCms.py @@ -0,0 +1,999 @@ +# The Python Imaging Library. +# $Id$ + +# Optional color management support, based on Kevin Cazabon's PyCMS +# library. + +# History: + +# 2009-03-08 fl Added to PIL. + +# Copyright (C) 2002-2003 Kevin Cazabon +# Copyright (c) 2009 by Fredrik Lundh +# Copyright (c) 2013 by Eric Soroos + +# See the README file for information on usage and redistribution. See +# below for the original description. + +import sys + +from PIL import Image + +try: + from PIL import _imagingcms +except ImportError as ex: + # Allow error import for doc purposes, but error out when accessing + # anything in core. + from ._util import deferred_error + + _imagingcms = deferred_error(ex) + +DESCRIPTION = """ +pyCMS + + a Python / PIL interface to the littleCMS ICC Color Management System + Copyright (C) 2002-2003 Kevin Cazabon + kevin@cazabon.com + http://www.cazabon.com + + pyCMS home page: http://www.cazabon.com/pyCMS + littleCMS home page: http://www.littlecms.com + (littleCMS is Copyright (C) 1998-2001 Marti Maria) + + Originally released under LGPL. Graciously donated to PIL in + March 2009, for distribution under the standard PIL license + + The pyCMS.py module provides a "clean" interface between Python/PIL and + pyCMSdll, taking care of some of the more complex handling of the direct + pyCMSdll functions, as well as error-checking and making sure that all + relevant data is kept together. + + While it is possible to call pyCMSdll functions directly, it's not highly + recommended. + + Version History: + + 1.0.0 pil Oct 2013 Port to LCMS 2. + + 0.1.0 pil mod March 10, 2009 + + Renamed display profile to proof profile. The proof + profile is the profile of the device that is being + simulated, not the profile of the device which is + actually used to display/print the final simulation + (that'd be the output profile) - also see LCMSAPI.txt + input colorspace -> using 'renderingIntent' -> proof + colorspace -> using 'proofRenderingIntent' -> output + colorspace + + Added LCMS FLAGS support. + Added FLAGS["SOFTPROOFING"] as default flag for + buildProofTransform (otherwise the proof profile/intent + would be ignored). + + 0.1.0 pil March 2009 - added to PIL, as PIL.ImageCms + + 0.0.2 alpha Jan 6, 2002 + + Added try/except statements around type() checks of + potential CObjects... Python won't let you use type() + on them, and raises a TypeError (stupid, if you ask + me!) + + Added buildProofTransformFromOpenProfiles() function. + Additional fixes in DLL, see DLL code for details. + + 0.0.1 alpha first public release, Dec. 26, 2002 + + Known to-do list with current version (of Python interface, not pyCMSdll): + + none + +""" + +VERSION = "1.0.0 pil" + +# --------------------------------------------------------------------. + +core = _imagingcms + +# +# intent/direction values + +INTENT_PERCEPTUAL = 0 +INTENT_RELATIVE_COLORIMETRIC = 1 +INTENT_SATURATION = 2 +INTENT_ABSOLUTE_COLORIMETRIC = 3 + +DIRECTION_INPUT = 0 +DIRECTION_OUTPUT = 1 +DIRECTION_PROOF = 2 + +# +# flags + +FLAGS = { + "MATRIXINPUT": 1, + "MATRIXOUTPUT": 2, + "MATRIXONLY": (1 | 2), + "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot + # Don't create prelinearization tables on precalculated transforms + # (internal use): + "NOPRELINEARIZATION": 16, + "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink) + "NOTCACHE": 64, # Inhibit 1-pixel cache + "NOTPRECALC": 256, + "NULLTRANSFORM": 512, # Don't transform anyway + "HIGHRESPRECALC": 1024, # Use more memory to give better accuracy + "LOWRESPRECALC": 2048, # Use less memory to minimize resources + "WHITEBLACKCOMPENSATION": 8192, + "BLACKPOINTCOMPENSATION": 8192, + "GAMUTCHECK": 4096, # Out of Gamut alarm + "SOFTPROOFING": 16384, # Do softproofing + "PRESERVEBLACK": 32768, # Black preservation + "NODEFAULTRESOURCEDEF": 16777216, # CRD special + "GRIDPOINTS": lambda n: ((n) & 0xFF) << 16, # Gridpoints +} + +_MAX_FLAG = 0 +for flag in FLAGS.values(): + if isinstance(flag, int): + _MAX_FLAG = _MAX_FLAG | flag + + +# --------------------------------------------------------------------. +# Experimental PIL-level API +# --------------------------------------------------------------------. + +## +# Profile. + + +class ImageCmsProfile: + def __init__(self, profile): + """ + :param profile: Either a string representing a filename, + a file like object containing a profile or a + low-level profile object + + """ + + if isinstance(profile, str): + if sys.platform == "win32": + profile_bytes_path = profile.encode() + try: + profile_bytes_path.decode("ascii") + except UnicodeDecodeError: + with open(profile, "rb") as f: + self._set(core.profile_frombytes(f.read())) + return + self._set(core.profile_open(profile), profile) + elif hasattr(profile, "read"): + self._set(core.profile_frombytes(profile.read())) + elif isinstance(profile, _imagingcms.CmsProfile): + self._set(profile) + else: + raise TypeError("Invalid type for Profile") + + def _set(self, profile, filename=None): + self.profile = profile + self.filename = filename + if profile: + self.product_name = None # profile.product_name + self.product_info = None # profile.product_info + else: + self.product_name = None + self.product_info = None + + def tobytes(self): + """ + Returns the profile in a format suitable for embedding in + saved images. + + :returns: a bytes object containing the ICC profile. + """ + + return core.profile_tobytes(self.profile) + + +class ImageCmsTransform(Image.ImagePointHandler): + + """ + Transform. This can be used with the procedural API, or with the standard + :py:func:`~PIL.Image.Image.point` method. + + Will return the output profile in the ``output.info['icc_profile']``. + """ + + def __init__( + self, + input, + output, + input_mode, + output_mode, + intent=INTENT_PERCEPTUAL, + proof=None, + proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, + flags=0, + ): + if proof is None: + self.transform = core.buildTransform( + input.profile, output.profile, input_mode, output_mode, intent, flags + ) + else: + self.transform = core.buildProofTransform( + input.profile, + output.profile, + proof.profile, + input_mode, + output_mode, + intent, + proof_intent, + flags, + ) + # Note: inputMode and outputMode are for pyCMS compatibility only + self.input_mode = self.inputMode = input_mode + self.output_mode = self.outputMode = output_mode + + self.output_profile = output + + def point(self, im): + return self.apply(im) + + def apply(self, im, imOut=None): + im.load() + if imOut is None: + imOut = Image.new(self.output_mode, im.size, None) + self.transform.apply(im.im.id, imOut.im.id) + imOut.info["icc_profile"] = self.output_profile.tobytes() + return imOut + + def apply_in_place(self, im): + im.load() + if im.mode != self.output_mode: + raise ValueError("mode mismatch") # wrong output mode + self.transform.apply(im.im.id, im.im.id) + im.info["icc_profile"] = self.output_profile.tobytes() + return im + + +def get_display_profile(handle=None): + """ + (experimental) Fetches the profile for the current display device. + + :returns: ``None`` if the profile is not known. + """ + + if sys.platform != "win32": + return None + + from PIL import ImageWin + + if isinstance(handle, ImageWin.HDC): + profile = core.get_display_profile_win32(handle, 1) + else: + profile = core.get_display_profile_win32(handle or 0) + if profile is None: + return None + return ImageCmsProfile(profile) + + +# --------------------------------------------------------------------. +# pyCMS compatible layer +# --------------------------------------------------------------------. + + +class PyCMSError(Exception): + + """(pyCMS) Exception class. + This is used for all errors in the pyCMS API.""" + + pass + + +def profileToProfile( + im, + inputProfile, + outputProfile, + renderingIntent=INTENT_PERCEPTUAL, + outputMode=None, + inPlace=False, + flags=0, +): + """ + (pyCMS) Applies an ICC transformation to a given image, mapping from + ``inputProfile`` to ``outputProfile``. + + If the input or output profiles specified are not valid filenames, a + :exc:`PyCMSError` will be raised. If ``inPlace`` is ``True`` and + ``outputMode != im.mode``, a :exc:`PyCMSError` will be raised. + If an error occurs during application of the profiles, + a :exc:`PyCMSError` will be raised. + If ``outputMode`` is not a mode supported by the ``outputProfile`` (or by pyCMS), + a :exc:`PyCMSError` will be raised. + + This function applies an ICC transformation to im from ``inputProfile``'s + color space to ``outputProfile``'s color space using the specified rendering + intent to decide how to handle out-of-gamut colors. + + ``outputMode`` can be used to specify that a color mode conversion is to + be done using these profiles, but the specified profiles must be able + to handle that mode. I.e., if converting im from RGB to CMYK using + profiles, the input profile must handle RGB data, and the output + profile must handle CMYK data. + + :param im: An open :py:class:`~PIL.Image.Image` object (i.e. Image.new(...) + or Image.open(...), etc.) + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this image, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + profile you wish to use for this image, or a profile object + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the transform + + ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) + ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 + ImageCms.INTENT_SATURATION = 2 + ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 + + see the pyCMS documentation for details on rendering intents and what + they do. + :param outputMode: A valid PIL mode for the output image (i.e. "RGB", + "CMYK", etc.). Note: if rendering the image "inPlace", outputMode + MUST be the same mode as the input, or omitted completely. If + omitted, the outputMode will be the same as the mode of the input + image (im.mode) + :param inPlace: Boolean. If ``True``, the original image is modified in-place, + and ``None`` is returned. If ``False`` (default), a new + :py:class:`~PIL.Image.Image` object is returned with the transform applied. + :param flags: Integer (0-...) specifying additional flags + :returns: Either None or a new :py:class:`~PIL.Image.Image` object, depending on + the value of ``inPlace`` + :exception PyCMSError: + """ + + if outputMode is None: + outputMode = im.mode + + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + transform = ImageCmsTransform( + inputProfile, + outputProfile, + im.mode, + outputMode, + renderingIntent, + flags=flags, + ) + if inPlace: + transform.apply_in_place(im) + imOut = None + else: + imOut = transform.apply(im) + except (OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + return imOut + + +def getOpenProfile(profileFilename): + """ + (pyCMS) Opens an ICC profile file. + + The PyCMSProfile object can be passed back into pyCMS for use in creating + transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). + + If ``profileFilename`` is not a valid filename for an ICC profile, + a :exc:`PyCMSError` will be raised. + + :param profileFilename: String, as a valid filename path to the ICC profile + you wish to open, or a file-like object. + :returns: A CmsProfile class object. + :exception PyCMSError: + """ + + try: + return ImageCmsProfile(profileFilename) + except (OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def buildTransform( + inputProfile, + outputProfile, + inMode, + outMode, + renderingIntent=INTENT_PERCEPTUAL, + flags=0, +): + """ + (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the + ``outputProfile``. Use applyTransform to apply the transform to a given + image. + + If the input or output profiles specified are not valid filenames, a + :exc:`PyCMSError` will be raised. If an error occurs during creation + of the transform, a :exc:`PyCMSError` will be raised. + + If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` + (or by pyCMS), a :exc:`PyCMSError` will be raised. + + This function builds and returns an ICC transform from the ``inputProfile`` + to the ``outputProfile`` using the ``renderingIntent`` to determine what to do + with out-of-gamut colors. It will ONLY work for converting images that + are in ``inMode`` to images that are in ``outMode`` color format (PIL mode, + i.e. "RGB", "RGBA", "CMYK", etc.). + + Building the transform is a fair part of the overhead in + ImageCms.profileToProfile(), so if you're planning on converting multiple + images using the same input/output settings, this can save you time. + Once you have a transform object, it can be used with + ImageCms.applyProfile() to convert images without the need to re-compute + the lookup table for the transform. + + The reason pyCMS returns a class object rather than a handle directly + to the transform is that it needs to keep track of the PIL input/output + modes that the transform is meant for. These attributes are stored in + the ``inMode`` and ``outMode`` attributes of the object (which can be + manually overridden if you really want to, but I don't know of any + time that would be of use, or would even work). + + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this transform, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + profile you wish to use for this transform, or a profile object + :param inMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param outMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the transform + + ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) + ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 + ImageCms.INTENT_SATURATION = 2 + ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 + + see the pyCMS documentation for details on rendering intents and what + they do. + :param flags: Integer (0-...) specifying additional flags + :returns: A CmsTransform class object. + :exception PyCMSError: + """ + + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + return ImageCmsTransform( + inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags + ) + except (OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def buildProofTransform( + inputProfile, + outputProfile, + proofProfile, + inMode, + outMode, + renderingIntent=INTENT_PERCEPTUAL, + proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC, + flags=FLAGS["SOFTPROOFING"], +): + """ + (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the + ``outputProfile``, but tries to simulate the result that would be + obtained on the ``proofProfile`` device. + + If the input, output, or proof profiles specified are not valid + filenames, a :exc:`PyCMSError` will be raised. + + If an error occurs during creation of the transform, + a :exc:`PyCMSError` will be raised. + + If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` + (or by pyCMS), a :exc:`PyCMSError` will be raised. + + This function builds and returns an ICC transform from the ``inputProfile`` + to the ``outputProfile``, but tries to simulate the result that would be + obtained on the ``proofProfile`` device using ``renderingIntent`` and + ``proofRenderingIntent`` to determine what to do with out-of-gamut + colors. This is known as "soft-proofing". It will ONLY work for + converting images that are in ``inMode`` to images that are in outMode + color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.). + + Usage of the resulting transform object is exactly the same as with + ImageCms.buildTransform(). + + Proof profiling is generally used when using an output device to get a + good idea of what the final printed/displayed image would look like on + the ``proofProfile`` device when it's quicker and easier to use the + output device for judging color. Generally, this means that the + output device is a monitor, or a dye-sub printer (etc.), and the simulated + device is something more expensive, complicated, or time consuming + (making it difficult to make a real print for color judgement purposes). + + Soft-proofing basically functions by adjusting the colors on the + output device to match the colors of the device being simulated. However, + when the simulated device has a much wider gamut than the output + device, you may obtain marginal results. + + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this transform, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + (monitor, usually) profile you wish to use for this transform, or a + profile object + :param proofProfile: String, as a valid filename path to the ICC proof + profile you wish to use for this transform, or a profile object + :param inMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param outMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the input->proof (simulated) transform + + ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) + ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 + ImageCms.INTENT_SATURATION = 2 + ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 + + see the pyCMS documentation for details on rendering intents and what + they do. + :param proofRenderingIntent: Integer (0-3) specifying the rendering intent + you wish to use for proof->output transform + + ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) + ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 + ImageCms.INTENT_SATURATION = 2 + ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 + + see the pyCMS documentation for details on rendering intents and what + they do. + :param flags: Integer (0-...) specifying additional flags + :returns: A CmsTransform class object. + :exception PyCMSError: + """ + + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + if not isinstance(proofProfile, ImageCmsProfile): + proofProfile = ImageCmsProfile(proofProfile) + return ImageCmsTransform( + inputProfile, + outputProfile, + inMode, + outMode, + renderingIntent, + proofProfile, + proofRenderingIntent, + flags, + ) + except (OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +buildTransformFromOpenProfiles = buildTransform +buildProofTransformFromOpenProfiles = buildProofTransform + + +def applyTransform(im, transform, inPlace=False): + """ + (pyCMS) Applies a transform to a given image. + + If ``im.mode != transform.inMode``, a :exc:`PyCMSError` is raised. + + If ``inPlace`` is ``True`` and ``transform.inMode != transform.outMode``, a + :exc:`PyCMSError` is raised. + + If ``im.mode``, ``transform.inMode`` or ``transform.outMode`` is not + supported by pyCMSdll or the profiles you used for the transform, a + :exc:`PyCMSError` is raised. + + If an error occurs while the transform is being applied, + a :exc:`PyCMSError` is raised. + + This function applies a pre-calculated transform (from + ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) + to an image. The transform can be used for multiple images, saving + considerable calculation time if doing the same conversion multiple times. + + If you want to modify im in-place instead of receiving a new image as + the return value, set ``inPlace`` to ``True``. This can only be done if + ``transform.inMode`` and ``transform.outMode`` are the same, because we can't + change the mode in-place (the buffer sizes for some modes are + different). The default behavior is to return a new :py:class:`~PIL.Image.Image` + object of the same dimensions in mode ``transform.outMode``. + + :param im: An :py:class:`~PIL.Image.Image` object, and im.mode must be the same + as the ``inMode`` supported by the transform. + :param transform: A valid CmsTransform class object + :param inPlace: Bool. If ``True``, ``im`` is modified in place and ``None`` is + returned, if ``False``, a new :py:class:`~PIL.Image.Image` object with the + transform applied is returned (and ``im`` is not changed). The default is + ``False``. + :returns: Either ``None``, or a new :py:class:`~PIL.Image.Image` object, + depending on the value of ``inPlace``. The profile will be returned in + the image's ``info['icc_profile']``. + :exception PyCMSError: + """ + + try: + if inPlace: + transform.apply_in_place(im) + imOut = None + else: + imOut = transform.apply(im) + except (TypeError, ValueError) as v: + raise PyCMSError(v) from v + + return imOut + + +def createProfile(colorSpace, colorTemp=-1): + """ + (pyCMS) Creates a profile. + + If colorSpace not in ``["LAB", "XYZ", "sRGB"]``, + a :exc:`PyCMSError` is raised. + + If using LAB and ``colorTemp`` is not a positive integer, + a :exc:`PyCMSError` is raised. + + If an error occurs while creating the profile, + a :exc:`PyCMSError` is raised. + + Use this function to create common profiles on-the-fly instead of + having to supply a profile on disk and knowing the path to it. It + returns a normal CmsProfile object that can be passed to + ImageCms.buildTransformFromOpenProfiles() to create a transform to apply + to images. + + :param colorSpace: String, the color space of the profile you wish to + create. + Currently only "LAB", "XYZ", and "sRGB" are supported. + :param colorTemp: Positive integer for the white point for the profile, in + degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50 + illuminant if omitted (5000k). colorTemp is ONLY applied to LAB + profiles, and is ignored for XYZ and sRGB. + :returns: A CmsProfile class object + :exception PyCMSError: + """ + + if colorSpace not in ["LAB", "XYZ", "sRGB"]: + raise PyCMSError( + f"Color space not supported for on-the-fly profile creation ({colorSpace})" + ) + + if colorSpace == "LAB": + try: + colorTemp = float(colorTemp) + except (TypeError, ValueError) as e: + raise PyCMSError( + f'Color temperature must be numeric, "{colorTemp}" not valid' + ) from e + + try: + return core.createProfile(colorSpace, colorTemp) + except (TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileName(profile): + """ + + (pyCMS) Gets the internal product name for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, + a :exc:`PyCMSError` is raised If an error occurs while trying + to obtain the name tag, a :exc:`PyCMSError` is raised. + + Use this function to obtain the INTERNAL name of the profile (stored + in an ICC tag in the profile itself), usually the one used when the + profile was originally created. Sometimes this tag also contains + additional information supplied by the creator. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal name of the profile as stored + in an ICC tag. + :exception PyCMSError: + """ + + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + # do it in python, not c. + # // name was "%s - %s" (model, manufacturer) || Description , + # // but if the Model and Manufacturer were the same or the model + # // was long, Just the model, in 1.x + model = profile.profile.model + manufacturer = profile.profile.manufacturer + + if not (model or manufacturer): + return (profile.profile.profile_description or "") + "\n" + if not manufacturer or len(model) > 30: + return model + "\n" + return f"{model} - {manufacturer}\n" + + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileInfo(profile): + """ + (pyCMS) Gets the internal product information for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, + a :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the info tag, + a :exc:`PyCMSError` is raised. + + Use this function to obtain the information stored in the profile's + info tag. This often contains details about the profile, and how it + was created, as supplied by the creator. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + # add an extra newline to preserve pyCMS compatibility + # Python, not C. the white point bits weren't working well, + # so skipping. + # info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint + description = profile.profile.profile_description + cpright = profile.profile.copyright + arr = [] + for elt in (description, cpright): + if elt: + arr.append(elt) + return "\r\n\r\n".join(arr) + "\r\n\r\n" + + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileCopyright(profile): + """ + (pyCMS) Gets the copyright for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the copyright tag, + a :exc:`PyCMSError` is raised. + + Use this function to obtain the information stored in the profile's + copyright tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return (profile.profile.copyright or "") + "\n" + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileManufacturer(profile): + """ + (pyCMS) Gets the manufacturer for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the manufacturer tag, a + :exc:`PyCMSError` is raised. + + Use this function to obtain the information stored in the profile's + manufacturer tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return (profile.profile.manufacturer or "") + "\n" + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileModel(profile): + """ + (pyCMS) Gets the model for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the model tag, + a :exc:`PyCMSError` is raised. + + Use this function to obtain the information stored in the profile's + model tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return (profile.profile.model or "") + "\n" + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileDescription(profile): + """ + (pyCMS) Gets the description for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the description tag, + a :exc:`PyCMSError` is raised. + + Use this function to obtain the information stored in the profile's + description tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in an + ICC tag. + :exception PyCMSError: + """ + + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return (profile.profile.profile_description or "") + "\n" + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getDefaultIntent(profile): + """ + (pyCMS) Gets the default intent name for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the default intent, a + :exc:`PyCMSError` is raised. + + Use this function to determine the default (and usually best optimized) + rendering intent for this profile. Most profiles support multiple + rendering intents, but are intended mostly for one type of conversion. + If you wish to use a different intent than returned, use + ImageCms.isIntentSupported() to verify it will work first. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: Integer 0-3 specifying the default rendering intent for this + profile. + + ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) + ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 + ImageCms.INTENT_SATURATION = 2 + ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 + + see the pyCMS documentation for details on rendering intents and what + they do. + :exception PyCMSError: + """ + + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.rendering_intent + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def isIntentSupported(profile, intent, direction): + """ + (pyCMS) Checks if a given intent is supported. + + Use this function to verify that you can use your desired + ``intent`` with ``profile``, and that ``profile`` can be used for the + input/output/proof profile as you desire. + + Some profiles are created specifically for one "direction", can cannot + be used for others. Some profiles can only be used for certain + rendering intents, so it's best to either verify this before trying + to create a transform with them (using this function), or catch the + potential :exc:`PyCMSError` that will occur if they don't + support the modes you select. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :param intent: Integer (0-3) specifying the rendering intent you wish to + use with this profile + + ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) + ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 + ImageCms.INTENT_SATURATION = 2 + ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 + + see the pyCMS documentation for details on rendering intents and what + they do. + :param direction: Integer specifying if the profile is to be used for + input, output, or proof + + INPUT = 0 (or use ImageCms.DIRECTION_INPUT) + OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT) + PROOF = 2 (or use ImageCms.DIRECTION_PROOF) + + :returns: 1 if the intent/direction are supported, -1 if they are not. + :exception PyCMSError: + """ + + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + # FIXME: I get different results for the same data w. different + # compilers. Bug in LittleCMS or in the binding? + if profile.profile.is_intent_supported(intent, direction): + return 1 + else: + return -1 + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def versions(): + """ + (pyCMS) Fetches versions. + """ + + return (VERSION, core.littlecms_version, sys.version.split()[0], Image.__version__) diff --git a/venv/lib/python3.11/site-packages/PIL/ImageColor.py b/venv/lib/python3.11/site-packages/PIL/ImageColor.py new file mode 100644 index 00000000..90911744 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageColor.py @@ -0,0 +1,300 @@ +# +# The Python Imaging Library +# $Id$ +# +# map CSS3-style colour description strings to RGB +# +# History: +# 2002-10-24 fl Added support for CSS-style color strings +# 2002-12-15 fl Added RGBA support +# 2004-03-27 fl Fixed remaining int() problems for Python 1.5.2 +# 2004-07-19 fl Fixed gray/grey spelling issues +# 2009-03-05 fl Fixed rounding error in grayscale calculation +# +# Copyright (c) 2002-2004 by Secret Labs AB +# Copyright (c) 2002-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import re + +from . import Image + + +def getrgb(color): + """ + Convert a color string to an RGB tuple. If the string cannot be parsed, + this function raises a :py:exc:`ValueError` exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :return: ``(red, green, blue[, alpha])`` + """ + color = color.lower() + + rgb = colormap.get(color, None) + if rgb: + if isinstance(rgb, tuple): + return rgb + colormap[color] = rgb = getrgb(rgb) + return rgb + + # check for known string formats + if re.match("#[a-f0-9]{3}$", color): + return (int(color[1] * 2, 16), int(color[2] * 2, 16), int(color[3] * 2, 16)) + + if re.match("#[a-f0-9]{4}$", color): + return ( + int(color[1] * 2, 16), + int(color[2] * 2, 16), + int(color[3] * 2, 16), + int(color[4] * 2, 16), + ) + + if re.match("#[a-f0-9]{6}$", color): + return (int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16)) + + if re.match("#[a-f0-9]{8}$", color): + return ( + int(color[1:3], 16), + int(color[3:5], 16), + int(color[5:7], 16), + int(color[7:9], 16), + ) + + m = re.match(r"rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) + if m: + return (int(m.group(1)), int(m.group(2)), int(m.group(3))) + + m = re.match(r"rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) + if m: + return ( + int((int(m.group(1)) * 255) / 100.0 + 0.5), + int((int(m.group(2)) * 255) / 100.0 + 0.5), + int((int(m.group(3)) * 255) / 100.0 + 0.5), + ) + + m = re.match( + r"hsl\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color + ) + if m: + from colorsys import hls_to_rgb + + rgb = hls_to_rgb( + float(m.group(1)) / 360.0, + float(m.group(3)) / 100.0, + float(m.group(2)) / 100.0, + ) + return ( + int(rgb[0] * 255 + 0.5), + int(rgb[1] * 255 + 0.5), + int(rgb[2] * 255 + 0.5), + ) + + m = re.match( + r"hs[bv]\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color + ) + if m: + from colorsys import hsv_to_rgb + + rgb = hsv_to_rgb( + float(m.group(1)) / 360.0, + float(m.group(2)) / 100.0, + float(m.group(3)) / 100.0, + ) + return ( + int(rgb[0] * 255 + 0.5), + int(rgb[1] * 255 + 0.5), + int(rgb[2] * 255 + 0.5), + ) + + m = re.match(r"rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) + if m: + return (int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4))) + raise ValueError(f"unknown color specifier: {repr(color)}") + + +def getcolor(color, mode): + """ + Same as :py:func:`~PIL.ImageColor.getrgb`, but converts the RGB value to a + greyscale value if the mode is not color or a palette image. If the string + cannot be parsed, this function raises a :py:exc:`ValueError` exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :return: ``(graylevel [, alpha]) or (red, green, blue[, alpha])`` + """ + # same as getrgb, but converts the result to the given mode + color, alpha = getrgb(color), 255 + if len(color) == 4: + color, alpha = color[0:3], color[3] + + if Image.getmodebase(mode) == "L": + r, g, b = color + # ITU-R Recommendation 601-2 for nonlinear RGB + # scaled to 24 bits to match the convert's implementation. + color = (r * 19595 + g * 38470 + b * 7471 + 0x8000) >> 16 + if mode[-1] == "A": + return (color, alpha) + else: + if mode[-1] == "A": + return color + (alpha,) + return color + + +colormap = { + # X11 colour table from https://drafts.csswg.org/css-color-4/, with + # gray/grey spelling issues fixed. This is a superset of HTML 4.0 + # colour names used in CSS 1. + "aliceblue": "#f0f8ff", + "antiquewhite": "#faebd7", + "aqua": "#00ffff", + "aquamarine": "#7fffd4", + "azure": "#f0ffff", + "beige": "#f5f5dc", + "bisque": "#ffe4c4", + "black": "#000000", + "blanchedalmond": "#ffebcd", + "blue": "#0000ff", + "blueviolet": "#8a2be2", + "brown": "#a52a2a", + "burlywood": "#deb887", + "cadetblue": "#5f9ea0", + "chartreuse": "#7fff00", + "chocolate": "#d2691e", + "coral": "#ff7f50", + "cornflowerblue": "#6495ed", + "cornsilk": "#fff8dc", + "crimson": "#dc143c", + "cyan": "#00ffff", + "darkblue": "#00008b", + "darkcyan": "#008b8b", + "darkgoldenrod": "#b8860b", + "darkgray": "#a9a9a9", + "darkgrey": "#a9a9a9", + "darkgreen": "#006400", + "darkkhaki": "#bdb76b", + "darkmagenta": "#8b008b", + "darkolivegreen": "#556b2f", + "darkorange": "#ff8c00", + "darkorchid": "#9932cc", + "darkred": "#8b0000", + "darksalmon": "#e9967a", + "darkseagreen": "#8fbc8f", + "darkslateblue": "#483d8b", + "darkslategray": "#2f4f4f", + "darkslategrey": "#2f4f4f", + "darkturquoise": "#00ced1", + "darkviolet": "#9400d3", + "deeppink": "#ff1493", + "deepskyblue": "#00bfff", + "dimgray": "#696969", + "dimgrey": "#696969", + "dodgerblue": "#1e90ff", + "firebrick": "#b22222", + "floralwhite": "#fffaf0", + "forestgreen": "#228b22", + "fuchsia": "#ff00ff", + "gainsboro": "#dcdcdc", + "ghostwhite": "#f8f8ff", + "gold": "#ffd700", + "goldenrod": "#daa520", + "gray": "#808080", + "grey": "#808080", + "green": "#008000", + "greenyellow": "#adff2f", + "honeydew": "#f0fff0", + "hotpink": "#ff69b4", + "indianred": "#cd5c5c", + "indigo": "#4b0082", + "ivory": "#fffff0", + "khaki": "#f0e68c", + "lavender": "#e6e6fa", + "lavenderblush": "#fff0f5", + "lawngreen": "#7cfc00", + "lemonchiffon": "#fffacd", + "lightblue": "#add8e6", + "lightcoral": "#f08080", + "lightcyan": "#e0ffff", + "lightgoldenrodyellow": "#fafad2", + "lightgreen": "#90ee90", + "lightgray": "#d3d3d3", + "lightgrey": "#d3d3d3", + "lightpink": "#ffb6c1", + "lightsalmon": "#ffa07a", + "lightseagreen": "#20b2aa", + "lightskyblue": "#87cefa", + "lightslategray": "#778899", + "lightslategrey": "#778899", + "lightsteelblue": "#b0c4de", + "lightyellow": "#ffffe0", + "lime": "#00ff00", + "limegreen": "#32cd32", + "linen": "#faf0e6", + "magenta": "#ff00ff", + "maroon": "#800000", + "mediumaquamarine": "#66cdaa", + "mediumblue": "#0000cd", + "mediumorchid": "#ba55d3", + "mediumpurple": "#9370db", + "mediumseagreen": "#3cb371", + "mediumslateblue": "#7b68ee", + "mediumspringgreen": "#00fa9a", + "mediumturquoise": "#48d1cc", + "mediumvioletred": "#c71585", + "midnightblue": "#191970", + "mintcream": "#f5fffa", + "mistyrose": "#ffe4e1", + "moccasin": "#ffe4b5", + "navajowhite": "#ffdead", + "navy": "#000080", + "oldlace": "#fdf5e6", + "olive": "#808000", + "olivedrab": "#6b8e23", + "orange": "#ffa500", + "orangered": "#ff4500", + "orchid": "#da70d6", + "palegoldenrod": "#eee8aa", + "palegreen": "#98fb98", + "paleturquoise": "#afeeee", + "palevioletred": "#db7093", + "papayawhip": "#ffefd5", + "peachpuff": "#ffdab9", + "peru": "#cd853f", + "pink": "#ffc0cb", + "plum": "#dda0dd", + "powderblue": "#b0e0e6", + "purple": "#800080", + "rebeccapurple": "#663399", + "red": "#ff0000", + "rosybrown": "#bc8f8f", + "royalblue": "#4169e1", + "saddlebrown": "#8b4513", + "salmon": "#fa8072", + "sandybrown": "#f4a460", + "seagreen": "#2e8b57", + "seashell": "#fff5ee", + "sienna": "#a0522d", + "silver": "#c0c0c0", + "skyblue": "#87ceeb", + "slateblue": "#6a5acd", + "slategray": "#708090", + "slategrey": "#708090", + "snow": "#fffafa", + "springgreen": "#00ff7f", + "steelblue": "#4682b4", + "tan": "#d2b48c", + "teal": "#008080", + "thistle": "#d8bfd8", + "tomato": "#ff6347", + "turquoise": "#40e0d0", + "violet": "#ee82ee", + "wheat": "#f5deb3", + "white": "#ffffff", + "whitesmoke": "#f5f5f5", + "yellow": "#ffff00", + "yellowgreen": "#9acd32", +} diff --git a/venv/lib/python3.11/site-packages/PIL/ImageDraw.py b/venv/lib/python3.11/site-packages/PIL/ImageDraw.py new file mode 100644 index 00000000..b823be9a --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageDraw.py @@ -0,0 +1,898 @@ +# +# The Python Imaging Library +# $Id$ +# +# drawing interface operations +# +# History: +# 1996-04-13 fl Created (experimental) +# 1996-08-07 fl Filled polygons, ellipses. +# 1996-08-13 fl Added text support +# 1998-06-28 fl Handle I and F images +# 1998-12-29 fl Added arc; use arc primitive to draw ellipses +# 1999-01-10 fl Added shape stuff (experimental) +# 1999-02-06 fl Added bitmap support +# 1999-02-11 fl Changed all primitives to take options +# 1999-02-20 fl Fixed backwards compatibility +# 2000-10-12 fl Copy on write, when necessary +# 2001-02-18 fl Use default ink for bitmap/text also in fill mode +# 2002-10-24 fl Added support for CSS-style color strings +# 2002-12-10 fl Added experimental support for RGBA-on-RGB drawing +# 2002-12-11 fl Refactored low-level drawing API (work in progress) +# 2004-08-26 fl Made Draw() a factory function, added getdraw() support +# 2004-09-04 fl Added width support to line primitive +# 2004-09-10 fl Added font mode handling +# 2006-06-19 fl Added font bearing support (getmask2) +# +# Copyright (c) 1997-2006 by Secret Labs AB +# Copyright (c) 1996-2006 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import math +import numbers + +from . import Image, ImageColor + +""" +A simple 2D drawing interface for PIL images. +

+Application code should use the Draw factory, instead of +directly. +""" + + +class ImageDraw: + def __init__(self, im, mode=None): + """ + Create a drawing instance. + + :param im: The image to draw in. + :param mode: Optional mode to use for color values. For RGB + images, this argument can be RGB or RGBA (to blend the + drawing into the image). For all other modes, this argument + must be the same as the image mode. If omitted, the mode + defaults to the mode of the image. + """ + im.load() + if im.readonly: + im._copy() # make it writeable + blend = 0 + if mode is None: + mode = im.mode + if mode != im.mode: + if mode == "RGBA" and im.mode == "RGB": + blend = 1 + else: + raise ValueError("mode mismatch") + if mode == "P": + self.palette = im.palette + else: + self.palette = None + self.im = im.im + self.draw = Image.core.draw(self.im, blend) + self.mode = mode + if mode in ("I", "F"): + self.ink = self.draw.draw_ink(1) + else: + self.ink = self.draw.draw_ink(-1) + if mode in ("1", "P", "I", "F"): + # FIXME: fix Fill2 to properly support matte for I+F images + self.fontmode = "1" + else: + self.fontmode = "L" # aliasing is okay for other modes + self.fill = 0 + self.font = None + + def getfont(self): + """ + Get the current default font. + + :returns: An image font.""" + if not self.font: + # FIXME: should add a font repository + from . import ImageFont + + self.font = ImageFont.load_default() + return self.font + + def _getink(self, ink, fill=None): + if ink is None and fill is None: + if self.fill: + fill = self.ink + else: + ink = self.ink + else: + if ink is not None: + if isinstance(ink, str): + ink = ImageColor.getcolor(ink, self.mode) + if self.palette and not isinstance(ink, numbers.Number): + ink = self.palette.getcolor(ink) + ink = self.draw.draw_ink(ink) + if fill is not None: + if isinstance(fill, str): + fill = ImageColor.getcolor(fill, self.mode) + if self.palette and not isinstance(fill, numbers.Number): + fill = self.palette.getcolor(fill) + fill = self.draw.draw_ink(fill) + return ink, fill + + def arc(self, xy, start, end, fill=None, width=1): + """Draw an arc.""" + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_arc(xy, start, end, ink, width) + + def bitmap(self, xy, bitmap, fill=None): + """Draw a bitmap.""" + bitmap.load() + ink, fill = self._getink(fill) + if ink is None: + ink = fill + if ink is not None: + self.draw.draw_bitmap(xy, bitmap.im, ink) + + def chord(self, xy, start, end, fill=None, outline=None, width=1): + """Draw a chord.""" + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_chord(xy, start, end, fill, 1) + if ink is not None and ink != fill and width != 0: + self.draw.draw_chord(xy, start, end, ink, 0, width) + + def ellipse(self, xy, fill=None, outline=None, width=1): + """Draw an ellipse.""" + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_ellipse(xy, fill, 1) + if ink is not None and ink != fill and width != 0: + self.draw.draw_ellipse(xy, ink, 0, width) + + def line(self, xy, fill=None, width=0, joint=None): + """Draw a line, or a connected sequence of line segments.""" + ink = self._getink(fill)[0] + if ink is not None: + self.draw.draw_lines(xy, ink, width) + if joint == "curve" and width > 4: + if not isinstance(xy[0], (list, tuple)): + xy = [tuple(xy[i : i + 2]) for i in range(0, len(xy), 2)] + for i in range(1, len(xy) - 1): + point = xy[i] + angles = [ + math.degrees(math.atan2(end[0] - start[0], start[1] - end[1])) + % 360 + for start, end in ((xy[i - 1], point), (point, xy[i + 1])) + ] + if angles[0] == angles[1]: + # This is a straight line, so no joint is required + continue + + def coord_at_angle(coord, angle): + x, y = coord + angle -= 90 + distance = width / 2 - 1 + return tuple( + [ + p + (math.floor(p_d) if p_d > 0 else math.ceil(p_d)) + for p, p_d in ( + (x, distance * math.cos(math.radians(angle))), + (y, distance * math.sin(math.radians(angle))), + ) + ] + ) + + flipped = ( + angles[1] > angles[0] and angles[1] - 180 > angles[0] + ) or (angles[1] < angles[0] and angles[1] + 180 > angles[0]) + coords = [ + (point[0] - width / 2 + 1, point[1] - width / 2 + 1), + (point[0] + width / 2 - 1, point[1] + width / 2 - 1), + ] + if flipped: + start, end = (angles[1] + 90, angles[0] + 90) + else: + start, end = (angles[0] - 90, angles[1] - 90) + self.pieslice(coords, start - 90, end - 90, fill) + + if width > 8: + # Cover potential gaps between the line and the joint + if flipped: + gapCoords = [ + coord_at_angle(point, angles[0] + 90), + point, + coord_at_angle(point, angles[1] + 90), + ] + else: + gapCoords = [ + coord_at_angle(point, angles[0] - 90), + point, + coord_at_angle(point, angles[1] - 90), + ] + self.line(gapCoords, fill, width=3) + + def shape(self, shape, fill=None, outline=None): + """(Experimental) Draw a shape.""" + shape.close() + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_outline(shape, fill, 1) + if ink is not None and ink != fill: + self.draw.draw_outline(shape, ink, 0) + + def pieslice(self, xy, start, end, fill=None, outline=None, width=1): + """Draw a pieslice.""" + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_pieslice(xy, start, end, fill, 1) + if ink is not None and ink != fill and width != 0: + self.draw.draw_pieslice(xy, start, end, ink, 0, width) + + def point(self, xy, fill=None): + """Draw one or more individual pixels.""" + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_points(xy, ink) + + def polygon(self, xy, fill=None, outline=None): + """Draw a polygon.""" + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_polygon(xy, fill, 1) + if ink is not None and ink != fill: + self.draw.draw_polygon(xy, ink, 0) + + def regular_polygon( + self, bounding_circle, n_sides, rotation=0, fill=None, outline=None + ): + """Draw a regular polygon.""" + xy = _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation) + self.polygon(xy, fill, outline) + + def rectangle(self, xy, fill=None, outline=None, width=1): + """Draw a rectangle.""" + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_rectangle(xy, fill, 1) + if ink is not None and ink != fill and width != 0: + self.draw.draw_rectangle(xy, ink, 0, width) + + def _multiline_check(self, text): + """Draw text.""" + split_character = "\n" if isinstance(text, str) else b"\n" + + return split_character in text + + def _multiline_split(self, text): + split_character = "\n" if isinstance(text, str) else b"\n" + + return text.split(split_character) + + def text( + self, + xy, + text, + fill=None, + font=None, + anchor=None, + spacing=4, + align="left", + direction=None, + features=None, + language=None, + stroke_width=0, + stroke_fill=None, + embedded_color=False, + *args, + **kwargs, + ): + if self._multiline_check(text): + return self.multiline_text( + xy, + text, + fill, + font, + anchor, + spacing, + align, + direction, + features, + language, + stroke_width, + stroke_fill, + embedded_color, + ) + + if embedded_color and self.mode not in ("RGB", "RGBA"): + raise ValueError("Embedded color supported only in RGB and RGBA modes") + + if font is None: + font = self.getfont() + + def getink(fill): + ink, fill = self._getink(fill) + if ink is None: + return fill + return ink + + def draw_text(ink, stroke_width=0, stroke_offset=None): + mode = self.fontmode + if stroke_width == 0 and embedded_color: + mode = "RGBA" + coord = xy + try: + mask, offset = font.getmask2( + text, + mode, + direction=direction, + features=features, + language=language, + stroke_width=stroke_width, + anchor=anchor, + ink=ink, + *args, + **kwargs, + ) + coord = coord[0] + offset[0], coord[1] + offset[1] + except AttributeError: + try: + mask = font.getmask( + text, + mode, + direction, + features, + language, + stroke_width, + anchor, + ink, + *args, + **kwargs, + ) + except TypeError: + mask = font.getmask(text) + if stroke_offset: + coord = coord[0] + stroke_offset[0], coord[1] + stroke_offset[1] + if mode == "RGBA": + # font.getmask2(mode="RGBA") returns color in RGB bands and mask in A + # extract mask and set text alpha + color, mask = mask, mask.getband(3) + color.fillband(3, (ink >> 24) & 0xFF) + coord2 = coord[0] + mask.size[0], coord[1] + mask.size[1] + self.im.paste(color, coord + coord2, mask) + else: + self.draw.draw_bitmap(coord, mask, ink) + + ink = getink(fill) + if ink is not None: + stroke_ink = None + if stroke_width: + stroke_ink = getink(stroke_fill) if stroke_fill is not None else ink + + if stroke_ink is not None: + # Draw stroked text + draw_text(stroke_ink, stroke_width) + + # Draw normal text + draw_text(ink, 0) + else: + # Only draw normal text + draw_text(ink) + + def multiline_text( + self, + xy, + text, + fill=None, + font=None, + anchor=None, + spacing=4, + align="left", + direction=None, + features=None, + language=None, + stroke_width=0, + stroke_fill=None, + embedded_color=False, + ): + if direction == "ttb": + raise ValueError("ttb direction is unsupported for multiline text") + + if anchor is None: + anchor = "la" + elif len(anchor) != 2: + raise ValueError("anchor must be a 2 character string") + elif anchor[1] in "tb": + raise ValueError("anchor not supported for multiline text") + + widths = [] + max_width = 0 + lines = self._multiline_split(text) + line_spacing = ( + self.textsize("A", font=font, stroke_width=stroke_width)[1] + spacing + ) + for line in lines: + line_width = self.textlength( + line, font, direction=direction, features=features, language=language + ) + widths.append(line_width) + max_width = max(max_width, line_width) + + top = xy[1] + if anchor[1] == "m": + top -= (len(lines) - 1) * line_spacing / 2.0 + elif anchor[1] == "d": + top -= (len(lines) - 1) * line_spacing + + for idx, line in enumerate(lines): + left = xy[0] + width_difference = max_width - widths[idx] + + # first align left by anchor + if anchor[0] == "m": + left -= width_difference / 2.0 + elif anchor[0] == "r": + left -= width_difference + + # then align by align parameter + if align == "left": + pass + elif align == "center": + left += width_difference / 2.0 + elif align == "right": + left += width_difference + else: + raise ValueError('align must be "left", "center" or "right"') + + self.text( + (left, top), + line, + fill, + font, + anchor, + direction=direction, + features=features, + language=language, + stroke_width=stroke_width, + stroke_fill=stroke_fill, + embedded_color=embedded_color, + ) + top += line_spacing + + def textsize( + self, + text, + font=None, + spacing=4, + direction=None, + features=None, + language=None, + stroke_width=0, + ): + """Get the size of a given string, in pixels.""" + if self._multiline_check(text): + return self.multiline_textsize( + text, font, spacing, direction, features, language, stroke_width + ) + + if font is None: + font = self.getfont() + return font.getsize(text, direction, features, language, stroke_width) + + def multiline_textsize( + self, + text, + font=None, + spacing=4, + direction=None, + features=None, + language=None, + stroke_width=0, + ): + max_width = 0 + lines = self._multiline_split(text) + line_spacing = ( + self.textsize("A", font=font, stroke_width=stroke_width)[1] + spacing + ) + for line in lines: + line_width, line_height = self.textsize( + line, font, spacing, direction, features, language, stroke_width + ) + max_width = max(max_width, line_width) + return max_width, len(lines) * line_spacing - spacing + + def textlength( + self, + text, + font=None, + direction=None, + features=None, + language=None, + embedded_color=False, + ): + """Get the length of a given string, in pixels with 1/64 precision.""" + if self._multiline_check(text): + raise ValueError("can't measure length of multiline text") + if embedded_color and self.mode not in ("RGB", "RGBA"): + raise ValueError("Embedded color supported only in RGB and RGBA modes") + + if font is None: + font = self.getfont() + mode = "RGBA" if embedded_color else self.fontmode + try: + return font.getlength(text, mode, direction, features, language) + except AttributeError: + size = self.textsize( + text, font, direction=direction, features=features, language=language + ) + if direction == "ttb": + return size[1] + return size[0] + + def textbbox( + self, + xy, + text, + font=None, + anchor=None, + spacing=4, + align="left", + direction=None, + features=None, + language=None, + stroke_width=0, + embedded_color=False, + ): + """Get the bounding box of a given string, in pixels.""" + if embedded_color and self.mode not in ("RGB", "RGBA"): + raise ValueError("Embedded color supported only in RGB and RGBA modes") + + if self._multiline_check(text): + return self.multiline_textbbox( + xy, + text, + font, + anchor, + spacing, + align, + direction, + features, + language, + stroke_width, + embedded_color, + ) + + if font is None: + font = self.getfont() + mode = "RGBA" if embedded_color else self.fontmode + bbox = font.getbbox( + text, mode, direction, features, language, stroke_width, anchor + ) + return bbox[0] + xy[0], bbox[1] + xy[1], bbox[2] + xy[0], bbox[3] + xy[1] + + def multiline_textbbox( + self, + xy, + text, + font=None, + anchor=None, + spacing=4, + align="left", + direction=None, + features=None, + language=None, + stroke_width=0, + embedded_color=False, + ): + if direction == "ttb": + raise ValueError("ttb direction is unsupported for multiline text") + + if anchor is None: + anchor = "la" + elif len(anchor) != 2: + raise ValueError("anchor must be a 2 character string") + elif anchor[1] in "tb": + raise ValueError("anchor not supported for multiline text") + + widths = [] + max_width = 0 + lines = self._multiline_split(text) + line_spacing = ( + self.textsize("A", font=font, stroke_width=stroke_width)[1] + spacing + ) + for line in lines: + line_width = self.textlength( + line, + font, + direction=direction, + features=features, + language=language, + embedded_color=embedded_color, + ) + widths.append(line_width) + max_width = max(max_width, line_width) + + top = xy[1] + if anchor[1] == "m": + top -= (len(lines) - 1) * line_spacing / 2.0 + elif anchor[1] == "d": + top -= (len(lines) - 1) * line_spacing + + bbox = None + + for idx, line in enumerate(lines): + left = xy[0] + width_difference = max_width - widths[idx] + + # first align left by anchor + if anchor[0] == "m": + left -= width_difference / 2.0 + elif anchor[0] == "r": + left -= width_difference + + # then align by align parameter + if align == "left": + pass + elif align == "center": + left += width_difference / 2.0 + elif align == "right": + left += width_difference + else: + raise ValueError('align must be "left", "center" or "right"') + + bbox_line = self.textbbox( + (left, top), + line, + font, + anchor, + direction=direction, + features=features, + language=language, + stroke_width=stroke_width, + embedded_color=embedded_color, + ) + if bbox is None: + bbox = bbox_line + else: + bbox = ( + min(bbox[0], bbox_line[0]), + min(bbox[1], bbox_line[1]), + max(bbox[2], bbox_line[2]), + max(bbox[3], bbox_line[3]), + ) + + top += line_spacing + + if bbox is None: + return xy[0], xy[1], xy[0], xy[1] + return bbox + + +def Draw(im, mode=None): + """ + A simple 2D drawing interface for PIL images. + + :param im: The image to draw in. + :param mode: Optional mode to use for color values. For RGB + images, this argument can be RGB or RGBA (to blend the + drawing into the image). For all other modes, this argument + must be the same as the image mode. If omitted, the mode + defaults to the mode of the image. + """ + try: + return im.getdraw(mode) + except AttributeError: + return ImageDraw(im, mode) + + +# experimental access to the outline API +try: + Outline = Image.core.outline +except AttributeError: + Outline = None + + +def getdraw(im=None, hints=None): + """ + (Experimental) A more advanced 2D drawing interface for PIL images, + based on the WCK interface. + + :param im: The image to draw in. + :param hints: An optional list of hints. + :returns: A (drawing context, drawing resource factory) tuple. + """ + # FIXME: this needs more work! + # FIXME: come up with a better 'hints' scheme. + handler = None + if not hints or "nicest" in hints: + try: + from . import _imagingagg as handler + except ImportError: + pass + if handler is None: + from . import ImageDraw2 as handler + if im: + im = handler.Draw(im) + return im, handler + + +def floodfill(image, xy, value, border=None, thresh=0): + """ + (experimental) Fills a bounded region with a given color. + + :param image: Target image. + :param xy: Seed position (a 2-item coordinate tuple). See + :ref:`coordinate-system`. + :param value: Fill color. + :param border: Optional border value. If given, the region consists of + pixels with a color different from the border color. If not given, + the region consists of pixels having the same color as the seed + pixel. + :param thresh: Optional threshold value which specifies a maximum + tolerable difference of a pixel value from the 'background' in + order for it to be replaced. Useful for filling regions of + non-homogeneous, but similar, colors. + """ + # based on an implementation by Eric S. Raymond + # amended by yo1995 @20180806 + pixel = image.load() + x, y = xy + try: + background = pixel[x, y] + if _color_diff(value, background) <= thresh: + return # seed point already has fill color + pixel[x, y] = value + except (ValueError, IndexError): + return # seed point outside image + edge = {(x, y)} + # use a set to keep record of current and previous edge pixels + # to reduce memory consumption + full_edge = set() + while edge: + new_edge = set() + for (x, y) in edge: # 4 adjacent method + for (s, t) in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)): + # If already processed, or if a coordinate is negative, skip + if (s, t) in full_edge or s < 0 or t < 0: + continue + try: + p = pixel[s, t] + except (ValueError, IndexError): + pass + else: + full_edge.add((s, t)) + if border is None: + fill = _color_diff(p, background) <= thresh + else: + fill = p != value and p != border + if fill: + pixel[s, t] = value + new_edge.add((s, t)) + full_edge = edge # discard pixels processed + edge = new_edge + + +def _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation): + """ + Generate a list of vertices for a 2D regular polygon. + + :param bounding_circle: The bounding circle is a tuple defined + by a point and radius. The polygon is inscribed in this circle. + (e.g. ``bounding_circle=(x, y, r)`` or ``((x, y), r)``) + :param n_sides: Number of sides + (e.g. ``n_sides=3`` for a triangle, ``6`` for a hexagon) + :param rotation: Apply an arbitrary rotation to the polygon + (e.g. ``rotation=90``, applies a 90 degree rotation) + :return: List of regular polygon vertices + (e.g. ``[(25, 50), (50, 50), (50, 25), (25, 25)]``) + + How are the vertices computed? + 1. Compute the following variables + - theta: Angle between the apothem & the nearest polygon vertex + - side_length: Length of each polygon edge + - centroid: Center of bounding circle (1st, 2nd elements of bounding_circle) + - polygon_radius: Polygon radius (last element of bounding_circle) + - angles: Location of each polygon vertex in polar grid + (e.g. A square with 0 degree rotation => [225.0, 315.0, 45.0, 135.0]) + + 2. For each angle in angles, get the polygon vertex at that angle + The vertex is computed using the equation below. + X= xcos(φ) + ysin(φ) + Y= −xsin(φ) + ycos(φ) + + Note: + φ = angle in degrees + x = 0 + y = polygon_radius + + The formula above assumes rotation around the origin. + In our case, we are rotating around the centroid. + To account for this, we use the formula below + X = xcos(φ) + ysin(φ) + centroid_x + Y = −xsin(φ) + ycos(φ) + centroid_y + """ + # 1. Error Handling + # 1.1 Check `n_sides` has an appropriate value + if not isinstance(n_sides, int): + raise TypeError("n_sides should be an int") + if n_sides < 3: + raise ValueError("n_sides should be an int > 2") + + # 1.2 Check `bounding_circle` has an appropriate value + if not isinstance(bounding_circle, (list, tuple)): + raise TypeError("bounding_circle should be a tuple") + + if len(bounding_circle) == 3: + *centroid, polygon_radius = bounding_circle + elif len(bounding_circle) == 2: + centroid, polygon_radius = bounding_circle + else: + raise ValueError( + "bounding_circle should contain 2D coordinates " + "and a radius (e.g. (x, y, r) or ((x, y), r) )" + ) + + if not all(isinstance(i, (int, float)) for i in (*centroid, polygon_radius)): + raise ValueError("bounding_circle should only contain numeric data") + + if not len(centroid) == 2: + raise ValueError( + "bounding_circle centre should contain 2D coordinates (e.g. (x, y))" + ) + + if polygon_radius <= 0: + raise ValueError("bounding_circle radius should be > 0") + + # 1.3 Check `rotation` has an appropriate value + if not isinstance(rotation, (int, float)): + raise ValueError("rotation should be an int or float") + + # 2. Define Helper Functions + def _apply_rotation(point, degrees, centroid): + return ( + round( + point[0] * math.cos(math.radians(360 - degrees)) + - point[1] * math.sin(math.radians(360 - degrees)) + + centroid[0], + 2, + ), + round( + point[1] * math.cos(math.radians(360 - degrees)) + + point[0] * math.sin(math.radians(360 - degrees)) + + centroid[1], + 2, + ), + ) + + def _compute_polygon_vertex(centroid, polygon_radius, angle): + start_point = [polygon_radius, 0] + return _apply_rotation(start_point, angle, centroid) + + def _get_angles(n_sides, rotation): + angles = [] + degrees = 360 / n_sides + # Start with the bottom left polygon vertex + current_angle = (270 - 0.5 * degrees) + rotation + for _ in range(0, n_sides): + angles.append(current_angle) + current_angle += degrees + if current_angle > 360: + current_angle -= 360 + return angles + + # 3. Variable Declarations + angles = _get_angles(n_sides, rotation) + + # 4. Compute Vertices + return [ + _compute_polygon_vertex(centroid, polygon_radius, angle) for angle in angles + ] + + +def _color_diff(color1, color2): + """ + Uses 1-norm distance to calculate difference between two values. + """ + if isinstance(color2, tuple): + return sum([abs(color1[i] - color2[i]) for i in range(0, len(color2))]) + else: + return abs(color1 - color2) diff --git a/venv/lib/python3.11/site-packages/PIL/ImageDraw2.py b/venv/lib/python3.11/site-packages/PIL/ImageDraw2.py new file mode 100644 index 00000000..1f63110f --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageDraw2.py @@ -0,0 +1,179 @@ +# +# The Python Imaging Library +# $Id$ +# +# WCK-style drawing interface operations +# +# History: +# 2003-12-07 fl created +# 2005-05-15 fl updated; added to PIL as ImageDraw2 +# 2005-05-15 fl added text support +# 2005-05-20 fl added arc/chord/pieslice support +# +# Copyright (c) 2003-2005 by Secret Labs AB +# Copyright (c) 2003-2005 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +""" +(Experimental) WCK-style drawing interface operations + +.. seealso:: :py:mod:`PIL.ImageDraw` +""" + + +from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath + + +class Pen: + """Stores an outline color and width.""" + + def __init__(self, color, width=1, opacity=255): + self.color = ImageColor.getrgb(color) + self.width = width + + +class Brush: + """Stores a fill color""" + + def __init__(self, color, opacity=255): + self.color = ImageColor.getrgb(color) + + +class Font: + """Stores a TrueType font and color""" + + def __init__(self, color, file, size=12): + # FIXME: add support for bitmap fonts + self.color = ImageColor.getrgb(color) + self.font = ImageFont.truetype(file, size) + + +class Draw: + """ + (Experimental) WCK-style drawing interface + """ + + def __init__(self, image, size=None, color=None): + if not hasattr(image, "im"): + image = Image.new(image, size, color) + self.draw = ImageDraw.Draw(image) + self.image = image + self.transform = None + + def flush(self): + return self.image + + def render(self, op, xy, pen, brush=None): + # handle color arguments + outline = fill = None + width = 1 + if isinstance(pen, Pen): + outline = pen.color + width = pen.width + elif isinstance(brush, Pen): + outline = brush.color + width = brush.width + if isinstance(brush, Brush): + fill = brush.color + elif isinstance(pen, Brush): + fill = pen.color + # handle transformation + if self.transform: + xy = ImagePath.Path(xy) + xy.transform(self.transform) + # render the item + if op == "line": + self.draw.line(xy, fill=outline, width=width) + else: + getattr(self.draw, op)(xy, fill=fill, outline=outline) + + def settransform(self, offset): + """Sets a transformation offset.""" + (xoffset, yoffset) = offset + self.transform = (1, 0, xoffset, 0, 1, yoffset) + + def arc(self, xy, start, end, *options): + """ + Draws an arc (a portion of a circle outline) between the start and end + angles, inside the given bounding box. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.arc` + """ + self.render("arc", xy, start, end, *options) + + def chord(self, xy, start, end, *options): + """ + Same as :py:meth:`~PIL.ImageDraw2.Draw.arc`, but connects the end points + with a straight line. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.chord` + """ + self.render("chord", xy, start, end, *options) + + def ellipse(self, xy, *options): + """ + Draws an ellipse inside the given bounding box. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.ellipse` + """ + self.render("ellipse", xy, *options) + + def line(self, xy, *options): + """ + Draws a line between the coordinates in the ``xy`` list. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.line` + """ + self.render("line", xy, *options) + + def pieslice(self, xy, start, end, *options): + """ + Same as arc, but also draws straight lines between the end points and the + center of the bounding box. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.pieslice` + """ + self.render("pieslice", xy, start, end, *options) + + def polygon(self, xy, *options): + """ + Draws a polygon. + + The polygon outline consists of straight lines between the given + coordinates, plus a straight line between the last and the first + coordinate. + + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.polygon` + """ + self.render("polygon", xy, *options) + + def rectangle(self, xy, *options): + """ + Draws a rectangle. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.rectangle` + """ + self.render("rectangle", xy, *options) + + def text(self, xy, text, font): + """ + Draws the string at the given position. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.text` + """ + if self.transform: + xy = ImagePath.Path(xy) + xy.transform(self.transform) + self.draw.text(xy, text, font=font.font, fill=font.color) + + def textsize(self, text, font): + """ + Return the size of the given string, in pixels. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textsize` + """ + return self.draw.textsize(text, font=font.font) diff --git a/venv/lib/python3.11/site-packages/PIL/ImageEnhance.py b/venv/lib/python3.11/site-packages/PIL/ImageEnhance.py new file mode 100644 index 00000000..3b79d5c4 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageEnhance.py @@ -0,0 +1,103 @@ +# +# The Python Imaging Library. +# $Id$ +# +# image enhancement classes +# +# For a background, see "Image Processing By Interpolation and +# Extrapolation", Paul Haeberli and Douglas Voorhies. Available +# at http://www.graficaobscura.com/interp/index.html +# +# History: +# 1996-03-23 fl Created +# 2009-06-16 fl Fixed mean calculation +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +from . import Image, ImageFilter, ImageStat + + +class _Enhance: + def enhance(self, factor): + """ + Returns an enhanced image. + + :param factor: A floating point value controlling the enhancement. + Factor 1.0 always returns a copy of the original image, + lower factors mean less color (brightness, contrast, + etc), and higher values more. There are no restrictions + on this value. + :rtype: :py:class:`~PIL.Image.Image` + """ + return Image.blend(self.degenerate, self.image, factor) + + +class Color(_Enhance): + """Adjust image color balance. + + This class can be used to adjust the colour balance of an image, in + a manner similar to the controls on a colour TV set. An enhancement + factor of 0.0 gives a black and white image. A factor of 1.0 gives + the original image. + """ + + def __init__(self, image): + self.image = image + self.intermediate_mode = "L" + if "A" in image.getbands(): + self.intermediate_mode = "LA" + + self.degenerate = image.convert(self.intermediate_mode).convert(image.mode) + + +class Contrast(_Enhance): + """Adjust image contrast. + + This class can be used to control the contrast of an image, similar + to the contrast control on a TV set. An enhancement factor of 0.0 + gives a solid grey image. A factor of 1.0 gives the original image. + """ + + def __init__(self, image): + self.image = image + mean = int(ImageStat.Stat(image.convert("L")).mean[0] + 0.5) + self.degenerate = Image.new("L", image.size, mean).convert(image.mode) + + if "A" in image.getbands(): + self.degenerate.putalpha(image.getchannel("A")) + + +class Brightness(_Enhance): + """Adjust image brightness. + + This class can be used to control the brightness of an image. An + enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the + original image. + """ + + def __init__(self, image): + self.image = image + self.degenerate = Image.new(image.mode, image.size, 0) + + if "A" in image.getbands(): + self.degenerate.putalpha(image.getchannel("A")) + + +class Sharpness(_Enhance): + """Adjust image sharpness. + + This class can be used to adjust the sharpness of an image. An + enhancement factor of 0.0 gives a blurred image, a factor of 1.0 gives the + original image, and a factor of 2.0 gives a sharpened image. + """ + + def __init__(self, image): + self.image = image + self.degenerate = image.filter(ImageFilter.SMOOTH) + + if "A" in image.getbands(): + self.degenerate.putalpha(image.getchannel("A")) diff --git a/venv/lib/python3.11/site-packages/PIL/ImageFile.py b/venv/lib/python3.11/site-packages/PIL/ImageFile.py new file mode 100644 index 00000000..f2a55cb5 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageFile.py @@ -0,0 +1,697 @@ +# +# The Python Imaging Library. +# $Id$ +# +# base class for image file handlers +# +# history: +# 1995-09-09 fl Created +# 1996-03-11 fl Fixed load mechanism. +# 1996-04-15 fl Added pcx/xbm decoders. +# 1996-04-30 fl Added encoders. +# 1996-12-14 fl Added load helpers +# 1997-01-11 fl Use encode_to_file where possible +# 1997-08-27 fl Flush output in _save +# 1998-03-05 fl Use memory mapping for some modes +# 1999-02-04 fl Use memory mapping also for "I;16" and "I;16B" +# 1999-05-31 fl Added image parser +# 2000-10-12 fl Set readonly flag on memory-mapped images +# 2002-03-20 fl Use better messages for common decoder errors +# 2003-04-21 fl Fall back on mmap/map_buffer if map is not available +# 2003-10-30 fl Added StubImageFile class +# 2004-02-25 fl Made incremental parser more robust +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1995-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import io +import struct +import sys +import warnings + +from . import Image +from ._util import isPath + +MAXBLOCK = 65536 + +SAFEBLOCK = 1024 * 1024 + +LOAD_TRUNCATED_IMAGES = False +"""Whether or not to load truncated image files. User code may change this.""" + +ERRORS = { + -1: "image buffer overrun error", + -2: "decoding error", + -3: "unknown error", + -8: "bad configuration", + -9: "out of memory error", +} +"""Dict of known error codes returned from :meth:`.PyDecoder.decode`.""" + + +# +# -------------------------------------------------------------------- +# Helpers + + +def raise_oserror(error): + try: + message = Image.core.getcodecstatus(error) + except AttributeError: + message = ERRORS.get(error) + if not message: + message = f"decoder error {error}" + raise OSError(message + " when reading image file") + + +def raise_ioerror(error): + warnings.warn( + "raise_ioerror is deprecated and will be removed in Pillow 9 (2022-01-02). " + "Use raise_oserror instead.", + DeprecationWarning, + ) + return raise_oserror(error) + + +def _tilesort(t): + # sort on offset + return t[2] + + +# +# -------------------------------------------------------------------- +# ImageFile base class + + +class ImageFile(Image.Image): + """Base class for image file format handlers.""" + + def __init__(self, fp=None, filename=None): + super().__init__() + + self._min_frame = 0 + + self.custom_mimetype = None + + self.tile = None + """ A list of tile descriptors, or ``None`` """ + + self.readonly = 1 # until we know better + + self.decoderconfig = () + self.decodermaxblock = MAXBLOCK + + if isPath(fp): + # filename + self.fp = open(fp, "rb") + self.filename = fp + self._exclusive_fp = True + else: + # stream + self.fp = fp + self.filename = filename + # can be overridden + self._exclusive_fp = None + + try: + try: + self._open() + except ( + IndexError, # end of data + TypeError, # end of data (ord) + KeyError, # unsupported mode + EOFError, # got header but not the first frame + struct.error, + ) as v: + raise SyntaxError(v) from v + + if not self.mode or self.size[0] <= 0: + raise SyntaxError("not identified by this driver") + except BaseException: + # close the file only if we have opened it this constructor + if self._exclusive_fp: + self.fp.close() + raise + + def get_format_mimetype(self): + if self.custom_mimetype: + return self.custom_mimetype + if self.format is not None: + return Image.MIME.get(self.format.upper()) + + def verify(self): + """Check file integrity""" + + # raise exception if something's wrong. must be called + # directly after open, and closes file when finished. + if self._exclusive_fp: + self.fp.close() + self.fp = None + + def load(self): + """Load image data based on tile list""" + + if self.tile is None: + raise OSError("cannot load this image") + + pixel = Image.Image.load(self) + if not self.tile: + return pixel + + self.map = None + use_mmap = self.filename and len(self.tile) == 1 + # As of pypy 2.1.0, memory mapping was failing here. + use_mmap = use_mmap and not hasattr(sys, "pypy_version_info") + + readonly = 0 + + # look for read/seek overrides + try: + read = self.load_read + # don't use mmap if there are custom read/seek functions + use_mmap = False + except AttributeError: + read = self.fp.read + + try: + seek = self.load_seek + use_mmap = False + except AttributeError: + seek = self.fp.seek + + if use_mmap: + # try memory mapping + decoder_name, extents, offset, args = self.tile[0] + if ( + decoder_name == "raw" + and len(args) >= 3 + and args[0] == self.mode + and args[0] in Image._MAPMODES + ): + try: + if hasattr(Image.core, "map"): + # use built-in mapper WIN32 only + self.map = Image.core.map(self.filename) + self.map.seek(offset) + self.im = self.map.readimage( + self.mode, self.size, args[1], args[2] + ) + else: + # use mmap, if possible + import mmap + + with open(self.filename) as fp: + self.map = mmap.mmap( + fp.fileno(), 0, access=mmap.ACCESS_READ + ) + self.im = Image.core.map_buffer( + self.map, self.size, decoder_name, offset, args + ) + readonly = 1 + # After trashing self.im, + # we might need to reload the palette data. + if self.palette: + self.palette.dirty = 1 + except (AttributeError, OSError, ImportError): + self.map = None + + self.load_prepare() + err_code = -3 # initialize to unknown error + if not self.map: + # sort tiles in file order + self.tile.sort(key=_tilesort) + + try: + # FIXME: This is a hack to handle TIFF's JpegTables tag. + prefix = self.tile_prefix + except AttributeError: + prefix = b"" + + for decoder_name, extents, offset, args in self.tile: + decoder = Image._getdecoder( + self.mode, decoder_name, args, self.decoderconfig + ) + try: + seek(offset) + decoder.setimage(self.im, extents) + if decoder.pulls_fd: + decoder.setfd(self.fp) + status, err_code = decoder.decode(b"") + else: + b = prefix + while True: + try: + s = read(self.decodermaxblock) + except (IndexError, struct.error) as e: + # truncated png/gif + if LOAD_TRUNCATED_IMAGES: + break + else: + raise OSError("image file is truncated") from e + + if not s: # truncated jpeg + if LOAD_TRUNCATED_IMAGES: + break + else: + raise OSError( + "image file is truncated " + f"({len(b)} bytes not processed)" + ) + + b = b + s + n, err_code = decoder.decode(b) + if n < 0: + break + b = b[n:] + finally: + # Need to cleanup here to prevent leaks + decoder.cleanup() + + self.tile = [] + self.readonly = readonly + + self.load_end() + + if self._exclusive_fp and self._close_exclusive_fp_after_loading: + self.fp.close() + self.fp = None + + if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0: + # still raised if decoder fails to return anything + raise_oserror(err_code) + + return Image.Image.load(self) + + def load_prepare(self): + # create image memory if necessary + if not self.im or self.im.mode != self.mode or self.im.size != self.size: + self.im = Image.core.new(self.mode, self.size) + # create palette (optional) + if self.mode == "P": + Image.Image.load(self) + + def load_end(self): + # may be overridden + pass + + # may be defined for contained formats + # def load_seek(self, pos): + # pass + + # may be defined for blocked formats (e.g. PNG) + # def load_read(self, bytes): + # pass + + def _seek_check(self, frame): + if ( + frame < self._min_frame + # Only check upper limit on frames if additional seek operations + # are not required to do so + or ( + not (hasattr(self, "_n_frames") and self._n_frames is None) + and frame >= self.n_frames + self._min_frame + ) + ): + raise EOFError("attempt to seek outside sequence") + + return self.tell() != frame + + +class StubImageFile(ImageFile): + """ + Base class for stub image loaders. + + A stub loader is an image loader that can identify files of a + certain format, but relies on external code to load the file. + """ + + def _open(self): + raise NotImplementedError("StubImageFile subclass must implement _open") + + def load(self): + loader = self._load() + if loader is None: + raise OSError(f"cannot find loader for this {self.format} file") + image = loader.load(self) + assert image is not None + # become the other object (!) + self.__class__ = image.__class__ + self.__dict__ = image.__dict__ + + def _load(self): + """(Hook) Find actual image loader.""" + raise NotImplementedError("StubImageFile subclass must implement _load") + + +class Parser: + """ + Incremental image parser. This class implements the standard + feed/close consumer interface. + """ + + incremental = None + image = None + data = None + decoder = None + offset = 0 + finished = 0 + + def reset(self): + """ + (Consumer) Reset the parser. Note that you can only call this + method immediately after you've created a parser; parser + instances cannot be reused. + """ + assert self.data is None, "cannot reuse parsers" + + def feed(self, data): + """ + (Consumer) Feed data to the parser. + + :param data: A string buffer. + :exception OSError: If the parser failed to parse the image file. + """ + # collect data + + if self.finished: + return + + if self.data is None: + self.data = data + else: + self.data = self.data + data + + # parse what we have + if self.decoder: + + if self.offset > 0: + # skip header + skip = min(len(self.data), self.offset) + self.data = self.data[skip:] + self.offset = self.offset - skip + if self.offset > 0 or not self.data: + return + + n, e = self.decoder.decode(self.data) + + if n < 0: + # end of stream + self.data = None + self.finished = 1 + if e < 0: + # decoding error + self.image = None + raise_oserror(e) + else: + # end of image + return + self.data = self.data[n:] + + elif self.image: + + # if we end up here with no decoder, this file cannot + # be incrementally parsed. wait until we've gotten all + # available data + pass + + else: + + # attempt to open this file + try: + with io.BytesIO(self.data) as fp: + im = Image.open(fp) + except OSError: + # traceback.print_exc() + pass # not enough data + else: + flag = hasattr(im, "load_seek") or hasattr(im, "load_read") + if flag or len(im.tile) != 1: + # custom load code, or multiple tiles + self.decode = None + else: + # initialize decoder + im.load_prepare() + d, e, o, a = im.tile[0] + im.tile = [] + self.decoder = Image._getdecoder(im.mode, d, a, im.decoderconfig) + self.decoder.setimage(im.im, e) + + # calculate decoder offset + self.offset = o + if self.offset <= len(self.data): + self.data = self.data[self.offset :] + self.offset = 0 + + self.image = im + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def close(self): + """ + (Consumer) Close the stream. + + :returns: An image object. + :exception OSError: If the parser failed to parse the image file either + because it cannot be identified or cannot be + decoded. + """ + # finish decoding + if self.decoder: + # get rid of what's left in the buffers + self.feed(b"") + self.data = self.decoder = None + if not self.finished: + raise OSError("image was incomplete") + if not self.image: + raise OSError("cannot parse this image") + if self.data: + # incremental parsing not possible; reopen the file + # not that we have all data + with io.BytesIO(self.data) as fp: + try: + self.image = Image.open(fp) + finally: + self.image.load() + return self.image + + +# -------------------------------------------------------------------- + + +def _save(im, fp, tile, bufsize=0): + """Helper to save image based on tile list + + :param im: Image object. + :param fp: File object. + :param tile: Tile list. + :param bufsize: Optional buffer size + """ + + im.load() + if not hasattr(im, "encoderconfig"): + im.encoderconfig = () + tile.sort(key=_tilesort) + # FIXME: make MAXBLOCK a configuration parameter + # It would be great if we could have the encoder specify what it needs + # But, it would need at least the image size in most cases. RawEncode is + # a tricky case. + bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c + if fp == sys.stdout: + fp.flush() + return + try: + fh = fp.fileno() + fp.flush() + except (AttributeError, io.UnsupportedOperation) as exc: + # compress to Python file-compatible object + for e, b, o, a in tile: + e = Image._getencoder(im.mode, e, a, im.encoderconfig) + if o > 0: + fp.seek(o) + e.setimage(im.im, b) + if e.pushes_fd: + e.setfd(fp) + l, s = e.encode_to_pyfd() + else: + while True: + l, s, d = e.encode(bufsize) + fp.write(d) + if s: + break + if s < 0: + raise OSError(f"encoder error {s} when writing image file") from exc + e.cleanup() + else: + # slight speedup: compress to real file object + for e, b, o, a in tile: + e = Image._getencoder(im.mode, e, a, im.encoderconfig) + if o > 0: + fp.seek(o) + e.setimage(im.im, b) + if e.pushes_fd: + e.setfd(fp) + l, s = e.encode_to_pyfd() + else: + s = e.encode_to_file(fh, bufsize) + if s < 0: + raise OSError(f"encoder error {s} when writing image file") + e.cleanup() + if hasattr(fp, "flush"): + fp.flush() + + +def _safe_read(fp, size): + """ + Reads large blocks in a safe way. Unlike fp.read(n), this function + doesn't trust the user. If the requested size is larger than + SAFEBLOCK, the file is read block by block. + + :param fp: File handle. Must implement a read method. + :param size: Number of bytes to read. + :returns: A string containing up to size bytes of data. + """ + if size <= 0: + return b"" + if size <= SAFEBLOCK: + return fp.read(size) + data = [] + while size > 0: + block = fp.read(min(size, SAFEBLOCK)) + if not block: + break + data.append(block) + size -= len(block) + return b"".join(data) + + +class PyCodecState: + def __init__(self): + self.xsize = 0 + self.ysize = 0 + self.xoff = 0 + self.yoff = 0 + + def extents(self): + return (self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize) + + +class PyDecoder: + """ + Python implementation of a format decoder. Override this class and + add the decoding logic in the :meth:`decode` method. + + See :ref:`Writing Your Own File Decoder in Python` + """ + + _pulls_fd = False + + def __init__(self, mode, *args): + self.im = None + self.state = PyCodecState() + self.fd = None + self.mode = mode + self.init(args) + + def init(self, args): + """ + Override to perform decoder specific initialization + + :param args: Array of args items from the tile entry + :returns: None + """ + self.args = args + + @property + def pulls_fd(self): + return self._pulls_fd + + def decode(self, buffer): + """ + Override to perform the decoding process. + + :param buffer: A bytes object with the data to be decoded. + :returns: A tuple of ``(bytes consumed, errcode)``. + If finished with decoding return <0 for the bytes consumed. + Err codes are from :data:`.ImageFile.ERRORS`. + """ + raise NotImplementedError() + + def cleanup(self): + """ + Override to perform decoder specific cleanup + + :returns: None + """ + pass + + def setfd(self, fd): + """ + Called from ImageFile to set the python file-like object + + :param fd: A python file-like object + :returns: None + """ + self.fd = fd + + def setimage(self, im, extents=None): + """ + Called from ImageFile to set the core output image for the decoder + + :param im: A core image object + :param extents: a 4 tuple of (x0, y0, x1, y1) defining the rectangle + for this tile + :returns: None + """ + + # following c code + self.im = im + + if extents: + (x0, y0, x1, y1) = extents + else: + (x0, y0, x1, y1) = (0, 0, 0, 0) + + if x0 == 0 and x1 == 0: + self.state.xsize, self.state.ysize = self.im.size + else: + self.state.xoff = x0 + self.state.yoff = y0 + self.state.xsize = x1 - x0 + self.state.ysize = y1 - y0 + + if self.state.xsize <= 0 or self.state.ysize <= 0: + raise ValueError("Size cannot be negative") + + if ( + self.state.xsize + self.state.xoff > self.im.size[0] + or self.state.ysize + self.state.yoff > self.im.size[1] + ): + raise ValueError("Tile cannot extend outside image") + + def set_as_raw(self, data, rawmode=None): + """ + Convenience method to set the internal image from a stream of raw data + + :param data: Bytes to be set + :param rawmode: The rawmode to be used for the decoder. + If not specified, it will default to the mode of the image + :returns: None + """ + + if not rawmode: + rawmode = self.mode + d = Image._getdecoder(self.mode, "raw", (rawmode)) + d.setimage(self.im, self.state.extents()) + s = d.decode(data) + + if s[0] >= 0: + raise ValueError("not enough image data") + if s[1] != 0: + raise ValueError("cannot decode image data") diff --git a/venv/lib/python3.11/site-packages/PIL/ImageFilter.py b/venv/lib/python3.11/site-packages/PIL/ImageFilter.py new file mode 100644 index 00000000..9ca17d9a --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageFilter.py @@ -0,0 +1,534 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard filters +# +# History: +# 1995-11-27 fl Created +# 2002-06-08 fl Added rank and mode filters +# 2003-09-15 fl Fixed rank calculation in rank filter; added expand call +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2002 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# +import functools + +try: + import numpy +except ImportError: # pragma: no cover + numpy = None + + +class Filter: + pass + + +class MultibandFilter(Filter): + pass + + +class BuiltinFilter(MultibandFilter): + def filter(self, image): + if image.mode == "P": + raise ValueError("cannot filter palette images") + return image.filter(*self.filterargs) + + +class Kernel(BuiltinFilter): + """ + Create a convolution kernel. The current version only + supports 3x3 and 5x5 integer and floating point kernels. + + In the current version, kernels can only be applied to + "L" and "RGB" images. + + :param size: Kernel size, given as (width, height). In the current + version, this must be (3,3) or (5,5). + :param kernel: A sequence containing kernel weights. + :param scale: Scale factor. If given, the result for each pixel is + divided by this value. The default is the sum of the + kernel weights. + :param offset: Offset. If given, this value is added to the result, + after it has been divided by the scale factor. + """ + + name = "Kernel" + + def __init__(self, size, kernel, scale=None, offset=0): + if scale is None: + # default scale is sum of kernel + scale = functools.reduce(lambda a, b: a + b, kernel) + if size[0] * size[1] != len(kernel): + raise ValueError("not enough coefficients in kernel") + self.filterargs = size, scale, offset, kernel + + +class RankFilter(Filter): + """ + Create a rank filter. The rank filter sorts all pixels in + a window of the given size, and returns the ``rank``'th value. + + :param size: The kernel size, in pixels. + :param rank: What pixel value to pick. Use 0 for a min filter, + ``size * size / 2`` for a median filter, ``size * size - 1`` + for a max filter, etc. + """ + + name = "Rank" + + def __init__(self, size, rank): + self.size = size + self.rank = rank + + def filter(self, image): + if image.mode == "P": + raise ValueError("cannot filter palette images") + image = image.expand(self.size // 2, self.size // 2) + return image.rankfilter(self.size, self.rank) + + +class MedianFilter(RankFilter): + """ + Create a median filter. Picks the median pixel value in a window with the + given size. + + :param size: The kernel size, in pixels. + """ + + name = "Median" + + def __init__(self, size=3): + self.size = size + self.rank = size * size // 2 + + +class MinFilter(RankFilter): + """ + Create a min filter. Picks the lowest pixel value in a window with the + given size. + + :param size: The kernel size, in pixels. + """ + + name = "Min" + + def __init__(self, size=3): + self.size = size + self.rank = 0 + + +class MaxFilter(RankFilter): + """ + Create a max filter. Picks the largest pixel value in a window with the + given size. + + :param size: The kernel size, in pixels. + """ + + name = "Max" + + def __init__(self, size=3): + self.size = size + self.rank = size * size - 1 + + +class ModeFilter(Filter): + """ + Create a mode filter. Picks the most frequent pixel value in a box with the + given size. Pixel values that occur only once or twice are ignored; if no + pixel value occurs more than twice, the original pixel value is preserved. + + :param size: The kernel size, in pixels. + """ + + name = "Mode" + + def __init__(self, size=3): + self.size = size + + def filter(self, image): + return image.modefilter(self.size) + + +class GaussianBlur(MultibandFilter): + """Gaussian blur filter. + + :param radius: Blur radius. + """ + + name = "GaussianBlur" + + def __init__(self, radius=2): + self.radius = radius + + def filter(self, image): + return image.gaussian_blur(self.radius) + + +class BoxBlur(MultibandFilter): + """Blurs the image by setting each pixel to the average value of the pixels + in a square box extending radius pixels in each direction. + Supports float radius of arbitrary size. Uses an optimized implementation + which runs in linear time relative to the size of the image + for any radius value. + + :param radius: Size of the box in one direction. Radius 0 does not blur, + returns an identical image. Radius 1 takes 1 pixel + in each direction, i.e. 9 pixels in total. + """ + + name = "BoxBlur" + + def __init__(self, radius): + self.radius = radius + + def filter(self, image): + return image.box_blur(self.radius) + + +class UnsharpMask(MultibandFilter): + """Unsharp mask filter. + + See Wikipedia's entry on `digital unsharp masking`_ for an explanation of + the parameters. + + :param radius: Blur Radius + :param percent: Unsharp strength, in percent + :param threshold: Threshold controls the minimum brightness change that + will be sharpened + + .. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking + + """ # noqa: E501 + + name = "UnsharpMask" + + def __init__(self, radius=2, percent=150, threshold=3): + self.radius = radius + self.percent = percent + self.threshold = threshold + + def filter(self, image): + return image.unsharp_mask(self.radius, self.percent, self.threshold) + + +class BLUR(BuiltinFilter): + name = "Blur" + # fmt: off + filterargs = (5, 5), 16, 0, ( + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1, + ) + # fmt: on + + +class CONTOUR(BuiltinFilter): + name = "Contour" + # fmt: off + filterargs = (3, 3), 1, 255, ( + -1, -1, -1, + -1, 8, -1, + -1, -1, -1, + ) + # fmt: on + + +class DETAIL(BuiltinFilter): + name = "Detail" + # fmt: off + filterargs = (3, 3), 6, 0, ( + 0, -1, 0, + -1, 10, -1, + 0, -1, 0, + ) + # fmt: on + + +class EDGE_ENHANCE(BuiltinFilter): + name = "Edge-enhance" + # fmt: off + filterargs = (3, 3), 2, 0, ( + -1, -1, -1, + -1, 10, -1, + -1, -1, -1, + ) + # fmt: on + + +class EDGE_ENHANCE_MORE(BuiltinFilter): + name = "Edge-enhance More" + # fmt: off + filterargs = (3, 3), 1, 0, ( + -1, -1, -1, + -1, 9, -1, + -1, -1, -1, + ) + # fmt: on + + +class EMBOSS(BuiltinFilter): + name = "Emboss" + # fmt: off + filterargs = (3, 3), 1, 128, ( + -1, 0, 0, + 0, 1, 0, + 0, 0, 0, + ) + # fmt: on + + +class FIND_EDGES(BuiltinFilter): + name = "Find Edges" + # fmt: off + filterargs = (3, 3), 1, 0, ( + -1, -1, -1, + -1, 8, -1, + -1, -1, -1, + ) + # fmt: on + + +class SHARPEN(BuiltinFilter): + name = "Sharpen" + # fmt: off + filterargs = (3, 3), 16, 0, ( + -2, -2, -2, + -2, 32, -2, + -2, -2, -2, + ) + # fmt: on + + +class SMOOTH(BuiltinFilter): + name = "Smooth" + # fmt: off + filterargs = (3, 3), 13, 0, ( + 1, 1, 1, + 1, 5, 1, + 1, 1, 1, + ) + # fmt: on + + +class SMOOTH_MORE(BuiltinFilter): + name = "Smooth More" + # fmt: off + filterargs = (5, 5), 100, 0, ( + 1, 1, 1, 1, 1, + 1, 5, 5, 5, 1, + 1, 5, 44, 5, 1, + 1, 5, 5, 5, 1, + 1, 1, 1, 1, 1, + ) + # fmt: on + + +class Color3DLUT(MultibandFilter): + """Three-dimensional color lookup table. + + Transforms 3-channel pixels using the values of the channels as coordinates + in the 3D lookup table and interpolating the nearest elements. + + This method allows you to apply almost any color transformation + in constant time by using pre-calculated decimated tables. + + .. versionadded:: 5.2.0 + + :param size: Size of the table. One int or tuple of (int, int, int). + Minimal size in any dimension is 2, maximum is 65. + :param table: Flat lookup table. A list of ``channels * size**3`` + float elements or a list of ``size**3`` channels-sized + tuples with floats. Channels are changed first, + then first dimension, then second, then third. + Value 0.0 corresponds lowest value of output, 1.0 highest. + :param channels: Number of channels in the table. Could be 3 or 4. + Default is 3. + :param target_mode: A mode for the result image. Should have not less + than ``channels`` channels. Default is ``None``, + which means that mode wouldn't be changed. + """ + + name = "Color 3D LUT" + + def __init__(self, size, table, channels=3, target_mode=None, **kwargs): + if channels not in (3, 4): + raise ValueError("Only 3 or 4 output channels are supported") + self.size = size = self._check_size(size) + self.channels = channels + self.mode = target_mode + + # Hidden flag `_copy_table=False` could be used to avoid extra copying + # of the table if the table is specially made for the constructor. + copy_table = kwargs.get("_copy_table", True) + items = size[0] * size[1] * size[2] + wrong_size = False + + if numpy and isinstance(table, numpy.ndarray): + if copy_table: + table = table.copy() + + if table.shape in [ + (items * channels,), + (items, channels), + (size[2], size[1], size[0], channels), + ]: + table = table.reshape(items * channels) + else: + wrong_size = True + + else: + if copy_table: + table = list(table) + + # Convert to a flat list + if table and isinstance(table[0], (list, tuple)): + table, raw_table = [], table + for pixel in raw_table: + if len(pixel) != channels: + raise ValueError( + "The elements of the table should " + "have a length of {}.".format(channels) + ) + table.extend(pixel) + + if wrong_size or len(table) != items * channels: + raise ValueError( + "The table should have either channels * size**3 float items " + "or size**3 items of channels-sized tuples with floats. " + f"Table should be: {channels}x{size[0]}x{size[1]}x{size[2]}. " + f"Actual length: {len(table)}" + ) + self.table = table + + @staticmethod + def _check_size(size): + try: + _, _, _ = size + except ValueError as e: + raise ValueError( + "Size should be either an integer or a tuple of three integers." + ) from e + except TypeError: + size = (size, size, size) + size = [int(x) for x in size] + for size1D in size: + if not 2 <= size1D <= 65: + raise ValueError("Size should be in [2, 65] range.") + return size + + @classmethod + def generate(cls, size, callback, channels=3, target_mode=None): + """Generates new LUT using provided callback. + + :param size: Size of the table. Passed to the constructor. + :param callback: Function with three parameters which correspond + three color channels. Will be called ``size**3`` + times with values from 0.0 to 1.0 and should return + a tuple with ``channels`` elements. + :param channels: The number of channels which should return callback. + :param target_mode: Passed to the constructor of the resulting + lookup table. + """ + size1D, size2D, size3D = cls._check_size(size) + if channels not in (3, 4): + raise ValueError("Only 3 or 4 output channels are supported") + + table = [0] * (size1D * size2D * size3D * channels) + idx_out = 0 + for b in range(size3D): + for g in range(size2D): + for r in range(size1D): + table[idx_out : idx_out + channels] = callback( + r / (size1D - 1), g / (size2D - 1), b / (size3D - 1) + ) + idx_out += channels + + return cls( + (size1D, size2D, size3D), + table, + channels=channels, + target_mode=target_mode, + _copy_table=False, + ) + + def transform(self, callback, with_normals=False, channels=None, target_mode=None): + """Transforms the table values using provided callback and returns + a new LUT with altered values. + + :param callback: A function which takes old lookup table values + and returns a new set of values. The number + of arguments which function should take is + ``self.channels`` or ``3 + self.channels`` + if ``with_normals`` flag is set. + Should return a tuple of ``self.channels`` or + ``channels`` elements if it is set. + :param with_normals: If true, ``callback`` will be called with + coordinates in the color cube as the first + three arguments. Otherwise, ``callback`` + will be called only with actual color values. + :param channels: The number of channels in the resulting lookup table. + :param target_mode: Passed to the constructor of the resulting + lookup table. + """ + if channels not in (None, 3, 4): + raise ValueError("Only 3 or 4 output channels are supported") + ch_in = self.channels + ch_out = channels or ch_in + size1D, size2D, size3D = self.size + + table = [0] * (size1D * size2D * size3D * ch_out) + idx_in = 0 + idx_out = 0 + for b in range(size3D): + for g in range(size2D): + for r in range(size1D): + values = self.table[idx_in : idx_in + ch_in] + if with_normals: + values = callback( + r / (size1D - 1), + g / (size2D - 1), + b / (size3D - 1), + *values, + ) + else: + values = callback(*values) + table[idx_out : idx_out + ch_out] = values + idx_in += ch_in + idx_out += ch_out + + return type(self)( + self.size, + table, + channels=ch_out, + target_mode=target_mode or self.mode, + _copy_table=False, + ) + + def __repr__(self): + r = [ + f"{self.__class__.__name__} from {self.table.__class__.__name__}", + "size={:d}x{:d}x{:d}".format(*self.size), + f"channels={self.channels:d}", + ] + if self.mode: + r.append(f"target_mode={self.mode}") + return "<{}>".format(" ".join(r)) + + def filter(self, image): + from . import Image + + return image.color_lut_3d( + self.mode or image.mode, + Image.LINEAR, + self.channels, + self.size[0], + self.size[1], + self.size[2], + self.table, + ) diff --git a/venv/lib/python3.11/site-packages/PIL/ImageFont.py b/venv/lib/python3.11/site-packages/PIL/ImageFont.py new file mode 100644 index 00000000..c48d8983 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageFont.py @@ -0,0 +1,1057 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PIL raster font management +# +# History: +# 1996-08-07 fl created (experimental) +# 1997-08-25 fl minor adjustments to handle fonts from pilfont 0.3 +# 1999-02-06 fl rewrote most font management stuff in C +# 1999-03-17 fl take pth files into account in load_path (from Richard Jones) +# 2001-02-17 fl added freetype support +# 2001-05-09 fl added TransposedFont wrapper class +# 2002-03-04 fl make sure we have a "L" or "1" font +# 2002-12-04 fl skip non-directory entries in the system path +# 2003-04-29 fl add embedded default font +# 2003-09-27 fl added support for truetype charmap encodings +# +# Todo: +# Adapt to PILFONT2 format (16-bit fonts, compressed, single file) +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import base64 +import os +import sys +import warnings +from io import BytesIO + +from . import Image, features +from ._util import isDirectory, isPath + +LAYOUT_BASIC = 0 +LAYOUT_RAQM = 1 + + +class _imagingft_not_installed: + # module placeholder + def __getattr__(self, id): + raise ImportError("The _imagingft C module is not installed") + + +try: + from . import _imagingft as core +except ImportError: + core = _imagingft_not_installed() + + +# FIXME: add support for pilfont2 format (see FontFile.py) + +# -------------------------------------------------------------------- +# Font metrics format: +# "PILfont" LF +# fontdescriptor LF +# (optional) key=value... LF +# "DATA" LF +# binary data: 256*10*2 bytes (dx, dy, dstbox, srcbox) +# +# To place a character, cut out srcbox and paste at dstbox, +# relative to the character position. Then move the character +# position according to dx, dy. +# -------------------------------------------------------------------- + + +class ImageFont: + "PIL font wrapper" + + def _load_pilfont(self, filename): + + with open(filename, "rb") as fp: + image = None + for ext in (".png", ".gif", ".pbm"): + if image: + image.close() + try: + fullname = os.path.splitext(filename)[0] + ext + image = Image.open(fullname) + except Exception: + pass + else: + if image and image.mode in ("1", "L"): + break + else: + if image: + image.close() + raise OSError("cannot find glyph data file") + + self.file = fullname + + self._load_pilfont_data(fp, image) + image.close() + + def _load_pilfont_data(self, file, image): + + # read PILfont header + if file.readline() != b"PILfont\n": + raise SyntaxError("Not a PILfont file") + file.readline().split(b";") + self.info = [] # FIXME: should be a dictionary + while True: + s = file.readline() + if not s or s == b"DATA\n": + break + self.info.append(s) + + # read PILfont metrics + data = file.read(256 * 20) + + # check image + if image.mode not in ("1", "L"): + raise TypeError("invalid font image mode") + + image.load() + + self.font = Image.core.font(image.im, data) + + def getsize(self, text, *args, **kwargs): + """ + Returns width and height (in pixels) of given text. + + :param text: Text to measure. + + :return: (width, height) + """ + return self.font.getsize(text) + + def getmask(self, text, mode="", *args, **kwargs): + """ + Create a bitmap for the text. + + If the font uses antialiasing, the bitmap should have mode ``L`` and use a + maximum value of 255. Otherwise, it should have mode ``1``. + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + .. versionadded:: 1.1.5 + + :return: An internal PIL storage memory instance as defined by the + :py:mod:`PIL.Image.core` interface module. + """ + return self.font.getmask(text, mode) + + +## +# Wrapper for FreeType fonts. Application code should use the +# truetype factory function to create font objects. + + +class FreeTypeFont: + "FreeType font wrapper (requires _imagingft service)" + + def __init__(self, font=None, size=10, index=0, encoding="", layout_engine=None): + # FIXME: use service provider instead + + self.path = font + self.size = size + self.index = index + self.encoding = encoding + + try: + from packaging.version import parse as parse_version + except ImportError: + pass + else: + freetype_version = parse_version(features.version_module("freetype2")) + if freetype_version < parse_version("2.8"): + warnings.warn( + "Support for FreeType 2.7 is deprecated and will be removed" + " in Pillow 9 (2022-01-02). Please upgrade to FreeType 2.8 " + "or newer, preferably FreeType 2.10.4 which fixes " + "CVE-2020-15999.", + DeprecationWarning, + ) + + if layout_engine not in (LAYOUT_BASIC, LAYOUT_RAQM): + layout_engine = LAYOUT_BASIC + if core.HAVE_RAQM: + layout_engine = LAYOUT_RAQM + elif layout_engine == LAYOUT_RAQM and not core.HAVE_RAQM: + layout_engine = LAYOUT_BASIC + + self.layout_engine = layout_engine + + def load_from_bytes(f): + self.font_bytes = f.read() + self.font = core.getfont( + "", size, index, encoding, self.font_bytes, layout_engine + ) + + if isPath(font): + if sys.platform == "win32": + font_bytes_path = font if isinstance(font, bytes) else font.encode() + try: + font_bytes_path.decode("ascii") + except UnicodeDecodeError: + # FreeType cannot load fonts with non-ASCII characters on Windows + # So load it into memory first + with open(font, "rb") as f: + load_from_bytes(f) + return + self.font = core.getfont( + font, size, index, encoding, layout_engine=layout_engine + ) + else: + load_from_bytes(font) + + def _multiline_split(self, text): + split_character = "\n" if isinstance(text, str) else b"\n" + return text.split(split_character) + + def getname(self): + """ + :return: A tuple of the font family (e.g. Helvetica) and the font style + (e.g. Bold) + """ + return self.font.family, self.font.style + + def getmetrics(self): + """ + :return: A tuple of the font ascent (the distance from the baseline to + the highest outline point) and descent (the distance from the + baseline to the lowest outline point, a negative value) + """ + return self.font.ascent, self.font.descent + + def getlength(self, text, mode="", direction=None, features=None, language=None): + """ + Returns length (in pixels with 1/64 precision) of given text when rendered + in font with provided direction, features, and language. + + This is the amount by which following text should be offset. + Text bounding box may extend past the length in some fonts, + e.g. when using italics or accents. + + The result is returned as a float; it is a whole number if using basic layout. + + Note that the sum of two lengths may not equal the length of a concatenated + string due to kerning. If you need to adjust for kerning, include the following + character and subtract its length. + + For example, instead of + + .. code-block:: python + + hello = font.getlength("Hello") + world = font.getlength("World") + hello_world = hello + world # not adjusted for kerning + assert hello_world == font.getlength("HelloWorld") # may fail + + use + + .. code-block:: python + + hello = font.getlength("HelloW") - font.getlength("W") # adjusted for kerning + world = font.getlength("World") + hello_world = hello + world # adjusted for kerning + assert hello_world == font.getlength("HelloWorld") # True + + or disable kerning with (requires libraqm) + + .. code-block:: python + + hello = draw.textlength("Hello", font, features=["-kern"]) + world = draw.textlength("World", font, features=["-kern"]) + hello_world = hello + world # kerning is disabled, no need to adjust + assert hello_world == draw.textlength("HelloWorld", font, features=["-kern"]) + + .. versionadded:: 8.0.0 + + :param text: Text to measure. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + :return: Width for horizontal, height for vertical text. + """ + return self.font.getlength(text, mode, direction, features, language) / 64 + + def getbbox( + self, + text, + mode="", + direction=None, + features=None, + language=None, + stroke_width=0, + anchor=None, + ): + """ + Returns bounding box (in pixels) of given text relative to given anchor + when rendered in font with provided direction, features, and language. + + Use :py:meth:`getlength()` to get the offset of following text with + 1/64 pixel precision. The bounding box includes extra margins for + some fonts, e.g. italics or accents. + + .. versionadded:: 8.0.0 + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + :param stroke_width: The width of the text stroke. + + :param anchor: The text anchor alignment. Determines the relative location of + the anchor to the text. The default alignment is top left. + See :ref:`text-anchors` for valid values. + + :return: ``(left, top, right, bottom)`` bounding box + """ + size, offset = self.font.getsize( + text, mode, direction, features, language, anchor + ) + left, top = offset[0] - stroke_width, offset[1] - stroke_width + width, height = size[0] + 2 * stroke_width, size[1] + 2 * stroke_width + return left, top, left + width, top + height + + def getsize( + self, text, direction=None, features=None, language=None, stroke_width=0 + ): + """ + Returns width and height (in pixels) of given text if rendered in font with + provided direction, features, and language. + + Use :py:meth:`getlength()` to measure the offset of following text with + 1/64 pixel precision. + Use :py:meth:`getbbox()` to get the exact bounding box based on an anchor. + + .. note:: For historical reasons this function measures text height from + the ascender line instead of the top, see :ref:`text-anchors`. + If you wish to measure text height from the top, it is recommended + to use the bottom value of :meth:`getbbox` with ``anchor='lt'`` instead. + + :param text: Text to measure. + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + .. versionadded:: 6.0.0 + + :param stroke_width: The width of the text stroke. + + .. versionadded:: 6.2.0 + + :return: (width, height) + """ + # vertical offset is added for historical reasons + # see https://github.com/python-pillow/Pillow/pull/4910#discussion_r486682929 + size, offset = self.font.getsize(text, "L", direction, features, language) + return ( + size[0] + stroke_width * 2, + size[1] + stroke_width * 2 + offset[1], + ) + + def getsize_multiline( + self, + text, + direction=None, + spacing=4, + features=None, + language=None, + stroke_width=0, + ): + """ + Returns width and height (in pixels) of given text if rendered in font + with provided direction, features, and language, while respecting + newline characters. + + :param text: Text to measure. + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + :param spacing: The vertical gap between lines, defaulting to 4 pixels. + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + .. versionadded:: 6.0.0 + + :param stroke_width: The width of the text stroke. + + .. versionadded:: 6.2.0 + + :return: (width, height) + """ + max_width = 0 + lines = self._multiline_split(text) + line_spacing = self.getsize("A", stroke_width=stroke_width)[1] + spacing + for line in lines: + line_width, line_height = self.getsize( + line, direction, features, language, stroke_width + ) + max_width = max(max_width, line_width) + + return max_width, len(lines) * line_spacing - spacing + + def getoffset(self, text): + """ + Returns the offset of given text. This is the gap between the + starting coordinate and the first marking. Note that this gap is + included in the result of :py:func:`~PIL.ImageFont.FreeTypeFont.getsize`. + + :param text: Text to measure. + + :return: A tuple of the x and y offset + """ + return self.font.getsize(text)[1] + + def getmask( + self, + text, + mode="", + direction=None, + features=None, + language=None, + stroke_width=0, + anchor=None, + ink=0, + ): + """ + Create a bitmap for the text. + + If the font uses antialiasing, the bitmap should have mode ``L`` and use a + maximum value of 255. If the font has embedded color data, the bitmap + should have mode ``RGBA``. Otherwise, it should have mode ``1``. + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + .. versionadded:: 1.1.5 + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + .. versionadded:: 6.0.0 + + :param stroke_width: The width of the text stroke. + + .. versionadded:: 6.2.0 + + :param anchor: The text anchor alignment. Determines the relative location of + the anchor to the text. The default alignment is top left. + See :ref:`text-anchors` for valid values. + + .. versionadded:: 8.0.0 + + :param ink: Foreground ink for rendering in RGBA mode. + + .. versionadded:: 8.0.0 + + :return: An internal PIL storage memory instance as defined by the + :py:mod:`PIL.Image.core` interface module. + """ + return self.getmask2( + text, + mode, + direction=direction, + features=features, + language=language, + stroke_width=stroke_width, + anchor=anchor, + ink=ink, + )[0] + + def getmask2( + self, + text, + mode="", + fill=Image.core.fill, + direction=None, + features=None, + language=None, + stroke_width=0, + anchor=None, + ink=0, + *args, + **kwargs, + ): + """ + Create a bitmap for the text. + + If the font uses antialiasing, the bitmap should have mode ``L`` and use a + maximum value of 255. If the font has embedded color data, the bitmap + should have mode ``RGBA``. Otherwise, it should have mode ``1``. + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + .. versionadded:: 1.1.5 + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + .. versionadded:: 6.0.0 + + :param stroke_width: The width of the text stroke. + + .. versionadded:: 6.2.0 + + :param anchor: The text anchor alignment. Determines the relative location of + the anchor to the text. The default alignment is top left. + See :ref:`text-anchors` for valid values. + + .. versionadded:: 8.0.0 + + :param ink: Foreground ink for rendering in RGBA mode. + + .. versionadded:: 8.0.0 + + :return: A tuple of an internal PIL storage memory instance as defined by the + :py:mod:`PIL.Image.core` interface module, and the text offset, the + gap between the starting coordinate and the first marking + """ + size, offset = self.font.getsize( + text, mode, direction, features, language, anchor + ) + size = size[0] + stroke_width * 2, size[1] + stroke_width * 2 + offset = offset[0] - stroke_width, offset[1] - stroke_width + im = fill("RGBA" if mode == "RGBA" else "L", size, 0) + self.font.render( + text, im.id, mode, direction, features, language, stroke_width, ink + ) + return im, offset + + def font_variant( + self, font=None, size=None, index=None, encoding=None, layout_engine=None + ): + """ + Create a copy of this FreeTypeFont object, + using any specified arguments to override the settings. + + Parameters are identical to the parameters used to initialize this + object. + + :return: A FreeTypeFont object. + """ + return FreeTypeFont( + font=self.path if font is None else font, + size=self.size if size is None else size, + index=self.index if index is None else index, + encoding=self.encoding if encoding is None else encoding, + layout_engine=layout_engine or self.layout_engine, + ) + + def get_variation_names(self): + """ + :returns: A list of the named styles in a variation font. + :exception OSError: If the font is not a variation font. + """ + try: + names = self.font.getvarnames() + except AttributeError as e: + raise NotImplementedError("FreeType 2.9.1 or greater is required") from e + return [name.replace(b"\x00", b"") for name in names] + + def set_variation_by_name(self, name): + """ + :param name: The name of the style. + :exception OSError: If the font is not a variation font. + """ + names = self.get_variation_names() + if not isinstance(name, bytes): + name = name.encode() + index = names.index(name) + + if index == getattr(self, "_last_variation_index", None): + # When the same name is set twice in a row, + # there is an 'unknown freetype error' + # https://savannah.nongnu.org/bugs/?56186 + return + self._last_variation_index = index + + self.font.setvarname(index) + + def get_variation_axes(self): + """ + :returns: A list of the axes in a variation font. + :exception OSError: If the font is not a variation font. + """ + try: + axes = self.font.getvaraxes() + except AttributeError as e: + raise NotImplementedError("FreeType 2.9.1 or greater is required") from e + for axis in axes: + axis["name"] = axis["name"].replace(b"\x00", b"") + return axes + + def set_variation_by_axes(self, axes): + """ + :param axes: A list of values for each axis. + :exception OSError: If the font is not a variation font. + """ + try: + self.font.setvaraxes(axes) + except AttributeError as e: + raise NotImplementedError("FreeType 2.9.1 or greater is required") from e + + +class TransposedFont: + "Wrapper for writing rotated or mirrored text" + + def __init__(self, font, orientation=None): + """ + Wrapper that creates a transposed font from any existing font + object. + + :param font: A font object. + :param orientation: An optional orientation. If given, this should + be one of Image.FLIP_LEFT_RIGHT, Image.FLIP_TOP_BOTTOM, + Image.ROTATE_90, Image.ROTATE_180, or Image.ROTATE_270. + """ + self.font = font + self.orientation = orientation # any 'transpose' argument, or None + + def getsize(self, text, *args, **kwargs): + w, h = self.font.getsize(text) + if self.orientation in (Image.ROTATE_90, Image.ROTATE_270): + return h, w + return w, h + + def getmask(self, text, mode="", *args, **kwargs): + im = self.font.getmask(text, mode, *args, **kwargs) + if self.orientation is not None: + return im.transpose(self.orientation) + return im + + +def load(filename): + """ + Load a font file. This function loads a font object from the given + bitmap font file, and returns the corresponding font object. + + :param filename: Name of font file. + :return: A font object. + :exception OSError: If the file could not be read. + """ + f = ImageFont() + f._load_pilfont(filename) + return f + + +def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): + """ + Load a TrueType or OpenType font from a file or file-like object, + and create a font object. + This function loads a font object from the given file or file-like + object, and creates a font object for a font of the given size. + + Pillow uses FreeType to open font files. If you are opening many fonts + simultaneously on Windows, be aware that Windows limits the number of files + that can be open in C at once to 512. If you approach that limit, an + ``OSError`` may be thrown, reporting that FreeType "cannot open resource". + + This function requires the _imagingft service. + + :param font: A filename or file-like object containing a TrueType font. + If the file is not found in this filename, the loader may also + search in other directories, such as the :file:`fonts/` + directory on Windows or :file:`/Library/Fonts/`, + :file:`/System/Library/Fonts/` and :file:`~/Library/Fonts/` on + macOS. + + :param size: The requested size, in points. + :param index: Which font face to load (default is first available face). + :param encoding: Which font encoding to use (default is Unicode). Possible + encodings include (see the FreeType documentation for more + information): + + * "unic" (Unicode) + * "symb" (Microsoft Symbol) + * "ADOB" (Adobe Standard) + * "ADBE" (Adobe Expert) + * "ADBC" (Adobe Custom) + * "armn" (Apple Roman) + * "sjis" (Shift JIS) + * "gb " (PRC) + * "big5" + * "wans" (Extended Wansung) + * "joha" (Johab) + * "lat1" (Latin-1) + + This specifies the character set to use. It does not alter the + encoding of any text provided in subsequent operations. + :param layout_engine: Which layout engine to use, if available: + :data:`.ImageFont.LAYOUT_BASIC` or :data:`.ImageFont.LAYOUT_RAQM`. + + You can check support for Raqm layout using + :py:func:`PIL.features.check_feature` with ``feature="raqm"``. + + .. versionadded:: 4.2.0 + :return: A font object. + :exception OSError: If the file could not be read. + """ + + def freetype(font): + return FreeTypeFont(font, size, index, encoding, layout_engine) + + try: + return freetype(font) + except OSError: + if not isPath(font): + raise + ttf_filename = os.path.basename(font) + + dirs = [] + if sys.platform == "win32": + # check the windows font repository + # NOTE: must use uppercase WINDIR, to work around bugs in + # 1.5.2's os.environ.get() + windir = os.environ.get("WINDIR") + if windir: + dirs.append(os.path.join(windir, "fonts")) + elif sys.platform in ("linux", "linux2"): + lindirs = os.environ.get("XDG_DATA_DIRS", "") + if not lindirs: + # According to the freedesktop spec, XDG_DATA_DIRS should + # default to /usr/share + lindirs = "/usr/share" + dirs += [os.path.join(lindir, "fonts") for lindir in lindirs.split(":")] + elif sys.platform == "darwin": + dirs += [ + "/Library/Fonts", + "/System/Library/Fonts", + os.path.expanduser("~/Library/Fonts"), + ] + + ext = os.path.splitext(ttf_filename)[1] + first_font_with_a_different_extension = None + for directory in dirs: + for walkroot, walkdir, walkfilenames in os.walk(directory): + for walkfilename in walkfilenames: + if ext and walkfilename == ttf_filename: + return freetype(os.path.join(walkroot, walkfilename)) + elif not ext and os.path.splitext(walkfilename)[0] == ttf_filename: + fontpath = os.path.join(walkroot, walkfilename) + if os.path.splitext(fontpath)[1] == ".ttf": + return freetype(fontpath) + if not ext and first_font_with_a_different_extension is None: + first_font_with_a_different_extension = fontpath + if first_font_with_a_different_extension: + return freetype(first_font_with_a_different_extension) + raise + + +def load_path(filename): + """ + Load font file. Same as :py:func:`~PIL.ImageFont.load`, but searches for a + bitmap font along the Python path. + + :param filename: Name of font file. + :return: A font object. + :exception OSError: If the file could not be read. + """ + for directory in sys.path: + if isDirectory(directory): + if not isinstance(filename, str): + filename = filename.decode("utf-8") + try: + return load(os.path.join(directory, filename)) + except OSError: + pass + raise OSError("cannot find font file") + + +def load_default(): + """Load a "better than nothing" default font. + + .. versionadded:: 1.1.4 + + :return: A font object. + """ + f = ImageFont() + f._load_pilfont_data( + # courB08 + BytesIO( + base64.b64decode( + b""" +UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAA//8AAQAAAAAAAAABAAEA +BgAAAAH/+gADAAAAAQAAAAMABgAGAAAAAf/6AAT//QADAAAABgADAAYAAAAA//kABQABAAYAAAAL +AAgABgAAAAD/+AAFAAEACwAAABAACQAGAAAAAP/5AAUAAAAQAAAAFQAHAAYAAP////oABQAAABUA +AAAbAAYABgAAAAH/+QAE//wAGwAAAB4AAwAGAAAAAf/5AAQAAQAeAAAAIQAIAAYAAAAB//kABAAB +ACEAAAAkAAgABgAAAAD/+QAE//0AJAAAACgABAAGAAAAAP/6AAX//wAoAAAALQAFAAYAAAAB//8A +BAACAC0AAAAwAAMABgAAAAD//AAF//0AMAAAADUAAQAGAAAAAf//AAMAAAA1AAAANwABAAYAAAAB +//kABQABADcAAAA7AAgABgAAAAD/+QAFAAAAOwAAAEAABwAGAAAAAP/5AAYAAABAAAAARgAHAAYA +AAAA//kABQAAAEYAAABLAAcABgAAAAD/+QAFAAAASwAAAFAABwAGAAAAAP/5AAYAAABQAAAAVgAH +AAYAAAAA//kABQAAAFYAAABbAAcABgAAAAD/+QAFAAAAWwAAAGAABwAGAAAAAP/5AAUAAABgAAAA +ZQAHAAYAAAAA//kABQAAAGUAAABqAAcABgAAAAD/+QAFAAAAagAAAG8ABwAGAAAAAf/8AAMAAABv +AAAAcQAEAAYAAAAA//wAAwACAHEAAAB0AAYABgAAAAD/+gAE//8AdAAAAHgABQAGAAAAAP/7AAT/ +/gB4AAAAfAADAAYAAAAB//oABf//AHwAAACAAAUABgAAAAD/+gAFAAAAgAAAAIUABgAGAAAAAP/5 +AAYAAQCFAAAAiwAIAAYAAP////oABgAAAIsAAACSAAYABgAA////+gAFAAAAkgAAAJgABgAGAAAA +AP/6AAUAAACYAAAAnQAGAAYAAP////oABQAAAJ0AAACjAAYABgAA////+gAFAAAAowAAAKkABgAG +AAD////6AAUAAACpAAAArwAGAAYAAAAA//oABQAAAK8AAAC0AAYABgAA////+gAGAAAAtAAAALsA +BgAGAAAAAP/6AAQAAAC7AAAAvwAGAAYAAP////oABQAAAL8AAADFAAYABgAA////+gAGAAAAxQAA +AMwABgAGAAD////6AAUAAADMAAAA0gAGAAYAAP////oABQAAANIAAADYAAYABgAA////+gAGAAAA +2AAAAN8ABgAGAAAAAP/6AAUAAADfAAAA5AAGAAYAAP////oABQAAAOQAAADqAAYABgAAAAD/+gAF +AAEA6gAAAO8ABwAGAAD////6AAYAAADvAAAA9gAGAAYAAAAA//oABQAAAPYAAAD7AAYABgAA//// ++gAFAAAA+wAAAQEABgAGAAD////6AAYAAAEBAAABCAAGAAYAAP////oABgAAAQgAAAEPAAYABgAA +////+gAGAAABDwAAARYABgAGAAAAAP/6AAYAAAEWAAABHAAGAAYAAP////oABgAAARwAAAEjAAYA +BgAAAAD/+gAFAAABIwAAASgABgAGAAAAAf/5AAQAAQEoAAABKwAIAAYAAAAA//kABAABASsAAAEv +AAgABgAAAAH/+QAEAAEBLwAAATIACAAGAAAAAP/5AAX//AEyAAABNwADAAYAAAAAAAEABgACATcA +AAE9AAEABgAAAAH/+QAE//wBPQAAAUAAAwAGAAAAAP/7AAYAAAFAAAABRgAFAAYAAP////kABQAA +AUYAAAFMAAcABgAAAAD/+wAFAAABTAAAAVEABQAGAAAAAP/5AAYAAAFRAAABVwAHAAYAAAAA//sA +BQAAAVcAAAFcAAUABgAAAAD/+QAFAAABXAAAAWEABwAGAAAAAP/7AAYAAgFhAAABZwAHAAYAAP// +//kABQAAAWcAAAFtAAcABgAAAAD/+QAGAAABbQAAAXMABwAGAAAAAP/5AAQAAgFzAAABdwAJAAYA +AP////kABgAAAXcAAAF+AAcABgAAAAD/+QAGAAABfgAAAYQABwAGAAD////7AAUAAAGEAAABigAF +AAYAAP////sABQAAAYoAAAGQAAUABgAAAAD/+wAFAAABkAAAAZUABQAGAAD////7AAUAAgGVAAAB +mwAHAAYAAAAA//sABgACAZsAAAGhAAcABgAAAAD/+wAGAAABoQAAAacABQAGAAAAAP/7AAYAAAGn +AAABrQAFAAYAAAAA//kABgAAAa0AAAGzAAcABgAA////+wAGAAABswAAAboABQAGAAD////7AAUA +AAG6AAABwAAFAAYAAP////sABgAAAcAAAAHHAAUABgAAAAD/+wAGAAABxwAAAc0ABQAGAAD////7 +AAYAAgHNAAAB1AAHAAYAAAAA//sABQAAAdQAAAHZAAUABgAAAAH/+QAFAAEB2QAAAd0ACAAGAAAA +Av/6AAMAAQHdAAAB3gAHAAYAAAAA//kABAABAd4AAAHiAAgABgAAAAD/+wAF//0B4gAAAecAAgAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAB +//sAAwACAecAAAHpAAcABgAAAAD/+QAFAAEB6QAAAe4ACAAGAAAAAP/5AAYAAAHuAAAB9AAHAAYA +AAAA//oABf//AfQAAAH5AAUABgAAAAD/+QAGAAAB+QAAAf8ABwAGAAAAAv/5AAMAAgH/AAACAAAJ +AAYAAAAA//kABQABAgAAAAIFAAgABgAAAAH/+gAE//sCBQAAAggAAQAGAAAAAP/5AAYAAAIIAAAC +DgAHAAYAAAAB//kABf/+Ag4AAAISAAUABgAA////+wAGAAACEgAAAhkABQAGAAAAAP/7AAX//gIZ +AAACHgADAAYAAAAA//wABf/9Ah4AAAIjAAEABgAAAAD/+QAHAAACIwAAAioABwAGAAAAAP/6AAT/ ++wIqAAACLgABAAYAAAAA//kABP/8Ai4AAAIyAAMABgAAAAD/+gAFAAACMgAAAjcABgAGAAAAAf/5 +AAT//QI3AAACOgAEAAYAAAAB//kABP/9AjoAAAI9AAQABgAAAAL/+QAE//sCPQAAAj8AAgAGAAD/ +///7AAYAAgI/AAACRgAHAAYAAAAA//kABgABAkYAAAJMAAgABgAAAAH//AAD//0CTAAAAk4AAQAG +AAAAAf//AAQAAgJOAAACUQADAAYAAAAB//kABP/9AlEAAAJUAAQABgAAAAH/+QAF//4CVAAAAlgA +BQAGAAD////7AAYAAAJYAAACXwAFAAYAAP////kABgAAAl8AAAJmAAcABgAA////+QAGAAACZgAA +Am0ABwAGAAD////5AAYAAAJtAAACdAAHAAYAAAAA//sABQACAnQAAAJ5AAcABgAA////9wAGAAAC +eQAAAoAACQAGAAD////3AAYAAAKAAAAChwAJAAYAAP////cABgAAAocAAAKOAAkABgAA////9wAG +AAACjgAAApUACQAGAAD////4AAYAAAKVAAACnAAIAAYAAP////cABgAAApwAAAKjAAkABgAA//// ++gAGAAACowAAAqoABgAGAAAAAP/6AAUAAgKqAAACrwAIAAYAAP////cABQAAAq8AAAK1AAkABgAA +////9wAFAAACtQAAArsACQAGAAD////3AAUAAAK7AAACwQAJAAYAAP////gABQAAAsEAAALHAAgA +BgAAAAD/9wAEAAACxwAAAssACQAGAAAAAP/3AAQAAALLAAACzwAJAAYAAAAA//cABAAAAs8AAALT +AAkABgAAAAD/+AAEAAAC0wAAAtcACAAGAAD////6AAUAAALXAAAC3QAGAAYAAP////cABgAAAt0A +AALkAAkABgAAAAD/9wAFAAAC5AAAAukACQAGAAAAAP/3AAUAAALpAAAC7gAJAAYAAAAA//cABQAA +Au4AAALzAAkABgAAAAD/9wAFAAAC8wAAAvgACQAGAAAAAP/4AAUAAAL4AAAC/QAIAAYAAAAA//oA +Bf//Av0AAAMCAAUABgAA////+gAGAAADAgAAAwkABgAGAAD////3AAYAAAMJAAADEAAJAAYAAP// +//cABgAAAxAAAAMXAAkABgAA////9wAGAAADFwAAAx4ACQAGAAD////4AAYAAAAAAAoABwASAAYA +AP////cABgAAAAcACgAOABMABgAA////+gAFAAAADgAKABQAEAAGAAD////6AAYAAAAUAAoAGwAQ +AAYAAAAA//gABgAAABsACgAhABIABgAAAAD/+AAGAAAAIQAKACcAEgAGAAAAAP/4AAYAAAAnAAoA +LQASAAYAAAAA//gABgAAAC0ACgAzABIABgAAAAD/+QAGAAAAMwAKADkAEQAGAAAAAP/3AAYAAAA5 +AAoAPwATAAYAAP////sABQAAAD8ACgBFAA8ABgAAAAD/+wAFAAIARQAKAEoAEQAGAAAAAP/4AAUA +AABKAAoATwASAAYAAAAA//gABQAAAE8ACgBUABIABgAAAAD/+AAFAAAAVAAKAFkAEgAGAAAAAP/5 +AAUAAABZAAoAXgARAAYAAAAA//gABgAAAF4ACgBkABIABgAAAAD/+AAGAAAAZAAKAGoAEgAGAAAA +AP/4AAYAAABqAAoAcAASAAYAAAAA//kABgAAAHAACgB2ABEABgAAAAD/+AAFAAAAdgAKAHsAEgAG +AAD////4AAYAAAB7AAoAggASAAYAAAAA//gABQAAAIIACgCHABIABgAAAAD/+AAFAAAAhwAKAIwA +EgAGAAAAAP/4AAUAAACMAAoAkQASAAYAAAAA//gABQAAAJEACgCWABIABgAAAAD/+QAFAAAAlgAK +AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA +pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG +AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA//// ++QAGAAIAzgAKANUAEw== +""" + ) + ), + Image.open( + BytesIO( + base64.b64decode( + b""" +iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u +Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9 +M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g +LeNZUworuN1cjTPIzrTX6ofHWeo3v336qPzfEwRmBnHTtf95/fglZK5N0PDgfRTslpGBvz7LFc4F +IUXBWQGjQ5MGCx34EDFPwXiY4YbYxavpnhHFrk14CDAAAAD//wBlAJr/AgKqRooH2gAgPeggvUAA +Bu2WfgPoAwzRAABAAAAAAACQgLz/3Uv4Gv+gX7BJgDeeGP6AAAD1NMDzKHD7ANWr3loYbxsAD791 +NAADfcoIDyP44K/jv4Y63/Z+t98Ovt+ub4T48LAAAAD//wBlAJr/AuplMlADJAAAAGuAphWpqhMx +in0A/fRvAYBABPgBwBUgABBQ/sYAyv9g0bCHgOLoGAAAAAAAREAAwI7nr0ArYpow7aX8//9LaP/9 +SjdavWA8ePHeBIKB//81/83ndznOaXx379wAAAD//wBlAJr/AqDxW+D3AABAAbUh/QMnbQag/gAY +AYDAAACgtgD/gOqAAAB5IA/8AAAk+n9w0AAA8AAAmFRJuPo27ciC0cD5oeW4E7KA/wD3ECMAn2tt +y8PgwH8AfAxFzC0JzeAMtratAsC/ffwAAAD//wBlAJr/BGKAyCAA4AAAAvgeYTAwHd1kmQF5chkG +ABoMIHcL5xVpTfQbUqzlAAAErwAQBgAAEOClA5D9il08AEh/tUzdCBsXkbgACED+woQg8Si9VeqY +lODCn7lmF6NhnAEYgAAA/NMIAAAAAAD//2JgjLZgVGBg5Pv/Tvpc8hwGBjYGJADjHDrAwPzAjv/H +/Wf3PzCwtzcwHmBgYGcwbZz8wHaCAQMDOwMDQ8MCBgYOC3W7mp+f0w+wHOYxO3OG+e376hsMZjk3 +AAAAAP//YmCMY2A4wMAIN5e5gQETPD6AZisDAwMDgzSDAAPjByiHcQMDAwMDg1nOze1lByRu5/47 +c4859311AYNZzg0AAAAA//9iYGDBYihOIIMuwIjGL39/fwffA8b//xv/P2BPtzzHwCBjUQAAAAD/ +/yLFBrIBAAAA//9i1HhcwdhizX7u8NZNzyLbvT97bfrMf/QHI8evOwcSqGUJAAAA//9iYBB81iSw +pEE170Qrg5MIYydHqwdDQRMrAwcVrQAAAAD//2J4x7j9AAMDn8Q/BgYLBoaiAwwMjPdvMDBYM1Tv +oJodAAAAAP//Yqo/83+dxePWlxl3npsel9lvLfPcqlE9725C+acfVLMEAAAA//9i+s9gwCoaaGMR +evta/58PTEWzr21hufPjA8N+qlnBwAAAAAD//2JiWLci5v1+HmFXDqcnULE/MxgYGBj+f6CaJQAA +AAD//2Ji2FrkY3iYpYC5qDeGgeEMAwPDvwQBBoYvcTwOVLMEAAAA//9isDBgkP///0EOg9z35v// +Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR +w7IkEbzhVQAAAABJRU5ErkJggg== +""" + ) + ) + ), + ) + return f diff --git a/venv/lib/python3.11/site-packages/PIL/ImageGrab.py b/venv/lib/python3.11/site-packages/PIL/ImageGrab.py new file mode 100644 index 00000000..b93ec3f2 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageGrab.py @@ -0,0 +1,120 @@ +# +# The Python Imaging Library +# $Id$ +# +# screen grabber +# +# History: +# 2001-04-26 fl created +# 2001-09-17 fl use builtin driver, if present +# 2002-11-19 fl added grabclipboard support +# +# Copyright (c) 2001-2002 by Secret Labs AB +# Copyright (c) 2001-2002 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import sys + +from . import Image + +if sys.platform == "darwin": + import os + import subprocess + import tempfile + + +def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None): + if xdisplay is None: + if sys.platform == "darwin": + fh, filepath = tempfile.mkstemp(".png") + os.close(fh) + subprocess.call(["screencapture", "-x", filepath]) + im = Image.open(filepath) + im.load() + os.unlink(filepath) + if bbox: + im_cropped = im.crop(bbox) + im.close() + return im_cropped + return im + elif sys.platform == "win32": + offset, size, data = Image.core.grabscreen_win32( + include_layered_windows, all_screens + ) + im = Image.frombytes( + "RGB", + size, + data, + # RGB, 32-bit line padding, origin lower left corner + "raw", + "BGR", + (size[0] * 3 + 3) & -4, + -1, + ) + if bbox: + x0, y0 = offset + left, top, right, bottom = bbox + im = im.crop((left - x0, top - y0, right - x0, bottom - y0)) + return im + # use xdisplay=None for default display on non-win32/macOS systems + if not Image.core.HAVE_XCB: + raise OSError("Pillow was built without XCB support") + size, data = Image.core.grabscreen_x11(xdisplay) + im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1) + if bbox: + im = im.crop(bbox) + return im + + +def grabclipboard(): + if sys.platform == "darwin": + fh, filepath = tempfile.mkstemp(".jpg") + os.close(fh) + commands = [ + 'set theFile to (open for access POSIX file "' + + filepath + + '" with write permission)', + "try", + " write (the clipboard as JPEG picture) to theFile", + "end try", + "close access theFile", + ] + script = ["osascript"] + for command in commands: + script += ["-e", command] + subprocess.call(script) + + im = None + if os.stat(filepath).st_size != 0: + im = Image.open(filepath) + im.load() + os.unlink(filepath) + return im + elif sys.platform == "win32": + fmt, data = Image.core.grabclipboard_win32() + if fmt == "file": # CF_HDROP + import struct + + o = struct.unpack_from("I", data)[0] + if data[16] != 0: + files = data[o:].decode("utf-16le").split("\0") + else: + files = data[o:].decode("mbcs").split("\0") + return files[: files.index("")] + if isinstance(data, bytes): + import io + + data = io.BytesIO(data) + if fmt == "png": + from . import PngImagePlugin + + return PngImagePlugin.PngImageFile(data) + elif fmt == "DIB": + from . import BmpImagePlugin + + return BmpImagePlugin.DibImageFile(data) + return None + else: + raise NotImplementedError("ImageGrab.grabclipboard() is macOS and Windows only") diff --git a/venv/lib/python3.11/site-packages/PIL/ImageMath.py b/venv/lib/python3.11/site-packages/PIL/ImageMath.py new file mode 100644 index 00000000..7f9c88e1 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageMath.py @@ -0,0 +1,253 @@ +# +# The Python Imaging Library +# $Id$ +# +# a simple math add-on for the Python Imaging Library +# +# History: +# 1999-02-15 fl Original PIL Plus release +# 2005-05-05 fl Simplified and cleaned up for PIL 1.1.6 +# 2005-09-12 fl Fixed int() and float() for Python 2.4.1 +# +# Copyright (c) 1999-2005 by Secret Labs AB +# Copyright (c) 2005 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import builtins + +from . import Image, _imagingmath + +VERBOSE = 0 + + +def _isconstant(v): + return isinstance(v, (int, float)) + + +class _Operand: + """Wraps an image operand, providing standard operators""" + + def __init__(self, im): + self.im = im + + def __fixup(self, im1): + # convert image to suitable mode + if isinstance(im1, _Operand): + # argument was an image. + if im1.im.mode in ("1", "L"): + return im1.im.convert("I") + elif im1.im.mode in ("I", "F"): + return im1.im + else: + raise ValueError(f"unsupported mode: {im1.im.mode}") + else: + # argument was a constant + if _isconstant(im1) and self.im.mode in ("1", "L", "I"): + return Image.new("I", self.im.size, im1) + else: + return Image.new("F", self.im.size, im1) + + def apply(self, op, im1, im2=None, mode=None): + im1 = self.__fixup(im1) + if im2 is None: + # unary operation + out = Image.new(mode or im1.mode, im1.size, None) + im1.load() + try: + op = getattr(_imagingmath, op + "_" + im1.mode) + except AttributeError as e: + raise TypeError(f"bad operand type for '{op}'") from e + _imagingmath.unop(op, out.im.id, im1.im.id) + else: + # binary operation + im2 = self.__fixup(im2) + if im1.mode != im2.mode: + # convert both arguments to floating point + if im1.mode != "F": + im1 = im1.convert("F") + if im2.mode != "F": + im2 = im2.convert("F") + if im1.mode != im2.mode: + raise ValueError("mode mismatch") + if im1.size != im2.size: + # crop both arguments to a common size + size = (min(im1.size[0], im2.size[0]), min(im1.size[1], im2.size[1])) + if im1.size != size: + im1 = im1.crop((0, 0) + size) + if im2.size != size: + im2 = im2.crop((0, 0) + size) + out = Image.new(mode or im1.mode, size, None) + else: + out = Image.new(mode or im1.mode, im1.size, None) + im1.load() + im2.load() + try: + op = getattr(_imagingmath, op + "_" + im1.mode) + except AttributeError as e: + raise TypeError(f"bad operand type for '{op}'") from e + _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id) + return _Operand(out) + + # unary operators + def __bool__(self): + # an image is "true" if it contains at least one non-zero pixel + return self.im.getbbox() is not None + + def __abs__(self): + return self.apply("abs", self) + + def __pos__(self): + return self + + def __neg__(self): + return self.apply("neg", self) + + # binary operators + def __add__(self, other): + return self.apply("add", self, other) + + def __radd__(self, other): + return self.apply("add", other, self) + + def __sub__(self, other): + return self.apply("sub", self, other) + + def __rsub__(self, other): + return self.apply("sub", other, self) + + def __mul__(self, other): + return self.apply("mul", self, other) + + def __rmul__(self, other): + return self.apply("mul", other, self) + + def __truediv__(self, other): + return self.apply("div", self, other) + + def __rtruediv__(self, other): + return self.apply("div", other, self) + + def __mod__(self, other): + return self.apply("mod", self, other) + + def __rmod__(self, other): + return self.apply("mod", other, self) + + def __pow__(self, other): + return self.apply("pow", self, other) + + def __rpow__(self, other): + return self.apply("pow", other, self) + + # bitwise + def __invert__(self): + return self.apply("invert", self) + + def __and__(self, other): + return self.apply("and", self, other) + + def __rand__(self, other): + return self.apply("and", other, self) + + def __or__(self, other): + return self.apply("or", self, other) + + def __ror__(self, other): + return self.apply("or", other, self) + + def __xor__(self, other): + return self.apply("xor", self, other) + + def __rxor__(self, other): + return self.apply("xor", other, self) + + def __lshift__(self, other): + return self.apply("lshift", self, other) + + def __rshift__(self, other): + return self.apply("rshift", self, other) + + # logical + def __eq__(self, other): + return self.apply("eq", self, other) + + def __ne__(self, other): + return self.apply("ne", self, other) + + def __lt__(self, other): + return self.apply("lt", self, other) + + def __le__(self, other): + return self.apply("le", self, other) + + def __gt__(self, other): + return self.apply("gt", self, other) + + def __ge__(self, other): + return self.apply("ge", self, other) + + +# conversions +def imagemath_int(self): + return _Operand(self.im.convert("I")) + + +def imagemath_float(self): + return _Operand(self.im.convert("F")) + + +# logical +def imagemath_equal(self, other): + return self.apply("eq", self, other, mode="I") + + +def imagemath_notequal(self, other): + return self.apply("ne", self, other, mode="I") + + +def imagemath_min(self, other): + return self.apply("min", self, other) + + +def imagemath_max(self, other): + return self.apply("max", self, other) + + +def imagemath_convert(self, mode): + return _Operand(self.im.convert(mode)) + + +ops = {} +for k, v in list(globals().items()): + if k[:10] == "imagemath_": + ops[k[10:]] = v + + +def eval(expression, _dict={}, **kw): + """ + Evaluates an image expression. + + :param expression: A string containing a Python-style expression. + :param options: Values to add to the evaluation context. You + can either use a dictionary, or one or more keyword + arguments. + :return: The evaluated expression. This is usually an image object, but can + also be an integer, a floating point value, or a pixel tuple, + depending on the expression. + """ + + # build execution namespace + args = ops.copy() + args.update(_dict) + args.update(kw) + for k, v in list(args.items()): + if hasattr(v, "im"): + args[k] = _Operand(v) + + out = builtins.eval(expression, args) + try: + return out.im + except AttributeError: + return out diff --git a/venv/lib/python3.11/site-packages/PIL/ImageMode.py b/venv/lib/python3.11/site-packages/PIL/ImageMode.py new file mode 100644 index 00000000..98828832 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageMode.py @@ -0,0 +1,64 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard mode descriptors +# +# History: +# 2006-03-20 fl Added +# +# Copyright (c) 2006 by Secret Labs AB. +# Copyright (c) 2006 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +# mode descriptor cache +_modes = None + + +class ModeDescriptor: + """Wrapper for mode strings.""" + + def __init__(self, mode, bands, basemode, basetype): + self.mode = mode + self.bands = bands + self.basemode = basemode + self.basetype = basetype + + def __str__(self): + return self.mode + + +def getmode(mode): + """Gets a mode descriptor for the given mode.""" + global _modes + if not _modes: + # initialize mode cache + + from . import Image + + modes = {} + # core modes + for m, (basemode, basetype, bands) in Image._MODEINFO.items(): + modes[m] = ModeDescriptor(m, bands, basemode, basetype) + # extra experimental modes + modes["RGBa"] = ModeDescriptor("RGBa", ("R", "G", "B", "a"), "RGB", "L") + modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L") + modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L") + modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L") + # mapping modes + for i16mode in ( + "I;16", + "I;16S", + "I;16L", + "I;16LS", + "I;16B", + "I;16BS", + "I;16N", + "I;16NS", + ): + modes[i16mode] = ModeDescriptor(i16mode, ("I",), "L", "L") + # set global mode cache atomically + _modes = modes + return _modes[mode] diff --git a/venv/lib/python3.11/site-packages/PIL/ImageMorph.py b/venv/lib/python3.11/site-packages/PIL/ImageMorph.py new file mode 100644 index 00000000..b76dfa01 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageMorph.py @@ -0,0 +1,245 @@ +# A binary morphology add-on for the Python Imaging Library +# +# History: +# 2014-06-04 Initial version. +# +# Copyright (c) 2014 Dov Grobgeld + +import re + +from . import Image, _imagingmorph + +LUT_SIZE = 1 << 9 + +# fmt: off +ROTATION_MATRIX = [ + 6, 3, 0, + 7, 4, 1, + 8, 5, 2, +] +MIRROR_MATRIX = [ + 2, 1, 0, + 5, 4, 3, + 8, 7, 6, +] +# fmt: on + + +class LutBuilder: + """A class for building a MorphLut from a descriptive language + + The input patterns is a list of a strings sequences like these:: + + 4:(... + .1. + 111)->1 + + (whitespaces including linebreaks are ignored). The option 4 + describes a series of symmetry operations (in this case a + 4-rotation), the pattern is described by: + + - . or X - Ignore + - 1 - Pixel is on + - 0 - Pixel is off + + The result of the operation is described after "->" string. + + The default is to return the current pixel value, which is + returned if no other match is found. + + Operations: + + - 4 - 4 way rotation + - N - Negate + - 1 - Dummy op for no other operation (an op must always be given) + - M - Mirroring + + Example:: + + lb = LutBuilder(patterns = ["4:(... .1. 111)->1"]) + lut = lb.build_lut() + + """ + + def __init__(self, patterns=None, op_name=None): + if patterns is not None: + self.patterns = patterns + else: + self.patterns = [] + self.lut = None + if op_name is not None: + known_patterns = { + "corner": ["1:(... ... ...)->0", "4:(00. 01. ...)->1"], + "dilation4": ["4:(... .0. .1.)->1"], + "dilation8": ["4:(... .0. .1.)->1", "4:(... .0. ..1)->1"], + "erosion4": ["4:(... .1. .0.)->0"], + "erosion8": ["4:(... .1. .0.)->0", "4:(... .1. ..0)->0"], + "edge": [ + "1:(... ... ...)->0", + "4:(.0. .1. ...)->1", + "4:(01. .1. ...)->1", + ], + } + if op_name not in known_patterns: + raise Exception("Unknown pattern " + op_name + "!") + + self.patterns = known_patterns[op_name] + + def add_patterns(self, patterns): + self.patterns += patterns + + def build_default_lut(self): + symbols = [0, 1] + m = 1 << 4 # pos of current pixel + self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE)) + + def get_lut(self): + return self.lut + + def _string_permute(self, pattern, permutation): + """string_permute takes a pattern and a permutation and returns the + string permuted according to the permutation list. + """ + assert len(permutation) == 9 + return "".join(pattern[p] for p in permutation) + + def _pattern_permute(self, basic_pattern, options, basic_result): + """pattern_permute takes a basic pattern and its result and clones + the pattern according to the modifications described in the $options + parameter. It returns a list of all cloned patterns.""" + patterns = [(basic_pattern, basic_result)] + + # rotations + if "4" in options: + res = patterns[-1][1] + for i in range(4): + patterns.append( + (self._string_permute(patterns[-1][0], ROTATION_MATRIX), res) + ) + # mirror + if "M" in options: + n = len(patterns) + for pattern, res in patterns[0:n]: + patterns.append((self._string_permute(pattern, MIRROR_MATRIX), res)) + + # negate + if "N" in options: + n = len(patterns) + for pattern, res in patterns[0:n]: + # Swap 0 and 1 + pattern = pattern.replace("0", "Z").replace("1", "0").replace("Z", "1") + res = 1 - int(res) + patterns.append((pattern, res)) + + return patterns + + def build_lut(self): + """Compile all patterns into a morphology lut. + + TBD :Build based on (file) morphlut:modify_lut + """ + self.build_default_lut() + patterns = [] + + # Parse and create symmetries of the patterns strings + for p in self.patterns: + m = re.search(r"(\w*):?\s*\((.+?)\)\s*->\s*(\d)", p.replace("\n", "")) + if not m: + raise Exception('Syntax error in pattern "' + p + '"') + options = m.group(1) + pattern = m.group(2) + result = int(m.group(3)) + + # Get rid of spaces + pattern = pattern.replace(" ", "").replace("\n", "") + + patterns += self._pattern_permute(pattern, options, result) + + # compile the patterns into regular expressions for speed + for i, pattern in enumerate(patterns): + p = pattern[0].replace(".", "X").replace("X", "[01]") + p = re.compile(p) + patterns[i] = (p, pattern[1]) + + # Step through table and find patterns that match. + # Note that all the patterns are searched. The last one + # caught overrides + for i in range(LUT_SIZE): + # Build the bit pattern + bitpattern = bin(i)[2:] + bitpattern = ("0" * (9 - len(bitpattern)) + bitpattern)[::-1] + + for p, r in patterns: + if p.match(bitpattern): + self.lut[i] = [0, 1][r] + + return self.lut + + +class MorphOp: + """A class for binary morphological operators""" + + def __init__(self, lut=None, op_name=None, patterns=None): + """Create a binary morphological operator""" + self.lut = lut + if op_name is not None: + self.lut = LutBuilder(op_name=op_name).build_lut() + elif patterns is not None: + self.lut = LutBuilder(patterns=patterns).build_lut() + + def apply(self, image): + """Run a single morphological operation on an image + + Returns a tuple of the number of changed pixels and the + morphed image""" + if self.lut is None: + raise Exception("No operator loaded") + + if image.mode != "L": + raise Exception("Image must be binary, meaning it must use mode L") + outimage = Image.new(image.mode, image.size, None) + count = _imagingmorph.apply(bytes(self.lut), image.im.id, outimage.im.id) + return count, outimage + + def match(self, image): + """Get a list of coordinates matching the morphological operation on + an image. + + Returns a list of tuples of (x,y) coordinates + of all matching pixels. See :ref:`coordinate-system`.""" + if self.lut is None: + raise Exception("No operator loaded") + + if image.mode != "L": + raise Exception("Image must be binary, meaning it must use mode L") + return _imagingmorph.match(bytes(self.lut), image.im.id) + + def get_on_pixels(self, image): + """Get a list of all turned on pixels in a binary image + + Returns a list of tuples of (x,y) coordinates + of all matching pixels. See :ref:`coordinate-system`.""" + + if image.mode != "L": + raise Exception("Image must be binary, meaning it must use mode L") + return _imagingmorph.get_on_pixels(image.im.id) + + def load_lut(self, filename): + """Load an operator from an mrl file""" + with open(filename, "rb") as f: + self.lut = bytearray(f.read()) + + if len(self.lut) != LUT_SIZE: + self.lut = None + raise Exception("Wrong size operator file!") + + def save_lut(self, filename): + """Save an operator to an mrl file""" + if self.lut is None: + raise Exception("No operator loaded") + with open(filename, "wb") as f: + f.write(self.lut) + + def set_lut(self, lut): + """Set the lut from an external source""" + self.lut = lut diff --git a/venv/lib/python3.11/site-packages/PIL/ImageOps.py b/venv/lib/python3.11/site-packages/PIL/ImageOps.py new file mode 100644 index 00000000..14602a5c --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageOps.py @@ -0,0 +1,558 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard image operations +# +# History: +# 2001-10-20 fl Created +# 2001-10-23 fl Added autocontrast operator +# 2001-12-18 fl Added Kevin's fit operator +# 2004-03-14 fl Fixed potential division by zero in equalize +# 2005-05-05 fl Fixed equalize for low number of values +# +# Copyright (c) 2001-2004 by Secret Labs AB +# Copyright (c) 2001-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import functools +import operator + +from . import Image + +# +# helpers + + +def _border(border): + if isinstance(border, tuple): + if len(border) == 2: + left, top = right, bottom = border + elif len(border) == 4: + left, top, right, bottom = border + else: + left = top = right = bottom = border + return left, top, right, bottom + + +def _color(color, mode): + if isinstance(color, str): + from . import ImageColor + + color = ImageColor.getcolor(color, mode) + return color + + +def _lut(image, lut): + if image.mode == "P": + # FIXME: apply to lookup table, not image data + raise NotImplementedError("mode P support coming soon") + elif image.mode in ("L", "RGB"): + if image.mode == "RGB" and len(lut) == 256: + lut = lut + lut + lut + return image.point(lut) + else: + raise OSError("not supported for this image mode") + + +# +# actions + + +def autocontrast(image, cutoff=0, ignore=None, mask=None): + """ + Maximize (normalize) image contrast. This function calculates a + histogram of the input image (or mask region), removes ``cutoff`` percent of the + lightest and darkest pixels from the histogram, and remaps the image + so that the darkest pixel becomes black (0), and the lightest + becomes white (255). + + :param image: The image to process. + :param cutoff: The percent to cut off from the histogram on the low and + high ends. Either a tuple of (low, high), or a single + number for both. + :param ignore: The background pixel value (use None for no background). + :param mask: Histogram used in contrast operation is computed using pixels + within the mask. If no mask is given the entire image is used + for histogram computation. + :return: An image. + """ + histogram = image.histogram(mask) + lut = [] + for layer in range(0, len(histogram), 256): + h = histogram[layer : layer + 256] + if ignore is not None: + # get rid of outliers + try: + h[ignore] = 0 + except TypeError: + # assume sequence + for ix in ignore: + h[ix] = 0 + if cutoff: + # cut off pixels from both ends of the histogram + if not isinstance(cutoff, tuple): + cutoff = (cutoff, cutoff) + # get number of pixels + n = 0 + for ix in range(256): + n = n + h[ix] + # remove cutoff% pixels from the low end + cut = n * cutoff[0] // 100 + for lo in range(256): + if cut > h[lo]: + cut = cut - h[lo] + h[lo] = 0 + else: + h[lo] -= cut + cut = 0 + if cut <= 0: + break + # remove cutoff% samples from the high end + cut = n * cutoff[1] // 100 + for hi in range(255, -1, -1): + if cut > h[hi]: + cut = cut - h[hi] + h[hi] = 0 + else: + h[hi] -= cut + cut = 0 + if cut <= 0: + break + # find lowest/highest samples after preprocessing + for lo in range(256): + if h[lo]: + break + for hi in range(255, -1, -1): + if h[hi]: + break + if hi <= lo: + # don't bother + lut.extend(list(range(256))) + else: + scale = 255.0 / (hi - lo) + offset = -lo * scale + for ix in range(256): + ix = int(ix * scale + offset) + if ix < 0: + ix = 0 + elif ix > 255: + ix = 255 + lut.append(ix) + return _lut(image, lut) + + +def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoint=127): + """ + Colorize grayscale image. + This function calculates a color wedge which maps all black pixels in + the source image to the first color and all white pixels to the + second color. If ``mid`` is specified, it uses three-color mapping. + The ``black`` and ``white`` arguments should be RGB tuples or color names; + optionally you can use three-color mapping by also specifying ``mid``. + Mapping positions for any of the colors can be specified + (e.g. ``blackpoint``), where these parameters are the integer + value corresponding to where the corresponding color should be mapped. + These parameters must have logical order, such that + ``blackpoint <= midpoint <= whitepoint`` (if ``mid`` is specified). + + :param image: The image to colorize. + :param black: The color to use for black input pixels. + :param white: The color to use for white input pixels. + :param mid: The color to use for midtone input pixels. + :param blackpoint: an int value [0, 255] for the black mapping. + :param whitepoint: an int value [0, 255] for the white mapping. + :param midpoint: an int value [0, 255] for the midtone mapping. + :return: An image. + """ + + # Initial asserts + assert image.mode == "L" + if mid is None: + assert 0 <= blackpoint <= whitepoint <= 255 + else: + assert 0 <= blackpoint <= midpoint <= whitepoint <= 255 + + # Define colors from arguments + black = _color(black, "RGB") + white = _color(white, "RGB") + if mid is not None: + mid = _color(mid, "RGB") + + # Empty lists for the mapping + red = [] + green = [] + blue = [] + + # Create the low-end values + for i in range(0, blackpoint): + red.append(black[0]) + green.append(black[1]) + blue.append(black[2]) + + # Create the mapping (2-color) + if mid is None: + + range_map = range(0, whitepoint - blackpoint) + + for i in range_map: + red.append(black[0] + i * (white[0] - black[0]) // len(range_map)) + green.append(black[1] + i * (white[1] - black[1]) // len(range_map)) + blue.append(black[2] + i * (white[2] - black[2]) // len(range_map)) + + # Create the mapping (3-color) + else: + + range_map1 = range(0, midpoint - blackpoint) + range_map2 = range(0, whitepoint - midpoint) + + for i in range_map1: + red.append(black[0] + i * (mid[0] - black[0]) // len(range_map1)) + green.append(black[1] + i * (mid[1] - black[1]) // len(range_map1)) + blue.append(black[2] + i * (mid[2] - black[2]) // len(range_map1)) + for i in range_map2: + red.append(mid[0] + i * (white[0] - mid[0]) // len(range_map2)) + green.append(mid[1] + i * (white[1] - mid[1]) // len(range_map2)) + blue.append(mid[2] + i * (white[2] - mid[2]) // len(range_map2)) + + # Create the high-end values + for i in range(0, 256 - whitepoint): + red.append(white[0]) + green.append(white[1]) + blue.append(white[2]) + + # Return converted image + image = image.convert("RGB") + return _lut(image, red + green + blue) + + +def pad(image, size, method=Image.BICUBIC, color=None, centering=(0.5, 0.5)): + """ + Returns a sized and padded version of the image, expanded to fill the + requested aspect ratio and size. + + :param image: The image to size and crop. + :param size: The requested output size in pixels, given as a + (width, height) tuple. + :param method: What resampling method to use. Default is + :py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`. + :param color: The background color of the padded image. + :param centering: Control the position of the original image within the + padded version. + + (0.5, 0.5) will keep the image centered + (0, 0) will keep the image aligned to the top left + (1, 1) will keep the image aligned to the bottom + right + :return: An image. + """ + + im_ratio = image.width / image.height + dest_ratio = size[0] / size[1] + + if im_ratio == dest_ratio: + out = image.resize(size, resample=method) + else: + out = Image.new(image.mode, size, color) + if im_ratio > dest_ratio: + new_height = int(image.height / image.width * size[0]) + if new_height != size[1]: + image = image.resize((size[0], new_height), resample=method) + + y = int((size[1] - new_height) * max(0, min(centering[1], 1))) + out.paste(image, (0, y)) + else: + new_width = int(image.width / image.height * size[1]) + if new_width != size[0]: + image = image.resize((new_width, size[1]), resample=method) + + x = int((size[0] - new_width) * max(0, min(centering[0], 1))) + out.paste(image, (x, 0)) + return out + + +def crop(image, border=0): + """ + Remove border from image. The same amount of pixels are removed + from all four sides. This function works on all image modes. + + .. seealso:: :py:meth:`~PIL.Image.Image.crop` + + :param image: The image to crop. + :param border: The number of pixels to remove. + :return: An image. + """ + left, top, right, bottom = _border(border) + return image.crop((left, top, image.size[0] - right, image.size[1] - bottom)) + + +def scale(image, factor, resample=Image.BICUBIC): + """ + Returns a rescaled image by a specific factor given in parameter. + A factor greater than 1 expands the image, between 0 and 1 contracts the + image. + + :param image: The image to rescale. + :param factor: The expansion factor, as a float. + :param resample: What resampling method to use. Default is + :py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + if factor == 1: + return image.copy() + elif factor <= 0: + raise ValueError("the factor must be greater than 0") + else: + size = (round(factor * image.width), round(factor * image.height)) + return image.resize(size, resample) + + +def deform(image, deformer, resample=Image.BILINEAR): + """ + Deform the image. + + :param image: The image to deform. + :param deformer: A deformer object. Any object that implements a + ``getmesh`` method can be used. + :param resample: An optional resampling filter. Same values possible as + in the PIL.Image.transform function. + :return: An image. + """ + return image.transform(image.size, Image.MESH, deformer.getmesh(image), resample) + + +def equalize(image, mask=None): + """ + Equalize the image histogram. This function applies a non-linear + mapping to the input image, in order to create a uniform + distribution of grayscale values in the output image. + + :param image: The image to equalize. + :param mask: An optional mask. If given, only the pixels selected by + the mask are included in the analysis. + :return: An image. + """ + if image.mode == "P": + image = image.convert("RGB") + h = image.histogram(mask) + lut = [] + for b in range(0, len(h), 256): + histo = [_f for _f in h[b : b + 256] if _f] + if len(histo) <= 1: + lut.extend(list(range(256))) + else: + step = (functools.reduce(operator.add, histo) - histo[-1]) // 255 + if not step: + lut.extend(list(range(256))) + else: + n = step // 2 + for i in range(256): + lut.append(n // step) + n = n + h[i + b] + return _lut(image, lut) + + +def expand(image, border=0, fill=0): + """ + Add border to the image + + :param image: The image to expand. + :param border: Border width, in pixels. + :param fill: Pixel fill value (a color value). Default is 0 (black). + :return: An image. + """ + left, top, right, bottom = _border(border) + width = left + image.size[0] + right + height = top + image.size[1] + bottom + out = Image.new(image.mode, (width, height), _color(fill, image.mode)) + out.paste(image, (left, top)) + return out + + +def fit(image, size, method=Image.BICUBIC, bleed=0.0, centering=(0.5, 0.5)): + """ + Returns a sized and cropped version of the image, cropped to the + requested aspect ratio and size. + + This function was contributed by Kevin Cazabon. + + :param image: The image to size and crop. + :param size: The requested output size in pixels, given as a + (width, height) tuple. + :param method: What resampling method to use. Default is + :py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`. + :param bleed: Remove a border around the outside of the image from all + four edges. The value is a decimal percentage (use 0.01 for + one percent). The default value is 0 (no border). + Cannot be greater than or equal to 0.5. + :param centering: Control the cropping position. Use (0.5, 0.5) for + center cropping (e.g. if cropping the width, take 50% off + of the left side, and therefore 50% off the right side). + (0.0, 0.0) will crop from the top left corner (i.e. if + cropping the width, take all of the crop off of the right + side, and if cropping the height, take all of it off the + bottom). (1.0, 0.0) will crop from the bottom left + corner, etc. (i.e. if cropping the width, take all of the + crop off the left side, and if cropping the height take + none from the top, and therefore all off the bottom). + :return: An image. + """ + + # by Kevin Cazabon, Feb 17/2000 + # kevin@cazabon.com + # http://www.cazabon.com + + # ensure centering is mutable + centering = list(centering) + + if not 0.0 <= centering[0] <= 1.0: + centering[0] = 0.5 + if not 0.0 <= centering[1] <= 1.0: + centering[1] = 0.5 + + if not 0.0 <= bleed < 0.5: + bleed = 0.0 + + # calculate the area to use for resizing and cropping, subtracting + # the 'bleed' around the edges + + # number of pixels to trim off on Top and Bottom, Left and Right + bleed_pixels = (bleed * image.size[0], bleed * image.size[1]) + + live_size = ( + image.size[0] - bleed_pixels[0] * 2, + image.size[1] - bleed_pixels[1] * 2, + ) + + # calculate the aspect ratio of the live_size + live_size_ratio = live_size[0] / live_size[1] + + # calculate the aspect ratio of the output image + output_ratio = size[0] / size[1] + + # figure out if the sides or top/bottom will be cropped off + if live_size_ratio == output_ratio: + # live_size is already the needed ratio + crop_width = live_size[0] + crop_height = live_size[1] + elif live_size_ratio >= output_ratio: + # live_size is wider than what's needed, crop the sides + crop_width = output_ratio * live_size[1] + crop_height = live_size[1] + else: + # live_size is taller than what's needed, crop the top and bottom + crop_width = live_size[0] + crop_height = live_size[0] / output_ratio + + # make the crop + crop_left = bleed_pixels[0] + (live_size[0] - crop_width) * centering[0] + crop_top = bleed_pixels[1] + (live_size[1] - crop_height) * centering[1] + + crop = (crop_left, crop_top, crop_left + crop_width, crop_top + crop_height) + + # resize the image and return it + return image.resize(size, method, box=crop) + + +def flip(image): + """ + Flip the image vertically (top to bottom). + + :param image: The image to flip. + :return: An image. + """ + return image.transpose(Image.FLIP_TOP_BOTTOM) + + +def grayscale(image): + """ + Convert the image to grayscale. + + :param image: The image to convert. + :return: An image. + """ + return image.convert("L") + + +def invert(image): + """ + Invert (negate) the image. + + :param image: The image to invert. + :return: An image. + """ + lut = [] + for i in range(256): + lut.append(255 - i) + return _lut(image, lut) + + +def mirror(image): + """ + Flip image horizontally (left to right). + + :param image: The image to mirror. + :return: An image. + """ + return image.transpose(Image.FLIP_LEFT_RIGHT) + + +def posterize(image, bits): + """ + Reduce the number of bits for each color channel. + + :param image: The image to posterize. + :param bits: The number of bits to keep for each channel (1-8). + :return: An image. + """ + lut = [] + mask = ~(2 ** (8 - bits) - 1) + for i in range(256): + lut.append(i & mask) + return _lut(image, lut) + + +def solarize(image, threshold=128): + """ + Invert all pixel values above a threshold. + + :param image: The image to solarize. + :param threshold: All pixels above this greyscale level are inverted. + :return: An image. + """ + lut = [] + for i in range(256): + if i < threshold: + lut.append(i) + else: + lut.append(255 - i) + return _lut(image, lut) + + +def exif_transpose(image): + """ + If an image has an EXIF Orientation tag, return a new image that is + transposed accordingly. Otherwise, return a copy of the image. + + :param image: The image to transpose. + :return: An image. + """ + exif = image.getexif() + orientation = exif.get(0x0112) + method = { + 2: Image.FLIP_LEFT_RIGHT, + 3: Image.ROTATE_180, + 4: Image.FLIP_TOP_BOTTOM, + 5: Image.TRANSPOSE, + 6: Image.ROTATE_270, + 7: Image.TRANSVERSE, + 8: Image.ROTATE_90, + }.get(orientation) + if method is not None: + transposed_image = image.transpose(method) + del exif[0x0112] + transposed_image.info["exif"] = exif.tobytes() + return transposed_image + return image.copy() diff --git a/venv/lib/python3.11/site-packages/PIL/ImagePalette.py b/venv/lib/python3.11/site-packages/PIL/ImagePalette.py new file mode 100644 index 00000000..d0604112 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImagePalette.py @@ -0,0 +1,221 @@ +# +# The Python Imaging Library. +# $Id$ +# +# image palette object +# +# History: +# 1996-03-11 fl Rewritten. +# 1997-01-03 fl Up and running. +# 1997-08-23 fl Added load hack +# 2001-04-16 fl Fixed randint shadow bug in random() +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import array + +from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile + + +class ImagePalette: + """ + Color palette for palette mapped images + + :param mode: The mode to use for the Palette. See: + :ref:`concept-modes`. Defaults to "RGB" + :param palette: An optional palette. If given, it must be a bytearray, + an array or a list of ints between 0-255 and of length ``size`` + times the number of colors in ``mode``. The list must be aligned + by channel (All R values must be contiguous in the list before G + and B values.) Defaults to 0 through 255 per channel. + :param size: An optional palette size. If given, it cannot be equal to + or greater than 256. Defaults to 0. + """ + + def __init__(self, mode="RGB", palette=None, size=0): + self.mode = mode + self.rawmode = None # if set, palette contains raw data + self.palette = palette or bytearray(range(256)) * len(self.mode) + self.colors = {} + self.dirty = None + if (size == 0 and len(self.mode) * 256 != len(self.palette)) or ( + size != 0 and size != len(self.palette) + ): + raise ValueError("wrong palette size") + + def copy(self): + new = ImagePalette() + + new.mode = self.mode + new.rawmode = self.rawmode + if self.palette is not None: + new.palette = self.palette[:] + new.colors = self.colors.copy() + new.dirty = self.dirty + + return new + + def getdata(self): + """ + Get palette contents in format suitable for the low-level + ``im.putpalette`` primitive. + + .. warning:: This method is experimental. + """ + if self.rawmode: + return self.rawmode, self.palette + return self.mode + ";L", self.tobytes() + + def tobytes(self): + """Convert palette to bytes. + + .. warning:: This method is experimental. + """ + if self.rawmode: + raise ValueError("palette contains raw palette data") + if isinstance(self.palette, bytes): + return self.palette + arr = array.array("B", self.palette) + if hasattr(arr, "tobytes"): + return arr.tobytes() + return arr.tostring() + + # Declare tostring as an alias for tobytes + tostring = tobytes + + def getcolor(self, color): + """Given an rgb tuple, allocate palette entry. + + .. warning:: This method is experimental. + """ + if self.rawmode: + raise ValueError("palette contains raw palette data") + if isinstance(color, tuple): + try: + return self.colors[color] + except KeyError as e: + # allocate new color slot + if isinstance(self.palette, bytes): + self.palette = bytearray(self.palette) + index = len(self.colors) + if index >= 256: + raise ValueError("cannot allocate more than 256 colors") from e + self.colors[color] = index + self.palette[index] = color[0] + self.palette[index + 256] = color[1] + self.palette[index + 512] = color[2] + self.dirty = 1 + return index + else: + raise ValueError(f"unknown color specifier: {repr(color)}") + + def save(self, fp): + """Save palette to text file. + + .. warning:: This method is experimental. + """ + if self.rawmode: + raise ValueError("palette contains raw palette data") + if isinstance(fp, str): + fp = open(fp, "w") + fp.write("# Palette\n") + fp.write(f"# Mode: {self.mode}\n") + for i in range(256): + fp.write(f"{i}") + for j in range(i * len(self.mode), (i + 1) * len(self.mode)): + try: + fp.write(f" {self.palette[j]}") + except IndexError: + fp.write(" 0") + fp.write("\n") + fp.close() + + +# -------------------------------------------------------------------- +# Internal + + +def raw(rawmode, data): + palette = ImagePalette() + palette.rawmode = rawmode + palette.palette = data + palette.dirty = 1 + return palette + + +# -------------------------------------------------------------------- +# Factories + + +def make_linear_lut(black, white): + lut = [] + if black == 0: + for i in range(256): + lut.append(white * i // 255) + else: + raise NotImplementedError # FIXME + return lut + + +def make_gamma_lut(exp): + lut = [] + for i in range(256): + lut.append(int(((i / 255.0) ** exp) * 255.0 + 0.5)) + return lut + + +def negative(mode="RGB"): + palette = list(range(256)) + palette.reverse() + return ImagePalette(mode, palette * len(mode)) + + +def random(mode="RGB"): + from random import randint + + palette = [] + for i in range(256 * len(mode)): + palette.append(randint(0, 255)) + return ImagePalette(mode, palette) + + +def sepia(white="#fff0c0"): + r, g, b = ImageColor.getrgb(white) + r = make_linear_lut(0, r) + g = make_linear_lut(0, g) + b = make_linear_lut(0, b) + return ImagePalette("RGB", r + g + b) + + +def wedge(mode="RGB"): + return ImagePalette(mode, list(range(256)) * len(mode)) + + +def load(filename): + + # FIXME: supports GIMP gradients only + + with open(filename, "rb") as fp: + + for paletteHandler in [ + GimpPaletteFile.GimpPaletteFile, + GimpGradientFile.GimpGradientFile, + PaletteFile.PaletteFile, + ]: + try: + fp.seek(0) + lut = paletteHandler(fp).getpalette() + if lut: + break + except (SyntaxError, ValueError): + # import traceback + # traceback.print_exc() + pass + else: + raise OSError("cannot load palette") + + return lut # data, rawmode diff --git a/venv/lib/python3.11/site-packages/PIL/ImagePath.py b/venv/lib/python3.11/site-packages/PIL/ImagePath.py new file mode 100644 index 00000000..3d3538c9 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImagePath.py @@ -0,0 +1,19 @@ +# +# The Python Imaging Library +# $Id$ +# +# path interface +# +# History: +# 1996-11-04 fl Created +# 2002-04-14 fl Added documentation stub class +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +from . import Image + +Path = Image.core.path diff --git a/venv/lib/python3.11/site-packages/PIL/ImageQt.py b/venv/lib/python3.11/site-packages/PIL/ImageQt.py new file mode 100644 index 00000000..64f07be1 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageQt.py @@ -0,0 +1,202 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a simple Qt image interface. +# +# history: +# 2006-06-03 fl: created +# 2006-06-04 fl: inherit from QImage instead of wrapping it +# 2006-06-05 fl: removed toimage helper; move string support to ImageQt +# 2013-11-13 fl: add support for Qt5 (aurelien.ballier@cyclonit.com) +# +# Copyright (c) 2006 by Secret Labs AB +# Copyright (c) 2006 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import sys +from io import BytesIO + +from . import Image +from ._util import isPath + +qt_versions = [ + ["side6", "PySide6"], + ["5", "PyQt5"], + ["side2", "PySide2"], +] + +# If a version has already been imported, attempt it first +qt_versions.sort(key=lambda qt_version: qt_version[1] in sys.modules, reverse=True) +for qt_version, qt_module in qt_versions: + try: + if qt_module == "PySide6": + from PySide6.QtCore import QBuffer, QIODevice + from PySide6.QtGui import QImage, QPixmap, qRgba + elif qt_module == "PyQt5": + from PyQt5.QtCore import QBuffer, QIODevice + from PyQt5.QtGui import QImage, QPixmap, qRgba + elif qt_module == "PySide2": + from PySide2.QtCore import QBuffer, QIODevice + from PySide2.QtGui import QImage, QPixmap, qRgba + except (ImportError, RuntimeError): + continue + qt_is_installed = True + break +else: + qt_is_installed = False + qt_version = None + + +def rgb(r, g, b, a=255): + """(Internal) Turns an RGB color into a Qt compatible color integer.""" + # use qRgb to pack the colors, and then turn the resulting long + # into a negative integer with the same bitpattern. + return qRgba(r, g, b, a) & 0xFFFFFFFF + + +def fromqimage(im): + """ + :param im: A PIL Image object, or a file name + (given either as Python string or a PyQt string object) + """ + buffer = QBuffer() + buffer.open(QIODevice.ReadWrite) + # preserve alpha channel with png + # otherwise ppm is more friendly with Image.open + if im.hasAlphaChannel(): + im.save(buffer, "png") + else: + im.save(buffer, "ppm") + + b = BytesIO() + b.write(buffer.data()) + buffer.close() + b.seek(0) + + return Image.open(b) + + +def fromqpixmap(im): + return fromqimage(im) + # buffer = QBuffer() + # buffer.open(QIODevice.ReadWrite) + # # im.save(buffer) + # # What if png doesn't support some image features like animation? + # im.save(buffer, 'ppm') + # bytes_io = BytesIO() + # bytes_io.write(buffer.data()) + # buffer.close() + # bytes_io.seek(0) + # return Image.open(bytes_io) + + +def align8to32(bytes, width, mode): + """ + converts each scanline of data from 8 bit to 32 bit aligned + """ + + bits_per_pixel = {"1": 1, "L": 8, "P": 8}[mode] + + # calculate bytes per line and the extra padding if needed + bits_per_line = bits_per_pixel * width + full_bytes_per_line, remaining_bits_per_line = divmod(bits_per_line, 8) + bytes_per_line = full_bytes_per_line + (1 if remaining_bits_per_line else 0) + + extra_padding = -bytes_per_line % 4 + + # already 32 bit aligned by luck + if not extra_padding: + return bytes + + new_data = [] + for i in range(len(bytes) // bytes_per_line): + new_data.append( + bytes[i * bytes_per_line : (i + 1) * bytes_per_line] + + b"\x00" * extra_padding + ) + + return b"".join(new_data) + + +def _toqclass_helper(im): + data = None + colortable = None + + # handle filename, if given instead of image name + if hasattr(im, "toUtf8"): + # FIXME - is this really the best way to do this? + im = str(im.toUtf8(), "utf-8") + if isPath(im): + im = Image.open(im) + + if im.mode == "1": + format = QImage.Format_Mono + elif im.mode == "L": + format = QImage.Format_Indexed8 + colortable = [] + for i in range(256): + colortable.append(rgb(i, i, i)) + elif im.mode == "P": + format = QImage.Format_Indexed8 + colortable = [] + palette = im.getpalette() + for i in range(0, len(palette), 3): + colortable.append(rgb(*palette[i : i + 3])) + elif im.mode == "RGB": + data = im.tobytes("raw", "BGRX") + format = QImage.Format_RGB32 + elif im.mode == "RGBA": + data = im.tobytes("raw", "BGRA") + format = QImage.Format_ARGB32 + else: + raise ValueError(f"unsupported image mode {repr(im.mode)}") + + __data = data or align8to32(im.tobytes(), im.size[0], im.mode) + return {"data": __data, "im": im, "format": format, "colortable": colortable} + + +if qt_is_installed: + + class ImageQt(QImage): + def __init__(self, im): + """ + An PIL image wrapper for Qt. This is a subclass of PyQt's QImage + class. + + :param im: A PIL Image object, or a file name (given either as + Python string or a PyQt string object). + """ + im_data = _toqclass_helper(im) + # must keep a reference, or Qt will crash! + # All QImage constructors that take data operate on an existing + # buffer, so this buffer has to hang on for the life of the image. + # Fixes https://github.com/python-pillow/Pillow/issues/1370 + self.__data = im_data["data"] + super().__init__( + self.__data, + im_data["im"].size[0], + im_data["im"].size[1], + im_data["format"], + ) + if im_data["colortable"]: + self.setColorTable(im_data["colortable"]) + + +def toqimage(im): + return ImageQt(im) + + +def toqpixmap(im): + # # This doesn't work. For now using a dumb approach. + # im_data = _toqclass_helper(im) + # result = QPixmap(im_data['im'].size[0], im_data['im'].size[1]) + # result.loadFromData(im_data['data']) + # Fix some strange bug that causes + if im.mode == "RGB": + im = im.convert("RGBA") + + qimage = toqimage(im) + return QPixmap.fromImage(qimage) diff --git a/venv/lib/python3.11/site-packages/PIL/ImageSequence.py b/venv/lib/python3.11/site-packages/PIL/ImageSequence.py new file mode 100644 index 00000000..9df910a4 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageSequence.py @@ -0,0 +1,75 @@ +# +# The Python Imaging Library. +# $Id$ +# +# sequence support classes +# +# history: +# 1997-02-20 fl Created +# +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1997 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +## + + +class Iterator: + """ + This class implements an iterator object that can be used to loop + over an image sequence. + + You can use the ``[]`` operator to access elements by index. This operator + will raise an :py:exc:`IndexError` if you try to access a nonexistent + frame. + + :param im: An image object. + """ + + def __init__(self, im): + if not hasattr(im, "seek"): + raise AttributeError("im must have seek method") + self.im = im + self.position = getattr(self.im, "_min_frame", 0) + + def __getitem__(self, ix): + try: + self.im.seek(ix) + return self.im + except EOFError as e: + raise IndexError from e # end of sequence + + def __iter__(self): + return self + + def __next__(self): + try: + self.im.seek(self.position) + self.position += 1 + return self.im + except EOFError as e: + raise StopIteration from e + + +def all_frames(im, func=None): + """ + Applies a given function to all frames in an image or a list of images. + The frames are returned as a list of separate images. + + :param im: An image, or a list of images. + :param func: The function to apply to all of the image frames. + :returns: A list of images. + """ + if not isinstance(im, list): + im = [im] + + ims = [] + for imSequence in im: + current = imSequence.tell() + + ims += [im_frame.copy() for im_frame in Iterator(imSequence)] + + imSequence.seek(current) + return [func(im) for im in ims] if func else ims diff --git a/venv/lib/python3.11/site-packages/PIL/ImageShow.py b/venv/lib/python3.11/site-packages/PIL/ImageShow.py new file mode 100644 index 00000000..1ada8252 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageShow.py @@ -0,0 +1,236 @@ +# +# The Python Imaging Library. +# $Id$ +# +# im.show() drivers +# +# History: +# 2008-04-06 fl Created +# +# Copyright (c) Secret Labs AB 2008. +# +# See the README file for information on usage and redistribution. +# +import os +import shutil +import subprocess +import sys +import tempfile +from shlex import quote + +from PIL import Image + +_viewers = [] + + +def register(viewer, order=1): + """ + The :py:func:`register` function is used to register additional viewers. + + :param viewer: The viewer to be registered. + :param order: + Zero or a negative integer to prepend this viewer to the list, + a positive integer to append it. + """ + try: + if issubclass(viewer, Viewer): + viewer = viewer() + except TypeError: + pass # raised if viewer wasn't a class + if order > 0: + _viewers.append(viewer) + else: + _viewers.insert(0, viewer) + + +def show(image, title=None, **options): + r""" + Display a given image. + + :param image: An image object. + :param title: Optional title. Not all viewers can display the title. + :param \**options: Additional viewer options. + :returns: ``True`` if a suitable viewer was found, ``False`` otherwise. + """ + for viewer in _viewers: + if viewer.show(image, title=title, **options): + return 1 + return 0 + + +class Viewer: + """Base class for viewers.""" + + # main api + + def show(self, image, **options): + """ + The main function for displaying an image. + Converts the given image to the target format and displays it. + """ + + # save temporary image to disk + if not ( + image.mode in ("1", "RGBA") + or (self.format == "PNG" and image.mode in ("I;16", "LA")) + ): + base = Image.getmodebase(image.mode) + if image.mode != base: + image = image.convert(base) + + return self.show_image(image, **options) + + # hook methods + + format = None + """The format to convert the image into.""" + options = {} + """Additional options used to convert the image.""" + + def get_format(self, image): + """Return format name, or ``None`` to save as PGM/PPM.""" + return self.format + + def get_command(self, file, **options): + """ + Returns the command used to display the file. + Not implemented in the base class. + """ + raise NotImplementedError + + def save_image(self, image): + """Save to temporary file and return filename.""" + return image._dump(format=self.get_format(image), **self.options) + + def show_image(self, image, **options): + """Display the given image.""" + return self.show_file(self.save_image(image), **options) + + def show_file(self, file, **options): + """Display the given file.""" + os.system(self.get_command(file, **options)) + return 1 + + +# -------------------------------------------------------------------- + + +class WindowsViewer(Viewer): + """The default viewer on Windows is the default system application for PNG files.""" + + format = "PNG" + options = {"compress_level": 1} + + def get_command(self, file, **options): + return ( + f'start "Pillow" /WAIT "{file}" ' + "&& ping -n 2 127.0.0.1 >NUL " + f'&& del /f "{file}"' + ) + + +if sys.platform == "win32": + register(WindowsViewer) + + +class MacViewer(Viewer): + """The default viewer on MacOS using ``Preview.app``.""" + + format = "PNG" + options = {"compress_level": 1} + + def get_command(self, file, **options): + # on darwin open returns immediately resulting in the temp + # file removal while app is opening + command = "open -a Preview.app" + command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&" + return command + + def show_file(self, file, **options): + """Display given file""" + fd, path = tempfile.mkstemp() + with os.fdopen(fd, "w") as f: + f.write(file) + with open(path) as f: + subprocess.Popen( + ["im=$(cat); open -a Preview.app $im; sleep 20; rm -f $im"], + shell=True, + stdin=f, + ) + os.remove(path) + return 1 + + +if sys.platform == "darwin": + register(MacViewer) + + +class UnixViewer(Viewer): + format = "PNG" + options = {"compress_level": 1} + + def get_command(self, file, **options): + command = self.get_command_ex(file, **options)[0] + return f"({command} {quote(file)}; rm -f {quote(file)})&" + + def show_file(self, file, **options): + """Display given file""" + fd, path = tempfile.mkstemp() + with os.fdopen(fd, "w") as f: + f.write(file) + with open(path) as f: + command = self.get_command_ex(file, **options)[0] + subprocess.Popen( + ["im=$(cat);" + command + " $im; rm -f $im"], shell=True, stdin=f + ) + os.remove(path) + return 1 + + +class DisplayViewer(UnixViewer): + """The ImageMagick ``display`` command.""" + + def get_command_ex(self, file, **options): + command = executable = "display" + return command, executable + + +class EogViewer(UnixViewer): + """The GNOME Image Viewer ``eog`` command.""" + + def get_command_ex(self, file, **options): + command = executable = "eog" + return command, executable + + +class XVViewer(UnixViewer): + """ + The X Viewer ``xv`` command. + This viewer supports the ``title`` parameter. + """ + + def get_command_ex(self, file, title=None, **options): + # note: xv is pretty outdated. most modern systems have + # imagemagick's display command instead. + command = executable = "xv" + if title: + command += f" -name {quote(title)}" + return command, executable + + +if sys.platform not in ("win32", "darwin"): # unixoids + if shutil.which("display"): + register(DisplayViewer) + if shutil.which("eog"): + register(EogViewer) + if shutil.which("xv"): + register(XVViewer) + +if __name__ == "__main__": + + if len(sys.argv) < 2: + print("Syntax: python ImageShow.py imagefile [title]") + sys.exit() + + with Image.open(sys.argv[1]) as im: + print(show(im, *sys.argv[2:])) diff --git a/venv/lib/python3.11/site-packages/PIL/ImageStat.py b/venv/lib/python3.11/site-packages/PIL/ImageStat.py new file mode 100644 index 00000000..50bafc97 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageStat.py @@ -0,0 +1,147 @@ +# +# The Python Imaging Library. +# $Id$ +# +# global image statistics +# +# History: +# 1996-04-05 fl Created +# 1997-05-21 fl Added mask; added rms, var, stddev attributes +# 1997-08-05 fl Added median +# 1998-07-05 hk Fixed integer overflow error +# +# Notes: +# This class shows how to implement delayed evaluation of attributes. +# To get a certain value, simply access the corresponding attribute. +# The __getattr__ dispatcher takes care of the rest. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996-97. +# +# See the README file for information on usage and redistribution. +# + +import functools +import math +import operator + + +class Stat: + def __init__(self, image_or_list, mask=None): + try: + if mask: + self.h = image_or_list.histogram(mask) + else: + self.h = image_or_list.histogram() + except AttributeError: + self.h = image_or_list # assume it to be a histogram list + if not isinstance(self.h, list): + raise TypeError("first argument must be image or list") + self.bands = list(range(len(self.h) // 256)) + + def __getattr__(self, id): + """Calculate missing attribute""" + if id[:4] == "_get": + raise AttributeError(id) + # calculate missing attribute + v = getattr(self, "_get" + id)() + setattr(self, id, v) + return v + + def _getextrema(self): + """Get min/max values for each band in the image""" + + def minmax(histogram): + n = 255 + x = 0 + for i in range(256): + if histogram[i]: + n = min(n, i) + x = max(x, i) + return n, x # returns (255, 0) if there's no data in the histogram + + v = [] + for i in range(0, len(self.h), 256): + v.append(minmax(self.h[i:])) + return v + + def _getcount(self): + """Get total number of pixels in each layer""" + + v = [] + for i in range(0, len(self.h), 256): + v.append(functools.reduce(operator.add, self.h[i : i + 256])) + return v + + def _getsum(self): + """Get sum of all pixels in each layer""" + + v = [] + for i in range(0, len(self.h), 256): + layerSum = 0.0 + for j in range(256): + layerSum += j * self.h[i + j] + v.append(layerSum) + return v + + def _getsum2(self): + """Get squared sum of all pixels in each layer""" + + v = [] + for i in range(0, len(self.h), 256): + sum2 = 0.0 + for j in range(256): + sum2 += (j ** 2) * float(self.h[i + j]) + v.append(sum2) + return v + + def _getmean(self): + """Get average pixel level for each layer""" + + v = [] + for i in self.bands: + v.append(self.sum[i] / self.count[i]) + return v + + def _getmedian(self): + """Get median pixel level for each layer""" + + v = [] + for i in self.bands: + s = 0 + half = self.count[i] // 2 + b = i * 256 + for j in range(256): + s = s + self.h[b + j] + if s > half: + break + v.append(j) + return v + + def _getrms(self): + """Get RMS for each layer""" + + v = [] + for i in self.bands: + v.append(math.sqrt(self.sum2[i] / self.count[i])) + return v + + def _getvar(self): + """Get variance for each layer""" + + v = [] + for i in self.bands: + n = self.count[i] + v.append((self.sum2[i] - (self.sum[i] ** 2.0) / n) / n) + return v + + def _getstddev(self): + """Get standard deviation for each layer""" + + v = [] + for i in self.bands: + v.append(math.sqrt(self.var[i])) + return v + + +Global = Stat # compatibility diff --git a/venv/lib/python3.11/site-packages/PIL/ImageTk.py b/venv/lib/python3.11/site-packages/PIL/ImageTk.py new file mode 100644 index 00000000..62db7a71 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageTk.py @@ -0,0 +1,300 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a Tk display interface +# +# History: +# 96-04-08 fl Created +# 96-09-06 fl Added getimage method +# 96-11-01 fl Rewritten, removed image attribute and crop method +# 97-05-09 fl Use PyImagingPaste method instead of image type +# 97-05-12 fl Minor tweaks to match the IFUNC95 interface +# 97-05-17 fl Support the "pilbitmap" booster patch +# 97-06-05 fl Added file= and data= argument to image constructors +# 98-03-09 fl Added width and height methods to Image classes +# 98-07-02 fl Use default mode for "P" images without palette attribute +# 98-07-02 fl Explicitly destroy Tkinter image objects +# 99-07-24 fl Support multiple Tk interpreters (from Greg Couch) +# 99-07-26 fl Automatically hook into Tkinter (if possible) +# 99-08-15 fl Hook uses _imagingtk instead of _imaging +# +# Copyright (c) 1997-1999 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import tkinter +from io import BytesIO + +from . import Image + +# -------------------------------------------------------------------- +# Check for Tkinter interface hooks + +_pilbitmap_ok = None + + +def _pilbitmap_check(): + global _pilbitmap_ok + if _pilbitmap_ok is None: + try: + im = Image.new("1", (1, 1)) + tkinter.BitmapImage(data=f"PIL:{im.im.id}") + _pilbitmap_ok = 1 + except tkinter.TclError: + _pilbitmap_ok = 0 + return _pilbitmap_ok + + +def _get_image_from_kw(kw): + source = None + if "file" in kw: + source = kw.pop("file") + elif "data" in kw: + source = BytesIO(kw.pop("data")) + if source: + return Image.open(source) + + +# -------------------------------------------------------------------- +# PhotoImage + + +class PhotoImage: + """ + A Tkinter-compatible photo image. This can be used + everywhere Tkinter expects an image object. If the image is an RGBA + image, pixels having alpha 0 are treated as transparent. + + The constructor takes either a PIL image, or a mode and a size. + Alternatively, you can use the ``file`` or ``data`` options to initialize + the photo image object. + + :param image: Either a PIL image, or a mode string. If a mode string is + used, a size must also be given. + :param size: If the first argument is a mode string, this defines the size + of the image. + :keyword file: A filename to load the image from (using + ``Image.open(file)``). + :keyword data: An 8-bit string containing image data (as loaded from an + image file). + """ + + def __init__(self, image=None, size=None, **kw): + + # Tk compatibility: file or data + if image is None: + image = _get_image_from_kw(kw) + + if hasattr(image, "mode") and hasattr(image, "size"): + # got an image instead of a mode + mode = image.mode + if mode == "P": + # palette mapped data + image.load() + try: + mode = image.palette.mode + except AttributeError: + mode = "RGB" # default + size = image.size + kw["width"], kw["height"] = size + else: + mode = image + image = None + + if mode not in ["1", "L", "RGB", "RGBA"]: + mode = Image.getmodebase(mode) + + self.__mode = mode + self.__size = size + self.__photo = tkinter.PhotoImage(**kw) + self.tk = self.__photo.tk + if image: + self.paste(image) + + def __del__(self): + name = self.__photo.name + self.__photo.name = None + try: + self.__photo.tk.call("image", "delete", name) + except Exception: + pass # ignore internal errors + + def __str__(self): + """ + Get the Tkinter photo image identifier. This method is automatically + called by Tkinter whenever a PhotoImage object is passed to a Tkinter + method. + + :return: A Tkinter photo image identifier (a string). + """ + return str(self.__photo) + + def width(self): + """ + Get the width of the image. + + :return: The width, in pixels. + """ + return self.__size[0] + + def height(self): + """ + Get the height of the image. + + :return: The height, in pixels. + """ + return self.__size[1] + + def paste(self, im, box=None): + """ + Paste a PIL image into the photo image. Note that this can + be very slow if the photo image is displayed. + + :param im: A PIL image. The size must match the target region. If the + mode does not match, the image is converted to the mode of + the bitmap image. + :param box: A 4-tuple defining the left, upper, right, and lower pixel + coordinate. See :ref:`coordinate-system`. If None is given + instead of a tuple, all of the image is assumed. + """ + + # convert to blittable + im.load() + image = im.im + if image.isblock() and im.mode == self.__mode: + block = image + else: + block = image.new_block(self.__mode, im.size) + image.convert2(block, image) # convert directly between buffers + + tk = self.__photo.tk + + try: + tk.call("PyImagingPhoto", self.__photo, block.id) + except tkinter.TclError: + # activate Tkinter hook + try: + from . import _imagingtk + + try: + if hasattr(tk, "interp"): + # Required for PyPy, which always has CFFI installed + from cffi import FFI + + ffi = FFI() + + # PyPy is using an FFI CDATA element + # (Pdb) self.tk.interp + # + _imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1) + else: + _imagingtk.tkinit(tk.interpaddr(), 1) + except AttributeError: + _imagingtk.tkinit(id(tk), 0) + tk.call("PyImagingPhoto", self.__photo, block.id) + except (ImportError, AttributeError, tkinter.TclError): + raise # configuration problem; cannot attach to Tkinter + + +# -------------------------------------------------------------------- +# BitmapImage + + +class BitmapImage: + """ + A Tkinter-compatible bitmap image. This can be used everywhere Tkinter + expects an image object. + + The given image must have mode "1". Pixels having value 0 are treated as + transparent. Options, if any, are passed on to Tkinter. The most commonly + used option is ``foreground``, which is used to specify the color for the + non-transparent parts. See the Tkinter documentation for information on + how to specify colours. + + :param image: A PIL image. + """ + + def __init__(self, image=None, **kw): + + # Tk compatibility: file or data + if image is None: + image = _get_image_from_kw(kw) + + self.__mode = image.mode + self.__size = image.size + + if _pilbitmap_check(): + # fast way (requires the pilbitmap booster patch) + image.load() + kw["data"] = f"PIL:{image.im.id}" + self.__im = image # must keep a reference + else: + # slow but safe way + kw["data"] = image.tobitmap() + self.__photo = tkinter.BitmapImage(**kw) + + def __del__(self): + name = self.__photo.name + self.__photo.name = None + try: + self.__photo.tk.call("image", "delete", name) + except Exception: + pass # ignore internal errors + + def width(self): + """ + Get the width of the image. + + :return: The width, in pixels. + """ + return self.__size[0] + + def height(self): + """ + Get the height of the image. + + :return: The height, in pixels. + """ + return self.__size[1] + + def __str__(self): + """ + Get the Tkinter bitmap image identifier. This method is automatically + called by Tkinter whenever a BitmapImage object is passed to a Tkinter + method. + + :return: A Tkinter bitmap image identifier (a string). + """ + return str(self.__photo) + + +def getimage(photo): + """Copies the contents of a PhotoImage to a PIL image memory.""" + im = Image.new("RGBA", (photo.width(), photo.height())) + block = im.im + + photo.tk.call("PyImagingPhotoGet", photo, block.id) + + return im + + +def _show(image, title): + """Helper for the Image.show method.""" + + class UI(tkinter.Label): + def __init__(self, master, im): + if im.mode == "1": + self.image = BitmapImage(im, foreground="white", master=master) + else: + self.image = PhotoImage(im, master=master) + super().__init__(master, image=self.image, bg="black", bd=0) + + if not tkinter._default_root: + raise OSError("tkinter not initialized") + top = tkinter.Toplevel() + if title: + top.title(title) + UI(top, image).pack() diff --git a/venv/lib/python3.11/site-packages/PIL/ImageTransform.py b/venv/lib/python3.11/site-packages/PIL/ImageTransform.py new file mode 100644 index 00000000..77791ab7 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageTransform.py @@ -0,0 +1,102 @@ +# +# The Python Imaging Library. +# $Id$ +# +# transform wrappers +# +# History: +# 2002-04-08 fl Created +# +# Copyright (c) 2002 by Secret Labs AB +# Copyright (c) 2002 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from . import Image + + +class Transform(Image.ImageTransformHandler): + def __init__(self, data): + self.data = data + + def getdata(self): + return self.method, self.data + + def transform(self, size, image, **options): + # can be overridden + method, data = self.getdata() + return image.transform(size, method, data, **options) + + +class AffineTransform(Transform): + """ + Define an affine image transform. + + This function takes a 6-tuple (a, b, c, d, e, f) which contain the first + two rows from an affine transform matrix. For each pixel (x, y) in the + output image, the new value is taken from a position (a x + b y + c, + d x + e y + f) in the input image, rounded to nearest pixel. + + This function can be used to scale, translate, rotate, and shear the + original image. + + See :py:meth:`~PIL.Image.Image.transform` + + :param matrix: A 6-tuple (a, b, c, d, e, f) containing the first two rows + from an affine transform matrix. + """ + + method = Image.AFFINE + + +class ExtentTransform(Transform): + """ + Define a transform to extract a subregion from an image. + + Maps a rectangle (defined by two corners) from the image to a rectangle of + the given size. The resulting image will contain data sampled from between + the corners, such that (x0, y0) in the input image will end up at (0,0) in + the output image, and (x1, y1) at size. + + This method can be used to crop, stretch, shrink, or mirror an arbitrary + rectangle in the current image. It is slightly slower than crop, but about + as fast as a corresponding resize operation. + + See :py:meth:`~PIL.Image.Image.transform` + + :param bbox: A 4-tuple (x0, y0, x1, y1) which specifies two points in the + input image's coordinate system. See :ref:`coordinate-system`. + """ + + method = Image.EXTENT + + +class QuadTransform(Transform): + """ + Define a quad image transform. + + Maps a quadrilateral (a region defined by four corners) from the image to a + rectangle of the given size. + + See :py:meth:`~PIL.Image.Image.transform` + + :param xy: An 8-tuple (x0, y0, x1, y1, x2, y2, x3, y3) which contain the + upper left, lower left, lower right, and upper right corner of the + source quadrilateral. + """ + + method = Image.QUAD + + +class MeshTransform(Transform): + """ + Define a mesh image transform. A mesh transform consists of one or more + individual quad transforms. + + See :py:meth:`~PIL.Image.Image.transform` + + :param data: A list of (bbox, quad) tuples. + """ + + method = Image.MESH diff --git a/venv/lib/python3.11/site-packages/PIL/ImageWin.py b/venv/lib/python3.11/site-packages/PIL/ImageWin.py new file mode 100644 index 00000000..ca9b14c8 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImageWin.py @@ -0,0 +1,230 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a Windows DIB display interface +# +# History: +# 1996-05-20 fl Created +# 1996-09-20 fl Fixed subregion exposure +# 1997-09-21 fl Added draw primitive (for tzPrint) +# 2003-05-21 fl Added experimental Window/ImageWindow classes +# 2003-09-05 fl Added fromstring/tostring methods +# +# Copyright (c) Secret Labs AB 1997-2003. +# Copyright (c) Fredrik Lundh 1996-2003. +# +# See the README file for information on usage and redistribution. +# + +from . import Image + + +class HDC: + """ + Wraps an HDC integer. The resulting object can be passed to the + :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` + methods. + """ + + def __init__(self, dc): + self.dc = dc + + def __int__(self): + return self.dc + + +class HWND: + """ + Wraps an HWND integer. The resulting object can be passed to the + :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` + methods, instead of a DC. + """ + + def __init__(self, wnd): + self.wnd = wnd + + def __int__(self): + return self.wnd + + +class Dib: + """ + A Windows bitmap with the given mode and size. The mode can be one of "1", + "L", "P", or "RGB". + + If the display requires a palette, this constructor creates a suitable + palette and associates it with the image. For an "L" image, 128 greylevels + are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together + with 20 greylevels. + + To make sure that palettes work properly under Windows, you must call the + ``palette`` method upon certain events from Windows. + + :param image: Either a PIL image, or a mode string. If a mode string is + used, a size must also be given. The mode can be one of "1", + "L", "P", or "RGB". + :param size: If the first argument is a mode string, this + defines the size of the image. + """ + + def __init__(self, image, size=None): + if hasattr(image, "mode") and hasattr(image, "size"): + mode = image.mode + size = image.size + else: + mode = image + image = None + if mode not in ["1", "L", "P", "RGB"]: + mode = Image.getmodebase(mode) + self.image = Image.core.display(mode, size) + self.mode = mode + self.size = size + if image: + self.paste(image) + + def expose(self, handle): + """ + Copy the bitmap contents to a device context. + + :param handle: Device context (HDC), cast to a Python integer, or an + HDC or HWND instance. In PythonWin, you can use + ``CDC.GetHandleAttrib()`` to get a suitable handle. + """ + if isinstance(handle, HWND): + dc = self.image.getdc(handle) + try: + result = self.image.expose(dc) + finally: + self.image.releasedc(handle, dc) + else: + result = self.image.expose(handle) + return result + + def draw(self, handle, dst, src=None): + """ + Same as expose, but allows you to specify where to draw the image, and + what part of it to draw. + + The destination and source areas are given as 4-tuple rectangles. If + the source is omitted, the entire image is copied. If the source and + the destination have different sizes, the image is resized as + necessary. + """ + if not src: + src = (0, 0) + self.size + if isinstance(handle, HWND): + dc = self.image.getdc(handle) + try: + result = self.image.draw(dc, dst, src) + finally: + self.image.releasedc(handle, dc) + else: + result = self.image.draw(handle, dst, src) + return result + + def query_palette(self, handle): + """ + Installs the palette associated with the image in the given device + context. + + This method should be called upon **QUERYNEWPALETTE** and + **PALETTECHANGED** events from Windows. If this method returns a + non-zero value, one or more display palette entries were changed, and + the image should be redrawn. + + :param handle: Device context (HDC), cast to a Python integer, or an + HDC or HWND instance. + :return: A true value if one or more entries were changed (this + indicates that the image should be redrawn). + """ + if isinstance(handle, HWND): + handle = self.image.getdc(handle) + try: + result = self.image.query_palette(handle) + finally: + self.image.releasedc(handle, handle) + else: + result = self.image.query_palette(handle) + return result + + def paste(self, im, box=None): + """ + Paste a PIL image into the bitmap image. + + :param im: A PIL image. The size must match the target region. + If the mode does not match, the image is converted to the + mode of the bitmap image. + :param box: A 4-tuple defining the left, upper, right, and + lower pixel coordinate. See :ref:`coordinate-system`. If + None is given instead of a tuple, all of the image is + assumed. + """ + im.load() + if self.mode != im.mode: + im = im.convert(self.mode) + if box: + self.image.paste(im.im, box) + else: + self.image.paste(im.im) + + def frombytes(self, buffer): + """ + Load display memory contents from byte data. + + :param buffer: A buffer containing display data (usually + data returned from :py:func:`~PIL.ImageWin.Dib.tobytes`) + """ + return self.image.frombytes(buffer) + + def tobytes(self): + """ + Copy display memory contents to bytes object. + + :return: A bytes object containing display data. + """ + return self.image.tobytes() + + +class Window: + """Create a Window with the given title size.""" + + def __init__(self, title="PIL", width=None, height=None): + self.hwnd = Image.core.createwindow( + title, self.__dispatcher, width or 0, height or 0 + ) + + def __dispatcher(self, action, *args): + return getattr(self, "ui_handle_" + action)(*args) + + def ui_handle_clear(self, dc, x0, y0, x1, y1): + pass + + def ui_handle_damage(self, x0, y0, x1, y1): + pass + + def ui_handle_destroy(self): + pass + + def ui_handle_repair(self, dc, x0, y0, x1, y1): + pass + + def ui_handle_resize(self, width, height): + pass + + def mainloop(self): + Image.core.eventloop() + + +class ImageWindow(Window): + """Create an image window which displays the given image.""" + + def __init__(self, image, title="PIL"): + if not isinstance(image, Dib): + image = Dib(image) + self.image = image + width, height = image.size + super().__init__(title, width=width, height=height) + + def ui_handle_repair(self, dc, x0, y0, x1, y1): + self.image.draw(dc, (x0, y0, x1, y1)) diff --git a/venv/lib/python3.11/site-packages/PIL/ImtImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/ImtImagePlugin.py new file mode 100644 index 00000000..21ffd747 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/ImtImagePlugin.py @@ -0,0 +1,93 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IM Tools support for PIL +# +# history: +# 1996-05-27 fl Created (read 8-bit images only) +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.2) +# +# Copyright (c) Secret Labs AB 1997-2001. +# Copyright (c) Fredrik Lundh 1996-2001. +# +# See the README file for information on usage and redistribution. +# + + +import re + +from . import Image, ImageFile + +# +# -------------------------------------------------------------------- + +field = re.compile(br"([a-z]*) ([^ \r\n]*)") + + +## +# Image plugin for IM Tools images. + + +class ImtImageFile(ImageFile.ImageFile): + + format = "IMT" + format_description = "IM Tools" + + def _open(self): + + # Quick rejection: if there's not a LF among the first + # 100 bytes, this is (probably) not a text header. + + if b"\n" not in self.fp.read(100): + raise SyntaxError("not an IM file") + self.fp.seek(0) + + xsize = ysize = 0 + + while True: + + s = self.fp.read(1) + if not s: + break + + if s == b"\x0C": + + # image data begins + self.tile = [ + ("raw", (0, 0) + self.size, self.fp.tell(), (self.mode, 0, 1)) + ] + + break + + else: + + # read key/value pair + # FIXME: dangerous, may read whole file + s = s + self.fp.readline() + if len(s) == 1 or len(s) > 100: + break + if s[0] == ord(b"*"): + continue # comment + + m = field.match(s) + if not m: + break + k, v = m.group(1, 2) + if k == "width": + xsize = int(v) + self._size = xsize, ysize + elif k == "height": + ysize = int(v) + self._size = xsize, ysize + elif k == "pixel" and v == "n8": + self.mode = "L" + + +# +# -------------------------------------------------------------------- + +Image.register_open(ImtImageFile.format, ImtImageFile) + +# +# no extension registered (".im" is simply too common) diff --git a/venv/lib/python3.11/site-packages/PIL/IptcImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/IptcImagePlugin.py new file mode 100644 index 00000000..0bbe5066 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/IptcImagePlugin.py @@ -0,0 +1,230 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IPTC/NAA file handling +# +# history: +# 1995-10-01 fl Created +# 1998-03-09 fl Cleaned up and added to PIL +# 2002-06-18 fl Added getiptcinfo helper +# +# Copyright (c) Secret Labs AB 1997-2002. +# Copyright (c) Fredrik Lundh 1995. +# +# See the README file for information on usage and redistribution. +# +import os +import tempfile + +from . import Image, ImageFile +from ._binary import i8 +from ._binary import i16be as i16 +from ._binary import i32be as i32 +from ._binary import o8 + +COMPRESSION = {1: "raw", 5: "jpeg"} + +PAD = o8(0) * 4 + + +# +# Helpers + + +def i(c): + return i32((PAD + c)[-4:]) + + +def dump(c): + for i in c: + print("%02x" % i8(i), end=" ") + print() + + +## +# Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields +# from TIFF and JPEG files, use the getiptcinfo function. + + +class IptcImageFile(ImageFile.ImageFile): + + format = "IPTC" + format_description = "IPTC/NAA" + + def getint(self, key): + return i(self.info[key]) + + def field(self): + # + # get a IPTC field header + s = self.fp.read(5) + if not len(s): + return None, 0 + + tag = s[1], s[2] + + # syntax + if s[0] != 0x1C or tag[0] < 1 or tag[0] > 9: + raise SyntaxError("invalid IPTC/NAA file") + + # field size + size = s[3] + if size > 132: + raise OSError("illegal field length in IPTC/NAA file") + elif size == 128: + size = 0 + elif size > 128: + size = i(self.fp.read(size - 128)) + else: + size = i16(s, 3) + + return tag, size + + def _open(self): + + # load descriptive fields + while True: + offset = self.fp.tell() + tag, size = self.field() + if not tag or tag == (8, 10): + break + if size: + tagdata = self.fp.read(size) + else: + tagdata = None + if tag in self.info: + if isinstance(self.info[tag], list): + self.info[tag].append(tagdata) + else: + self.info[tag] = [self.info[tag], tagdata] + else: + self.info[tag] = tagdata + + # mode + layers = i8(self.info[(3, 60)][0]) + component = i8(self.info[(3, 60)][1]) + if (3, 65) in self.info: + id = i8(self.info[(3, 65)][0]) - 1 + else: + id = 0 + if layers == 1 and not component: + self.mode = "L" + elif layers == 3 and component: + self.mode = "RGB"[id] + elif layers == 4 and component: + self.mode = "CMYK"[id] + + # size + self._size = self.getint((3, 20)), self.getint((3, 30)) + + # compression + try: + compression = COMPRESSION[self.getint((3, 120))] + except KeyError as e: + raise OSError("Unknown IPTC image compression") from e + + # tile + if tag == (8, 10): + self.tile = [ + ("iptc", (compression, offset), (0, 0, self.size[0], self.size[1])) + ] + + def load(self): + + if len(self.tile) != 1 or self.tile[0][0] != "iptc": + return ImageFile.ImageFile.load(self) + + type, tile, box = self.tile[0] + + encoding, offset = tile + + self.fp.seek(offset) + + # Copy image data to temporary file + o_fd, outfile = tempfile.mkstemp(text=False) + o = os.fdopen(o_fd) + if encoding == "raw": + # To simplify access to the extracted file, + # prepend a PPM header + o.write("P5\n%d %d\n255\n" % self.size) + while True: + type, size = self.field() + if type != (8, 10): + break + while size > 0: + s = self.fp.read(min(size, 8192)) + if not s: + break + o.write(s) + size -= len(s) + o.close() + + try: + with Image.open(outfile) as _im: + _im.load() + self.im = _im.im + finally: + try: + os.unlink(outfile) + except OSError: + pass + + +Image.register_open(IptcImageFile.format, IptcImageFile) + +Image.register_extension(IptcImageFile.format, ".iim") + + +def getiptcinfo(im): + """ + Get IPTC information from TIFF, JPEG, or IPTC file. + + :param im: An image containing IPTC data. + :returns: A dictionary containing IPTC information, or None if + no IPTC information block was found. + """ + import io + + from . import JpegImagePlugin, TiffImagePlugin + + data = None + + if isinstance(im, IptcImageFile): + # return info dictionary right away + return im.info + + elif isinstance(im, JpegImagePlugin.JpegImageFile): + # extract the IPTC/NAA resource + photoshop = im.info.get("photoshop") + if photoshop: + data = photoshop.get(0x0404) + + elif isinstance(im, TiffImagePlugin.TiffImageFile): + # get raw data from the IPTC/NAA tag (PhotoShop tags the data + # as 4-byte integers, so we cannot use the get method...) + try: + data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK] + except (AttributeError, KeyError): + pass + + if data is None: + return None # no properties + + # create an IptcImagePlugin object without initializing it + class FakeImage: + pass + + im = FakeImage() + im.__class__ = IptcImageFile + + # parse the IPTC information chunk + im.info = {} + im.fp = io.BytesIO(data) + + try: + im._open() + except (IndexError, KeyError): + pass # expected failure + + return im.info diff --git a/venv/lib/python3.11/site-packages/PIL/Jpeg2KImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/Jpeg2KImagePlugin.py new file mode 100644 index 00000000..0b0d433d --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/Jpeg2KImagePlugin.py @@ -0,0 +1,314 @@ +# +# The Python Imaging Library +# $Id$ +# +# JPEG2000 file handling +# +# History: +# 2014-03-12 ajh Created +# +# Copyright (c) 2014 Coriolis Systems Limited +# Copyright (c) 2014 Alastair Houghton +# +# See the README file for information on usage and redistribution. +# +import io +import os +import struct + +from . import Image, ImageFile + + +def _parse_codestream(fp): + """Parse the JPEG 2000 codestream to extract the size and component + count from the SIZ marker segment, returning a PIL (size, mode) tuple.""" + + hdr = fp.read(2) + lsiz = struct.unpack(">H", hdr)[0] + siz = hdr + fp.read(lsiz - 2) + lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, _, _, _, _, csiz = struct.unpack_from( + ">HHIIIIIIIIH", siz + ) + ssiz = [None] * csiz + xrsiz = [None] * csiz + yrsiz = [None] * csiz + for i in range(csiz): + ssiz[i], xrsiz[i], yrsiz[i] = struct.unpack_from(">BBB", siz, 36 + 3 * i) + + size = (xsiz - xosiz, ysiz - yosiz) + if csiz == 1: + if (yrsiz[0] & 0x7F) > 8: + mode = "I;16" + else: + mode = "L" + elif csiz == 2: + mode = "LA" + elif csiz == 3: + mode = "RGB" + elif csiz == 4: + mode = "RGBA" + else: + mode = None + + return (size, mode) + + +def _parse_jp2_header(fp): + """Parse the JP2 header box to extract size, component count and + color space information, returning a (size, mode, mimetype) tuple.""" + + # Find the JP2 header box + header = None + mimetype = None + while True: + lbox, tbox = struct.unpack(">I4s", fp.read(8)) + if lbox == 1: + lbox = struct.unpack(">Q", fp.read(8))[0] + hlen = 16 + else: + hlen = 8 + + if lbox < hlen: + raise SyntaxError("Invalid JP2 header length") + + if tbox == b"jp2h": + header = fp.read(lbox - hlen) + break + elif tbox == b"ftyp": + if fp.read(4) == b"jpx ": + mimetype = "image/jpx" + fp.seek(lbox - hlen - 4, os.SEEK_CUR) + else: + fp.seek(lbox - hlen, os.SEEK_CUR) + + if header is None: + raise SyntaxError("could not find JP2 header") + + size = None + mode = None + bpc = None + nc = None + + hio = io.BytesIO(header) + while True: + lbox, tbox = struct.unpack(">I4s", hio.read(8)) + if lbox == 1: + lbox = struct.unpack(">Q", hio.read(8))[0] + hlen = 16 + else: + hlen = 8 + + content = hio.read(lbox - hlen) + + if tbox == b"ihdr": + height, width, nc, bpc, c, unkc, ipr = struct.unpack(">IIHBBBB", content) + size = (width, height) + if unkc: + if nc == 1 and (bpc & 0x7F) > 8: + mode = "I;16" + elif nc == 1: + mode = "L" + elif nc == 2: + mode = "LA" + elif nc == 3: + mode = "RGB" + elif nc == 4: + mode = "RGBA" + break + elif tbox == b"colr": + meth, prec, approx = struct.unpack_from(">BBB", content) + if meth == 1: + cs = struct.unpack_from(">I", content, 3)[0] + if cs == 16: # sRGB + if nc == 1 and (bpc & 0x7F) > 8: + mode = "I;16" + elif nc == 1: + mode = "L" + elif nc == 3: + mode = "RGB" + elif nc == 4: + mode = "RGBA" + break + elif cs == 17: # grayscale + if nc == 1 and (bpc & 0x7F) > 8: + mode = "I;16" + elif nc == 1: + mode = "L" + elif nc == 2: + mode = "LA" + break + elif cs == 18: # sYCC + if nc == 3: + mode = "RGB" + elif nc == 4: + mode = "RGBA" + break + + if size is None or mode is None: + raise SyntaxError("Malformed jp2 header") + + return (size, mode, mimetype) + + +## +# Image plugin for JPEG2000 images. + + +class Jpeg2KImageFile(ImageFile.ImageFile): + format = "JPEG2000" + format_description = "JPEG 2000 (ISO 15444)" + + def _open(self): + sig = self.fp.read(4) + if sig == b"\xff\x4f\xff\x51": + self.codec = "j2k" + self._size, self.mode = _parse_codestream(self.fp) + else: + sig = sig + self.fp.read(8) + + if sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a": + self.codec = "jp2" + header = _parse_jp2_header(self.fp) + self._size, self.mode, self.custom_mimetype = header + else: + raise SyntaxError("not a JPEG 2000 file") + + if self.size is None or self.mode is None: + raise SyntaxError("unable to determine size/mode") + + self._reduce = 0 + self.layers = 0 + + fd = -1 + length = -1 + + try: + fd = self.fp.fileno() + length = os.fstat(fd).st_size + except Exception: + fd = -1 + try: + pos = self.fp.tell() + self.fp.seek(0, io.SEEK_END) + length = self.fp.tell() + self.fp.seek(pos) + except Exception: + length = -1 + + self.tile = [ + ( + "jpeg2k", + (0, 0) + self.size, + 0, + (self.codec, self._reduce, self.layers, fd, length), + ) + ] + + @property + def reduce(self): + # https://github.com/python-pillow/Pillow/issues/4343 found that the + # new Image 'reduce' method was shadowed by this plugin's 'reduce' + # property. This attempts to allow for both scenarios + return self._reduce or super().reduce + + @reduce.setter + def reduce(self, value): + self._reduce = value + + def load(self): + if self.tile and self._reduce: + power = 1 << self._reduce + adjust = power >> 1 + self._size = ( + int((self.size[0] + adjust) / power), + int((self.size[1] + adjust) / power), + ) + + # Update the reduce and layers settings + t = self.tile[0] + t3 = (t[3][0], self._reduce, self.layers, t[3][3], t[3][4]) + self.tile = [(t[0], (0, 0) + self.size, t[2], t3)] + + return ImageFile.ImageFile.load(self) + + +def _accept(prefix): + return ( + prefix[:4] == b"\xff\x4f\xff\x51" + or prefix[:12] == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a" + ) + + +# ------------------------------------------------------------ +# Save support + + +def _save(im, fp, filename): + if filename.endswith(".j2k"): + kind = "j2k" + else: + kind = "jp2" + + # Get the keyword arguments + info = im.encoderinfo + + offset = info.get("offset", None) + tile_offset = info.get("tile_offset", None) + tile_size = info.get("tile_size", None) + quality_mode = info.get("quality_mode", "rates") + quality_layers = info.get("quality_layers", None) + if quality_layers is not None and not ( + isinstance(quality_layers, (list, tuple)) + and all( + [ + isinstance(quality_layer, (int, float)) + for quality_layer in quality_layers + ] + ) + ): + raise ValueError("quality_layers must be a sequence of numbers") + + num_resolutions = info.get("num_resolutions", 0) + cblk_size = info.get("codeblock_size", None) + precinct_size = info.get("precinct_size", None) + irreversible = info.get("irreversible", False) + progression = info.get("progression", "LRCP") + cinema_mode = info.get("cinema_mode", "no") + fd = -1 + + if hasattr(fp, "fileno"): + try: + fd = fp.fileno() + except Exception: + fd = -1 + + im.encoderconfig = ( + offset, + tile_offset, + tile_size, + quality_mode, + quality_layers, + num_resolutions, + cblk_size, + precinct_size, + irreversible, + progression, + cinema_mode, + fd, + ) + + ImageFile._save(im, fp, [("jpeg2k", (0, 0) + im.size, 0, kind)]) + + +# ------------------------------------------------------------ +# Registry stuff + + +Image.register_open(Jpeg2KImageFile.format, Jpeg2KImageFile, _accept) +Image.register_save(Jpeg2KImageFile.format, _save) + +Image.register_extensions( + Jpeg2KImageFile.format, [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"] +) + +Image.register_mime(Jpeg2KImageFile.format, "image/jp2") diff --git a/venv/lib/python3.11/site-packages/PIL/JpegImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/JpegImagePlugin.py new file mode 100644 index 00000000..054495e6 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/JpegImagePlugin.py @@ -0,0 +1,805 @@ +# +# The Python Imaging Library. +# $Id$ +# +# JPEG (JFIF) file handling +# +# See "Digital Compression and Coding of Continuous-Tone Still Images, +# Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1) +# +# History: +# 1995-09-09 fl Created +# 1995-09-13 fl Added full parser +# 1996-03-25 fl Added hack to use the IJG command line utilities +# 1996-05-05 fl Workaround Photoshop 2.5 CMYK polarity bug +# 1996-05-28 fl Added draft support, JFIF version (0.1) +# 1996-12-30 fl Added encoder options, added progression property (0.2) +# 1997-08-27 fl Save mode 1 images as BW (0.3) +# 1998-07-12 fl Added YCbCr to draft and save methods (0.4) +# 1998-10-19 fl Don't hang on files using 16-bit DQT's (0.4.1) +# 2001-04-16 fl Extract DPI settings from JFIF files (0.4.2) +# 2002-07-01 fl Skip pad bytes before markers; identify Exif files (0.4.3) +# 2003-04-25 fl Added experimental EXIF decoder (0.5) +# 2003-06-06 fl Added experimental EXIF GPSinfo decoder +# 2003-09-13 fl Extract COM markers +# 2009-09-06 fl Added icc_profile support (from Florian Hoech) +# 2009-03-06 fl Changed CMYK handling; always use Adobe polarity (0.6) +# 2009-03-08 fl Added subsampling support (from Justin Huff). +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# +import array +import io +import os +import struct +import subprocess +import sys +import tempfile +import warnings + +from . import Image, ImageFile, TiffImagePlugin +from ._binary import i16be as i16 +from ._binary import i32be as i32 +from ._binary import o8 +from .JpegPresets import presets + +# +# Parser + + +def Skip(self, marker): + n = i16(self.fp.read(2)) - 2 + ImageFile._safe_read(self.fp, n) + + +def APP(self, marker): + # + # Application marker. Store these in the APP dictionary. + # Also look for well-known application markers. + + n = i16(self.fp.read(2)) - 2 + s = ImageFile._safe_read(self.fp, n) + + app = "APP%d" % (marker & 15) + + self.app[app] = s # compatibility + self.applist.append((app, s)) + + if marker == 0xFFE0 and s[:4] == b"JFIF": + # extract JFIF information + self.info["jfif"] = version = i16(s, 5) # version + self.info["jfif_version"] = divmod(version, 256) + # extract JFIF properties + try: + jfif_unit = s[7] + jfif_density = i16(s, 8), i16(s, 10) + except Exception: + pass + else: + if jfif_unit == 1: + self.info["dpi"] = jfif_density + self.info["jfif_unit"] = jfif_unit + self.info["jfif_density"] = jfif_density + elif marker == 0xFFE1 and s[:5] == b"Exif\0": + if "exif" not in self.info: + # extract EXIF information (incomplete) + self.info["exif"] = s # FIXME: value will change + elif marker == 0xFFE2 and s[:5] == b"FPXR\0": + # extract FlashPix information (incomplete) + self.info["flashpix"] = s # FIXME: value will change + elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0": + # Since an ICC profile can be larger than the maximum size of + # a JPEG marker (64K), we need provisions to split it into + # multiple markers. The format defined by the ICC specifies + # one or more APP2 markers containing the following data: + # Identifying string ASCII "ICC_PROFILE\0" (12 bytes) + # Marker sequence number 1, 2, etc (1 byte) + # Number of markers Total of APP2's used (1 byte) + # Profile data (remainder of APP2 data) + # Decoders should use the marker sequence numbers to + # reassemble the profile, rather than assuming that the APP2 + # markers appear in the correct sequence. + self.icclist.append(s) + elif marker == 0xFFED and s[:14] == b"Photoshop 3.0\x00": + # parse the image resource block + offset = 14 + photoshop = self.info.setdefault("photoshop", {}) + while s[offset : offset + 4] == b"8BIM": + try: + offset += 4 + # resource code + code = i16(s, offset) + offset += 2 + # resource name (usually empty) + name_len = s[offset] + # name = s[offset+1:offset+1+name_len] + offset += 1 + name_len + offset += offset & 1 # align + # resource data block + size = i32(s, offset) + offset += 4 + data = s[offset : offset + size] + if code == 0x03ED: # ResolutionInfo + data = { + "XResolution": i32(data, 0) / 65536, + "DisplayedUnitsX": i16(data, 4), + "YResolution": i32(data, 8) / 65536, + "DisplayedUnitsY": i16(data, 12), + } + photoshop[code] = data + offset += size + offset += offset & 1 # align + except struct.error: + break # insufficient data + + elif marker == 0xFFEE and s[:5] == b"Adobe": + self.info["adobe"] = i16(s, 5) + # extract Adobe custom properties + try: + adobe_transform = s[1] + except Exception: + pass + else: + self.info["adobe_transform"] = adobe_transform + elif marker == 0xFFE2 and s[:4] == b"MPF\0": + # extract MPO information + self.info["mp"] = s[4:] + # offset is current location minus buffer size + # plus constant header size + self.info["mpoffset"] = self.fp.tell() - n + 4 + + # If DPI isn't in JPEG header, fetch from EXIF + if "dpi" not in self.info and "exif" in self.info: + try: + exif = self.getexif() + resolution_unit = exif[0x0128] + x_resolution = exif[0x011A] + try: + dpi = float(x_resolution[0]) / x_resolution[1] + except TypeError: + dpi = x_resolution + if resolution_unit == 3: # cm + # 1 dpcm = 2.54 dpi + dpi *= 2.54 + self.info["dpi"] = int(dpi + 0.5), int(dpi + 0.5) + except (KeyError, SyntaxError, ValueError, ZeroDivisionError): + # SyntaxError for invalid/unreadable EXIF + # KeyError for dpi not included + # ZeroDivisionError for invalid dpi rational value + # ValueError for x_resolution[0] being an invalid float + self.info["dpi"] = 72, 72 + + +def COM(self, marker): + # + # Comment marker. Store these in the APP dictionary. + n = i16(self.fp.read(2)) - 2 + s = ImageFile._safe_read(self.fp, n) + + self.info["comment"] = s + self.app["COM"] = s # compatibility + self.applist.append(("COM", s)) + + +def SOF(self, marker): + # + # Start of frame marker. Defines the size and mode of the + # image. JPEG is colour blind, so we use some simple + # heuristics to map the number of layers to an appropriate + # mode. Note that this could be made a bit brighter, by + # looking for JFIF and Adobe APP markers. + + n = i16(self.fp.read(2)) - 2 + s = ImageFile._safe_read(self.fp, n) + self._size = i16(s, 3), i16(s, 1) + + self.bits = s[0] + if self.bits != 8: + raise SyntaxError(f"cannot handle {self.bits}-bit layers") + + self.layers = s[5] + if self.layers == 1: + self.mode = "L" + elif self.layers == 3: + self.mode = "RGB" + elif self.layers == 4: + self.mode = "CMYK" + else: + raise SyntaxError(f"cannot handle {self.layers}-layer images") + + if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]: + self.info["progressive"] = self.info["progression"] = 1 + + if self.icclist: + # fixup icc profile + self.icclist.sort() # sort by sequence number + if self.icclist[0][13] == len(self.icclist): + profile = [] + for p in self.icclist: + profile.append(p[14:]) + icc_profile = b"".join(profile) + else: + icc_profile = None # wrong number of fragments + self.info["icc_profile"] = icc_profile + self.icclist = [] + + for i in range(6, len(s), 3): + t = s[i : i + 3] + # 4-tuples: id, vsamp, hsamp, qtable + self.layer.append((t[0], t[1] // 16, t[1] & 15, t[2])) + + +def DQT(self, marker): + # + # Define quantization table. Note that there might be more + # than one table in each marker. + + # FIXME: The quantization tables can be used to estimate the + # compression quality. + + n = i16(self.fp.read(2)) - 2 + s = ImageFile._safe_read(self.fp, n) + while len(s): + v = s[0] + precision = 1 if (v // 16 == 0) else 2 # in bytes + qt_length = 1 + precision * 64 + if len(s) < qt_length: + raise SyntaxError("bad quantization table marker") + data = array.array("B" if precision == 1 else "H", s[1:qt_length]) + if sys.byteorder == "little" and precision > 1: + data.byteswap() # the values are always big-endian + self.quantization[v & 15] = data + s = s[qt_length:] + + +# +# JPEG marker table + +MARKER = { + 0xFFC0: ("SOF0", "Baseline DCT", SOF), + 0xFFC1: ("SOF1", "Extended Sequential DCT", SOF), + 0xFFC2: ("SOF2", "Progressive DCT", SOF), + 0xFFC3: ("SOF3", "Spatial lossless", SOF), + 0xFFC4: ("DHT", "Define Huffman table", Skip), + 0xFFC5: ("SOF5", "Differential sequential DCT", SOF), + 0xFFC6: ("SOF6", "Differential progressive DCT", SOF), + 0xFFC7: ("SOF7", "Differential spatial", SOF), + 0xFFC8: ("JPG", "Extension", None), + 0xFFC9: ("SOF9", "Extended sequential DCT (AC)", SOF), + 0xFFCA: ("SOF10", "Progressive DCT (AC)", SOF), + 0xFFCB: ("SOF11", "Spatial lossless DCT (AC)", SOF), + 0xFFCC: ("DAC", "Define arithmetic coding conditioning", Skip), + 0xFFCD: ("SOF13", "Differential sequential DCT (AC)", SOF), + 0xFFCE: ("SOF14", "Differential progressive DCT (AC)", SOF), + 0xFFCF: ("SOF15", "Differential spatial (AC)", SOF), + 0xFFD0: ("RST0", "Restart 0", None), + 0xFFD1: ("RST1", "Restart 1", None), + 0xFFD2: ("RST2", "Restart 2", None), + 0xFFD3: ("RST3", "Restart 3", None), + 0xFFD4: ("RST4", "Restart 4", None), + 0xFFD5: ("RST5", "Restart 5", None), + 0xFFD6: ("RST6", "Restart 6", None), + 0xFFD7: ("RST7", "Restart 7", None), + 0xFFD8: ("SOI", "Start of image", None), + 0xFFD9: ("EOI", "End of image", None), + 0xFFDA: ("SOS", "Start of scan", Skip), + 0xFFDB: ("DQT", "Define quantization table", DQT), + 0xFFDC: ("DNL", "Define number of lines", Skip), + 0xFFDD: ("DRI", "Define restart interval", Skip), + 0xFFDE: ("DHP", "Define hierarchical progression", SOF), + 0xFFDF: ("EXP", "Expand reference component", Skip), + 0xFFE0: ("APP0", "Application segment 0", APP), + 0xFFE1: ("APP1", "Application segment 1", APP), + 0xFFE2: ("APP2", "Application segment 2", APP), + 0xFFE3: ("APP3", "Application segment 3", APP), + 0xFFE4: ("APP4", "Application segment 4", APP), + 0xFFE5: ("APP5", "Application segment 5", APP), + 0xFFE6: ("APP6", "Application segment 6", APP), + 0xFFE7: ("APP7", "Application segment 7", APP), + 0xFFE8: ("APP8", "Application segment 8", APP), + 0xFFE9: ("APP9", "Application segment 9", APP), + 0xFFEA: ("APP10", "Application segment 10", APP), + 0xFFEB: ("APP11", "Application segment 11", APP), + 0xFFEC: ("APP12", "Application segment 12", APP), + 0xFFED: ("APP13", "Application segment 13", APP), + 0xFFEE: ("APP14", "Application segment 14", APP), + 0xFFEF: ("APP15", "Application segment 15", APP), + 0xFFF0: ("JPG0", "Extension 0", None), + 0xFFF1: ("JPG1", "Extension 1", None), + 0xFFF2: ("JPG2", "Extension 2", None), + 0xFFF3: ("JPG3", "Extension 3", None), + 0xFFF4: ("JPG4", "Extension 4", None), + 0xFFF5: ("JPG5", "Extension 5", None), + 0xFFF6: ("JPG6", "Extension 6", None), + 0xFFF7: ("JPG7", "Extension 7", None), + 0xFFF8: ("JPG8", "Extension 8", None), + 0xFFF9: ("JPG9", "Extension 9", None), + 0xFFFA: ("JPG10", "Extension 10", None), + 0xFFFB: ("JPG11", "Extension 11", None), + 0xFFFC: ("JPG12", "Extension 12", None), + 0xFFFD: ("JPG13", "Extension 13", None), + 0xFFFE: ("COM", "Comment", COM), +} + + +def _accept(prefix): + # Magic number was taken from https://en.wikipedia.org/wiki/JPEG + return prefix[0:3] == b"\xFF\xD8\xFF" + + +## +# Image plugin for JPEG and JFIF images. + + +class JpegImageFile(ImageFile.ImageFile): + + format = "JPEG" + format_description = "JPEG (ISO 10918)" + + def _open(self): + + s = self.fp.read(3) + + if not _accept(s): + raise SyntaxError("not a JPEG file") + s = b"\xFF" + + # Create attributes + self.bits = self.layers = 0 + + # JPEG specifics (internal) + self.layer = [] + self.huffman_dc = {} + self.huffman_ac = {} + self.quantization = {} + self.app = {} # compatibility + self.applist = [] + self.icclist = [] + + while True: + + i = s[0] + if i == 0xFF: + s = s + self.fp.read(1) + i = i16(s) + else: + # Skip non-0xFF junk + s = self.fp.read(1) + continue + + if i in MARKER: + name, description, handler = MARKER[i] + if handler is not None: + handler(self, i) + if i == 0xFFDA: # start of scan + rawmode = self.mode + if self.mode == "CMYK": + rawmode = "CMYK;I" # assume adobe conventions + self.tile = [("jpeg", (0, 0) + self.size, 0, (rawmode, ""))] + # self.__offset = self.fp.tell() + break + s = self.fp.read(1) + elif i == 0 or i == 0xFFFF: + # padded marker or junk; move on + s = b"\xff" + elif i == 0xFF00: # Skip extraneous data (escaped 0xFF) + s = self.fp.read(1) + else: + raise SyntaxError("no marker found") + + def load_read(self, read_bytes): + """ + internal: read more image data + For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker + so libjpeg can finish decoding + """ + s = self.fp.read(read_bytes) + + if not s and ImageFile.LOAD_TRUNCATED_IMAGES: + # Premature EOF. + # Pretend file is finished adding EOI marker + return b"\xFF\xD9" + + return s + + def draft(self, mode, size): + + if len(self.tile) != 1: + return + + # Protect from second call + if self.decoderconfig: + return + + d, e, o, a = self.tile[0] + scale = 1 + original_size = self.size + + if a[0] == "RGB" and mode in ["L", "YCbCr"]: + self.mode = mode + a = mode, "" + + if size: + scale = min(self.size[0] // size[0], self.size[1] // size[1]) + for s in [8, 4, 2, 1]: + if scale >= s: + break + e = ( + e[0], + e[1], + (e[2] - e[0] + s - 1) // s + e[0], + (e[3] - e[1] + s - 1) // s + e[1], + ) + self._size = ((self.size[0] + s - 1) // s, (self.size[1] + s - 1) // s) + scale = s + + self.tile = [(d, e, o, a)] + self.decoderconfig = (scale, 0) + + box = (0, 0, original_size[0] / scale, original_size[1] / scale) + return (self.mode, box) + + def load_djpeg(self): + + # ALTERNATIVE: handle JPEGs via the IJG command line utilities + + f, path = tempfile.mkstemp() + os.close(f) + if os.path.exists(self.filename): + subprocess.check_call(["djpeg", "-outfile", path, self.filename]) + else: + raise ValueError("Invalid Filename") + + try: + with Image.open(path) as _im: + _im.load() + self.im = _im.im + finally: + try: + os.unlink(path) + except OSError: + pass + + self.mode = self.im.mode + self._size = self.im.size + + self.tile = [] + + def _getexif(self): + return _getexif(self) + + def _getmp(self): + return _getmp(self) + + +def _getexif(self): + if "exif" not in self.info: + return None + return dict(self.getexif()) + + +def _getmp(self): + # Extract MP information. This method was inspired by the "highly + # experimental" _getexif version that's been in use for years now, + # itself based on the ImageFileDirectory class in the TIFF plugin. + + # The MP record essentially consists of a TIFF file embedded in a JPEG + # application marker. + try: + data = self.info["mp"] + except KeyError: + return None + file_contents = io.BytesIO(data) + head = file_contents.read(8) + endianness = ">" if head[:4] == b"\x4d\x4d\x00\x2a" else "<" + # process dictionary + try: + info = TiffImagePlugin.ImageFileDirectory_v2(head) + file_contents.seek(info.next) + info.load(file_contents) + mp = dict(info) + except Exception as e: + raise SyntaxError("malformed MP Index (unreadable directory)") from e + # it's an error not to have a number of images + try: + quant = mp[0xB001] + except KeyError as e: + raise SyntaxError("malformed MP Index (no number of images)") from e + # get MP entries + mpentries = [] + try: + rawmpentries = mp[0xB002] + for entrynum in range(0, quant): + unpackedentry = struct.unpack_from( + f"{endianness}LLLHH", rawmpentries, entrynum * 16 + ) + labels = ("Attribute", "Size", "DataOffset", "EntryNo1", "EntryNo2") + mpentry = dict(zip(labels, unpackedentry)) + mpentryattr = { + "DependentParentImageFlag": bool(mpentry["Attribute"] & (1 << 31)), + "DependentChildImageFlag": bool(mpentry["Attribute"] & (1 << 30)), + "RepresentativeImageFlag": bool(mpentry["Attribute"] & (1 << 29)), + "Reserved": (mpentry["Attribute"] & (3 << 27)) >> 27, + "ImageDataFormat": (mpentry["Attribute"] & (7 << 24)) >> 24, + "MPType": mpentry["Attribute"] & 0x00FFFFFF, + } + if mpentryattr["ImageDataFormat"] == 0: + mpentryattr["ImageDataFormat"] = "JPEG" + else: + raise SyntaxError("unsupported picture format in MPO") + mptypemap = { + 0x000000: "Undefined", + 0x010001: "Large Thumbnail (VGA Equivalent)", + 0x010002: "Large Thumbnail (Full HD Equivalent)", + 0x020001: "Multi-Frame Image (Panorama)", + 0x020002: "Multi-Frame Image: (Disparity)", + 0x020003: "Multi-Frame Image: (Multi-Angle)", + 0x030000: "Baseline MP Primary Image", + } + mpentryattr["MPType"] = mptypemap.get(mpentryattr["MPType"], "Unknown") + mpentry["Attribute"] = mpentryattr + mpentries.append(mpentry) + mp[0xB002] = mpentries + except KeyError as e: + raise SyntaxError("malformed MP Index (bad MP Entry)") from e + # Next we should try and parse the individual image unique ID list; + # we don't because I've never seen this actually used in a real MPO + # file and so can't test it. + return mp + + +# -------------------------------------------------------------------- +# stuff to save JPEG files + +RAWMODE = { + "1": "L", + "L": "L", + "RGB": "RGB", + "RGBX": "RGB", + "CMYK": "CMYK;I", # assume adobe conventions + "YCbCr": "YCbCr", +} + +# fmt: off +zigzag_index = ( + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63, +) + +samplings = { + (1, 1, 1, 1, 1, 1): 0, + (2, 1, 1, 1, 1, 1): 1, + (2, 2, 1, 1, 1, 1): 2, +} +# fmt: on + + +def convert_dict_qtables(qtables): + qtables = [qtables[key] for key in range(len(qtables)) if key in qtables] + for idx, table in enumerate(qtables): + qtables[idx] = [table[i] for i in zigzag_index] + return qtables + + +def get_sampling(im): + # There's no subsampling when images have only 1 layer + # (grayscale images) or when they are CMYK (4 layers), + # so set subsampling to the default value. + # + # NOTE: currently Pillow can't encode JPEG to YCCK format. + # If YCCK support is added in the future, subsampling code will have + # to be updated (here and in JpegEncode.c) to deal with 4 layers. + if not hasattr(im, "layers") or im.layers in (1, 4): + return -1 + sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3] + return samplings.get(sampling, -1) + + +def _save(im, fp, filename): + + try: + rawmode = RAWMODE[im.mode] + except KeyError as e: + raise OSError(f"cannot write mode {im.mode} as JPEG") from e + + info = im.encoderinfo + + dpi = [round(x) for x in info.get("dpi", (0, 0))] + + quality = info.get("quality", -1) + subsampling = info.get("subsampling", -1) + qtables = info.get("qtables") + + if quality == "keep": + quality = -1 + subsampling = "keep" + qtables = "keep" + elif quality in presets: + preset = presets[quality] + quality = -1 + subsampling = preset.get("subsampling", -1) + qtables = preset.get("quantization") + elif not isinstance(quality, int): + raise ValueError("Invalid quality setting") + else: + if subsampling in presets: + subsampling = presets[subsampling].get("subsampling", -1) + if isinstance(qtables, str) and qtables in presets: + qtables = presets[qtables].get("quantization") + + if subsampling == "4:4:4": + subsampling = 0 + elif subsampling == "4:2:2": + subsampling = 1 + elif subsampling == "4:2:0": + subsampling = 2 + elif subsampling == "4:1:1": + # For compatibility. Before Pillow 4.3, 4:1:1 actually meant 4:2:0. + # Set 4:2:0 if someone is still using that value. + subsampling = 2 + elif subsampling == "keep": + if im.format != "JPEG": + raise ValueError("Cannot use 'keep' when original image is not a JPEG") + subsampling = get_sampling(im) + + def validate_qtables(qtables): + if qtables is None: + return qtables + if isinstance(qtables, str): + try: + lines = [ + int(num) + for line in qtables.splitlines() + for num in line.split("#", 1)[0].split() + ] + except ValueError as e: + raise ValueError("Invalid quantization table") from e + else: + qtables = [lines[s : s + 64] for s in range(0, len(lines), 64)] + if isinstance(qtables, (tuple, list, dict)): + if isinstance(qtables, dict): + qtables = convert_dict_qtables(qtables) + elif isinstance(qtables, tuple): + qtables = list(qtables) + if not (0 < len(qtables) < 5): + raise ValueError("None or too many quantization tables") + for idx, table in enumerate(qtables): + try: + if len(table) != 64: + raise TypeError + table = array.array("H", table) + except TypeError as e: + raise ValueError("Invalid quantization table") from e + else: + qtables[idx] = list(table) + return qtables + + if qtables == "keep": + if im.format != "JPEG": + raise ValueError("Cannot use 'keep' when original image is not a JPEG") + qtables = getattr(im, "quantization", None) + qtables = validate_qtables(qtables) + + extra = b"" + + icc_profile = info.get("icc_profile") + if icc_profile: + ICC_OVERHEAD_LEN = 14 + MAX_BYTES_IN_MARKER = 65533 + MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN + markers = [] + while icc_profile: + markers.append(icc_profile[:MAX_DATA_BYTES_IN_MARKER]) + icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:] + i = 1 + for marker in markers: + size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker)) + extra += ( + b"\xFF\xE2" + + size + + b"ICC_PROFILE\0" + + o8(i) + + o8(len(markers)) + + marker + ) + i += 1 + + # "progressive" is the official name, but older documentation + # says "progression" + # FIXME: issue a warning if the wrong form is used (post-1.1.7) + progressive = info.get("progressive", False) or info.get("progression", False) + + optimize = info.get("optimize", False) + + exif = info.get("exif", b"") + if isinstance(exif, Image.Exif): + exif = exif.tobytes() + + # get keyword arguments + im.encoderconfig = ( + quality, + progressive, + info.get("smooth", 0), + optimize, + info.get("streamtype", 0), + dpi[0], + dpi[1], + subsampling, + qtables, + extra, + exif, + ) + + # if we optimize, libjpeg needs a buffer big enough to hold the whole image + # in a shot. Guessing on the size, at im.size bytes. (raw pixel size is + # channels*size, this is a value that's been used in a django patch. + # https://github.com/matthewwithanm/django-imagekit/issues/50 + bufsize = 0 + if optimize or progressive: + # CMYK can be bigger + if im.mode == "CMYK": + bufsize = 4 * im.size[0] * im.size[1] + # keep sets quality to -1, but the actual value may be high. + elif quality >= 95 or quality == -1: + bufsize = 2 * im.size[0] * im.size[1] + else: + bufsize = im.size[0] * im.size[1] + + # The EXIF info needs to be written as one block, + APP1, + one spare byte. + # Ensure that our buffer is big enough. Same with the icc_profile block. + bufsize = max(ImageFile.MAXBLOCK, bufsize, len(exif) + 5, len(extra) + 1) + + ImageFile._save(im, fp, [("jpeg", (0, 0) + im.size, 0, rawmode)], bufsize) + + +def _save_cjpeg(im, fp, filename): + # ALTERNATIVE: handle JPEGs via the IJG command line utilities. + tempfile = im._dump() + subprocess.check_call(["cjpeg", "-outfile", filename, tempfile]) + try: + os.unlink(tempfile) + except OSError: + pass + + +## +# Factory for making JPEG and MPO instances +def jpeg_factory(fp=None, filename=None): + im = JpegImageFile(fp, filename) + try: + mpheader = im._getmp() + if mpheader[45057] > 1: + # It's actually an MPO + from .MpoImagePlugin import MpoImageFile + + # Don't reload everything, just convert it. + im = MpoImageFile.adopt(im, mpheader) + except (TypeError, IndexError): + # It is really a JPEG + pass + except SyntaxError: + warnings.warn( + "Image appears to be a malformed MPO file, it will be " + "interpreted as a base JPEG file" + ) + return im + + +# --------------------------------------------------------------------- +# Registry stuff + +Image.register_open(JpegImageFile.format, jpeg_factory, _accept) +Image.register_save(JpegImageFile.format, _save) + +Image.register_extensions(JpegImageFile.format, [".jfif", ".jpe", ".jpg", ".jpeg"]) + +Image.register_mime(JpegImageFile.format, "image/jpeg") diff --git a/venv/lib/python3.11/site-packages/PIL/JpegPresets.py b/venv/lib/python3.11/site-packages/PIL/JpegPresets.py new file mode 100644 index 00000000..79d10ebb --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/JpegPresets.py @@ -0,0 +1,248 @@ +""" +JPEG quality settings equivalent to the Photoshop settings. +Can be used when saving JPEG files. + +The following presets are available by default: +``web_low``, ``web_medium``, ``web_high``, ``web_very_high``, ``web_maximum``, +``low``, ``medium``, ``high``, ``maximum``. +More presets can be added to the :py:data:`presets` dict if needed. + +To apply the preset, specify:: + + quality="preset_name" + +To apply only the quantization table:: + + qtables="preset_name" + +To apply only the subsampling setting:: + + subsampling="preset_name" + +Example:: + + im.save("image_name.jpg", quality="web_high") + +Subsampling +----------- + +Subsampling is the practice of encoding images by implementing less resolution +for chroma information than for luma information. +(ref.: https://en.wikipedia.org/wiki/Chroma_subsampling) + +Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and +4:2:0. + +You can get the subsampling of a JPEG with the +:func:`.JpegImagePlugin.get_sampling` function. + +In JPEG compressed data a JPEG marker is used instead of an EXIF tag. +(ref.: https://www.exiv2.org/tags.html) + + +Quantization tables +------------------- + +They are values use by the DCT (Discrete cosine transform) to remove +*unnecessary* information from the image (the lossy part of the compression). +(ref.: https://en.wikipedia.org/wiki/Quantization_matrix#Quantization_matrices, +https://en.wikipedia.org/wiki/JPEG#Quantization) + +You can get the quantization tables of a JPEG with:: + + im.quantization + +This will return a dict with a number of arrays. You can pass this dict +directly as the qtables argument when saving a JPEG. + +The tables format between im.quantization and quantization in presets differ in +3 ways: + +1. The base container of the preset is a list with sublists instead of dict. + dict[0] -> list[0], dict[1] -> list[1], ... +2. Each table in a preset is a list instead of an array. +3. The zigzag order is remove in the preset (needed by libjpeg >= 6a). + +You can convert the dict format to the preset format with the +:func:`.JpegImagePlugin.convert_dict_qtables()` function. + +Libjpeg ref.: +https://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/libjpeg-3.html + +""" + +# fmt: off +presets = { + 'web_low': {'subsampling': 2, # "4:2:0" + 'quantization': [ + [20, 16, 25, 39, 50, 46, 62, 68, + 16, 18, 23, 38, 38, 53, 65, 68, + 25, 23, 31, 38, 53, 65, 68, 68, + 39, 38, 38, 53, 65, 68, 68, 68, + 50, 38, 53, 65, 68, 68, 68, 68, + 46, 53, 65, 68, 68, 68, 68, 68, + 62, 65, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68], + [21, 25, 32, 38, 54, 68, 68, 68, + 25, 28, 24, 38, 54, 68, 68, 68, + 32, 24, 32, 43, 66, 68, 68, 68, + 38, 38, 43, 53, 68, 68, 68, 68, + 54, 54, 66, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68] + ]}, + 'web_medium': {'subsampling': 2, # "4:2:0" + 'quantization': [ + [16, 11, 11, 16, 23, 27, 31, 30, + 11, 12, 12, 15, 20, 23, 23, 30, + 11, 12, 13, 16, 23, 26, 35, 47, + 16, 15, 16, 23, 26, 37, 47, 64, + 23, 20, 23, 26, 39, 51, 64, 64, + 27, 23, 26, 37, 51, 64, 64, 64, + 31, 23, 35, 47, 64, 64, 64, 64, + 30, 30, 47, 64, 64, 64, 64, 64], + [17, 15, 17, 21, 20, 26, 38, 48, + 15, 19, 18, 17, 20, 26, 35, 43, + 17, 18, 20, 22, 26, 30, 46, 53, + 21, 17, 22, 28, 30, 39, 53, 64, + 20, 20, 26, 30, 39, 48, 64, 64, + 26, 26, 30, 39, 48, 63, 64, 64, + 38, 35, 46, 53, 64, 64, 64, 64, + 48, 43, 53, 64, 64, 64, 64, 64] + ]}, + 'web_high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [6, 4, 4, 6, 9, 11, 12, 16, + 4, 5, 5, 6, 8, 10, 12, 12, + 4, 5, 5, 6, 10, 12, 14, 19, + 6, 6, 6, 11, 12, 15, 19, 28, + 9, 8, 10, 12, 16, 20, 27, 31, + 11, 10, 12, 15, 20, 27, 31, 31, + 12, 12, 14, 19, 27, 31, 31, 31, + 16, 12, 19, 28, 31, 31, 31, 31], + [7, 7, 13, 24, 26, 31, 31, 31, + 7, 12, 16, 21, 31, 31, 31, 31, + 13, 16, 17, 31, 31, 31, 31, 31, + 24, 21, 31, 31, 31, 31, 31, 31, + 26, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31] + ]}, + 'web_very_high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 4, 5, 7, 9, + 2, 2, 2, 4, 5, 7, 9, 12, + 3, 3, 4, 5, 8, 10, 12, 12, + 4, 4, 5, 7, 10, 12, 12, 12, + 5, 5, 7, 9, 12, 12, 12, 12, + 6, 6, 9, 12, 12, 12, 12, 12], + [3, 3, 5, 9, 13, 15, 15, 15, + 3, 4, 6, 11, 14, 12, 12, 12, + 5, 6, 9, 14, 12, 12, 12, 12, + 9, 11, 14, 12, 12, 12, 12, 12, + 13, 14, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'web_maximum': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 2, 2, + 1, 1, 1, 1, 1, 2, 2, 3, + 1, 1, 1, 1, 2, 2, 3, 3, + 1, 1, 1, 2, 2, 3, 3, 3, + 1, 1, 2, 2, 3, 3, 3, 3], + [1, 1, 1, 2, 2, 3, 3, 3, + 1, 1, 1, 2, 3, 3, 3, 3, + 1, 1, 1, 3, 3, 3, 3, 3, + 2, 2, 3, 3, 3, 3, 3, 3, + 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3] + ]}, + 'low': {'subsampling': 2, # "4:2:0" + 'quantization': [ + [18, 14, 14, 21, 30, 35, 34, 17, + 14, 16, 16, 19, 26, 23, 12, 12, + 14, 16, 17, 21, 23, 12, 12, 12, + 21, 19, 21, 23, 12, 12, 12, 12, + 30, 26, 23, 12, 12, 12, 12, 12, + 35, 23, 12, 12, 12, 12, 12, 12, + 34, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12], + [20, 19, 22, 27, 20, 20, 17, 17, + 19, 25, 23, 14, 14, 12, 12, 12, + 22, 23, 14, 14, 12, 12, 12, 12, + 27, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'medium': {'subsampling': 2, # "4:2:0" + 'quantization': [ + [12, 8, 8, 12, 17, 21, 24, 17, + 8, 9, 9, 11, 15, 19, 12, 12, + 8, 9, 10, 12, 19, 12, 12, 12, + 12, 11, 12, 21, 12, 12, 12, 12, + 17, 15, 19, 12, 12, 12, 12, 12, + 21, 19, 12, 12, 12, 12, 12, 12, + 24, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12], + [13, 11, 13, 16, 20, 20, 17, 17, + 11, 14, 14, 14, 14, 12, 12, 12, + 13, 14, 14, 14, 12, 12, 12, 12, + 16, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [6, 4, 4, 6, 9, 11, 12, 16, + 4, 5, 5, 6, 8, 10, 12, 12, + 4, 5, 5, 6, 10, 12, 12, 12, + 6, 6, 6, 11, 12, 12, 12, 12, + 9, 8, 10, 12, 12, 12, 12, 12, + 11, 10, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 16, 12, 12, 12, 12, 12, 12, 12], + [7, 7, 13, 24, 20, 20, 17, 17, + 7, 12, 16, 14, 14, 12, 12, 12, + 13, 16, 14, 14, 12, 12, 12, 12, + 24, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'maximum': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 4, 5, 7, 9, + 2, 2, 2, 4, 5, 7, 9, 12, + 3, 3, 4, 5, 8, 10, 12, 12, + 4, 4, 5, 7, 10, 12, 12, 12, + 5, 5, 7, 9, 12, 12, 12, 12, + 6, 6, 9, 12, 12, 12, 12, 12], + [3, 3, 5, 9, 13, 15, 15, 15, + 3, 4, 6, 10, 14, 12, 12, 12, + 5, 6, 9, 14, 12, 12, 12, 12, + 9, 10, 14, 12, 12, 12, 12, 12, + 13, 14, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12] + ]}, +} +# fmt: on diff --git a/venv/lib/python3.11/site-packages/PIL/McIdasImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/McIdasImagePlugin.py new file mode 100644 index 00000000..cd047fe9 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/McIdasImagePlugin.py @@ -0,0 +1,75 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Basic McIdas support for PIL +# +# History: +# 1997-05-05 fl Created (8-bit images only) +# 2009-03-08 fl Added 16/32-bit support. +# +# Thanks to Richard Jones and Craig Swank for specs and samples. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +import struct + +from . import Image, ImageFile + + +def _accept(s): + return s[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04" + + +## +# Image plugin for McIdas area images. + + +class McIdasImageFile(ImageFile.ImageFile): + + format = "MCIDAS" + format_description = "McIdas area file" + + def _open(self): + + # parse area file directory + s = self.fp.read(256) + if not _accept(s) or len(s) != 256: + raise SyntaxError("not an McIdas area file") + + self.area_descriptor_raw = s + self.area_descriptor = w = [0] + list(struct.unpack("!64i", s)) + + # get mode + if w[11] == 1: + mode = rawmode = "L" + elif w[11] == 2: + # FIXME: add memory map support + mode = "I" + rawmode = "I;16B" + elif w[11] == 4: + # FIXME: add memory map support + mode = "I" + rawmode = "I;32B" + else: + raise SyntaxError("unsupported McIdas format") + + self.mode = mode + self._size = w[10], w[9] + + offset = w[34] + w[15] + stride = w[15] + w[10] * w[11] * w[14] + + self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride, 1))] + + +# -------------------------------------------------------------------- +# registry + +Image.register_open(McIdasImageFile.format, McIdasImageFile, _accept) + +# no default extension diff --git a/venv/lib/python3.11/site-packages/PIL/MicImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/MicImagePlugin.py new file mode 100644 index 00000000..2aed2603 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/MicImagePlugin.py @@ -0,0 +1,107 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Microsoft Image Composer support for PIL +# +# Notes: +# uses TiffImagePlugin.py to read the actual image streams +# +# History: +# 97-01-20 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + + +import olefile + +from . import Image, TiffImagePlugin + +# +# -------------------------------------------------------------------- + + +def _accept(prefix): + return prefix[:8] == olefile.MAGIC + + +## +# Image plugin for Microsoft's Image Composer file format. + + +class MicImageFile(TiffImagePlugin.TiffImageFile): + + format = "MIC" + format_description = "Microsoft Image Composer" + _close_exclusive_fp_after_loading = False + + def _open(self): + + # read the OLE directory and see if this is a likely + # to be a Microsoft Image Composer file + + try: + self.ole = olefile.OleFileIO(self.fp) + except OSError as e: + raise SyntaxError("not an MIC file; invalid OLE file") from e + + # find ACI subfiles with Image members (maybe not the + # best way to identify MIC files, but what the... ;-) + + self.images = [] + for path in self.ole.listdir(): + if path[1:] and path[0][-4:] == ".ACI" and path[1] == "Image": + self.images.append(path) + + # if we didn't find any images, this is probably not + # an MIC file. + if not self.images: + raise SyntaxError("not an MIC file; no image entries") + + self.__fp = self.fp + self.frame = None + self._n_frames = len(self.images) + self.is_animated = self._n_frames > 1 + + if len(self.images) > 1: + self.category = Image.CONTAINER + + self.seek(0) + + def seek(self, frame): + if not self._seek_check(frame): + return + try: + filename = self.images[frame] + except IndexError as e: + raise EOFError("no such frame") from e + + self.fp = self.ole.openstream(filename) + + TiffImagePlugin.TiffImageFile._open(self) + + self.frame = frame + + def tell(self): + return self.frame + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +# +# -------------------------------------------------------------------- + +Image.register_open(MicImageFile.format, MicImageFile, _accept) + +Image.register_extension(MicImageFile.format, ".mic") diff --git a/venv/lib/python3.11/site-packages/PIL/MpegImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/MpegImagePlugin.py new file mode 100644 index 00000000..a358dfdc --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/MpegImagePlugin.py @@ -0,0 +1,83 @@ +# +# The Python Imaging Library. +# $Id$ +# +# MPEG file handling +# +# History: +# 95-09-09 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995. +# +# See the README file for information on usage and redistribution. +# + + +from . import Image, ImageFile +from ._binary import i8 + +# +# Bitstream parser + + +class BitStream: + def __init__(self, fp): + self.fp = fp + self.bits = 0 + self.bitbuffer = 0 + + def next(self): + return i8(self.fp.read(1)) + + def peek(self, bits): + while self.bits < bits: + c = self.next() + if c < 0: + self.bits = 0 + continue + self.bitbuffer = (self.bitbuffer << 8) + c + self.bits += 8 + return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1 + + def skip(self, bits): + while self.bits < bits: + self.bitbuffer = (self.bitbuffer << 8) + i8(self.fp.read(1)) + self.bits += 8 + self.bits = self.bits - bits + + def read(self, bits): + v = self.peek(bits) + self.bits = self.bits - bits + return v + + +## +# Image plugin for MPEG streams. This plugin can identify a stream, +# but it cannot read it. + + +class MpegImageFile(ImageFile.ImageFile): + + format = "MPEG" + format_description = "MPEG" + + def _open(self): + + s = BitStream(self.fp) + + if s.read(32) != 0x1B3: + raise SyntaxError("not an MPEG file") + + self.mode = "RGB" + self._size = s.read(12), s.read(12) + + +# -------------------------------------------------------------------- +# Registry stuff + +Image.register_open(MpegImageFile.format, MpegImageFile) + +Image.register_extensions(MpegImageFile.format, [".mpg", ".mpeg"]) + +Image.register_mime(MpegImageFile.format, "video/mpeg") diff --git a/venv/lib/python3.11/site-packages/PIL/MpoImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/MpoImagePlugin.py new file mode 100644 index 00000000..575cc9c8 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/MpoImagePlugin.py @@ -0,0 +1,134 @@ +# +# The Python Imaging Library. +# $Id$ +# +# MPO file handling +# +# See "Multi-Picture Format" (CIPA DC-007-Translation 2009, Standard of the +# Camera & Imaging Products Association) +# +# The multi-picture object combines multiple JPEG images (with a modified EXIF +# data format) into a single file. While it can theoretically be used much like +# a GIF animation, it is commonly used to represent 3D photographs and is (as +# of this writing) the most commonly used format by 3D cameras. +# +# History: +# 2014-03-13 Feneric Created +# +# See the README file for information on usage and redistribution. +# + +from . import Image, ImageFile, JpegImagePlugin +from ._binary import i16be as i16 + + +def _accept(prefix): + return JpegImagePlugin._accept(prefix) + + +def _save(im, fp, filename): + # Note that we can only save the current frame at present + return JpegImagePlugin._save(im, fp, filename) + + +## +# Image plugin for MPO images. + + +class MpoImageFile(JpegImagePlugin.JpegImageFile): + + format = "MPO" + format_description = "MPO (CIPA DC-007)" + _close_exclusive_fp_after_loading = False + + def _open(self): + self.fp.seek(0) # prep the fp in order to pass the JPEG test + JpegImagePlugin.JpegImageFile._open(self) + self._after_jpeg_open() + + def _after_jpeg_open(self, mpheader=None): + self.mpinfo = mpheader if mpheader is not None else self._getmp() + self.n_frames = self.mpinfo[0xB001] + self.__mpoffsets = [ + mpent["DataOffset"] + self.info["mpoffset"] for mpent in self.mpinfo[0xB002] + ] + self.__mpoffsets[0] = 0 + # Note that the following assertion will only be invalid if something + # gets broken within JpegImagePlugin. + assert self.n_frames == len(self.__mpoffsets) + del self.info["mpoffset"] # no longer needed + self.is_animated = self.n_frames > 1 + self.__fp = self.fp # FIXME: hack + self.__fp.seek(self.__mpoffsets[0]) # get ready to read first frame + self.__frame = 0 + self.offset = 0 + # for now we can only handle reading and individual frame extraction + self.readonly = 1 + + def load_seek(self, pos): + self.__fp.seek(pos) + + def seek(self, frame): + if not self._seek_check(frame): + return + self.fp = self.__fp + self.offset = self.__mpoffsets[frame] + + self.fp.seek(self.offset + 2) # skip SOI marker + segment = self.fp.read(2) + if not segment: + raise ValueError("No data found for frame") + if i16(segment) == 0xFFE1: # APP1 + n = i16(self.fp.read(2)) - 2 + self.info["exif"] = ImageFile._safe_read(self.fp, n) + + exif = self.getexif() + if 40962 in exif and 40963 in exif: + self._size = (exif[40962], exif[40963]) + elif "exif" in self.info: + del self.info["exif"] + + self.tile = [("jpeg", (0, 0) + self.size, self.offset, (self.mode, ""))] + self.__frame = frame + + def tell(self): + return self.__frame + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + @staticmethod + def adopt(jpeg_instance, mpheader=None): + """ + Transform the instance of JpegImageFile into + an instance of MpoImageFile. + After the call, the JpegImageFile is extended + to be an MpoImageFile. + + This is essentially useful when opening a JPEG + file that reveals itself as an MPO, to avoid + double call to _open. + """ + jpeg_instance.__class__ = MpoImageFile + jpeg_instance._after_jpeg_open(mpheader) + return jpeg_instance + + +# --------------------------------------------------------------------- +# Registry stuff + +# Note that since MPO shares a factory with JPEG, we do not need to do a +# separate registration for it here. +# Image.register_open(MpoImageFile.format, +# JpegImagePlugin.jpeg_factory, _accept) +Image.register_save(MpoImageFile.format, _save) + +Image.register_extension(MpoImageFile.format, ".mpo") + +Image.register_mime(MpoImageFile.format, "image/mpo") diff --git a/venv/lib/python3.11/site-packages/PIL/MspImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/MspImagePlugin.py new file mode 100644 index 00000000..e1fdc1fd --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/MspImagePlugin.py @@ -0,0 +1,194 @@ +# +# The Python Imaging Library. +# +# MSP file handling +# +# This is the format used by the Paint program in Windows 1 and 2. +# +# History: +# 95-09-05 fl Created +# 97-01-03 fl Read/write MSP images +# 17-02-21 es Fixed RLE interpretation +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995-97. +# Copyright (c) Eric Soroos 2017. +# +# See the README file for information on usage and redistribution. +# +# More info on this format: https://archive.org/details/gg243631 +# Page 313: +# Figure 205. Windows Paint Version 1: "DanM" Format +# Figure 206. Windows Paint Version 2: "LinS" Format. Used in Windows V2.03 +# +# See also: http://www.fileformat.info/format/mspaint/egff.htm + +import io +import struct + +from . import Image, ImageFile +from ._binary import i16le as i16 +from ._binary import o16le as o16 + +# +# read MSP files + + +def _accept(prefix): + return prefix[:4] in [b"DanM", b"LinS"] + + +## +# Image plugin for Windows MSP images. This plugin supports both +# uncompressed (Windows 1.0). + + +class MspImageFile(ImageFile.ImageFile): + + format = "MSP" + format_description = "Windows Paint" + + def _open(self): + + # Header + s = self.fp.read(32) + if not _accept(s): + raise SyntaxError("not an MSP file") + + # Header checksum + checksum = 0 + for i in range(0, 32, 2): + checksum = checksum ^ i16(s, i) + if checksum != 0: + raise SyntaxError("bad MSP checksum") + + self.mode = "1" + self._size = i16(s, 4), i16(s, 6) + + if s[:4] == b"DanM": + self.tile = [("raw", (0, 0) + self.size, 32, ("1", 0, 1))] + else: + self.tile = [("MSP", (0, 0) + self.size, 32, None)] + + +class MspDecoder(ImageFile.PyDecoder): + # The algo for the MSP decoder is from + # http://www.fileformat.info/format/mspaint/egff.htm + # cc-by-attribution -- That page references is taken from the + # Encyclopedia of Graphics File Formats and is licensed by + # O'Reilly under the Creative Common/Attribution license + # + # For RLE encoded files, the 32byte header is followed by a scan + # line map, encoded as one 16bit word of encoded byte length per + # line. + # + # NOTE: the encoded length of the line can be 0. This was not + # handled in the previous version of this encoder, and there's no + # mention of how to handle it in the documentation. From the few + # examples I've seen, I've assumed that it is a fill of the + # background color, in this case, white. + # + # + # Pseudocode of the decoder: + # Read a BYTE value as the RunType + # If the RunType value is zero + # Read next byte as the RunCount + # Read the next byte as the RunValue + # Write the RunValue byte RunCount times + # If the RunType value is non-zero + # Use this value as the RunCount + # Read and write the next RunCount bytes literally + # + # e.g.: + # 0x00 03 ff 05 00 01 02 03 04 + # would yield the bytes: + # 0xff ff ff 00 01 02 03 04 + # + # which are then interpreted as a bit packed mode '1' image + + _pulls_fd = True + + def decode(self, buffer): + + img = io.BytesIO() + blank_line = bytearray((0xFF,) * ((self.state.xsize + 7) // 8)) + try: + self.fd.seek(32) + rowmap = struct.unpack_from( + f"<{self.state.ysize}H", self.fd.read(self.state.ysize * 2) + ) + except struct.error as e: + raise OSError("Truncated MSP file in row map") from e + + for x, rowlen in enumerate(rowmap): + try: + if rowlen == 0: + img.write(blank_line) + continue + row = self.fd.read(rowlen) + if len(row) != rowlen: + raise OSError( + "Truncated MSP file, expected %d bytes on row %s", (rowlen, x) + ) + idx = 0 + while idx < rowlen: + runtype = row[idx] + idx += 1 + if runtype == 0: + (runcount, runval) = struct.unpack_from("Bc", row, idx) + img.write(runval * runcount) + idx += 2 + else: + runcount = runtype + img.write(row[idx : idx + runcount]) + idx += runcount + + except struct.error as e: + raise OSError(f"Corrupted MSP file in row {x}") from e + + self.set_as_raw(img.getvalue(), ("1", 0, 1)) + + return 0, 0 + + +Image.register_decoder("MSP", MspDecoder) + + +# +# write MSP files (uncompressed only) + + +def _save(im, fp, filename): + + if im.mode != "1": + raise OSError(f"cannot write mode {im.mode} as MSP") + + # create MSP header + header = [0] * 16 + + header[0], header[1] = i16(b"Da"), i16(b"nM") # version 1 + header[2], header[3] = im.size + header[4], header[5] = 1, 1 + header[6], header[7] = 1, 1 + header[8], header[9] = im.size + + checksum = 0 + for h in header: + checksum = checksum ^ h + header[12] = checksum # FIXME: is this the right field? + + # header + for h in header: + fp.write(o16(h)) + + # image body + ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 32, ("1", 0, 1))]) + + +# +# registry + +Image.register_open(MspImageFile.format, MspImageFile, _accept) +Image.register_save(MspImageFile.format, _save) + +Image.register_extension(MspImageFile.format, ".msp") diff --git a/venv/lib/python3.11/site-packages/PIL/PSDraw.py b/venv/lib/python3.11/site-packages/PIL/PSDraw.py new file mode 100644 index 00000000..c1bd933d --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/PSDraw.py @@ -0,0 +1,235 @@ +# +# The Python Imaging Library +# $Id$ +# +# Simple PostScript graphics interface +# +# History: +# 1996-04-20 fl Created +# 1999-01-10 fl Added gsave/grestore to image method +# 2005-05-04 fl Fixed floating point issue in image (from Eric Etheridge) +# +# Copyright (c) 1997-2005 by Secret Labs AB. All rights reserved. +# Copyright (c) 1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +import sys + +from . import EpsImagePlugin + +## +# Simple PostScript graphics interface. + + +class PSDraw: + """ + Sets up printing to the given file. If ``fp`` is omitted, + :py:data:`sys.stdout` is assumed. + """ + + def __init__(self, fp=None): + if not fp: + fp = sys.stdout + self.fp = fp + + def _fp_write(self, to_write): + if self.fp == sys.stdout: + self.fp.write(to_write) + else: + self.fp.write(bytes(to_write, "UTF-8")) + + def begin_document(self, id=None): + """Set up printing of a document. (Write PostScript DSC header.)""" + # FIXME: incomplete + self._fp_write( + "%!PS-Adobe-3.0\n" + "save\n" + "/showpage { } def\n" + "%%EndComments\n" + "%%BeginDocument\n" + ) + # self._fp_write(ERROR_PS) # debugging! + self._fp_write(EDROFF_PS) + self._fp_write(VDI_PS) + self._fp_write("%%EndProlog\n") + self.isofont = {} + + def end_document(self): + """Ends printing. (Write PostScript DSC footer.)""" + self._fp_write("%%EndDocument\nrestore showpage\n%%End\n") + if hasattr(self.fp, "flush"): + self.fp.flush() + + def setfont(self, font, size): + """ + Selects which font to use. + + :param font: A PostScript font name + :param size: Size in points. + """ + if font not in self.isofont: + # reencode font + self._fp_write(f"/PSDraw-{font} ISOLatin1Encoding /{font} E\n") + self.isofont[font] = 1 + # rough + self._fp_write(f"/F0 {size} /PSDraw-{font} F\n") + + def line(self, xy0, xy1): + """ + Draws a line between the two points. Coordinates are given in + PostScript point coordinates (72 points per inch, (0, 0) is the lower + left corner of the page). + """ + self._fp_write("%d %d %d %d Vl\n" % (*xy0, *xy1)) + + def rectangle(self, box): + """ + Draws a rectangle. + + :param box: A 4-tuple of integers whose order and function is currently + undocumented. + + Hint: the tuple is passed into this format string: + + .. code-block:: python + + %d %d M %d %d 0 Vr\n + """ + self._fp_write("%d %d M %d %d 0 Vr\n" % box) + + def text(self, xy, text): + """ + Draws text at the given position. You must use + :py:meth:`~PIL.PSDraw.PSDraw.setfont` before calling this method. + """ + text = "\\(".join(text.split("(")) + text = "\\)".join(text.split(")")) + self._fp_write(f"{xy[0]} {xy[1]} M ({text}) S\n") + + def image(self, box, im, dpi=None): + """Draw a PIL image, centered in the given box.""" + # default resolution depends on mode + if not dpi: + if im.mode == "1": + dpi = 200 # fax + else: + dpi = 100 # greyscale + # image size (on paper) + x = im.size[0] * 72 / dpi + y = im.size[1] * 72 / dpi + # max allowed size + xmax = float(box[2] - box[0]) + ymax = float(box[3] - box[1]) + if x > xmax: + y = y * xmax / x + x = xmax + if y > ymax: + x = x * ymax / y + y = ymax + dx = (xmax - x) / 2 + box[0] + dy = (ymax - y) / 2 + box[1] + self._fp_write(f"gsave\n{dx:f} {dy:f} translate\n") + if (x, y) != im.size: + # EpsImagePlugin._save prints the image at (0,0,xsize,ysize) + sx = x / im.size[0] + sy = y / im.size[1] + self._fp_write(f"{sx:f} {sy:f} scale\n") + EpsImagePlugin._save(im, self.fp, None, 0) + self._fp_write("\ngrestore\n") + + +# -------------------------------------------------------------------- +# PostScript driver + +# +# EDROFF.PS -- PostScript driver for Edroff 2 +# +# History: +# 94-01-25 fl: created (edroff 2.04) +# +# Copyright (c) Fredrik Lundh 1994. +# + + +EDROFF_PS = """\ +/S { show } bind def +/P { moveto show } bind def +/M { moveto } bind def +/X { 0 rmoveto } bind def +/Y { 0 exch rmoveto } bind def +/E { findfont + dup maxlength dict begin + { + 1 index /FID ne { def } { pop pop } ifelse + } forall + /Encoding exch def + dup /FontName exch def + currentdict end definefont pop +} bind def +/F { findfont exch scalefont dup setfont + [ exch /setfont cvx ] cvx bind def +} bind def +""" + +# +# VDI.PS -- PostScript driver for VDI meta commands +# +# History: +# 94-01-25 fl: created (edroff 2.04) +# +# Copyright (c) Fredrik Lundh 1994. +# + +VDI_PS = """\ +/Vm { moveto } bind def +/Va { newpath arcn stroke } bind def +/Vl { moveto lineto stroke } bind def +/Vc { newpath 0 360 arc closepath } bind def +/Vr { exch dup 0 rlineto + exch dup neg 0 exch rlineto + exch neg 0 rlineto + 0 exch rlineto + 100 div setgray fill 0 setgray } bind def +/Tm matrix def +/Ve { Tm currentmatrix pop + translate scale newpath 0 0 .5 0 360 arc closepath + Tm setmatrix +} bind def +/Vf { currentgray exch setgray fill setgray } bind def +""" + +# +# ERROR.PS -- Error handler +# +# History: +# 89-11-21 fl: created (pslist 1.10) +# + +ERROR_PS = """\ +/landscape false def +/errorBUF 200 string def +/errorNL { currentpoint 10 sub exch pop 72 exch moveto } def +errordict begin /handleerror { + initmatrix /Courier findfont 10 scalefont setfont + newpath 72 720 moveto $error begin /newerror false def + (PostScript Error) show errorNL errorNL + (Error: ) show + /errorname load errorBUF cvs show errorNL errorNL + (Command: ) show + /command load dup type /stringtype ne { errorBUF cvs } if show + errorNL errorNL + (VMstatus: ) show + vmstatus errorBUF cvs show ( bytes available, ) show + errorBUF cvs show ( bytes used at level ) show + errorBUF cvs show errorNL errorNL + (Operand stargck: ) show errorNL /ostargck load { + dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL + } forall errorNL + (Execution stargck: ) show errorNL /estargck load { + dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL + } forall + end showpage +} def end +""" diff --git a/venv/lib/python3.11/site-packages/PIL/PaletteFile.py b/venv/lib/python3.11/site-packages/PIL/PaletteFile.py new file mode 100644 index 00000000..6ccaa1f5 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/PaletteFile.py @@ -0,0 +1,53 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read simple, teragon-style palette files +# +# History: +# 97-08-23 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +from ._binary import o8 + + +class PaletteFile: + """File handler for Teragon-style palette files.""" + + rawmode = "RGB" + + def __init__(self, fp): + + self.palette = [(i, i, i) for i in range(256)] + + while True: + + s = fp.readline() + + if not s: + break + if s[0:1] == b"#": + continue + if len(s) > 100: + raise SyntaxError("bad palette file") + + v = [int(x) for x in s.split()] + try: + [i, r, g, b] = v + except ValueError: + [i, r] = v + g = b = r + + if 0 <= i <= 255: + self.palette[i] = o8(r) + o8(g) + o8(b) + + self.palette = b"".join(self.palette) + + def getpalette(self): + + return self.palette, self.rawmode diff --git a/venv/lib/python3.11/site-packages/PIL/PalmImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/PalmImagePlugin.py new file mode 100644 index 00000000..700f10e3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/PalmImagePlugin.py @@ -0,0 +1,227 @@ +# +# The Python Imaging Library. +# $Id$ +# + +## +# Image plugin for Palm pixmap images (output only). +## + +from . import Image, ImageFile +from ._binary import o8 +from ._binary import o16be as o16b + +# fmt: off +_Palm8BitColormapValues = ( + (255, 255, 255), (255, 204, 255), (255, 153, 255), (255, 102, 255), + (255, 51, 255), (255, 0, 255), (255, 255, 204), (255, 204, 204), + (255, 153, 204), (255, 102, 204), (255, 51, 204), (255, 0, 204), + (255, 255, 153), (255, 204, 153), (255, 153, 153), (255, 102, 153), + (255, 51, 153), (255, 0, 153), (204, 255, 255), (204, 204, 255), + (204, 153, 255), (204, 102, 255), (204, 51, 255), (204, 0, 255), + (204, 255, 204), (204, 204, 204), (204, 153, 204), (204, 102, 204), + (204, 51, 204), (204, 0, 204), (204, 255, 153), (204, 204, 153), + (204, 153, 153), (204, 102, 153), (204, 51, 153), (204, 0, 153), + (153, 255, 255), (153, 204, 255), (153, 153, 255), (153, 102, 255), + (153, 51, 255), (153, 0, 255), (153, 255, 204), (153, 204, 204), + (153, 153, 204), (153, 102, 204), (153, 51, 204), (153, 0, 204), + (153, 255, 153), (153, 204, 153), (153, 153, 153), (153, 102, 153), + (153, 51, 153), (153, 0, 153), (102, 255, 255), (102, 204, 255), + (102, 153, 255), (102, 102, 255), (102, 51, 255), (102, 0, 255), + (102, 255, 204), (102, 204, 204), (102, 153, 204), (102, 102, 204), + (102, 51, 204), (102, 0, 204), (102, 255, 153), (102, 204, 153), + (102, 153, 153), (102, 102, 153), (102, 51, 153), (102, 0, 153), + (51, 255, 255), (51, 204, 255), (51, 153, 255), (51, 102, 255), + (51, 51, 255), (51, 0, 255), (51, 255, 204), (51, 204, 204), + (51, 153, 204), (51, 102, 204), (51, 51, 204), (51, 0, 204), + (51, 255, 153), (51, 204, 153), (51, 153, 153), (51, 102, 153), + (51, 51, 153), (51, 0, 153), (0, 255, 255), (0, 204, 255), + (0, 153, 255), (0, 102, 255), (0, 51, 255), (0, 0, 255), + (0, 255, 204), (0, 204, 204), (0, 153, 204), (0, 102, 204), + (0, 51, 204), (0, 0, 204), (0, 255, 153), (0, 204, 153), + (0, 153, 153), (0, 102, 153), (0, 51, 153), (0, 0, 153), + (255, 255, 102), (255, 204, 102), (255, 153, 102), (255, 102, 102), + (255, 51, 102), (255, 0, 102), (255, 255, 51), (255, 204, 51), + (255, 153, 51), (255, 102, 51), (255, 51, 51), (255, 0, 51), + (255, 255, 0), (255, 204, 0), (255, 153, 0), (255, 102, 0), + (255, 51, 0), (255, 0, 0), (204, 255, 102), (204, 204, 102), + (204, 153, 102), (204, 102, 102), (204, 51, 102), (204, 0, 102), + (204, 255, 51), (204, 204, 51), (204, 153, 51), (204, 102, 51), + (204, 51, 51), (204, 0, 51), (204, 255, 0), (204, 204, 0), + (204, 153, 0), (204, 102, 0), (204, 51, 0), (204, 0, 0), + (153, 255, 102), (153, 204, 102), (153, 153, 102), (153, 102, 102), + (153, 51, 102), (153, 0, 102), (153, 255, 51), (153, 204, 51), + (153, 153, 51), (153, 102, 51), (153, 51, 51), (153, 0, 51), + (153, 255, 0), (153, 204, 0), (153, 153, 0), (153, 102, 0), + (153, 51, 0), (153, 0, 0), (102, 255, 102), (102, 204, 102), + (102, 153, 102), (102, 102, 102), (102, 51, 102), (102, 0, 102), + (102, 255, 51), (102, 204, 51), (102, 153, 51), (102, 102, 51), + (102, 51, 51), (102, 0, 51), (102, 255, 0), (102, 204, 0), + (102, 153, 0), (102, 102, 0), (102, 51, 0), (102, 0, 0), + (51, 255, 102), (51, 204, 102), (51, 153, 102), (51, 102, 102), + (51, 51, 102), (51, 0, 102), (51, 255, 51), (51, 204, 51), + (51, 153, 51), (51, 102, 51), (51, 51, 51), (51, 0, 51), + (51, 255, 0), (51, 204, 0), (51, 153, 0), (51, 102, 0), + (51, 51, 0), (51, 0, 0), (0, 255, 102), (0, 204, 102), + (0, 153, 102), (0, 102, 102), (0, 51, 102), (0, 0, 102), + (0, 255, 51), (0, 204, 51), (0, 153, 51), (0, 102, 51), + (0, 51, 51), (0, 0, 51), (0, 255, 0), (0, 204, 0), + (0, 153, 0), (0, 102, 0), (0, 51, 0), (17, 17, 17), + (34, 34, 34), (68, 68, 68), (85, 85, 85), (119, 119, 119), + (136, 136, 136), (170, 170, 170), (187, 187, 187), (221, 221, 221), + (238, 238, 238), (192, 192, 192), (128, 0, 0), (128, 0, 128), + (0, 128, 0), (0, 128, 128), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)) +# fmt: on + + +# so build a prototype image to be used for palette resampling +def build_prototype_image(): + image = Image.new("L", (1, len(_Palm8BitColormapValues))) + image.putdata(list(range(len(_Palm8BitColormapValues)))) + palettedata = () + for colormapValue in _Palm8BitColormapValues: + palettedata += colormapValue + palettedata += (0, 0, 0) * (256 - len(_Palm8BitColormapValues)) + image.putpalette(palettedata) + return image + + +Palm8BitColormapImage = build_prototype_image() + +# OK, we now have in Palm8BitColormapImage, +# a "P"-mode image with the right palette +# +# -------------------------------------------------------------------- + +_FLAGS = {"custom-colormap": 0x4000, "is-compressed": 0x8000, "has-transparent": 0x2000} + +_COMPRESSION_TYPES = {"none": 0xFF, "rle": 0x01, "scanline": 0x00} + + +# +# -------------------------------------------------------------------- + +## +# (Internal) Image save plugin for the Palm format. + + +def _save(im, fp, filename): + + if im.mode == "P": + + # we assume this is a color Palm image with the standard colormap, + # unless the "info" dict has a "custom-colormap" field + + rawmode = "P" + bpp = 8 + version = 1 + + elif im.mode == "L": + if im.encoderinfo.get("bpp") in (1, 2, 4): + # this is 8-bit grayscale, so we shift it to get the high-order bits, + # and invert it because + # Palm does greyscale from white (0) to black (1) + bpp = im.encoderinfo["bpp"] + im = im.point( + lambda x, shift=8 - bpp, maxval=(1 << bpp) - 1: maxval - (x >> shift) + ) + elif im.info.get("bpp") in (1, 2, 4): + # here we assume that even though the inherent mode is 8-bit grayscale, + # only the lower bpp bits are significant. + # We invert them to match the Palm. + bpp = im.info["bpp"] + im = im.point(lambda x, maxval=(1 << bpp) - 1: maxval - (x & maxval)) + else: + raise OSError(f"cannot write mode {im.mode} as Palm") + + # we ignore the palette here + im.mode = "P" + rawmode = "P;" + str(bpp) + version = 1 + + elif im.mode == "1": + + # monochrome -- write it inverted, as is the Palm standard + rawmode = "1;I" + bpp = 1 + version = 0 + + else: + + raise OSError(f"cannot write mode {im.mode} as Palm") + + # + # make sure image data is available + im.load() + + # write header + + cols = im.size[0] + rows = im.size[1] + + rowbytes = int((cols + (16 // bpp - 1)) / (16 // bpp)) * 2 + transparent_index = 0 + compression_type = _COMPRESSION_TYPES["none"] + + flags = 0 + if im.mode == "P" and "custom-colormap" in im.info: + flags = flags & _FLAGS["custom-colormap"] + colormapsize = 4 * 256 + 2 + colormapmode = im.palette.mode + colormap = im.getdata().getpalette() + else: + colormapsize = 0 + + if "offset" in im.info: + offset = (rowbytes * rows + 16 + 3 + colormapsize) // 4 + else: + offset = 0 + + fp.write(o16b(cols) + o16b(rows) + o16b(rowbytes) + o16b(flags)) + fp.write(o8(bpp)) + fp.write(o8(version)) + fp.write(o16b(offset)) + fp.write(o8(transparent_index)) + fp.write(o8(compression_type)) + fp.write(o16b(0)) # reserved by Palm + + # now write colormap if necessary + + if colormapsize > 0: + fp.write(o16b(256)) + for i in range(256): + fp.write(o8(i)) + if colormapmode == "RGB": + fp.write( + o8(colormap[3 * i]) + + o8(colormap[3 * i + 1]) + + o8(colormap[3 * i + 2]) + ) + elif colormapmode == "RGBA": + fp.write( + o8(colormap[4 * i]) + + o8(colormap[4 * i + 1]) + + o8(colormap[4 * i + 2]) + ) + + # now convert data to raw form + ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, rowbytes, 1))]) + + if hasattr(fp, "flush"): + fp.flush() + + +# +# -------------------------------------------------------------------- + +Image.register_save("Palm", _save) + +Image.register_extension("Palm", ".palm") + +Image.register_mime("Palm", "image/palm") diff --git a/venv/lib/python3.11/site-packages/PIL/PcdImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/PcdImagePlugin.py new file mode 100644 index 00000000..38caf5c6 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/PcdImagePlugin.py @@ -0,0 +1,63 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PCD file handling +# +# History: +# 96-05-10 fl Created +# 96-05-27 fl Added draft mode (128x192, 256x384) +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + + +from . import Image, ImageFile + +## +# Image plugin for PhotoCD images. This plugin only reads the 768x512 +# image from the file; higher resolutions are encoded in a proprietary +# encoding. + + +class PcdImageFile(ImageFile.ImageFile): + + format = "PCD" + format_description = "Kodak PhotoCD" + + def _open(self): + + # rough + self.fp.seek(2048) + s = self.fp.read(2048) + + if s[:4] != b"PCD_": + raise SyntaxError("not a PCD file") + + orientation = s[1538] & 3 + self.tile_post_rotate = None + if orientation == 1: + self.tile_post_rotate = 90 + elif orientation == 3: + self.tile_post_rotate = -90 + + self.mode = "RGB" + self._size = 768, 512 # FIXME: not correct for rotated images! + self.tile = [("pcd", (0, 0) + self.size, 96 * 2048, None)] + + def load_end(self): + if self.tile_post_rotate: + # Handle rotated PCDs + self.im = self.im.rotate(self.tile_post_rotate) + self._size = self.im.size + + +# +# registry + +Image.register_open(PcdImageFile.format, PcdImageFile) + +Image.register_extension(PcdImageFile.format, ".pcd") diff --git a/venv/lib/python3.11/site-packages/PIL/PcfFontFile.py b/venv/lib/python3.11/site-packages/PIL/PcfFontFile.py new file mode 100644 index 00000000..6a4eb22a --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/PcfFontFile.py @@ -0,0 +1,248 @@ +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library +# $Id$ +# +# portable compiled font file parser +# +# history: +# 1997-08-19 fl created +# 2003-09-13 fl fixed loading of unicode fonts +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1997-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +import io + +from . import FontFile, Image +from ._binary import i8 +from ._binary import i16be as b16 +from ._binary import i16le as l16 +from ._binary import i32be as b32 +from ._binary import i32le as l32 + +# -------------------------------------------------------------------- +# declarations + +PCF_MAGIC = 0x70636601 # "\x01fcp" + +PCF_PROPERTIES = 1 << 0 +PCF_ACCELERATORS = 1 << 1 +PCF_METRICS = 1 << 2 +PCF_BITMAPS = 1 << 3 +PCF_INK_METRICS = 1 << 4 +PCF_BDF_ENCODINGS = 1 << 5 +PCF_SWIDTHS = 1 << 6 +PCF_GLYPH_NAMES = 1 << 7 +PCF_BDF_ACCELERATORS = 1 << 8 + +BYTES_PER_ROW = [ + lambda bits: ((bits + 7) >> 3), + lambda bits: ((bits + 15) >> 3) & ~1, + lambda bits: ((bits + 31) >> 3) & ~3, + lambda bits: ((bits + 63) >> 3) & ~7, +] + + +def sz(s, o): + return s[o : s.index(b"\0", o)] + + +class PcfFontFile(FontFile.FontFile): + """Font file plugin for the X11 PCF format.""" + + name = "name" + + def __init__(self, fp, charset_encoding="iso8859-1"): + + self.charset_encoding = charset_encoding + + magic = l32(fp.read(4)) + if magic != PCF_MAGIC: + raise SyntaxError("not a PCF file") + + super().__init__() + + count = l32(fp.read(4)) + self.toc = {} + for i in range(count): + type = l32(fp.read(4)) + self.toc[type] = l32(fp.read(4)), l32(fp.read(4)), l32(fp.read(4)) + + self.fp = fp + + self.info = self._load_properties() + + metrics = self._load_metrics() + bitmaps = self._load_bitmaps(metrics) + encoding = self._load_encoding() + + # + # create glyph structure + + for ch in range(256): + ix = encoding[ch] + if ix is not None: + x, y, l, r, w, a, d, f = metrics[ix] + glyph = (w, 0), (l, d - y, x + l, d), (0, 0, x, y), bitmaps[ix] + self.glyph[ch] = glyph + + def _getformat(self, tag): + + format, size, offset = self.toc[tag] + + fp = self.fp + fp.seek(offset) + + format = l32(fp.read(4)) + + if format & 4: + i16, i32 = b16, b32 + else: + i16, i32 = l16, l32 + + return fp, format, i16, i32 + + def _load_properties(self): + + # + # font properties + + properties = {} + + fp, format, i16, i32 = self._getformat(PCF_PROPERTIES) + + nprops = i32(fp.read(4)) + + # read property description + p = [] + for i in range(nprops): + p.append((i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4)))) + if nprops & 3: + fp.seek(4 - (nprops & 3), io.SEEK_CUR) # pad + + data = fp.read(i32(fp.read(4))) + + for k, s, v in p: + k = sz(data, k) + if s: + v = sz(data, v) + properties[k] = v + + return properties + + def _load_metrics(self): + + # + # font metrics + + metrics = [] + + fp, format, i16, i32 = self._getformat(PCF_METRICS) + + append = metrics.append + + if (format & 0xFF00) == 0x100: + + # "compressed" metrics + for i in range(i16(fp.read(2))): + left = i8(fp.read(1)) - 128 + right = i8(fp.read(1)) - 128 + width = i8(fp.read(1)) - 128 + ascent = i8(fp.read(1)) - 128 + descent = i8(fp.read(1)) - 128 + xsize = right - left + ysize = ascent + descent + append((xsize, ysize, left, right, width, ascent, descent, 0)) + + else: + + # "jumbo" metrics + for i in range(i32(fp.read(4))): + left = i16(fp.read(2)) + right = i16(fp.read(2)) + width = i16(fp.read(2)) + ascent = i16(fp.read(2)) + descent = i16(fp.read(2)) + attributes = i16(fp.read(2)) + xsize = right - left + ysize = ascent + descent + append((xsize, ysize, left, right, width, ascent, descent, attributes)) + + return metrics + + def _load_bitmaps(self, metrics): + + # + # bitmap data + + bitmaps = [] + + fp, format, i16, i32 = self._getformat(PCF_BITMAPS) + + nbitmaps = i32(fp.read(4)) + + if nbitmaps != len(metrics): + raise OSError("Wrong number of bitmaps") + + offsets = [] + for i in range(nbitmaps): + offsets.append(i32(fp.read(4))) + + bitmapSizes = [] + for i in range(4): + bitmapSizes.append(i32(fp.read(4))) + + # byteorder = format & 4 # non-zero => MSB + bitorder = format & 8 # non-zero => MSB + padindex = format & 3 + + bitmapsize = bitmapSizes[padindex] + offsets.append(bitmapsize) + + data = fp.read(bitmapsize) + + pad = BYTES_PER_ROW[padindex] + mode = "1;R" + if bitorder: + mode = "1" + + for i in range(nbitmaps): + x, y, l, r, w, a, d, f = metrics[i] + b, e = offsets[i], offsets[i + 1] + bitmaps.append(Image.frombytes("1", (x, y), data[b:e], "raw", mode, pad(x))) + + return bitmaps + + def _load_encoding(self): + + # map character code to bitmap index + encoding = [None] * 256 + + fp, format, i16, i32 = self._getformat(PCF_BDF_ENCODINGS) + + firstCol, lastCol = i16(fp.read(2)), i16(fp.read(2)) + firstRow, lastRow = i16(fp.read(2)), i16(fp.read(2)) + + i16(fp.read(2)) # default + + nencoding = (lastCol - firstCol + 1) * (lastRow - firstRow + 1) + + encodingOffsets = [i16(fp.read(2)) for _ in range(nencoding)] + + for i in range(firstCol, len(encoding)): + try: + encodingOffset = encodingOffsets[ + ord(bytearray([i]).decode(self.charset_encoding)) + ] + if encodingOffset != 0xFFFF: + encoding[i] = encodingOffset + except UnicodeDecodeError: + # character is not supported in selected encoding + pass + + return encoding diff --git a/venv/lib/python3.11/site-packages/PIL/PcxImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/PcxImagePlugin.py new file mode 100644 index 00000000..a24d44b4 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/PcxImagePlugin.py @@ -0,0 +1,213 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PCX file handling +# +# This format was originally used by ZSoft's popular PaintBrush +# program for the IBM PC. It is also supported by many MS-DOS and +# Windows applications, including the Windows PaintBrush program in +# Windows 3. +# +# history: +# 1995-09-01 fl Created +# 1996-05-20 fl Fixed RGB support +# 1997-01-03 fl Fixed 2-bit and 4-bit support +# 1999-02-03 fl Fixed 8-bit support (broken in 1.0b1) +# 1999-02-07 fl Added write support +# 2002-06-09 fl Made 2-bit and 4-bit support a bit more robust +# 2002-07-30 fl Seek from to current position, not beginning of file +# 2003-06-03 fl Extract DPI settings (info["dpi"]) +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +import io +import logging + +from . import Image, ImageFile, ImagePalette +from ._binary import i16le as i16 +from ._binary import o8 +from ._binary import o16le as o16 + +logger = logging.getLogger(__name__) + + +def _accept(prefix): + return prefix[0] == 10 and prefix[1] in [0, 2, 3, 5] + + +## +# Image plugin for Paintbrush images. + + +class PcxImageFile(ImageFile.ImageFile): + + format = "PCX" + format_description = "Paintbrush" + + def _open(self): + + # header + s = self.fp.read(128) + if not _accept(s): + raise SyntaxError("not a PCX file") + + # image + bbox = i16(s, 4), i16(s, 6), i16(s, 8) + 1, i16(s, 10) + 1 + if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]: + raise SyntaxError("bad PCX image size") + logger.debug("BBox: %s %s %s %s", *bbox) + + # format + version = s[1] + bits = s[3] + planes = s[65] + ignored_stride = i16(s, 66) + logger.debug( + "PCX version %s, bits %s, planes %s, stride %s", + version, + bits, + planes, + ignored_stride, + ) + + self.info["dpi"] = i16(s, 12), i16(s, 14) + + if bits == 1 and planes == 1: + mode = rawmode = "1" + + elif bits == 1 and planes in (2, 4): + mode = "P" + rawmode = "P;%dL" % planes + self.palette = ImagePalette.raw("RGB", s[16:64]) + + elif version == 5 and bits == 8 and planes == 1: + mode = rawmode = "L" + # FIXME: hey, this doesn't work with the incremental loader !!! + self.fp.seek(-769, io.SEEK_END) + s = self.fp.read(769) + if len(s) == 769 and s[0] == 12: + # check if the palette is linear greyscale + for i in range(256): + if s[i * 3 + 1 : i * 3 + 4] != o8(i) * 3: + mode = rawmode = "P" + break + if mode == "P": + self.palette = ImagePalette.raw("RGB", s[1:]) + self.fp.seek(128) + + elif version == 5 and bits == 8 and planes == 3: + mode = "RGB" + rawmode = "RGB;L" + + else: + raise OSError("unknown PCX mode") + + self.mode = mode + self._size = bbox[2] - bbox[0], bbox[3] - bbox[1] + + # don't trust the passed in stride. Calculate for ourselves. + # CVE-2020-35655 + stride = (self._size[0] * bits + 7) // 8 + stride += stride % 2 + + bbox = (0, 0) + self.size + logger.debug("size: %sx%s", *self.size) + + self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))] + + +# -------------------------------------------------------------------- +# save PCX files + + +SAVE = { + # mode: (version, bits, planes, raw mode) + "1": (2, 1, 1, "1"), + "L": (5, 8, 1, "L"), + "P": (5, 8, 1, "P"), + "RGB": (5, 8, 3, "RGB;L"), +} + + +def _save(im, fp, filename): + + try: + version, bits, planes, rawmode = SAVE[im.mode] + except KeyError as e: + raise ValueError(f"Cannot save {im.mode} images as PCX") from e + + # bytes per plane + stride = (im.size[0] * bits + 7) // 8 + # stride should be even + stride += stride % 2 + # Stride needs to be kept in sync with the PcxEncode.c version. + # Ideally it should be passed in in the state, but the bytes value + # gets overwritten. + + logger.debug( + "PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d", + im.size[0], + bits, + stride, + ) + + # under windows, we could determine the current screen size with + # "Image.core.display_mode()[1]", but I think that's overkill... + + screen = im.size + + dpi = 100, 100 + + # PCX header + fp.write( + o8(10) + + o8(version) + + o8(1) + + o8(bits) + + o16(0) + + o16(0) + + o16(im.size[0] - 1) + + o16(im.size[1] - 1) + + o16(dpi[0]) + + o16(dpi[1]) + + b"\0" * 24 + + b"\xFF" * 24 + + b"\0" + + o8(planes) + + o16(stride) + + o16(1) + + o16(screen[0]) + + o16(screen[1]) + + b"\0" * 54 + ) + + assert fp.tell() == 128 + + ImageFile._save(im, fp, [("pcx", (0, 0) + im.size, 0, (rawmode, bits * planes))]) + + if im.mode == "P": + # colour palette + fp.write(o8(12)) + fp.write(im.im.getpalette("RGB", "RGB")) # 768 bytes + elif im.mode == "L": + # greyscale palette + fp.write(o8(12)) + for i in range(256): + fp.write(o8(i) * 3) + + +# -------------------------------------------------------------------- +# registry + + +Image.register_open(PcxImageFile.format, PcxImageFile, _accept) +Image.register_save(PcxImageFile.format, _save) + +Image.register_extension(PcxImageFile.format, ".pcx") + +Image.register_mime(PcxImageFile.format, "image/x-pcx") diff --git a/venv/lib/python3.11/site-packages/PIL/PdfImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/PdfImagePlugin.py new file mode 100644 index 00000000..36c8fb84 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/PdfImagePlugin.py @@ -0,0 +1,246 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PDF (Acrobat) file handling +# +# History: +# 1996-07-16 fl Created +# 1997-01-18 fl Fixed header +# 2004-02-21 fl Fixes for 1/L/CMYK images, etc. +# 2004-02-24 fl Fixes for 1 and P images. +# +# Copyright (c) 1997-2004 by Secret Labs AB. All rights reserved. +# Copyright (c) 1996-1997 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +## +# Image plugin for PDF images (output only). +## + +import io +import os +import time + +from . import Image, ImageFile, ImageSequence, PdfParser, __version__ + +# +# -------------------------------------------------------------------- + +# object ids: +# 1. catalogue +# 2. pages +# 3. image +# 4. page +# 5. page contents + + +def _save_all(im, fp, filename): + _save(im, fp, filename, save_all=True) + + +## +# (Internal) Image save plugin for the PDF format. + + +def _save(im, fp, filename, save_all=False): + is_appending = im.encoderinfo.get("append", False) + if is_appending: + existing_pdf = PdfParser.PdfParser(f=fp, filename=filename, mode="r+b") + else: + existing_pdf = PdfParser.PdfParser(f=fp, filename=filename, mode="w+b") + + resolution = im.encoderinfo.get("resolution", 72.0) + + info = { + "title": None + if is_appending + else os.path.splitext(os.path.basename(filename))[0], + "author": None, + "subject": None, + "keywords": None, + "creator": None, + "producer": None, + "creationDate": None if is_appending else time.gmtime(), + "modDate": None if is_appending else time.gmtime(), + } + for k, default in info.items(): + v = im.encoderinfo.get(k) if k in im.encoderinfo else default + if v: + existing_pdf.info[k[0].upper() + k[1:]] = v + + # + # make sure image data is available + im.load() + + existing_pdf.start_writing() + existing_pdf.write_header() + existing_pdf.write_comment(f"created by Pillow {__version__} PDF driver") + + # + # pages + ims = [im] + if save_all: + append_images = im.encoderinfo.get("append_images", []) + for append_im in append_images: + append_im.encoderinfo = im.encoderinfo.copy() + ims.append(append_im) + numberOfPages = 0 + image_refs = [] + page_refs = [] + contents_refs = [] + for im in ims: + im_numberOfPages = 1 + if save_all: + try: + im_numberOfPages = im.n_frames + except AttributeError: + # Image format does not have n_frames. + # It is a single frame image + pass + numberOfPages += im_numberOfPages + for i in range(im_numberOfPages): + image_refs.append(existing_pdf.next_object_id(0)) + page_refs.append(existing_pdf.next_object_id(0)) + contents_refs.append(existing_pdf.next_object_id(0)) + existing_pdf.pages.append(page_refs[-1]) + + # + # catalog and list of pages + existing_pdf.write_catalog() + + pageNumber = 0 + for imSequence in ims: + im_pages = ImageSequence.Iterator(imSequence) if save_all else [imSequence] + for im in im_pages: + # FIXME: Should replace ASCIIHexDecode with RunLengthDecode + # (packbits) or LZWDecode (tiff/lzw compression). Note that + # PDF 1.2 also supports Flatedecode (zip compression). + + bits = 8 + params = None + decode = None + + if im.mode == "1": + filter = "ASCIIHexDecode" + colorspace = PdfParser.PdfName("DeviceGray") + procset = "ImageB" # grayscale + bits = 1 + elif im.mode == "L": + filter = "DCTDecode" + # params = f"<< /Predictor 15 /Columns {width-2} >>" + colorspace = PdfParser.PdfName("DeviceGray") + procset = "ImageB" # grayscale + elif im.mode == "P": + filter = "ASCIIHexDecode" + palette = im.im.getpalette("RGB") + colorspace = [ + PdfParser.PdfName("Indexed"), + PdfParser.PdfName("DeviceRGB"), + 255, + PdfParser.PdfBinary(palette), + ] + procset = "ImageI" # indexed color + elif im.mode == "RGB": + filter = "DCTDecode" + colorspace = PdfParser.PdfName("DeviceRGB") + procset = "ImageC" # color images + elif im.mode == "CMYK": + filter = "DCTDecode" + colorspace = PdfParser.PdfName("DeviceCMYK") + procset = "ImageC" # color images + decode = [1, 0, 1, 0, 1, 0, 1, 0] + else: + raise ValueError(f"cannot save mode {im.mode}") + + # + # image + + op = io.BytesIO() + + if filter == "ASCIIHexDecode": + if bits == 1: + # FIXME: the hex encoder doesn't support packed 1-bit + # images; do things the hard way... + data = im.tobytes("raw", "1") + im = Image.new("L", im.size) + im.putdata(data) + ImageFile._save(im, op, [("hex", (0, 0) + im.size, 0, im.mode)]) + elif filter == "DCTDecode": + Image.SAVE["JPEG"](im, op, filename) + elif filter == "FlateDecode": + ImageFile._save(im, op, [("zip", (0, 0) + im.size, 0, im.mode)]) + elif filter == "RunLengthDecode": + ImageFile._save(im, op, [("packbits", (0, 0) + im.size, 0, im.mode)]) + else: + raise ValueError(f"unsupported PDF filter ({filter})") + + # + # Get image characteristics + + width, height = im.size + + existing_pdf.write_obj( + image_refs[pageNumber], + stream=op.getvalue(), + Type=PdfParser.PdfName("XObject"), + Subtype=PdfParser.PdfName("Image"), + Width=width, # * 72.0 / resolution, + Height=height, # * 72.0 / resolution, + Filter=PdfParser.PdfName(filter), + BitsPerComponent=bits, + Decode=decode, + DecodeParams=params, + ColorSpace=colorspace, + ) + + # + # page + + existing_pdf.write_page( + page_refs[pageNumber], + Resources=PdfParser.PdfDict( + ProcSet=[PdfParser.PdfName("PDF"), PdfParser.PdfName(procset)], + XObject=PdfParser.PdfDict(image=image_refs[pageNumber]), + ), + MediaBox=[ + 0, + 0, + int(width * 72.0 / resolution), + int(height * 72.0 / resolution), + ], + Contents=contents_refs[pageNumber], + ) + + # + # page contents + + page_contents = b"q %d 0 0 %d 0 0 cm /image Do Q\n" % ( + int(width * 72.0 / resolution), + int(height * 72.0 / resolution), + ) + + existing_pdf.write_obj(contents_refs[pageNumber], stream=page_contents) + + pageNumber += 1 + + # + # trailer + existing_pdf.write_xref_and_trailer() + if hasattr(fp, "flush"): + fp.flush() + existing_pdf.close() + + +# +# -------------------------------------------------------------------- + + +Image.register_save("PDF", _save) +Image.register_save_all("PDF", _save_all) + +Image.register_extension("PDF", ".pdf") + +Image.register_mime("PDF", "application/pdf") diff --git a/venv/lib/python3.11/site-packages/PIL/PdfParser.py b/venv/lib/python3.11/site-packages/PIL/PdfParser.py new file mode 100644 index 00000000..975905f9 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/PdfParser.py @@ -0,0 +1,994 @@ +import calendar +import codecs +import collections +import mmap +import os +import re +import time +import zlib + + +# see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set +# on page 656 +def encode_text(s): + return codecs.BOM_UTF16_BE + s.encode("utf_16_be") + + +PDFDocEncoding = { + 0x16: "\u0017", + 0x18: "\u02D8", + 0x19: "\u02C7", + 0x1A: "\u02C6", + 0x1B: "\u02D9", + 0x1C: "\u02DD", + 0x1D: "\u02DB", + 0x1E: "\u02DA", + 0x1F: "\u02DC", + 0x80: "\u2022", + 0x81: "\u2020", + 0x82: "\u2021", + 0x83: "\u2026", + 0x84: "\u2014", + 0x85: "\u2013", + 0x86: "\u0192", + 0x87: "\u2044", + 0x88: "\u2039", + 0x89: "\u203A", + 0x8A: "\u2212", + 0x8B: "\u2030", + 0x8C: "\u201E", + 0x8D: "\u201C", + 0x8E: "\u201D", + 0x8F: "\u2018", + 0x90: "\u2019", + 0x91: "\u201A", + 0x92: "\u2122", + 0x93: "\uFB01", + 0x94: "\uFB02", + 0x95: "\u0141", + 0x96: "\u0152", + 0x97: "\u0160", + 0x98: "\u0178", + 0x99: "\u017D", + 0x9A: "\u0131", + 0x9B: "\u0142", + 0x9C: "\u0153", + 0x9D: "\u0161", + 0x9E: "\u017E", + 0xA0: "\u20AC", +} + + +def decode_text(b): + if b[: len(codecs.BOM_UTF16_BE)] == codecs.BOM_UTF16_BE: + return b[len(codecs.BOM_UTF16_BE) :].decode("utf_16_be") + else: + return "".join(PDFDocEncoding.get(byte, chr(byte)) for byte in b) + + +class PdfFormatError(RuntimeError): + """An error that probably indicates a syntactic or semantic error in the + PDF file structure""" + + pass + + +def check_format_condition(condition, error_message): + if not condition: + raise PdfFormatError(error_message) + + +class IndirectReference( + collections.namedtuple("IndirectReferenceTuple", ["object_id", "generation"]) +): + def __str__(self): + return "%s %s R" % self + + def __bytes__(self): + return self.__str__().encode("us-ascii") + + def __eq__(self, other): + return ( + other.__class__ is self.__class__ + and other.object_id == self.object_id + and other.generation == self.generation + ) + + def __ne__(self, other): + return not (self == other) + + def __hash__(self): + return hash((self.object_id, self.generation)) + + +class IndirectObjectDef(IndirectReference): + def __str__(self): + return "%s %s obj" % self + + +class XrefTable: + def __init__(self): + self.existing_entries = {} # object ID => (offset, generation) + self.new_entries = {} # object ID => (offset, generation) + self.deleted_entries = {0: 65536} # object ID => generation + self.reading_finished = False + + def __setitem__(self, key, value): + if self.reading_finished: + self.new_entries[key] = value + else: + self.existing_entries[key] = value + if key in self.deleted_entries: + del self.deleted_entries[key] + + def __getitem__(self, key): + try: + return self.new_entries[key] + except KeyError: + return self.existing_entries[key] + + def __delitem__(self, key): + if key in self.new_entries: + generation = self.new_entries[key][1] + 1 + del self.new_entries[key] + self.deleted_entries[key] = generation + elif key in self.existing_entries: + generation = self.existing_entries[key][1] + 1 + self.deleted_entries[key] = generation + elif key in self.deleted_entries: + generation = self.deleted_entries[key] + else: + raise IndexError( + "object ID " + str(key) + " cannot be deleted because it doesn't exist" + ) + + def __contains__(self, key): + return key in self.existing_entries or key in self.new_entries + + def __len__(self): + return len( + set(self.existing_entries.keys()) + | set(self.new_entries.keys()) + | set(self.deleted_entries.keys()) + ) + + def keys(self): + return ( + set(self.existing_entries.keys()) - set(self.deleted_entries.keys()) + ) | set(self.new_entries.keys()) + + def write(self, f): + keys = sorted(set(self.new_entries.keys()) | set(self.deleted_entries.keys())) + deleted_keys = sorted(set(self.deleted_entries.keys())) + startxref = f.tell() + f.write(b"xref\n") + while keys: + # find a contiguous sequence of object IDs + prev = None + for index, key in enumerate(keys): + if prev is None or prev + 1 == key: + prev = key + else: + contiguous_keys = keys[:index] + keys = keys[index:] + break + else: + contiguous_keys = keys + keys = None + f.write(b"%d %d\n" % (contiguous_keys[0], len(contiguous_keys))) + for object_id in contiguous_keys: + if object_id in self.new_entries: + f.write(b"%010d %05d n \n" % self.new_entries[object_id]) + else: + this_deleted_object_id = deleted_keys.pop(0) + check_format_condition( + object_id == this_deleted_object_id, + f"expected the next deleted object ID to be {object_id}, " + f"instead found {this_deleted_object_id}", + ) + try: + next_in_linked_list = deleted_keys[0] + except IndexError: + next_in_linked_list = 0 + f.write( + b"%010d %05d f \n" + % (next_in_linked_list, self.deleted_entries[object_id]) + ) + return startxref + + +class PdfName: + def __init__(self, name): + if isinstance(name, PdfName): + self.name = name.name + elif isinstance(name, bytes): + self.name = name + else: + self.name = name.encode("us-ascii") + + def name_as_str(self): + return self.name.decode("us-ascii") + + def __eq__(self, other): + return ( + isinstance(other, PdfName) and other.name == self.name + ) or other == self.name + + def __hash__(self): + return hash(self.name) + + def __repr__(self): + return f"PdfName({repr(self.name)})" + + @classmethod + def from_pdf_stream(cls, data): + return cls(PdfParser.interpret_name(data)) + + allowed_chars = set(range(33, 127)) - {ord(c) for c in "#%/()<>[]{}"} + + def __bytes__(self): + result = bytearray(b"/") + for b in self.name: + if b in self.allowed_chars: + result.append(b) + else: + result.extend(b"#%02X" % b) + return bytes(result) + + +class PdfArray(list): + def __bytes__(self): + return b"[ " + b" ".join(pdf_repr(x) for x in self) + b" ]" + + +class PdfDict(collections.UserDict): + def __setattr__(self, key, value): + if key == "data": + collections.UserDict.__setattr__(self, key, value) + else: + self[key.encode("us-ascii")] = value + + def __getattr__(self, key): + try: + value = self[key.encode("us-ascii")] + except KeyError as e: + raise AttributeError(key) from e + if isinstance(value, bytes): + value = decode_text(value) + if key.endswith("Date"): + if value.startswith("D:"): + value = value[2:] + + relationship = "Z" + if len(value) > 17: + relationship = value[14] + offset = int(value[15:17]) * 60 + if len(value) > 20: + offset += int(value[18:20]) + + format = "%Y%m%d%H%M%S"[: len(value) - 2] + value = time.strptime(value[: len(format) + 2], format) + if relationship in ["+", "-"]: + offset *= 60 + if relationship == "+": + offset *= -1 + value = time.gmtime(calendar.timegm(value) + offset) + return value + + def __bytes__(self): + out = bytearray(b"<<") + for key, value in self.items(): + if value is None: + continue + value = pdf_repr(value) + out.extend(b"\n") + out.extend(bytes(PdfName(key))) + out.extend(b" ") + out.extend(value) + out.extend(b"\n>>") + return bytes(out) + + +class PdfBinary: + def __init__(self, data): + self.data = data + + def __bytes__(self): + return b"<%s>" % b"".join(b"%02X" % b for b in self.data) + + +class PdfStream: + def __init__(self, dictionary, buf): + self.dictionary = dictionary + self.buf = buf + + def decode(self): + try: + filter = self.dictionary.Filter + except AttributeError: + return self.buf + if filter == b"FlateDecode": + try: + expected_length = self.dictionary.DL + except AttributeError: + expected_length = self.dictionary.Length + return zlib.decompress(self.buf, bufsize=int(expected_length)) + else: + raise NotImplementedError( + f"stream filter {repr(self.dictionary.Filter)} unknown/unsupported" + ) + + +def pdf_repr(x): + if x is True: + return b"true" + elif x is False: + return b"false" + elif x is None: + return b"null" + elif isinstance(x, (PdfName, PdfDict, PdfArray, PdfBinary)): + return bytes(x) + elif isinstance(x, int): + return str(x).encode("us-ascii") + elif isinstance(x, time.struct_time): + return b"(D:" + time.strftime("%Y%m%d%H%M%SZ", x).encode("us-ascii") + b")" + elif isinstance(x, dict): + return bytes(PdfDict(x)) + elif isinstance(x, list): + return bytes(PdfArray(x)) + elif isinstance(x, str): + return pdf_repr(encode_text(x)) + elif isinstance(x, bytes): + # XXX escape more chars? handle binary garbage + x = x.replace(b"\\", b"\\\\") + x = x.replace(b"(", b"\\(") + x = x.replace(b")", b"\\)") + return b"(" + x + b")" + else: + return bytes(x) + + +class PdfParser: + """Based on + https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PDF32000_2008.pdf + Supports PDF up to 1.4 + """ + + def __init__(self, filename=None, f=None, buf=None, start_offset=0, mode="rb"): + if buf and f: + raise RuntimeError("specify buf or f or filename, but not both buf and f") + self.filename = filename + self.buf = buf + self.f = f + self.start_offset = start_offset + self.should_close_buf = False + self.should_close_file = False + if filename is not None and f is None: + self.f = f = open(filename, mode) + self.should_close_file = True + if f is not None: + self.buf = buf = self.get_buf_from_file(f) + self.should_close_buf = True + if not filename and hasattr(f, "name"): + self.filename = f.name + self.cached_objects = {} + if buf: + self.read_pdf_info() + else: + self.file_size_total = self.file_size_this = 0 + self.root = PdfDict() + self.root_ref = None + self.info = PdfDict() + self.info_ref = None + self.page_tree_root = {} + self.pages = [] + self.orig_pages = [] + self.pages_ref = None + self.last_xref_section_offset = None + self.trailer_dict = {} + self.xref_table = XrefTable() + self.xref_table.reading_finished = True + if f: + self.seek_end() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + return False # do not suppress exceptions + + def start_writing(self): + self.close_buf() + self.seek_end() + + def close_buf(self): + try: + self.buf.close() + except AttributeError: + pass + self.buf = None + + def close(self): + if self.should_close_buf: + self.close_buf() + if self.f is not None and self.should_close_file: + self.f.close() + self.f = None + + def seek_end(self): + self.f.seek(0, os.SEEK_END) + + def write_header(self): + self.f.write(b"%PDF-1.4\n") + + def write_comment(self, s): + self.f.write(f"% {s}\n".encode("utf-8")) + + def write_catalog(self): + self.del_root() + self.root_ref = self.next_object_id(self.f.tell()) + self.pages_ref = self.next_object_id(0) + self.rewrite_pages() + self.write_obj(self.root_ref, Type=PdfName(b"Catalog"), Pages=self.pages_ref) + self.write_obj( + self.pages_ref, + Type=PdfName(b"Pages"), + Count=len(self.pages), + Kids=self.pages, + ) + return self.root_ref + + def rewrite_pages(self): + pages_tree_nodes_to_delete = [] + for i, page_ref in enumerate(self.orig_pages): + page_info = self.cached_objects[page_ref] + del self.xref_table[page_ref.object_id] + pages_tree_nodes_to_delete.append(page_info[PdfName(b"Parent")]) + if page_ref not in self.pages: + # the page has been deleted + continue + # make dict keys into strings for passing to write_page + stringified_page_info = {} + for key, value in page_info.items(): + # key should be a PdfName + stringified_page_info[key.name_as_str()] = value + stringified_page_info["Parent"] = self.pages_ref + new_page_ref = self.write_page(None, **stringified_page_info) + for j, cur_page_ref in enumerate(self.pages): + if cur_page_ref == page_ref: + # replace the page reference with the new one + self.pages[j] = new_page_ref + # delete redundant Pages tree nodes from xref table + for pages_tree_node_ref in pages_tree_nodes_to_delete: + while pages_tree_node_ref: + pages_tree_node = self.cached_objects[pages_tree_node_ref] + if pages_tree_node_ref.object_id in self.xref_table: + del self.xref_table[pages_tree_node_ref.object_id] + pages_tree_node_ref = pages_tree_node.get(b"Parent", None) + self.orig_pages = [] + + def write_xref_and_trailer(self, new_root_ref=None): + if new_root_ref: + self.del_root() + self.root_ref = new_root_ref + if self.info: + self.info_ref = self.write_obj(None, self.info) + start_xref = self.xref_table.write(self.f) + num_entries = len(self.xref_table) + trailer_dict = {b"Root": self.root_ref, b"Size": num_entries} + if self.last_xref_section_offset is not None: + trailer_dict[b"Prev"] = self.last_xref_section_offset + if self.info: + trailer_dict[b"Info"] = self.info_ref + self.last_xref_section_offset = start_xref + self.f.write( + b"trailer\n" + + bytes(PdfDict(trailer_dict)) + + b"\nstartxref\n%d\n%%%%EOF" % start_xref + ) + + def write_page(self, ref, *objs, **dict_obj): + if isinstance(ref, int): + ref = self.pages[ref] + if "Type" not in dict_obj: + dict_obj["Type"] = PdfName(b"Page") + if "Parent" not in dict_obj: + dict_obj["Parent"] = self.pages_ref + return self.write_obj(ref, *objs, **dict_obj) + + def write_obj(self, ref, *objs, **dict_obj): + f = self.f + if ref is None: + ref = self.next_object_id(f.tell()) + else: + self.xref_table[ref.object_id] = (f.tell(), ref.generation) + f.write(bytes(IndirectObjectDef(*ref))) + stream = dict_obj.pop("stream", None) + if stream is not None: + dict_obj["Length"] = len(stream) + if dict_obj: + f.write(pdf_repr(dict_obj)) + for obj in objs: + f.write(pdf_repr(obj)) + if stream is not None: + f.write(b"stream\n") + f.write(stream) + f.write(b"\nendstream\n") + f.write(b"endobj\n") + return ref + + def del_root(self): + if self.root_ref is None: + return + del self.xref_table[self.root_ref.object_id] + del self.xref_table[self.root[b"Pages"].object_id] + + @staticmethod + def get_buf_from_file(f): + if hasattr(f, "getbuffer"): + return f.getbuffer() + elif hasattr(f, "getvalue"): + return f.getvalue() + else: + try: + return mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) + except ValueError: # cannot mmap an empty file + return b"" + + def read_pdf_info(self): + self.file_size_total = len(self.buf) + self.file_size_this = self.file_size_total - self.start_offset + self.read_trailer() + self.root_ref = self.trailer_dict[b"Root"] + self.info_ref = self.trailer_dict.get(b"Info", None) + self.root = PdfDict(self.read_indirect(self.root_ref)) + if self.info_ref is None: + self.info = PdfDict() + else: + self.info = PdfDict(self.read_indirect(self.info_ref)) + check_format_condition(b"Type" in self.root, "/Type missing in Root") + check_format_condition( + self.root[b"Type"] == b"Catalog", "/Type in Root is not /Catalog" + ) + check_format_condition(b"Pages" in self.root, "/Pages missing in Root") + check_format_condition( + isinstance(self.root[b"Pages"], IndirectReference), + "/Pages in Root is not an indirect reference", + ) + self.pages_ref = self.root[b"Pages"] + self.page_tree_root = self.read_indirect(self.pages_ref) + self.pages = self.linearize_page_tree(self.page_tree_root) + # save the original list of page references + # in case the user modifies, adds or deletes some pages + # and we need to rewrite the pages and their list + self.orig_pages = self.pages[:] + + def next_object_id(self, offset=None): + try: + # TODO: support reuse of deleted objects + reference = IndirectReference(max(self.xref_table.keys()) + 1, 0) + except ValueError: + reference = IndirectReference(1, 0) + if offset is not None: + self.xref_table[reference.object_id] = (offset, 0) + return reference + + delimiter = br"[][()<>{}/%]" + delimiter_or_ws = br"[][()<>{}/%\000\011\012\014\015\040]" + whitespace = br"[\000\011\012\014\015\040]" + whitespace_or_hex = br"[\000\011\012\014\015\0400-9a-fA-F]" + whitespace_optional = whitespace + b"*" + whitespace_mandatory = whitespace + b"+" + newline_only = br"[\r\n]+" + newline = whitespace_optional + newline_only + whitespace_optional + re_trailer_end = re.compile( + whitespace_mandatory + + br"trailer" + + whitespace_optional + + br"\<\<(.*\>\>)" + + newline + + br"startxref" + + newline + + br"([0-9]+)" + + newline + + br"%%EOF" + + whitespace_optional + + br"$", + re.DOTALL, + ) + re_trailer_prev = re.compile( + whitespace_optional + + br"trailer" + + whitespace_optional + + br"\<\<(.*?\>\>)" + + newline + + br"startxref" + + newline + + br"([0-9]+)" + + newline + + br"%%EOF" + + whitespace_optional, + re.DOTALL, + ) + + def read_trailer(self): + search_start_offset = len(self.buf) - 16384 + if search_start_offset < self.start_offset: + search_start_offset = self.start_offset + m = self.re_trailer_end.search(self.buf, search_start_offset) + check_format_condition(m, "trailer end not found") + # make sure we found the LAST trailer + last_match = m + while m: + last_match = m + m = self.re_trailer_end.search(self.buf, m.start() + 16) + if not m: + m = last_match + trailer_data = m.group(1) + self.last_xref_section_offset = int(m.group(2)) + self.trailer_dict = self.interpret_trailer(trailer_data) + self.xref_table = XrefTable() + self.read_xref_table(xref_section_offset=self.last_xref_section_offset) + if b"Prev" in self.trailer_dict: + self.read_prev_trailer(self.trailer_dict[b"Prev"]) + + def read_prev_trailer(self, xref_section_offset): + trailer_offset = self.read_xref_table(xref_section_offset=xref_section_offset) + m = self.re_trailer_prev.search( + self.buf[trailer_offset : trailer_offset + 16384] + ) + check_format_condition(m, "previous trailer not found") + trailer_data = m.group(1) + check_format_condition( + int(m.group(2)) == xref_section_offset, + "xref section offset in previous trailer doesn't match what was expected", + ) + trailer_dict = self.interpret_trailer(trailer_data) + if b"Prev" in trailer_dict: + self.read_prev_trailer(trailer_dict[b"Prev"]) + + re_whitespace_optional = re.compile(whitespace_optional) + re_name = re.compile( + whitespace_optional + + br"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?=" + + delimiter_or_ws + + br")" + ) + re_dict_start = re.compile(whitespace_optional + br"\<\<") + re_dict_end = re.compile(whitespace_optional + br"\>\>" + whitespace_optional) + + @classmethod + def interpret_trailer(cls, trailer_data): + trailer = {} + offset = 0 + while True: + m = cls.re_name.match(trailer_data, offset) + if not m: + m = cls.re_dict_end.match(trailer_data, offset) + check_format_condition( + m and m.end() == len(trailer_data), + "name not found in trailer, remaining data: " + + repr(trailer_data[offset:]), + ) + break + key = cls.interpret_name(m.group(1)) + value, offset = cls.get_value(trailer_data, m.end()) + trailer[key] = value + check_format_condition( + b"Size" in trailer and isinstance(trailer[b"Size"], int), + "/Size not in trailer or not an integer", + ) + check_format_condition( + b"Root" in trailer and isinstance(trailer[b"Root"], IndirectReference), + "/Root not in trailer or not an indirect reference", + ) + return trailer + + re_hashes_in_name = re.compile(br"([^#]*)(#([0-9a-fA-F]{2}))?") + + @classmethod + def interpret_name(cls, raw, as_text=False): + name = b"" + for m in cls.re_hashes_in_name.finditer(raw): + if m.group(3): + name += m.group(1) + bytearray.fromhex(m.group(3).decode("us-ascii")) + else: + name += m.group(1) + if as_text: + return name.decode("utf-8") + else: + return bytes(name) + + re_null = re.compile(whitespace_optional + br"null(?=" + delimiter_or_ws + br")") + re_true = re.compile(whitespace_optional + br"true(?=" + delimiter_or_ws + br")") + re_false = re.compile(whitespace_optional + br"false(?=" + delimiter_or_ws + br")") + re_int = re.compile( + whitespace_optional + br"([-+]?[0-9]+)(?=" + delimiter_or_ws + br")" + ) + re_real = re.compile( + whitespace_optional + + br"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?=" + + delimiter_or_ws + + br")" + ) + re_array_start = re.compile(whitespace_optional + br"\[") + re_array_end = re.compile(whitespace_optional + br"]") + re_string_hex = re.compile( + whitespace_optional + br"\<(" + whitespace_or_hex + br"*)\>" + ) + re_string_lit = re.compile(whitespace_optional + br"\(") + re_indirect_reference = re.compile( + whitespace_optional + + br"([-+]?[0-9]+)" + + whitespace_mandatory + + br"([-+]?[0-9]+)" + + whitespace_mandatory + + br"R(?=" + + delimiter_or_ws + + br")" + ) + re_indirect_def_start = re.compile( + whitespace_optional + + br"([-+]?[0-9]+)" + + whitespace_mandatory + + br"([-+]?[0-9]+)" + + whitespace_mandatory + + br"obj(?=" + + delimiter_or_ws + + br")" + ) + re_indirect_def_end = re.compile( + whitespace_optional + br"endobj(?=" + delimiter_or_ws + br")" + ) + re_comment = re.compile( + br"(" + whitespace_optional + br"%[^\r\n]*" + newline + br")*" + ) + re_stream_start = re.compile(whitespace_optional + br"stream\r?\n") + re_stream_end = re.compile( + whitespace_optional + br"endstream(?=" + delimiter_or_ws + br")" + ) + + @classmethod + def get_value(cls, data, offset, expect_indirect=None, max_nesting=-1): + if max_nesting == 0: + return None, None + m = cls.re_comment.match(data, offset) + if m: + offset = m.end() + m = cls.re_indirect_def_start.match(data, offset) + if m: + check_format_condition( + int(m.group(1)) > 0, + "indirect object definition: object ID must be greater than 0", + ) + check_format_condition( + int(m.group(2)) >= 0, + "indirect object definition: generation must be non-negative", + ) + check_format_condition( + expect_indirect is None + or expect_indirect + == IndirectReference(int(m.group(1)), int(m.group(2))), + "indirect object definition different than expected", + ) + object, offset = cls.get_value(data, m.end(), max_nesting=max_nesting - 1) + if offset is None: + return object, None + m = cls.re_indirect_def_end.match(data, offset) + check_format_condition(m, "indirect object definition end not found") + return object, m.end() + check_format_condition( + not expect_indirect, "indirect object definition not found" + ) + m = cls.re_indirect_reference.match(data, offset) + if m: + check_format_condition( + int(m.group(1)) > 0, + "indirect object reference: object ID must be greater than 0", + ) + check_format_condition( + int(m.group(2)) >= 0, + "indirect object reference: generation must be non-negative", + ) + return IndirectReference(int(m.group(1)), int(m.group(2))), m.end() + m = cls.re_dict_start.match(data, offset) + if m: + offset = m.end() + result = {} + m = cls.re_dict_end.match(data, offset) + while not m: + key, offset = cls.get_value(data, offset, max_nesting=max_nesting - 1) + if offset is None: + return result, None + value, offset = cls.get_value(data, offset, max_nesting=max_nesting - 1) + result[key] = value + if offset is None: + return result, None + m = cls.re_dict_end.match(data, offset) + offset = m.end() + m = cls.re_stream_start.match(data, offset) + if m: + try: + stream_len = int(result[b"Length"]) + except (TypeError, KeyError, ValueError) as e: + raise PdfFormatError( + "bad or missing Length in stream dict (%r)" + % result.get(b"Length", None) + ) from e + stream_data = data[m.end() : m.end() + stream_len] + m = cls.re_stream_end.match(data, m.end() + stream_len) + check_format_condition(m, "stream end not found") + offset = m.end() + result = PdfStream(PdfDict(result), stream_data) + else: + result = PdfDict(result) + return result, offset + m = cls.re_array_start.match(data, offset) + if m: + offset = m.end() + result = [] + m = cls.re_array_end.match(data, offset) + while not m: + value, offset = cls.get_value(data, offset, max_nesting=max_nesting - 1) + result.append(value) + if offset is None: + return result, None + m = cls.re_array_end.match(data, offset) + return result, m.end() + m = cls.re_null.match(data, offset) + if m: + return None, m.end() + m = cls.re_true.match(data, offset) + if m: + return True, m.end() + m = cls.re_false.match(data, offset) + if m: + return False, m.end() + m = cls.re_name.match(data, offset) + if m: + return PdfName(cls.interpret_name(m.group(1))), m.end() + m = cls.re_int.match(data, offset) + if m: + return int(m.group(1)), m.end() + m = cls.re_real.match(data, offset) + if m: + # XXX Decimal instead of float??? + return float(m.group(1)), m.end() + m = cls.re_string_hex.match(data, offset) + if m: + # filter out whitespace + hex_string = bytearray( + [b for b in m.group(1) if b in b"0123456789abcdefABCDEF"] + ) + if len(hex_string) % 2 == 1: + # append a 0 if the length is not even - yes, at the end + hex_string.append(ord(b"0")) + return bytearray.fromhex(hex_string.decode("us-ascii")), m.end() + m = cls.re_string_lit.match(data, offset) + if m: + return cls.get_literal_string(data, m.end()) + # return None, offset # fallback (only for debugging) + raise PdfFormatError("unrecognized object: " + repr(data[offset : offset + 32])) + + re_lit_str_token = re.compile( + br"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))" + ) + escaped_chars = { + b"n": b"\n", + b"r": b"\r", + b"t": b"\t", + b"b": b"\b", + b"f": b"\f", + b"(": b"(", + b")": b")", + b"\\": b"\\", + ord(b"n"): b"\n", + ord(b"r"): b"\r", + ord(b"t"): b"\t", + ord(b"b"): b"\b", + ord(b"f"): b"\f", + ord(b"("): b"(", + ord(b")"): b")", + ord(b"\\"): b"\\", + } + + @classmethod + def get_literal_string(cls, data, offset): + nesting_depth = 0 + result = bytearray() + for m in cls.re_lit_str_token.finditer(data, offset): + result.extend(data[offset : m.start()]) + if m.group(1): + result.extend(cls.escaped_chars[m.group(1)[1]]) + elif m.group(2): + result.append(int(m.group(2)[1:], 8)) + elif m.group(3): + pass + elif m.group(5): + result.extend(b"\n") + elif m.group(6): + result.extend(b"(") + nesting_depth += 1 + elif m.group(7): + if nesting_depth == 0: + return bytes(result), m.end() + result.extend(b")") + nesting_depth -= 1 + offset = m.end() + raise PdfFormatError("unfinished literal string") + + re_xref_section_start = re.compile(whitespace_optional + br"xref" + newline) + re_xref_subsection_start = re.compile( + whitespace_optional + + br"([0-9]+)" + + whitespace_mandatory + + br"([0-9]+)" + + whitespace_optional + + newline_only + ) + re_xref_entry = re.compile(br"([0-9]{10}) ([0-9]{5}) ([fn])( \r| \n|\r\n)") + + def read_xref_table(self, xref_section_offset): + subsection_found = False + m = self.re_xref_section_start.match( + self.buf, xref_section_offset + self.start_offset + ) + check_format_condition(m, "xref section start not found") + offset = m.end() + while True: + m = self.re_xref_subsection_start.match(self.buf, offset) + if not m: + check_format_condition( + subsection_found, "xref subsection start not found" + ) + break + subsection_found = True + offset = m.end() + first_object = int(m.group(1)) + num_objects = int(m.group(2)) + for i in range(first_object, first_object + num_objects): + m = self.re_xref_entry.match(self.buf, offset) + check_format_condition(m, "xref entry not found") + offset = m.end() + is_free = m.group(3) == b"f" + generation = int(m.group(2)) + if not is_free: + new_entry = (int(m.group(1)), generation) + check_format_condition( + i not in self.xref_table or self.xref_table[i] == new_entry, + "xref entry duplicated (and not identical)", + ) + self.xref_table[i] = new_entry + return offset + + def read_indirect(self, ref, max_nesting=-1): + offset, generation = self.xref_table[ref[0]] + check_format_condition( + generation == ref[1], + f"expected to find generation {ref[1]} for object ID {ref[0]} in xref " + f"table, instead found generation {generation} at offset {offset}", + ) + value = self.get_value( + self.buf, + offset + self.start_offset, + expect_indirect=IndirectReference(*ref), + max_nesting=max_nesting, + )[0] + self.cached_objects[ref] = value + return value + + def linearize_page_tree(self, node=None): + if node is None: + node = self.page_tree_root + check_format_condition( + node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages" + ) + pages = [] + for kid in node[b"Kids"]: + kid_object = self.read_indirect(kid) + if kid_object[b"Type"] == b"Page": + pages.append(kid) + else: + pages.extend(self.linearize_page_tree(node=kid_object)) + return pages diff --git a/venv/lib/python3.11/site-packages/PIL/PixarImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/PixarImagePlugin.py new file mode 100644 index 00000000..c4860b6c --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/PixarImagePlugin.py @@ -0,0 +1,70 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PIXAR raster support for PIL +# +# history: +# 97-01-29 fl Created +# +# notes: +# This is incomplete; it is based on a few samples created with +# Photoshop 2.5 and 3.0, and a summary description provided by +# Greg Coats . Hopefully, "L" and +# "RGBA" support will be added in future versions. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +from . import Image, ImageFile +from ._binary import i16le as i16 + +# +# helpers + + +def _accept(prefix): + return prefix[:4] == b"\200\350\000\000" + + +## +# Image plugin for PIXAR raster images. + + +class PixarImageFile(ImageFile.ImageFile): + + format = "PIXAR" + format_description = "PIXAR raster image" + + def _open(self): + + # assuming a 4-byte magic label + s = self.fp.read(4) + if not _accept(s): + raise SyntaxError("not a PIXAR file") + + # read rest of header + s = s + self.fp.read(508) + + self._size = i16(s, 418), i16(s, 416) + + # get channel/depth descriptions + mode = i16(s, 424), i16(s, 426) + + if mode == (14, 2): + self.mode = "RGB" + # FIXME: to be continued... + + # create tile descriptor (assuming "dumped") + self.tile = [("raw", (0, 0) + self.size, 1024, (self.mode, 0, 1))] + + +# +# -------------------------------------------------------------------- + +Image.register_open(PixarImageFile.format, PixarImageFile, _accept) + +Image.register_extension(PixarImageFile.format, ".pxr") diff --git a/venv/lib/python3.11/site-packages/PIL/PngImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/PngImagePlugin.py new file mode 100644 index 00000000..2d4ac760 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/PngImagePlugin.py @@ -0,0 +1,1395 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PNG support code +# +# See "PNG (Portable Network Graphics) Specification, version 1.0; +# W3C Recommendation", 1996-10-01, Thomas Boutell (ed.). +# +# history: +# 1996-05-06 fl Created (couldn't resist it) +# 1996-12-14 fl Upgraded, added read and verify support (0.2) +# 1996-12-15 fl Separate PNG stream parser +# 1996-12-29 fl Added write support, added getchunks +# 1996-12-30 fl Eliminated circular references in decoder (0.3) +# 1998-07-12 fl Read/write 16-bit images as mode I (0.4) +# 2001-02-08 fl Added transparency support (from Zircon) (0.5) +# 2001-04-16 fl Don't close data source in "open" method (0.6) +# 2004-02-24 fl Don't even pretend to support interlaced files (0.7) +# 2004-08-31 fl Do basic sanity check on chunk identifiers (0.8) +# 2004-09-20 fl Added PngInfo chunk container +# 2004-12-18 fl Added DPI read support (based on code by Niki Spahiev) +# 2008-08-13 fl Added tRNS support for RGB images +# 2009-03-06 fl Support for preserving ICC profiles (by Florian Hoech) +# 2009-03-08 fl Added zTXT support (from Lowell Alleman) +# 2009-03-29 fl Read interlaced PNG files (from Conrado Porto Lopes Gouvua) +# +# Copyright (c) 1997-2009 by Secret Labs AB +# Copyright (c) 1996 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import itertools +import logging +import re +import struct +import warnings +import zlib + +from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence +from ._binary import i16be as i16 +from ._binary import i32be as i32 +from ._binary import o8 +from ._binary import o16be as o16 +from ._binary import o32be as o32 + +logger = logging.getLogger(__name__) + +is_cid = re.compile(br"\w\w\w\w").match + + +_MAGIC = b"\211PNG\r\n\032\n" + + +_MODES = { + # supported bits/color combinations, and corresponding modes/rawmodes + # Greyscale + (1, 0): ("1", "1"), + (2, 0): ("L", "L;2"), + (4, 0): ("L", "L;4"), + (8, 0): ("L", "L"), + (16, 0): ("I", "I;16B"), + # Truecolour + (8, 2): ("RGB", "RGB"), + (16, 2): ("RGB", "RGB;16B"), + # Indexed-colour + (1, 3): ("P", "P;1"), + (2, 3): ("P", "P;2"), + (4, 3): ("P", "P;4"), + (8, 3): ("P", "P"), + # Greyscale with alpha + (8, 4): ("LA", "LA"), + (16, 4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available + # Truecolour with alpha + (8, 6): ("RGBA", "RGBA"), + (16, 6): ("RGBA", "RGBA;16B"), +} + + +_simple_palette = re.compile(b"^\xff*\x00\xff*$") + +MAX_TEXT_CHUNK = ImageFile.SAFEBLOCK +""" +Maximum decompressed size for a iTXt or zTXt chunk. +Eliminates decompression bombs where compressed chunks can expand 1000x. +See :ref:`Text in PNG File Format`. +""" +MAX_TEXT_MEMORY = 64 * MAX_TEXT_CHUNK +""" +Set the maximum total text chunk size. +See :ref:`Text in PNG File Format`. +""" + + +# APNG frame disposal modes +APNG_DISPOSE_OP_NONE = 0 +""" +No disposal is done on this frame before rendering the next frame. +See :ref:`Saving APNG sequences`. +""" +APNG_DISPOSE_OP_BACKGROUND = 1 +""" +This frame’s modified region is cleared to fully transparent black before rendering +the next frame. +See :ref:`Saving APNG sequences`. +""" +APNG_DISPOSE_OP_PREVIOUS = 2 +""" +This frame’s modified region is reverted to the previous frame’s contents before +rendering the next frame. +See :ref:`Saving APNG sequences`. +""" + +# APNG frame blend modes +APNG_BLEND_OP_SOURCE = 0 +""" +All color components of this frame, including alpha, overwrite the previous output +image contents. +See :ref:`Saving APNG sequences`. +""" +APNG_BLEND_OP_OVER = 1 +""" +This frame should be alpha composited with the previous output image contents. +See :ref:`Saving APNG sequences`. +""" + + +def _safe_zlib_decompress(s): + dobj = zlib.decompressobj() + plaintext = dobj.decompress(s, MAX_TEXT_CHUNK) + if dobj.unconsumed_tail: + raise ValueError("Decompressed Data Too Large") + return plaintext + + +def _crc32(data, seed=0): + return zlib.crc32(data, seed) & 0xFFFFFFFF + + +# -------------------------------------------------------------------- +# Support classes. Suitable for PNG and related formats like MNG etc. + + +class ChunkStream: + def __init__(self, fp): + + self.fp = fp + self.queue = [] + + def read(self): + """Fetch a new chunk. Returns header information.""" + cid = None + + if self.queue: + cid, pos, length = self.queue.pop() + self.fp.seek(pos) + else: + s = self.fp.read(8) + cid = s[4:] + pos = self.fp.tell() + length = i32(s) + + if not is_cid(cid): + if not ImageFile.LOAD_TRUNCATED_IMAGES: + raise SyntaxError(f"broken PNG file (chunk {repr(cid)})") + + return cid, pos, length + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def close(self): + self.queue = self.crc = self.fp = None + + def push(self, cid, pos, length): + + self.queue.append((cid, pos, length)) + + def call(self, cid, pos, length): + """Call the appropriate chunk handler""" + + logger.debug("STREAM %r %s %s", cid, pos, length) + return getattr(self, "chunk_" + cid.decode("ascii"))(pos, length) + + def crc(self, cid, data): + """Read and verify checksum""" + + # Skip CRC checks for ancillary chunks if allowed to load truncated + # images + # 5th byte of first char is 1 [specs, section 5.4] + if ImageFile.LOAD_TRUNCATED_IMAGES and (cid[0] >> 5 & 1): + self.crc_skip(cid, data) + return + + try: + crc1 = _crc32(data, _crc32(cid)) + crc2 = i32(self.fp.read(4)) + if crc1 != crc2: + raise SyntaxError( + f"broken PNG file (bad header checksum in {repr(cid)})" + ) + except struct.error as e: + raise SyntaxError( + f"broken PNG file (incomplete checksum in {repr(cid)})" + ) from e + + def crc_skip(self, cid, data): + """Read checksum. Used if the C module is not present""" + + self.fp.read(4) + + def verify(self, endchunk=b"IEND"): + + # Simple approach; just calculate checksum for all remaining + # blocks. Must be called directly after open. + + cids = [] + + while True: + try: + cid, pos, length = self.read() + except struct.error as e: + raise OSError("truncated PNG file") from e + + if cid == endchunk: + break + self.crc(cid, ImageFile._safe_read(self.fp, length)) + cids.append(cid) + + return cids + + +class iTXt(str): + """ + Subclass of string to allow iTXt chunks to look like strings while + keeping their extra information + + """ + + @staticmethod + def __new__(cls, text, lang=None, tkey=None): + """ + :param cls: the class to use when creating the instance + :param text: value for this key + :param lang: language code + :param tkey: UTF-8 version of the key name + """ + + self = str.__new__(cls, text) + self.lang = lang + self.tkey = tkey + return self + + +class PngInfo: + """ + PNG chunk container (for use with save(pnginfo=)) + + """ + + def __init__(self): + self.chunks = [] + + def add(self, cid, data, after_idat=False): + """Appends an arbitrary chunk. Use with caution. + + :param cid: a byte string, 4 bytes long. + :param data: a byte string of the encoded data + :param after_idat: for use with private chunks. Whether the chunk + should be written after IDAT + + """ + + chunk = [cid, data] + if after_idat: + chunk.append(True) + self.chunks.append(tuple(chunk)) + + def add_itxt(self, key, value, lang="", tkey="", zip=False): + """Appends an iTXt chunk. + + :param key: latin-1 encodable text key name + :param value: value for this key + :param lang: language code + :param tkey: UTF-8 version of the key name + :param zip: compression flag + + """ + + if not isinstance(key, bytes): + key = key.encode("latin-1", "strict") + if not isinstance(value, bytes): + value = value.encode("utf-8", "strict") + if not isinstance(lang, bytes): + lang = lang.encode("utf-8", "strict") + if not isinstance(tkey, bytes): + tkey = tkey.encode("utf-8", "strict") + + if zip: + self.add( + b"iTXt", + key + b"\0\x01\0" + lang + b"\0" + tkey + b"\0" + zlib.compress(value), + ) + else: + self.add(b"iTXt", key + b"\0\0\0" + lang + b"\0" + tkey + b"\0" + value) + + def add_text(self, key, value, zip=False): + """Appends a text chunk. + + :param key: latin-1 encodable text key name + :param value: value for this key, text or an + :py:class:`PIL.PngImagePlugin.iTXt` instance + :param zip: compression flag + + """ + if isinstance(value, iTXt): + return self.add_itxt(key, value, value.lang, value.tkey, zip=zip) + + # The tEXt chunk stores latin-1 text + if not isinstance(value, bytes): + try: + value = value.encode("latin-1", "strict") + except UnicodeError: + return self.add_itxt(key, value, zip=zip) + + if not isinstance(key, bytes): + key = key.encode("latin-1", "strict") + + if zip: + self.add(b"zTXt", key + b"\0\0" + zlib.compress(value)) + else: + self.add(b"tEXt", key + b"\0" + value) + + +# -------------------------------------------------------------------- +# PNG image stream (IHDR/IEND) + + +class PngStream(ChunkStream): + def __init__(self, fp): + super().__init__(fp) + + # local copies of Image attributes + self.im_info = {} + self.im_text = {} + self.im_size = (0, 0) + self.im_mode = None + self.im_tile = None + self.im_palette = None + self.im_custom_mimetype = None + self.im_n_frames = None + self._seq_num = None + self.rewind_state = None + + self.text_memory = 0 + + def check_text_memory(self, chunklen): + self.text_memory += chunklen + if self.text_memory > MAX_TEXT_MEMORY: + raise ValueError( + "Too much memory used in text chunks: " + f"{self.text_memory}>MAX_TEXT_MEMORY" + ) + + def save_rewind(self): + self.rewind_state = { + "info": self.im_info.copy(), + "tile": self.im_tile, + "seq_num": self._seq_num, + } + + def rewind(self): + self.im_info = self.rewind_state["info"] + self.im_tile = self.rewind_state["tile"] + self._seq_num = self.rewind_state["seq_num"] + + def chunk_iCCP(self, pos, length): + + # ICC profile + s = ImageFile._safe_read(self.fp, length) + # according to PNG spec, the iCCP chunk contains: + # Profile name 1-79 bytes (character string) + # Null separator 1 byte (null character) + # Compression method 1 byte (0) + # Compressed profile n bytes (zlib with deflate compression) + i = s.find(b"\0") + logger.debug("iCCP profile name %r", s[:i]) + logger.debug("Compression method %s", s[i]) + comp_method = s[i] + if comp_method != 0: + raise SyntaxError(f"Unknown compression method {comp_method} in iCCP chunk") + try: + icc_profile = _safe_zlib_decompress(s[i + 2 :]) + except ValueError: + if ImageFile.LOAD_TRUNCATED_IMAGES: + icc_profile = None + else: + raise + except zlib.error: + icc_profile = None # FIXME + self.im_info["icc_profile"] = icc_profile + return s + + def chunk_IHDR(self, pos, length): + + # image header + s = ImageFile._safe_read(self.fp, length) + self.im_size = i32(s, 0), i32(s, 4) + try: + self.im_mode, self.im_rawmode = _MODES[(s[8], s[9])] + except Exception: + pass + if s[12]: + self.im_info["interlace"] = 1 + if s[11]: + raise SyntaxError("unknown filter category") + return s + + def chunk_IDAT(self, pos, length): + + # image data + if "bbox" in self.im_info: + tile = [("zip", self.im_info["bbox"], pos, self.im_rawmode)] + else: + if self.im_n_frames is not None: + self.im_info["default_image"] = True + tile = [("zip", (0, 0) + self.im_size, pos, self.im_rawmode)] + self.im_tile = tile + self.im_idat = length + raise EOFError + + def chunk_IEND(self, pos, length): + + # end of PNG image + raise EOFError + + def chunk_PLTE(self, pos, length): + + # palette + s = ImageFile._safe_read(self.fp, length) + if self.im_mode == "P": + self.im_palette = "RGB", s + return s + + def chunk_tRNS(self, pos, length): + + # transparency + s = ImageFile._safe_read(self.fp, length) + if self.im_mode == "P": + if _simple_palette.match(s): + # tRNS contains only one full-transparent entry, + # other entries are full opaque + i = s.find(b"\0") + if i >= 0: + self.im_info["transparency"] = i + else: + # otherwise, we have a byte string with one alpha value + # for each palette entry + self.im_info["transparency"] = s + elif self.im_mode in ("1", "L", "I"): + self.im_info["transparency"] = i16(s) + elif self.im_mode == "RGB": + self.im_info["transparency"] = i16(s), i16(s, 2), i16(s, 4) + return s + + def chunk_gAMA(self, pos, length): + # gamma setting + s = ImageFile._safe_read(self.fp, length) + self.im_info["gamma"] = i32(s) / 100000.0 + return s + + def chunk_cHRM(self, pos, length): + # chromaticity, 8 unsigned ints, actual value is scaled by 100,000 + # WP x,y, Red x,y, Green x,y Blue x,y + + s = ImageFile._safe_read(self.fp, length) + raw_vals = struct.unpack(">%dI" % (len(s) // 4), s) + self.im_info["chromaticity"] = tuple(elt / 100000.0 for elt in raw_vals) + return s + + def chunk_sRGB(self, pos, length): + # srgb rendering intent, 1 byte + # 0 perceptual + # 1 relative colorimetric + # 2 saturation + # 3 absolute colorimetric + + s = ImageFile._safe_read(self.fp, length) + self.im_info["srgb"] = s[0] + return s + + def chunk_pHYs(self, pos, length): + + # pixels per unit + s = ImageFile._safe_read(self.fp, length) + px, py = i32(s, 0), i32(s, 4) + unit = s[8] + if unit == 1: # meter + dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5) + self.im_info["dpi"] = dpi + elif unit == 0: + self.im_info["aspect"] = px, py + return s + + def chunk_tEXt(self, pos, length): + + # text + s = ImageFile._safe_read(self.fp, length) + try: + k, v = s.split(b"\0", 1) + except ValueError: + # fallback for broken tEXt tags + k = s + v = b"" + if k: + k = k.decode("latin-1", "strict") + v_str = v.decode("latin-1", "replace") + + self.im_info[k] = v if k == "exif" else v_str + self.im_text[k] = v_str + self.check_text_memory(len(v_str)) + + return s + + def chunk_zTXt(self, pos, length): + + # compressed text + s = ImageFile._safe_read(self.fp, length) + try: + k, v = s.split(b"\0", 1) + except ValueError: + k = s + v = b"" + if v: + comp_method = v[0] + else: + comp_method = 0 + if comp_method != 0: + raise SyntaxError(f"Unknown compression method {comp_method} in zTXt chunk") + try: + v = _safe_zlib_decompress(v[1:]) + except ValueError: + if ImageFile.LOAD_TRUNCATED_IMAGES: + v = b"" + else: + raise + except zlib.error: + v = b"" + + if k: + k = k.decode("latin-1", "strict") + v = v.decode("latin-1", "replace") + + self.im_info[k] = self.im_text[k] = v + self.check_text_memory(len(v)) + + return s + + def chunk_iTXt(self, pos, length): + + # international text + r = s = ImageFile._safe_read(self.fp, length) + try: + k, r = r.split(b"\0", 1) + except ValueError: + return s + if len(r) < 2: + return s + cf, cm, r = r[0], r[1], r[2:] + try: + lang, tk, v = r.split(b"\0", 2) + except ValueError: + return s + if cf != 0: + if cm == 0: + try: + v = _safe_zlib_decompress(v) + except ValueError: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + else: + raise + except zlib.error: + return s + else: + return s + try: + k = k.decode("latin-1", "strict") + lang = lang.decode("utf-8", "strict") + tk = tk.decode("utf-8", "strict") + v = v.decode("utf-8", "strict") + except UnicodeError: + return s + + self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk) + self.check_text_memory(len(v)) + + return s + + def chunk_eXIf(self, pos, length): + s = ImageFile._safe_read(self.fp, length) + self.im_info["exif"] = b"Exif\x00\x00" + s + return s + + # APNG chunks + def chunk_acTL(self, pos, length): + s = ImageFile._safe_read(self.fp, length) + if self.im_n_frames is not None: + self.im_n_frames = None + warnings.warn("Invalid APNG, will use default PNG image if possible") + return s + n_frames = i32(s) + if n_frames == 0 or n_frames > 0x80000000: + warnings.warn("Invalid APNG, will use default PNG image if possible") + return s + self.im_n_frames = n_frames + self.im_info["loop"] = i32(s, 4) + self.im_custom_mimetype = "image/apng" + return s + + def chunk_fcTL(self, pos, length): + s = ImageFile._safe_read(self.fp, length) + seq = i32(s) + if (self._seq_num is None and seq != 0) or ( + self._seq_num is not None and self._seq_num != seq - 1 + ): + raise SyntaxError("APNG contains frame sequence errors") + self._seq_num = seq + width, height = i32(s, 4), i32(s, 8) + px, py = i32(s, 12), i32(s, 16) + im_w, im_h = self.im_size + if px + width > im_w or py + height > im_h: + raise SyntaxError("APNG contains invalid frames") + self.im_info["bbox"] = (px, py, px + width, py + height) + delay_num, delay_den = i16(s, 20), i16(s, 22) + if delay_den == 0: + delay_den = 100 + self.im_info["duration"] = float(delay_num) / float(delay_den) * 1000 + self.im_info["disposal"] = s[24] + self.im_info["blend"] = s[25] + return s + + def chunk_fdAT(self, pos, length): + s = ImageFile._safe_read(self.fp, 4) + seq = i32(s) + if self._seq_num != seq - 1: + raise SyntaxError("APNG contains frame sequence errors") + self._seq_num = seq + return self.chunk_IDAT(pos + 4, length - 4) + + +# -------------------------------------------------------------------- +# PNG reader + + +def _accept(prefix): + return prefix[:8] == _MAGIC + + +## +# Image plugin for PNG images. + + +class PngImageFile(ImageFile.ImageFile): + + format = "PNG" + format_description = "Portable network graphics" + + def _open(self): + + if not _accept(self.fp.read(8)): + raise SyntaxError("not a PNG file") + self.__fp = self.fp + self.__frame = 0 + + # + # Parse headers up to the first IDAT or fDAT chunk + + self.private_chunks = [] + self.png = PngStream(self.fp) + + while True: + + # + # get next chunk + + cid, pos, length = self.png.read() + + try: + s = self.png.call(cid, pos, length) + except EOFError: + break + except AttributeError: + logger.debug("%r %s %s (unknown)", cid, pos, length) + s = ImageFile._safe_read(self.fp, length) + if cid[1:2].islower(): + self.private_chunks.append((cid, s)) + + self.png.crc(cid, s) + + # + # Copy relevant attributes from the PngStream. An alternative + # would be to let the PngStream class modify these attributes + # directly, but that introduces circular references which are + # difficult to break if things go wrong in the decoder... + # (believe me, I've tried ;-) + + self.mode = self.png.im_mode + self._size = self.png.im_size + self.info = self.png.im_info + self._text = None + self.tile = self.png.im_tile + self.custom_mimetype = self.png.im_custom_mimetype + self.n_frames = self.png.im_n_frames or 1 + self.default_image = self.info.get("default_image", False) + + if self.png.im_palette: + rawmode, data = self.png.im_palette + self.palette = ImagePalette.raw(rawmode, data) + + if cid == b"fdAT": + self.__prepare_idat = length - 4 + else: + self.__prepare_idat = length # used by load_prepare() + + if self.png.im_n_frames is not None: + self._close_exclusive_fp_after_loading = False + self.png.save_rewind() + self.__rewind_idat = self.__prepare_idat + self.__rewind = self.__fp.tell() + if self.default_image: + # IDAT chunk contains default image and not first animation frame + self.n_frames += 1 + self._seek(0) + self.is_animated = self.n_frames > 1 + + @property + def text(self): + # experimental + if self._text is None: + # iTxt, tEXt and zTXt chunks may appear at the end of the file + # So load the file to ensure that they are read + if self.is_animated: + frame = self.__frame + # for APNG, seek to the final frame before loading + self.seek(self.n_frames - 1) + self.load() + if self.is_animated: + self.seek(frame) + return self._text + + def verify(self): + """Verify PNG file""" + + if self.fp is None: + raise RuntimeError("verify must be called directly after open") + + # back up to beginning of IDAT block + self.fp.seek(self.tile[0][2] - 8) + + self.png.verify() + self.png.close() + + if self._exclusive_fp: + self.fp.close() + self.fp = None + + def seek(self, frame): + if not self._seek_check(frame): + return + if frame < self.__frame: + self._seek(0, True) + + last_frame = self.__frame + for f in range(self.__frame + 1, frame + 1): + try: + self._seek(f) + except EOFError as e: + self.seek(last_frame) + raise EOFError("no more images in APNG file") from e + + def _seek(self, frame, rewind=False): + if frame == 0: + if rewind: + self.__fp.seek(self.__rewind) + self.png.rewind() + self.__prepare_idat = self.__rewind_idat + self.im = None + if self.pyaccess: + self.pyaccess = None + self.info = self.png.im_info + self.tile = self.png.im_tile + self.fp = self.__fp + self._prev_im = None + self.dispose = None + self.default_image = self.info.get("default_image", False) + self.dispose_op = self.info.get("disposal") + self.blend_op = self.info.get("blend") + self.dispose_extent = self.info.get("bbox") + self.__frame = 0 + else: + if frame != self.__frame + 1: + raise ValueError(f"cannot seek to frame {frame}") + + # ensure previous frame was loaded + self.load() + + if self.dispose: + self.im.paste(self.dispose, self.dispose_extent) + self._prev_im = self.im.copy() + + self.fp = self.__fp + + # advance to the next frame + if self.__prepare_idat: + ImageFile._safe_read(self.fp, self.__prepare_idat) + self.__prepare_idat = 0 + frame_start = False + while True: + self.fp.read(4) # CRC + + try: + cid, pos, length = self.png.read() + except (struct.error, SyntaxError): + break + + if cid == b"IEND": + raise EOFError("No more images in APNG file") + if cid == b"fcTL": + if frame_start: + # there must be at least one fdAT chunk between fcTL chunks + raise SyntaxError("APNG missing frame data") + frame_start = True + + try: + self.png.call(cid, pos, length) + except UnicodeDecodeError: + break + except EOFError: + if cid == b"fdAT": + length -= 4 + if frame_start: + self.__prepare_idat = length + break + ImageFile._safe_read(self.fp, length) + except AttributeError: + logger.debug("%r %s %s (unknown)", cid, pos, length) + ImageFile._safe_read(self.fp, length) + + self.__frame = frame + self.tile = self.png.im_tile + self.dispose_op = self.info.get("disposal") + self.blend_op = self.info.get("blend") + self.dispose_extent = self.info.get("bbox") + + if not self.tile: + raise EOFError + + # setup frame disposal (actual disposal done when needed in the next _seek()) + if self._prev_im is None and self.dispose_op == APNG_DISPOSE_OP_PREVIOUS: + self.dispose_op = APNG_DISPOSE_OP_BACKGROUND + + if self.dispose_op == APNG_DISPOSE_OP_PREVIOUS: + self.dispose = self._prev_im.copy() + self.dispose = self._crop(self.dispose, self.dispose_extent) + elif self.dispose_op == APNG_DISPOSE_OP_BACKGROUND: + self.dispose = Image.core.fill(self.mode, self.size) + self.dispose = self._crop(self.dispose, self.dispose_extent) + else: + self.dispose = None + + def tell(self): + return self.__frame + + def load_prepare(self): + """internal: prepare to read PNG file""" + + if self.info.get("interlace"): + self.decoderconfig = self.decoderconfig + (1,) + + self.__idat = self.__prepare_idat # used by load_read() + ImageFile.ImageFile.load_prepare(self) + + def load_read(self, read_bytes): + """internal: read more image data""" + + while self.__idat == 0: + # end of chunk, skip forward to next one + + self.fp.read(4) # CRC + + cid, pos, length = self.png.read() + + if cid not in [b"IDAT", b"DDAT", b"fdAT"]: + self.png.push(cid, pos, length) + return b"" + + if cid == b"fdAT": + try: + self.png.call(cid, pos, length) + except EOFError: + pass + self.__idat = length - 4 # sequence_num has already been read + else: + self.__idat = length # empty chunks are allowed + + # read more data from this chunk + if read_bytes <= 0: + read_bytes = self.__idat + else: + read_bytes = min(read_bytes, self.__idat) + + self.__idat = self.__idat - read_bytes + + return self.fp.read(read_bytes) + + def load_end(self): + """internal: finished reading image data""" + while True: + self.fp.read(4) # CRC + + try: + cid, pos, length = self.png.read() + except (struct.error, SyntaxError): + break + + if cid == b"IEND": + break + elif cid == b"fcTL" and self.is_animated: + # start of the next frame, stop reading + self.__prepare_idat = 0 + self.png.push(cid, pos, length) + break + + try: + self.png.call(cid, pos, length) + except UnicodeDecodeError: + break + except EOFError: + if cid == b"fdAT": + length -= 4 + ImageFile._safe_read(self.fp, length) + except AttributeError: + logger.debug("%r %s %s (unknown)", cid, pos, length) + s = ImageFile._safe_read(self.fp, length) + if cid[1:2].islower(): + self.private_chunks.append((cid, s, True)) + self._text = self.png.im_text + if not self.is_animated: + self.png.close() + self.png = None + else: + if self._prev_im and self.blend_op == APNG_BLEND_OP_OVER: + updated = self._crop(self.im, self.dispose_extent) + self._prev_im.paste( + updated, self.dispose_extent, updated.convert("RGBA") + ) + self.im = self._prev_im + if self.pyaccess: + self.pyaccess = None + + def _getexif(self): + if "exif" not in self.info: + self.load() + if "exif" not in self.info and "Raw profile type exif" not in self.info: + return None + return dict(self.getexif()) + + def getexif(self): + if "exif" not in self.info: + self.load() + + return super().getexif() + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +# -------------------------------------------------------------------- +# PNG writer + +_OUTMODES = { + # supported PIL modes, and corresponding rawmodes/bits/color combinations + "1": ("1", b"\x01\x00"), + "L;1": ("L;1", b"\x01\x00"), + "L;2": ("L;2", b"\x02\x00"), + "L;4": ("L;4", b"\x04\x00"), + "L": ("L", b"\x08\x00"), + "LA": ("LA", b"\x08\x04"), + "I": ("I;16B", b"\x10\x00"), + "I;16": ("I;16B", b"\x10\x00"), + "P;1": ("P;1", b"\x01\x03"), + "P;2": ("P;2", b"\x02\x03"), + "P;4": ("P;4", b"\x04\x03"), + "P": ("P", b"\x08\x03"), + "RGB": ("RGB", b"\x08\x02"), + "RGBA": ("RGBA", b"\x08\x06"), +} + + +def putchunk(fp, cid, *data): + """Write a PNG chunk (including CRC field)""" + + data = b"".join(data) + + fp.write(o32(len(data)) + cid) + fp.write(data) + crc = _crc32(data, _crc32(cid)) + fp.write(o32(crc)) + + +class _idat: + # wrap output from the encoder in IDAT chunks + + def __init__(self, fp, chunk): + self.fp = fp + self.chunk = chunk + + def write(self, data): + self.chunk(self.fp, b"IDAT", data) + + +class _fdat: + # wrap encoder output in fdAT chunks + + def __init__(self, fp, chunk, seq_num): + self.fp = fp + self.chunk = chunk + self.seq_num = seq_num + + def write(self, data): + self.chunk(self.fp, b"fdAT", o32(self.seq_num), data) + self.seq_num += 1 + + +def _write_multiple_frames(im, fp, chunk, rawmode): + default_image = im.encoderinfo.get("default_image", im.info.get("default_image")) + duration = im.encoderinfo.get("duration", im.info.get("duration", 0)) + loop = im.encoderinfo.get("loop", im.info.get("loop", 0)) + disposal = im.encoderinfo.get("disposal", im.info.get("disposal")) + blend = im.encoderinfo.get("blend", im.info.get("blend")) + + if default_image: + chain = itertools.chain(im.encoderinfo.get("append_images", [])) + else: + chain = itertools.chain([im], im.encoderinfo.get("append_images", [])) + + im_frames = [] + frame_count = 0 + for im_seq in chain: + for im_frame in ImageSequence.Iterator(im_seq): + im_frame = im_frame.copy() + if im_frame.mode != im.mode: + if im.mode == "P": + im_frame = im_frame.convert(im.mode, palette=im.palette) + else: + im_frame = im_frame.convert(im.mode) + encoderinfo = im.encoderinfo.copy() + if isinstance(duration, (list, tuple)): + encoderinfo["duration"] = duration[frame_count] + if isinstance(disposal, (list, tuple)): + encoderinfo["disposal"] = disposal[frame_count] + if isinstance(blend, (list, tuple)): + encoderinfo["blend"] = blend[frame_count] + frame_count += 1 + + if im_frames: + previous = im_frames[-1] + prev_disposal = previous["encoderinfo"].get("disposal") + prev_blend = previous["encoderinfo"].get("blend") + if prev_disposal == APNG_DISPOSE_OP_PREVIOUS and len(im_frames) < 2: + prev_disposal = APNG_DISPOSE_OP_BACKGROUND + + if prev_disposal == APNG_DISPOSE_OP_BACKGROUND: + base_im = previous["im"] + dispose = Image.core.fill("RGBA", im.size, (0, 0, 0, 0)) + bbox = previous["bbox"] + if bbox: + dispose = dispose.crop(bbox) + else: + bbox = (0, 0) + im.size + base_im.paste(dispose, bbox) + elif prev_disposal == APNG_DISPOSE_OP_PREVIOUS: + base_im = im_frames[-2]["im"] + else: + base_im = previous["im"] + delta = ImageChops.subtract_modulo( + im_frame.convert("RGB"), base_im.convert("RGB") + ) + bbox = delta.getbbox() + if ( + not bbox + and prev_disposal == encoderinfo.get("disposal") + and prev_blend == encoderinfo.get("blend") + ): + duration = encoderinfo.get("duration", 0) + if duration: + if "duration" in previous["encoderinfo"]: + previous["encoderinfo"]["duration"] += duration + else: + previous["encoderinfo"]["duration"] = duration + continue + else: + bbox = None + im_frames.append({"im": im_frame, "bbox": bbox, "encoderinfo": encoderinfo}) + + # animation control + chunk( + fp, + b"acTL", + o32(len(im_frames)), # 0: num_frames + o32(loop), # 4: num_plays + ) + + # default image IDAT (if it exists) + if default_image: + ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)]) + + seq_num = 0 + for frame, frame_data in enumerate(im_frames): + im_frame = frame_data["im"] + if not frame_data["bbox"]: + bbox = (0, 0) + im_frame.size + else: + bbox = frame_data["bbox"] + im_frame = im_frame.crop(bbox) + size = im_frame.size + duration = int(round(frame_data["encoderinfo"].get("duration", 0))) + disposal = frame_data["encoderinfo"].get("disposal", APNG_DISPOSE_OP_NONE) + blend = frame_data["encoderinfo"].get("blend", APNG_BLEND_OP_SOURCE) + # frame control + chunk( + fp, + b"fcTL", + o32(seq_num), # sequence_number + o32(size[0]), # width + o32(size[1]), # height + o32(bbox[0]), # x_offset + o32(bbox[1]), # y_offset + o16(duration), # delay_numerator + o16(1000), # delay_denominator + o8(disposal), # dispose_op + o8(blend), # blend_op + ) + seq_num += 1 + # frame data + if frame == 0 and not default_image: + # first frame must be in IDAT chunks for backwards compatibility + ImageFile._save( + im_frame, + _idat(fp, chunk), + [("zip", (0, 0) + im_frame.size, 0, rawmode)], + ) + else: + fdat_chunks = _fdat(fp, chunk, seq_num) + ImageFile._save( + im_frame, + fdat_chunks, + [("zip", (0, 0) + im_frame.size, 0, rawmode)], + ) + seq_num = fdat_chunks.seq_num + + +def _save_all(im, fp, filename): + _save(im, fp, filename, save_all=True) + + +def _save(im, fp, filename, chunk=putchunk, save_all=False): + # save an image to disk (called by the save method) + + mode = im.mode + + if mode == "P": + + # + # attempt to minimize storage requirements for palette images + if "bits" in im.encoderinfo: + # number of bits specified by user + colors = 1 << im.encoderinfo["bits"] + else: + # check palette contents + if im.palette: + colors = max(min(len(im.palette.getdata()[1]) // 3, 256), 2) + else: + colors = 256 + + if colors <= 2: + bits = 1 + elif colors <= 4: + bits = 2 + elif colors <= 16: + bits = 4 + else: + bits = 8 + if bits != 8: + mode = f"{mode};{bits}" + + # encoder options + im.encoderconfig = ( + im.encoderinfo.get("optimize", False), + im.encoderinfo.get("compress_level", -1), + im.encoderinfo.get("compress_type", -1), + im.encoderinfo.get("dictionary", b""), + ) + + # get the corresponding PNG mode + try: + rawmode, mode = _OUTMODES[mode] + except KeyError as e: + raise OSError(f"cannot write mode {mode} as PNG") from e + + # + # write minimal PNG file + + fp.write(_MAGIC) + + chunk( + fp, + b"IHDR", + o32(im.size[0]), # 0: size + o32(im.size[1]), + mode, # 8: depth/type + b"\0", # 10: compression + b"\0", # 11: filter category + b"\0", # 12: interlace flag + ) + + chunks = [b"cHRM", b"gAMA", b"sBIT", b"sRGB", b"tIME"] + + icc = im.encoderinfo.get("icc_profile", im.info.get("icc_profile")) + if icc: + # ICC profile + # according to PNG spec, the iCCP chunk contains: + # Profile name 1-79 bytes (character string) + # Null separator 1 byte (null character) + # Compression method 1 byte (0) + # Compressed profile n bytes (zlib with deflate compression) + name = b"ICC Profile" + data = name + b"\0\0" + zlib.compress(icc) + chunk(fp, b"iCCP", data) + + # You must either have sRGB or iCCP. + # Disallow sRGB chunks when an iCCP-chunk has been emitted. + chunks.remove(b"sRGB") + + info = im.encoderinfo.get("pnginfo") + if info: + chunks_multiple_allowed = [b"sPLT", b"iTXt", b"tEXt", b"zTXt"] + for info_chunk in info.chunks: + cid, data = info_chunk[:2] + if cid in chunks: + chunks.remove(cid) + chunk(fp, cid, data) + elif cid in chunks_multiple_allowed: + chunk(fp, cid, data) + elif cid[1:2].islower(): + # Private chunk + after_idat = info_chunk[2:3] + if not after_idat: + chunk(fp, cid, data) + + if im.mode == "P": + palette_byte_number = (2 ** bits) * 3 + palette_bytes = im.im.getpalette("RGB")[:palette_byte_number] + while len(palette_bytes) < palette_byte_number: + palette_bytes += b"\0" + chunk(fp, b"PLTE", palette_bytes) + + transparency = im.encoderinfo.get("transparency", im.info.get("transparency", None)) + + if transparency or transparency == 0: + if im.mode == "P": + # limit to actual palette size + alpha_bytes = 2 ** bits + if isinstance(transparency, bytes): + chunk(fp, b"tRNS", transparency[:alpha_bytes]) + else: + transparency = max(0, min(255, transparency)) + alpha = b"\xFF" * transparency + b"\0" + chunk(fp, b"tRNS", alpha[:alpha_bytes]) + elif im.mode in ("1", "L", "I"): + transparency = max(0, min(65535, transparency)) + chunk(fp, b"tRNS", o16(transparency)) + elif im.mode == "RGB": + red, green, blue = transparency + chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue)) + else: + if "transparency" in im.encoderinfo: + # don't bother with transparency if it's an RGBA + # and it's in the info dict. It's probably just stale. + raise OSError("cannot use transparency for this mode") + else: + if im.mode == "P" and im.im.getpalettemode() == "RGBA": + alpha = im.im.getpalette("RGBA", "A") + alpha_bytes = 2 ** bits + chunk(fp, b"tRNS", alpha[:alpha_bytes]) + + dpi = im.encoderinfo.get("dpi") + if dpi: + chunk( + fp, + b"pHYs", + o32(int(dpi[0] / 0.0254 + 0.5)), + o32(int(dpi[1] / 0.0254 + 0.5)), + b"\x01", + ) + + if info: + chunks = [b"bKGD", b"hIST"] + for info_chunk in info.chunks: + cid, data = info_chunk[:2] + if cid in chunks: + chunks.remove(cid) + chunk(fp, cid, data) + + exif = im.encoderinfo.get("exif", im.info.get("exif")) + if exif: + if isinstance(exif, Image.Exif): + exif = exif.tobytes(8) + if exif.startswith(b"Exif\x00\x00"): + exif = exif[6:] + chunk(fp, b"eXIf", exif) + + if save_all: + _write_multiple_frames(im, fp, chunk, rawmode) + else: + ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)]) + + if info: + for info_chunk in info.chunks: + cid, data = info_chunk[:2] + if cid[1:2].islower(): + # Private chunk + after_idat = info_chunk[2:3] + if after_idat: + chunk(fp, cid, data) + + chunk(fp, b"IEND", b"") + + if hasattr(fp, "flush"): + fp.flush() + + +# -------------------------------------------------------------------- +# PNG chunk converter + + +def getchunks(im, **params): + """Return a list of PNG chunks representing this image.""" + + class collector: + data = [] + + def write(self, data): + pass + + def append(self, chunk): + self.data.append(chunk) + + def append(fp, cid, *data): + data = b"".join(data) + crc = o32(_crc32(data, _crc32(cid))) + fp.append((cid, data, crc)) + + fp = collector() + + try: + im.encoderinfo = params + _save(im, fp, None, append) + finally: + del im.encoderinfo + + return fp.data + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(PngImageFile.format, PngImageFile, _accept) +Image.register_save(PngImageFile.format, _save) +Image.register_save_all(PngImageFile.format, _save_all) + +Image.register_extensions(PngImageFile.format, [".png", ".apng"]) + +Image.register_mime(PngImageFile.format, "image/png") diff --git a/venv/lib/python3.11/site-packages/PIL/PpmImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/PpmImagePlugin.py new file mode 100644 index 00000000..abf4d651 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/PpmImagePlugin.py @@ -0,0 +1,164 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PPM support for PIL +# +# History: +# 96-03-24 fl Created +# 98-03-06 fl Write RGBA images (as RGB, that is) +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + + +from . import Image, ImageFile + +# +# -------------------------------------------------------------------- + +b_whitespace = b"\x20\x09\x0a\x0b\x0c\x0d" + +MODES = { + # standard + b"P4": "1", + b"P5": "L", + b"P6": "RGB", + # extensions + b"P0CMYK": "CMYK", + # PIL extensions (for test purposes only) + b"PyP": "P", + b"PyRGBA": "RGBA", + b"PyCMYK": "CMYK", +} + + +def _accept(prefix): + return prefix[0:1] == b"P" and prefix[1] in b"0456y" + + +## +# Image plugin for PBM, PGM, and PPM images. + + +class PpmImageFile(ImageFile.ImageFile): + + format = "PPM" + format_description = "Pbmplus image" + + def _token(self, s=b""): + while True: # read until next whitespace + c = self.fp.read(1) + if not c or c in b_whitespace: + break + if c > b"\x79": + raise ValueError("Expected ASCII value, found binary") + s = s + c + if len(s) > 9: + raise ValueError("Expected int, got > 9 digits") + return s + + def _open(self): + + # check magic + s = self.fp.read(1) + if s != b"P": + raise SyntaxError("not a PPM file") + magic_number = self._token(s) + mode = MODES[magic_number] + + self.custom_mimetype = { + b"P4": "image/x-portable-bitmap", + b"P5": "image/x-portable-graymap", + b"P6": "image/x-portable-pixmap", + }.get(magic_number) + + if mode == "1": + self.mode = "1" + rawmode = "1;I" + else: + self.mode = rawmode = mode + + for ix in range(3): + while True: + while True: + s = self.fp.read(1) + if s not in b_whitespace: + break + if s == b"": + raise ValueError("File does not extend beyond magic number") + if s != b"#": + break + s = self.fp.readline() + s = int(self._token(s)) + if ix == 0: + xsize = s + elif ix == 1: + ysize = s + if mode == "1": + break + elif ix == 2: + # maxgrey + if s > 255: + if not mode == "L": + raise ValueError(f"Too many colors for band: {s}") + if s < 2 ** 16: + self.mode = "I" + rawmode = "I;16B" + else: + self.mode = "I" + rawmode = "I;32B" + + self._size = xsize, ysize + self.tile = [("raw", (0, 0, xsize, ysize), self.fp.tell(), (rawmode, 0, 1))] + + +# +# -------------------------------------------------------------------- + + +def _save(im, fp, filename): + if im.mode == "1": + rawmode, head = "1;I", b"P4" + elif im.mode == "L": + rawmode, head = "L", b"P5" + elif im.mode == "I": + if im.getextrema()[1] < 2 ** 16: + rawmode, head = "I;16B", b"P5" + else: + rawmode, head = "I;32B", b"P5" + elif im.mode == "RGB": + rawmode, head = "RGB", b"P6" + elif im.mode == "RGBA": + rawmode, head = "RGB", b"P6" + else: + raise OSError(f"cannot write mode {im.mode} as PPM") + fp.write(head + ("\n%d %d\n" % im.size).encode("ascii")) + if head == b"P6": + fp.write(b"255\n") + if head == b"P5": + if rawmode == "L": + fp.write(b"255\n") + elif rawmode == "I;16B": + fp.write(b"65535\n") + elif rawmode == "I;32B": + fp.write(b"2147483648\n") + ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))]) + + # ALTERNATIVE: save via builtin debug function + # im._dump(filename) + + +# +# -------------------------------------------------------------------- + + +Image.register_open(PpmImageFile.format, PpmImageFile, _accept) +Image.register_save(PpmImageFile.format, _save) + +Image.register_extensions(PpmImageFile.format, [".pbm", ".pgm", ".ppm", ".pnm"]) + +Image.register_mime(PpmImageFile.format, "image/x-portable-anymap") diff --git a/venv/lib/python3.11/site-packages/PIL/PsdImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/PsdImagePlugin.py new file mode 100644 index 00000000..d3799edc --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/PsdImagePlugin.py @@ -0,0 +1,313 @@ +# +# The Python Imaging Library +# $Id$ +# +# Adobe PSD 2.5/3.0 file handling +# +# History: +# 1995-09-01 fl Created +# 1997-01-03 fl Read most PSD images +# 1997-01-18 fl Fixed P and CMYK support +# 2001-10-21 fl Added seek/tell support (for layers) +# +# Copyright (c) 1997-2001 by Secret Labs AB. +# Copyright (c) 1995-2001 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import io + +from . import Image, ImageFile, ImagePalette +from ._binary import i8 +from ._binary import i16be as i16 +from ._binary import i32be as i32 + +MODES = { + # (photoshop mode, bits) -> (pil mode, required channels) + (0, 1): ("1", 1), + (0, 8): ("L", 1), + (1, 8): ("L", 1), + (2, 8): ("P", 1), + (3, 8): ("RGB", 3), + (4, 8): ("CMYK", 4), + (7, 8): ("L", 1), # FIXME: multilayer + (8, 8): ("L", 1), # duotone + (9, 8): ("LAB", 3), +} + + +# --------------------------------------------------------------------. +# read PSD images + + +def _accept(prefix): + return prefix[:4] == b"8BPS" + + +## +# Image plugin for Photoshop images. + + +class PsdImageFile(ImageFile.ImageFile): + + format = "PSD" + format_description = "Adobe Photoshop" + _close_exclusive_fp_after_loading = False + + def _open(self): + + read = self.fp.read + + # + # header + + s = read(26) + if not _accept(s) or i16(s, 4) != 1: + raise SyntaxError("not a PSD file") + + psd_bits = i16(s, 22) + psd_channels = i16(s, 12) + psd_mode = i16(s, 24) + + mode, channels = MODES[(psd_mode, psd_bits)] + + if channels > psd_channels: + raise OSError("not enough channels") + + self.mode = mode + self._size = i32(s, 18), i32(s, 14) + + # + # color mode data + + size = i32(read(4)) + if size: + data = read(size) + if mode == "P" and size == 768: + self.palette = ImagePalette.raw("RGB;L", data) + + # + # image resources + + self.resources = [] + + size = i32(read(4)) + if size: + # load resources + end = self.fp.tell() + size + while self.fp.tell() < end: + read(4) # signature + id = i16(read(2)) + name = read(i8(read(1))) + if not (len(name) & 1): + read(1) # padding + data = read(i32(read(4))) + if len(data) & 1: + read(1) # padding + self.resources.append((id, name, data)) + if id == 1039: # ICC profile + self.info["icc_profile"] = data + + # + # layer and mask information + + self.layers = [] + + size = i32(read(4)) + if size: + end = self.fp.tell() + size + size = i32(read(4)) + if size: + self.layers = _layerinfo(self.fp) + self.fp.seek(end) + self.n_frames = len(self.layers) + self.is_animated = self.n_frames > 1 + + # + # image descriptor + + self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels) + + # keep the file open + self.__fp = self.fp + self.frame = 1 + self._min_frame = 1 + + def seek(self, layer): + if not self._seek_check(layer): + return + + # seek to given layer (1..max) + try: + name, mode, bbox, tile = self.layers[layer - 1] + self.mode = mode + self.tile = tile + self.frame = layer + self.fp = self.__fp + return name, bbox + except IndexError as e: + raise EOFError("no such layer") from e + + def tell(self): + # return layer number (0=image, 1..max=layers) + return self.frame + + def load_prepare(self): + # create image memory if necessary + if not self.im or self.im.mode != self.mode or self.im.size != self.size: + self.im = Image.core.fill(self.mode, self.size, 0) + # create palette (optional) + if self.mode == "P": + Image.Image.load(self) + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +def _layerinfo(file): + # read layerinfo block + layers = [] + read = file.read + for i in range(abs(i16(read(2)))): + + # bounding box + y0 = i32(read(4)) + x0 = i32(read(4)) + y1 = i32(read(4)) + x1 = i32(read(4)) + + # image info + info = [] + mode = [] + types = list(range(i16(read(2)))) + if len(types) > 4: + continue + + for i in types: + type = i16(read(2)) + + if type == 65535: + m = "A" + else: + m = "RGBA"[type] + + mode.append(m) + size = i32(read(4)) + info.append((m, size)) + + # figure out the image mode + mode.sort() + if mode == ["R"]: + mode = "L" + elif mode == ["B", "G", "R"]: + mode = "RGB" + elif mode == ["A", "B", "G", "R"]: + mode = "RGBA" + else: + mode = None # unknown + + # skip over blend flags and extra information + read(12) # filler + name = "" + size = i32(read(4)) # length of the extra data field + combined = 0 + if size: + data_end = file.tell() + size + + length = i32(read(4)) + if length: + file.seek(length - 16, io.SEEK_CUR) + combined += length + 4 + + length = i32(read(4)) + if length: + file.seek(length, io.SEEK_CUR) + combined += length + 4 + + length = i8(read(1)) + if length: + # Don't know the proper encoding, + # Latin-1 should be a good guess + name = read(length).decode("latin-1", "replace") + combined += length + 1 + + file.seek(data_end) + layers.append((name, mode, (x0, y0, x1, y1))) + + # get tiles + i = 0 + for name, mode, bbox in layers: + tile = [] + for m in mode: + t = _maketile(file, m, bbox, 1) + if t: + tile.extend(t) + layers[i] = name, mode, bbox, tile + i += 1 + + return layers + + +def _maketile(file, mode, bbox, channels): + + tile = None + read = file.read + + compression = i16(read(2)) + + xsize = bbox[2] - bbox[0] + ysize = bbox[3] - bbox[1] + + offset = file.tell() + + if compression == 0: + # + # raw compression + tile = [] + for channel in range(channels): + layer = mode[channel] + if mode == "CMYK": + layer += ";I" + tile.append(("raw", bbox, offset, layer)) + offset = offset + xsize * ysize + + elif compression == 1: + # + # packbits compression + i = 0 + tile = [] + bytecount = read(channels * ysize * 2) + offset = file.tell() + for channel in range(channels): + layer = mode[channel] + if mode == "CMYK": + layer += ";I" + tile.append(("packbits", bbox, offset, layer)) + for y in range(ysize): + offset = offset + i16(bytecount, i) + i += 2 + + file.seek(offset) + + if offset & 1: + read(1) # padding + + return tile + + +# -------------------------------------------------------------------- +# registry + + +Image.register_open(PsdImageFile.format, PsdImageFile, _accept) + +Image.register_extension(PsdImageFile.format, ".psd") + +Image.register_mime(PsdImageFile.format, "image/vnd.adobe.photoshop") diff --git a/venv/lib/python3.11/site-packages/PIL/PyAccess.py b/venv/lib/python3.11/site-packages/PIL/PyAccess.py new file mode 100644 index 00000000..494f5f9f --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/PyAccess.py @@ -0,0 +1,352 @@ +# +# The Python Imaging Library +# Pillow fork +# +# Python implementation of the PixelAccess Object +# +# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-2009 by Fredrik Lundh. +# Copyright (c) 2013 Eric Soroos +# +# See the README file for information on usage and redistribution +# + +# Notes: +# +# * Implements the pixel access object following Access. +# * Does not implement the line functions, as they don't appear to be used +# * Taking only the tuple form, which is used from python. +# * Fill.c uses the integer form, but it's still going to use the old +# Access.c implementation. +# + +import logging +import sys + +try: + from cffi import FFI + + defs = """ + struct Pixel_RGBA { + unsigned char r,g,b,a; + }; + struct Pixel_I16 { + unsigned char l,r; + }; + """ + ffi = FFI() + ffi.cdef(defs) +except ImportError as ex: + # Allow error import for doc purposes, but error out when accessing + # anything in core. + from ._util import deferred_error + + FFI = ffi = deferred_error(ex) + +logger = logging.getLogger(__name__) + + +class PyAccess: + def __init__(self, img, readonly=False): + vals = dict(img.im.unsafe_ptrs) + self.readonly = readonly + self.image8 = ffi.cast("unsigned char **", vals["image8"]) + self.image32 = ffi.cast("int **", vals["image32"]) + self.image = ffi.cast("unsigned char **", vals["image"]) + self.xsize, self.ysize = img.im.size + + # Keep pointer to im object to prevent dereferencing. + self._im = img.im + if self._im.mode == "P": + self._palette = img.palette + + # Debugging is polluting test traces, only useful here + # when hacking on PyAccess + # logger.debug("%s", vals) + self._post_init() + + def _post_init(self): + pass + + def __setitem__(self, xy, color): + """ + Modifies the pixel at x,y. The color is given as a single + numerical value for single band images, and a tuple for + multi-band images + + :param xy: The pixel coordinate, given as (x, y). See + :ref:`coordinate-system`. + :param color: The pixel value. + """ + if self.readonly: + raise ValueError("Attempt to putpixel a read only image") + (x, y) = xy + if x < 0: + x = self.xsize + x + if y < 0: + y = self.ysize + y + (x, y) = self.check_xy((x, y)) + + if ( + self._im.mode == "P" + and isinstance(color, (list, tuple)) + and len(color) in [3, 4] + ): + # RGB or RGBA value for a P image + color = self._palette.getcolor(color) + + return self.set_pixel(x, y, color) + + def __getitem__(self, xy): + """ + Returns the pixel at x,y. The pixel is returned as a single + value for single band images or a tuple for multiple band + images + + :param xy: The pixel coordinate, given as (x, y). See + :ref:`coordinate-system`. + :returns: a pixel value for single band images, a tuple of + pixel values for multiband images. + """ + (x, y) = xy + if x < 0: + x = self.xsize + x + if y < 0: + y = self.ysize + y + (x, y) = self.check_xy((x, y)) + return self.get_pixel(x, y) + + putpixel = __setitem__ + getpixel = __getitem__ + + def check_xy(self, xy): + (x, y) = xy + if not (0 <= x < self.xsize and 0 <= y < self.ysize): + raise ValueError("pixel location out of range") + return xy + + +class _PyAccess32_2(PyAccess): + """ PA, LA, stored in first and last bytes of a 32 bit word """ + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.a) + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + # tuple + pixel.r = min(color[0], 255) + pixel.a = min(color[1], 255) + + +class _PyAccess32_3(PyAccess): + """ RGB and friends, stored in the first three bytes of a 32 bit word """ + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.g, pixel.b) + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + # tuple + pixel.r = min(color[0], 255) + pixel.g = min(color[1], 255) + pixel.b = min(color[2], 255) + pixel.a = 255 + + +class _PyAccess32_4(PyAccess): + """ RGBA etc, all 4 bytes of a 32 bit word """ + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.g, pixel.b, pixel.a) + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + # tuple + pixel.r = min(color[0], 255) + pixel.g = min(color[1], 255) + pixel.b = min(color[2], 255) + pixel.a = min(color[3], 255) + + +class _PyAccess8(PyAccess): + """ 1, L, P, 8 bit images stored as uint8 """ + + def _post_init(self, *args, **kwargs): + self.pixels = self.image8 + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + try: + # integer + self.pixels[y][x] = min(color, 255) + except TypeError: + # tuple + self.pixels[y][x] = min(color[0], 255) + + +class _PyAccessI16_N(PyAccess): + """ I;16 access, native bitendian without conversion """ + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("unsigned short **", self.image) + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + try: + # integer + self.pixels[y][x] = min(color, 65535) + except TypeError: + # tuple + self.pixels[y][x] = min(color[0], 65535) + + +class _PyAccessI16_L(PyAccess): + """ I;16L access, with conversion """ + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_I16 **", self.image) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return pixel.l + pixel.r * 256 + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + try: + color = min(color, 65535) + except TypeError: + color = min(color[0], 65535) + + pixel.l = color & 0xFF # noqa: E741 + pixel.r = color >> 8 + + +class _PyAccessI16_B(PyAccess): + """ I;16B access, with conversion """ + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_I16 **", self.image) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return pixel.l * 256 + pixel.r + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + try: + color = min(color, 65535) + except Exception: + color = min(color[0], 65535) + + pixel.l = color >> 8 # noqa: E741 + pixel.r = color & 0xFF + + +class _PyAccessI32_N(PyAccess): + """ Signed Int32 access, native endian """ + + def _post_init(self, *args, **kwargs): + self.pixels = self.image32 + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + self.pixels[y][x] = color + + +class _PyAccessI32_Swap(PyAccess): + """ I;32L/B access, with byteswapping conversion """ + + def _post_init(self, *args, **kwargs): + self.pixels = self.image32 + + def reverse(self, i): + orig = ffi.new("int *", i) + chars = ffi.cast("unsigned char *", orig) + chars[0], chars[1], chars[2], chars[3] = chars[3], chars[2], chars[1], chars[0] + return ffi.cast("int *", chars)[0] + + def get_pixel(self, x, y): + return self.reverse(self.pixels[y][x]) + + def set_pixel(self, x, y, color): + self.pixels[y][x] = self.reverse(color) + + +class _PyAccessF(PyAccess): + """ 32 bit float access """ + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("float **", self.image32) + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + try: + # not a tuple + self.pixels[y][x] = color + except TypeError: + # tuple + self.pixels[y][x] = color[0] + + +mode_map = { + "1": _PyAccess8, + "L": _PyAccess8, + "P": _PyAccess8, + "LA": _PyAccess32_2, + "La": _PyAccess32_2, + "PA": _PyAccess32_2, + "RGB": _PyAccess32_3, + "LAB": _PyAccess32_3, + "HSV": _PyAccess32_3, + "YCbCr": _PyAccess32_3, + "RGBA": _PyAccess32_4, + "RGBa": _PyAccess32_4, + "RGBX": _PyAccess32_4, + "CMYK": _PyAccess32_4, + "F": _PyAccessF, + "I": _PyAccessI32_N, +} + +if sys.byteorder == "little": + mode_map["I;16"] = _PyAccessI16_N + mode_map["I;16L"] = _PyAccessI16_N + mode_map["I;16B"] = _PyAccessI16_B + + mode_map["I;32L"] = _PyAccessI32_N + mode_map["I;32B"] = _PyAccessI32_Swap +else: + mode_map["I;16"] = _PyAccessI16_L + mode_map["I;16L"] = _PyAccessI16_L + mode_map["I;16B"] = _PyAccessI16_N + + mode_map["I;32L"] = _PyAccessI32_Swap + mode_map["I;32B"] = _PyAccessI32_N + + +def new(img, readonly=False): + access_type = mode_map.get(img.mode, None) + if not access_type: + logger.debug("PyAccess Not Implemented: %s", img.mode) + return None + return access_type(img, readonly) diff --git a/venv/lib/python3.11/site-packages/PIL/SgiImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/SgiImagePlugin.py new file mode 100644 index 00000000..d0f7c999 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/SgiImagePlugin.py @@ -0,0 +1,229 @@ +# +# The Python Imaging Library. +# $Id$ +# +# SGI image file handling +# +# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli. +# +# +# +# History: +# 2017-22-07 mb Add RLE decompression +# 2016-16-10 mb Add save method without compression +# 1995-09-10 fl Created +# +# Copyright (c) 2016 by Mickael Bonfill. +# Copyright (c) 2008 by Karsten Hiddemann. +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1995 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +import os +import struct + +from . import Image, ImageFile +from ._binary import i16be as i16 +from ._binary import o8 + + +def _accept(prefix): + return len(prefix) >= 2 and i16(prefix) == 474 + + +MODES = { + (1, 1, 1): "L", + (1, 2, 1): "L", + (2, 1, 1): "L;16B", + (2, 2, 1): "L;16B", + (1, 3, 3): "RGB", + (2, 3, 3): "RGB;16B", + (1, 3, 4): "RGBA", + (2, 3, 4): "RGBA;16B", +} + + +## +# Image plugin for SGI images. +class SgiImageFile(ImageFile.ImageFile): + + format = "SGI" + format_description = "SGI Image File Format" + + def _open(self): + + # HEAD + headlen = 512 + s = self.fp.read(headlen) + + if not _accept(s): + raise ValueError("Not an SGI image file") + + # compression : verbatim or RLE + compression = s[2] + + # bpc : 1 or 2 bytes (8bits or 16bits) + bpc = s[3] + + # dimension : 1, 2 or 3 (depending on xsize, ysize and zsize) + dimension = i16(s, 4) + + # xsize : width + xsize = i16(s, 6) + + # ysize : height + ysize = i16(s, 8) + + # zsize : channels count + zsize = i16(s, 10) + + # layout + layout = bpc, dimension, zsize + + # determine mode from bits/zsize + rawmode = "" + try: + rawmode = MODES[layout] + except KeyError: + pass + + if rawmode == "": + raise ValueError("Unsupported SGI image mode") + + self._size = xsize, ysize + self.mode = rawmode.split(";")[0] + if self.mode == "RGB": + self.custom_mimetype = "image/rgb" + + # orientation -1 : scanlines begins at the bottom-left corner + orientation = -1 + + # decoder info + if compression == 0: + pagesize = xsize * ysize * bpc + if bpc == 2: + self.tile = [ + ("SGI16", (0, 0) + self.size, headlen, (self.mode, 0, orientation)) + ] + else: + self.tile = [] + offset = headlen + for layer in self.mode: + self.tile.append( + ("raw", (0, 0) + self.size, offset, (layer, 0, orientation)) + ) + offset += pagesize + elif compression == 1: + self.tile = [ + ("sgi_rle", (0, 0) + self.size, headlen, (rawmode, orientation, bpc)) + ] + + +def _save(im, fp, filename): + if im.mode != "RGB" and im.mode != "RGBA" and im.mode != "L": + raise ValueError("Unsupported SGI image mode") + + # Get the keyword arguments + info = im.encoderinfo + + # Byte-per-pixel precision, 1 = 8bits per pixel + bpc = info.get("bpc", 1) + + if bpc not in (1, 2): + raise ValueError("Unsupported number of bytes per pixel") + + # Flip the image, since the origin of SGI file is the bottom-left corner + orientation = -1 + # Define the file as SGI File Format + magicNumber = 474 + # Run-Length Encoding Compression - Unsupported at this time + rle = 0 + + # Number of dimensions (x,y,z) + dim = 3 + # X Dimension = width / Y Dimension = height + x, y = im.size + if im.mode == "L" and y == 1: + dim = 1 + elif im.mode == "L": + dim = 2 + # Z Dimension: Number of channels + z = len(im.mode) + + if dim == 1 or dim == 2: + z = 1 + + # assert we've got the right number of bands. + if len(im.getbands()) != z: + raise ValueError( + f"incorrect number of bands in SGI write: {z} vs {len(im.getbands())}" + ) + + # Minimum Byte value + pinmin = 0 + # Maximum Byte value (255 = 8bits per pixel) + pinmax = 255 + # Image name (79 characters max, truncated below in write) + imgName = os.path.splitext(os.path.basename(filename))[0] + imgName = imgName.encode("ascii", "ignore") + # Standard representation of pixel in the file + colormap = 0 + fp.write(struct.pack(">h", magicNumber)) + fp.write(o8(rle)) + fp.write(o8(bpc)) + fp.write(struct.pack(">H", dim)) + fp.write(struct.pack(">H", x)) + fp.write(struct.pack(">H", y)) + fp.write(struct.pack(">H", z)) + fp.write(struct.pack(">l", pinmin)) + fp.write(struct.pack(">l", pinmax)) + fp.write(struct.pack("4s", b"")) # dummy + fp.write(struct.pack("79s", imgName)) # truncates to 79 chars + fp.write(struct.pack("s", b"")) # force null byte after imgname + fp.write(struct.pack(">l", colormap)) + fp.write(struct.pack("404s", b"")) # dummy + + rawmode = "L" + if bpc == 2: + rawmode = "L;16B" + + for channel in im.split(): + fp.write(channel.tobytes("raw", rawmode, 0, orientation)) + + fp.close() + + +class SGI16Decoder(ImageFile.PyDecoder): + _pulls_fd = True + + def decode(self, buffer): + rawmode, stride, orientation = self.args + pagesize = self.state.xsize * self.state.ysize + zsize = len(self.mode) + self.fd.seek(512) + + for band in range(zsize): + channel = Image.new("L", (self.state.xsize, self.state.ysize)) + channel.frombytes( + self.fd.read(2 * pagesize), "raw", "L;16B", stride, orientation + ) + self.im.putband(channel.im, band) + + return -1, 0 + + +# +# registry + + +Image.register_decoder("SGI16", SGI16Decoder) +Image.register_open(SgiImageFile.format, SgiImageFile, _accept) +Image.register_save(SgiImageFile.format, _save) +Image.register_mime(SgiImageFile.format, "image/sgi") + +Image.register_extensions(SgiImageFile.format, [".bw", ".rgb", ".rgba", ".sgi"]) + +# End of file diff --git a/venv/lib/python3.11/site-packages/PIL/SpiderImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/SpiderImagePlugin.py new file mode 100644 index 00000000..819f2ed0 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/SpiderImagePlugin.py @@ -0,0 +1,324 @@ +# +# The Python Imaging Library. +# +# SPIDER image file handling +# +# History: +# 2004-08-02 Created BB +# 2006-03-02 added save method +# 2006-03-13 added support for stack images +# +# Copyright (c) 2004 by Health Research Inc. (HRI) RENSSELAER, NY 12144. +# Copyright (c) 2004 by William Baxter. +# Copyright (c) 2004 by Secret Labs AB. +# Copyright (c) 2004 by Fredrik Lundh. +# + +## +# Image plugin for the Spider image format. This format is is used +# by the SPIDER software, in processing image data from electron +# microscopy and tomography. +## + +# +# SpiderImagePlugin.py +# +# The Spider image format is used by SPIDER software, in processing +# image data from electron microscopy and tomography. +# +# Spider home page: +# https://spider.wadsworth.org/spider_doc/spider/docs/spider.html +# +# Details about the Spider image format: +# https://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html +# +import os +import struct +import sys + +from PIL import Image, ImageFile + + +def isInt(f): + try: + i = int(f) + if f - i == 0: + return 1 + else: + return 0 + except (ValueError, OverflowError): + return 0 + + +iforms = [1, 3, -11, -12, -21, -22] + + +# There is no magic number to identify Spider files, so just check a +# series of header locations to see if they have reasonable values. +# Returns no. of bytes in the header, if it is a valid Spider header, +# otherwise returns 0 + + +def isSpiderHeader(t): + h = (99,) + t # add 1 value so can use spider header index start=1 + # header values 1,2,5,12,13,22,23 should be integers + for i in [1, 2, 5, 12, 13, 22, 23]: + if not isInt(h[i]): + return 0 + # check iform + iform = int(h[5]) + if iform not in iforms: + return 0 + # check other header values + labrec = int(h[13]) # no. records in file header + labbyt = int(h[22]) # total no. of bytes in header + lenbyt = int(h[23]) # record length in bytes + if labbyt != (labrec * lenbyt): + return 0 + # looks like a valid header + return labbyt + + +def isSpiderImage(filename): + with open(filename, "rb") as fp: + f = fp.read(92) # read 23 * 4 bytes + t = struct.unpack(">23f", f) # try big-endian first + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + t = struct.unpack("<23f", f) # little-endian + hdrlen = isSpiderHeader(t) + return hdrlen + + +class SpiderImageFile(ImageFile.ImageFile): + + format = "SPIDER" + format_description = "Spider 2D image" + _close_exclusive_fp_after_loading = False + + def _open(self): + # check header + n = 27 * 4 # read 27 float values + f = self.fp.read(n) + + try: + self.bigendian = 1 + t = struct.unpack(">27f", f) # try big-endian first + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + self.bigendian = 0 + t = struct.unpack("<27f", f) # little-endian + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + raise SyntaxError("not a valid Spider file") + except struct.error as e: + raise SyntaxError("not a valid Spider file") from e + + h = (99,) + t # add 1 value : spider header index starts at 1 + iform = int(h[5]) + if iform != 1: + raise SyntaxError("not a Spider 2D image") + + self._size = int(h[12]), int(h[2]) # size in pixels (width, height) + self.istack = int(h[24]) + self.imgnumber = int(h[27]) + + if self.istack == 0 and self.imgnumber == 0: + # stk=0, img=0: a regular 2D image + offset = hdrlen + self._nimages = 1 + elif self.istack > 0 and self.imgnumber == 0: + # stk>0, img=0: Opening the stack for the first time + self.imgbytes = int(h[12]) * int(h[2]) * 4 + self.hdrlen = hdrlen + self._nimages = int(h[26]) + # Point to the first image in the stack + offset = hdrlen * 2 + self.imgnumber = 1 + elif self.istack == 0 and self.imgnumber > 0: + # stk=0, img>0: an image within the stack + offset = hdrlen + self.stkoffset + self.istack = 2 # So Image knows it's still a stack + else: + raise SyntaxError("inconsistent stack header values") + + if self.bigendian: + self.rawmode = "F;32BF" + else: + self.rawmode = "F;32F" + self.mode = "F" + + self.tile = [("raw", (0, 0) + self.size, offset, (self.rawmode, 0, 1))] + self.__fp = self.fp # FIXME: hack + + @property + def n_frames(self): + return self._nimages + + @property + def is_animated(self): + return self._nimages > 1 + + # 1st image index is zero (although SPIDER imgnumber starts at 1) + def tell(self): + if self.imgnumber < 1: + return 0 + else: + return self.imgnumber - 1 + + def seek(self, frame): + if self.istack == 0: + raise EOFError("attempt to seek in a non-stack file") + if not self._seek_check(frame): + return + self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) + self.fp = self.__fp + self.fp.seek(self.stkoffset) + self._open() + + # returns a byte image after rescaling to 0..255 + def convert2byte(self, depth=255): + (minimum, maximum) = self.getextrema() + m = 1 + if maximum != minimum: + m = depth / (maximum - minimum) + b = -m * minimum + return self.point(lambda i, m=m, b=b: i * m + b).convert("L") + + # returns a ImageTk.PhotoImage object, after rescaling to 0..255 + def tkPhotoImage(self): + from PIL import ImageTk + + return ImageTk.PhotoImage(self.convert2byte(), palette=256) + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +# -------------------------------------------------------------------- +# Image series + +# given a list of filenames, return a list of images +def loadImageSeries(filelist=None): + """create a list of :py:class:`~PIL.Image.Image` objects for use in a montage""" + if filelist is None or len(filelist) < 1: + return + + imglist = [] + for img in filelist: + if not os.path.exists(img): + print(f"unable to find {img}") + continue + try: + with Image.open(img) as im: + im = im.convert2byte() + except Exception: + if not isSpiderImage(img): + print(img + " is not a Spider image file") + continue + im.info["filename"] = img + imglist.append(im) + return imglist + + +# -------------------------------------------------------------------- +# For saving images in Spider format + + +def makeSpiderHeader(im): + nsam, nrow = im.size + lenbyt = nsam * 4 # There are labrec records in the header + labrec = int(1024 / lenbyt) + if 1024 % lenbyt != 0: + labrec += 1 + labbyt = labrec * lenbyt + hdr = [] + nvalues = int(labbyt / 4) + for i in range(nvalues): + hdr.append(0.0) + + if len(hdr) < 23: + return [] + + # NB these are Fortran indices + hdr[1] = 1.0 # nslice (=1 for an image) + hdr[2] = float(nrow) # number of rows per slice + hdr[5] = 1.0 # iform for 2D image + hdr[12] = float(nsam) # number of pixels per line + hdr[13] = float(labrec) # number of records in file header + hdr[22] = float(labbyt) # total number of bytes in header + hdr[23] = float(lenbyt) # record length in bytes + + # adjust for Fortran indexing + hdr = hdr[1:] + hdr.append(0.0) + # pack binary data into a string + hdrstr = [] + for v in hdr: + hdrstr.append(struct.pack("f", v)) + return hdrstr + + +def _save(im, fp, filename): + if im.mode[0] != "F": + im = im.convert("F") + + hdr = makeSpiderHeader(im) + if len(hdr) < 256: + raise OSError("Error creating Spider header") + + # write the SPIDER header + fp.writelines(hdr) + + rawmode = "F;32NF" # 32-bit native floating point + ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))]) + + +def _save_spider(im, fp, filename): + # get the filename extension and register it with Image + ext = os.path.splitext(filename)[1] + Image.register_extension(SpiderImageFile.format, ext) + _save(im, fp, filename) + + +# -------------------------------------------------------------------- + + +Image.register_open(SpiderImageFile.format, SpiderImageFile) +Image.register_save(SpiderImageFile.format, _save_spider) + +if __name__ == "__main__": + + if len(sys.argv) < 2: + print("Syntax: python SpiderImagePlugin.py [infile] [outfile]") + sys.exit() + + filename = sys.argv[1] + if not isSpiderImage(filename): + print("input image must be in Spider format") + sys.exit() + + with Image.open(filename) as im: + print("image: " + str(im)) + print("format: " + str(im.format)) + print("size: " + str(im.size)) + print("mode: " + str(im.mode)) + print("max, min: ", end=" ") + print(im.getextrema()) + + if len(sys.argv) > 2: + outfile = sys.argv[2] + + # perform some image operation + im = im.transpose(Image.FLIP_LEFT_RIGHT) + print( + f"saving a flipped version of {os.path.basename(filename)} " + f"as {outfile} " + ) + im.save(outfile, SpiderImageFile.format) diff --git a/venv/lib/python3.11/site-packages/PIL/SunImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/SunImagePlugin.py new file mode 100644 index 00000000..c03759a0 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/SunImagePlugin.py @@ -0,0 +1,136 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Sun image file handling +# +# History: +# 1995-09-10 fl Created +# 1996-05-28 fl Fixed 32-bit alignment +# 1998-12-29 fl Import ImagePalette module +# 2001-12-18 fl Fixed palette loading (from Jean-Claude Rimbault) +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1995-1996 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +from . import Image, ImageFile, ImagePalette +from ._binary import i32be as i32 + + +def _accept(prefix): + return len(prefix) >= 4 and i32(prefix) == 0x59A66A95 + + +## +# Image plugin for Sun raster files. + + +class SunImageFile(ImageFile.ImageFile): + + format = "SUN" + format_description = "Sun Raster File" + + def _open(self): + + # The Sun Raster file header is 32 bytes in length + # and has the following format: + + # typedef struct _SunRaster + # { + # DWORD MagicNumber; /* Magic (identification) number */ + # DWORD Width; /* Width of image in pixels */ + # DWORD Height; /* Height of image in pixels */ + # DWORD Depth; /* Number of bits per pixel */ + # DWORD Length; /* Size of image data in bytes */ + # DWORD Type; /* Type of raster file */ + # DWORD ColorMapType; /* Type of color map */ + # DWORD ColorMapLength; /* Size of the color map in bytes */ + # } SUNRASTER; + + # HEAD + s = self.fp.read(32) + if not _accept(s): + raise SyntaxError("not an SUN raster file") + + offset = 32 + + self._size = i32(s, 4), i32(s, 8) + + depth = i32(s, 12) + # data_length = i32(s, 16) # unreliable, ignore. + file_type = i32(s, 20) + palette_type = i32(s, 24) # 0: None, 1: RGB, 2: Raw/arbitrary + palette_length = i32(s, 28) + + if depth == 1: + self.mode, rawmode = "1", "1;I" + elif depth == 4: + self.mode, rawmode = "L", "L;4" + elif depth == 8: + self.mode = rawmode = "L" + elif depth == 24: + if file_type == 3: + self.mode, rawmode = "RGB", "RGB" + else: + self.mode, rawmode = "RGB", "BGR" + elif depth == 32: + if file_type == 3: + self.mode, rawmode = "RGB", "RGBX" + else: + self.mode, rawmode = "RGB", "BGRX" + else: + raise SyntaxError("Unsupported Mode/Bit Depth") + + if palette_length: + if palette_length > 1024: + raise SyntaxError("Unsupported Color Palette Length") + + if palette_type != 1: + raise SyntaxError("Unsupported Palette Type") + + offset = offset + palette_length + self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length)) + if self.mode == "L": + self.mode = "P" + rawmode = rawmode.replace("L", "P") + + # 16 bit boundaries on stride + stride = ((self.size[0] * depth + 15) // 16) * 2 + + # file type: Type is the version (or flavor) of the bitmap + # file. The following values are typically found in the Type + # field: + # 0000h Old + # 0001h Standard + # 0002h Byte-encoded + # 0003h RGB format + # 0004h TIFF format + # 0005h IFF format + # FFFFh Experimental + + # Old and standard are the same, except for the length tag. + # byte-encoded is run-length-encoded + # RGB looks similar to standard, but RGB byte order + # TIFF and IFF mean that they were converted from T/IFF + # Experimental means that it's something else. + # (https://www.fileformat.info/format/sunraster/egff.htm) + + if file_type in (0, 1, 3, 4, 5): + self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride))] + elif file_type == 2: + self.tile = [("sun_rle", (0, 0) + self.size, offset, rawmode)] + else: + raise SyntaxError("Unsupported Sun Raster file type") + + +# +# registry + + +Image.register_open(SunImageFile.format, SunImageFile, _accept) + +Image.register_extension(SunImageFile.format, ".ras") diff --git a/venv/lib/python3.11/site-packages/PIL/TarIO.py b/venv/lib/python3.11/site-packages/PIL/TarIO.py new file mode 100644 index 00000000..d108362f --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/TarIO.py @@ -0,0 +1,65 @@ +# +# The Python Imaging Library. +# $Id$ +# +# read files from within a tar file +# +# History: +# 95-06-18 fl Created +# 96-05-28 fl Open files in binary mode +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995-96. +# +# See the README file for information on usage and redistribution. +# + +import io + +from . import ContainerIO + + +class TarIO(ContainerIO.ContainerIO): + """A file object that provides read access to a given member of a TAR file.""" + + def __init__(self, tarfile, file): + """ + Create file object. + + :param tarfile: Name of TAR file. + :param file: Name of member file. + """ + self.fh = open(tarfile, "rb") + + while True: + + s = self.fh.read(512) + if len(s) != 512: + raise OSError("unexpected end of tar file") + + name = s[:100].decode("utf-8") + i = name.find("\0") + if i == 0: + raise OSError("cannot find subfile") + if i > 0: + name = name[:i] + + size = int(s[124:135], 8) + + if file == name: + break + + self.fh.seek((size + 511) & (~511), io.SEEK_CUR) + + # Open region + super().__init__(self.fh, self.fh.tell(), size) + + # Context manager support + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def close(self): + self.fh.close() diff --git a/venv/lib/python3.11/site-packages/PIL/TgaImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/TgaImagePlugin.py new file mode 100644 index 00000000..2b936d68 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/TgaImagePlugin.py @@ -0,0 +1,248 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TGA file handling +# +# History: +# 95-09-01 fl created (reads 24-bit files only) +# 97-01-04 fl support more TGA versions, including compressed images +# 98-07-04 fl fixed orientation and alpha layer bugs +# 98-09-11 fl fixed orientation for runlength decoder +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1995-97. +# +# See the README file for information on usage and redistribution. +# + + +import warnings + +from . import Image, ImageFile, ImagePalette +from ._binary import i16le as i16 +from ._binary import o8 +from ._binary import o16le as o16 + +# +# -------------------------------------------------------------------- +# Read RGA file + + +MODES = { + # map imagetype/depth to rawmode + (1, 8): "P", + (3, 1): "1", + (3, 8): "L", + (3, 16): "LA", + (2, 16): "BGR;5", + (2, 24): "BGR", + (2, 32): "BGRA", +} + + +## +# Image plugin for Targa files. + + +class TgaImageFile(ImageFile.ImageFile): + + format = "TGA" + format_description = "Targa" + + def _open(self): + + # process header + s = self.fp.read(18) + + id_len = s[0] + + colormaptype = s[1] + imagetype = s[2] + + depth = s[16] + + flags = s[17] + + self._size = i16(s, 12), i16(s, 14) + + # validate header fields + if ( + colormaptype not in (0, 1) + or self.size[0] <= 0 + or self.size[1] <= 0 + or depth not in (1, 8, 16, 24, 32) + ): + raise SyntaxError("not a TGA file") + + # image mode + if imagetype in (3, 11): + self.mode = "L" + if depth == 1: + self.mode = "1" # ??? + elif depth == 16: + self.mode = "LA" + elif imagetype in (1, 9): + self.mode = "P" + elif imagetype in (2, 10): + self.mode = "RGB" + if depth == 32: + self.mode = "RGBA" + else: + raise SyntaxError("unknown TGA mode") + + # orientation + orientation = flags & 0x30 + if orientation == 0x20: + orientation = 1 + elif not orientation: + orientation = -1 + else: + raise SyntaxError("unknown TGA orientation") + + self.info["orientation"] = orientation + + if imagetype & 8: + self.info["compression"] = "tga_rle" + + if id_len: + self.info["id_section"] = self.fp.read(id_len) + + if colormaptype: + # read palette + start, size, mapdepth = i16(s, 3), i16(s, 5), i16(s, 7) + if mapdepth == 16: + self.palette = ImagePalette.raw( + "BGR;16", b"\0" * 2 * start + self.fp.read(2 * size) + ) + elif mapdepth == 24: + self.palette = ImagePalette.raw( + "BGR", b"\0" * 3 * start + self.fp.read(3 * size) + ) + elif mapdepth == 32: + self.palette = ImagePalette.raw( + "BGRA", b"\0" * 4 * start + self.fp.read(4 * size) + ) + + # setup tile descriptor + try: + rawmode = MODES[(imagetype & 7, depth)] + if imagetype & 8: + # compressed + self.tile = [ + ( + "tga_rle", + (0, 0) + self.size, + self.fp.tell(), + (rawmode, orientation, depth), + ) + ] + else: + self.tile = [ + ( + "raw", + (0, 0) + self.size, + self.fp.tell(), + (rawmode, 0, orientation), + ) + ] + except KeyError: + pass # cannot decode + + +# +# -------------------------------------------------------------------- +# Write TGA file + + +SAVE = { + "1": ("1", 1, 0, 3), + "L": ("L", 8, 0, 3), + "LA": ("LA", 16, 0, 3), + "P": ("P", 8, 1, 1), + "RGB": ("BGR", 24, 0, 2), + "RGBA": ("BGRA", 32, 0, 2), +} + + +def _save(im, fp, filename): + + try: + rawmode, bits, colormaptype, imagetype = SAVE[im.mode] + except KeyError as e: + raise OSError(f"cannot write mode {im.mode} as TGA") from e + + if "rle" in im.encoderinfo: + rle = im.encoderinfo["rle"] + else: + compression = im.encoderinfo.get("compression", im.info.get("compression")) + rle = compression == "tga_rle" + if rle: + imagetype += 8 + + id_section = im.encoderinfo.get("id_section", im.info.get("id_section", "")) + id_len = len(id_section) + if id_len > 255: + id_len = 255 + id_section = id_section[:255] + warnings.warn("id_section has been trimmed to 255 characters") + + if colormaptype: + colormapfirst, colormaplength, colormapentry = 0, 256, 24 + else: + colormapfirst, colormaplength, colormapentry = 0, 0, 0 + + if im.mode in ("LA", "RGBA"): + flags = 8 + else: + flags = 0 + + orientation = im.encoderinfo.get("orientation", im.info.get("orientation", -1)) + if orientation > 0: + flags = flags | 0x20 + + fp.write( + o8(id_len) + + o8(colormaptype) + + o8(imagetype) + + o16(colormapfirst) + + o16(colormaplength) + + o8(colormapentry) + + o16(0) + + o16(0) + + o16(im.size[0]) + + o16(im.size[1]) + + o8(bits) + + o8(flags) + ) + + if id_section: + fp.write(id_section) + + if colormaptype: + fp.write(im.im.getpalette("RGB", "BGR")) + + if rle: + ImageFile._save( + im, fp, [("tga_rle", (0, 0) + im.size, 0, (rawmode, orientation))] + ) + else: + ImageFile._save( + im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))] + ) + + # write targa version 2 footer + fp.write(b"\000" * 8 + b"TRUEVISION-XFILE." + b"\000") + + +# +# -------------------------------------------------------------------- +# Registry + + +Image.register_open(TgaImageFile.format, TgaImageFile) +Image.register_save(TgaImageFile.format, _save) + +Image.register_extensions(TgaImageFile.format, [".tga", ".icb", ".vda", ".vst"]) + +Image.register_mime(TgaImageFile.format, "image/x-tga") diff --git a/venv/lib/python3.11/site-packages/PIL/TiffImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/TiffImagePlugin.py new file mode 100644 index 00000000..0b70ce38 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/TiffImagePlugin.py @@ -0,0 +1,1924 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TIFF file handling +# +# TIFF is a flexible, if somewhat aged, image file format originally +# defined by Aldus. Although TIFF supports a wide variety of pixel +# layouts and compression methods, the name doesn't really stand for +# "thousands of incompatible file formats," it just feels that way. +# +# To read TIFF data from a stream, the stream must be seekable. For +# progressive decoding, make sure to use TIFF files where the tag +# directory is placed first in the file. +# +# History: +# 1995-09-01 fl Created +# 1996-05-04 fl Handle JPEGTABLES tag +# 1996-05-18 fl Fixed COLORMAP support +# 1997-01-05 fl Fixed PREDICTOR support +# 1997-08-27 fl Added support for rational tags (from Perry Stoll) +# 1998-01-10 fl Fixed seek/tell (from Jan Blom) +# 1998-07-15 fl Use private names for internal variables +# 1999-06-13 fl Rewritten for PIL 1.0 (1.0) +# 2000-10-11 fl Additional fixes for Python 2.0 (1.1) +# 2001-04-17 fl Fixed rewind support (seek to frame 0) (1.2) +# 2001-05-12 fl Added write support for more tags (from Greg Couch) (1.3) +# 2001-12-18 fl Added workaround for broken Matrox library +# 2002-01-18 fl Don't mess up if photometric tag is missing (D. Alan Stewart) +# 2003-05-19 fl Check FILLORDER tag +# 2003-09-26 fl Added RGBa support +# 2004-02-24 fl Added DPI support; fixed rational write support +# 2005-02-07 fl Added workaround for broken Corel Draw 10 files +# 2006-01-09 fl Added support for float/double tags (from Russell Nelson) +# +# Copyright (c) 1997-2006 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# +import io +import itertools +import logging +import os +import struct +import warnings +from collections.abc import MutableMapping +from fractions import Fraction +from numbers import Number, Rational + +from . import Image, ImageFile, ImagePalette, TiffTags +from ._binary import o8 +from .TiffTags import TYPES + +logger = logging.getLogger(__name__) + +# Set these to true to force use of libtiff for reading or writing. +READ_LIBTIFF = False +WRITE_LIBTIFF = False +IFD_LEGACY_API = True + +II = b"II" # little-endian (Intel style) +MM = b"MM" # big-endian (Motorola style) + +# +# -------------------------------------------------------------------- +# Read TIFF files + +# a few tag names, just to make the code below a bit more readable +IMAGEWIDTH = 256 +IMAGELENGTH = 257 +BITSPERSAMPLE = 258 +COMPRESSION = 259 +PHOTOMETRIC_INTERPRETATION = 262 +FILLORDER = 266 +IMAGEDESCRIPTION = 270 +STRIPOFFSETS = 273 +SAMPLESPERPIXEL = 277 +ROWSPERSTRIP = 278 +STRIPBYTECOUNTS = 279 +X_RESOLUTION = 282 +Y_RESOLUTION = 283 +PLANAR_CONFIGURATION = 284 +RESOLUTION_UNIT = 296 +TRANSFERFUNCTION = 301 +SOFTWARE = 305 +DATE_TIME = 306 +ARTIST = 315 +PREDICTOR = 317 +COLORMAP = 320 +TILEOFFSETS = 324 +SUBIFD = 330 +EXTRASAMPLES = 338 +SAMPLEFORMAT = 339 +JPEGTABLES = 347 +REFERENCEBLACKWHITE = 532 +COPYRIGHT = 33432 +IPTC_NAA_CHUNK = 33723 # newsphoto properties +PHOTOSHOP_CHUNK = 34377 # photoshop properties +ICCPROFILE = 34675 +EXIFIFD = 34665 +XMP = 700 +JPEGQUALITY = 65537 # pseudo-tag by libtiff + +# https://github.com/imagej/ImageJA/blob/master/src/main/java/ij/io/TiffDecoder.java +IMAGEJ_META_DATA_BYTE_COUNTS = 50838 +IMAGEJ_META_DATA = 50839 + +COMPRESSION_INFO = { + # Compression => pil compression name + 1: "raw", + 2: "tiff_ccitt", + 3: "group3", + 4: "group4", + 5: "tiff_lzw", + 6: "tiff_jpeg", # obsolete + 7: "jpeg", + 8: "tiff_adobe_deflate", + 32771: "tiff_raw_16", # 16-bit padding + 32773: "packbits", + 32809: "tiff_thunderscan", + 32946: "tiff_deflate", + 34676: "tiff_sgilog", + 34677: "tiff_sgilog24", + 34925: "lzma", + 50000: "zstd", + 50001: "webp", +} + +COMPRESSION_INFO_REV = {v: k for k, v in COMPRESSION_INFO.items()} + +OPEN_INFO = { + # (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample, + # ExtraSamples) => mode, rawmode + (II, 0, (1,), 1, (1,), ()): ("1", "1;I"), + (MM, 0, (1,), 1, (1,), ()): ("1", "1;I"), + (II, 0, (1,), 2, (1,), ()): ("1", "1;IR"), + (MM, 0, (1,), 2, (1,), ()): ("1", "1;IR"), + (II, 1, (1,), 1, (1,), ()): ("1", "1"), + (MM, 1, (1,), 1, (1,), ()): ("1", "1"), + (II, 1, (1,), 2, (1,), ()): ("1", "1;R"), + (MM, 1, (1,), 2, (1,), ()): ("1", "1;R"), + (II, 0, (1,), 1, (2,), ()): ("L", "L;2I"), + (MM, 0, (1,), 1, (2,), ()): ("L", "L;2I"), + (II, 0, (1,), 2, (2,), ()): ("L", "L;2IR"), + (MM, 0, (1,), 2, (2,), ()): ("L", "L;2IR"), + (II, 1, (1,), 1, (2,), ()): ("L", "L;2"), + (MM, 1, (1,), 1, (2,), ()): ("L", "L;2"), + (II, 1, (1,), 2, (2,), ()): ("L", "L;2R"), + (MM, 1, (1,), 2, (2,), ()): ("L", "L;2R"), + (II, 0, (1,), 1, (4,), ()): ("L", "L;4I"), + (MM, 0, (1,), 1, (4,), ()): ("L", "L;4I"), + (II, 0, (1,), 2, (4,), ()): ("L", "L;4IR"), + (MM, 0, (1,), 2, (4,), ()): ("L", "L;4IR"), + (II, 1, (1,), 1, (4,), ()): ("L", "L;4"), + (MM, 1, (1,), 1, (4,), ()): ("L", "L;4"), + (II, 1, (1,), 2, (4,), ()): ("L", "L;4R"), + (MM, 1, (1,), 2, (4,), ()): ("L", "L;4R"), + (II, 0, (1,), 1, (8,), ()): ("L", "L;I"), + (MM, 0, (1,), 1, (8,), ()): ("L", "L;I"), + (II, 0, (1,), 2, (8,), ()): ("L", "L;IR"), + (MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"), + (II, 1, (1,), 1, (8,), ()): ("L", "L"), + (MM, 1, (1,), 1, (8,), ()): ("L", "L"), + (II, 1, (1,), 2, (8,), ()): ("L", "L;R"), + (MM, 1, (1,), 2, (8,), ()): ("L", "L;R"), + (II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"), + (II, 1, (1,), 1, (16,), ()): ("I;16", "I;16"), + (MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"), + (II, 1, (2,), 1, (16,), ()): ("I", "I;16S"), + (MM, 1, (2,), 1, (16,), ()): ("I", "I;16BS"), + (II, 0, (3,), 1, (32,), ()): ("F", "F;32F"), + (MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"), + (II, 1, (1,), 1, (32,), ()): ("I", "I;32N"), + (II, 1, (2,), 1, (32,), ()): ("I", "I;32S"), + (MM, 1, (2,), 1, (32,), ()): ("I", "I;32BS"), + (II, 1, (3,), 1, (32,), ()): ("F", "F;32F"), + (MM, 1, (3,), 1, (32,), ()): ("F", "F;32BF"), + (II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), + (MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), + (II, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), + (MM, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), + (II, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), + (MM, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), + (II, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples + (MM, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples + (II, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), + (MM, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGBX", "RGBXX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGBX", "RGBXX"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGBX", "RGBXXX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGBX", "RGBXXX"), + (II, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), + (MM, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (1, 0, 0)): ("RGBA", "RGBaXX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (1, 0, 0)): ("RGBA", "RGBaXX"), + (II, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), + (MM, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8), (2, 0)): ("RGBA", "RGBAX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (2, 0)): ("RGBA", "RGBAX"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"), + (II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (II, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16L"), + (MM, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16B"), + (II, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16L"), + (MM, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16B"), + (II, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGBX", "RGBX;16L"), + (MM, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGBX", "RGBX;16B"), + (II, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16L"), + (MM, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16B"), + (II, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16L"), + (MM, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16B"), + (II, 3, (1,), 1, (1,), ()): ("P", "P;1"), + (MM, 3, (1,), 1, (1,), ()): ("P", "P;1"), + (II, 3, (1,), 2, (1,), ()): ("P", "P;1R"), + (MM, 3, (1,), 2, (1,), ()): ("P", "P;1R"), + (II, 3, (1,), 1, (2,), ()): ("P", "P;2"), + (MM, 3, (1,), 1, (2,), ()): ("P", "P;2"), + (II, 3, (1,), 2, (2,), ()): ("P", "P;2R"), + (MM, 3, (1,), 2, (2,), ()): ("P", "P;2R"), + (II, 3, (1,), 1, (4,), ()): ("P", "P;4"), + (MM, 3, (1,), 1, (4,), ()): ("P", "P;4"), + (II, 3, (1,), 2, (4,), ()): ("P", "P;4R"), + (MM, 3, (1,), 2, (4,), ()): ("P", "P;4R"), + (II, 3, (1,), 1, (8,), ()): ("P", "P"), + (MM, 3, (1,), 1, (8,), ()): ("P", "P"), + (II, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), + (MM, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), + (II, 3, (1,), 2, (8,), ()): ("P", "P;R"), + (MM, 3, (1,), 2, (8,), ()): ("P", "P;R"), + (II, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), + (MM, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), + (II, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"), + (MM, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"), + (II, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"), + (MM, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"), + (II, 5, (1,), 1, (16, 16, 16, 16), ()): ("CMYK", "CMYK;16L"), + # JPEG compressed images handled by LibTiff and auto-converted to RGBX + # Minimal Baseline TIFF requires YCbCr images to have 3 SamplesPerPixel + (II, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"), + (MM, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"), + (II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), + (MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), +} + +PREFIXES = [ + b"MM\x00\x2A", # Valid TIFF header with big-endian byte order + b"II\x2A\x00", # Valid TIFF header with little-endian byte order + b"MM\x2A\x00", # Invalid TIFF header, assume big-endian + b"II\x00\x2A", # Invalid TIFF header, assume little-endian +] + + +def _accept(prefix): + return prefix[:4] in PREFIXES + + +def _limit_rational(val, max_val): + inv = abs(val) > 1 + n_d = IFDRational(1 / val if inv else val).limit_rational(max_val) + return n_d[::-1] if inv else n_d + + +def _limit_signed_rational(val, max_val, min_val): + frac = Fraction(val) + n_d = frac.numerator, frac.denominator + + if min(n_d) < min_val: + n_d = _limit_rational(val, abs(min_val)) + + if max(n_d) > max_val: + val = Fraction(*n_d) + n_d = _limit_rational(val, max_val) + + return n_d + + +## +# Wrapper for TIFF IFDs. + +_load_dispatch = {} +_write_dispatch = {} + + +class IFDRational(Rational): + """Implements a rational class where 0/0 is a legal value to match + the in the wild use of exif rationals. + + e.g., DigitalZoomRatio - 0.00/0.00 indicates that no digital zoom was used + """ + + """ If the denominator is 0, store this as a float('nan'), otherwise store + as a fractions.Fraction(). Delegate as appropriate + + """ + + __slots__ = ("_numerator", "_denominator", "_val") + + def __init__(self, value, denominator=1): + """ + :param value: either an integer numerator, a + float/rational/other number, or an IFDRational + :param denominator: Optional integer denominator + """ + if isinstance(value, IFDRational): + self._numerator = value.numerator + self._denominator = value.denominator + self._val = value._val + return + + if isinstance(value, Fraction): + self._numerator = value.numerator + self._denominator = value.denominator + else: + self._numerator = value + self._denominator = denominator + + if denominator == 0: + self._val = float("nan") + elif denominator == 1: + self._val = Fraction(value) + else: + self._val = Fraction(value, denominator) + + @property + def numerator(a): + return a._numerator + + @property + def denominator(a): + return a._denominator + + def limit_rational(self, max_denominator): + """ + + :param max_denominator: Integer, the maximum denominator value + :returns: Tuple of (numerator, denominator) + """ + + if self.denominator == 0: + return (self.numerator, self.denominator) + + f = self._val.limit_denominator(max_denominator) + return (f.numerator, f.denominator) + + def __repr__(self): + return str(float(self._val)) + + def __hash__(self): + return self._val.__hash__() + + def __eq__(self, other): + if isinstance(other, IFDRational): + other = other._val + return self._val == other + + def _delegate(op): + def delegate(self, *args): + return getattr(self._val, op)(*args) + + return delegate + + """ a = ['add','radd', 'sub', 'rsub', 'mul', 'rmul', + 'truediv', 'rtruediv', 'floordiv', 'rfloordiv', + 'mod','rmod', 'pow','rpow', 'pos', 'neg', + 'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'bool', + 'ceil', 'floor', 'round'] + print("\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a)) + """ + + __add__ = _delegate("__add__") + __radd__ = _delegate("__radd__") + __sub__ = _delegate("__sub__") + __rsub__ = _delegate("__rsub__") + __mul__ = _delegate("__mul__") + __rmul__ = _delegate("__rmul__") + __truediv__ = _delegate("__truediv__") + __rtruediv__ = _delegate("__rtruediv__") + __floordiv__ = _delegate("__floordiv__") + __rfloordiv__ = _delegate("__rfloordiv__") + __mod__ = _delegate("__mod__") + __rmod__ = _delegate("__rmod__") + __pow__ = _delegate("__pow__") + __rpow__ = _delegate("__rpow__") + __pos__ = _delegate("__pos__") + __neg__ = _delegate("__neg__") + __abs__ = _delegate("__abs__") + __trunc__ = _delegate("__trunc__") + __lt__ = _delegate("__lt__") + __gt__ = _delegate("__gt__") + __le__ = _delegate("__le__") + __ge__ = _delegate("__ge__") + __bool__ = _delegate("__bool__") + __ceil__ = _delegate("__ceil__") + __floor__ = _delegate("__floor__") + __round__ = _delegate("__round__") + + +class ImageFileDirectory_v2(MutableMapping): + """This class represents a TIFF tag directory. To speed things up, we + don't decode tags unless they're asked for. + + Exposes a dictionary interface of the tags in the directory:: + + ifd = ImageFileDirectory_v2() + ifd[key] = 'Some Data' + ifd.tagtype[key] = TiffTags.ASCII + print(ifd[key]) + 'Some Data' + + Individual values are returned as the strings or numbers, sequences are + returned as tuples of the values. + + The tiff metadata type of each item is stored in a dictionary of + tag types in + :attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v2.tagtype`. The types + are read from a tiff file, guessed from the type added, or added + manually. + + Data Structures: + + * self.tagtype = {} + + * Key: numerical tiff tag number + * Value: integer corresponding to the data type from + ~PIL.TiffTags.TYPES` + + .. versionadded:: 3.0.0 + """ + + """ + Documentation: + + 'internal' data structures: + * self._tags_v2 = {} Key: numerical tiff tag number + Value: decoded data, as tuple for multiple values + * self._tagdata = {} Key: numerical tiff tag number + Value: undecoded byte string from file + * self._tags_v1 = {} Key: numerical tiff tag number + Value: decoded data in the v1 format + + Tags will be found in the private attributes self._tagdata, and in + self._tags_v2 once decoded. + + Self.legacy_api is a value for internal use, and shouldn't be + changed from outside code. In cooperation with the + ImageFileDirectory_v1 class, if legacy_api is true, then decoded + tags will be populated into both _tags_v1 and _tags_v2. _Tags_v2 + will be used if this IFD is used in the TIFF save routine. Tags + should be read from tags_v1 if legacy_api == true. + + """ + + def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None): + """Initialize an ImageFileDirectory. + + To construct an ImageFileDirectory from a real file, pass the 8-byte + magic header to the constructor. To only set the endianness, pass it + as the 'prefix' keyword argument. + + :param ifh: One of the accepted magic headers (cf. PREFIXES); also sets + endianness. + :param prefix: Override the endianness of the file. + """ + if ifh[:4] not in PREFIXES: + raise SyntaxError(f"not a TIFF file (header {repr(ifh)} not valid)") + self._prefix = prefix if prefix is not None else ifh[:2] + if self._prefix == MM: + self._endian = ">" + elif self._prefix == II: + self._endian = "<" + else: + raise SyntaxError("not a TIFF IFD") + self.tagtype = {} + """ Dictionary of tag types """ + self.reset() + (self.next,) = self._unpack("L", ifh[4:]) + self._legacy_api = False + + prefix = property(lambda self: self._prefix) + offset = property(lambda self: self._offset) + legacy_api = property(lambda self: self._legacy_api) + + @legacy_api.setter + def legacy_api(self, value): + raise Exception("Not allowing setting of legacy api") + + def reset(self): + self._tags_v1 = {} # will remain empty if legacy_api is false + self._tags_v2 = {} # main tag storage + self._tagdata = {} + self.tagtype = {} # added 2008-06-05 by Florian Hoech + self._next = None + self._offset = None + + def __str__(self): + return str(dict(self)) + + def named(self): + """ + :returns: dict of name|key: value + + Returns the complete tag dictionary, with named tags where possible. + """ + return {TiffTags.lookup(code).name: value for code, value in self.items()} + + def __len__(self): + return len(set(self._tagdata) | set(self._tags_v2)) + + def __getitem__(self, tag): + if tag not in self._tags_v2: # unpack on the fly + data = self._tagdata[tag] + typ = self.tagtype[tag] + size, handler = self._load_dispatch[typ] + self[tag] = handler(self, data, self.legacy_api) # check type + val = self._tags_v2[tag] + if self.legacy_api and not isinstance(val, (tuple, bytes)): + val = (val,) + return val + + def __contains__(self, tag): + return tag in self._tags_v2 or tag in self._tagdata + + def __setitem__(self, tag, value): + self._setitem(tag, value, self.legacy_api) + + def _setitem(self, tag, value, legacy_api): + basetypes = (Number, bytes, str) + + info = TiffTags.lookup(tag) + values = [value] if isinstance(value, basetypes) else value + + if tag not in self.tagtype: + if info.type: + self.tagtype[tag] = info.type + else: + self.tagtype[tag] = TiffTags.UNDEFINED + if all(isinstance(v, IFDRational) for v in values): + self.tagtype[tag] = ( + TiffTags.RATIONAL + if all(v >= 0 for v in values) + else TiffTags.SIGNED_RATIONAL + ) + elif all(isinstance(v, int) for v in values): + if all(0 <= v < 2 ** 16 for v in values): + self.tagtype[tag] = TiffTags.SHORT + elif all(-(2 ** 15) < v < 2 ** 15 for v in values): + self.tagtype[tag] = TiffTags.SIGNED_SHORT + else: + self.tagtype[tag] = ( + TiffTags.LONG + if all(v >= 0 for v in values) + else TiffTags.SIGNED_LONG + ) + elif all(isinstance(v, float) for v in values): + self.tagtype[tag] = TiffTags.DOUBLE + elif all(isinstance(v, str) for v in values): + self.tagtype[tag] = TiffTags.ASCII + elif all(isinstance(v, bytes) for v in values): + self.tagtype[tag] = TiffTags.BYTE + + if self.tagtype[tag] == TiffTags.UNDEFINED: + values = [ + value.encode("ascii", "replace") if isinstance(value, str) else value + ] + elif self.tagtype[tag] == TiffTags.RATIONAL: + values = [float(v) if isinstance(v, int) else v for v in values] + + is_ifd = self.tagtype[tag] == TiffTags.LONG and isinstance(values, dict) + if not is_ifd: + values = tuple(info.cvt_enum(value) for value in values) + + dest = self._tags_v1 if legacy_api else self._tags_v2 + + # Three branches: + # Spec'd length == 1, Actual length 1, store as element + # Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed. + # No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple. + # Don't mess with the legacy api, since it's frozen. + if not is_ifd and ( + (info.length == 1) + or self.tagtype[tag] == TiffTags.BYTE + or (info.length is None and len(values) == 1 and not legacy_api) + ): + # Don't mess with the legacy api, since it's frozen. + if legacy_api and self.tagtype[tag] in [ + TiffTags.RATIONAL, + TiffTags.SIGNED_RATIONAL, + ]: # rationals + values = (values,) + try: + (dest[tag],) = values + except ValueError: + # We've got a builtin tag with 1 expected entry + warnings.warn( + f"Metadata Warning, tag {tag} had too many entries: " + f"{len(values)}, expected 1" + ) + dest[tag] = values[0] + + else: + # Spec'd length > 1 or undefined + # Unspec'd, and length > 1 + dest[tag] = values + + def __delitem__(self, tag): + self._tags_v2.pop(tag, None) + self._tags_v1.pop(tag, None) + self._tagdata.pop(tag, None) + + def __iter__(self): + return iter(set(self._tagdata) | set(self._tags_v2)) + + def _unpack(self, fmt, data): + return struct.unpack(self._endian + fmt, data) + + def _pack(self, fmt, *values): + return struct.pack(self._endian + fmt, *values) + + def _register_loader(idx, size): + def decorator(func): + from .TiffTags import TYPES + + if func.__name__.startswith("load_"): + TYPES[idx] = func.__name__[5:].replace("_", " ") + _load_dispatch[idx] = size, func # noqa: F821 + return func + + return decorator + + def _register_writer(idx): + def decorator(func): + _write_dispatch[idx] = func # noqa: F821 + return func + + return decorator + + def _register_basic(idx_fmt_name): + from .TiffTags import TYPES + + idx, fmt, name = idx_fmt_name + TYPES[idx] = name + size = struct.calcsize("=" + fmt) + _load_dispatch[idx] = ( # noqa: F821 + size, + lambda self, data, legacy_api=True: ( + self._unpack("{}{}".format(len(data) // size, fmt), data) + ), + ) + _write_dispatch[idx] = lambda self, *values: ( # noqa: F821 + b"".join(self._pack(fmt, value) for value in values) + ) + + list( + map( + _register_basic, + [ + (TiffTags.SHORT, "H", "short"), + (TiffTags.LONG, "L", "long"), + (TiffTags.SIGNED_BYTE, "b", "signed byte"), + (TiffTags.SIGNED_SHORT, "h", "signed short"), + (TiffTags.SIGNED_LONG, "l", "signed long"), + (TiffTags.FLOAT, "f", "float"), + (TiffTags.DOUBLE, "d", "double"), + (TiffTags.IFD, "L", "long"), + ], + ) + ) + + @_register_loader(1, 1) # Basic type, except for the legacy API. + def load_byte(self, data, legacy_api=True): + return data + + @_register_writer(1) # Basic type, except for the legacy API. + def write_byte(self, data): + return data + + @_register_loader(2, 1) + def load_string(self, data, legacy_api=True): + if data.endswith(b"\0"): + data = data[:-1] + return data.decode("latin-1", "replace") + + @_register_writer(2) + def write_string(self, value): + # remerge of https://github.com/python-pillow/Pillow/pull/1416 + return b"" + value.encode("ascii", "replace") + b"\0" + + @_register_loader(5, 8) + def load_rational(self, data, legacy_api=True): + vals = self._unpack("{}L".format(len(data) // 4), data) + + def combine(a, b): + return (a, b) if legacy_api else IFDRational(a, b) + + return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) + + @_register_writer(5) + def write_rational(self, *values): + return b"".join( + self._pack("2L", *_limit_rational(frac, 2 ** 32 - 1)) for frac in values + ) + + @_register_loader(7, 1) + def load_undefined(self, data, legacy_api=True): + return data + + @_register_writer(7) + def write_undefined(self, value): + return value + + @_register_loader(10, 8) + def load_signed_rational(self, data, legacy_api=True): + vals = self._unpack("{}l".format(len(data) // 4), data) + + def combine(a, b): + return (a, b) if legacy_api else IFDRational(a, b) + + return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) + + @_register_writer(10) + def write_signed_rational(self, *values): + return b"".join( + self._pack("2l", *_limit_signed_rational(frac, 2 ** 31 - 1, -(2 ** 31))) + for frac in values + ) + + def _ensure_read(self, fp, size): + ret = fp.read(size) + if len(ret) != size: + raise OSError( + "Corrupt EXIF data. " + f"Expecting to read {size} bytes but only got {len(ret)}. " + ) + return ret + + def load(self, fp): + + self.reset() + self._offset = fp.tell() + + try: + for i in range(self._unpack("H", self._ensure_read(fp, 2))[0]): + tag, typ, count, data = self._unpack("HHL4s", self._ensure_read(fp, 12)) + + tagname = TiffTags.lookup(tag).name + typname = TYPES.get(typ, "unknown") + msg = f"tag: {tagname} ({tag}) - type: {typname} ({typ})" + + try: + unit_size, handler = self._load_dispatch[typ] + except KeyError: + logger.debug(msg + f" - unsupported type {typ}") + continue # ignore unsupported type + size = count * unit_size + if size > 4: + here = fp.tell() + (offset,) = self._unpack("L", data) + msg += f" Tag Location: {here} - Data Location: {offset}" + fp.seek(offset) + data = ImageFile._safe_read(fp, size) + fp.seek(here) + else: + data = data[:size] + + if len(data) != size: + warnings.warn( + "Possibly corrupt EXIF data. " + f"Expecting to read {size} bytes but only got {len(data)}." + f" Skipping tag {tag}" + ) + logger.debug(msg) + continue + + if not data: + logger.debug(msg) + continue + + self._tagdata[tag] = data + self.tagtype[tag] = typ + + msg += " - value: " + ( + "" % size if size > 32 else repr(data) + ) + logger.debug(msg) + + (self.next,) = self._unpack("L", self._ensure_read(fp, 4)) + except OSError as msg: + warnings.warn(str(msg)) + return + + def tobytes(self, offset=0): + # FIXME What about tagdata? + result = self._pack("H", len(self._tags_v2)) + + entries = [] + offset = offset + len(result) + len(self._tags_v2) * 12 + 4 + stripoffsets = None + + # pass 1: convert tags to binary format + # always write tags in ascending order + for tag, value in sorted(self._tags_v2.items()): + if tag == STRIPOFFSETS: + stripoffsets = len(entries) + typ = self.tagtype.get(tag) + logger.debug(f"Tag {tag}, Type: {typ}, Value: {repr(value)}") + is_ifd = typ == TiffTags.LONG and isinstance(value, dict) + if is_ifd: + if self._endian == "<": + ifh = b"II\x2A\x00\x08\x00\x00\x00" + else: + ifh = b"MM\x00\x2A\x00\x00\x00\x08" + ifd = ImageFileDirectory_v2(ifh) + for ifd_tag, ifd_value in self._tags_v2[tag].items(): + ifd[ifd_tag] = ifd_value + data = ifd.tobytes(offset) + else: + values = value if isinstance(value, tuple) else (value,) + data = self._write_dispatch[typ](self, *values) + + tagname = TiffTags.lookup(tag).name + typname = "ifd" if is_ifd else TYPES.get(typ, "unknown") + msg = f"save: {tagname} ({tag}) - type: {typname} ({typ})" + msg += " - value: " + ( + "" % len(data) if len(data) >= 16 else str(values) + ) + logger.debug(msg) + + # count is sum of lengths for string and arbitrary data + if is_ifd: + count = 1 + elif typ in [TiffTags.BYTE, TiffTags.ASCII, TiffTags.UNDEFINED]: + count = len(data) + else: + count = len(values) + # figure out if data fits into the entry + if len(data) <= 4: + entries.append((tag, typ, count, data.ljust(4, b"\0"), b"")) + else: + entries.append((tag, typ, count, self._pack("L", offset), data)) + offset += (len(data) + 1) // 2 * 2 # pad to word + + # update strip offset data to point beyond auxiliary data + if stripoffsets is not None: + tag, typ, count, value, data = entries[stripoffsets] + if data: + raise NotImplementedError("multistrip support not yet implemented") + value = self._pack("L", self._unpack("L", value)[0] + offset) + entries[stripoffsets] = tag, typ, count, value, data + + # pass 2: write entries to file + for tag, typ, count, value, data in entries: + logger.debug(f"{tag} {typ} {count} {repr(value)} {repr(data)}") + result += self._pack("HHL4s", tag, typ, count, value) + + # -- overwrite here for multi-page -- + result += b"\0\0\0\0" # end of entries + + # pass 3: write auxiliary data to file + for tag, typ, count, value, data in entries: + result += data + if len(data) & 1: + result += b"\0" + + return result + + def save(self, fp): + + if fp.tell() == 0: # skip TIFF header on subsequent pages + # tiff header -- PIL always starts the first IFD at offset 8 + fp.write(self._prefix + self._pack("HL", 42, 8)) + + offset = fp.tell() + result = self.tobytes(offset) + fp.write(result) + return offset + len(result) + + +ImageFileDirectory_v2._load_dispatch = _load_dispatch +ImageFileDirectory_v2._write_dispatch = _write_dispatch +for idx, name in TYPES.items(): + name = name.replace(" ", "_") + setattr(ImageFileDirectory_v2, "load_" + name, _load_dispatch[idx][1]) + setattr(ImageFileDirectory_v2, "write_" + name, _write_dispatch[idx]) +del _load_dispatch, _write_dispatch, idx, name + + +# Legacy ImageFileDirectory support. +class ImageFileDirectory_v1(ImageFileDirectory_v2): + """This class represents the **legacy** interface to a TIFF tag directory. + + Exposes a dictionary interface of the tags in the directory:: + + ifd = ImageFileDirectory_v1() + ifd[key] = 'Some Data' + ifd.tagtype[key] = TiffTags.ASCII + print(ifd[key]) + ('Some Data',) + + Also contains a dictionary of tag types as read from the tiff image file, + :attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v1.tagtype`. + + Values are returned as a tuple. + + .. deprecated:: 3.0.0 + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._legacy_api = True + + tags = property(lambda self: self._tags_v1) + tagdata = property(lambda self: self._tagdata) + + # defined in ImageFileDirectory_v2 + tagtype: dict + """Dictionary of tag types""" + + @classmethod + def from_v2(cls, original): + """Returns an + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` + instance with the same data as is contained in the original + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` + instance. + + :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` + + """ + + ifd = cls(prefix=original.prefix) + ifd._tagdata = original._tagdata + ifd.tagtype = original.tagtype + ifd.next = original.next # an indicator for multipage tiffs + return ifd + + def to_v2(self): + """Returns an + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` + instance with the same data as is contained in the original + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` + instance. + + :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` + + """ + + ifd = ImageFileDirectory_v2(prefix=self.prefix) + ifd._tagdata = dict(self._tagdata) + ifd.tagtype = dict(self.tagtype) + ifd._tags_v2 = dict(self._tags_v2) + return ifd + + def __contains__(self, tag): + return tag in self._tags_v1 or tag in self._tagdata + + def __len__(self): + return len(set(self._tagdata) | set(self._tags_v1)) + + def __iter__(self): + return iter(set(self._tagdata) | set(self._tags_v1)) + + def __setitem__(self, tag, value): + for legacy_api in (False, True): + self._setitem(tag, value, legacy_api) + + def __getitem__(self, tag): + if tag not in self._tags_v1: # unpack on the fly + data = self._tagdata[tag] + typ = self.tagtype[tag] + size, handler = self._load_dispatch[typ] + for legacy in (False, True): + self._setitem(tag, handler(self, data, legacy), legacy) + val = self._tags_v1[tag] + if not isinstance(val, (tuple, bytes)): + val = (val,) + return val + + +# undone -- switch this pointer when IFD_LEGACY_API == False +ImageFileDirectory = ImageFileDirectory_v1 + + +## +# Image plugin for TIFF files. + + +class TiffImageFile(ImageFile.ImageFile): + + format = "TIFF" + format_description = "Adobe TIFF" + _close_exclusive_fp_after_loading = False + + def __init__(self, fp=None, filename=None): + self.tag_v2 = None + """ Image file directory (tag dictionary) """ + + self.tag = None + """ Legacy tag entries """ + + super().__init__(fp, filename) + + def _open(self): + """Open the first image in a TIFF file""" + + # Header + ifh = self.fp.read(8) + + self.tag_v2 = ImageFileDirectory_v2(ifh) + + # legacy IFD entries will be filled in later + self.ifd = None + + # setup frame pointers + self.__first = self.__next = self.tag_v2.next + self.__frame = -1 + self.__fp = self.fp + self._frame_pos = [] + self._n_frames = None + + logger.debug("*** TiffImageFile._open ***") + logger.debug(f"- __first: {self.__first}") + logger.debug(f"- ifh: {repr(ifh)}") # Use repr to avoid str(bytes) + + # and load the first frame + self._seek(0) + + @property + def n_frames(self): + if self._n_frames is None: + current = self.tell() + self._seek(len(self._frame_pos)) + while self._n_frames is None: + self._seek(self.tell() + 1) + self.seek(current) + return self._n_frames + + def seek(self, frame): + """Select a given frame as current image""" + if not self._seek_check(frame): + return + self._seek(frame) + # Create a new core image object on second and + # subsequent frames in the image. Image may be + # different size/mode. + Image._decompression_bomb_check(self.size) + self.im = Image.core.new(self.mode, self.size) + + def _seek(self, frame): + self.fp = self.__fp + while len(self._frame_pos) <= frame: + if not self.__next: + raise EOFError("no more images in TIFF file") + logger.debug( + f"Seeking to frame {frame}, on frame {self.__frame}, " + f"__next {self.__next}, location: {self.fp.tell()}" + ) + # reset buffered io handle in case fp + # was passed to libtiff, invalidating the buffer + self.fp.tell() + self.fp.seek(self.__next) + self._frame_pos.append(self.__next) + logger.debug("Loading tags, location: %s" % self.fp.tell()) + self.tag_v2.load(self.fp) + self.__next = self.tag_v2.next + if self.__next == 0: + self._n_frames = frame + 1 + if len(self._frame_pos) == 1: + self.is_animated = self.__next != 0 + self.__frame += 1 + self.fp.seek(self._frame_pos[frame]) + self.tag_v2.load(self.fp) + # fill the legacy tag/ifd entries + self.tag = self.ifd = ImageFileDirectory_v1.from_v2(self.tag_v2) + self.__frame = frame + self._setup() + + def tell(self): + """Return the current frame number""" + return self.__frame + + def load(self): + if self.tile and self.use_load_libtiff: + return self._load_libtiff() + return super().load() + + def load_end(self): + if self._tile_orientation: + method = { + 2: Image.FLIP_LEFT_RIGHT, + 3: Image.ROTATE_180, + 4: Image.FLIP_TOP_BOTTOM, + 5: Image.TRANSPOSE, + 6: Image.ROTATE_270, + 7: Image.TRANSVERSE, + 8: Image.ROTATE_90, + }.get(self._tile_orientation) + if method is not None: + self.im = self.im.transpose(method) + self._size = self.im.size + + # allow closing if we're on the first frame, there's no next + # This is the ImageFile.load path only, libtiff specific below. + if not self.is_animated: + self._close_exclusive_fp_after_loading = True + + def _load_libtiff(self): + """Overload method triggered when we detect a compressed tiff + Calls out to libtiff""" + + Image.Image.load(self) + + self.load_prepare() + + if not len(self.tile) == 1: + raise OSError("Not exactly one tile") + + # (self._compression, (extents tuple), + # 0, (rawmode, self._compression, fp)) + extents = self.tile[0][1] + args = list(self.tile[0][3]) + + # To be nice on memory footprint, if there's a + # file descriptor, use that instead of reading + # into a string in python. + # libtiff closes the file descriptor, so pass in a dup. + try: + fp = hasattr(self.fp, "fileno") and os.dup(self.fp.fileno()) + # flush the file descriptor, prevents error on pypy 2.4+ + # should also eliminate the need for fp.tell + # in _seek + if hasattr(self.fp, "flush"): + self.fp.flush() + except OSError: + # io.BytesIO have a fileno, but returns an OSError if + # it doesn't use a file descriptor. + fp = False + + if fp: + args[2] = fp + + decoder = Image._getdecoder( + self.mode, "libtiff", tuple(args), self.decoderconfig + ) + try: + decoder.setimage(self.im, extents) + except ValueError as e: + raise OSError("Couldn't set the image") from e + + close_self_fp = self._exclusive_fp and not self.is_animated + if hasattr(self.fp, "getvalue"): + # We've got a stringio like thing passed in. Yay for all in memory. + # The decoder needs the entire file in one shot, so there's not + # a lot we can do here other than give it the entire file. + # unless we could do something like get the address of the + # underlying string for stringio. + # + # Rearranging for supporting byteio items, since they have a fileno + # that returns an OSError if there's no underlying fp. Easier to + # deal with here by reordering. + logger.debug("have getvalue. just sending in a string from getvalue") + n, err = decoder.decode(self.fp.getvalue()) + elif fp: + # we've got a actual file on disk, pass in the fp. + logger.debug("have fileno, calling fileno version of the decoder.") + if not close_self_fp: + self.fp.seek(0) + # 4 bytes, otherwise the trace might error out + n, err = decoder.decode(b"fpfp") + else: + # we have something else. + logger.debug("don't have fileno or getvalue. just reading") + self.fp.seek(0) + # UNDONE -- so much for that buffer size thing. + n, err = decoder.decode(self.fp.read()) + + self.tile = [] + self.readonly = 0 + + self.load_end() + + # libtiff closed the fp in a, we need to close self.fp, if possible + if close_self_fp: + self.fp.close() + self.fp = None # might be shared + + if err < 0: + raise OSError(err) + + return Image.Image.load(self) + + def _setup(self): + """Setup this image object based on current tags""" + + if 0xBC01 in self.tag_v2: + raise OSError("Windows Media Photo files not yet supported") + + # extract relevant tags + self._compression = COMPRESSION_INFO[self.tag_v2.get(COMPRESSION, 1)] + self._planar_configuration = self.tag_v2.get(PLANAR_CONFIGURATION, 1) + + # photometric is a required tag, but not everyone is reading + # the specification + photo = self.tag_v2.get(PHOTOMETRIC_INTERPRETATION, 0) + + # old style jpeg compression images most certainly are YCbCr + if self._compression == "tiff_jpeg": + photo = 6 + + fillorder = self.tag_v2.get(FILLORDER, 1) + + logger.debug("*** Summary ***") + logger.debug(f"- compression: {self._compression}") + logger.debug(f"- photometric_interpretation: {photo}") + logger.debug(f"- planar_configuration: {self._planar_configuration}") + logger.debug(f"- fill_order: {fillorder}") + logger.debug(f"- YCbCr subsampling: {self.tag.get(530)}") + + # size + xsize = int(self.tag_v2.get(IMAGEWIDTH)) + ysize = int(self.tag_v2.get(IMAGELENGTH)) + self._size = xsize, ysize + + logger.debug(f"- size: {self.size}") + + sampleFormat = self.tag_v2.get(SAMPLEFORMAT, (1,)) + if len(sampleFormat) > 1 and max(sampleFormat) == min(sampleFormat) == 1: + # SAMPLEFORMAT is properly per band, so an RGB image will + # be (1,1,1). But, we don't support per band pixel types, + # and anything more than one band is a uint8. So, just + # take the first element. Revisit this if adding support + # for more exotic images. + sampleFormat = (1,) + + bps_tuple = self.tag_v2.get(BITSPERSAMPLE, (1,)) + extra_tuple = self.tag_v2.get(EXTRASAMPLES, ()) + if photo in (2, 6, 8): # RGB, YCbCr, LAB + bps_count = 3 + elif photo == 5: # CMYK + bps_count = 4 + else: + bps_count = 1 + bps_count += len(extra_tuple) + # Some files have only one value in bps_tuple, + # while should have more. Fix it + if bps_count > len(bps_tuple) and len(bps_tuple) == 1: + bps_tuple = bps_tuple * bps_count + + # mode: check photometric interpretation and bits per pixel + key = ( + self.tag_v2.prefix, + photo, + sampleFormat, + fillorder, + bps_tuple, + extra_tuple, + ) + logger.debug(f"format key: {key}") + try: + self.mode, rawmode = OPEN_INFO[key] + except KeyError as e: + logger.debug("- unsupported format") + raise SyntaxError("unknown pixel mode") from e + + logger.debug(f"- raw mode: {rawmode}") + logger.debug(f"- pil mode: {self.mode}") + + self.info["compression"] = self._compression + + xres = self.tag_v2.get(X_RESOLUTION, 1) + yres = self.tag_v2.get(Y_RESOLUTION, 1) + + if xres and yres: + resunit = self.tag_v2.get(RESOLUTION_UNIT) + if resunit == 2: # dots per inch + self.info["dpi"] = int(xres + 0.5), int(yres + 0.5) + elif resunit == 3: # dots per centimeter. convert to dpi + self.info["dpi"] = int(xres * 2.54 + 0.5), int(yres * 2.54 + 0.5) + elif resunit is None: # used to default to 1, but now 2) + self.info["dpi"] = int(xres + 0.5), int(yres + 0.5) + # For backward compatibility, + # we also preserve the old behavior + self.info["resolution"] = xres, yres + else: # No absolute unit of measurement + self.info["resolution"] = xres, yres + + # build tile descriptors + x = y = layer = 0 + self.tile = [] + self.use_load_libtiff = READ_LIBTIFF or self._compression != "raw" + if self.use_load_libtiff: + # Decoder expects entire file as one tile. + # There's a buffer size limit in load (64k) + # so large g4 images will fail if we use that + # function. + # + # Setup the one tile for the whole image, then + # use the _load_libtiff function. + + # libtiff handles the fillmode for us, so 1;IR should + # actually be 1;I. Including the R double reverses the + # bits, so stripes of the image are reversed. See + # https://github.com/python-pillow/Pillow/issues/279 + if fillorder == 2: + # Replace fillorder with fillorder=1 + key = key[:3] + (1,) + key[4:] + logger.debug(f"format key: {key}") + # this should always work, since all the + # fillorder==2 modes have a corresponding + # fillorder=1 mode + self.mode, rawmode = OPEN_INFO[key] + # libtiff always returns the bytes in native order. + # we're expecting image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if rawmode == "I;16": + rawmode = "I;16N" + if ";16B" in rawmode: + rawmode = rawmode.replace(";16B", ";16N") + if ";16L" in rawmode: + rawmode = rawmode.replace(";16L", ";16N") + + # Offset in the tile tuple is 0, we go from 0,0 to + # w,h, and we only do this once -- eds + a = (rawmode, self._compression, False, self.tag_v2.offset) + self.tile.append(("libtiff", (0, 0, xsize, ysize), 0, a)) + + elif STRIPOFFSETS in self.tag_v2 or TILEOFFSETS in self.tag_v2: + # striped image + if STRIPOFFSETS in self.tag_v2: + offsets = self.tag_v2[STRIPOFFSETS] + h = self.tag_v2.get(ROWSPERSTRIP, ysize) + w = self.size[0] + else: + # tiled image + offsets = self.tag_v2[TILEOFFSETS] + w = self.tag_v2.get(322) + h = self.tag_v2.get(323) + + for offset in offsets: + if x + w > xsize: + stride = w * sum(bps_tuple) / 8 # bytes per line + else: + stride = 0 + + tile_rawmode = rawmode + if self._planar_configuration == 2: + # each band on it's own layer + tile_rawmode = rawmode[layer] + # adjust stride width accordingly + stride /= bps_count + + a = (tile_rawmode, int(stride), 1) + self.tile.append( + ( + self._compression, + (x, y, min(x + w, xsize), min(y + h, ysize)), + offset, + a, + ) + ) + x = x + w + if x >= self.size[0]: + x, y = 0, y + h + if y >= self.size[1]: + x = y = 0 + layer += 1 + else: + logger.debug("- unsupported data organization") + raise SyntaxError("unknown data organization") + + # Fix up info. + if ICCPROFILE in self.tag_v2: + self.info["icc_profile"] = self.tag_v2[ICCPROFILE] + + # fixup palette descriptor + + if self.mode in ["P", "PA"]: + palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]] + self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) + + self._tile_orientation = self.tag_v2.get(0x0112) + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +# +# -------------------------------------------------------------------- +# Write TIFF files + +# little endian is default except for image modes with +# explicit big endian byte-order + +SAVE_INFO = { + # mode => rawmode, byteorder, photometrics, + # sampleformat, bitspersample, extra + "1": ("1", II, 1, 1, (1,), None), + "L": ("L", II, 1, 1, (8,), None), + "LA": ("LA", II, 1, 1, (8, 8), 2), + "P": ("P", II, 3, 1, (8,), None), + "PA": ("PA", II, 3, 1, (8, 8), 2), + "I": ("I;32S", II, 1, 2, (32,), None), + "I;16": ("I;16", II, 1, 1, (16,), None), + "I;16S": ("I;16S", II, 1, 2, (16,), None), + "F": ("F;32F", II, 1, 3, (32,), None), + "RGB": ("RGB", II, 2, 1, (8, 8, 8), None), + "RGBX": ("RGBX", II, 2, 1, (8, 8, 8, 8), 0), + "RGBA": ("RGBA", II, 2, 1, (8, 8, 8, 8), 2), + "CMYK": ("CMYK", II, 5, 1, (8, 8, 8, 8), None), + "YCbCr": ("YCbCr", II, 6, 1, (8, 8, 8), None), + "LAB": ("LAB", II, 8, 1, (8, 8, 8), None), + "I;32BS": ("I;32BS", MM, 1, 2, (32,), None), + "I;16B": ("I;16B", MM, 1, 1, (16,), None), + "I;16BS": ("I;16BS", MM, 1, 2, (16,), None), + "F;32BF": ("F;32BF", MM, 1, 3, (32,), None), +} + + +def _save(im, fp, filename): + + try: + rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] + except KeyError as e: + raise OSError(f"cannot write mode {im.mode} as TIFF") from e + + ifd = ImageFileDirectory_v2(prefix=prefix) + + compression = im.encoderinfo.get("compression", im.info.get("compression")) + if compression is None: + compression = "raw" + elif compression == "tiff_jpeg": + # OJPEG is obsolete, so use new-style JPEG compression instead + compression = "jpeg" + + libtiff = WRITE_LIBTIFF or compression != "raw" + + # required for color libtiff images + ifd[PLANAR_CONFIGURATION] = getattr(im, "_planar_configuration", 1) + + ifd[IMAGEWIDTH] = im.size[0] + ifd[IMAGELENGTH] = im.size[1] + + # write any arbitrary tags passed in as an ImageFileDirectory + info = im.encoderinfo.get("tiffinfo", {}) + logger.debug("Tiffinfo Keys: %s" % list(info)) + if isinstance(info, ImageFileDirectory_v1): + info = info.to_v2() + for key in info: + ifd[key] = info.get(key) + try: + ifd.tagtype[key] = info.tagtype[key] + except Exception: + pass # might not be an IFD. Might not have populated type + + # additions written by Greg Couch, gregc@cgl.ucsf.edu + # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com + if hasattr(im, "tag_v2"): + # preserve tags from original TIFF image file + for key in ( + RESOLUTION_UNIT, + X_RESOLUTION, + Y_RESOLUTION, + IPTC_NAA_CHUNK, + PHOTOSHOP_CHUNK, + XMP, + ): + if key in im.tag_v2: + ifd[key] = im.tag_v2[key] + ifd.tagtype[key] = im.tag_v2.tagtype[key] + + # preserve ICC profile (should also work when saving other formats + # which support profiles as TIFF) -- 2008-06-06 Florian Hoech + if "icc_profile" in im.info: + ifd[ICCPROFILE] = im.info["icc_profile"] + + for key, name in [ + (IMAGEDESCRIPTION, "description"), + (X_RESOLUTION, "resolution"), + (Y_RESOLUTION, "resolution"), + (X_RESOLUTION, "x_resolution"), + (Y_RESOLUTION, "y_resolution"), + (RESOLUTION_UNIT, "resolution_unit"), + (SOFTWARE, "software"), + (DATE_TIME, "date_time"), + (ARTIST, "artist"), + (COPYRIGHT, "copyright"), + ]: + if name in im.encoderinfo: + ifd[key] = im.encoderinfo[name] + + dpi = im.encoderinfo.get("dpi") + if dpi: + ifd[RESOLUTION_UNIT] = 2 + ifd[X_RESOLUTION] = int(dpi[0] + 0.5) + ifd[Y_RESOLUTION] = int(dpi[1] + 0.5) + + if bits != (1,): + ifd[BITSPERSAMPLE] = bits + if len(bits) != 1: + ifd[SAMPLESPERPIXEL] = len(bits) + if extra is not None: + ifd[EXTRASAMPLES] = extra + if format != 1: + ifd[SAMPLEFORMAT] = format + + ifd[PHOTOMETRIC_INTERPRETATION] = photo + + if im.mode in ["P", "PA"]: + lut = im.im.getpalette("RGB", "RGB;L") + ifd[COLORMAP] = tuple(v * 256 for v in lut) + # data orientation + stride = len(bits) * ((im.size[0] * bits[0] + 7) // 8) + ifd[ROWSPERSTRIP] = im.size[1] + strip_byte_counts = stride * im.size[1] + if strip_byte_counts >= 2 ** 16: + ifd.tagtype[STRIPBYTECOUNTS] = TiffTags.LONG + ifd[STRIPBYTECOUNTS] = strip_byte_counts + ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer + # no compression by default: + ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) + + if libtiff: + if "quality" in im.encoderinfo: + quality = im.encoderinfo["quality"] + if not isinstance(quality, int) or quality < 0 or quality > 100: + raise ValueError("Invalid quality setting") + if compression != "jpeg": + raise ValueError( + "quality setting only supported for 'jpeg' compression" + ) + ifd[JPEGQUALITY] = quality + + logger.debug("Saving using libtiff encoder") + logger.debug("Items: %s" % sorted(ifd.items())) + _fp = 0 + if hasattr(fp, "fileno"): + try: + fp.seek(0) + _fp = os.dup(fp.fileno()) + except io.UnsupportedOperation: + pass + + # optional types for non core tags + types = {} + # SAMPLEFORMAT is determined by the image format and should not be copied + # from legacy_ifd. + # STRIPOFFSETS and STRIPBYTECOUNTS are added by the library + # based on the data in the strip. + # The other tags expect arrays with a certain length (fixed or depending on + # BITSPERSAMPLE, etc), passing arrays with a different length will result in + # segfaults. Block these tags until we add extra validation. + # SUBIFD may also cause a segfault. + blocklist = [ + REFERENCEBLACKWHITE, + SAMPLEFORMAT, + STRIPBYTECOUNTS, + STRIPOFFSETS, + TRANSFERFUNCTION, + SUBIFD, + ] + + atts = {} + # bits per sample is a single short in the tiff directory, not a list. + atts[BITSPERSAMPLE] = bits[0] + # Merge the ones that we have with (optional) more bits from + # the original file, e.g x,y resolution so that we can + # save(load('')) == original file. + legacy_ifd = {} + if hasattr(im, "tag"): + legacy_ifd = im.tag.to_v2() + for tag, value in itertools.chain( + ifd.items(), getattr(im, "tag_v2", {}).items(), legacy_ifd.items() + ): + # Libtiff can only process certain core items without adding + # them to the custom dictionary. + # Custom items are supported for int, float, unicode, string and byte + # values. Other types and tuples require a tagtype. + if tag not in TiffTags.LIBTIFF_CORE: + if not Image.core.libtiff_support_custom_tags: + continue + + if tag in ifd.tagtype: + types[tag] = ifd.tagtype[tag] + elif not (isinstance(value, (int, float, str, bytes))): + continue + else: + type = TiffTags.lookup(tag).type + if type: + types[tag] = type + if tag not in atts and tag not in blocklist: + if isinstance(value, str): + atts[tag] = value.encode("ascii", "replace") + b"\0" + elif isinstance(value, IFDRational): + atts[tag] = float(value) + else: + atts[tag] = value + + logger.debug("Converted items: %s" % sorted(atts.items())) + + # libtiff always expects the bytes in native order. + # we're storing image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if im.mode in ("I;16B", "I;16"): + rawmode = "I;16N" + + # Pass tags as sorted list so that the tags are set in a fixed order. + # This is required by libtiff for some tags. For example, the JPEGQUALITY + # pseudo tag requires that the COMPRESS tag was already set. + tags = list(atts.items()) + tags.sort() + a = (rawmode, compression, _fp, filename, tags, types) + e = Image._getencoder(im.mode, "libtiff", a, im.encoderconfig) + e.setimage(im.im, (0, 0) + im.size) + while True: + # undone, change to self.decodermaxblock: + l, s, d = e.encode(16 * 1024) + if not _fp: + fp.write(d) + if s: + break + if s < 0: + raise OSError(f"encoder error {s} when writing image file") + + else: + offset = ifd.save(fp) + + ImageFile._save( + im, fp, [("raw", (0, 0) + im.size, offset, (rawmode, stride, 1))] + ) + + # -- helper for multi-page save -- + if "_debug_multipage" in im.encoderinfo: + # just to access o32 and o16 (using correct byte order) + im._debug_multipage = ifd + + +class AppendingTiffWriter: + fieldSizes = [ + 0, # None + 1, # byte + 1, # ascii + 2, # short + 4, # long + 8, # rational + 1, # sbyte + 1, # undefined + 2, # sshort + 4, # slong + 8, # srational + 4, # float + 8, # double + ] + + # StripOffsets = 273 + # FreeOffsets = 288 + # TileOffsets = 324 + # JPEGQTables = 519 + # JPEGDCTables = 520 + # JPEGACTables = 521 + Tags = {273, 288, 324, 519, 520, 521} + + def __init__(self, fn, new=False): + if hasattr(fn, "read"): + self.f = fn + self.close_fp = False + else: + self.name = fn + self.close_fp = True + try: + self.f = open(fn, "w+b" if new else "r+b") + except OSError: + self.f = open(fn, "w+b") + self.beginning = self.f.tell() + self.setup() + + def setup(self): + # Reset everything. + self.f.seek(self.beginning, os.SEEK_SET) + + self.whereToWriteNewIFDOffset = None + self.offsetOfNewPage = 0 + + self.IIMM = IIMM = self.f.read(4) + if not IIMM: + # empty file - first page + self.isFirst = True + return + + self.isFirst = False + if IIMM == b"II\x2a\x00": + self.setEndian("<") + elif IIMM == b"MM\x00\x2a": + self.setEndian(">") + else: + raise RuntimeError("Invalid TIFF file header") + + self.skipIFDs() + self.goToEnd() + + def finalize(self): + if self.isFirst: + return + + # fix offsets + self.f.seek(self.offsetOfNewPage) + + IIMM = self.f.read(4) + if not IIMM: + # raise RuntimeError("nothing written into new page") + # Make it easy to finish a frame without committing to a new one. + return + + if IIMM != self.IIMM: + raise RuntimeError("IIMM of new page doesn't match IIMM of first page") + + IFDoffset = self.readLong() + IFDoffset += self.offsetOfNewPage + self.f.seek(self.whereToWriteNewIFDOffset) + self.writeLong(IFDoffset) + self.f.seek(IFDoffset) + self.fixIFD() + + def newFrame(self): + # Call this to finish a frame. + self.finalize() + self.setup() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + if self.close_fp: + self.close() + return False + + def tell(self): + return self.f.tell() - self.offsetOfNewPage + + def seek(self, offset, whence=io.SEEK_SET): + if whence == os.SEEK_SET: + offset += self.offsetOfNewPage + + self.f.seek(offset, whence) + return self.tell() + + def goToEnd(self): + self.f.seek(0, os.SEEK_END) + pos = self.f.tell() + + # pad to 16 byte boundary + padBytes = 16 - pos % 16 + if 0 < padBytes < 16: + self.f.write(bytes(padBytes)) + self.offsetOfNewPage = self.f.tell() + + def setEndian(self, endian): + self.endian = endian + self.longFmt = self.endian + "L" + self.shortFmt = self.endian + "H" + self.tagFormat = self.endian + "HHL" + + def skipIFDs(self): + while True: + IFDoffset = self.readLong() + if IFDoffset == 0: + self.whereToWriteNewIFDOffset = self.f.tell() - 4 + break + + self.f.seek(IFDoffset) + numTags = self.readShort() + self.f.seek(numTags * 12, os.SEEK_CUR) + + def write(self, data): + return self.f.write(data) + + def readShort(self): + (value,) = struct.unpack(self.shortFmt, self.f.read(2)) + return value + + def readLong(self): + (value,) = struct.unpack(self.longFmt, self.f.read(4)) + return value + + def rewriteLastShortToLong(self, value): + self.f.seek(-2, os.SEEK_CUR) + bytesWritten = self.f.write(struct.pack(self.longFmt, value)) + if bytesWritten is not None and bytesWritten != 4: + raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 4") + + def rewriteLastShort(self, value): + self.f.seek(-2, os.SEEK_CUR) + bytesWritten = self.f.write(struct.pack(self.shortFmt, value)) + if bytesWritten is not None and bytesWritten != 2: + raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 2") + + def rewriteLastLong(self, value): + self.f.seek(-4, os.SEEK_CUR) + bytesWritten = self.f.write(struct.pack(self.longFmt, value)) + if bytesWritten is not None and bytesWritten != 4: + raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 4") + + def writeShort(self, value): + bytesWritten = self.f.write(struct.pack(self.shortFmt, value)) + if bytesWritten is not None and bytesWritten != 2: + raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 2") + + def writeLong(self, value): + bytesWritten = self.f.write(struct.pack(self.longFmt, value)) + if bytesWritten is not None and bytesWritten != 4: + raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 4") + + def close(self): + self.finalize() + self.f.close() + + def fixIFD(self): + numTags = self.readShort() + + for i in range(numTags): + tag, fieldType, count = struct.unpack(self.tagFormat, self.f.read(8)) + + fieldSize = self.fieldSizes[fieldType] + totalSize = fieldSize * count + isLocal = totalSize <= 4 + if not isLocal: + offset = self.readLong() + offset += self.offsetOfNewPage + self.rewriteLastLong(offset) + + if tag in self.Tags: + curPos = self.f.tell() + + if isLocal: + self.fixOffsets( + count, isShort=(fieldSize == 2), isLong=(fieldSize == 4) + ) + self.f.seek(curPos + 4) + else: + self.f.seek(offset) + self.fixOffsets( + count, isShort=(fieldSize == 2), isLong=(fieldSize == 4) + ) + self.f.seek(curPos) + + offset = curPos = None + + elif isLocal: + # skip the locally stored value that is not an offset + self.f.seek(4, os.SEEK_CUR) + + def fixOffsets(self, count, isShort=False, isLong=False): + if not isShort and not isLong: + raise RuntimeError("offset is neither short nor long") + + for i in range(count): + offset = self.readShort() if isShort else self.readLong() + offset += self.offsetOfNewPage + if isShort and offset >= 65536: + # offset is now too large - we must convert shorts to longs + if count != 1: + raise RuntimeError("not implemented") # XXX TODO + + # simple case - the offset is just one and therefore it is + # local (not referenced with another offset) + self.rewriteLastShortToLong(offset) + self.f.seek(-10, os.SEEK_CUR) + self.writeShort(TiffTags.LONG) # rewrite the type to LONG + self.f.seek(8, os.SEEK_CUR) + elif isShort: + self.rewriteLastShort(offset) + else: + self.rewriteLastLong(offset) + + +def _save_all(im, fp, filename): + encoderinfo = im.encoderinfo.copy() + encoderconfig = im.encoderconfig + append_images = list(encoderinfo.get("append_images", [])) + if not hasattr(im, "n_frames") and not append_images: + return _save(im, fp, filename) + + cur_idx = im.tell() + try: + with AppendingTiffWriter(fp) as tf: + for ims in [im] + append_images: + ims.encoderinfo = encoderinfo + ims.encoderconfig = encoderconfig + if not hasattr(ims, "n_frames"): + nfr = 1 + else: + nfr = ims.n_frames + + for idx in range(nfr): + ims.seek(idx) + ims.load() + _save(ims, tf, filename) + tf.newFrame() + finally: + im.seek(cur_idx) + + +# +# -------------------------------------------------------------------- +# Register + +Image.register_open(TiffImageFile.format, TiffImageFile, _accept) +Image.register_save(TiffImageFile.format, _save) +Image.register_save_all(TiffImageFile.format, _save_all) + +Image.register_extensions(TiffImageFile.format, [".tif", ".tiff"]) + +Image.register_mime(TiffImageFile.format, "image/tiff") diff --git a/venv/lib/python3.11/site-packages/PIL/TiffTags.py b/venv/lib/python3.11/site-packages/PIL/TiffTags.py new file mode 100644 index 00000000..796ff347 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/TiffTags.py @@ -0,0 +1,499 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TIFF tags +# +# This module provides clear-text names for various well-known +# TIFF tags. the TIFF codec works just fine without it. +# +# Copyright (c) Secret Labs AB 1999. +# +# See the README file for information on usage and redistribution. +# + +## +# This module provides constants and clear-text names for various +# well-known TIFF tags. +## + +from collections import namedtuple + + +class TagInfo(namedtuple("_TagInfo", "value name type length enum")): + __slots__ = [] + + def __new__(cls, value=None, name="unknown", type=None, length=None, enum=None): + return super().__new__(cls, value, name, type, length, enum or {}) + + def cvt_enum(self, value): + # Using get will call hash(value), which can be expensive + # for some types (e.g. Fraction). Since self.enum is rarely + # used, it's usually better to test it first. + return self.enum.get(value, value) if self.enum else value + + +def lookup(tag): + """ + :param tag: Integer tag number + :returns: Taginfo namedtuple, From the TAGS_V2 info if possible, + otherwise just populating the value and name from TAGS. + If the tag is not recognized, "unknown" is returned for the name + + """ + + return TAGS_V2.get(tag, TagInfo(tag, TAGS.get(tag, "unknown"))) + + +## +# Map tag numbers to tag info. +# +# id: (Name, Type, Length, enum_values) +# +# The length here differs from the length in the tiff spec. For +# numbers, the tiff spec is for the number of fields returned. We +# agree here. For string-like types, the tiff spec uses the length of +# field in bytes. In Pillow, we are using the number of expected +# fields, in general 1 for string-like types. + + +BYTE = 1 +ASCII = 2 +SHORT = 3 +LONG = 4 +RATIONAL = 5 +SIGNED_BYTE = 6 +UNDEFINED = 7 +SIGNED_SHORT = 8 +SIGNED_LONG = 9 +SIGNED_RATIONAL = 10 +FLOAT = 11 +DOUBLE = 12 +IFD = 13 + +TAGS_V2 = { + 254: ("NewSubfileType", LONG, 1), + 255: ("SubfileType", SHORT, 1), + 256: ("ImageWidth", LONG, 1), + 257: ("ImageLength", LONG, 1), + 258: ("BitsPerSample", SHORT, 0), + 259: ( + "Compression", + SHORT, + 1, + { + "Uncompressed": 1, + "CCITT 1d": 2, + "Group 3 Fax": 3, + "Group 4 Fax": 4, + "LZW": 5, + "JPEG": 6, + "PackBits": 32773, + }, + ), + 262: ( + "PhotometricInterpretation", + SHORT, + 1, + { + "WhiteIsZero": 0, + "BlackIsZero": 1, + "RGB": 2, + "RGB Palette": 3, + "Transparency Mask": 4, + "CMYK": 5, + "YCbCr": 6, + "CieLAB": 8, + "CFA": 32803, # TIFF/EP, Adobe DNG + "LinearRaw": 32892, # Adobe DNG + }, + ), + 263: ("Threshholding", SHORT, 1), + 264: ("CellWidth", SHORT, 1), + 265: ("CellLength", SHORT, 1), + 266: ("FillOrder", SHORT, 1), + 269: ("DocumentName", ASCII, 1), + 270: ("ImageDescription", ASCII, 1), + 271: ("Make", ASCII, 1), + 272: ("Model", ASCII, 1), + 273: ("StripOffsets", LONG, 0), + 274: ("Orientation", SHORT, 1), + 277: ("SamplesPerPixel", SHORT, 1), + 278: ("RowsPerStrip", LONG, 1), + 279: ("StripByteCounts", LONG, 0), + 280: ("MinSampleValue", SHORT, 0), + 281: ("MaxSampleValue", SHORT, 0), + 282: ("XResolution", RATIONAL, 1), + 283: ("YResolution", RATIONAL, 1), + 284: ("PlanarConfiguration", SHORT, 1, {"Contiguous": 1, "Separate": 2}), + 285: ("PageName", ASCII, 1), + 286: ("XPosition", RATIONAL, 1), + 287: ("YPosition", RATIONAL, 1), + 288: ("FreeOffsets", LONG, 1), + 289: ("FreeByteCounts", LONG, 1), + 290: ("GrayResponseUnit", SHORT, 1), + 291: ("GrayResponseCurve", SHORT, 0), + 292: ("T4Options", LONG, 1), + 293: ("T6Options", LONG, 1), + 296: ("ResolutionUnit", SHORT, 1, {"none": 1, "inch": 2, "cm": 3}), + 297: ("PageNumber", SHORT, 2), + 301: ("TransferFunction", SHORT, 0), + 305: ("Software", ASCII, 1), + 306: ("DateTime", ASCII, 1), + 315: ("Artist", ASCII, 1), + 316: ("HostComputer", ASCII, 1), + 317: ("Predictor", SHORT, 1, {"none": 1, "Horizontal Differencing": 2}), + 318: ("WhitePoint", RATIONAL, 2), + 319: ("PrimaryChromaticities", RATIONAL, 6), + 320: ("ColorMap", SHORT, 0), + 321: ("HalftoneHints", SHORT, 2), + 322: ("TileWidth", LONG, 1), + 323: ("TileLength", LONG, 1), + 324: ("TileOffsets", LONG, 0), + 325: ("TileByteCounts", LONG, 0), + 332: ("InkSet", SHORT, 1), + 333: ("InkNames", ASCII, 1), + 334: ("NumberOfInks", SHORT, 1), + 336: ("DotRange", SHORT, 0), + 337: ("TargetPrinter", ASCII, 1), + 338: ("ExtraSamples", SHORT, 0), + 339: ("SampleFormat", SHORT, 0), + 340: ("SMinSampleValue", DOUBLE, 0), + 341: ("SMaxSampleValue", DOUBLE, 0), + 342: ("TransferRange", SHORT, 6), + 347: ("JPEGTables", UNDEFINED, 1), + # obsolete JPEG tags + 512: ("JPEGProc", SHORT, 1), + 513: ("JPEGInterchangeFormat", LONG, 1), + 514: ("JPEGInterchangeFormatLength", LONG, 1), + 515: ("JPEGRestartInterval", SHORT, 1), + 517: ("JPEGLosslessPredictors", SHORT, 0), + 518: ("JPEGPointTransforms", SHORT, 0), + 519: ("JPEGQTables", LONG, 0), + 520: ("JPEGDCTables", LONG, 0), + 521: ("JPEGACTables", LONG, 0), + 529: ("YCbCrCoefficients", RATIONAL, 3), + 530: ("YCbCrSubSampling", SHORT, 2), + 531: ("YCbCrPositioning", SHORT, 1), + 532: ("ReferenceBlackWhite", RATIONAL, 6), + 700: ("XMP", BYTE, 0), + 33432: ("Copyright", ASCII, 1), + 33723: ("IptcNaaInfo", UNDEFINED, 0), + 34377: ("PhotoshopInfo", BYTE, 0), + # FIXME add more tags here + 34665: ("ExifIFD", LONG, 1), + 34675: ("ICCProfile", UNDEFINED, 1), + 34853: ("GPSInfoIFD", LONG, 1), + # MPInfo + 45056: ("MPFVersion", UNDEFINED, 1), + 45057: ("NumberOfImages", LONG, 1), + 45058: ("MPEntry", UNDEFINED, 1), + 45059: ("ImageUIDList", UNDEFINED, 0), # UNDONE, check + 45060: ("TotalFrames", LONG, 1), + 45313: ("MPIndividualNum", LONG, 1), + 45569: ("PanOrientation", LONG, 1), + 45570: ("PanOverlap_H", RATIONAL, 1), + 45571: ("PanOverlap_V", RATIONAL, 1), + 45572: ("BaseViewpointNum", LONG, 1), + 45573: ("ConvergenceAngle", SIGNED_RATIONAL, 1), + 45574: ("BaselineLength", RATIONAL, 1), + 45575: ("VerticalDivergence", SIGNED_RATIONAL, 1), + 45576: ("AxisDistance_X", SIGNED_RATIONAL, 1), + 45577: ("AxisDistance_Y", SIGNED_RATIONAL, 1), + 45578: ("AxisDistance_Z", SIGNED_RATIONAL, 1), + 45579: ("YawAngle", SIGNED_RATIONAL, 1), + 45580: ("PitchAngle", SIGNED_RATIONAL, 1), + 45581: ("RollAngle", SIGNED_RATIONAL, 1), + 50741: ("MakerNoteSafety", SHORT, 1, {"Unsafe": 0, "Safe": 1}), + 50780: ("BestQualityScale", RATIONAL, 1), + 50838: ("ImageJMetaDataByteCounts", LONG, 0), # Can be more than one + 50839: ("ImageJMetaData", UNDEFINED, 1), # see Issue #2006 +} + +# Legacy Tags structure +# these tags aren't included above, but were in the previous versions +TAGS = { + 347: "JPEGTables", + 700: "XMP", + # Additional Exif Info + 32932: "Wang Annotation", + 33434: "ExposureTime", + 33437: "FNumber", + 33445: "MD FileTag", + 33446: "MD ScalePixel", + 33447: "MD ColorTable", + 33448: "MD LabName", + 33449: "MD SampleInfo", + 33450: "MD PrepDate", + 33451: "MD PrepTime", + 33452: "MD FileUnits", + 33550: "ModelPixelScaleTag", + 33723: "IptcNaaInfo", + 33918: "INGR Packet Data Tag", + 33919: "INGR Flag Registers", + 33920: "IrasB Transformation Matrix", + 33922: "ModelTiepointTag", + 34264: "ModelTransformationTag", + 34377: "PhotoshopInfo", + 34735: "GeoKeyDirectoryTag", + 34736: "GeoDoubleParamsTag", + 34737: "GeoAsciiParamsTag", + 34850: "ExposureProgram", + 34852: "SpectralSensitivity", + 34855: "ISOSpeedRatings", + 34856: "OECF", + 34864: "SensitivityType", + 34865: "StandardOutputSensitivity", + 34866: "RecommendedExposureIndex", + 34867: "ISOSpeed", + 34868: "ISOSpeedLatitudeyyy", + 34869: "ISOSpeedLatitudezzz", + 34908: "HylaFAX FaxRecvParams", + 34909: "HylaFAX FaxSubAddress", + 34910: "HylaFAX FaxRecvTime", + 36864: "ExifVersion", + 36867: "DateTimeOriginal", + 36868: "DateTImeDigitized", + 37121: "ComponentsConfiguration", + 37122: "CompressedBitsPerPixel", + 37724: "ImageSourceData", + 37377: "ShutterSpeedValue", + 37378: "ApertureValue", + 37379: "BrightnessValue", + 37380: "ExposureBiasValue", + 37381: "MaxApertureValue", + 37382: "SubjectDistance", + 37383: "MeteringMode", + 37384: "LightSource", + 37385: "Flash", + 37386: "FocalLength", + 37396: "SubjectArea", + 37500: "MakerNote", + 37510: "UserComment", + 37520: "SubSec", + 37521: "SubSecTimeOriginal", + 37522: "SubsecTimeDigitized", + 40960: "FlashPixVersion", + 40961: "ColorSpace", + 40962: "PixelXDimension", + 40963: "PixelYDimension", + 40964: "RelatedSoundFile", + 40965: "InteroperabilityIFD", + 41483: "FlashEnergy", + 41484: "SpatialFrequencyResponse", + 41486: "FocalPlaneXResolution", + 41487: "FocalPlaneYResolution", + 41488: "FocalPlaneResolutionUnit", + 41492: "SubjectLocation", + 41493: "ExposureIndex", + 41495: "SensingMethod", + 41728: "FileSource", + 41729: "SceneType", + 41730: "CFAPattern", + 41985: "CustomRendered", + 41986: "ExposureMode", + 41987: "WhiteBalance", + 41988: "DigitalZoomRatio", + 41989: "FocalLengthIn35mmFilm", + 41990: "SceneCaptureType", + 41991: "GainControl", + 41992: "Contrast", + 41993: "Saturation", + 41994: "Sharpness", + 41995: "DeviceSettingDescription", + 41996: "SubjectDistanceRange", + 42016: "ImageUniqueID", + 42032: "CameraOwnerName", + 42033: "BodySerialNumber", + 42034: "LensSpecification", + 42035: "LensMake", + 42036: "LensModel", + 42037: "LensSerialNumber", + 42112: "GDAL_METADATA", + 42113: "GDAL_NODATA", + 42240: "Gamma", + 50215: "Oce Scanjob Description", + 50216: "Oce Application Selector", + 50217: "Oce Identification Number", + 50218: "Oce ImageLogic Characteristics", + # Adobe DNG + 50706: "DNGVersion", + 50707: "DNGBackwardVersion", + 50708: "UniqueCameraModel", + 50709: "LocalizedCameraModel", + 50710: "CFAPlaneColor", + 50711: "CFALayout", + 50712: "LinearizationTable", + 50713: "BlackLevelRepeatDim", + 50714: "BlackLevel", + 50715: "BlackLevelDeltaH", + 50716: "BlackLevelDeltaV", + 50717: "WhiteLevel", + 50718: "DefaultScale", + 50719: "DefaultCropOrigin", + 50720: "DefaultCropSize", + 50721: "ColorMatrix1", + 50722: "ColorMatrix2", + 50723: "CameraCalibration1", + 50724: "CameraCalibration2", + 50725: "ReductionMatrix1", + 50726: "ReductionMatrix2", + 50727: "AnalogBalance", + 50728: "AsShotNeutral", + 50729: "AsShotWhiteXY", + 50730: "BaselineExposure", + 50731: "BaselineNoise", + 50732: "BaselineSharpness", + 50733: "BayerGreenSplit", + 50734: "LinearResponseLimit", + 50735: "CameraSerialNumber", + 50736: "LensInfo", + 50737: "ChromaBlurRadius", + 50738: "AntiAliasStrength", + 50740: "DNGPrivateData", + 50778: "CalibrationIlluminant1", + 50779: "CalibrationIlluminant2", + 50784: "Alias Layer Metadata", +} + + +def _populate(): + for k, v in TAGS_V2.items(): + # Populate legacy structure. + TAGS[k] = v[0] + if len(v) == 4: + for sk, sv in v[3].items(): + TAGS[(k, sv)] = sk + + TAGS_V2[k] = TagInfo(k, *v) + + +_populate() +## +# Map type numbers to type names -- defined in ImageFileDirectory. + +TYPES = {} + +# was: +# TYPES = { +# 1: "byte", +# 2: "ascii", +# 3: "short", +# 4: "long", +# 5: "rational", +# 6: "signed byte", +# 7: "undefined", +# 8: "signed short", +# 9: "signed long", +# 10: "signed rational", +# 11: "float", +# 12: "double", +# } + +# +# These tags are handled by default in libtiff, without +# adding to the custom dictionary. From tif_dir.c, searching for +# case TIFFTAG in the _TIFFVSetField function: +# Line: item. +# 148: case TIFFTAG_SUBFILETYPE: +# 151: case TIFFTAG_IMAGEWIDTH: +# 154: case TIFFTAG_IMAGELENGTH: +# 157: case TIFFTAG_BITSPERSAMPLE: +# 181: case TIFFTAG_COMPRESSION: +# 202: case TIFFTAG_PHOTOMETRIC: +# 205: case TIFFTAG_THRESHHOLDING: +# 208: case TIFFTAG_FILLORDER: +# 214: case TIFFTAG_ORIENTATION: +# 221: case TIFFTAG_SAMPLESPERPIXEL: +# 228: case TIFFTAG_ROWSPERSTRIP: +# 238: case TIFFTAG_MINSAMPLEVALUE: +# 241: case TIFFTAG_MAXSAMPLEVALUE: +# 244: case TIFFTAG_SMINSAMPLEVALUE: +# 247: case TIFFTAG_SMAXSAMPLEVALUE: +# 250: case TIFFTAG_XRESOLUTION: +# 256: case TIFFTAG_YRESOLUTION: +# 262: case TIFFTAG_PLANARCONFIG: +# 268: case TIFFTAG_XPOSITION: +# 271: case TIFFTAG_YPOSITION: +# 274: case TIFFTAG_RESOLUTIONUNIT: +# 280: case TIFFTAG_PAGENUMBER: +# 284: case TIFFTAG_HALFTONEHINTS: +# 288: case TIFFTAG_COLORMAP: +# 294: case TIFFTAG_EXTRASAMPLES: +# 298: case TIFFTAG_MATTEING: +# 305: case TIFFTAG_TILEWIDTH: +# 316: case TIFFTAG_TILELENGTH: +# 327: case TIFFTAG_TILEDEPTH: +# 333: case TIFFTAG_DATATYPE: +# 344: case TIFFTAG_SAMPLEFORMAT: +# 361: case TIFFTAG_IMAGEDEPTH: +# 364: case TIFFTAG_SUBIFD: +# 376: case TIFFTAG_YCBCRPOSITIONING: +# 379: case TIFFTAG_YCBCRSUBSAMPLING: +# 383: case TIFFTAG_TRANSFERFUNCTION: +# 389: case TIFFTAG_REFERENCEBLACKWHITE: +# 393: case TIFFTAG_INKNAMES: + +# Following pseudo-tags are also handled by default in libtiff: +# TIFFTAG_JPEGQUALITY 65537 + +# some of these are not in our TAGS_V2 dict and were included from tiff.h + +# This list also exists in encode.c +LIBTIFF_CORE = { + 255, + 256, + 257, + 258, + 259, + 262, + 263, + 266, + 274, + 277, + 278, + 280, + 281, + 340, + 341, + 282, + 283, + 284, + 286, + 287, + 296, + 297, + 321, + 320, + 338, + 32995, + 322, + 323, + 32998, + 32996, + 339, + 32997, + 330, + 531, + 530, + 301, + 532, + 333, + # as above + 269, # this has been in our tests forever, and works + 65537, +} + +LIBTIFF_CORE.remove(301) # Array of short, crashes +LIBTIFF_CORE.remove(532) # Array of long, crashes + +LIBTIFF_CORE.remove(255) # We don't have support for subfiletypes +LIBTIFF_CORE.remove(322) # We don't have support for writing tiled images with libtiff +LIBTIFF_CORE.remove(323) # Tiled images +LIBTIFF_CORE.remove(333) # Ink Names either + +# Note to advanced users: There may be combinations of these +# parameters and values that when added properly, will work and +# produce valid tiff images that may work in your application. +# It is safe to add and remove tags from this set from Pillow's point +# of view so long as you test against libtiff. diff --git a/venv/lib/python3.11/site-packages/PIL/WalImageFile.py b/venv/lib/python3.11/site-packages/PIL/WalImageFile.py new file mode 100644 index 00000000..b578d698 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/WalImageFile.py @@ -0,0 +1,126 @@ +# +# The Python Imaging Library. +# $Id$ +# +# WAL file handling +# +# History: +# 2003-04-23 fl created +# +# Copyright (c) 2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +""" +This reader is based on the specification available from: +https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml +and has been tested with a few sample files found using google. + +.. note:: + This format cannot be automatically recognized, so the reader + is not registered for use with :py:func:`PIL.Image.open()`. + To open a WAL file, use the :py:func:`PIL.WalImageFile.open()` function instead. +""" + +import builtins + +from . import Image +from ._binary import i32le as i32 + + +def open(filename): + """ + Load texture from a Quake2 WAL texture file. + + By default, a Quake2 standard palette is attached to the texture. + To override the palette, use the :py:func:`PIL.Image.Image.putpalette()` method. + + :param filename: WAL file name, or an opened file handle. + :returns: An image instance. + """ + # FIXME: modify to return a WalImageFile instance instead of + # plain Image object ? + + def imopen(fp): + # read header fields + header = fp.read(32 + 24 + 32 + 12) + size = i32(header, 32), i32(header, 36) + offset = i32(header, 40) + + # load pixel data + fp.seek(offset) + + Image._decompression_bomb_check(size) + im = Image.frombytes("P", size, fp.read(size[0] * size[1])) + im.putpalette(quake2palette) + + im.format = "WAL" + im.format_description = "Quake2 Texture" + + # strings are null-terminated + im.info["name"] = header[:32].split(b"\0", 1)[0] + next_name = header[56 : 56 + 32].split(b"\0", 1)[0] + if next_name: + im.info["next_name"] = next_name + + return im + + if hasattr(filename, "read"): + return imopen(filename) + else: + with builtins.open(filename, "rb") as fp: + return imopen(fp) + + +quake2palette = ( + # default palette taken from piffo 0.93 by Hans Häggström + b"\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e" + b"\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f" + b"\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c" + b"\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b" + b"\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10" + b"\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07" + b"\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f" + b"\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16" + b"\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d" + b"\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31" + b"\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28" + b"\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07" + b"\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27" + b"\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b" + b"\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01" + b"\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21" + b"\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14" + b"\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07" + b"\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14" + b"\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f" + b"\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34" + b"\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d" + b"\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14" + b"\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01" + b"\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24" + b"\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10" + b"\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01" + b"\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27" + b"\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c" + b"\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a" + b"\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26" + b"\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d" + b"\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01" + b"\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20" + b"\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17" + b"\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07" + b"\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25" + b"\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c" + b"\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01" + b"\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23" + b"\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f" + b"\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b" + b"\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37" + b"\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b" + b"\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01" + b"\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10" + b"\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b" + b"\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20" +) diff --git a/venv/lib/python3.11/site-packages/PIL/WebPImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/WebPImagePlugin.py new file mode 100644 index 00000000..2e9746fa --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/WebPImagePlugin.py @@ -0,0 +1,351 @@ +from io import BytesIO + +from . import Image, ImageFile + +try: + from . import _webp + + SUPPORTED = True +except ImportError: + SUPPORTED = False + + +_VALID_WEBP_MODES = {"RGBX": True, "RGBA": True, "RGB": True} + +_VALID_WEBP_LEGACY_MODES = {"RGB": True, "RGBA": True} + +_VP8_MODES_BY_IDENTIFIER = { + b"VP8 ": "RGB", + b"VP8X": "RGBA", + b"VP8L": "RGBA", # lossless +} + + +def _accept(prefix): + is_riff_file_format = prefix[:4] == b"RIFF" + is_webp_file = prefix[8:12] == b"WEBP" + is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER + + if is_riff_file_format and is_webp_file and is_valid_vp8_mode: + if not SUPPORTED: + return ( + "image file could not be identified because WEBP support not installed" + ) + return True + + +class WebPImageFile(ImageFile.ImageFile): + + format = "WEBP" + format_description = "WebP image" + __loaded = 0 + __logical_frame = 0 + + def _open(self): + if not _webp.HAVE_WEBPANIM: + # Legacy mode + data, width, height, self.mode, icc_profile, exif = _webp.WebPDecode( + self.fp.read() + ) + if icc_profile: + self.info["icc_profile"] = icc_profile + if exif: + self.info["exif"] = exif + self._size = width, height + self.fp = BytesIO(data) + self.tile = [("raw", (0, 0) + self.size, 0, self.mode)] + self.n_frames = 1 + self.is_animated = False + return + + # Use the newer AnimDecoder API to parse the (possibly) animated file, + # and access muxed chunks like ICC/EXIF/XMP. + self._decoder = _webp.WebPAnimDecoder(self.fp.read()) + + # Get info from decoder + width, height, loop_count, bgcolor, frame_count, mode = self._decoder.get_info() + self._size = width, height + self.info["loop"] = loop_count + bg_a, bg_r, bg_g, bg_b = ( + (bgcolor >> 24) & 0xFF, + (bgcolor >> 16) & 0xFF, + (bgcolor >> 8) & 0xFF, + bgcolor & 0xFF, + ) + self.info["background"] = (bg_r, bg_g, bg_b, bg_a) + self.n_frames = frame_count + self.is_animated = self.n_frames > 1 + self.mode = "RGB" if mode == "RGBX" else mode + self.rawmode = mode + self.tile = [] + + # Attempt to read ICC / EXIF / XMP chunks from file + icc_profile = self._decoder.get_chunk("ICCP") + exif = self._decoder.get_chunk("EXIF") + xmp = self._decoder.get_chunk("XMP ") + if icc_profile: + self.info["icc_profile"] = icc_profile + if exif: + self.info["exif"] = exif + if xmp: + self.info["xmp"] = xmp + + # Initialize seek state + self._reset(reset=False) + + def _getexif(self): + if "exif" not in self.info: + return None + return dict(self.getexif()) + + def seek(self, frame): + if not self._seek_check(frame): + return + + # Set logical frame to requested position + self.__logical_frame = frame + + def _reset(self, reset=True): + if reset: + self._decoder.reset() + self.__physical_frame = 0 + self.__loaded = -1 + self.__timestamp = 0 + + def _get_next(self): + # Get next frame + ret = self._decoder.get_next() + self.__physical_frame += 1 + + # Check if an error occurred + if ret is None: + self._reset() # Reset just to be safe + self.seek(0) + raise EOFError("failed to decode next frame in WebP file") + + # Compute duration + data, timestamp = ret + duration = timestamp - self.__timestamp + self.__timestamp = timestamp + + # libwebp gives frame end, adjust to start of frame + timestamp -= duration + return data, timestamp, duration + + def _seek(self, frame): + if self.__physical_frame == frame: + return # Nothing to do + if frame < self.__physical_frame: + self._reset() # Rewind to beginning + while self.__physical_frame < frame: + self._get_next() # Advance to the requested frame + + def load(self): + if _webp.HAVE_WEBPANIM: + if self.__loaded != self.__logical_frame: + self._seek(self.__logical_frame) + + # We need to load the image data for this frame + data, timestamp, duration = self._get_next() + self.info["timestamp"] = timestamp + self.info["duration"] = duration + self.__loaded = self.__logical_frame + + # Set tile + if self.fp and self._exclusive_fp: + self.fp.close() + self.fp = BytesIO(data) + self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)] + + return super().load() + + def tell(self): + if not _webp.HAVE_WEBPANIM: + return super().tell() + + return self.__logical_frame + + +def _save_all(im, fp, filename): + encoderinfo = im.encoderinfo.copy() + append_images = list(encoderinfo.get("append_images", [])) + + # If total frame count is 1, then save using the legacy API, which + # will preserve non-alpha modes + total = 0 + for ims in [im] + append_images: + total += getattr(ims, "n_frames", 1) + if total == 1: + _save(im, fp, filename) + return + + background = (0, 0, 0, 0) + if "background" in encoderinfo: + background = encoderinfo["background"] + elif "background" in im.info: + background = im.info["background"] + if isinstance(background, int): + # GifImagePlugin stores a global color table index in + # info["background"]. So it must be converted to an RGBA value + palette = im.getpalette() + if palette: + r, g, b = palette[background * 3 : (background + 1) * 3] + background = (r, g, b, 0) + + duration = im.encoderinfo.get("duration", 0) + loop = im.encoderinfo.get("loop", 0) + minimize_size = im.encoderinfo.get("minimize_size", False) + kmin = im.encoderinfo.get("kmin", None) + kmax = im.encoderinfo.get("kmax", None) + allow_mixed = im.encoderinfo.get("allow_mixed", False) + verbose = False + lossless = im.encoderinfo.get("lossless", False) + quality = im.encoderinfo.get("quality", 80) + method = im.encoderinfo.get("method", 0) + icc_profile = im.encoderinfo.get("icc_profile", "") + exif = im.encoderinfo.get("exif", "") + if isinstance(exif, Image.Exif): + exif = exif.tobytes() + xmp = im.encoderinfo.get("xmp", "") + if allow_mixed: + lossless = False + + # Sensible keyframe defaults are from gif2webp.c script + if kmin is None: + kmin = 9 if lossless else 3 + if kmax is None: + kmax = 17 if lossless else 5 + + # Validate background color + if ( + not isinstance(background, (list, tuple)) + or len(background) != 4 + or not all(v >= 0 and v < 256 for v in background) + ): + raise OSError( + "Background color is not an RGBA tuple clamped to (0-255): %s" + % str(background) + ) + + # Convert to packed uint + bg_r, bg_g, bg_b, bg_a = background + background = (bg_a << 24) | (bg_r << 16) | (bg_g << 8) | (bg_b << 0) + + # Setup the WebP animation encoder + enc = _webp.WebPAnimEncoder( + im.size[0], + im.size[1], + background, + loop, + minimize_size, + kmin, + kmax, + allow_mixed, + verbose, + ) + + # Add each frame + frame_idx = 0 + timestamp = 0 + cur_idx = im.tell() + try: + for ims in [im] + append_images: + # Get # of frames in this image + nfr = getattr(ims, "n_frames", 1) + + for idx in range(nfr): + ims.seek(idx) + ims.load() + + # Make sure image mode is supported + frame = ims + rawmode = ims.mode + if ims.mode not in _VALID_WEBP_MODES: + alpha = ( + "A" in ims.mode + or "a" in ims.mode + or (ims.mode == "P" and "A" in ims.im.getpalettemode()) + ) + rawmode = "RGBA" if alpha else "RGB" + frame = ims.convert(rawmode) + + if rawmode == "RGB": + # For faster conversion, use RGBX + rawmode = "RGBX" + + # Append the frame to the animation encoder + enc.add( + frame.tobytes("raw", rawmode), + timestamp, + frame.size[0], + frame.size[1], + rawmode, + lossless, + quality, + method, + ) + + # Update timestamp and frame index + if isinstance(duration, (list, tuple)): + timestamp += duration[frame_idx] + else: + timestamp += duration + frame_idx += 1 + + finally: + im.seek(cur_idx) + + # Force encoder to flush frames + enc.add(None, timestamp, 0, 0, "", lossless, quality, 0) + + # Get the final output from the encoder + data = enc.assemble(icc_profile, exif, xmp) + if data is None: + raise OSError("cannot write file as WebP (encoder returned None)") + + fp.write(data) + + +def _save(im, fp, filename): + lossless = im.encoderinfo.get("lossless", False) + quality = im.encoderinfo.get("quality", 80) + icc_profile = im.encoderinfo.get("icc_profile", "") + exif = im.encoderinfo.get("exif", "") + if isinstance(exif, Image.Exif): + exif = exif.tobytes() + xmp = im.encoderinfo.get("xmp", "") + method = im.encoderinfo.get("method", 0) + + if im.mode not in _VALID_WEBP_LEGACY_MODES: + alpha = ( + "A" in im.mode + or "a" in im.mode + or (im.mode == "P" and "A" in im.im.getpalettemode()) + ) + im = im.convert("RGBA" if alpha else "RGB") + + data = _webp.WebPEncode( + im.tobytes(), + im.size[0], + im.size[1], + lossless, + float(quality), + im.mode, + icc_profile, + method, + exif, + xmp, + ) + if data is None: + raise OSError("cannot write file as WebP (encoder returned None)") + + fp.write(data) + + +Image.register_open(WebPImageFile.format, WebPImageFile, _accept) +if SUPPORTED: + Image.register_save(WebPImageFile.format, _save) + if _webp.HAVE_WEBPANIM: + Image.register_save_all(WebPImageFile.format, _save_all) + Image.register_extension(WebPImageFile.format, ".webp") + Image.register_mime(WebPImageFile.format, "image/webp") diff --git a/venv/lib/python3.11/site-packages/PIL/WmfImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/WmfImagePlugin.py new file mode 100644 index 00000000..87847a10 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/WmfImagePlugin.py @@ -0,0 +1,178 @@ +# +# The Python Imaging Library +# $Id$ +# +# WMF stub codec +# +# history: +# 1996-12-14 fl Created +# 2004-02-22 fl Turned into a stub driver +# 2004-02-23 fl Added EMF support +# +# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# +# WMF/EMF reference documentation: +# https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-WMF/[MS-WMF].pdf +# http://wvware.sourceforge.net/caolan/index.html +# http://wvware.sourceforge.net/caolan/ora-wmf.html + +from . import Image, ImageFile +from ._binary import i16le as word +from ._binary import i32le as dword +from ._binary import si16le as short +from ._binary import si32le as _long + +_handler = None + + +def register_handler(handler): + """ + Install application-specific WMF image handler. + + :param handler: Handler object. + """ + global _handler + _handler = handler + + +if hasattr(Image.core, "drawwmf"): + # install default handler (windows only) + + class WmfHandler: + def open(self, im): + im.mode = "RGB" + self.bbox = im.info["wmf_bbox"] + + def load(self, im): + im.fp.seek(0) # rewind + return Image.frombytes( + "RGB", + im.size, + Image.core.drawwmf(im.fp.read(), im.size, self.bbox), + "raw", + "BGR", + (im.size[0] * 3 + 3) & -4, + -1, + ) + + register_handler(WmfHandler()) + +# +# -------------------------------------------------------------------- +# Read WMF file + + +def _accept(prefix): + return ( + prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or prefix[:4] == b"\x01\x00\x00\x00" + ) + + +## +# Image plugin for Windows metafiles. + + +class WmfStubImageFile(ImageFile.StubImageFile): + + format = "WMF" + format_description = "Windows Metafile" + + def _open(self): + self._inch = None + + # check placable header + s = self.fp.read(80) + + if s[:6] == b"\xd7\xcd\xc6\x9a\x00\x00": + + # placeable windows metafile + + # get units per inch + self._inch = word(s, 14) + + # get bounding box + x0 = short(s, 6) + y0 = short(s, 8) + x1 = short(s, 10) + y1 = short(s, 12) + + # normalize size to 72 dots per inch + self.info["dpi"] = 72 + size = ( + (x1 - x0) * self.info["dpi"] // self._inch, + (y1 - y0) * self.info["dpi"] // self._inch, + ) + + self.info["wmf_bbox"] = x0, y0, x1, y1 + + # sanity check (standard metafile header) + if s[22:26] != b"\x01\x00\t\x00": + raise SyntaxError("Unsupported WMF file format") + + elif dword(s) == 1 and s[40:44] == b" EMF": + # enhanced metafile + + # get bounding box + x0 = _long(s, 8) + y0 = _long(s, 12) + x1 = _long(s, 16) + y1 = _long(s, 20) + + # get frame (in 0.01 millimeter units) + frame = _long(s, 24), _long(s, 28), _long(s, 32), _long(s, 36) + + size = x1 - x0, y1 - y0 + + # calculate dots per inch from bbox and frame + xdpi = int(2540.0 * (x1 - y0) / (frame[2] - frame[0]) + 0.5) + ydpi = int(2540.0 * (y1 - y0) / (frame[3] - frame[1]) + 0.5) + + self.info["wmf_bbox"] = x0, y0, x1, y1 + + if xdpi == ydpi: + self.info["dpi"] = xdpi + else: + self.info["dpi"] = xdpi, ydpi + + else: + raise SyntaxError("Unsupported file format") + + self.mode = "RGB" + self._size = size + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + def load(self, dpi=None): + if dpi is not None and self._inch is not None: + self.info["dpi"] = int(dpi + 0.5) + x0, y0, x1, y1 = self.info["wmf_bbox"] + self._size = ( + (x1 - x0) * self.info["dpi"] // self._inch, + (y1 - y0) * self.info["dpi"] // self._inch, + ) + super().load() + + +def _save(im, fp, filename): + if _handler is None or not hasattr(_handler, "save"): + raise OSError("WMF save handler not installed") + _handler.save(im, fp, filename) + + +# +# -------------------------------------------------------------------- +# Registry stuff + + +Image.register_open(WmfStubImageFile.format, WmfStubImageFile, _accept) +Image.register_save(WmfStubImageFile.format, _save) + +Image.register_extensions(WmfStubImageFile.format, [".wmf", ".emf"]) diff --git a/venv/lib/python3.11/site-packages/PIL/XVThumbImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/XVThumbImagePlugin.py new file mode 100644 index 00000000..4efedb77 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/XVThumbImagePlugin.py @@ -0,0 +1,78 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XV Thumbnail file handler by Charles E. "Gene" Cash +# (gcash@magicnet.net) +# +# see xvcolor.c and xvbrowse.c in the sources to John Bradley's XV, +# available from ftp://ftp.cis.upenn.edu/pub/xv/ +# +# history: +# 98-08-15 cec created (b/w only) +# 98-12-09 cec added color palette +# 98-12-28 fl added to PIL (with only a few very minor modifications) +# +# To do: +# FIXME: make save work (this requires quantization support) +# + +from . import Image, ImageFile, ImagePalette +from ._binary import o8 + +_MAGIC = b"P7 332" + +# standard color palette for thumbnails (RGB332) +PALETTE = b"" +for r in range(8): + for g in range(8): + for b in range(4): + PALETTE = PALETTE + ( + o8((r * 255) // 7) + o8((g * 255) // 7) + o8((b * 255) // 3) + ) + + +def _accept(prefix): + return prefix[:6] == _MAGIC + + +## +# Image plugin for XV thumbnail images. + + +class XVThumbImageFile(ImageFile.ImageFile): + + format = "XVThumb" + format_description = "XV thumbnail image" + + def _open(self): + + # check magic + if not _accept(self.fp.read(6)): + raise SyntaxError("not an XV thumbnail file") + + # Skip to beginning of next line + self.fp.readline() + + # skip info comments + while True: + s = self.fp.readline() + if not s: + raise SyntaxError("Unexpected EOF reading XV thumbnail file") + if s[0] != 35: # ie. when not a comment: '#' + break + + # parse header line (already read) + s = s.strip().split() + + self.mode = "P" + self._size = int(s[0]), int(s[1]) + + self.palette = ImagePalette.raw("RGB", PALETTE) + + self.tile = [("raw", (0, 0) + self.size, self.fp.tell(), (self.mode, 0, 1))] + + +# -------------------------------------------------------------------- + +Image.register_open(XVThumbImageFile.format, XVThumbImageFile, _accept) diff --git a/venv/lib/python3.11/site-packages/PIL/XbmImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/XbmImagePlugin.py new file mode 100644 index 00000000..644cfb39 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/XbmImagePlugin.py @@ -0,0 +1,94 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XBM File handling +# +# History: +# 1995-09-08 fl Created +# 1996-11-01 fl Added save support +# 1997-07-07 fl Made header parser more tolerant +# 1997-07-22 fl Fixed yet another parser bug +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) +# 2001-05-13 fl Added hotspot handling (based on code from Bernhard Herzog) +# 2004-02-24 fl Allow some whitespace before first #define +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import re + +from . import Image, ImageFile + +# XBM header +xbm_head = re.compile( + br"\s*#define[ \t]+.*_width[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+.*_height[ \t]+(?P[0-9]+)[\r\n]+" + b"(?P" + b"#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" + b")?" + b"[\\000-\\377]*_bits\\[\\]" +) + + +def _accept(prefix): + return prefix.lstrip()[:7] == b"#define" + + +## +# Image plugin for X11 bitmaps. + + +class XbmImageFile(ImageFile.ImageFile): + + format = "XBM" + format_description = "X11 Bitmap" + + def _open(self): + + m = xbm_head.match(self.fp.read(512)) + + if m: + + xsize = int(m.group("width")) + ysize = int(m.group("height")) + + if m.group("hotspot"): + self.info["hotspot"] = (int(m.group("xhot")), int(m.group("yhot"))) + + self.mode = "1" + self._size = xsize, ysize + + self.tile = [("xbm", (0, 0) + self.size, m.end(), None)] + + +def _save(im, fp, filename): + + if im.mode != "1": + raise OSError(f"cannot write mode {im.mode} as XBM") + + fp.write(f"#define im_width {im.size[0]}\n".encode("ascii")) + fp.write(f"#define im_height {im.size[1]}\n".encode("ascii")) + + hotspot = im.encoderinfo.get("hotspot") + if hotspot: + fp.write(f"#define im_x_hot {hotspot[0]}\n".encode("ascii")) + fp.write(f"#define im_y_hot {hotspot[1]}\n".encode("ascii")) + + fp.write(b"static char im_bits[] = {\n") + + ImageFile._save(im, fp, [("xbm", (0, 0) + im.size, 0, None)]) + + fp.write(b"};\n") + + +Image.register_open(XbmImageFile.format, XbmImageFile, _accept) +Image.register_save(XbmImageFile.format, _save) + +Image.register_extension(XbmImageFile.format, ".xbm") + +Image.register_mime(XbmImageFile.format, "image/xbm") diff --git a/venv/lib/python3.11/site-packages/PIL/XpmImagePlugin.py b/venv/lib/python3.11/site-packages/PIL/XpmImagePlugin.py new file mode 100644 index 00000000..ebd65ba5 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/XpmImagePlugin.py @@ -0,0 +1,130 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XPM File handling +# +# History: +# 1996-12-29 fl Created +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) +# +# Copyright (c) Secret Labs AB 1997-2001. +# Copyright (c) Fredrik Lundh 1996-2001. +# +# See the README file for information on usage and redistribution. +# + + +import re + +from . import Image, ImageFile, ImagePalette +from ._binary import o8 + +# XPM header +xpm_head = re.compile(b'"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)') + + +def _accept(prefix): + return prefix[:9] == b"/* XPM */" + + +## +# Image plugin for X11 pixel maps. + + +class XpmImageFile(ImageFile.ImageFile): + + format = "XPM" + format_description = "X11 Pixel Map" + + def _open(self): + + if not _accept(self.fp.read(9)): + raise SyntaxError("not an XPM file") + + # skip forward to next string + while True: + s = self.fp.readline() + if not s: + raise SyntaxError("broken XPM file") + m = xpm_head.match(s) + if m: + break + + self._size = int(m.group(1)), int(m.group(2)) + + pal = int(m.group(3)) + bpp = int(m.group(4)) + + if pal > 256 or bpp != 1: + raise ValueError("cannot read this XPM file") + + # + # load palette description + + palette = [b"\0\0\0"] * 256 + + for i in range(pal): + + s = self.fp.readline() + if s[-2:] == b"\r\n": + s = s[:-2] + elif s[-1:] in b"\r\n": + s = s[:-1] + + c = s[1] + s = s[2:-2].split() + + for i in range(0, len(s), 2): + + if s[i] == b"c": + + # process colour key + rgb = s[i + 1] + if rgb == b"None": + self.info["transparency"] = c + elif rgb[0:1] == b"#": + # FIXME: handle colour names (see ImagePalette.py) + rgb = int(rgb[1:], 16) + palette[c] = ( + o8((rgb >> 16) & 255) + o8((rgb >> 8) & 255) + o8(rgb & 255) + ) + else: + # unknown colour + raise ValueError("cannot read this XPM file") + break + + else: + + # missing colour key + raise ValueError("cannot read this XPM file") + + self.mode = "P" + self.palette = ImagePalette.raw("RGB", b"".join(palette)) + + self.tile = [("raw", (0, 0) + self.size, self.fp.tell(), ("P", 0, 1))] + + def load_read(self, bytes): + + # + # load all image data in one chunk + + xsize, ysize = self.size + + s = [None] * ysize + + for i in range(ysize): + s[i] = self.fp.readline()[1 : xsize + 1].ljust(xsize) + + return b"".join(s) + + +# +# Registry + + +Image.register_open(XpmImageFile.format, XpmImageFile, _accept) + +Image.register_extension(XpmImageFile.format, ".xpm") + +Image.register_mime(XpmImageFile.format, "image/xpm") diff --git a/venv/lib/python3.11/site-packages/PIL/__init__.py b/venv/lib/python3.11/site-packages/PIL/__init__.py new file mode 100644 index 00000000..890ae44f --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/__init__.py @@ -0,0 +1,139 @@ +"""Pillow (Fork of the Python Imaging Library) + +Pillow is the friendly PIL fork by Alex Clark and Contributors. + https://github.com/python-pillow/Pillow/ + +Pillow is forked from PIL 1.1.7. + +PIL is the Python Imaging Library by Fredrik Lundh and Contributors. +Copyright (c) 1999 by Secret Labs AB. + +Use PIL.__version__ for this Pillow version. + +;-) +""" + +import sys +import warnings + +from . import _version + +# VERSION was removed in Pillow 6.0.0. +__version__ = _version.__version__ + + +# PILLOW_VERSION is deprecated and will be removed in a future release. +# Use __version__ instead. +def _raise_version_warning(): + warnings.warn( + "PILLOW_VERSION is deprecated and will be removed in Pillow 9 (2022-01-02). " + "Use __version__ instead.", + DeprecationWarning, + stacklevel=3, + ) + + +if sys.version_info >= (3, 7): + + def __getattr__(name): + if name == "PILLOW_VERSION": + _raise_version_warning() + return __version__ + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") + + +else: + + class _Deprecated_Version(str): + def __str__(self): + _raise_version_warning() + return super().__str__() + + def __getitem__(self, key): + _raise_version_warning() + return super().__getitem__(key) + + def __eq__(self, other): + _raise_version_warning() + return super().__eq__(other) + + def __ne__(self, other): + _raise_version_warning() + return super().__ne__(other) + + def __gt__(self, other): + _raise_version_warning() + return super().__gt__(other) + + def __lt__(self, other): + _raise_version_warning() + return super().__lt__(other) + + def __ge__(self, other): + _raise_version_warning() + return super().__gt__(other) + + def __le__(self, other): + _raise_version_warning() + return super().__lt__(other) + + PILLOW_VERSION = _Deprecated_Version(__version__) + +del _version + + +_plugins = [ + "BlpImagePlugin", + "BmpImagePlugin", + "BufrStubImagePlugin", + "CurImagePlugin", + "DcxImagePlugin", + "DdsImagePlugin", + "EpsImagePlugin", + "FitsStubImagePlugin", + "FliImagePlugin", + "FpxImagePlugin", + "FtexImagePlugin", + "GbrImagePlugin", + "GifImagePlugin", + "GribStubImagePlugin", + "Hdf5StubImagePlugin", + "IcnsImagePlugin", + "IcoImagePlugin", + "ImImagePlugin", + "ImtImagePlugin", + "IptcImagePlugin", + "JpegImagePlugin", + "Jpeg2KImagePlugin", + "McIdasImagePlugin", + "MicImagePlugin", + "MpegImagePlugin", + "MpoImagePlugin", + "MspImagePlugin", + "PalmImagePlugin", + "PcdImagePlugin", + "PcxImagePlugin", + "PdfImagePlugin", + "PixarImagePlugin", + "PngImagePlugin", + "PpmImagePlugin", + "PsdImagePlugin", + "SgiImagePlugin", + "SpiderImagePlugin", + "SunImagePlugin", + "TgaImagePlugin", + "TiffImagePlugin", + "WebPImagePlugin", + "WmfImagePlugin", + "XbmImagePlugin", + "XpmImagePlugin", + "XVThumbImagePlugin", +] + + +class UnidentifiedImageError(OSError): + """ + Raised in :py:meth:`PIL.Image.open` if an image cannot be opened and identified. + """ + + pass diff --git a/venv/lib/python3.11/site-packages/PIL/__main__.py b/venv/lib/python3.11/site-packages/PIL/__main__.py new file mode 100644 index 00000000..a05323f9 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/__main__.py @@ -0,0 +1,3 @@ +from .features import pilinfo + +pilinfo() diff --git a/venv/lib/python3.11/site-packages/PIL/_binary.py b/venv/lib/python3.11/site-packages/PIL/_binary.py new file mode 100644 index 00000000..5564f450 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/_binary.py @@ -0,0 +1,92 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Binary input/output support routines. +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1995-2003 by Fredrik Lundh +# Copyright (c) 2012 by Brian Crowell +# +# See the README file for information on usage and redistribution. +# + + +"""Binary input/output support routines.""" + + +from struct import pack, unpack_from + + +def i8(c): + return c if c.__class__ is int else c[0] + + +def o8(i): + return bytes((i & 255,)) + + +# Input, le = little endian, be = big endian +def i16le(c, o=0): + """ + Converts a 2-bytes (16 bits) string to an unsigned integer. + + :param c: string containing bytes to convert + :param o: offset of bytes to convert in string + """ + return unpack_from("H", c, o)[0] + + +def i32be(c, o=0): + return unpack_from(">I", c, o)[0] + + +# Output, le = little endian, be = big endian +def o16le(i): + return pack("H", i) + + +def o32be(i): + return pack(">I", i) diff --git a/venv/lib/python3.11/site-packages/PIL/_imaging.cpython-311-x86_64-linux-gnu.so b/venv/lib/python3.11/site-packages/PIL/_imaging.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..048e4268a27ec93132e0eab28f3911330e301e60 GIT binary patch literal 1735600 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

nha>79 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

^GFZy$+GT-!n77IQoq93OJ*znbRjj0mC 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

4?-jk#% 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

>8~y>X^O&!A^LV#KU@RqqBC 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%0D&{X1P2J*gv;VF)xZ|*^u{l5>58UAc z1el5<#~vWSPo7JI0|ZV1R~8%~&;s1y0|fp+CMz9#fB zz)S}R2$17mk#Bd0|e%Q@ovCH{AusvZ$Rb%fd>eB z#DZ9jhd~Bu4iI>kNbac+>V`>L@zBxdkJ3&1OVk9Gv02y`W00G&N?|h4#8z8CNAk6^+H^R@m*&^2+fmHrB@Hjw# zy9s$)Er@rpDA8O0`{n?FeFS}MLA(PrNOaDZeEg#>)V-P!fqi=ExWs6wM$5C=@Y|cw zfPPKr$F|O-wQeE&Ud!oDzn-{AA+aUWF|cP+zBxdE73-S=1ST`UR0d#yv9&rJz^Fb0 zc7_84SYW<6K!B#>n*#(MqK3z);YDa*3uVGR9YMHva=530_ZIzGv}~tz3{udH6x&ax z0r_r@Xf+22h}#?>@Bm{o2M9U93U{3 z$O#rXJwR?{QtBQ)KwuSovgH7QVmUw{dluh=t4w=<07Lrb0D(v0_MXH=O7V3#bj0G4 zJbJ~1`00A<=0Rm)->;VG2*MkEDMuQ0s5FisAAV8+l9w4Aw zaC3kFeS!l7W`PL~5Fj(s9w0y_I6#0*aDV`r-~fTqV2*x(0DXc31Q;ebK!D6Ddw>9$ z-~a(K!2tryX>fo5eYV;I1jqyj2#^U55FisAAV4NKK!8kefB>1`00A<=0Rm*u)t2Cf z!T|!K!6iHP00H`;t2GA*kjuv?*Bl_w5{3}P0Rs23vWFBS__;AQq`?6K#0Limun7$g z5Fm5(0|eNT9{m6THj2Rk0#sdT4-lX>9Bg9_5GX=&qBua{Qzolr5t4O@NtQW4Kqt!_ zAizc$9g;afU=zal<^X{|;r4tCN{ z72q+u9>aT|FliiddmO|2<^X|th~3)*f4PY{?$+7S4Sgr-PtMr|v2P9#_>;Q)7y_`# zD1@V0{w0*h2P~gEw&4JQ<^Xu*xTKi_1UxjO_5gu7aR2umAVAH*0Rl_F1P2J*3MM!} zU^C)8`T+v>0`@l!5ZDaZP~~&~HXI<34$jNQCCeTlFcgeEKtSh^`?ui$fvxZe4iI=5 zOu_*IQ5+y}2|QA{gWJ&$5a8xDJV1c4Jt%oybB3w*CeFo&#pjY8P^Hsb$-~j?3!3P_B;-yk>fWWqNr2X$5AW-A*0RlDt zKR7^uDft@*2(T!QJV2m^K0ttXXD^j$4-j|>>bzHRi5Lz2Kez$#D}UQGz;tg!ciUNPG|ymBOem z13Lqha)xRkf^A^#BPIu!22o;a{0r*}K#>?l*%TcQwkIg%9Mw>Q%>*-rn6LX{KTHXB z7uY+98LvtOa}dl{Q0WBalnMl!I+vzX-=81jBRmi`buP`NV8sb^0(KKfrMHKIpP?Xy>nfIrg0#=@!WN!TuuK%30zprZC|D*6)_}bUq|)C& z%5qV4qTp#!@I5>}63;WCy(c^+d7p@9`_Hj|AE;o)J~a3EvEFOS&U^Sce8M|gI93ZC ztZ7lT>^xH~yN#!Mv{FwuRh)=#17y1C0J{_XCjCn*s2H#L3FqSTV7+H%uHL(|jh<=B z)pJO7%v976z;n$hmU;`%T)l;7uHM2keL4Yp3(vOypj+?V2@&`1%+-5$wo6VIKfQNn z`n|xlPrnJ*I&A&TcGcHVQFA@7I~cq-W^;Iy=FcoT2yYq&u3S_v@rP zBSY`kNq2UJ-mjDH{0z3M!?UTfC`0epnFF1r8G66YI&ha|=>0nH1HN5`-mjAhtjy5+ zbv6Q~SH@@z)yJn$XTOZ8D9H)p9+aW?>ue9qP*sQRz1OW2kKihOF)kg0J*N4#U-Gmh zqVvRb-IUq|;lfSyew}^cPWGGV{W>L6zUst1rhi7*&SsBkCQN1M{W^(BR(06eV~=UZ z(3C~Z{B>dFg|MgwZaEJ&y@W1?NT<54OmNBuuqwSdoK1Vc#ROC`AR2qw`?-$U2YM-F zbwcp*{zyNEdd|nC8I>)9BjxagA}N;?27zi^x^)?hfXYd~lOeg*K|;R3kVHu;Qizm` z>b`Oqh6e5280}u`Y4k)br9LINVK-RTqZvQaOH$us^C+O3x@q*yLLH^_rY2rgQBC|M z-60@l=D*a#5Yi2U{jnr?p;lUo0Io_{hCBv zjD}|-D4Q<_`udG1`zQFvC_AqOW#vqM{ufvy4oaR{ruzB06wL-Zg`z5oYE$$!*qwrE z2~h)zT78K{C!l1UDBREI`$KgZ*h?rHrTY5?G@sSrR)Dg9M1$PluSe8#;Gd%G*ka0B zQuY=2e}QVxg;5Rk3q~Sr-LJ5{CaC!-CbO*7y2z%>bDV#%#~5VJG~V~3A3kQaVOSil zyCB0~kiQ6}SZ^@&e(JFh)_2rJcV;FS{G8=QX{P8t^@@l%8Id*tNzr}kwV>>bud$0K zh{d$ui%V_~Viz9}i)p`CLeWd#;9Vgo8RstV_nK1l`5{L=3t}mcwaf@-sc{YK-i0Y8T_wzS`P8N{syzXHUQ z+z)wjmh-D%pP>jxxgYW}c>A^d5#O zl6l}x>Z&zhL<9|gdKvdekcKbdU)Ua2Ki}VKKLCw zYk>TGFgyOlvI&r10_IIP_L6AQu$Ff z82Hf}ZAN&8TCi_=^;#!N)fx_!S0Uz481ormzJl&g7}-QHd9I6|7fcnHhH%scC2ctF zu<9)m^WgBMM^m34>vv{Rl1?{Oeh_FMwGAkCcSnY}iF4B9#(vqM6dT`Z;yVSt z-xy!^p6$hVkW-4W(PG;2l5xpgrO&OH)&#rckU5BCm0|0;&ZIeRuogx7bj|M zpTK9O_F>X8YnTRCk7i5QmUgeyP*fR;HQE(-IV4xeNXp+RA83D_2nmSipjGZv0h z$czVbHypQ-84l(}IG!Oh5RBuy>Sr>&z{~=30Vv!FjECXZH)P7dOae0;l*Rl)uB&$*sn~(X9P+gn=Iv1Mh+`=%dsED(C^`DFl9$ z3bq|-oM~tWkJ{3%3dAtCo~C+fjO(D1rWzPy{(^40@@aMO(H4inINR3U#>fx5+VTFx zFC`04!qUoqhIH)*Nh=$Tx+(&aHnISYJdoCvY*K-q)wo({RjU37tE)cH^%$Hjb}Kl_ z$gsVwgrf^cC7op2d)|1{5b($w?|RdgGp{!r-yp9^PM5=ZeMtM7JFLj$ly}%m)qOH= zGCrTcC+RuEaOlF_Ija{Xx;S?RBm>;(xDLgp?XGhQkMkEDO60h1OLWz@$;ifI z8OJ#Wyv@type4?+-)&xfx}zAl-0sz2j~<0F-0syBCk86Fd&OExXJc`WQmefj4ou=4 zm8|yaP#QnBm~I*67jisuk5^L$BvaKIJ$&%0;F3Xz$HB+FRw`~8bQE&1ai5kKa*%PK z9%OhJT&%V5DmY_|(Hm8!?l;m=E#WlMR&Zu&KslT_+F1c-fp)frvyjeW-aH;3_}}tm zkf9n2_l_q61Qg%1$l%SM1Y_459R)6%S&vSIO9sEuD!A$sqU!z&Y3?GW?Xc3Mw7Yom zptQSqO>Aj*_3{~prQOvl5+_Q#t5>R}be4&8l&bVkX1Y zc9Jf^aLiSoL&iaNk|$3Y>mXVN;vhT8lc$XSX|C!B;vhT8Yr`T8r@QJ~iZIAd^14uT zF+}HpYVe67P8J zgeCN+DYqomp*el(*T!T@_+U0JcEl7&D-#!~9 zZ7|+!-A>ou{sQ#p99UrCRnm*bpY7W;?JteaAVI05w~aqr^CdR?Qj8&vy|+C?_{mu7 zkt2g*l%>?^Eg(`go$F#b(@idE6Uc$>n1H8%?JFWlH3QB8JB=b_ z7AV@%`(W<{MOrt(_k+18O4!vxotkg*Flq%kurDKOpYN*HAZJp!6%7Y_s)#DA=mxO! zK`L?xT07(&q4gPfY!!T<^-r)rizv|Ar@&Rmft*Q!)}O(C1H!v!XubYOtxJ)aMWAYF zeGTkx5d~UHYq_cjR88wUU|$8{JbGyT|-Jdv}P2-fI!u>J`VOl z5RRFL)=v3Hl)#wUt{MuergbIQ`63Fmehv105d|gC1GC>@@)Wf0KT>OpB3Cs5Rns~F>=+RRS|0_wK}3Pp zF?C&a8c0Q6gw`&&J!x4cVP3JT{)A^Wt=%B%2%=>f)B>>Ah$ztdGuUrID)KtCE;>@{ zDfL`67*tK`46v7oDA2kZ>?#ojTHge_7o^fYgw|^5OvYF!1gfSn8*GM%0*%eVHWpE! zaVXe9AUsIULw__|TCK8ADZqS_dC%i4&%|I`TCLe`0b8wE2r>o4R%^D~fUVXuVAoT` zR%^D~lJ{|=`gmXhNvk#6?M&Gg@M}TN+3HF+)*6zY4bYB((2mW~?bzkuMo>~^B~2S* zpax<)W5cIWgQp_a-c>p^6Coc8 zV(l5xZ(#o=qH-%*-xzZeAl|(O^-vR6-2qZLCx5Q1Y#d_eFBCr_h8ay^UZ83*ECYLk zh=Le)f&Hh5f*2+=bJa+Yiad;TpLV3yui^1IsG8Okyz}uuygN)HyMS#YqCo3gV0VL5 z&d&#Qy3f09~ zrtwX%dqosz{1)ujA__DXwschi2rpo0v{|cgyHv^TvhNF>hlhjPrL@T0E>-$=>49(^ zLA+h6^zAYQ>`01uyHx4hnfb4Q!}KEtNMUgg+??V>@^}Px1tBZt`|{-6&(QkF-S!kqxo!9=LoI&ZC#ZMs;0Fm z*ajjBv|b2yqKE>muY%nLQjrvBopGer#_e2H3aX~H6WDel3bc*_d%B1Mtv7;Q2vU)3 zXyxX}G-8w8XW{X*;DZu)9qb+v1zLXt`;&+Qtu5PQh7qLF>X;71XdDUQa8R{$o&)wQ z5d|8r1UpMafyVVW(k{jY`OY7p$-NQQL6>>7ynZ$wXn zeL_Snt>|;GZ-Z$621WZfPTD`5jIyFLI!+Mn--yP69V#LTK-4O*OGQ*+QCq-1K@sfV z1bi9nb0U(|GT@(Jzo!Tp2dX9tuWc7h9D*Xhqn`HE)wO@FPR%07=Ym*QM)VHYH$+r! zMNU^&{Vt*kD{2E#E0BteZ3_D@K0@o6@HhihP3sD<1}|Z!`E}=6?l~9UC*sx#mh5%J-3o>Z?vi(Ji|B8hK*-<>w+N%&X$1_ zCJLzphiO_Tj4g1A1_2;iNNHRus}Q$pt|YZzGA1qXO9|%5J}xo0YObDts6y_}2k}BRRu*Xt_1Omm9m<@IU zNM*f@5=oU3De`oQEM-aI=PXZYU`ixaKeM~XpTqS;;zZ_XwuUie3QwEQn=f!m*6t zWL7_f&sz+DGBTn_FFYcESVl%v2W)|eBqj#z2(~pvNHS0yxt;>HKS*V5gn=t5qpeB0 zjIIjGs39i(W1QDex{RE`EFGmwXEuUQ0ZHj7T{U;t^YLJu;v{LpE_5MG4Bnh{k~( z17cYi5z7KjDxCtKi41_UFrpP;Z=wigVMGssT`MAqi2>gL`wB%U3!r#6{Q~wY5bh>e z%5zc{`#u4W-wAT%-L&48#dEeSVz89wbXhoqVr)~M)1}~yiqSfti7`5(k@FWEndfYM z5@xkQ$oY0HISJeeAeD6-BHt{L&oq$_3?gsV$W+AU>XC0gBJ#~gL=H_svTK$@$>UlgNi7@^unfBwb&d>zoWacbI!A^-Cg@M3|Uxy0dV%gMD5^73x;4YcJS6B5Dnh==uQcyCP}>k=!QVgZ)NC zZ6Oj}DShxl93-ocZr93cfiIwp*0fs7>VvNbQdtvGWplN${_C%$mWyG6l-gWJ)=8OC zo9i?SN^PzwDVEw?M_&DyQk(1a3`*@6}%rFNZ8NIS4?MAQ-@$!T}6l_F{d zk>vD5u)Rf8uIA~0L%|LbQ3XU2@EovbiKsP15^y@$OGVTMt68L;=7POaL~S9GfXl(& zL=o@41sZl2_&X?LsV&s94dB;-RMuTEo+(mlb{R05d-*xb8#FVeHpQt~&DN$kr9r7p zaa!?VR%~sG(_Y-#)}}b!gHpRxm)d5;_$X6?Qk$j|@-*0|M1-dzorf2}ZWj@rigf1p zg54vcay4BC+z0j@5mi7WnLh~j3lX)3NJ{Mwu)k5n@|dYZhWcWZ0h02#Ov}>1M?tFI zKhUhTb@(7t*-|(EgRr)TKWF)CEzGOOwvH@~Y>E)sB)%SR>&Uop1NfC7dF9t0Z^tks z)C=u8=-a%kAC{AV(ByS+Vhm>WAOcpFgkSiaQGqPM45!1)tjc5hfk&JX=EUV*z=>bX$QO;3? z@i-g8GeMX+N^Y%H^cDqJR%29*c4F*{<^eUADsrp}n?{6INvf4j7+-qMHNKZZUf0MML;hg(*iyW6xGohxyx7FjtAhF!S_9ug=09$V zif=BNK&VpbXX8?jOZ8I3Xb(2m0buj;bC&OHX^b{S8*Ra0SEYc&Xj8P&P62-wNQ^dB zjMiOHiEcWojb$(Vwt!%>QEjtBhPdiPkTVtE7-^eb33d)eFwm$rp6(~3O$0^WL`$+3 zD~DJu4YYC0hx{6l*oBeZ34S$5MfM}WdnQ1sHieDwSZ@Ou*^}TOmjFK^KsvI@7@Fvc z-wu!GZ2%*C4g9MNkkqh+E-6-dGX!LbPL?W1xn_2p8|{szrbr>Z*OnaZNT-yBbrp38E#S23&lzn|A=4sL)jPvNR1-PCrGe^kDkdgDAyoFu(muTd?s-a3*Dp)+G+-E}4?rO_F)vH&v>x12O3=W(ENxSh z(_B>u%42)=vaAKF0((A1Y_48z$cE%juq!~&G*!JKDM~pVlOPnav3f+Y1bT*cVk4{31=$IDke|!Dg#K%V$A0T`MRucjNIoyn4_m_+1*NhX7hf1>3Cx zEq@)dJ)$7#K2y6i(pM0eMyi*MVbMkASH@S2G{2EftQcvjVQ8cg*aO4qc4-=nvCR0= zNQ;dx+pGRYP9r_h$R)PF8fn1^O>|_^E`4W;$+k=U?5BPi#;J>GY%_~B^rK)}{l_-O zKo?6dnmYndKOiyC#nOq+0y_~T2D+r0fi4wua7N-)CA?vvOT|EULGU1m#&F=|Ri4Dsp)I%aE#P^IdBkc<@55c|coZ?mAK^46(&oun78BEhjV+bFnN%<*J^`cuJBt3b=A zK~^OSlCCy&Npt)RfoYDq6NXu3Za2PSj^7wY%+V>-5z`!tQTvC@nB%e(<4bewV0@=Q z-o(ghj$@2mVoNZ`6jL%{j!TMkWY!#^O4a{7hL7uLZDqBzZxKG$;Lllp3)%-U$F8*r z*odsI28lU#ZAQ^|U_S$iId-EtVweWw>n_&N;9LyTLF|;fi#hfIcN{3`OcNQ)t0tmh zzV%>-x9@7>E9JG*Fj8Lc8HVLGz=YGcFK^RNj4#V;g7HQ97t}UI{y-%Q&oC^7`PD%7 zc4Seh`b)47rUtWkFdsPlI@3__bCzF%L4g!Y4Jnp+$j@|;6iW>$mbby~1WB=E+hUoj za->+wCgP1C2<4O`rLq~!9TeaO%#oY4*LkjL3d(u8K*xTpj$DoCsBewta%&s_tU(b-_f3%nuVPZ)T)nEgeNPquI^ngMni;YsyP z!dQWCBiP{zTeVIGzmCIvtJ+$S}>%!Lfx5)BZgi-+&|!KJ!pxGA55fKJ#)69Bs%jPZz^6g$(m{ z4IEdKVIG&jaRV9VbqySMl3|{A!tp4mRtGSbAlZGoAn9Gfw73xKu0VcoFn!=Sk<4H) zqu>}pW+a$P;kX#2YH@|<6VPxyHQWwn3B)%LcR!d%;n+mxQ7|vUv7O8_U_OQ8W00!F z6`wny;T3Aw3&y?3Reun-4@?P|Vvzqin6_}VCi4TB0dVvKsajkC`UW&)!;pLr%-Il+ zCoT=lKj4^5rU1+WIIbg80%kKDn?R~3SGMkl2LF6G-i7E*VlD-fJ_XeP^5=l*2}d_F z3&Feq$I~GGT9wwrt~fc`vNQA_nygzZVcG5Ab^JfDf({fu222$k=aT6QW*r=Nkr@W& zJvd$irJe~U^?Y6~;pGZmuIJ@uUhd>&122#B@*FPZOh8$#C#k{rLC8xzS9GRNMY|7@ z*hauHm<-dg4vss>FopZzcny@spg!YIt3pOeOh-pJT9aXV2E);x4AV6ZjxiufpU?D7 z1$P0xna(9}EGEPB-VVn~GEDcwaBL*Q^uGkhvmpE`>{bQ6>~5JAb{7Nn3kGpX%1OSW zA11;I050@QGqT=%!_p84{@UM)?_sO$mFepZ3hL+`41B#$0scN|fb6>AvvXm0;}Oo+ z`xF$77I*SX5s3751@Pf^1-_d!2^0TylU;%6?F!s$3D=t>_>; zwxArCp(S!dZITg5%*%_OtZ11yr8ls}gQo zfG?L-eY0%=6Nb0;@a`uGNLDekZ2@D*91UOQFB=00Y+K-F!|hIl+pSF(wYpT-fC&!Y z3s$+MaAp$21TYPR$mRl+(<~U@B{t zv1pwZP8Jh@pUcX~B=bE&OD%U$X%PpXt}_*t198D`2~hl@g@yKX6vly?EQ`F0F{cTN zuuKj}?VSO3AVn;j1FabAqhRjk?+7h9s{Kegf(3*X8Z0ebi<&OV<-~a>3O-U+JjQ& z^hYLQw?Oq}V5=xPw=YCl6x{`OB`Df&fKDl-c(bvo1UNpE3xh#S3Y|M3oRCJlq_YZ>$!8bHTZK;8bs78R) z0j2N`*;<1s83wjLLuM>CA>Rd4AE8h&hezb9O^!)`)QQWyrm-rAvRY>L)tRV|dg9Mn zUWgu(x6shqtd{wR;0jQLJHrfZ#CV?u`zS>hP}G*9AHjYJLQS0}HPz?}*cAvhb(+*v zKQO&ODfJNObivF3dmboSWK81{NddloKo+o}4Ng%{XA)J$%_fnDkTeejBgDt+G`})c zcuzG?dR%HeRN5~M(Z-7HnZdw|K_;n*nas~;CU+qfkAos7T#OJ!tiHl4G1vzoM{!B* z$zV>Ppi0FhM@zt50g@aQ3g!UVH$k<2g6*_r4xG-C1?RMk3;XDTW*xm$Jr5~fK?Q1q zzrs1yKtU!`ywT?E4QaO9FQE@Lao@!#wep8dabr|@OS?9q1$ zUjd(ycBk;$!2~;n-wo!!Z>R9J(DwiPPT?C7X|PlHCNRNH;g5s)pPj-ZA7`7L!Xsv< z@RaI1g{M^CDLmzW-%jD_S!nh=-d_njg-8Bpr||!4cM4DaH#>!={?AU~*fu7Pj_1Ri z|E8V73y<6>JV$m4uPr-;=jffnGyXew3QzrS*eN`hJB4SZqT9UzeenNm6pk`DSvE^w z^?&0=;Sp>UzJ`@Oq!=0fKVqZsI!I0g8-;(uWVI{;^Y=Fje;8rxM&WG1+$j7Iv>)Cmye!KQzw+_ybyO;r0&PXWiVH)nuVuwJ5|QA-p@dVwr>L)t|@a3 z%1LbsrdSx?**Fo5-b_2$slXgVtf9<2M4dgY!8De+u?&abg~pmlm~1Dz6_}>NX{Q4l)ReO~sojL$qrG#o%sqKR}Z6E0>Z3uKb znTG}bFvi*4W1-dMOQEzTY4d?;28`ZM{ULCAKlS1gWYFp2Fy()p9&wg8$5cu^PNH|I zNdxUX2kZDjS+ZyP-ZZ7I23JK1Gt-3?v>i@0n#zHz@AUV%h!gh3l51dJouS0O!juuGkN9m|eVIOL zYo?DXhiL^#IYA#&mis|=mG5y}H`ayHq{||TX0cJ&!^$%Cw z?!KY>&J8njXYLFG40%w%K~X?a2jeKKfC?feK;jZaL_kGB(G?>G6x2~MyGG1eF)pr( zm@#7xtFIVVQCIzbzf;vcca+`rd!G0If1dBV&s0yHs#E8jirro3^r`>0ee@XaD}QwR zsPf-!ALZ=!(I+I9v)f1C0?6h5*!EG+-)~-ra)tK zVr@O3w*;E36ZhgJ(tlf^nL05CD}?_}j$;I;6+PSm(0k>_K+c6aaR;UTK=Kyr#L>+A zu<{p(bT*s-WB-U1&z0^UBT&*byn6+?NRQP!p_WV#zh*5E0N(q{aSR=96L5VX>K+P9L)l3Fs@du1uSWM(i|Hs zJZX+YCp>BHE>gIk6rKSEu28m|J!$S0=D)#wHZ9jF!$BIFOjUT&oKUqV%}LyzG>q6|`$u`Re^d5Yg%l2{+eDWGy{K(v>fG*`g77H%MMDy#6MIRep1a|EK3<_JV5%@L>% zPnzRgk4~C93_x_!9D(ShIRZ_(2Uq?fb_KU5%`qoBX>JjK=%hIU(MfXzqLbzbL?_J= zz}gL46MvuVVE{IL(j0T5ljc|^I%$qTw1Gq*I%$qTbkZDa8l5!9oK@jTa|EK3<_JV5 z%@K%Bnj^4Y$8q(?<_JV5%@K%Bnj^3=F$WeO{&+!CI$=+mI}BM#o!Ff>!pV|{Tdh55 zj>U?2%(W-YZHa-9!3NTe>}EoMl5^MoDs{OG{;4`^`yCbP$t|!g6R@Gn&Xn> z-0Ogd8TGPK5X8A-kRpQ(q%nZP4Wz@Q1&$}*lVq$U^&@P|lLd%Qn)?=#Ww?RFD*f+mAdzykfpj5& zXang=0DoWu=_a84feoYwf!4Z#lmp}xaH$SAkdmVTg*!;5igpKy?&}i?8u6uD(k}vl zH2|&n(wE4~D$MvY*g-lD8M*&{)Ev3gj6(ymsvWoygrnvzMcnKlUB?(#b%*pSD0Hj~ z`awCjAzCv7F-p*z1!2Y~J4nTF2kArP;6NUw`t<3AMKb-hPClN6oTW$2rTK>ctrp4M zbkE+fG&4Z5w)LpF+Sa4y(o463xr;%mOw$T;qXCL(nqh9#vNckd9t&gqjhaWntNQdQ zTM`hPysqi3Hpekxx-4 zQFEN0&8isgAUzLq&MUZ7`~#O5i^XJE%Pp~b@Ge}S%67?h!hVXDeU{xiGOon9vTS#0 zfTWiFnC756a?;8nDQV+v088imqTE>^RknxZWtz%{d`p6|FSU=*u=`T`n-(?uQpP68 z?n@n1x-WHT>Aut$DO0I=lvdecRis?hw>JS8FF?KK5l>|k1ZdF5O$IPo$~5W^2pEIa zewrOzW-L|*m`$pwf;LDwtclwXZCf_o*$MG25-wj?bcgts*dEERz@CfBUyZBIpN?xR ze#s!_?TF$T3B> zx*7eV&y?LLMe9HOcEv4=Ki#q;c4InXH|oqz7XINVsR9mxjVc4MSzbWwBs~fB15So%ab>m<&bhb3g?f`dY5}W;8Y1@MU zj${(Y-Fa0M=PH1+5IWt9me|l~6|Ud+yA4;Zb-pmFFE-#He!E#m;Y^?cnU6mcUkf8- zcR}gh$D1l@bv7_JJb{fV?@&?&!W9VK2mrSuaw|eG1;F*d^Z$&1R>u5JZYTcka844>TgpwMZWz?nvdegKH$e8Xg7S{71#TD3smygT9pc{SVZ`3 zrYan<71k}h;Y+uEFWq`GxLt%G?SAdJQ1Z)w`F#sGh~z&5;U@%lbqFFkzjPMK z>yT23klOyRO-U}~@3cLx)x#wJQ%HV0lpBa3QqD$5rDy(I>7A%sEBb-_y&?I#QSMFz zA^#*Ik0S{A-oTT_c?BtJ5K>Dv-K|egZ|&CCL67>ZDEYZnws9r{Z6bn5KNpds5JdXZ!a;coQWhYj z#vZ(>#t{Zx4>egh`qD617c#g8C9gsd26rNIJAyE{C1mgfQXWJ|-D#<`?Ob87)0mLK zeTKn6XfdRFKS0U%5QM=uh6LlA_);fPE}5C*+N1`Cif z7a=ualj~0E;!V!`<7D)x!uA-51290g;VnUz>+ZZ2uK>9g1T#X`S;4j12|Qh$zYzG+ zzPAGB76g%Q6(SEIh;#>q(yc|xYJ}81c8+YKedpSy*J>=JN$T1-#-%X$5+%Pt5C*Yx zp)CQ>nKzA_Y?&ja)9hrb0Y)An<=a`)mRb9|?I`^S1iB({zRcHHBV!BFs>|y(mv{Rv z11)_5%J~6}(*FY90=vt7+&PmJc+0Hj9kXlat#RSh8Zc1War0U>W9k*FTxl!Dww!jM z-sNs9ziy#WQqLVwgKZF`1tuV}KY~+p8URnB=AB=RRk7@^CK%OR@|4G;5tib$>XX^%n zK$>xw3ZFt^6HuXKIxoaRQ{i_>EHoAV0>FPV6?UlKiRbEQgjB=0b0=yPFE3t2R#w;Z zG+9|wVb(M<6>h+Te-dvxDHN%!dlhK;P}g%*iK=H9(9|YXJ)~Bz`=CbtvJM0q^ouCy zU!1FiERm{*IRo^bpzmLjZ$wU{>KO|lQuQoFWg}J3GUP<69vTrxs-8;$M5-Rz4M(b; zD^Vs=_3-WPOtoB-c zL1$EZb^Z(FM5>-20Ib&mH6_oK-vp+Ss)u&Nk*bGw!;z^lJ4dN@YrX|Ii{$Fr&)<}KEq zRrP!i$)M_?sW3@w(o~ok$$?7_pUORXu} ze{F+_TLR$J+U%V~x-UfH3>G5h9ot#W7JokbI+5N9kvIbklF*d5amT89T#QdQg-gJZ zYNY2xnrvk2ZR#vO(8N|H7xJV;$B{Cd1_>}WA#%Su*0z1}O%i*yuJDmk) zu>c#4t5s_OOWLY>*kI0FJ{P=R;Lvf37|N+P3b&BL9i;FiC~$?c<*cgbIp)8_d^RoD zDZ@b;noMCS!2$P{p=wnJcgUa06A3#I34lYm9GqTU>+-7oJ!HinX#* z&Z~GTO}$?@l$e|jf;lh3lyOwss_MB2Ipw12X(y_la--_WgsL8vbZR?d`fZcF9dY*_ zT%;ABN5n9enAFj&VSD_{RfdvP*OgG88ZVVYmULcFqxqR6PVDRS$tk)kB~@RP}JKN2;EK07R-D0+FhR zKvSsdF)O%L^)M$=^~?tlsd@-RsvZK7s)sLC!RdJY1xX;lw%B2^E|M5-PFk*bG4 zr0O9Msd`w`NY%rfNYz6iQuPpsR6PVDRS$tk)k7ds^$>_uJp?v-R@FlQw_2;}IS5cv zdsfxMJltxns)rC%J&slNY>t6ITj8tO*;duVg)~z25I<7&a0!i6Jp?wb>fuVdX;lvw z#Yojd(oLbNht!&ta|A6c<~g+xhql7+Qmnr90FEs)Vp&y>5zDH2xG3X>G`a`yj(2WF z8D|Y=(Zb>nh`Tfxj&tr+$1GNo}%%I z+vPL7L%$%23Rz}d+5*cY=5x2Hj#=ofs%JHlooi74WGeduY5l?iTr#S$K$p~CO1VcN z!=ZnM^q?LMKyYa=oVKc-pAipLJttrx`QKCZkaDEzIR~U8RS%5^BUR6x;AtE+q$kSr z4pxU=kZ)2A?Q8#ls^?CijZzN1HKYeOqyV`z7|u^eM;6`m(tQAgnjTX_tLfn#B(fEL z8o(xPg^xnUf3OvH=&&I@+##0=zT>f`?YBiFu3Xv;yE71XS1`sNa!D_s%whtoTv|kn zSt%tDEd(?@?qZ;3LQT(G$Z=^f+%eMhJb=yeKWHnQZPoN-TQxn2QcX{yRMV3v)$}Av zH9d(^O;5sVdML>suoY%gw6zt^8ch#pXS2$Lnx3aX&ZWU{@IEdv7K_QKX5*>)zi)xg zEXsz&Z(r(lOC^*FwKOb#5qkqqAQZ&Fl}6ZA=fQ6QLM2rSJCN!DfQBFxs8QIPH}We0 zorNHJz1lR9egya#lg29ZWWMcEI5j}XjZ^K!o^>pM{Sn}l22Uy6eozNu+{yL>%}V%N zdjy_6@H@rAE1uBGew^&tH+;Jp(F`oln4fya?iBxMcj{TY)0sptyAZWgBrZ-{JhK6T z9Bx2(W&@&KQp{9APfSbC6WZ?6wHW9n-dY@bYst!Lu#L;?qM@%A z#zS8%jEBBj7!Q55Fdq79;q(Z7wd@U=p|6%vh?_NFLi#e`hrU`UrC9+c)xiCvT>z%Y z5uBl~7Sa!Wwafy}D3xsA32WSrn=J-6=S*C(7h^pwE+%)W(CM~9bTV^>5KC%)ys;#h z3j#Tnw9a0hB7rry)S2bOS(?lOL1-+qG`M_R6u`TMO5}{Q7id_pc;t+;7ikq&wWqU} zEs@c|I(uQwly&w(oTN%xXD{TCXK%8BI&us^OS~`QqNR5ZW{f2qlB;Kp<2L|Q&Lrjx zMJ&%Xo>FvTX{w4(!~f(3N#wts<#to~i`{S(*3B%-i*nAzwY&SA* zo-OkjCgimUm4CzjWm8WkjXoa-S0Pls$E5xwn7jZ-S0Je?9Rll}YS|+2ESQ)FyL2}9 z-;n=Tgv8NmXQ}*M%VKH_LSl;AncoAz{|7uKAS5QkZk>XS05A}tj2=674T$pqzG-r>e^Co`h%(qaOq59fYcR*d(5dp_`a8L(M31 zXcRAoFDV37wFr6V*}T*1nRhDE=Q6MQkvXQJ_5(5g?zebX*K<(437nS%Z~0toMivpTHHOwroREC6K(n&P`c5k{{npXm6F0d3^J7I&7)e0 zK3tN%!nPHeao-`tKf<;R)4wc9FP~@VP>2Kq@gYQJf1BPLb4fYu#)>uQ`kovg%C^%~ z+s?X$r8#hW%E0Y|mhOsx0kyddsDlBFKyZt;!}T`GZ7wO!Z3mQed@MokVK#%)@7buVN=>!toWZKwB&-v!(>ybLPZ+-$YkPLDV*j5uFmId{Vj z+5bt-e}VLTRJaD5Hh|7aQ@kqu^8zGx*lr?SCJL#3ED;$>bth(X?#B&Qh?x7J7@`$W1!86SB`8S0L_D!4Y3nb1makaKz84{E9fHN3y0zJSsI3nYu2R zDFsn*B)1ulxF|T1r3#MZfcyr$^B4t3a-gVqb9zTU+H5C!kfQQdVDKffbu&qf@rG3J z5Q$O2k+KSoa(>K`DoYpe(3mXe$1JH#nx9DJ%K0%%Dl2-BqQ1U>GPz(WM6OpJl^Tg$ z9fqgMFz!*okti>|K~|#R@W;onT8M(fpO|A!je^6URB1T!R47X$T13z;OZ*Ag&GA%a7~e)uNnnMaBcOg0E~ho zxFPd8$`}PlaASTQq&5nU;3lDF6db|LXa8 zj^L?sel=ng9Kkc?e6uzRj^NpHev)ex9Ko9Me*oB+`m8gUu9ePGIzAK(w2w)T( z>Gt`(!Nw>!(j5da3XXI~0h;7nmUO*HfQ_W*Fx!fh%;6;PsNhI1-Jj`<2bxC(hyQrhWh8hNE=iKU9U;bu@~GhOpQz#!gY!5p zlF#uj`4))^4*$uj4~X<>h{VC#iexdV;PC&J%^=UIz(p)|JBOIcdlo$X)m43%zZLUY z$(>sBso?ORD(ubteVI=wCx!VjV#5J;1CXB197&{kL>i3LbJ`$Taj4+%pUqrC%qv37 z`y)&ex0qCL_|IkkMojv86PoMWU@GrX!2a6o=S2D{MB)rK?Z9} z5@&!x5}F2+3J!cBwjD8>aLIF}iE=Po1xbVad4SUkcLj{NbenH+8FAgzpi7;gxb@C{Y ze^rd0-F#7R^bz%jZ`2zVRB!m##T=INsNM*+$$o;k^93%_hH*>`Q$vYK4V`^i1*$iK z{<&%(ICZ$>x$d;0Dv#=oU|?YY5w^!gTI|3GNn(8tOqxu&9Z4BiJd& zd2F;B!Oj90?MASR07knJ>?(lKZUnmtV6+>-?s8)@+Kr$&dmgA6?MASN07knJ>?uH# zXg7i(cKN1uBlwf#80|)|mykBvjbLv9jCLa!DuB^$1j7U{+Kphi07knJ>?72Sb|ctV z*2Be`)}+CHl4GU0?H-hm3;66+3Mlez8kklz^ zH-gEMhx;tG8^IKTpxyAO-3b2djKMg_4GX5d#?Gd8BbZ*sRn%xVf*FEuv>UYBz%URxD~ag5xTGc#nFAgCNhbM?WAX95!KMzBa4Q0e3ALE|Lbn*5L@I9ULr-3b1Yq3;ts zC{w!;oZ<`vl*kBns<%lAiKx*5_=LPXw15~}bk)`iuX+KZ8-d1`c~K1&)(^U#@0C$Y6?(*MF8 zEz>#2*BCrjjKe?Y7+gGVl4b|>xD_RwyKpJUCTVtGIvkM=!R$!i#{z7&Nwemg0JBe; z9VN8QMrrl{0nAQmcC-Lyt2BF{0A{Z=dq^cIo6XYf*s@xXHoK+Sab;ZrnC;T+g!Jd& zY4%IA6XgjCOAcz6omA5YXhvm{Ep>05E%k4mou1wSXrq+JJ z6;zpI&(oVvWZ|i}REf$Y`?cN=H))rjPGr9^U!*9P+JS8Nb&SieW3u7rF_rUF_B(@@ zt~{D?+3nA{RGVbOFJxTmO|lyeUeTrE1d#bg#--*2koic)rRoHb`AWv6?j(!PWGGaF z%kN9F;de4F^$~#NJCc4bKa|PhOBuGy0GH;MS$rzPq2E*q1xog3Ef3+%s)8Dn?E5-5 z7BroSxa4UN8DqiN3bb2{ZK=GnEila+tJ!>B5>?Lam1WC$NmML1Qqqg(p`f?@1SA}0 z5O?o`_@v@UB6ef^;;IFVuS2bJPnVIhHyw4#?^?c|?8Ovw$N(hlR`C@RULwVlz&gK2 zHkL>Coq~jNM5Nz$R9;si@0ss_B zOnz*z8YLF0^h*4jg!GW;lSCco^g~ijQRgRJ&GKyR{FD^qctb}8@>5fMfzPLe*bzdk zQmgb;pvDZLmNW1ZA^fZI-X!l?&XGvUjmgjb2bK%{F^L~5ZqAjyjV&@?09UVM1&rK?F_~ps44Pz*(6GAXS}SyfuI&0@Uk!D9A;E z)>VIve#~DXISqO?t9PXUz4Q#M5&7$+dX0J&`{G6^(@%F`Ti+zW0NsUXHw!RGcVT^Q z5uiyINqVIKyXf~C0o*D;v)-^jfV;D7f?;}hmbov*F$%pb`@_4M$oD(XGky^!Z2qaP z$qICA87_q+DpsF=`as0x+pYXFHU9!3r9Y+g&!*T9NqriNt;uoNrS%Tm19(n=oP5=l zUmLJ|Q8^sDor(3@^GNXb+X_+tCy1o|kI`r`|1De{KaMtY{T|@p`Ev$hL&~271_}QH z#FPHLNKg5<_eD|vTcij6uDGWCGmu{9FF|cG{vcel{uoULs{Yd6N z14a4gp+U9(9j-8NegOFSADy?5-6=sa^PcWr39J?fr2K!PEwn!a;==XLSM1l6cH+pd z8VwE&`C5HGfXWH8W~qET?My15Q04Rq(*O2k;?j*jFJZTU2^|0=B>j+QZKiKBk)s`H{X4oQhyao@)o|( zO_Ivbv1;DA7rSE#{bDfBcGr-5%#pn1?vpI%IKp9t+rokD8t5wJu&t^B$TvAZ*kS9q z*HDC=o#0EU*1!4z+QmRgx5(z+&B>Kx|0Cy?p%3!SPJU}ZMRzZXu%}Zs4p6~;|NoACie+N*L`zpB~=twg*yW5b;L8ja= zcQelQ!yIXVQSM{xnu!J)qnv5zlB!#gR-gB!W8G7@8U&^=j7ca$nL$swJhLjFF;LF^ ziS3*UhMyc4FE!tUky%>PA722$#9s9JaZn%jlx8c(G5mu~uvHUun z{7buV0KV2{00Jr*fPhK{AfS=~2&iNL0+In(dE~6aRQ^rfZWx9~gL^6a=3OmoSEDJA!%tFi?}bfq7qOSt)jLZ|Ac6g>J`r*X*t! z%`dflX*bN>g_vJySsq8Z$1?A010Cc}WfOj@bDY#;+y&(DPm?#^-H|lEGtgvr3ql(W zG|kK5~GHfNj0py^b|%XP_4MCXT(jSmr}4 z3oG1799rEC&FkFjSZ;HJd87L(Cm1xgybG;#Zy~g$$-BdSm6%(ZynEb!lzLDsH37Z3 z%DtXa?;gvZj={6q9Y%h8#xfTJ`XoLQ{Dv3+Ur?+D&3z2X1|8oX!*#z{kz=k=$4`gU zqfJ8$(D6YSAqN_0kdFV1mOnU_S_#Z19X|?feTadE>G;9`AMih+*S@ejfM&jvb1$3KFcQ)50KIq*Hr20+scbi9sF zh1Amx&4oHX9+J&4-de3Y=No!avk3XmyltI#g@==k1%E-~fS z>G(FN(WM4@Q^)&*`wCNTy^aqDmCIv^wJ7(2uHE-YB_k&ttKD~kGM_caY6nlo`vb;P zC8E=m9BWdH#s37Vw;MWjv3U6|U>xgkIEF!eEIu1c)4j3IM{|nD;zwef-xuq=2vB1z zej>)t{jttx0vZsD|B5<25bLxIP*W_Q`WpZZi^X3>ogR+W z-3e$^EM5z_SH)@{1~euX{~EG966^dFpvkfLU%~IuSpAEDX5u7~4du9}^O=R?P@eJ} zmA(OS1`9iU;*&=~wxA?tRhr|GEfW@EBdibMl&bn1cclUO&JLF{x0>^?i<2KN_neb4 zrt)1KA420+x{^zEcgG)#yqw$2`$G?>>?lA*x0;v@j(-}UdN;j4pq@_Im4F)DdoiN( zo0)Qr${}xgLE&r@p>|Zx2*fj$Ky?1hSRTX4na$YiAnY8^*z;%*XE9?FAf$6HW7XRs z*1}j1mc53t0-DiT$ykzj_c7KNL(q9#@JMeBVtt3@--|ilBi3b#dOwzu*pcdkn4f^u zMnc9!!i0JGs}A9zxCbE~I}4qQ4Fp8HZ9ppJ(!;=(*%l5afeJtH|bac;^))Cb_us(V`@mt7wcD<$Oz&E*NWk-qI0gn$MB`B<>P-(p=XImma3}il|{5RxDM5cUvXfep-(@HN!9`NBsJPOIj8G}a_ zzr%X~+1|St+U4~fHXxqnri|)nJ034%Gj&C-E8y~(M~zbz`F4CzQ~F0t@{01Z4^ZMd z<&DL1P*LHqSgxs}X5R?CwvNMH>D|!_6-J*lKzUP%UgtCdt)|(n=THI9hwtF$VVi~OH+l5a(^{M5UbroA>DeLlB>Y+wJTjwZB z(lx{P6~tvYV`uD1%HQ8P6iHilM?t+sf zhrBPGJX?A*=F32?9*oqFoJtOoJ;T%(yGj~eRsRk7ADU8Q!hF@07F(4qBz_DN4@N{T zs`^skzvjpr`}rZh>D_K$jf8ESvocC$aWjCcm!QirLMrQJ=5xP;v2f`ATGR`T$3uUCAG?-6-7*f?TXT;F zD2)f;|IM&1xgKE~ZiDFUbdDI0BViRn|G}031%$H@bi#n)=zgdGD9|vTE`{HE=xzAZ zEzdyl&>faH=e6Um(pw<$8bTczp4XS`z5@6KLU(FD&)bps@k?-OH$ojZY0ew^6$sS= ztU_o{q0SeP`T*PvVc_9YmA*iRRWraH5Vk$38`R6k7+Lg1tp9DP!5EkwE=0y`V9Y@1 za29|I5IGlNJ7V>?3Mn@M`YQt7M^CWTVdEc+;t4$>eok||%c{5vv?b5+a+Jw~Igko2 zu<0CK%cJxJJ_VkG^22psXs0boB~VsIC@T2Urn7BVm!wZP*3^W9=3SHS%vtWiOu=Sw zjK#Z%e2{^>k~@Hx&hIH`g)Hk(YFOt!~5*5h@2zTwN!eQ;?> z-Kc`EZRQwco@g^!L!25_Jj7G-3u=!u6o_=KMVkLxq-{!&o@t9TsZ&U4pAw|6+9Gx6 z7a~n5L8`()LVv+PPGs7BRETtP3DS0Lk-9{bE-yhE(-x^IqV!S;(#aOdHbJXQ8%vO` zZ;KSMYpAocitR+9EtQBBl;v2A2Cc(luTRyVf=oLupr;n(HoiUAPkn-3WzCw3ru8xV z26w|Y7BNq#B<3m%>jx0pv%sPZb6)_s2BBe{ExZv^8ZCx#svIuJJ4`0=XZbM#Crv#mq23*Vuf<91?*ZQwHq0?Mrvu`v^7!_kIOJp+W|R6YoxXS@l6`3 zmEq3vpN!Pp6&OV@Qahc*7>AMCYa|AXn#f2kV~y14;y;p>j|{Zp^@6(T%aw4DiB6$ zR{(9(Mrx#19~!9X-GLYBx#jX9B#+KI@CjMQ!i5E-d`nC8CnUKsH(Qu`D+k&)VG z$cc>9z61~%seKC|GE(~|%0xzL-$3ex;&2&8YNQq!sa*>oGE#dBKxCxG&#eA{ky<^< zL`G`uF_Tt>Mrws5?wqjMS0oM4^!y z(N$=qCZd|pj$x#>0BGWH8AfXNk>JC)q)2)Vg4p#aakva4wbz;dPMFW}ZtQ-Dtz2uQ z_A8NG^qvr5H?|^Ka@I(#C-NLxqREYE`w&x!!(|w$?ZJFnqNOPE!L9k$NNo!9XE2`v zoEYZEh^@roGK|#DBGP$88jRE%+aOtStdZJ1#C$Nsd?mspaf@k<)ZQcJ$06oVZ7_|) zWf-X?Frb_?F2a~I*tCO;A#u12BQ<(ScKU`$oB;+&Xc|mwq&AY62jWt}ks333)<})t z%sX?L$FpH0hk%5ac|CQ~I2mtmwP zRIQPk#I2FqujFQp)Iqqo~xKwcE zX+>4ya2aN5BZx2>7iqJ35t79Epvk0+a>j#bEzQ&p4KU;BD+gw3oXL@y8iB}6jX-3k zMj$d%BTz3N;loUgQ#~?MyBR=arbZw#QzOt6nyGQ-H*0IA#+=AZZ4H3POpQQfrbZw# zQzH z{+3~;wkP7wP+U@+dXo`poxL%F%Hf(pGc}%r5t^ypjI_v1jez+GA7*LLt(n?AND9r=NMqAxYJ9yZHB)n~nc5nZa9+fvXwB5FMB9XBYCW)> z^}lDPM#_@aw3%+$O#W@=s=GqtJ`GqtJ`Gc~V`nVQ$eOwDU!rsj>^4qXzOsd;V8)V$D4jY2he zZOqiXHfCyGXr{(?8Q_IxY8?7aRn|<6lc8A^LNm4FLDN};OT}_rVk{VY5bYLYTf%9_ z7MSM7OwF}sYHT^T#7wP{x>tAm2|zSvYHo>{npr|bxYkVVPfQ>p&D3V&e`u!0cx0x=efxzfcozRo zLV8HlnyC#(QZ3EY9%XrJrp9<=ruG7WeA1e!kr>R>f=BS58A2^*;P*oKSGdpWgF>^M zvyntIHQHVMUoca94N^sBY6R*xX{N@U$V`nuWTwXI{U0(@V-rMXY8<05Q)7R))=cex z*i>EftERE3s7=p5BFb-T(f4SA8EytT@%&?wQJH~KJpY)e#fzE2lU4q4Nw3R5EuMc| z((8@Zd_IawPr=E@ojZf{6HX11Dw*zZKzHF@*lToW{C~-I&9$~`=RyKu{#`FJ|G5!z zw6R@tt?gPC+0~&r+C>1WLUS~lDXFT^9F4X`NiQ@<<1TN`3(e8k>P0U!M`K^tdu`3p zszY-$67@oJwCk~fqP)-??O{MkZ#*QWIU4hFUTBW?5}=|NnxiqV-fLry=7r{HW02R& zYh#Y)wJ}HYLUT0Up%;5&`+{F+j^>5tXv|ygwJ}HYeq)ZtwyF-z(bzg(XpXiAROZSH z&Cv)Yz0e$O8lapPnxicNRP;h~vW3bZuCNPv^?~*1H8~2Z6Kf~ zFEmGEGd6pnIU36i^V*oBdA~77I|5x&9h#%DW4+KEjfIuh#vILSV~*yvF-P-$V~(~h zXcaJ(^GD#~NsbM>DAWc%6C%d8<5{LFFeHXst&xsQg3&t@qxg zWH|DSvTX3~V;!dGnm18yV`3%haJUZGEJ`P6Xf@M7u1>7p1y3{v!f7)!XPRZ8v`$?>jaIkPS-wfs0}(nBdjwFw3$xSK+ZD_&0acjBA}(V`*ebaWS5w{ zemE=+l3i+K8K4t1ySmIkgLHz1R@azvO*lwy8>MbFdAsNYO{;Fx#dpzU%{oETsyhuP zT9u|%cNu7!PSCXK9#d|nPSCXKUY+;~HwNh?rJgg{U>Hx=tr( zOto6q#WBlP>I99cp3+Ncv% z;P-;5lfvPhy-=_rU*&MRc*8gcs-56W+*6ddfKbtzJb6c<=kv9W{{hJ4yn5!fbILXX zDtgE51gOsB)hmZ{TWrJw-m*E|?X5HT8{!NWcKE;}PeL5Ol9& zTbRgOogl^JHO6_IiEQ53)l%{`L{ps0Y-F4EDWVRoN(!NkQx~4jPZgkVxlwwg*UNHqi|y)6~l#h zwh|OdFH~ADFh?SC4KBR#{Ws)Ch)nr-Cp5_9(@IaQYLxRo#G{bBM;bh`_#IvqvR!NA zv?t;L-{?mpB3+3mdD=J~4>)6OoOt(-Y@BvL#&ycIHcl)iub3Bw<&!GcP_g~WsQ3<@ zI2XY2A5G}nCWnZ%Nw;FBM`0|bj~RtTbI6U-bOczjr|d+ydKyM!#a;p=^@CVRE6i6| zY5f+__LZERo`?ZoF;aj^y(7zvl+~xGcftIuFdwkh84Id|j6tFvKuHd*pa6}!7YU3L z0t5684FDzt=9T}?ob`ZV*a#cWPz1{ z1wc$L6bz65=)7{eoGQTOR7YlVpsVtO(6adloXnO?lG)Vp5+>d2RB}wykU)}S99c3( zRQ-|D@Io7LiDYrNlesCvkUMdC zN2cHAWS;m<`fW_V)yb?2(^dHqOuE|1{4-3F1;dQ-@{cLOO-`n+9xPcalPRFcBJ7;M zP?mlqw8F{s3vpFFt;?!f#NKo$Qx687F)u6sJF!o5GSfnALm9V5bPTP?@-QRg6a6A0 z$ooS1ZY;CJ$y^YYQT4`cOukr;jBOB1!d^!7>e@Y~u;0tvD9SKu6Aiz;!=4EoUFt^u*C-jjHAp+e}1^kaSdKpU^sn%sj>~W71_TF4g177*DZ*5Sr}Jk z`!6shIAAU`na=!mt|~juW^jZ(WQCSfU|>N8(vF`*&Caw)KcE5MDn)YUuTH44HCCKO zpz(vrjtl2us%(^P6wd7WE>d5c7d8wn;>s3VG!FmW44QLJQye%cC$f?%yWV1T0@fi0 zi*+w6EHbs@L_Ri3M`4ogXfwGEEN_$9sLEE@Os+*MZRU|M&$-#Ar;*ZLZ3MO*u$ED{APyT}4u%9>0G=g74v2vfdyRoOi@ zn@iFtOAs}$S!dI^Bt=6>MiHA;IefTx{)Xw;z}vv?Gwm2DigcXL--1{t-Y<5qWIEaj=AFjjzbtQ*A{-wiboG)HH&Q`?*I_MITBw+SmQC?aRz2EPxJtNwnf8| zCO_S>CB|+CG9J*0OC#*3cxxIkkm)oY;~i>SKDO$zb+Pg7u-DO%lS59CF-be8Y(g{6xcaFtR4pYS>Pyl%kYY!i+xCI1K`&Y($m$p zMBWdqc0lNm=>eSDGM2j$@D&K|JnM}2Edb9k;b`lOx8vn_5k=_u#wlhto`+)moet22 z@663A*@n9UWqSno@S|p{%_ZqLfX5<~eS^E3J4f}Yz68ZiLCVPp7;bMN{X0u#kQ7;l zj3o#{WqV0_8Q`-BWuFL@M%5%_%C3O1GlDy(uNo{V)qo0;vZdNdQaSx_`AOw6n@xp5MgRVHRu z8RT%4iJ4U98b$t#;7)B(c&uOav~~+t4tzXWn_86D&s6dR`Joj zk^fj3$DbgZ66rhk1-!j16CJ1jN2q8(-38FOlDgu0oaqbs_Q+9`mKMM!dSDahJ#55ScO-aJ($Z=LG^ zyugH`Rh7Jl{1d>JOqimohQxad?f zleYr_{yZ#iTh|d~1+p(iz{A^nb_TP_zX5&TvvP(&VFnq)N+tQ>9Z6qr>u^D3*wubz@t^lWyFs z8>EeOBb$ruo%#@E#s7V5Xs{6J;nN9m@P!+nPKG;VkYIbWcH+?*%Z7)zEZ+_n6Y+cO zkXkv#buS~f!v{23Z^*xb6Q9e*T>P)({Eyw8Bi^AbFdPw=T;-w)od2;;GRGsOs=Nwv z6*X~xJGgX9HRpb@hM5mx65N4Mv=0$XTMzhcgycMxBKiM8{3F6gt2ANQ52`pq%^h~w z?g(i)nXTtcLqEuDJ;ct|4HoaQ6!Gw;{iasMk{4>6*2x!Ydum70JnG^fA9a)FQ8&v+ z-K2Tcm6Kp#8~Pj`ZYyRm-8}Cio6o!UgwWcgJp?q%2k~}oPy5Usg|sZo%F{+2?WP>K z_gc-AXK`u&9xi3K@^T|DgK<$E>UED;vJ=*vJm0PwVtL+1;V-8%zV9+XGEdW@&0_L2 zlFL^ei=ByB;eKEhRY%h7i_-Oca~!QYJpd_mub4xMDQ5+}^5a*@EpBuC`y^zPEkRlt z-?+;-xK$-z8O{DM?@VPBK`yHE$SmmH64PXtyTJ6#l`MmwZs~?zic#)cVjWp%J$n8X zgc!xYCDxZ|mDggxBE*Irz_cCjLfTG%cR;|i*{!Bt2LRd^q011;(Q|=ILlABoQC2VK8baVW!UW4B*rsW$5=g^e;%04M2Iy( z$d0jw!AQFe@XZJitCJDy?|>dhP>D0Zq$)P^cTBzvnN*cBsWMFdfuipt2$L$qq}z4a zSw;YpqG7TDi#7d@$$qGs)TFGGNtt1C2#StG5GG}Y$#TF;5x^vCn5+bJ zJ%UOs1QR!wvrIan-u&s7dqXB}DHGQ)c^*ZdLJ%gdVG{fm2Ol7SN!&2$0;oNLO6+(5 zyy)unOHH425f)@p#IvZ>1P=2|pJdP{Kk82G6Ld9u<44_-z0m|hgADnfjmN%Gfc6pc z<@zmuWfvod^cWVjoIZ%3dEpBovT z1^5(#$nd$5;eEjGAe6uH-(=XTLnK2QGJL4l+Y(r*&|@rzfk5IUUf}3%TB^tH||oU|)Vy5%ekD5-0UY2nn3a0G+^u7{$4G;t5Woe+d=i|$3X zI|JSk0c?@2YV*wwE&}}a~=J4hG3ggmqKGJ3&K*rrb z;Vj)~WgLqn{&WjJXAl|B(&cQKyMTTh0$b)R-IXo#G{7eiM8va(n#|Ihu_h+~JQhJ}GD~mC#=Zpb zg$QWu+0xkE7h2L`M;3nEgSKVz&~qy=Z$XgOpJ&n@1AH$6WGh0p@w(9x;ByUsx@9Dm zH<4|;ku7mEUA&M}SK%Hf`n!$`y7 z02JF7fgDB}4s!t>fgl`48V=_HUV;D)qb!HX;LxmBS`K)8lb>$kDVkD;X2anI6uTOM z9GVS>rvN^NARL+vhYtbsEHH4`({k8=-KcH#Ov|Bv#9@BOVOzuDXB7J$fgH9q9NOK2 z1qMMlY->352fP&mI1I8J4nQ4x>qg6AtBAwfA&1^(RUL|AdmxZQZ?mdS2RI2qIP^BF z>ZyQFMgWH`EQbbg=%_cGZTbPzPJX(jDQ#P+qp8E?D0VRdIdn92co^Wl2*RPGslyw9 zUqt|iPL{)RaB%fP%VBcF;j)l}YwGX=ihY5=hY{D*LGLHi$TiZ-G>V&P)N3W2Tp%d_ zZY-4>n=JLd5%nKIJ;$ezjX6#tJV$`+#?G8QJEPq82t7DO^~TLOd-QO3iuzF=@24Vb zl92uwq(9GD(ydGXXl$iAB>lNOKAiyEV-d*Ub9sik4A6xL0U3NQ4^sC7yc?lmiqiih zY#{paSYX~+zr-{>9%tmITkZ?#za#gvmr?9lgdFItk9z<^#V~IkE zYa8Y9kmwCUbU)w^Lm<%`gy?iYham{j8-(a(0MA2E{s4@@%Ns4xZV}NAc(am0a=CpF zW*b~?pM!DL3ef(hf)S@MSaJb82ME$0<3x_1 z0Dg}EImU||xjXS1ivT&siyS)u*pdn3)Oe9&CVO~n1A@r0m%N!g0`Nfu$T3vpcp1QROqi~QiX7^$nEI9pfjlyu2hzk0MJ9$n(SlyCjyPmzQKF{RMzrie6rlrALt^bHNe_R*xeV z(CYDA-Fcu^!?;5)FY)r?Rb(YzUQ*+qCabl$q$cKAQ>A|fW@=Ie54%soOd1bq@ya?V zcj2rSFTBgOzw{`+_%1|-f4(;iXi8sF08n}qnZHb^)r*%Ge_02THeO!*i*uFWV7$Ee zErMpey!e;o8o{L3@TkYl{O_*Yl)tcfxDOG4Q^5Q>=&%tPJPcJY26H;yC z<;7o}{{lJ2%ZvY1`40fr>!1jpJ(!2$4vo`7B~&jTuIk6vDIXcD`nUVHTN;=i0}kGux$(aVef zN_JmBjoPD^7ys3=o`42ukHq|S8JZe3Y44O6px309&Dy&WgV%puxDV4Fy}bBu2sBE2 z^z!1rDbN`0t*r<2mOzuWM=vk_+XBth9=*Ky@5mULr#*Uk@!u;y268Uc9=*KyA4uL} z?a|AN|6%3#5DDvvKP;$Z#Z$Dc3<@#_REl0+f-0e%Owr3rP%V`B2%hq;y6j{*8 z;T&=~`TSyPIwz4#sML%M(P6ZW9!VW;M8!&kDyC*Sy@BS?5m)N*s>vic9TzzZem){{ zv^%~t!1F`uiK@lSKQqkdcyB%WT{*Kcc~ehT-9)6@LL?5>RwPS~US3jv%ks=q_f29B zhIl-H(&j%4Ftxhsd*=U(`K)As;bHUXyrk9}DW+kWNWE*~^zxE=udp@7x4SJa!i5V@ ztO3PZSt;jLm^`N5FC0otP6xr97h%dcsx@9-Qt&5OUY0LIOWzl#9I&5OUQ z0LIOWzncK{;^xKQUG8nh&5PfhJqXl{n-_l%0gRg$e@_9LbPo(9e~4Yd>E^}%ljImT zFaBOa+PHb~_ZDD`egM_>hYDcay!gWeFm7J_;R4LliJm~)N2nP$FaEx=BpNp_{(h2U z+`RZBrHpa&;_olO3UTw|j}pMRdGQYrz_@wwM@vnOn-~8;$uVwT{DTBAZeIL@1u$-2 z{6hpVZeILD1z0a`Ui`5F7&kBeI01~C7ymE;HhOa~f&K9UDDBbBi$787kksDpyb(^8 zJltyO=Ea{P5Zt^tJdMo%v$Ht{f2^$GCa%XXm&m8aFTg93kDLds51|LJdwr_%)S3 z&*3hSU-XZ8mtys;2XL$vD~2Zwf4&uqZeIN3D!3@;l*7}={NtTlk?8O=GJj$52gKc9 zaEWv7RimsK^|DeBgxdxTM!()a@c=;L=EYwm4QSlF_$S%ce1u}2EP!$I;{PS{1qkE( z7Jkj>pW@U}vYdbFViKa87yq=%Yr$2Cn-~A|;wu2~@Jcr?{u#~$pt)q~a-zBH0GHKu zS>9bw(fGve@)_Qto0mj|EVJ&j$d^gX=WbISv(VGcOEM|v<2pQzESbvwKw5OlC#y&` z=2c1krIdRVI61V1P7mtQ00fsOn5D()BE9d=h>KxPdZd2>s$`6f(xnH7rOl@w#@r}f zdT>~}^x&{`>A_*?Ln=wxm>i|YmYoCA#^xwJuIyp}#^@+LA$=!!8mpu9M0wu9>d*`F zO{%#XXvXd+U1}GdE;Wo!Pfy?QIR)+>s?)8X@I-U}AU_sa)xr^~XLys(J931u* za@_ZD=_npq(qHT31Guv#=#nKoI4r@>eZq6Y5_HHC9vhaOr@~XivPUB-Z`8R2y|IL6 zh9&5XB|I`LL0>H4iD3!4VhKI5B;C)GY~5IpkAmV;THftNh}In=zbp< zB=5#5fRLL3uLv`c&<8+gCU6K8r5c9-9Lc09sz&xJjt4LgK`Fi=atq>vnRxe=Er_#` z!r!pvc>chT=PL5o_X`|jT(SjWtq*JegBez03d&jQ!*(2MF=NcJNxS8`WI|BPE<=LP za_!riALi`01ot2-@gOc8%#3lCCVv4Wwi?TPMpfQe>t!(16XnKQuOs3~x4ey&Z;~8| zBu8e)Rgw)zL}QuhsKg>B@RLRlpEUMIj&mAPaB$u<+HNpDGE8)q19JK>yYc~_mLkla z%4|tYryKDCmN^57r!kQc=U`qWyYL{^?2lQb^8T@~B$CoMhovnJO_n|(PP)}PuQ8vwIIc{3~-Hr4_VaIyW~v}0G5 zy4q&2MO5{n;CyS-MMmxbPvrdRmMY#4Dfg|S?DFo3sh`2IY!>|01ED|Q9tdTV)DDDC zLVPyiacWn>FCzXJf}Wyom*L;_UTCrr^qlY8d?NBPCZ@0gpmODL5M$w zrg)}`O)&-F1SY-Bq&g-o1UL^tb>F`Ev0LLSEFaAI5y$6&PRkr_ss&3uew{z~M6S2uT zNs_1n1{4rb83QyRAVG3c!Wc%;F)-?giiin8#W3bHB4Uo19T737QOp_reZHqo-M*;r z{oY&az5n{H|FYNGxA%9SeZoGe&N)@J%SG16`<}um{VX2%$@g5QcI4&Vb`Vc2DB?~x z?eB!wA106)L#MCph^LX}LWmyJ*AAU;0G~=Q+4Qw1-3{;OUN=M!>RY5D?+fS$B{YKj z+0u;z9tpMj#O0c(g77)ZTwR(A!sje&>nv~Y z4%F%j$id7T&1|rqvn&YTE9#iEcM(YYMZ$8gK(ABoDytpLpm+u0nSZW^PK6flyvXp}`)tOFTf1j3XwJgeHegQn`%F`A<({p@o?EzQYpv(j z?b+&3N0%tc#@Rnr8Z$n(5oxx{zL(tVay{#-wKR9iGiYv&OC0vpcgEn~}2*I)|GIxA?f2+;T7Dr%P(_u`{yiLT8i8 zb#eAk;$<`s7avE!CxhmDEIzvG6{8pd-P|iiDwxUED>l(uy~4f7s2p0<5i0w-_lDu6 z?^<|!Gbgu$IHH+cCAXIfTzC)AE+W23Q8O1j-u(@Otob zA!wB(?IdZ;&n=)EA+H4RILuaoB;W&>x1bVb?w^Px?-AepjKu#7;0VAWkbf@VOqf3l zTnxAy<{wb$dZ`h)0p<3u1MEinwbbqabbZt}Z6Q;>6*N(qJOncd(Rj$;23P_!4=R5h zFi$EwU@k`THz@Ono5BiGA6-BuF+26gH1$_RFLySam8O)Ykec?XbyNHa$zFPGRNbq| zsgeJ-u9M^Pc;Jq_?t;8gfEQq%6&MQm80LL}z5xF*st1tQ1>m*hrvgbSlvgcD0YB|P ztspM|>;uzZ;5%|312YmTQRe=1f@gy!LH+{3dYCH&RsimSxeaRjS3qpl^~+0F!~BDY zCsStsQoz3meop!u0Y#7VDi37(+zxtcQ)uTwvYfv=6z2{CMxk;Li}=U zyex1&AomF#a)!K>fVMC#p}b`PZ!SOkg8E4^MUo@=83#HN@`eNE!^{@g7jQbvDNu@Ac$vt$1>nCA=!bMKseK8U2QwWq z<%S`uFI)Q_%NuUK_E6ir7M49^+ zBY7S4Jmg;mD14eSK>iJYePQ|u+zFTfa}-p%72tCe^k$O(j{z=2dZE-_1iT3I6lBWZ z0+plZzYSCR49{3V{w}~lFb6>V6>p<=wRC;ZGcD1t^pmVhyr%hFm{A%9d1Zj(VWtUW z0sabelE5#--vV_}VJExPT$cIgG_^B+t(KY-GovA@ ze6EVrYzd;Ss*r!~5fU|-LSES;f2Nv#%aaFo<>qai-Cu|2D;EEwG4RJYj-<*LYM!Cyy?c9JG^9e*@bPU!mmsMpBKb=`i{vrlbhEW%5my*=CzEnJ&s;o39{*VbEjS-pjq)mwO3y@i*hS-39E!gXmDu1i^XRm#GvQWjp7X5rP& zLJe_i$8~9TT)&5fccz8hm9nr879Q{EI%_RF-m@=P*)}}hYn^Jt<2}2GOVWn3JiBC7 z8_x3V?p|#;%WD!gr=+b@Z8$qrwrw~&tdE4+aJDBGg@!hqpR#a%%EI|+7A|lWYBgdl zJR!}F6Zf$2FKHpCg?xU__w3cr@Tz8#8-JCJv3EaDbDgz!ztG;qD>ZM3?~s(e2d3;jFtj)4Sc-IzXAhNgzNECnJnMtGOp5|* z@#r+W#_VBnombK!>dzBgh^gGKseF-6Wrnmi)_Qpwf7ZrYuZe6_JJyEJ$g3S|y{@Si zYwl%PUZbwLXU}tNi#7KeyB2GmvY>UE1#QwSXd7A(j;uam8I9?Cf>s<)+w;z7Ykga4 zPjPHHN1~}c#rkJiEiG5goV4^ zO$5*T<^!bYbT)RD`7@FkP(QnucqZCPG}DZ;%}yt85&9C;OjWSW9@p*l0^VMrX6n1o zS*@>t??LUfZFt^__J7eg?V)ya$^Ln}akvM#3F>cmDleoqD#e}H%2%%=HU2!KFSuuW z_ZPC5zlZ?uY<_+w+INt5vQ%HGHJLGQyRKCpR9P)1m9162PRQdqmO!TKxWdC8n za-<8TwheF_%qD@?03XA=4VgyogI>Ejw7HLGP3JXvgpFR|Sy;&b63_>xhrn*YaF_$2 z@?@nke^&uBVHO~n1L?206v3mHM>V+~ow)ZdClh(g2=M0ea}KI2A#Vy`6U;_|qXC;? z?iCmXcp2t-fdc>^!MrCh0I(b8dx7qNvRApo19@!#-C;TkGy;r*IT$KYA^ep{7K3I$ z{@(yMz+4NJUILJ->-Ch3veW4)7z)mjbr|I{%A3G-Mh*02+N&XeKw2?4Q2{ z;bel3f&8ZcSHoN?@G{_SnAafw2D}6QG1cE9Zhn{!J?_a!FbZb4KptQ=%rt=ja3;)Y0^d`D>tL=C_!Mw2%-xW$G5#dX zR;b}b*OcM5W<;>+^yO@XiZwlKydrAlH>kaGZx^@5^PVBV+se-u#Qrzr-32Jv&VYry z8v)H=8VOtt=m)cxz$JiVVE!bq3NQy|rodkSD`8F*I03K*X0^a9z>P51K_x1L|1Ttu zg8m8lZv(!9`Ap#7fWp^##2G667O)Hbizu@H6JQe3qaZ(n)0j(O&J!pEY=e0WGL33L zFS;iG!nKs&5Mk~P_GOUY8qgA^xjBlR-;bmZs59h03OF8Sg1|Pw2AH)_>2^RP^xrNe&YOT2 zkUlB3e*?01!ic^B41nngacIC)((Rhi=C0Aq6GM0+!3!W?&ZJhuoG;J-a2?DwP7_ja2UcX;J?~poSmlh@t#Wc8;ocKDUUe4u)f4mk5EXX^pFMzx*ljZ3um~qK)YGE z!~ty<4iDp`Zx*h0!FIDy_4X0;81=nC7y0!S_fUg@M*jL%mepl_y=4s^-hU=;&hsyEmLN}x;h2K$B%jv=nM7iJtnTyOUWMcNf? z3O5BBUDj98KkS}BZ#~{(xgzZjZndn*w;KOUY2070vDvcu@$qI$xm#UU4H;|9QeE1u zkxT21Tv~7B(v*?QQbsOI8M!QFiE`#l_?X> zs5jwE%Q_=3NNN1NUgJW`%E*gSMiyh_XkB%!k)z8}EjijQ)B?5SXuCpFOOCNCGqvQH z%BUsBG>uwvjNQuFmORWV+Lk=bR)=lL!&*s|mJDx3(2{?uH}c5PNUbZZk>gV))YY3X z!LrWC87YmK^%}=pRz}WF8JWPy4!USsBRkX^*`eOZ4k;r$ri|>EGO}aJ$d2_!cB(hB zQ@xR$A|ty+E!oW)S*DA=wX9dF+`a3|-N&-dvLSXSpk6%Ga<&)mA8MGYK|Cv3txKvk zr#e5?X4MVFs-dcFA=FUSouY=S?in>y^#F0Up=#{*#5Poob(L)!s>bgBXrCt51~ah! z1)M>tdu^)*x!AyovL_`6kZkof&Oa_AcMAH}&D0IF))4=PeFVUxDn4IGyw! zPVt*OItJPIaQf*roP9yPA^RH6!FSVgb3xOff<>mr9y44DI16g>6mKng;Zw=4fnS!A z?)W#dZ{~ar{7@o20@~P$TE9gsh^Hppp~PN`xv5|eC0=uf5}(}?9vSa)hZ3^_9!&b} zp+tlK#63uCDmgeutGViY&e7_B>>i{d8Kk)fi78I-TlXL_RYzq^H*7(;lR)GoWwp4OjlE4-Jwo z;2IY>3DM(TDJLNt0VyXTdJ;Y5B;*=Oy&`fFqSR7OLjD9uISJ82`za?OdU))=;3VV) zZn34Dglr?9&5@Ik$4D*ZBt&MXoP>NyNXkjbFMyqqlaO1;H02~jPpqY!gxm*6ISEl? z8831YQVinF<&{lYvprXo%_D8Rx$K;Tl!NM>gs2paQ%*vf5!Nc@B;*KCr<9YBj-Yxc zA@Wx_Amt=PRt`-$2~qCDQ%*vJ>YaoLjY~NR5t@>65+XD!jch8$ZbmDZl&-fDQJdr@tl*87Zv_5g{x{cPlY+yidLpL zS5XVUUTRZyPC_j2oP^w>+?l|kFCX*VZw|pXKjo%l@ZQK$OlsY zgr989JoTz3a85!p_4I5WKh~OdFrkv=I#5mvO-wHh;I|zoAtgK>8aWBkP)<1s5lA@+ z5lA@+5l9_P2&9~ZXsoB4gwz32PC^7yPC^6*L{37&8N8NTWH<>?NXkjbNr04-5P_7F z5P_7F5P_7F5P_7FkUGHcI|)%p%1Magq@08Zq@08Zq@08Zq@0Av(v*`Bg`}K>2&9~Z z2&9~Z2&9~Z2<(iUgb1XZgb1XZgb1XZga~k{bxuO+K*^Mo5QTB6bxuNr8h9pfPC`1< z5#%J~IyLqfU7Kg*Y9cj}lMv~roP=l!O*sh>_u8Js@%tqSSC+ z<(z~xrZ{pEqSpW-Cm~_6d{!7Z2?>kkoP=mn=7Kb~B|*z#cMvCVPC|Zxj|D_YXzbO% zguUL*6oMuuR!%~W0Yy$i>TC@xYfQ)~w&x|i2YkS(7Nnen=)HsnCU8zdnv#H=gy;o> z$Vtd1a!nmgYzMG-bxuO`fY%Nfq?(SNl4(FgeVt#G$BhH^~NZ^*0KgZHWNO! zUYuGeru4E^X1^)TU}87H7SDuJ3G8<;c=0fHI)apwkhclpyN5hon{pDeg-3(`-*6J5 zlKdB(gs3Wh*GY&*XPT3cXGkvg0zcUw^5ZL-Z+o>W&orGjmQ`&>`~GAmn|`~<@oyu< z@1XEzo7>UYp%al`Z~fiTg=+l>P7Q`Z;nBrQ5-rW(jhU}R{d5&T&0M3MJ5ADJ85qdRuUlsgsh|Yz|x^uK*T`jBwt%2|~ zGT1&FbQj=esQEd!g+~^$pTFs`n2-bu21vasm2o?z0aL$=yWG_sv+NV zd%8X?{=@a@f8kW)57(!3ocdD@IaI1Y)ex#b)ex#b)ex#b)sWN3|L9ahVfCjPazN-# zHI&5fo@yu{b*iCmo;uYK`0t)-l#^QOR6`(jsv+>JQ;lFXBh31h`or}pOJaMY{14Zs zEEfK7eQIL=v#w98>Q6P~elf?oKCQN=8abis(}MrkT%Xq1Q;k1dpIQf-sZ$NPe@&fg z2&7Im1X8COa_F2o)lf+4R6`(jsv(d%)euOXY6zrGH3U+p8Um?P4T03DhCuzP#*v`< zQw@dHpK1tks_}>GQ+y_}<^RL=sZKS1zjS?CqJ5zrxUe2f{~NAP zOJ)*Lu21c8NaXso;5V*M8>db+{u{1O3+r8<7XH8F`m`|J^=ZL>&Gl)Mj9>{>BiWoHQ0m&eia|`}`z_2wsJKBIDP)es4`FzESNH8J}}}@Gev)*QoZ1j4tnR z6akgWGpc>VKWBa(rLf~vEoj^4E!pSPonNpf+ znp%~OfU6-Kf^b@@mrZ&sr5N_P4t;Dwe+By7Cx7R=vyjiSaKsb(RjYB&a)j>N+*>Dm ze)uImk*MyGj)q5B-uiU=-4l;S$+%Ss-MPhAquw}vnj(dtaLLips=s&3n-Rrt=ch^Z z)$7wd=4rWb^T%XSK7f3<`G?t$4<`dTaq|ziAScjKk$;2*1@;{sf2;*1_AMR%PZl%? zbad$-U80m52lC|RkI$_n=~jVUx%qY0*IB1PzTEstITw*<&p^)H{A06K9az&Dx*7+3{Qb3* zi5EH(Z3!>V-iMeOg`rQkQSiy2F!brxonr7E>>|E{{WFo9sn_utY?aOga_Qy|w`EA0 z938FtBVx*ri7bU%l6Ho{|c2r(v;d#i=c!B=uRmCO|k~{N`F`ZMnDsaMIi@2lHKo+>a$tIbD)>sRP_zAc>y*wMiyq zmi;k#@(~wXV?T=BA{B1)ZQ(x8t82)?e{ynlE{{a4yPoMsxa2A>cEM`$jJq9B0lJ2|zb(KsPH8I% z9_W-#B=XSv!zxjKoscG9>g3WtAx+-E)xPBC)ypfda`LyCn@xNzKc#iGya|0Z+~#}2 zJZ@(o>d#x#K7*Q7pF6Eg3ahBeKSTDb+o!cs_x%v~yhIv)r}ft8JD18^1lc}!rtNdF zRd+78o$jx;(|tqer_zwu@w3V_eEn4eGGECweC|z;GL-%sSAS5eZ#0abj9qtcSh9=? z#}L^ZM~-Sl+96}4lWUyn!@HrJ#-)Z$##k4s%2?+@m*PQjhLiI$_k+KRB(GGf2AGWb zPDS>?M&Zdrm*4djxPa z#Gs#N2mM^YGzk`)d3MlW1UL`MRI@I~(R6(a@Mb7`iYvr{Dgl2Q)QbvQqjDd?_ds?& zSX`i3j{qNn%8z4QdJD`6g_;zfNAe8BU)F6DYrf@Q0474#q{;S*wFI~b$~Mj<%4q*& z(o|diQwTl@vL+p8tvnm}SBSDdNdCjexw4-VMP73y9oaU@@JhA7#VG$BvW+slwM3hM z8zqtj!w0HSHUsa4uwbOM;1$4g5-c_&tp#5LK8LIYqimyOe2SAgD0`7B#Pp~TSTM#~ zP)=|@WGy(%ibey6L0E7h`S;uDmScO(9c?M4$5aj)`EW4f-8V)C6WdG ztOd6LZ-%g7fVJQ$z#|eYHUq2$9|7KjtObLt1wR3QfU>`Hg&?`G5LmFUEpE;(p8AEX z1^Zdi0l+~J7Q9P@_Oo2%X;I`gEt%k~1?{Z`r=Yw7vKF+r7Mu?}S0Y)^-db=Ka2->d96hv= zvj>y4f&3drOX5SL$ZIq$SPL53$#*)+Qy@D68a7uiawDK&U-h382wMnI_QtmCs{kt{ zSj-dG8UdRC8zEcvrnc<&0Pljb8@saijIyWfwYH!S68r#U%ii3Io&Y`u8Pg{9X8Eh+ zpvC?L%Z(Xm7yGT4n)G**yX1V_JjBNr;Tk6GA?~8ki}Rpx;a?Pbaps@AI2VQ60Q-T3 zqR@+T(>jtV3O}$=6gqGwL=K#bLI=({E1EFjwy`eU_X$6?Ko)Z5yw}IWSt$06*)MTs zXt8f!xqlocmijRn&w%&MSj}(FNhB4oEI9+d>5Kdng`0+=aIa$b4vD>68ZMX_aeg}5 zy+h*AvFWfsM&QG>MAvEzqXQK8FiHoz(BOs$kxOcb;J{k1b!yk0(g2cg@2NO`K z%|qf4TfER8bRu#HZ8r~z5iw=bppN3p(u&?N30$(lXXp}`u~^(daZ0bb${hO&XfjTQ zZ6F<$V6Y;xd9Yu(u$za(j{LtPYaL{i2U<&1&M_GcO6_H}ax$!SL*Z4{!B$A>SB{6Z zPV8ceRT&t21veR0rQ%#>1+cc1F*78{D9h{)?li$vByJ;*PUB5=LYZ}1z>_}eO(tj9 zk(9MzO!1mZIp>UVR9IovVG<1Hr{_upIUll!P8Q`$qv?@SyN7tm;vyx|bcO`2k48{h zat3^czI)tCL2@z_vp-YdSS9hBlcA_(`M1JnXwcgs@ElCNX3VPR^p<9M!47G@%}?{) zdulBfR5hBM_t@Yi8x&-+G|tH5r+F(al2cj7EVQZ_wzxNqL%*V+m-PEd|Ipv+^D%ar zU0HC9v?faHY^th{m=ZM^wrVaX>CD2ii%@NPIzK(vA;?)v0W*v9uSJk?BR|&a zM`2?2CvEhfkFYhL;m&*qCR@8JuT7q9EtAuP8u8l3o+6}UNB99=`-1*))W))K@j5gR zZ?f03*Yi3w5ol03%Xl5LZ9f<~5_7yxMf1_|)_K|bNRZdro|*JC4GuwDWV;VLuFuFL zr<3yQ5)3bL!1>%6@x1X^X! z#C)%Z&1*n*?sz~?3--+(qC)ku1`ahDoV(+cuZeA%Ny8) zL48MohV9{6NK;Z1K5Hdy(@fj)#GAQxwXYwOv5_&9cMRpuO2nmerJ~s%HB-#~Y>;e| ze*U2!n^cBMnIXjt>0~IlnfCXIow<)m%O>B;exF8t)o$9e*H}+5pDNrIO+UsSAtXM^ zlbXKnXoE~I>%U#HWPyxn z^vzqFfa!e!4SHzo&nac;+0W&~u2PJ=BdOJ76I&yfOKH#1XY%HaC%mx@e;`UVRzG?U z+{G*zEcJ6=P@t`_ycQH9v&=@>C8_=S3eIF`Xl1C-c|X$a~6eAuFfgDC9KD) zC%{)rc73^I_R~RgzTn&#%9_e+6n5l40J>6=)-A6c zN7eL6*x4txYR&TED;Cl(!p#4I{yfDwj4snRk#7g{stwDF^IEduNaU(Yo+11(sJLbv z8{IJ8kKlI|Pe)z@5|x@Q^l1Wrubp89KTm-k&Z2@jJ2;VFyyB-EMf+pZls?{5slm}q zSNij>aY}0aLRRUdl)cm}GYvzB?Dm=Z#d=c;4Zv5zjj!hS9hJb+Ob17p7w2q|(VXcb=1$%L`7fiR*??o*6qgI-ux! z*jr)YH1Bmi%>m71{`9xQ5M{J8Eu(o(u8h7*%V>p@DPL z6pA#OkNw6sW1-x$h_pRkqG|gw@CA~6h2HC->?LG7<7K*HXMM|8ji9|XiM<)OQw<*+$(mx`j^}F{;f+c%I7B{5!niLd9K} z`9BI}M}x;mHkUGf9M4x7PX?a=mEOe+)gkeN7RvmUp2~%|UE+%fK0s)j#77Z)4A?5M z)4`I)EN@PKJ&fs@1?;~o6PY&_u6+8#C3CVR**#Wo*<~D(k0I$MqgZxXJ&AK}O33KhV5girS zNqOhyuvUy;>MBcXpQWL0z_$Yu%$dQeXZb*Y|FYB2!so&?4O7_PT~-evggk6K8p(ge zb#u+(Pe(CG%T3~Y{hEu^@G8^G%{(?(o7n(=t-f5va5SET6 zjRIF0D&(k?j`15hxe7Tqln1LerwTbVE%;m)O^Mpe&-k>!O?w3PH2w+~_>Gz*l_4<$ zK3t-D^_P>*NDjuiv*PMj6UdiQAUK@Ng=ci)mt&IX~!J zZStRTDcI?+fu^vhNXKn?`fg|BKOWz?C2ZIk8rJ-IYvwYmS4|_KqfcxyC)24}=x!(>7HTkV4+7*zU zx!(zjW%hmGJ0#Q0{cb3G6a1oNnz`Q#iZpXq|AYs0$j;pF2PHDBGq}BEnz=s+N;NAE z1`mSl%>7|ty|6QLA2fwM>&HR9_MQhn5TCIcAowe_zqP26BYJZs1{pw)iOJ8e-Y|5-A$<>J8yp(*m-*t_#lXj z!dHR4D4Yyh2$kN<X--T*B z=q3=J11M|FfOyuWV_w1X7V*sKY?k7C6EY4H4~F8M01w05E6^NZexWu11&Y#dD3ah% ze)dM(4GN9|%z!ycU@G8lm>Zz#xd4{h%epOx>GMLEb;(>BpzStSCFTnfz1%MtSx!0r zOvr~sd>1NEKyW6KA3)zh!3BUU6XU#+k49exXbjU(U?ZRdOgn*F0DHsq7Pt>^01O{v z$C8r2QjiN>-Z8VAEUxf#mT;NRZ7&mfbrr(Ev;7fj*foh=O7r;Q6UIN)|xRVG|2UV8>VqAr* zn^PuI9tCZN;<-hhJ70m&d{ zeiHZ*Fc8ok3gT_?jQ}%KARq7y%wqx-fKOoF6=()1h{sG86m$f%foUbs2haniE5zk( z#PebQ(OB$)ed^`O;l>}ACO;rep80a9ugm1bP#&zhX=%a~7N&(Lm1QJv{8?_C=$OTR zy+S*%o3@0B7=Nh?R`xfiW#hT7^*xIYeKbM|+C2wL3^MbpT)57IcBO~2HsBlSZiFay zKI1Yiwuu);VxzFX!iY5PgCs&pG(e$s?*jx2V zg)h5_Uy)T7B2t!Hn{tQT8O7C)EPKW?f2gJ`UlMW>xh{a>?*s0GxkKPJzy~mI2|NP` z5;60$z=MDmfW}aP5(|DovIsN?s?KZ{PEtWB%-tC=b1PKc$VwS`ZDDpIc@FYdM1{&^ z<5y*i9IX0aX|%$9EG_t{Fqr=27LOM=Q?=s$#0ADJuBA$6n3FF+KE(9~Eqtek^7OKQoi+|manY3h?lk!tCs+3IS7K@_ zqik+<@)MEgrpcdla%EqUwmJAXl&58%Khy;)IeI`(YJIayiOmd+HaebpLqlrj5<)7I zF&J&QU>e|7nCk_O0lW_LvcRE$Utqo$7y@XL88cN- zf$|KlLDC=87YhCX7zQ&$;GclCFc%9v4!93ylfVmrgRr z{jH4$O&hrxWDM4ff(jI1XOMBAqapvFE|MmJU&5?35#&GNF$+MIm+` zj~1v3x!Gl_P99C5l0Ol}_)KX{a8tucE%~ZT6hc~B``KAQt8C*x;|BGqt+=bx*xKVR z9p$e571CmlkQUK6p$_(oLTqc>P3yR{;JPr_?%QnOvb4bEdjzthz03t_N4v=d+Ud;J zkL^s@rai3&{`G0C^+;3S4f!@Y+j=Qzqcg4Nf;QR#Jsp?^%6K=Kxczcj6Jj4^tvQa% zm9=JAV2}Q-H3tXwh~Ivu{17LXHN!)Bxr#j^EJ#Ad9vRrCB?{#Sox5a)VR$uI$v9Q?Zkl~xL0v$Wr z+}?`iYDb)q32%owl<(b`-Kb_hL+!m6^2u4|{g~Zh+LrksR^Kuo#_S%}l(iu7^|lNx zm_BV8u1PDy23H1kgpE$Fe6LL_+jXvNYTN5$w(1gU+fA_o?Z^{F#JwSuC&%w=ZVI!` zTtPB7#fqhpoXQ6%Zgz#9%91&}Z+jeEz77d8`_dQah7XL zoe;BQ%9PzjHT1QXYl^a#_l;Gg27JF*l`7E=`2J3=b{^p5vV35y+Kza&?Vwmqs%`fP zB&rdc>N&BFcbEj%E!(6soJVl}l^efCgz2^-{L z27lg~n_6)Cq;fZnHBq@|as8YE<*NM6Vog>4)!=iXs#!eh-cYr%6?CVx7Giz#F#8|C zUqBoOwG0bUn-epY5VtQ|hGq;09RL;PJrWjVJi}F2ES>v}@Om=!XT~;1yCPF|GX?W) z$!l9u?<%JsYnrOxSge`KS3b)Gu|~F)RGxUOiCT%#o{_4?j4=P?G-QQ#AUuR-GVFnw zRq&Z^thcy|KJbIDnK~*+iK>b}Z;dXtEyokw^l{*S8yff@uls4LmjVOhW>>RN15CGwwl)-0o^Z2 z-SLZ%^-w+R0>2M&PPW6(*ApRyxiQ|~M>5g8;TJ2-CZGyQW}BV9eGKFPP(R4ecO612 zrPip1%N^tQadH_v#LC^O!d{sVP79s%>(Ih2E>sJ>)bdG3=q5vt>-613ZQtw^G44CsDj5D)cOKOK8{z&=po9QITxj4E!gQf2>PQi<$g+>EKKUv{o3l zHVsg;*p=RBF7sDVpp$)NdKyJ2=MeN{zg%qMa5~&6eyJVwy5gVe+b>-v=S?@K`SzHU zUUs@4UXgV)y3)5tqsb}e3_oiZ6_9x-iJuut+YBR#>p1*V(aXLi^kYx`Y$OL8iA6WaMw4v3lleCFtk2RlqNu`_l z5&7@(!V>BUqg`IO3Yp6L!l85|Mt<()*Gc;%G`?{B5=#4}m%B*v!3XluKF`{g8MAz` zN@^OY{EF!o8TPp(#jx0mq^dw2C52K`_9dg>NNf907SH8V0Jg%*=b57%H*<&Mc|9pFW*R_Q6HI}h~d56j&5_vYC5L(i0A1JMfdv|2ahz; z1)|YJ^O!Z*^nZfRd4Xi@JD{?D9H>%P$nKiPE@-a-iS~t7d$>~YR@;?eqp9X1J9JFj zNo2BefNMc|vHkPbtR%t4Q_RXHY9ViQ`+2CHQLePTQSGlKr86saVe&?_f0U5FmUWlf z$o4JAaqL^6e7(a>+YX?Uqk9nb!{&)F?UP;Fji*gojGEfr8`XHhcqZolVcMqc6zo{q z_&24?L<}0;B<&iH@`$z-M<3PkYJ!$V7DlL2b?d#AhV!A6|oMm z7Am~aU6$OTWq4WYa2ItsU3$^IpKdA~f8Lq~Y;P&w^ct1#dXm@x*-1{JoR>zSQ6*{&>*xa@f#&%aGpOW5tkgbHV z`%6?<7~{EKm<~5m_)#i+bfVNEm7?wklj5kBE=3LQQF|$RH==e>^bs8yA_LqSaf|C8 z-WqMqv-RTMKlq4-R|^Ds{cEC&1p^A%KmDnO5x z1gl`06mu^F3N8hVgy|x14WNBV%(R4pTLAmR>?3eLU>eK>fhPc$z?=n*d>PPf2S4vU zjXC^vdl&Gg10~v)U-)Pk5||6QIdplD)vgQHjbgX_3Msq*#fJiR!F(*RxAxJc%s)`P z6QBmBLZBAV38tMu1He8o{RMIWV`0WX16gAE=Teb=mjXa9`F{-4uLxXrRCTJ1zQ1K zU}^=P23!MkslY3Mp$#}og{t2H*vDZ$gBge9XejszFdb%!K(aaaxnWKaCMd09VC=;wZZ3KSfC0+&+}Gd~b5_!e+1U^oE+sRn%sv{VuE%}vY}XqVeD80Aho%L;8V1{g&hSB+w7_rn2;|tY zziTS(>6_ZFuEzk$m9y*cK^oms3-5zZb(N~^!rq~-)iVC zyftxyTIp~l*<*78f7v5r)g?;r{&^56Lt`LIyowOqieLDP`?)9X_6&*3{B%D z-#dkC1GfTA%^XtK-@5n0xZfv2JftIgknruO=tCU?3CRfG@yZC_*J_{42;bkzaIbfT z?{8&Z*H9Zh{xRJmBYXoaBYZcEG+%6nFnlE|I0^NjvSc3WjmzKS zH*XFzRw+w4*vYWvV!gaN*b1?J-gww5u`$!Q@+;{rF!8EVamF-JK;Y4=G9YF;2Jy1Y z*5C@g&~qcQ3cb*yoGSD}&y_GH7{=Nt-Y}+k!5QP2%L=Oo!&i3ar`bvbtr{mUfzQ^9 zwN3K5xdOjWUvjYtB<))v%`Bgjec5|gjzInm>@C~wi!MDg_U2Yni;6t`2 ziK2X!tWqfldN(LhT%__;UN3>(4N8=jJO&?snx9$)>5VSm{#*mc?8tARcY_jT`Cr1v zzvHKN20?{>x~6Z{1HBuRC@-iXEZ&r#lD+rTx|7(xN@xPT8HqDw`X7?ixTVtiGj&xqq%s`mymd-}OYMS-TX-a5!^?b|r>B(|ERv&XTd z_A->fxt}2BkJ-!qd26)P;`FAw*G8N-x{Dqp^SXOor0$K%pJVp&Y!B(e{MbozWL#aa zR4+4h4ijDB5{;)4{Vq(jv)4%-!y9c%caCFy;$>Zl$}FcIq|{r&Qik?k$4LE1dPTJt zm${$)o5c8=Z+}T`&8MgrOd-6b)c2)Ky`citeYMw1-D)0qCe&3+eXrUZsz&@fg1`tP_vtwfmg3S<~ZX0@eYLgd1+a#H48hJ^T-~-S*P)@>?{ACKjq-pQX*k_Nb_i`bT zbcBweObfku(!ks4EO0X&77U}Ru%JO$=|7V8w@|@^V-fXMFDT!H_`x>hg*V{dQ0WU0vAMQa)p}4JQbT_er%?Gb`}Yr3dN?H zAG>H?xDBvLg8Amh-s*a<173s@GtE!7U48-VmSBLeHQ`A2m_c+(lA1 zLoxN|kJ@W0c?0;WMA}_`)JHSQFTii1?l&@&<1R&W4R@KnkF%585jLxdnMP3ZeLzo` zZUWl?hrsL)?e#Dqc^^O1K$D;}kG>_O%CHXusTy()bRiX39EI2?m$T|z$Wc^5r!dqTV9NjE#AlLz7(rFm z@ZNE(x@~QC`Y3GRCne|4TcaAGHXrDw+I)}l?0{@-Zt1Gp%xOsDK@A4cBoB6q2;N>Q8$Fo|t2@u62Q`!8u{5 zH^4M{Hj1;%tW3o*S=*gh6?=KTyzDzCKY&zB>$%j&TFrq?Q7f8a*0xrq7O88l?XJo! zQkPu2kGf>>Xel2k?cyV_ckLLld>-vGTYtGT7nsXzz2(j`>ui1HPBoX?dTOKkx#CLH zP3t2`WwBzaWMsLxgR(5v(evWos&<>t;(b#2EIwFl5tHZQ37R`?VoQ`5d7qHV`-Cv> zspdqRrD=Nr8BOiS1EicuXS?WN>0&;?KfnJ{v&>g5^WV^HA+M2FfM z`Zw?ch^{-z+7xfhbSuG3PGME;BM}US5;Kw5YsQ~JOCehmV{EMRf#*U!)nB@P5Ej+@ zo2I1+SzoaMNbXC(Ml`O3l82;&v4BUBZc#`tz&kKI1UdoyCT!~ft&~{iL?lg+G=ef` z0(!%A5?BbB2y?W+p8?BYmOwduTyf5!7#eWP8EQ+M?8y;6DWxd|OH{8G zw2jxd@JdpM_C}v8IsUvgTS%tCe5TjF&82W2_MHXACQhAW`gWXwXcO>isQ06sTzUQS z6(cid#)gYDrmX(Cin$Gyr=YB%u8^GKm|JZj$H!LunI=m7ITY+}Cf_vF zTd*B+G)8Z7*3Ushf8H8d9Gh~i>CjAjkN3&v4M=^nLl^Bp@|$uAhZ0jv$9yHx9ncZV zdfcU@$!q|rs5X|W0P@ZaeW=CAT4dxa}+ox1E0I ze}u4C6c%nZNk?1F_}S!9?1vjpKiuVJUOtY>DlEH!ppr}ZDP4!4fmfCMHz3QaV?~@@ zMWyk!I($vxK-gr~Q<&D!@;PPHtJkPN>IEiKjtXMM`QL%!yZI?UmY*`>$L!BaP)t82 zzEM9z`NF~jiedniFWU((~FNMT5YwkQExl#`-p z?6Fy;o|{oXHGj5?rsZ#}{Z(&~G{A;)TgrrE=mv*{ePdOYf=&f2hmx_+!kCRo_H~G+ z_J;Dtx)ZCAKZfF17JVP9GiT%HchziF0AbFjC8FOh6knT0nVafPOH&p(<%rw?i@lGz?0f z<`Pg+Mr6%(>RoG1&P`4?syiO~W9+t$ zN@*uLlC^uD487`qcvvlNQ2iuz{=79Zj%qo)sjBlhN;(b-=9t6Vs!~@2&W5OzBW#a; z0&u?s)6EgZ14-d~z?Tv%Fk`b+k43dq22^vjYXbdq&6~d@)hubNE7S1f@u+mnQLTIO z8`5YButp_n{EZE2r>vU%EWVS?#Wa=w0k2IWnkGt7(4V(P)rUoGnkarNO41l=cD=J) z6;^v&8lY(5E~&Xj)uD|!df;tq9u3#D^w=33OxtN`L((R1hPzJNnVC+14SL~WDV&Om z-Rz_>njKC{6)Jc4WOg_=WruSU=R=yCvctJ)uW)WEUeSax+@?$svcq|*_@qKa?W=5h z;haT?TqlvC=Byqc564t|9^FEuygT{nIgOtuq0H2e$=e3*Rje~K+#TskP<#pr$E!+C zg>PA2^neR{e@sA>~$`Ch*hrE?E0;S8b?A*}Uet zJBX2_Y+eh?7Em^?rDYpXHm{Xs8=K_oYpeHI}|oG*E!fyySaMDiBma}uAzx3~HQiD`B4woOYCXNzwlod$_d#J3h-otR2G z9jt!i#B2_-J5)+uo5-02-%Na~#2nJ>VEc5t#4JReid1i%5)(+LbAgK8(-b!&A+Jkt zzK6KoKtQuJf6xS<_$9rWrBHIt#7uvgq^t@SKzKX^> zjTtXeeRI4#)%+~NkA*TWKsGm&T@PL@*RjzLaBrDC>r-6_oK6aTj>D zac6-iN>)OjSQyGSfiIVAHt8)2W$%HvL)APhyV%Q<2~AqExQD8znk8Q5GRisuG#<*x zCxa6_+mjZ6r%Uz%1v$ZMqTY1{_+rUwtgM4%4}kBN>|!!K(QBYp<}={OpuVdQqe6c2 zyn#Q*P-S^@N&^Pb4WtBN!E8<7|jBL&ABq&z_v7eC~ z4LTBvWiWBif|)6x#pOvbD+DS4=fnIB%5MgU%*rxlx(HFNz!jX`RQt$iJ>;hvl<^0`XQ9H z!zE%H_PsQ)XTwGz`2Tb|QNw=Y1=mLTeocyBLbhu~iotK9*b@AL-fop(S6ZgYZ__}k zVQuG*LhyV4s}g+c1?{xgQ|HcX%S;E^5=4r@Z=zV;V)P23EaPTBTL;NB5OxEa*_I#* z!PmXhDRVvGJ1@8?DnSh?Rz)R<6ocPHu_b7YUQ3mrEG^Sy!!$q%cqfIszpe~}U6^Mg zE@}tk2VFSYvgvjw&8|@yB1O)TT;wQ<)$Na7UzOpIv}}`yrGY(bAqwH8f2R}Gg69Xf zM(x+TRzTKg%o)5fZs2|jZdt#u{5 zBW%TXRtWZUGi(N9Xm^_Y7$<+e$e1QM;rNQ)j@Nk;m)+o@G@V{fXDRZx)6yJ~ip$Mr zH!JPO2wxh?ZR2Uw+Nap(lA-=(?Wu1wpN#Yp+0)n z67s=nHkstJBhOJaCRo}PqW(y}*O zf?PK)`%$LjLtP$6S+3X4)IzP&8gyk8OgXroq=nd~{!Se}Py2;Dtr7FID6~r>dmPeE zuVNhO{X$5IR+2}0jV!Mfi#Q#r`cc9Tce7Pe?`RJUPEb2qL&{7#aCF_ULDXp1-wkM7z@ST-Z1H13BFXaGpybu z>1_eu3uVa1TAy(7_y+hjC@6M=LRJ57te_U-bIk(NR9%k)yr7FySXRldj#8;Bg!hNk ze)grs)AVCZi8~AVArz`f$oRwdNxWRZk{1LeLee7hnRT~ z%2j|}&prcs3bO0j_hH@@(0cY4nB4+e&z5)O$_Cl>Y?OYsd52`&dbT5>?IF9K9RjnT zfY!6ez#Ju@_3T2J`2t$co&$3h11uZ1#BBcXO-e|{PK7-SpZJeMku zlVM-GumMi*5;K2+YyuzT{J>t^b>#IT1LDknHtUl9m9U#y1 z&iy7#?E6%MXkNDslIJzmxO|E1o`vE%j>z-6XqNmF;)x0u{s2_55!n4OpR3ytQWos3+HjJ#(Lc=oJZO0=sE)R z!ywzZ6JW+exze(YJ0CO+vW*+1!p@uU^u{gi88gLDt^#c1HUiZ^wsCvFbQe(L9s)B= zK#e;YW)fr@H%dR#^d%YBxXTG$3facJ5at2_HSUctn*`LjkH9=6pvHXz=5@&a1yQmZ zTYtlD&BNicUL$O!>+*JlH5^DYJXWOE8uw)wpR*48hi_39U1jPWk9E=b`j!mdhst#F z^|-w>_3yiik`k6Pqt#2$y3xW7w*1Y zN%z|5O{1)lFrF&6hF9b9W}{ukVR4<=*jFn$?A>>cD(WIHI^;hZvHrX@hcG#nX*#{A zqjW0k$-rlKQocIjKrh znQ6EKqk4GaJGu6*zf+~|fk*TnC2H0>s&y=cNBOoZuPI7s)E zNJaORjPAiJul3|rZS#7^<#n#xj$O=oo&I)`clrlE+{57ROMg7gZJ+H9Q+H2<2UCgX z9`|lYd&ApL{J96n3DMm#_rQ3gWMEaof(4hN-Z=QHB85ZBJ&$g`))Cvg<$Z(%TlfjT z^cOocc?zS#lLzfFS#cWIc>2(v`&NgypgDIaGSwk-JbmWReW}BHVl+1&th~f4f_LBP z@U&4mx*~-%w{_1iY!FTfXfxk&PYe}=Gr|KB8q`s4 z+1I=`3||+mgb!xI2esmeW&$PGLIsZB3^YLpSoRn}s(9Yy47kcI z2OB=KH!q46My(7#<#%c%HLap&6;?~6hDzxx>1tZG{8_Ex~-)>3#z z3RawZ7i}B6y+B3WB`I5Xo{EotOzz|K=GaFzR-C4>;ikfR{8vhC`f>H}`gn@}>jB`c z(KA17r_Mm3N(!w9@lq31rFymZ`_2PRmSlDf=L5?2uzlFQLV0;D*~;cB+B?8!q3lJb zpKXkT_T|kiD0{K#m!nAE174M2Dz2a;ID0?V7Es<#JeS_z>f{b)dni#?4h;I+C0Uap ztlSZ|+Xs=i)O**XGWg5iO6xoipRF&D&dgNnRu1I`8C1}Y;XcKLiX0}RIaA*JlLsL0 zhC(MOIS6nd%sv9W0Vl#Nf(jMEmrKYCKxab!SimDN_XyMh%>FU+jlgk$?tr$CuUAi| z!c2mSmH_-d&2R#X4|Bgg+)>)_~KSzEM3Ku}h4*}Q1Y!G-8@CeLRE($Z}rYw(;*b<@qHcrR421kfrL8Qc=h zIXYHLu15ZU*n1B!tBRxzxci)QbDTR77+{DqgM=AIf&raD6fgk_h>8-FAUQ}BfyDqQ zC@7-h5X@pkToh3eF@Z`@6fk4JfV$!u*6jbj)#qLgimv@nzxR3S-mdEE>QvpQ zPu=S7ZD7~}2+jlY9s+L@nFZtr1ik@eF@d!Vl)U4x{s&mM0XYVNLy4>aavlQXh^z)u zjldN|)&Y42fu{he4L}a3q>Tu?3d+j>R`_gJML8ENM6pm|S>!gh_BF))hQ41zeB;Jv z8bXZY;VM^MI3fICk~#CIPxJeA_Y{ns3!Ev_x-^=i>X#RWeymxD<1XCn&jf#M6ph(W z#Wzf&NTtG;y78RAT4mx@&Jvf-DI{mLi*UXPf8t3o$ddwgc~)3%K~>I;2;~-Il(uDv zIvMJx+Opq5BUD?C_|Q=EYoFu(4sm~nxxd5R--$*>o_($fK=APp+=fCpwjsC;M*sv1 z4T9wtg3a#`_jj24JKX)n{SlNw!4iPrLm;>{9}8@D2yV@p1cF<;C6-?ZHov%%f^>VB z`#aqIooHlGun5CUnO+5<%brhSR>p$l$SaVcEJ#!cQt&DBn@n6MQndl?kapC~*80-_9;1S2ykYyc`0jAn_j z@Sp+bs$eYo7KMe!5{XAY0g?=#J@PCSObA=Qe#mx;XrRRC|R^76ExEwHX6o zwXP55@Y>k3$Y-5y22yx^*5i=2K{sQXC#Bk3^}_l4iB_O}U7)q$7u`Pcoy~MF#cLz3MEhQVEh4-q!9rkf z2sTRl#kcPTGv@}u} zS&Rad=sj2xMrvo}Bbze)Hkvn5N07F9H`**xSCC5G3h|L5DM2UYTm#jPvt$kjf!!AU z5UFQ#3^^Qpp@PtG4k8&)p%HPiD?J%K5*lfWikS#i42`mzfz2*OfkIE@4W_{1`0`Wq z6oAEmvOC5R|76}QAkJKTNj&vk;$5b5q4}Yw^6nzj$}|&|bsv+E_z0t}PLG@KLaf%C;AZ|`wJ_=<3*bEP~98*%8RWD(;U zF^+ScTh@BetI+n`!x>kFFKHysL?#C+pR#{|j)r#RvSV#JzJzurhBLt!CSZecw%V7# z6@3=;Ec9}M4Q5Zkb2;=%E>*`aV3x1u-a`q0rG%#-fiskw?s3e)q1SS^F#bixvuQa` znH;2{nW?hZu!mnaQr}@mzG1?h@p>G3GnYDMXED6XlwueLUg#|o{vJa=^mcA5=(odn z63K-Vk5z(X`?6B@4ouIXcXIoXlfywcCuTTBk81ejH8*sV6<>sy*m86nu7SYNoEDn_ zbUM;X`t53@#^#FDYYV1Pqgenxja9DklY@wTg7OT_}!uP4h7wZlB~(RytJz(vva*{}!uJ4Begin?!ah4u}ehGJ%n0fkg#`Kgl*XSsEDSV0`%XCd>Tez1X zx9O&A{8I&4p`&b&)1;=Wbpuo}e7eM}(-*R)y#?8z%lIHWLy)a{c@dB^1=*?JXB+ep zWS90+KtDk~&`T(~zaV?{5?1>xLB95l#{dqWEeIa94cQF?r4B*u>&T`bEOB_$zRQ}Q zBUph}_QiZ^4Y3b`B80kzhuzH1?n%kRW1L8p&Sfu-5dNUvMNuPDoI;b>mjPsyAO-ps zw#R5`!y^42W5(oiCN0q&AUZr&V#+l4goV#da#C!o$5GmN5nZWi&I(TuX}H*6G#|SY z?V2b~s31J~Ef%YsjdY${EQ@QHDQ>Z9u@dKJb5c$!`?O+UQ|)^}v^R4UO)vNcVdqDD z`8oD@|CrHwnJI`Whrv)$c*d!~^g~Q`fi$4fQTsYr>;Q+= zb(r4S#iDV=?QjimZ$&o;vSpfeHX&XHG1uLuI+uf6*}Nhn7|h)OvV99G5)382p{yU7 zpb8xM7+0S9ODbm^ljfsB(Zh>Nz~D5)S5$5)j-LDj!eS=)2B(W;UVFei?$tl{YP0w=oQBp{}SSGzTweHK_3 zMfd~>_-G1^R!5%&5_EjDg+>?4A}r~kv!f@DLqwRi&}bJap0R~S)5cKy5%gZPo6THO z{ZSXzwD3>_U<_S|urY=%V~9QEP+o*(whGZe%DEjuIjfB&pkuQbLucVP@nRc@zEU6b zI%1r+@l~feTBd9C?>hJ}rmq11MEYDyVHFM`NI;9p-u&k4|${k1^~0yJiSZO%R{4D>sUNIr{R6qwnPNNpkw0~H6s+^BO|l9Iq7 zt(E%L2q+7@G=xZkYzGHs9?flCQ8q=}0B>6VUdxP8nH+ew`a#PYxsyuNDM4M|(aDVv z=WNDTlhpE~njC*XqelMkG-|}Cysl9L8?ZSV z0&ikkgUy3~wFJ!{Lk0ieH)@2uMvaiys1f>oMvWT(0i#CpPou`4Mh!f%{+LFMuxr!^ zd5sz&uTdlPe`nN)aXoinqel2oqsE^`jX#YV{|Tc8Hnelqls0N)iBY4L7&WqtQ6v6G zGint6Y1F{l_5U|Ujqo4Ss1g3(GirqYQ$~&OpGFN%c1iK+`Xd`PlK%mtM%XoKg#JaN zhS(Ca zdSF)qj9H?lm?gFWe;&|aE(}0RkViurJ#+){C3pi7uK-k|mO*2LSP70?xWG(-r{8fd zjU~&)*1a+~`K8Yih85*2*rsUF7_krrEwN~{b}bt7+=N^Dp-}jIdjf8?H$JO%jTIwd zd`0m?Pt=5aRLU9PO&A~5qMi~KU(tjyU?7GDjD#^@)C)$0#du#trjy-xi6(;1vo&F4 zp{4Zj;@c4sxd&hM#ZYRU92~9ja5YY%dl6J&<0RS&!C=@pi834w8z)hQlVRf|%5YBD zIEgY`5T*(8%p8g=3L7U;n2JzoW9Po4s_eUq3 zBgbZR4I3v>%I_XFPNMYQ>80{$8m%gsg3Ka+!B+z_iC7DQ>w!s?t`3_ju~>^DG}s3U zjF0G{2nU@4<0C3{a#X{lbz*2Y(l+80Vs(*Lgpst0KBD9asyv!SyV=a32HTspYup^a zeifmMZKOZKkpx3b5TUeU7B_n-P!*|xU=8vx{}LvIe^z7ye)AThHg6=RB3N@L_FH z&+syVYBCGi>fSyr{Qwb*%`W|FRPG^{J{75WA(M?=o#*5GBv>PD z+-QiQ9evB?XLUdN9sD1<{P4F@^dw|?nD3Z#Oaq>Xmf$JEr0<`vieBpS+=an=$^m)y zFWZeS|Ly3*nZJYoNtgdkDAB#Y%FkNwaXD)@fD7WUa*hY*k1pqFm`Hv)Ag79E4M7Ay z&`bV_c!0X+*K&D^@lR5;xr>xSvAh3Dg0?Qt7W})G-`+*4bCJ4-i}0?}D}E()AD3q@ zre>9O^#J^N;7sphia80Y!H=%K0wF(D@Jpv5Rj@->oN5LTz5mjwAnkMDy8)ulUpnom z&&PMe21tO6g)g1X)cY5JuOW?N;Y+6nX?;$@=3IcDsJ?Mz9(e`W3Q{J4BBS!tld(So zpeK?k!DRV8a8XcqGLcILb>1m(NbSij8%JS_7ydi6=AceLIyu*X|HD&l^$x%{4Utx0 zAV|Jk3QC*?B0!JDz9C-$dAk8Uj^x|P8_I>@$ydlaT?FDZ06tFt4#ve(GKu*llRFT1 zBNI+Tr1b%EPnt}2g18mHOtMTS_UYK6OWqsF%S^IOCT$Q=4qzrL5jP$G#7ZWGCX>?< z*8@OVwGHWfpc4ST4QW~Rd<87+^p^7ck~$@>O*S>BeuI#iA}h$sgzlkX5mM{p_cVJ4F^5Z43XtDA0=BTOb2gE#}g zOpY{}ECqTkd0V9`Qe`sP2I3|FKE4i&W7i&QGWZho&j6HjoFT=}fL}7ecXC=z7hg>l zwmpc41MrE=$5+-~$hpJWdYVI$T48>m_MCr2?eB1ksN!cMQD1P%bhC5>u-htrL;GeJ8M0JXozsQuHx?j>asD5Cb~_klAw zfNFn_QTw`mvFj8-wZF%x{ry010Z5aqaH7;;-491xf#$r@9#2$MMLKttlf|rKh>QSy z|3IW@=>v`og?b?71K5zO9T^HEfSwI-+Ms7Y!-~x+$Iw~h$Z%MJh`Rw+8_*syw1V?D+{1EZz=N zyyHP?4{)9U?>yF%#e3Ni#k>p=a{yAjR}5_t@M{3dcLk+Wjo&pfw}5&RWAMpZ39V@4 zRGwtiVGb0BA6?BSmuN*Jrw%n>9TKbnh*mUmnvquTEV#h~L@OFOZ8#2R0UrYpt!U&N z#cnw0Y+HRt8nvR4(}lE>0Z0dcR+Jj8coNw4q)Y-uv|`~voLdE;R+Jj8ST+b7#sSod zQlk~i2V+VAIPXw!UVoHk9oX(;AXfT&D!C+8ew{Q>Y@q>V$}TNqmYFjNKL zd=DW(3SmE$o9?JWL>mCRqoq?o8FPWo1o%=_ZsWFwHxufAABcAW;GB53+o-ef)Zs&L z3ch*fr*i5c(R1mN9%)MY5)wTRkdjuKQtbu)0ck90m7&!gj;%ESrwl^qx+;3z!IbnU zL>viVNsl%q?G3aiz}G!p(vGI2GeDdIKuNcsY`Xs!B~5MYX<{~ljh}PzSF*HkS5xX6 zAYd6lO5M$ra0T!?Nn@!`GPEawKMr8QPBx_Hfo=h$^11rK=M?#dha&T!zMkR~uu0zn z)2jd#f5k1$FU$E;FC*h_ZG=q1if&2U{{hdc5Im%XlTce#6dy;(8@t;M0FVfe6XRl1g#}$ z^MGGsXceFxN!m)_cLIENddvVE`vz)U~<}nGk;*(6p_aM=G0IB$7 zQ%OAv4>;1;LFXA-A!s=OXAFe&q7XjRr<#g)L_`$;6`y7*ITYAgq_E=C4XtoAUc~`Y z@#&`GM*weYXf44f74HlDG()QZO)5SE_!NNeCS)(2Kf_df8Hh^&sCaRYU#Yn7F(%@u zpKmICClcHakV;=+D)=z)`$=P^uQasRfbRsLUJDH68(^Q3!g^h0XgkJW_dG!Ab(K?( z9kmzu9z$yhzUHK*@J12^fUg3y!$>oue3`^n*w|cKqlywb_FNsmB4QS zg!tIL-T1&Os&LgeYPCUW@(mtzgZ1Ps>ufRyriBlUdXlSpGJUof;QfnQD2}&R_@r%zxWfQY@|SHKKcdtFzlYQ;q!)@^dpnB#WF`V z1LTfZADg5qw)Ys5WQ_TdoNPyO>I~M7hTU-_Cp5+hZo9dvtvkC9!K|q$eiys%p+?TS zkST$#=3AX8b{C`AgCVgmKoq-+QSAA^X9GmByBNiO1o(XbQS6hA0z0DEC!20^pxE6F z9ajdb55Nn>?rs!&0x10eqS!r*(&-xT1OWJ2f+mVR>}f|G0T9LR zVHA5QUZl?lh!XcS@`BFXO6*>QCnVM5G@~9*e+VT8hdJMF+fT) z)JW_H{0!1qnqh`^F7Q#Lu{6UCZ6@#wNMmV67}_G>3jxl3$fpB(mWnyrXxBZUF9$#| z#~AI3edMSeps3WYBaK?nRL9Q*H|bwY(u)dwY8n8FIm1;<-;I?<`c;PIK}6qYSQff0 zSne)E(Z000Rv76Kye0CZt7&@>rTfy7+4Li%d>(E_r5psws!ET4FFNZ9}LZL`c-1B z4Bvlty*6F{IhZ~LNY`7Y`}{}ylmn2iw+$@`JWd+B-Z8YAzzYDe58ReTlziCTexr0H zh_4SoqG6S=7of$Nd&1=26EVjFB=@9|dNJ@Bq*3v*4DDLrSCPitQ-*dY@Y?{XPCPdq zpEu+iw>PndC_%1KiATWm5C967XH;Sju+0GF%&5fsk;~rDu#fKPQzHRjZ)Di-1a=`n z`6hs!l3N?rPLq7<7yww?7}m>yoesbZIMHpB^bB}NdIl^)6;@hBEli;snja`5t!w7d zZj`a#13q;sz&zTGGR_A67l3)R8)du~_;P@Gw0o8Dexr<r*=c;KQTcDB}g|eQFRulyS9D#?(_j_0^N;DH<>y zH1g(d^r?#g=Fx6w+9^U~o`-)*s19o_d9>dRrrQCc4v$*$XnzX$69C^qZsR{@YV{&$ zn*r#>!w^%2)}y2iMv{g5#=l0aNZM#59Rpf>fYTHFuP|qH<#U!i#7+Ud2LN5U$&!cI z6kvk^Ds@r0>7xgb7eBeo3Q2dEq<`JwQ!4>Ty3-_m3)qtYyy90H)^`l+)K`3J3IMF{ z8rD_7E(bti+PfN&R+x&k!kj$-FGW_@gG?zNcOTmD^o3Pa6y_A8Ft>c-Q%eA%FsB%W zc@Fqf08yAzjKX{Zd^bQ8=5(VNT4t})_sw3t4ZYdlFuVa!nBGQV)&aYn6wY2}n9e-> zQ%nQ^(8@s*g<1M{%t`=Jm@|yRoc@JRod6JpIm0N-qhI>eJpfUdGmX4X|M00wfGA9# zw8Fe`BvwQCGlJcGmg(l9U>X3BZa&*|^Gx6uk;eIBfT7I?emQBJKL#4wb-=F$_&$VO z_UB+z`{khB2!LXJftdTz%FJ=J$?;{xYyn7)V~nKYuYIZkKyn;wXomuCO&W7N*U(M? z-WlNJLM=|GT;w{zs78O#djp^v6OC#t1U3txoG{ogaoI06?32Fpsj&dC&ou0J0b2@C zzMtIsTw_=-SAI1c0M=^_>tn!H0ibl_U9I|s(hWkfqgGk5QM$UBS&9aCQM#y6Iu*ci zmwvxdx~NgQ#-J4en(^-+9W`2~yNSLfjb2%zZ%NbQ7AB}HLq7${js!s8vW&hx2W&Md z)VGw;g{q)m)dJ*F-%>{3M9)%2&)&j@pG^SKvy{=Zi{pMZ3LrX_ZRA+a2DDWXs>B}7 zH$A*9=~r6-(!&L&=iUSUE@{-M8iuwP_}>7rd2S=MMuYOhUae(n_9NoI2B25Au~(2w z1CvW>mS5EcNG=VH#1X)Ukj7jZ8QNcfpASf_YhwCfq~w9KBUx(XRAPF0A$aBi(95N! zmtO$37NAmFOHI9oY}2yDT7aFklL4YU+q5jPHrDa0M*yNcFB*B;nTalKS7f?%rw1T@Lt1Q(IxL` zd3r4X{W1VL|9ve_ul2wl2B_2zjZ9BYaeH!`n{=;9`e{AC`VfGmpP8hk_5CUzpnNOc z@_uJnH#PRFrvYI7-mpfB{pw3l@X5M-AZ{7ZE9#ltZpf#0cIElSPcAHfGEJlrlZYM>SEJ;oHb?|`g7pB695I6X%wJG3%}|N5CxcJ zI&2>B3k|JY%`ysbL788T28aU8G74}@xnC^=hyu(q3UFOZznTva1(=;yfTPhsC)GC! zFxPb5;0nL$50I|A%yiuh;L}OtxW3%b<^aD0;Opu(@_bXPMW8JJp!rW{3}%dLjpS!Q zT?Y`!i;R@S!SFl)h~(=Gtv>KN0A~u)KgQBQQI?o4Yy)~Z0A0A$bm3XR&HyN9G}!mL z?6({CA+7xCYyjBrFzm~K%>yXkK)2kh4eQ9ZesvB2tPdL2#lWrtP&syB3DZDtt!0$s zMLt~d(bfEyT4sj^Mmg3Uipd@z%F)0m#~$Fj0HPcXjB?aE%&)Qm@_$1EePk<0I~w?5 z0H{T=QH#rf4I^a|C^9cJ#jf}ifK5`Y<&V~tKoXv2UHG_-Qn)5x6#e1@U51g$mmx(WCafbxBeI!Hc)O+M>DT|@5i>Kr3;H?Y@9 zVfk@i4%1&&=~uG=pp}Cr<-Z;HO@`JIG|A^F;Oh*n0<@!;_HE#=0k9nU($!FRIVAof z^5;$Ay#w(&qjHS`KSgGIbT!>Fa_Bwqpgi5ahnTMcIdqzNP#$hIa0WvRP@9XP2WgA# z0qi(H4$ZF*itf$;sz9q^s9T zG98#R0M}^9^KL8Ro&f~LfwWFYUjf|%aE@+=Cl^W`;Qt&?$dl`!4t`Y(@by7l5aZ)1 zdCay4=}lk4xqv_$ke(6JTA-@|%69|!16141 z67emFUo!#ceh1|0d<^h=?ix%K51jY5*Vo%jD8jJrQ_Z9scVLAm2 zJ;Df>X+ zeGRPwG#PR;fKLH9(~GD#Ik4w?B@J*!hfm!=xQ>`P)^BEeiw3f0c0qrWGH_R z^zQ%}$|(-z($0QW50Jw#n3ADhWu#!d=ZX7I)8qXr3Xmb5FXQ=opo>Z35HAqYBS7x~I4>WLzr(Sa zS%y@h45|7j;Jpms`x&dvC1NtQZ z(YN|SS_pIjK*e^1O|zaVjf!*}?@fhi85ow331j8v!(b~$drRRCk;1J+)NKG~BjVm; zmMq}mGLUwGz8!#p)J{fC(TRRl2#|qvgpj%b?Py39>IfM~qw;eIh08qHTQj!K;pdtV% zTt6XQ2lPrqDp&oaaO;4sHl&uSpNy20>bLArnUThwkuAk8V14%7ePa)2N^h>2tu9_Pnm^%F@+z1dKH*X06J8b!35)Rw4=nPLbIv=pWw*NH0O6{`XNS`&2=a{%3mtIZcope~QTI zf)x1abZwuRL&-&cI)mH&VnvuTOZ@Z(xBJJM04ej+9UNzh+=9Zi_0u2R9vER!EB$l` zw+GcY7;K&VbX>AsmvH|?bgn%_^6BoUPq;li+5v36R1TfOadI%v{R%C{SNU)s&&$Bc z!Mie5yxOgzlyFg`Gg1Z`7{72jTnC*7`}u{p=O@9?b5H@9on*7#l;&53=7i-{8A5(lJ@7psY%h^IG$V`>RqQ2hL3ok``F z3eoYtP#pbZ^er^h!BY5d}Uve!v3B;wE zWnIpADSxSh?ip#ZWxCD)6`x8}gdbg9MJ?l7%SfvZIa*`ZYYMPA!be&qY?TqfPa};3 zeWX=E+D71yk;WN!l+}oGeggg>pn$JDqpX9E0d45XF#7=P&!Cs1tz!5xtHr>t0@Ut~ zf{igW+&>zA1n9X|jKz2X(Q5%=zGICuwDRsug2&-GFrLeBnhtCVAbcuxW`eOBUkiLb zX>){Dn}uBi{C?6Vf;Q2l-2r?PX>STmo(sMnSk?l#<#hpm z6d?8!N<7&LQxEO=*G!9jh;);!8U|lBEp`F$^Q;(E)SiFav{*O63k<$uTHtxNKf1z+ zWHd$u?-t@M5#|VIT=77uTK8gN6|gQay+`h)F1OqoAqPGS_cFu10^DO=ZYuvH!p+?Z zCI{8zdN)=M`2EO@HWyToshbaHNH1_8lRG7RcevbCPv0sp@~jtm$&17;G^Ld^`5q)t z?jkJNDFN2nl&l+%JZ{p%jdkYZQ0Kq6(Vmj&GM4ImOx|a>oE-1hx{NICAWvq#(Ox8V zk*7?B9wgAvML5^co!pd3`406k`8s%!6TL_;FEY%FjQ1k*T;y;0(4vscn1>@&$^(ik zWQ3+U_+r5i?%@czBecS*!J%;p6mK#>MreiAfC~90@GYcqgjQJ1NjtbF>{kGrkB18D z5YlD?p9GK*T48nM2z>+iWTJ`j@mYcHWm1I zz(F^<72{lZ(03lhm%7+AIp?FpuXGVkq-f!g{n}}GPRl2^0%PMly=%B(zm>B(KDzpT zOnNq_f$wxN>whEiyAB|;)_1x!Y0m?HoU~Yb@>Q~~v0iuv1MKY>F5hcurv||5l2+7! zv=$twM*u$Bl<-(S?5!MfodnwPfbfiNq}3#C4Di7KYpBv@ z0$By@RzRTk2-8F-;)lNBR8^>bpm?OA^LcaIKDuw_C_@*e%Y78hyjk}hYG_nJespzh zybwz>Z`P%3(&JC}tM&kC=FPe-X;XoZCymX#S+{3F?*zVtG&b`V-H5a=fqz08n|X^a zXEWFCjkgv6n|X__Vly8Nv>ia2`T6~u8Gq>T;WZY~0|0F1ZN^CZCeV!}p_#X7Y32oI z;5h)W$Ry2t@R?A4Kv<(mw`pnSO~4-_?WYq-lV(1>4-C2hH1msE=IQr=?EnN8yG_Jq z{zt?#^E5Q`;$f!g*vttuT6(5<-PK&v%+F$wOw#MlGrY4f!SbW4&*!q0+B``&W|Pk7 zi@zrU(#(@|E7INs{w!&1=1IoN-MSy%+5pnbleN6E91pw`X>8`nx;dM9B=9pxn~4HU z)<>|Np9cOoK-ziAe(hYQ&Nr>P2k|=r7T@^J*Ku~^QT_eu5CCW`K@)4lWZEXjM@lyu&$5I0Yk7DBfTLm=YD zy^eRjBSFTp!#DpmSy5nZvui5GSGLQ-@pX!@$dw0y2Im{G9A6{%(S4oVwUQZMIhe*8 z>UrY~Z!9LkOyGk^j5&pihnk~+`pDehxk<%K*m>z8DBR5 zUubA8ah{}%uie03Hna-RWPFtjh1Cnd@l|TZ*G!;&NW%CkHRG$(Fj)Bjm;_7B_zDfj zdK?s)1WV2MI$;FN{Q#K+OU?M2G!k}o0LB;o=VC1M<}Lr)#tXuh9z8sQ2A?JhUa=)u_ZPcmXZ zhscl8VmrDsDGCQ=>#b|d&WM$4k8|VUre)%nXW}K>biAb+NqjonBam%x=nQ6yvrM*g z#^7%%fQTJ3FsMxPU<38#@QGR{OD@Fd)QG7zND_jG17mmU!4i4&2f{|^1i6M*XHO*>NXr5*mylr zrHtB61?RZ{>Q%N;wdFu>1*modkVdc7l)87csd{spxia5Hxp2TK0ms^WGOR+1djZLF zMwF?wjkt*34D=~ODpzaENctM+7l0Zp@LE{`=bZ~{I-mwuyN}lAV)uBU9Y}frT#uI1 zJ~ayHFhFwX&{68qqnPq@WzcEnZZ?)zSzXa$)lDHXVT`ozF0eys|dr2xJX*l%3jE+39t3M$*l-@G+X@(t=|D#uo5wbOfiV-+u}z7nI(I+;swCng6H!$v z5j8(RiZTYbx~ARP-+ue*FWp&5C#M!?IuNB&ZD>w+;=d=k!-9YclbS%VdkWey#Y~ggXsl8J@R#O`ILI6 z1DjMhT2Az<#(+vTZkoeVpMyi6n7o{V-a^b!u$%>`B3ti_pmeAropn{7uwM?=*?^)c zE-USG(oTKRPGGJ_IoD<6*ih9I>q*kvk?adz_S&CbhjyG5JIG(F|>?$%b6PWgl5Mgq~d9LcQ@w#U? zFU)YVnO#oLRXz4qY0l;QapIm>;OA9-M z=2bmeIruVH^*ElZdTd_RBiE9-W%XRuWB=M!J&xz99>;T4kNulh^*Co?0q9=U<9M#> zaXeS`*ndTF{7<2(`>yJ7JXiJDFEXA@d!Va&9M4rfj_0Z#$8%MWuIh0-SM}Kc{Hh*11)(K>o@xt|)_?V>wvOkjp6DOvR9nY$RZsL^ zJk{3mT-D=vuIjOW^Qs=3Q%LmRIn~zjT-6i(=cn2_o~wFnUe#mssvetH_1JImi&yp7 zyD;(DysF3MRXvX9svi5-uIh0-SM@lat9tC;ysF0@$EOKhkKC(zY+luq^IvvVk3-iZ z_o^P7SM}5yj&A=quj+}qr`kq-{Zw1Wb5&1mzT@$#o>IP4!Z^+QU2MJ}+RGT?^1>d6 zP}VAZnX7tiUe!~31Y&qqPZK7D>ybSRzxm!%ZEarFbI^XL+6KK>^#r|F^#r|F^%U$o z)wW>YskTAyRXss^nSAYD)f42O*XgTzg5Ilog5Ilog5Ilog5Ilog5Ilof-{fiU%}?8 zo}l-to`Qj%t9tUir`p=Qswe9}SM@|IWA=G?Z;$jJb=JPCdJ?cw#3C_91a)yMAaOyG z`qdUdvZ9mGngx0z8aI|BNTL3sHIRI{-ls@6Zwtg+nb}Z3e=v~R5>ujmFzv*O1Zk>u zGawBFDbt}gKpKZTL3CSXGY99BrXZEbB|)i&`oZGU)ROaeZ=32= zn)eYg87$+O<_#d1A~2iCOF*ti;7TIT0NIVe4kC{NX>cAY4Jf0m#NRJ(;s+r8 z5jc%V5JjJlz-$2gDYJkMy2R9XU+77xKU(04nq#t2sE**e7YXhG1dagmFam3cv;y)R z0#6WW0_1Z9J|a>VNWm1?+5w4^fEzb7c$6U zBJvAsXXF<)k>ChbSV0!ZFO-Z#exYO~^6TuAUnecUuE?+830yiqrbTpqAt&HCr-b}c zibg5XD747Fu?&CkibeL~5a~Dx(`}PISP${@(1(_acr3E+<>`%nnX9%F2P>=D-(JBLY)F*a}w>8>%BlW))%VBNdYBV0uJ4=;WdF@at9 zswIo4lY`f5^r$(-yS@dwP4{j0wnLlV+wNtD_WMv6_pU?n8*{HZ6n`=IrbC;4(P!pR zWRY!7@oov3CHBL}ynpO)gv)GmiuVd+)z&trc(de{wmHT781Qtm>9g%#acI+J+r8n? zHm7*^1Wzv&;i}1BRdN?{3#`OfiCH!I7X&{~*Xio8sT0e!C~`W|1qzM3_ArElPN8wv zu0jEljw((1PYmTDq6uexf0qagrfhSJH+h08!X=Zxo6Q_#$;A2{goiVhOo29(PlXOt zF~k%BifhUO`?dg8f#VRAB@+wKj0v%13S5BST3hO)d9F`>g$R2HHXhs{j}K+XsU8yrg{yo^`hhy}*73gXcC07uxS*)>|&&BKx)y z`~xB3hIR$C`wj`0s0a(;ztgdhXJ9nMHLq#{!piy#w^)d-&`o@-Sjaw?|MlQg767l# z(zQbNE_g8T6Q6t>5^c_>BAkR+9|NjRv)b^9z67E2Wpxn$Q7n%ugHns6Nn~q|fQs|q zkzl(xsn?RqfaGc7^S&Pm&qPUdUnW{owrmg8^>4|?QF3mU@yWzfc7S8`VI4tVA9KPQ!`OR7mu@9bc;tWtPxmQYPp9@kGz zrGVWT!?Ky8*AX{s)+c}ciSU)T`d$qNe5eQf=<2l~I5XNavXB~c51v)a0nXYo(#lEO z415D1bg`S9?GU=$MGgm|q63b{-{BUy_bxPfpiuJB)jhDzayaDMS#?;M&mil4=0T^j zc2-lSDn1|QWB?pGcDJ(*BW*D7UH~iVX2s?>6vFr)BfW^b+c)YSSZ2D>?D|+Y7imk? zz)hVHEyY^q77OK+kFF+%6sy>(&0?)YR=2YVd}tI~#Z{oa4*Vt3xX>@QT9OuotAYX~ zvFt0gVk~Jn(1QThoo+6d;BoY^DJjcnirO7c%D}oQ>)|vTbi#o&Qz3QmYc8F-)4A(0=&)gD@20C*&s6a^l(8c~s#wodu^;d=NMjZ2 znJP{IJ{FKz>D zFpE|aY6$*HhU%g(@j}3KD*6{pJ3B;1*Q<;!FDD%zU0sfcKmnL{=sIM63B`LBfQwyU z*2z`m!J6iKQqBP-o0J-$WC5&Ut~72F`NlLT+RpmOC39FefOxcXaEdaX1yCGpdNW}a z`^M$uFjuW7v9epVPOlHJLX?lL9_}Q=9)`@@0Ag$1l?3M>75x&`oGI0uccrQ3 zeL5onI>c(uG}Sx>sXGH4HrPzvm`%0-_#D#syqKxWNqZ3ZDgc^ews}R~2JBft_sq8$PRBlAK$}@?m`ij4A4}oP48K$^;J8#5@cA|Yd_w`@QII*u z27ho>Pf zc7#_~vi&YQbquV!5)YLAr!p6`SE#KcNN=mso>zd-l_DpdAgcg06n?@%oJvwtGgyRj9~~HCnkHrK-|Q zzGA+~^lOtRGUd6~l4+H0#7w6^_$YvLC`wtS%h+9y0l$khs%n+)KwA9^;6DTqB|6F| z(FMRq0<44Fy0LK8s8zZpJp<1=(&gdEU1NCm4NI%P8_ULh-o$cTpPwdUW&f>Lu*PalDeL_YopC!_!wRCfe9fjnBDV9sWTKW)*y$1L^Kys9-ts|F!vIf{{ zfK}-VWY@e1VC%`F8QzI1)ZJ4IYqX27p|w20x`)$q+rbT0_98dlqoap&Go1cX&vRRY zh2ck6UjhN`sIy<@bBcQoWmyjh-Rg33iE_j$7a1hEi7#R3ahJxHNj&F4P;yyLs3ULT z!1$ZYjMH5P6hl6`IvU4}o_VVL19^T9sF8WP#4bW905vjCnFhe>0uqcJIvd&A>u+xeIiPjh1_fya!ajd4&|x#(EgK_wce6J(yMI9|b8@A7l) zJ>T$$?1D4h)s}VIT$33!WqBqu6&vW%yCd4V%|$5VrC-WucbSpFmFu^zjPp^j?jd_v zy>v$1Jxq0B7vLCjne@SFrlB6D4*N3&GE6f(Oy}&+RR7epq-#A)m+jBAuktHAO!w{2 zH2?6lq{lr>JNIY0Cd0Jd!}P=cOiMFN(=XrGPW7>n@if8U^zzqQi$d(GE7AtCWZ0msa8Ec#Q}%b}oUn97q<^rzo~DC`AA(+ZD}6&1x7|(XPLI z|4~*8myL^VIybX(_dNl7xUsktM_FfkSc532mzD2^VU<+bqg^KNOP)#`?@}`zoOvr$ z9n$WdA^b6h0Y)og)0^?mFIL2*pz^cKeJ}2bXFNZ}Us%fir4_N@H>`*bo{MQKVywS} z#ji6qf??y=2_qx@I)lcqvqsSNm=YbPu|0mBt|`%3OuEOf)Asmv?r&DKJ${|hf7h?m zUWQI_{W_xzAIPsWX^m2_3FBpek zJsn*dzRK5?)39Eg3uIvy*9Hapr&1tSC;5+3k>*Z>@YQvQl;~|p7``r#9#^4CMOw)rje(->wPd=DceW z(^r4W65LX-1jt~$9)l%(YyOQuM(G&LZs9xPHzLhM{Taqb_|B~BQR?ZsC980kNSmea zgM)SW?o{-N1b`}!6DdsVPr+Sh{}_nKgRwQp+?u-63}tbGq+jURqPuuE4sHKG^Xs%dex1(TbUgLl{6o!xDBI)L>HMZ& zr|t3UbY7#lDx}}%*J*D8>iTs$-!q<-+~?P6d;B_`LTJr_{5oxqU#C+=CT4OP0;bFq zY5pg-J${|eZ~ArG9=}fK*Zex|^~l5Z>vYznGya)hr|t3UblyxeaRivsEobO*6j9k8 zzfOmKoe|D-Zk)%j)1hByq>OQ{5oxqU#IQy>$E+7oz8Fi zb=n@kPH}TSkY8u5{rx(##ILhZ{5rFYUuWFw*J*qFI-TG2>%=8X=m3vjr$fKa$j|&b z?VIo>89pS}#$(0#=YE|I{W@zG>2SLc-OcosoDAA0r+}(-sszYUf&}%Y^?-B`B&n~3 zx`mGxq(DbFsxT8GVO7ur~4G4Ei^oF!D{YqmXods#D6ODizD@dg-=9BU` zcLr}*6Ugxr(^XHa599<9-An(S$ccjV)erL-&_$5JdJFUED#$4P5$F7Ff=tu_E(1;y zY18%ncB||@Q_@_cqfLOEBFHjb6KWUkCCF{MX+t2V3bH~+ z*&wG$O;_s%sABkZiCL#FWKDYuvO$+&r&jn3LAL7UML^CJWT$?gZO})MUD{6p{RH_y zFQMrEg6!2xSnaa}`Pw%gYvu6Sf++24$Zi-YbqH!-M>hRniA!qVyR7*+f)!|Gd;B`X ze;mI~+vC?6{uli^ZI554?eXh$e$%hh;ba&7clr4(uxBr`donhCnGxY0zowmoXGu0n0Js)}3ITQhyk?Gyyj9^F8uhXewh<=@s zeSVz|{W??Rp-??!GYpTsQ%*UuR;UUuR;U zUuVGU*BPK;HSO0Kplvnn*BS8obq2hCodK_3XTa;%8K7(5w>rV<9vtxcbq2hCoe8gB zXWZ-8>Cmq;@(#W%wxC7-CX3QkJ-+V8+KG3gjn{oy2=rD!-V;bwYXnjS=lIoEq-+7Dkdy(S^Z+#2buC^@@nKCH>TZ$wf5pF7 zuDKVs7(C)=&S-rrjP7F2cmz*)`7AQ&1G}DXQjgm@{OXs_PW>`^*`* zN6%x+L?X|-w8NN-lXomsg?jpv!wR(yp9Cf7;1e*kA_PyvRygq9qq;1g6pcdgLrLDK>gHJ9ROUjj5l2 z?9vgg51&ij2RR?;kKyeV-jrGmWUpRV56I@!BS60P?O~d&(pgw(UC$-z^NDPBo%YRU z+zY7))}UBv<#MI9EtLNDjnI3*oN>QYJXEWVPaXK@UXx|H53vN%G3am`~c0+T+ zJ7O(>wbec!eqhx25jCpJ7&TbSP8p*HbMKllY7pCJ)F9S3W7HrvIAhcxHY#J(ppN{S zQG;=_GDeLbP$bu=K@kTuYA_<0Hfm7glDZtjE>a*ZP@wNZr$mgCe33pC#*#=aCLJfLpBE=jT#(8GN3{u;$&BPGI}I5Qc9Op>CLJYU`uaS9YKL(@#UxJ832oc%x2Zg zfY?{!OX8{T67Mp(MveQ(v^veiZrsP@a=J#1*U9-dIjfM#XK7BA-mLlq<1J`~e;wkp z5OCAGMvdB_*!3CDQnpUVTjW;h&8qFm)R9cgr0;%At~l4IF^rrzx5see`S#0#yGD(L z+B2>rzNC>j6PX;Sd@8+Jl^vVjtU8tnCNcpVjI-6g1g>b; zsKEwHZ&s!1q&KUsqJ%Y+@Ejy?hH}%nMva#k{|e*TZ=9!04#dpzCcRlzq`F283A;uO z>R5WSs+7VtYDm~MY8-|kY**q-a^b{dHD?Br3%^u)v+4kH4oP!P&2X**kE*lJsId$& z*<#eFD@Kj%pBXinGQC;#L4@tK_>xxKfq=JwPYA_~a)R+MzW7Hs$F=`OW7&VAwj2c8TMveYJ4s6t5 zOvb3eG#R4?k&ID;NXDo^BxBTIO*2Lf#$=2dL^4JVA{nCwk&ID;NXDo^BxBSdk}+x! z$rv?=;8E)uHTnY!W{et)!=u(UY7m1_!*PuoKlWYt(oE z#Pnv>pAfbkB=K|X@u*Lu^)iNuDw{zXHTnS44{=R%L4pma^i<}1p&&v17>jw4AW3}_ z{;&>T9RD2Bu~v4C8nU{AQR6ZSnjHtnB{_E?SEZLV0CH);Ye2Agb&VQB!Dcg4n*+^e z2iUBx&GhzrEE-qbHrMd!&8nB7T=q7^%OK{u+f>I)^sZ53Gsx-9sy|VdgU7baBDE3l z)L&BVw^_A080-psrHvXks-fvN6zv(FiLlb#Xc2XHT4?UHh`L)XG`Ctr-Mto?@4-=b zvxVk+aMayxp}DIedS(tK7in&{i1v$J4WvYKzeTiv?0O(&nj0>n1ELQh&$gQGfYE`n z-ofn96v&_&cYv*v=AMhFyXiu6(?!(Xb)mWIB04J2i;Y1%f#v5Ml}HGhuqOYR0JX4r1&N@YNt=)L4(Y z|1pdjEJ?{fH)^mc+8)TL!O_`CWsDkIAkKafUy%>-Wigp0TFu78^_au3ujO)t0vVIW zDqzN>kxP?DJH|H3#4?$QZFo&vC_qW&lg{KAjPW#KzthLgkq19ZVy#8UEVNTjND)kb_{j5mJfd~-lcN!{OR zDaqnkaJP?TjbQjdjvqz8-%?WVe`qO5xb7eS5lcy88CnPKAHT4aq>uFyOG)}zFR_&T zj$^%ODXIT+OG)Bix0K}kX({>BQi3JOAJbC8W4$=pAMjW&Mg-Hxdac8B@xN>-$zlO) zX8a3F36J&q(^7&((6jOXI!j6XH!LObKP@FHeXQ4i$Wqc^e@jWOSV|g-r6ku_N>XX} zkNo?;lr;L&Qi3Hg+&^e0`0uxr@K~=uprwSzdi^h1N_ec-f5cM4W4-KQfk*_<<}X`MY70s1$QUkj4}5dBv&WvRQc*J1z8rfW0kPtt_!C7;aNU7+1=RW!R*_f= z^oB8}9tc{0Koi{wS8Jd&HOGvcpbiqF%mtv21H?W@UQM${fc+VuYf1VTq-Np`;h%?{ zJAhhANQl*j>2stdyIE7& z1kHa$@9Lnayr%3z>R&O9hlRMsfO^w{s-=Un2U^wU2zeDyJ8d6Rotqi!Sl*qeomj`d zajj!}-7>GD%@&`xzTe+EmNduRR~YM9(j0e>pZ6N0xaPQfA8glnt(C)-3TYinYS|Vg zj!sEq9YZv?MLGAuB4jttr}8Gv7AdFa;UJY1_5{f(>w<7;4y)u$uZM7xyvYoo(+%OK zHP9W(nE}nTo7Lhp>9ohr8T;B=q#wMW#m&cZqLFY+ z=RSrnM14cGa-L;)I9fe?L5;5%<{q)|{G6vDB597EN_x)zkHibVK};Pn5=L7eYGJCR zJ5|(mr?Tiy6>Xcf0sS7d=uQ=N-Ki|PQ$_c4r-~#BdDKxIF%rfi3n+~4R590`D#krx zu~_sI3X5@%SS%j>21qi-Jz}wh*`8(Ho{Kce2wS5FC+y(=w@9*Z8KjkDjD(R?!M)5% zjD+#CcQPxwQ^f}+SyN@vohm*kyA@2st~*sEr|?~{l^P>qVgVQQ7WarHuJWA$Hf3>- zSi;>SW^s>L;%bprWO0vJ;_5mSU1D*MSmL^5y&(R@vA9PpQ7vq3E$$IZEY50&m`aO# z#1c!gMgZw#agSJHY3?#qwySjlx+igc{3gWovbaYqadY0yi0Nx_k67ZCg4=-%wzx+u zacllP_!(t!k67Z4_zI+%XmO8N;?AtQQR?Xy_lPC#5^1w6?h#Aeo$3#Uc^3DGCGJUG z1LPWud&CkebN&ix%d9QMKvrcRidx@h-H$F#JRohq!b%nac_{TNns2qmKl~DFV>^-0 zI;$DJ6YI0KLfQt4?o^2F@KWPOX6ofh4x5*t%~lxmkncdEp5sRxni2NvC_ z5}Q(Mf$X)`v22@Dj|2HSNO!8lR_QEEPjshBJfEnE{9r(*J5}O^R0{GiVbGl_u`N{x zSb+}GohtF7=_4JaJ5}PPcoW2x=pfyx65A7fftBeX-Ki2gVy%I-)j_&bC3eR7Q>la8 zBbInsYS~E#?_^iMBDr_fLGBStyee2P9poOd#A|}})xoW7s@DY@tb^PmmUu(3Q98&y zVu?3JM<(hZ_lPCZd&Gj=BbInq;%4a}_lPBS=Qz+7xKrhIP3ExTG0xq|Tp0r@#yw)m zJdquYagSIsUn-rn=uVX^5TwAOJ5|zkr?Tiym2};yEV@%A3nQPQze_B-QzdI>@vrnU zi|$m(I)b#d=uVZaD@dh9cdBHOl%SLH-Hb*mWXXIS1Y#si*0VW=d>njv1@Yk=L^7b_ zBjRLN7Tu}hBTZ3_J5_v?eJI$(ohtrB9bu0dOmwG;Kb5zJOpm6SsI2>#Tu!=E#h*^ROU@6;S%plvLCTHa1T?-O&wD{KncVE?P8Hu2ryr4z zeni4aKO)0P;VviLsp4A_^p*78kmkI9KThLL72ldzPo}5SOdP>R9OqFbXaC2aPrOg2 zkJC&X0ftFr8cw=X#a~QjASp21AGsd-K9OpW>taPV}Z~uST`|dC) zitgR6?w;A1*@?S5y9+F^-&LL`1*{1{47WaX~~0Dk>NO z1DFs+1p`r3444DzXT(qM`<|-q8H9W9_q+G^JooG;`I}#IFVG1j-!?;>yDj3lWr;6`Pu)`#rD!wbtsUzW3 z@psdF-zV@KCA^JJ<_cx%F`O#CJI%O+z=ten*K(cG8lQu$>omiu z;vb~fQXAh9i|^4^jKql-|4^qHP8I(s-38Mza3T((g$s{ri)tBFI-Dy0ae5><$9SCA z`kXS4+G{6a{9Zf03?)%<5*{T^!f2M0FuvS&SX08O673S}kq*3sgY@G2NNB~9k~RjK zvJniYN*tN|j_m)!A)hNxhE<1CC5}obksV0mAgyWRGfAo?x=iX=fw~ZSSgA^Ms)>t* zJsbU>=#u1_jIT7I9}~w32-$o^EYVd!(&j5-iQ@$n+k8bVae{zSo3Dr^PLxYqnax+k z65SFbz+GG-m*HO!6G*x1> z@Q3U@6m?M^SJ0%*XsSe&fMT1`REaUthov^7sS+2bxsaCGjHXJAm6CFs(Nu{`l3Wwp z+l;14j2F=rHlwK$mx?qKO_iA7G=@2a#fgdE(5%+%q|1$2jHXIVGG;NFDse?V*JKk- zm6+@>qFbV=5>tx>*BvblZmEEOmoU0pfH)ZehvjOnksQa!DG;B*^H)2+*tfEfQhC` z+~kY_TYyRfJkbIifB>5tVEw>1=*a+YxC6X}muRYJzO1r=ohX-cn77@!IUhrdC7=F} zh0^aJTcW9A;Y0vt1R^-dBGLeftoBQ-z&KXrD`KgRO^zV@Q8?5QA7Sc@FwO&;(NwA4 zkr_x^HlwLhCYs7-3`ojEQ`wBBN||UXo6%G$6HR3^nkr?YscZ%-rOqp$qp4B@W6J=_ZAMe2hD2wfAKKfDrb-Q!+a0V9%>jnhSOvCjHlwLhCYs7-G*!w( zQ`wBBN{xy>hkquTD%H6x4HT$}LsRV|OwBnDK(}zNX`vW6KX(HPazc!zN>xX<1BBcV zqp4DUG*w|T)%{8o#ONbToi65CHa|+0suXt*2Ai*lrFuBjQga~sk1dtolGsFYG17t2 z$PBDt3iYf2<>k|6Rfb`(0$ieMl7|*F00jGHjRx5QGvXr6+I45;^>SVTtD zdr>UH7sXP)sv@?aG{QHqoR>^81hu@W{JZv^|4CwD=vZ0aLE4y2gQ0yra0wU$)vjhv&)@` z-pf0q@jVLZJK7@MyYa(FAD!b)1XtcHH4_($X!8nyXdY^+=<6`Vj&?l`K8ENHq}}K{ zU7#uYIdX#0vEa;!-i-WEv@i0*(WN*>qWh7b8|@FCXtdJ_{E_@P@{`d5HY++7={(C_ z2L*ZeI<=tEoqrK5Z=7C^blLf%kTGv0GCCK=Y3XrQNO#S9is|`~kbhzxt#*f9i1cZB z4^qG$@^?>s49nej(aQV_b$SK`-zd(O!e03^9>r;8pUS*ja+$X5S`W@b~e)q<`=}y zVLG4bVmm@*52e`J5_>OeKb%X2RiVRKvQoe&MWNo*vPw{&R=S5i1)D4fy94lXvFamE zC*-+ja0H*M`zO+MdLlhD|Fz0AX5%FzhFbY=<3#CcMc*x%EGdPL9C>#t>W#2=#ui_&BCfEh+ zgRcclwP(<_Z>0~W+YhniI{`OY?iw2ReDYkF5Ey_%{u~^X9PVI>=gQv?bTKM9aD}o0 zKVWDAax(u#X)h(|9t%87X>8hmNt#~tWaSg#9Ygsa1h~&_rvalc!|tugGNhX=LxVo5 z#c9yY#=w1Cdm)lmKFdU#B~=2|DcHdwm74|j`JrsF0!N}Gz~%;8vnia4fw`!bcR2g1 zL={|4hN(E@-$o(*piCbKtpNS0dCzeyYO8`}VK&6L+Fr z$SYbXvStj><#usza7H6HkaLnPSA}@w0-`H4N<_{lnrtV&2WQgCQ?zi3okP1yXhLE8 zf;vy)zx^sM*adZC@6nRj$cqM91*K7%8b4Rqze@!rm%xGwj=rvh_X_kv>Pm+iCn)s&qaq*-i!~tneCUf2nGZ2KtuSOjzLx zW|!)IEr9P-54ZG_oLRC&gcIbdW2c$l_CPM(-LJCwQ*}q4>M>a1^*l>1);%`D%a(c6 z7<+89&Tf^O!~$LqtngPH@aY!GbCuMCGN=N9KIhv}EQmGDpeq(frUP$X+)&vi= zZ5gmd58{^{{sM9YNA(4$(Rqo<-iXO>HCMRhonf;T$rYOYY6#<>s}u7&68Gwk~O zRr4wynm!Lo@Hfc}yZvtb`vLe%=J8j+4EqFL&1%lVUiY8`e<#eads288;0~bN9cb8G zJ4w+)fd?{=%ga1lmY2(b$AMbxH`YL`{0)LrGq=?Qe6!MyQQ!?=nhpwvO>+h`x2!Y4 z$>TO(s8mRaQQ&9}n{aTt4gC$MM0E{KBk! zK#$Io(i-k1fdb;*EX$m_`K&I;h=pvKbgSLuFVkBnzQU2rR= zuC`-*DC*q0XmT!h<+{d}#r##2Zv+LuG4Qu0~_Ii|_Sq5rJ9Ybq?OueNl{ z{o2yWzNL@TJ3~wR+DFjRZ4mqxD8UO~U%MNxd*1?o4ytwGkjs$QhhvcDo}eat0c4O&cGaW_(nCzT3h7~z zwu(nJz&D`neA;Z^S{XdTw4WE4Wys^iy0sF$fKR3ubT@_42Q_)geF-e`+XsEp1-~6j zbf4_IPxr}NexLkMYWk$aE@z*NhJ8api9TW0>clnTPT*TXwQylBu}@+1Ujuv|B+YNA zoBuoT&!Dc{w@x+EZAQu}rU$68Ee?)|^YUSOA-F4etQjF*BW{2lm~{A3Y!5b#GrdRH z=e2?Z4ev;fzu~Ru*w9L^HU+#0=~;{VnVJxT&&uMo2|J~WbmJY>C+tl1!P#M_|J}L- zoiz#52%kn^8!MLT+$9bG{#5m4jr2m)U&%sLV-CJq4w5<5U&%t$5BN;xaUtrjWFeXc zd^z*JSe{=ewjE2YJ~|%*T!Il4WbK zl4YwSY9B!f{IN7dMfv8GbL&G>atB<5bx!+DhM>G3sO68CZ3Xrj%XBMS>^IGg>(*Mw z*2h~o7mv5@P_ocfqsn|xZodweHD4upN46CBUQjwA-rTjH$;YD9@n3M zSBJ$-^9Z;f0@c3^Idqp;F9AIV$~^@pFIN&`v>W&xQ1Kv~nD?pVMDRJ-N3D1su8H@l z5{)05k~>pyQ(_CB82E#7uEm|hed-wI~v1vaxp`bhFBbri>^5G+h>UytxjZu;a~eD_Gx5FAl+jTD&&|LEX6zp>xX( z7jIr)^0};_H^X`Mer58xzVwoX0WS<9*vty{GqrhD41m+?Fe|vplyHSPPnTp{ip9pW zl-)WBHUG24reKB{^UaNMBJMSVC2M%|6ELy$))Q0jbumzuwHAv8|5P>Ec{@}~)#UBa zWK7cWAX$ZKDOtsr0xx79Z)s|&PP`x70=xl~TZR*)MCJ12`wRFN=5gZJ*1pqvbFtR| zsJILA>L}UF)WP?qUHH9;Etf+8e=g>t1oQj2_yaOL<%#f zg0n=oi?`5YeJ-n@mdTeB;ba4NH@?7UGBfBrQ^-5`=dugsj;p5~{>v)pU}l;0>Iqfo z4YgV??u)g0ZVbmiRXrI`CmWz!SJ!2)c1KH20Ht`pu(~ZTjpG4FGmD+Nx(oYs5#a5h zIQ#Tj>C(3VUu71%be(vk{{{F1sGL2z-gId7_&8Uf^|F}#iPAq<>3l4L>zhr4LD6~W zO3;byV)dNd4c!1d9n@{as9~0RKA)~2+y#v zV3gOB?>>x!rOrGFcPr}dww&gwS5d4Bs#fr#rFz79^S@XwxwoS?XMwENkKl}}w#;3D zIoBYzRa1!l07G3}SAM&mS#&in&ebjH!+=;mt*JhmJ`C(nR--Ls%oY2*_((^rLuvH{ zPte>_BglmnbIX|zL18p@?y+?CKv00vYn%gVH;S}9=KciDmp@g_XtWf~y0L`P9z+wC zfl`!tV{^)U4R8aqDEY>tDfuhFy`VVf^i9(K2%fyVp!5$$1up~~`LR$`V=*TBRUx)+ ziz!A$V?p1;j0IOodzwN>1CYqRT4Z+v>_XW)4<-9VT#_e9>(-m%q%V85CwoF2wr&W7 z4De)MCbBOFyp*zEIZ!rt8i3(f!SKOixOZ#U&llbky(nEbSPZ`nQf7b}Tr_m3HMk`k z`wZaYpj0WvNObI8!1qAXydh#jY(AdBfr4YQOy~sRru)*}N3pD9eKz|j2yX#uz}T;jtvM&g16}}1ao%-2hGHHCTm}-$I*Dcb06$^YrI-!J zh*PZ@zP(fr)aul3aGOAnEKkR$D6|LiPY1<+9$;A~q$so>;Mt&bSJMWbwfc&QQ~Y*w zmT<1{nIRiD9{G-8rla6g$u~cKu|`TrT-T0t>}FNU5JR<(Y-z7+5RoPyPB#Vj2cszz?yLV<8LMh@;|&qi|zk zsnC5Z@nZ-#8CLB&5#836=7;w^Si<<14m(y8KAp6;6?Fj4)4|kbI+!|72UFu8gQ@d$ zFm0s)*xZ+rOI+z;845rT00n|-H5X%_mS_{B(Se}jr&eO5LwZBRacKS#4yjc@8d74#Z-*3>KP|**s0)` zo$=SQRQ=$FPDIbu=I_EEUHxsv+ID?}^J@^Q%?G_dxpL7<);OVxzwgv|(O*|{H9sAr(ursP5e-0=P}W^dNl;^f}0KkHVP0=awI&j&BzYU_2RR<_prIn=b!eR@8l1H_Y7C=ovmU|tPEOjdm zQqmQty>!=7t)x4+PXJNL(gTz%)k>zL@EQ=5EZ0gF0nGD!KCjC3k8i`%(A{2uc=cCHaf+niB}$=4ika=KN)x;2yHhxbAV z=1kj>^xKZKX}g!AGNk>sBki{xX=!`UF8X%mJyDmFC1wj+)Gh7(!Ow{r=v?`QX#E{zlhV}IyW9QmS2|0e}j>#c+KBlF5KP zDsFlYeiMN(8BETpKwX$aLy$*9t^vLZB$EMoY^_WNq-oI2D7y({xevpzYux?^8CDT9 zhFz0w7yu1hf~t3d#4zO1usy)LKrqbYIQIsv-jjf!{Mc<3~7HjkoLzKX&H{fewavCxxahDugVBV z(ie`j5$>fZ9BE%T(*9T@t;f0(gkR}yc|(siTmjAnmtLtk^;iQ?;W=n@Pmpve z@~HSG;OQWAsmZw)Xd!cG2=ZviI^btOmir6Ny{Yb^gA{-5DW00G7=Vg5qw32bQH(q) zR!f4`zmW&UCZ`5+@aPI441|s|IkSOoVGhkf9?e+}d@o4aZ206P zK$>Pfjk2{M%Y9)C!UWvXgUtKHGw*U;<8^Sq0-|}yqIsVIegG2lkVo?(OW_0o!90^w z3RJ=znuk1^*A}=nNX$c?e(Gw#!FEd z(*DqR`5bqosl+PWhqhnlF7$+7mJyDmFC1wj+)Gh7(!Ow{{c%THgr~p7xKD8Rd%`DV zgd^z-N7@MYQWTD~FC1xqbwOGS?~QJ`)a{dJy5-W0a3p=HV|}sa)ME`mg|DH}FM^~?kw?XU0{;#|mztcy z<@go_NV*hxG^90fD-d3Y8e>^w+^GjCZkINS$7Cx8pyJM`dK5?$Bae#50$&7zVv}!Rzieruw%4UjvEy z?ruw}{}%X5P-KKv=`sWF7DGcJ&`EnOMgk58iH1(vcX1W)WKf+4 zjKl%BeT1)%4?o}r#J^to*c0d;)#LDn-kv~i6gISTXPO$g)i*ZScrYaHnReQSc@R7Y zL>t;^8y*6@A0#%k(>A;aydG3%{{PN~))^auu%VT^($om~HeBP`&`R5|3xcTL1IHIZ9@?55*t)!_5aF-F#5sog>Ny|zd0*6>GOyFYW}He2d;YQh2NY)&ZasL zTMVKFzd2=`O~(MX1BnH{Ipv%@gMj;iFqxy;i*y~(WKh9p$H4FJMtKUa0A32R+}^mz zeCJFx>N7WjmwD>HbBd_`FL3V%QT=yLQ>rg|0IoNXsQ=DsP4#VnTY)gu_BqUue&=c3>lCxbFmMk7QR`mkFlwCwco|5v?sX2Qk~@Ltf}r)& zjMmSz)<;qBAjnEK9fzNoPSR-Yh>HAE)jevMuHNp{pw>6Ry$M9E+nr|A`W@gGAkn(r zX+y2KEAW#X1g$$otK~+ZVZF2GRXv!8fr)>r`a@`trD45OKn;yhtu}}n);mq8p)cSW zAknbiX+;e)fUgH(FrRnC*}e?uE>P@Oqr;oB_K5M;OOC`P?L_%j5c;|0c(_@eMTR$X z>o~lov1gSdOV!U{-v^>Ss~lOX3Lk{85+wGla%8DG3b-u@Eq+v599wBb<0*vv(Tpdw z8E1j<6cEg~5?8k+PSru(@~daY5^cuG_MIfrQq9{$TP-J$L2{BY1Z8bo{U(Dn=f zJR2nT+@bBc26!q6-EybymWsbMmEE#Xo3R*-w}UVr7sHGh&c0XlY{{76;1QpgF+-pF zvsdDQFopC4da|jV?%#r@lRD3)i(VwPMq;Ph&GJVXU#0Yn?Yj3IDOVE0lpgq z8!pv0tOa@u6ss~i+G!QiClj?jAENwi5bQ}{O&sPV4R7YGxeWHmk~~b?6MO>fAlfrb zPmhLxbwFazFg-mgfIEUPJw}*y;vt-uJ)C_n>yb|>ZH}rA)-f&ap(kK}RO<<%h8|i& zHQ+puXy~CeJP!N-2pY~14bk=N>;YD9Mb|5jPqh1zI2R;de?mtF%e-+Sh=J0mvSSLoobAfLK zMdxZuyJ$z@w5^F*vH`yWiET}^ zZSHFP5d??3-}la#;k@hJME+~rVXck5Gd;7@ zmfMVNVBZ3wF*|L!;QtHoTaXyD)0PW<@+r&-5R7?OjER*R(O4Mdk7m5D&1ek9`XIQT zZou8n>-K|C8syJ|*X=a#0r=zYbz5%aD^`>sNOIDX>Vz1XiQh!TdcFU zJg0j$bN6xt9?{CxVx3)+axa9^K_E6}o!x@>7Bc~F07+xk*%E=a0(dzHjd@NtW{(lw z7&Z9E#=NN2zY4}@K`^5w`t(tI=0RpW<(cuQHsh|RaZd)K8INi+UIcs=BxXFS&DaCH z8w4{R*Jdm_gc+-~8GnND2N29ae4w?=t~9(y(_Z|k>Mr;_CcUvtoALHDxR(RbjAh!4 zUjcsri5bhZ83oVcrzQwy+^5axZ$vlJlBG9RXfqB6V`C65pG~kn&9k?x*Ml=z>n~KH8T0Ix9F*gc)d?hK%(L5dM&6~sV?kJ-=IcptE70{IxqF{) z%eNDs16~Kh*RwDf*V|P_eJ@}AkqwRd>+M=p|0TFT0#W_-x)&1b@u~nw)L*ZAp&4)! z5PIQ8=>^exqt<#X3Oa$X@d;+)M0@=@-Dm_4$)BoT;%S|zwe|;h9}u-p)LO3voB|T9 z6Sda6ffs3L+i_QQL)D+aADOAklWA z*7gVRuOMg}mC-iJ)AlLacCKCitZv&bziqQTZRhF%Eqo43GKkvF)dSiK@Klg!J68|r zb--7FFreq@0bK+%4~G-y9z^x0 z>BiQ19zPI4qW(19*bcz$KxpjgnZ};(HFi2qqGRltPyfBKQ#@_Q=*ISfh*Lq-c8qT9 zEWjH;qU{*n*cXA-p`c}HJ6M!!Pp|Pzqjcx5UwmDYe2KG#&Ewf@=g7{-uF&fzN>u(lB z+yJ7s26l6vs!sy00*ST;_7NPjw}Cf{coJlvB%Ref!l>5W`%#ul(|05NPOSDRrXuX42ksSUWV24T=+ zde9pKl`=PEEb+Vwv?Qr5ISJ**f-noNz_|RSRvJq(*R^O{>-@mIkHsV0(2vD zMj=OD;93TB7jwoUM_%B16=);KaxE;UPpf^awSj|t11Dihih)mS1GjxvPR8~Ue4XXS}JpdnyLaN;Ask}j}+ynL(K~#BzR(a+d@E?IhZ_&?BweyYld&pgNI*V1%UzijdS4iMi@i~kmazSQFTYw^J?u$<&R4)J(3 z+=xHYxBf$jmzAui?xH5(t_xxtdg{?oy^0yn*i(-K!rVt#y|nm2VC)BqRT#P13)ES9 zfw~IilR&u6?u88}s3r(=mS0{c=mznZ*9m$^x8UUfMR$5Cn`@Qhka-~p zDm!4Utf^K$rdxTg-^!_;%9>i`{a{}VqRN_D<(q)7fJ9|Yt@2mkeW2(Yp2|8}W!txL z@c}{QMhs6>RT!0-j{VV78Px-J0@#lMQDs!m!%=`kK%z3LXXEX_H-Vxrcq)@x^$r$yP)$zE{jxrs z1b93sw$O;XPD`^&xcvUb#djo%`+%VMU9|PfEsto$OAM!~-svg+Qm&Mn!2Aq|iocW# zWzJSK5+sVhlcRh0Y)a?} zHn`*9SgsXi>dZz^vsTooUZP;FC~&t!3g`&jDXx>JD=Z3|*MmP!ZS5402{YL-d|t^-^Hiq7-eaX;Jf6SB5~pll`PqTR^h zpQ?VtQ+7d;r{G>NZvs)-1@(D3?(!}^iUJa47aYc`@j$?HK+y@Fve8sF9a&d_@Hmne zmVIW$MAylms_uYuG5j@(mV#>$DEuqHi%2|6;9w2kgT!u7+XR5st_F{PyS(JoMCQ-n z`2hr7jMUg;p3gTFO>EaH>f$S~NAAWN2MY76b*Ce75<$2Co(v?0fpXFSS0Zr*L0y2` zk(fo$4B#OomJ=Kaun~z3AghRRDoGQ)l2KdM4r*^h(M~XJ1%>Yh_#TO`2vz{Z-a`VE zvl^fQ5_Jil2WXGP5d?1lR3dRQ!7hLckQffKiWoh$-l)yoC)`D~r=sX`FkK1?_W_uV z#0-K#01qSa04V1ofR~VXj^HwYT}W&rxE5d^5?>SC3gEnt1rKBuF@kL0L23(eD-r$< zMFn6=g2MX&nj_JeAP5oNkmw4^$pbhKiE{`_0LCJ55kVQi^+;Sxa0Ea#67xY;5#u>6 z6Ksm9B7drS2GuS{(Su;R4-|e3;3Xv15v&9F5Q+CdIhz1}LE=Y(tpL#v@Td%w^C3V( zBVMq)h7zl7R5>r4qqXF(f;x>W_ z01qK?KfyHs8<1E}FcV-867PeoBF0$nIY{m0RBPvA7J=y-P&f_{-h*o}C|m=e5fb%4 zIgJ30M&d|40zmt9CnB#=rjC^kS^Q!M`&Ocfgmk3--nI3zpaq;;Y73 zVDt-?O21&K^t+Wxzg8LVgPKaeR*4e6R;l!Bl@P_3DwTenQt8(zbIu2Q4(o)lL|e;2 zVb1lxEc;ZeXda#PLd za#XWb`F7pr)=E@h?OcSE%d7V6nzNC&9{sl(qzyZOz?`2y$zP=t42vyZ@1Yo$NJ zMFW&O!n%z(7(N;6$Hl`2T6ZKU?+BE&1O*F_9~fzoCvdU;H%$KVv~dRFZzM ztt4N%RMV6_b`;5?V^sl^i+B@ ziXYt`61^Vs+be!^duT50tWZs`<_xP515Y=_FK&+znU#tk+`d54y%fKd$T4b3*(KinM92X%p?5pIaaGa$@}2c6GE3(jhm-k8Ljy;Yrsj zO4{ASd}p_qAKSK17sD*Y*XS#SCuHS3gJQdfLk$u3HQN{Gq=CQSPnPj4wnLC^vkPsC zt3iX^+W;*)56K95Xh2O?#J9F>%xf#>cDCpJ#N|x$af^y3Dn0K0NQfE-JK11RKCD89 z%Y$XN$1zb@j8*hnKeLuw8zr?=l3E{NHWT{5Ma89YR>@WzREc8Q-zTT4&c#Fy#o+@l zDIvopnBF5nq0kS@^oeV=g_Q0;VseJ-A$u9hmw>DknL6w=OkM)FiSpOEK_iq%jaA@&m^DP~xt`eL zAhzep9@A4`SmQH2>M?Z(Q}2sBrkB9*0+~d&de`Xo5^1CRob=sZjm_YGoi(EOn01KL zh4*qnya9d%j{KrpPz?y&<19(F4B`t+ZLH`T!^`ay_8Q*FkTJ@%mJ{>enfyA(8cSNB zM=ZN5O6#6-3dT5*=A?I+U5b@iCF*j-ta|>J560n+k;`WENi0sXY>tkQ151MuIT<6x zzq#$OY-WA=Af(sMLcMl|^xBDkTsuR0?R2q*U^$a*DOLQWSG34e6z{ zDCA7SqUAg<8^F1&<4i_|J2L-P*3nC>yMXzlpeS&4jm1n4#(itx)`GR<(Mx+sufPs> zOAE$JzD7w@c23J_*8(k4+&L}B?3|{!b6QUO)YDN^SKK)*$LyS@xN};Lw{x1a2y-x) zC^-rOqD+^Ca;H%kcTNkLozueHIV}{6en??q?wl5iN526`hPiWE$n2b^_-W`+GWQl3 zR%$uiEiITV;hrqTOgoRGMZvt{d#OrxP74p-L{(dG2oFuNrIzB(Y2jh{WfzRZGlQo4#l0*B4+0_#c$O_=8CjZ#hue4bL&uanPS*( z{qj zxN}yV6h}k(! zB})OG%A=cnwc^fck!NF@pl7{mfvm{$sTUw^qvFnKk(Z>|TNHOri)>7NgOVMJJEuin z$@>jpkK)d0k=ODbgq+V6cTS7Ep7#jAKDC~7WqWVrmFyVPK)d<_ys1- zR92)iw}1@~^XoaeX*mb1Fn3PNEfm?IFn3PNEs{nj6?aa{Ef!F$xN};r**Q&d=d@h2 zbDHALX}Kl2TpY?2cTUT#ohpKwa>bp~a_b0aueftsZe0NtiaV#}mWl<~qGleZcAS>E zJPDlk7>C??4$mQ%CtsvEJc=igoKWG>ak5*AJEw&&(xzf1LKDMPvf?^TVNm$R!r>G+ z3Wp#?PXgIYSk8%Yl)qFs9l)J|gOqc=OSxg<&S~Kdg%6VH5s!(JHN#{$OK_5fUruZ% z=WcR#hLW#5PCWYp3~wy_h2?*+oQ=%I^f2{@)0|h*MaXeWa1e9Ky>gq}mNTpjOq=2z z$<&!lRC3M%Ohz1cP7A*tA4<*(JvDKknp4LqrpkBI4^zTpl&}F3xI&qF+&L}0JH46Z@35R* z%XLa?kdCHO%UR12eosr~&S~NIb($}OhCfJijyWl&_h>6d;=~JosMFjzE&NfsE#|k| z0SD2-g~u*KwTvpu*^cEo{Binxa`JQ#&IvxJ%%d85=d>_(PMeRCnCzTZQ+7^^>7COO z-*Gx)=QP%IYI0d^mv|IucQp>ui*F*K6-!Fm=$5b%frel|GWikNKf@uHD^G^ia_&V> zM2<>3IJw*q4q{_tBs7zxTB6GYT5cYM)+$wzPU8K+&vw-AlH{pusk}0P;{=2hw@!<6 z6_8YSU?d~Q3n*6HIxTX7fKtV+(;_FzwXIBX>$FI>#JP}Gu68vBI7vWz#jVpKCkv=h z+&V3Cidn(Abz0<9DXCQ4IxTXVi0-4fby}pmfc}bGr$tT|FkEe>o=O2#id&~edI*@H za+(746lqfx_a%y)Axq+P#jVpKy`*HOx`idZrOrIXtd86gN;r#aX0 zs&#?W3=<(z85y|*P;u+D$fy_>QcH2`w8&`T52-yAbx|Id(4^wlX^|=c#fl$5iHwmx zELESft;D)c-fuc)TDLA zk%=GDtk&$L%Z*vwIxRBEn8mHrB3I;dQBGRUIZc2jJNyoV^9IkNsl~q`?f!{Fkmp_z z>gri9D+QtDG8rk2Ty-{(;?`-AtEB@iHJN&^F}?X5&Ae7XQgQ3F$aV2=Ah_6alDv7E z=9JK~WaRqk6m&h$x*H0XL93;>bz0=c;%xx9d3}iYE;l(tz~)k^OE;^_0dUz|m-XE} zG>v!MF7M%;Eg0sUd|77Q*HJDfG4H!|b8Z2*<#eR{Pouz0vi@X+&fqA* zd6m_EspXDiReEot9h-3XH@5{2QSshH&-gde;^>I>j$DZ*Y41(cY@H_Vo2c13P5d`e zvvry{aH3}GH1Xg>&DLq+!ik<&K*`#N6CDt{6{58hCps{8CxG_iM2AElhfeLri4K+f z9jp$`Vc)PCOTeZbIZ?B9ns{=eX6rO@%b_nP+PN$YGGjl!@OyU{s4 z0dx!JnikTR6P=&SD@aa&&YWmk`73~fp|O8?r5alMNDyoT*}L(&2|Yo$a3#TGH*0ev_QV44tI__ z9T|ytj)Z7zo%S9|+>damBU`6Mv31(hSiW*{doa399&%ba{Me?qaas;HPV@FngZtxW z%@l>_<7XY#l+2ma29GmzenAesao(0-v>fi4=53jl!!6UWV;U_f z%i)LgybaTG`2BHjzqA~Fpd8z!u`Al=aJw|@md1HmksLbM`nQsM8ob4}I68jQ>Nx5q(c?EE1x#Hz=Mph~1Q8^HQ3 z2#Yozb3BOu;J@H_ z$mfqpIv(oKW8pa-;s-e%>i(ZN9ugVHL*oA@$3r69@eu#d91o?Z(+iLm$HU)$)T`un0=?!y*X$u!s^k3jDALmiS>2?NQ=~MT`LO!y>BDT0bn}E|mCT z5qG1+4~tj|;D<##0N{s3Jb*gBcY#11J*~p~uJzi)e(hGCwS$FHpH37SS3g6Bc2?;0ix1 zg01Z4hec3(r5_eSlnIL<>hFg|5DoXkB8aN|un5kPOjyJeG;gZ%!Xj8U-4Bc4SKSd7 z5k*)8MdX^W2%ZF1t_h1^K`7URMX=F+SOmd;5Eii*Y<^foT}bo8A_)Aj2m(JWf)?QM z(_J`qQZ&G!F4u%b@Eme@@<$vM{J zyvgTe7Z^?x7BP>U3p~zM2XI=h35$4!OiYVnp1~r-mO12_u!xVyge#$D;u)ZsM5g96 zVG&W3IVl_xoT)~N35zIWSqqkNtuwZou!ycK<5m>XNnDAv22MW9HDM7PSl5I_jAMm~ ztiTTAYL%&AM4PY(c9?6zA~1Zm2 z&y5rAy|hR*VG)uxVG*2Tt_h0}D@<5~q>Xo?6HZ~L3l5@%3y)onY8h47xkHCV@ErJw!XjAWhecci;D<#J z_+b$Qepm#79~MF2heZqq@WUc_n)qQ6Eb+r4SjP{GAn?N?2>h@J0zWK*E%n19SmK98 z5cpvc1b$cqfgct@;D<#J_+b$Qepm#79~MD?OD)$jghdPn3i)9XEW@SNghdb`EW$Nm z5p6ILq6mw4kb`Z)BDj$HVG-mHd0`P;LjAA^0zWK*edvcpa3%G_B3R;wMQ~B{!y+iU z!V8O_G=%G!u!#CFCyKC$FKCtti_m77un29I35(#OoU~jM7O@6(ToV?da6jS%aR~C< ztBZu5^|DeBS`L#@ghdPh^1>npOAkygMQ>hXdeekOu*46G_z9YDw`{^98bUx6VG%Pa z$b>~a46S}x#QOlac{O1Xqrm1+slyY^;Q%;nuEYAyUYf>RZijdAt_h2n3GL2Sl*>uX z8*kklz0jMmh%Lx=O<07&ndAgj- zAw);SW~gHJTkNyLaJWSkPa$}|1r9S+F*`E0gKW2}!XF?icG?j*+@*@C-0jjDLBl=r zjzvI&(-ntcV;uT{Y$oi#sGA4Ny$P`?@t%1jknN1dAu-wKBCD16J>*u!dndafQcv}7 z1Mag5n6}if%>equDJP_kfi3Z~<5AQ}s#@&<`X8i9PL2M*C4iS!wW zv63??p`A09dIoh9wIxEwRU2CZlnMx`G+LLaFCePQ&?AXPxol8UjfIv(V<{_@rgD9R;mfD0ge!` zTJ2!{b^_L`b6CH<2-~Rcqn#ZDyr$+;Kt};v5PwR(W1P`2r?4BW9gP<2lR9-ilF8PW zqQml-8+P`Mz!YYSg2=3CGPih{ucH5Inaq`5W_xg!n9P}8=C9zaZ8ED&X2EuLf5=XA z`u_-Zc0ZV8734!ee5R7@M83(kzS02VZ zQq9Fl>G}A6;)NK0s(Mfn`jL)GF0iLTa%)f>aa7WXcYxM|YKxK-g`1$o=@X8Wq|cW4eDs{ z9ZUyB23k*)FynlHzRVb8JyDk#7XyrDM%4i8NvSgp=t{{MXgyh*Jga~nmYhM>ll7SM z6wn%wRd)zfwkm|mK^Kj*-c7Oabrie+YW*u;Y{TD9IpWqArWKcA!hI^!?I-Yj4=TL_ zGLrm|%onxBg4Mb8$tj25^{K9#qk6fw&YTj=!E19kQLQ!{uoWNcLST=k|Srzub)5Z;%rYh=_yRV}>jdnW5Pa6+%bD$|bY8gQ#l z{3$~X=~SN!XIoB^ z_ME<|fy`g)C@wPhgYv8n{)>x)YiLh>vu7=<6ltsWx`^)fQge=9Yw*6~4)oOL>K7O5 zX-|EwesK~1K5dMfGxO5-2#CtnPI`Gxl&hWe^5`d5JLyr(7Z-E&i;MMrCq0*VhbyG# zq`#H2jFXnUUb#Fc{aW&9Cw;DdanWZ0eQzYWrg=&<*k^h>GBbJD*-U59#i zu^BFS87KWNuoZhw`U#kbo|FCzUKc$ly|n3n|L$TDjPafH>|x(Y-v%YVlYTIO@1&oL z*7{ER`6%(7^mO0*PI`LmeJ4FV_P&#zk^jDv{$5x;)pOEQn(w6V1K>O9ZwBz4^mO?j z+DXr5`%Zd%*DT|te*=2fdrtZdkmft-*=*lQzYit8ll~6?-$}m`N_{6iJ@&qno*sMO zNzcK8=fF7WlR)qs7$T_sv9Mi+pH%|JR$Z_bY z7fo%va{cb2=cGTDO!U-?l70s;8F9u*KZ2a2JTGFKeH={xCH zlZl>sVd5DqLiAgQo|FDPGSO2nOgsZLlgO0W<{Bq`4(6vrPko9r)krZ;`UWhcr#{8C z&e&?4^vAM{o_gsdu0&eH0(7bGr02kTPWmcVpr>9;;%b$tU_=`yJv+>E(sSx~PWneE z;Ymu^03BSROg-bIe~abYS+;-s&ko%H^@ zi>&E6>7PK_p{HJY@f{?zVo6CGow{s zc<$9iLeF|xDF`ix$vB+!=K^_7`tzj+jFX-qq>PiEz<1K`gY=A(o<9FLob)$Rka5!A z53QE>?joPBWt{ZG!RAn@!xPQn061)}!}`u1n#Nmhhj;Owlm13%cj$zdlbAQ&x;c8G zH%|IZ$o8D{e^3@Z^|Ff8L?WyGQp*_!O`em!85kUT>U}4@!VfL&!tNcp7U};!Cp{(m zPWsybd?)=~0KSv{ap?4&^t|R^d1x+9`uo84A2{jPfGy*sj{!OK)E9eB`rZJ#g>y{{ zjg$Ua6!=d1%>V~E>Em$HUxtF1anj2r@z73sYN^TJd2DIp4oKj`Wb=`h2rqfTTzly$ zFGZWh`HVMRk*qly{IsA6D$1E zKcT%B8Kc=A`*g-*pU!yfquCz&Xtu{bn(eWVW_#?T*&h3-@z~RnvS_x)KAP>Zj~b6X zyQO{9c!* z3D0ByJ4EHzOQP6w+us6S&uxDy3Y;D|;&hZYG=e8e@9OrTzQa7?pxOm98 z?N5h4)pOf3?Yr$M%y-)t0QhcuviWX%viWX%vX#Y*+n)2#ciW$hI=?GsZhHc_?b$&^;ArCZI%P%pGdQSb*S&<1{rzKJuM?Le0l0mW$^Bnb;d3CJX z{6z0~MbV3X(5^z9#PX-AmxHa@5|cd=&9G|U+=cgVY-E+mudlh`URMy+T)g_jRkJFI337Bt#;Kst)kXmAvm8#rmiIEmxQ2-L{C+1Izn`i;1Af6e^xpHS z-$+R44XP{Ndn$VaXalH@c<(to{tfgMdHgWr;Xh!jI8Y%CswL+90r)E@Oz(Y(xG0N% zgo_+h$j4Rs(bcJdCo_u=t!j(wyfWT+F^i$CKR5Hemvg#I)=Wb+81GSPb`<2$@q~w?Vl24tCt)Agn50AIj z)*TJboS%c10$IroC^}acJ>P&uM;L3*aze~|d zWUl~)2Uz>%NyAG3>zFan+TV~Ju@B&DW(>0SH(`eJ3wD%Y2A*L2A$5w8lLrb{4YdAf zM4qld6_PW^`lBgx?gY96WQD4XH<@!Ta+Lu*gKmOh{DdDI)sfj*`S)h!FU!h*IxGLF zto(Pf@;7JYf1Q>8aaO)tl+io&3w;7|qSumq`sgFyne3l%hIH}S`DwxSLDf?o8&rMN zQC8%bFLmu1U^~aKU4hp0GWipbe{oj+gC?H~+AUdPd#NL=$PZbCD@@^7bdVU-@>{Km zHD5WPX1NvWYYN%Jp<&rzY&MvZ4Q|W=k=X`t{jHDB+L%!Zo%@~EDuRb+gG;i()NF85 z7KqF-fPyd22#%~Y`CP+iXYxZoW>pUTl?`(DWtmcx4H{&DNHYVd@M|M@EJmb_$>)f; z7;F9MNGo(sR^`ypY;bWln4Ar6$OgmAih3TOpp<8xv$Xb~vs?g`0ZWA%Jcqkha@F1y z!4ZEeP2G)3HbJRIc^}mdt^)I{t?ma`K9oP?bDpu?N3KReZ`~4^6VA z`c(*fSUx|47s8JWdW#B5c7UU<_mm|x7*o@G%yKL*<%fLCLUE_cOe{j>-@(o zEb$++)J2K^n57cHf6Oukt@R(XT!#|>G0Qb5@gK8X58yv$xf#HJ%rXmg{KqUaVYUC5 zh0^@TEc9~vk69RI>_29i4QcOW>#0N_7nSq$)Nke`U$B7vgKwnrA%2klxhTI-225673XyM0mD_0ABB8hK$Qyen~)zQ zIVUElAU_HDQQkOo&Quko7v~cxnXZETAmrYHPhp37@qz91UVPw;_g{RF4)9)lU_bjW zJ`nr|FFtT7@Lzo32gLkmDg^#B6$1a63N3)|>JFSJg*3pWF7JDgJYig(YSChQ6weqr zd+gD1vg>Cm_C+!x`1}^1sn}JHe3gzbKG-i7o`otdzxI|x(R5@5(g94y96nRAUyq+jPJWwA zILG>&>;l8dXDasQ#1wK~<8kugidL*Ui!VFaTN3w@={}E%r>ka?IV9hMwBJm;LZ&x7 zCeC}!Br-K8pQ+gIBysC)xxeC&<`lMd8K0@xTayXoxcp3HnhTq;mCsb{ZRut#E5|`P ziOY`BAf3b)AMEXE4y=3+(%z9cmlgQgTTtG@q&1I}_|M`5vUbE6u4R--EQ@ zO)sQ`dnn;$DB*Hs>hYP1y*n+>;$L7nJB*8x)*v0tQL^NFkoJ38>cTa|+_p9+tOZ2KqtjF9uhs^L7ypRoeZij+Q%kkq6-xm$@>=#biSG`c{1 zDvtoz?o%Nt2cHTP!KcDR@To8nd@4*#xle_(9(*d>kqAB&CW23eiB0ZPQO0dP6_x~_ z%F~euJ{2Z{Plbu#Q(+?bRG0`pmF-9bp9)DK_*7UDd@6h<_*9q(J{2Z{Plbu#Q{hO1 zPlYAHr@}<=sW1_IDog~Q3KI|bd5}y5p9&Mfr^3XqI(Q!BIsLrP@(%8MIInLz5?y%* zPlLQbN+4J1P`OYN$yt!&Kmu-hxCP?N{V8Ok!JoorgFl7rGx$@O2>ukpFZff)MT0+u zCBdIUh8X-QY&!T;*c$x&w&|fA-QlK(FR)u{rm#r3wx-=Oe~Na?{3&F}@hr$ac*f6y zq=TZ3TM0TzcYIbcUeKONoD~fA!A%cakaB+t!57YgWFq)emQiZ)58u_YvW7XNMvoCfHn@djnB7zn_Z)B-9~L)&VuY3 z5*FL`K9maqQ^VHb5XO2IWOvudyOAwtL3Vfd(dE*{%@M*uN|aGDbor5|`riC!==SVU z@~KfT&-y-tp48@#vflp8r9rGDylnat00BNhF?eLGefh;O+%4;6gXb}qH>ErHL#~qb zZG);+J3!1k&-15Bce0#%N!Isq^r7!#^0qgIa(N|x7G^_dTnIY`U%&e2=n?+5$uD5L z=PJl?9vrx3E<5{fm^I#stHO^y94Tts=aKv*vp#^VqeOH0K9b+YRXY^xza7?Oe%pd3PkD&Y)xVoQV<%B~!BW>~OfvuLw;hc>}P*8qtesViw4#zLzpqO_8>=oVK zKB4c=55XjsttShA;6HUhTjUWA=tPJo4rsj$s0ZS@3)f(6GoDyM7)&EMmswNKK-LDf zas-lx;F4hjW^$ntN-$6ot@1rp(c9= zl5fE^NE7AE2Db7QBtMHwhPG6O_B$m1i&<}2f8Oc}U5*StmU|&M=^SH+7#OkT?!Se3+-7t2M=;A#>nSx3J^EL0W!< znlT+5Ij9vQ&IWuer=h}Xt8_i8N~rPZCDx&HP?F7jnM&J8X}HYFi!Z&5iN!?iYzbN7 ziBp)WY-zAItwMELoBsUcs36v+pI1;9e>WQay%~Be|L#7%Xl?4&sNSV&ZR*yjUi|mq zB4AkZIg)OT>YY3bZFFl?Z?^{YqJ#mx-5Sul?6Iv)cwMkIEq_dFQ@2{1x|8pbn6+tO zU)HA2LM%OAYtz8qtWEt4>V>uG$84;hLA|gx{Thk>eg^gK+uF3E(b_byFKZK#s%UMR z0mRj7?`D5sXa?)(ys{TTv2>O^n+no4HvJuX9gIzEEf|}gh(s_p{Us8?*u;q1$6;*xBDAz%Z2AJ;6O2utKx?gy zO+Q9SFgE=fiC}DECSI-rk1FSeN8 zL8Q#!gzu4%_fDzB3Ev}`i?YRPa>DmWPC{z6+MMt`lBGzQ(Fxxp0k^|EiPZ_;BjG5w zso4qNBjMY3sND(QBVo!6Pxu}QQ)jB>3Ev}O>H;-A;d>-ZU8c4te2;`UV#X(Yk0h}^ z;d>-3dx4st@I8_?==QKb^&zI0P4rt#?~5?BtlwgKSgA zMB9t;Bjv<*DYr^ye|iTiz1u5s8e=7^Y4)f4So2$~Nj>aGUNhLAe#P?Nu$<8C0eaZ; zW`7z*PTL&(NO#tFx$4?(e>#MfHnS36^5jA#YtHOX=dpGX9m2BB_z@tH6X_eMwdnUqV1MGoy8Y=AK5#i7Ai&72;se&S*`Ek7 zw?7ee-2QY2TX-*9_%vD|hqC9){&X+Pzs7QcmV8RzAb{pegZ)Wb{qAB^_?Hp6H~SND z%Eh#z?i8Bb{m9*eI+T=8tAzdLd!Yo6;huMRbdb=k__EkdQ| zm1fa~yDA!^-h^s%#Td0(j8Svd7&Xs~QO`rMf189hr#4x^a&X+gubq%P8E@Mh4#;d# zJb$C@=lB^U7m8JbEsE!Fw9)V+Bjec%T1oD>YLsNwHiSmkh%JhXeYZuCtb;9ziC~Lj zBG{ss2(~CDf-Q=4A8b))AQ5a)OaxmL6Tudx+}vzYED5%#>yZeyC?J_C?nHr;T2*(*nb*VK9-zH%h#ID8nGL_h))&mjxV2^qiC)?tC z$y9?qiuHp%itIGlqnK!Ik0MuXZI2>@4E88Cy(zbEld!dDkD5Vu`e2XxF}t;9O6*bE zEwe{yx6B?z1|98Dci|bgM|D6qX{+L=i=;P=8^wCz8cgC#73f7D>`~_-<@TsE1OgV{ z%S5n8F%j%hzs8%QJ!%OW=z~4#Wo*doQSZT9gFWiINJM+ovrw&#FKy!}+BgAi9Bvz* z--|u!g?M}0{U}!^h#MV_GJVGP!XEWCWV=1811qn#Dt?47bK7w;etBx>2WazD0%7Q1 zvG_bw?rwv7?-IGY6(Z*f-pS`nY6s7p$`;SS&w!lMQ9E%3tT7V1S9@aR?{TjYz_s-A zCAIYPCAIYPCAIYPCAIYPCAFsvvt^9{u05^xQ6w}1xOP^J=VfXHaP8b4+O;$SxOSeb ztHD5)V^Mwn*w;`^BY-z_ph?2MHbF zD{O>}0It2V%9AfTJNWsM+Mb?Ak?3mc;O9$f;qxVZ{k7xy5fwQ=vi1b&UM3O1wZi91 zYA2NWmJzNaInt?vaD!7sKZSe!d+R}Mya^}D)8v&;OJXuBA9)p0OYp5)cu${% z`(-2aQg)YbzX;@pzmfmI&m}08&X%+4DOaNp6Hh@-cX`Sx?SC*W`TG1OJh9HLpFDX(MO3A75kUNrevbZ8 z=8EBxBPfNRY}QXfO4YM@q(q4#GpkIX^ZA6tz|An1@3o@upl?`0KAO!sY#tzd9#7s-#j286Wnb*MiB-08*Bl_i(&1b2@awz>_y{Ck-amho zZGI%YT(pSFKX7^bfdLkMjYTrwl{Z|7;eS|$|BLWIGV&KJhTdDN!ce1Q zCUoi%8fAzG@*k|MO8~>+F8JxU8n@^$aGX*BT)7r`BGdxM5&{5S;3WeKd`HZXmw29N4zRy+j-9` zR%O}tTyVd<`=MPu??C-yJ^k>m9#X(@`s(uTU+t1$_T%*vJ>}i^?vi-+)0m-?vL5;dglYo4Xcv!UFXIS}XYX?(JFj^8AhH((FOxe-IshKd!9v&kS0e zn14rcv}ksh4~}+AjtCY@c7)bnyuUp^t-Sl;T{v;ZS|Iv%e%%s8{$6l$T1H`x>y*#9 zq8}usMUua>)b+ls_CD(%(XaE$N42*^t)2QX413mztaG;&lZ3AH;*0CV3clzB1n+g; znKbb-uk(t~#9dZQ+I_neOW0%Qza)=+!5=$1Jk}w0KAgFSH~-L!cZK3!D`u-@;2Ld> z9Q2$deJgY;8y;R|E1kt!vWcAwe#ptVMnJP+-pC^@qxOgE~+H)eHDz!0vN zy7CXXZZL*eCDl4 zz8=@u)!8S-I{pPDK7nh7eBqPA)yn_7Bl`{OV6r|XTwxw^#&A_h^`8-f7+`pbRhYKM_~1{k{A46LDYbtkh2=r%{-n=%ajJB5r-Y3lD~wK;3)bQ62VdQO1v~Uie8OGa1^}`36DzVWMv+e{8P*h>{S|- z{O@QC<|>U!{sj`|B>HN3y%sKQ;Nu23X$t=0Rqmr4p%%hTt6XqBy_=UdL&yp8-RPsaU4!lA&c!l_Q zZ|W85HLnm0x_Zqk#9;=n5EH*|R5JDG;1yybc!ikQF+@{2(zBkVvM-{n!s zB$zgmS`A(yQjB|rSUp4~OINe9MI{fQntO$g#eEySNu6x^Ox%=s6TCtfv;3J}PUMzy zt7KlG-K_L-uf*Alm8_4tnu$tx-x`H*Y_L_fHs2RLM zf6Gd@c_oso_Kq`{c~tUUtn_iOM7-BZ(x%oluh2uRiG%*KVIpeLE7S&plP}m0ld)M} z%_}s;GJe5c=9Fwl-*BsR!=jQovF;T*fe+AoBmg68iVs-R<`p8q+$%)Xajy`MaBrhq zyz@hN3E7c7XI`Oqv;2K5Cuqr(^bG=N&QkCSNvq}+l6&(C5y#vsB)u@NkldSBXbdFW zMxV62h0LbZgU8~lf>-DW*4*YbPY*Q(kLJ5qh%{u!zA!}jW>nJgeKf?J4*;1*&cxP_RQ@(^VbesBwILL#_@mw8jV$+-4 zCB)XEOQ;jwslg?554*Ky3Jbq$YuYVy32C>?B}DEVT|&3v8FvXii2Jr5;-`~z*9M(L zF>3sAHmdF(qI@yl-p1e@Az-T8IvnNr<`Q}@ zvfU;0Alv#OA0WGEqrj4(%a1-a4%1A}rp5gzYa7AOtY7ux?u)2i8_g#-BS$?7bJ|rK zVBlsxcbIJf=HKKRabB6bM(jc<4HP z=#1uvaHR7SP~kaR;R>&i^$qZ4H){4Ro-Id}WD_*b_o{LJZ7h;$;_`ZFHtMHxx^gC_*>Yl--Liq*sSlsf3R7< z?}n_$TPGl%BMpd|y83==_&@lXHe%b@7jgGGR8fo>Sc!2zk7Kyos_rk{ulIkrDsKx|92jkND5qJJ2s1 z@u#g8CwB|7(-GgFOzv*Abut>gwEI-Cb)KG8y=hWbb-{0S{4$*bQvl0F1j}m6er5p6 z0WiM^ST35&srxa;|Dc2AqLqAR__weo;UZWrHn3obNEldjh}T-vx!?E@FK_?%IK-z~ zEe>%4uTF>fFX<4^G#g^onG~_LW!r# zu%3+M$ug|f*;C@MkSGjmyU(28_^{4xzZ?CxnOl|2+|M4@u)I04x#~0Raha{jb07$g z=dd2O(Vh}!i#vd2SjSJY!d2-16+W!vmow`YBwvK9{}mwQj^7bDz<$43ZY`ZO`Z2V$ z>`kcic2s^duAULAOlH3arI;F;0(bIRzalri| zs2P+5_u9BdtT*|dBhZl*BG5L?<0$fzSMm!eUa{mi_CS_z?q2n6RJaQr{}3)<_94LW zd_#VRwGC^%{N;NH2!Yv#Ix+knRR0vN>W8f+W!9QcS%M->yAQ+boFTTkDCc82dGvnd z?fK$iW@P)*z*+iXJ}gSXTG%*|Sj^sMbI7qv&z-~dN|}?o?$Wc67A}(=KvM~??2(mt zS(bNw`*Se-kMX%4!>%0$O&%7(zptKR=U>~o41*dee~6kr2Q^Y2|2+s04{D@*$Ax%q zP$T8{KTGbrzF0w)M#`gvk@ABYDSv2}e4olFdz(S7Jz&SIlK%arrBFu=2n9Pr)lzm(BRe zlJ(_d@Pf*#1{m?Vsr+}Cy~=F^jBDRk-UyaadG*Kxz{nlt(QA0Tl-`C|Z0<&Cp{g2ZLz8W6kk*4_v4%vI%|0y!&h8@L(0zOlR? zhwyf3?FHp`!*@`5#~^J3x0Ih=N8+7>_aJd=`L}HPUBf>|YqyubID^D{hp1`3seC8q zxbhD&>^sW+Q%HPpZ~)J|uRI@>D|gj+s_@jw11e!AH=}L**Yq1+Dz!;BS$5xO_MJcF$lLWBgS`k7VFv zWqC!9WS}FbE3c^Wlct0IyrRa>laLtAD|#e@Oix}p2hDt~_e_*5&MSH(1NS_x&MSH( z13v|~KCkGJ3>z`FO?gF+WZ>9vn%mnj!T0y^r91MRe2>nlA)qUGO*+Yc}0(8I0{n+iN?8Qn}(~O@VWlf&TAYl2L*VJRKaU{dHS&(B7zJ&m6o zP%!oF6v{_XNFgZUPXFU)n z<@{`%_3$&c@Lz1&TY^y>q}if?JFW>u9pm{pa? zE9Yn9tOx0ZSykoUtg2V>E!X>7$i?zO$4%>x#oGTJYw}zM=?@RR)|!HLn}4?mT|ulp ziSvjUYtO^3*GjDY7KycAtFiW5-KzQ-l>1@YWw5D^F8l~c>$uSY%bg6kgBz#)Fl`E= zjsx+tjQlND4Tou;$O`m?ONXxvmE;bpmOhh7NYjy1dF7DqCqdmRzXT(wZ0;w+$jXC@ zkvKvUUFDnWNNkZrf0^!p%2r8?ma7!lM@nLhfLuFgvk!Wpo`F;SY zaw`gJIXguz#S(|pIZJI6UR&9%<_i-yUG(JaY6Zu ze9sO^Tvq-e<=_dDxT@T_1c{yUo*T<|LJ_T;D2(?7<%__%D$}4*C14|&i)vC$_tg8OC%WHuIBe1G6F12id_GHyrd4w}~iM*n#`~YY0(m^um z{_@Ghhsz`}T7Cmz@k~9xtNb8Ko;5-?ytuptO;;|LlGWuWRFJr$pWJbM`PppkN@;pi z*#hm^G~9l6@WXlN&IqikE1;{C*APJ0*0ft@Rpl_c%4St1i;f3r{|3+cLE2mKe8(~P z=_cKsjhlpJFbReOHa!BX>g$mze~>EDO@aVfc^BXNTuF45?I3Lew!i#3tU4;s>)n7y z;z8Ox8G8g))vvN4v#Roxm8^XG0#v_wbP)goW!MgW_FPTY+A+&YrK~F+k!Sz^__Yc^K73rh41Y z^Xk1FrfDxlS9&XbEEvs}69iY{b_MlO0PkeXX>YZB1Rr1A*ZwYlyf1ycudAN`94&ux z2A0ba(L1K#UR;ROtnzd4j2!=s<0>d6I=+YuVLRW&jZT8dqWW!ELiSI}(Avx{{5d|% z?{(tn3L0xVdhjDWa3OAz8PpbbLxUeUW0a$)bBn;hC3??I0s~jbeFp(EvN&JL?^z1u z@?{1&9x#V1HAt;0ACIDScc9B_MmQNT4PS<8 z_*?!Eem;m&0b+aB&#?0arHQv9tGj&TrB;gzv~PQ@JnN^YaKF;Tm!#H|)#^VJFFT_& zPMqj2-%VWwfBid=*a*sEc%}Nd3?+)3uD-HwH z?~ux0WUfeOT80;9wG|ja|AtWt$e*)}H#t>f{b4ez^<0Ze2TBk(KeMO}#7ex&W<3h% z4iA^4sdb?i-<8eUiUx+eO47oyUY6Bv#O=tGs4b`6HzGc%is=dhe8lhvj% z_K`^$&aKRqKF<2zmja4w6^id+HUXuetbGoH9KKVYxHr7V0I>5|^zTj5^bh?V_M#8r zvDwq|SP7y_&K?HHK8S%1%t)_CnXN;-nZw^xlGvTW9mIf&ON3f&U2YnM)gGDZfsq4FC-?tx!hf<6vNXJ-8s zj5)(2xlGJwLzR^;Sd1#QfAtI*T{decfIEDX&eSixEIaBN;P1fT;7a0u_3S<{+ng#- zR+G&V(>;gdekpSa3@eh&5DTPelNV(*hR}`ci8OMUw@lL=cwFad^z9htdJSUjB3=O$ zkWKu{_l(TS@Fhts?ADj&^1kPH9oes87wcVtzN{~;d9B{dsvTWk3bPGa?=`8+!&>pH{=2;pqt8nk@=2wLV+baP3* zP5NcD`vqM6&j$NALfy3RC0f9F3-if5SoDIWhJu7Ovg z{WoLMdcgaRDUFc#b!VOOZ~QNp!BuR%DP^xm&0|XAJ*eFNDwK?%(g3bu7R-G!a@MC0 zAT}ELYjdf)6!jK)-9FYm9I1`CIzM19Ag6j1AOw66r`=VlDA>rP_;>UoJoK^98NWMJ zOS61?X{!BrR5=Z8o`S3W2qezI%~?zwn3VKA35iRPdoixgFIki1Y}aCFoSrLT;rf>4 zlMlWm=2suJe3G3_C2;I*n8eepB+FiefgfKQ{w*POA4+!NMOU&LpGV>yxOoc`cOmgn z+300f7fs`TA^8eiV>s_S7Y&Xs&oLbKo%ekWucDK33@3f(gPOYIhOu*y8qpNa z>NyvwQKmrRW9K0;p^0t%osfjT(KfNgVBmAxv+O$X)}E`ffs%0ix8c2SVRx>Ep<8m^ zhtzv;Wvk|5>OL&e7Q+p9RlJ9P^GfY+LR0gWpjOs7Vf83;en9O*&cQlK@a!s^Mqn%l z29)K9O6qnkncKDN+L8j8BC~6cC1k!wpARN|kIcf3Hm!6@n1wsXY!>dxCpgvjqU)c* zC9`l(zKl%zmqm9D!X>-vF}QvU$! zJQmo9{!2sO*M{DUM*f}+ozkvDWkaK=RA{Jrqm>%cf)juTbzmyIm!<4sbn~Zq`&oFl zeE=n&L#v;_)%7ZSb^&ND>wKYgi8J?NeGkv^l9{_B&Kw|-b-mu|Qa$QgVa-V5jh4^} zdkG-;o}5zpOd~G#kAL}|8^>+J-jh#q!hVb{J%DS&h1rTbQDZTVB%Q>y;nK^pgE)In zL*hhSluvgm#k>T`-MBhmVa>DiThXwh-3ik9r!P?eW}SCgEpondq!zfJ97)SARlkuG z%ac2u?_{Ij$4Fi(sCS2r@UVb-*MfZu19)jZ%8|SVJ^EW*f_g8_7qj!9M*5?;fM%~y zH2V=!4=@MAxmAZViRi|0T>TRu*S}T#B&7`W13`xIma(g{*XI37@8AVjXRq_r9XAN_ zygnb|+g75HX*Nbo*wvxq&$Fdy=52ZRvDi&{Kv~6^TV_2 z@a!A&+E@APBPcl&&z+9zSiZumwPmcv{bGV?m!terw*OFz_Os3-Eth)pkI=3V2NgZb z=VWScv_$v@8OF}iE_6`%(knv;{jSQn#NRvf_Dukp@ulzDtg+2!AX^bcvHJrNy9w>A z>nGNzBJndZ^)D8c+I@|NPe5Wy`{Tsb`C^@WsBMduV)pW@b5%;I)Xk9^SLbtx9dqWP zJru7blFymex`#25En3gi)9uNDIsW9+l|rU3WT6m8HhKhF*(R^Rgz(|)UGhuPqKirp za+^N*V$8vFa0xs$C)j2 zaP?fxEWyRKNUmlU!XA#xMR;sS@(8KLERjjiLh=G!og>y?PQ_0##&jw!a*A7UyVVNP z_;N4GMz6uxr)BK7n|!DTVyt5ECS#x0vEPPQz8sf~eOkx<38e4Bg|RQuvHu%V-(t>1 z$Qj~f)H^z}*|-Rer3#JXkUSb!|4*;RGCxSu&ikzs9MER~`gA~l6>$rK7a!Ot>tt~W z+4%xXaE!mv2fNGPtZNP6eEW6T<=JG`^--%Q_;}FEq4oTZRDNT>*4!2bzqyXkG$PyY z##rG@boO*yGe4g~))ILJ$!Bp4pSuW{qAUj5LO+ckC4~jH1CC+P`0U!zcoTXp+W)8Q zWm)$6Hs)9Fu>8V$ zp!1#93X$V`R{L@c43ejxv61+r29GV6UFTW@TmW}oZ3#X%iD?BWz?S7XKOIt@+mveLd_|iV!Fcp6Vs^jj&}KIRzxd2jpbZh?S>wc!(=hstxQ@RJXplc^ z35&mn^nJJ{F3hgzXDb`JIw zA@vko1M2|nm(0i3^l{;Zv&k13eHhU%YokS?MsoZTB>RCr&x9n z>Q|!afrlaL-U5T}y611jrr;sU4DNs*Xh+X>A%B8j)qDQ%KLUE+!HfO{myGQjRc#Dz zXViS*;Dx9wWBUba{1n$P^CKaB&+5a}f&8`kle~UMwKHqOHOzb&R6kO^xH^Ao&u~!N zQJ;i#xe?OUhLJ`{Un>rEwSnWez&qj`huZl*YmOF}J=&ZiBa6G*WfvpVYdn$-+zYS_ zE<~)xRoUA_7L<42w+mr)Z=0=p%LBW($?@%?El;7Zqqt@t043s}FwXCziFXXln+9CA zq2MUgI2_l!laQ$X$k3N&PU;2JD2KTI{J9mB!W~NF?~c)(Y>P|Kr=gvv;>w2J1GMo+ z+QS{t-yV52ANdL@Jss6A#MS;OB(B2EmALvIK;kCc{5h_yvu1s#h3x%!5RhxzefKUH zeYO}pX;S-2TGI72E1@B-db1@+?*6b4yKAqa{M@%o7EKTA0-RrjNxx>1!1=Be&UY2y z{1Jikt?1+}xCGA6x9Q5VSE7l#2cD0~5;%VZHQtX);e4=FYl8C@D=1}tXE4LQ;194Z z!ug-k&ew4%oV{Xuc*NoSvC&S9LE!vdRR1Eci|E^zsZ;e z4k$ts-x_#`10_D6i5gGBC2;QAYSjp5MdYI`r?+O#Y4}|Jno(ZVLI2MR8rfut#W(?drC!(?Z`hXbE9} z!sB-tFUqq|9*fZM%4QhGg~xvee!0Gsb$!bKrhHyl@D5T=4(j0|AAJ2h)XfcE4F8TSG8x}()}?$ zLd^OvRR0Ao!K}7gXI8=`nAL}y8ZK>Y%DPm_{LbJ1q5eL|Noi~h)o0_9#unjb8kaP- z1~;p4Wu3>_Kxq7*!GlB+ELuCyuzdO#4omW%VfpVu{wXoP>)DpiIqh6w(@68cO;QO; z+RvY^(mLy0Z^hK_{+G1@@z;4#^4JIbvHSh8t}Coqw6G4U)E)O=)H`-z5s{yp-)p-4 zEQ3V8hpOxh6{B;-E^;^qya89&x2z`Lzu-YjJX@rooHY++cADzc)|~28PPJ$W)%twv z$en0>HI=!o)9k>K-$=dXha>w0QNfNH;xjupUY2biV2$e9P93&tJST~?mlLn?mR>%+ zHfOz@QdHlvRy;i$@s>#bUgdl(J945+d zLYu2KVp-m}AD8>F>_{-J|^ zvl1QTdgyy-`=9tSI!&f~(K%eQhB^T^N8^%3(v`Tm0@q3Bq1vWnVFW<~ediKW0i?c( zt8=bBL*BO%^~naV!&>ogE#Fk!XB3h!JXxA6yL0_9Q1VX8=gRK-m|wlc^3#>wel-2u zZUrX>Kt9A?)1UJT^zSFQ#M3hiA5-eZB|e}-adQB!1>dx%IVDi<;Oojx_K5c<(qVbx z6jl+ypQE$V=q}ojA>KhJ5GBBBTmeli& z?JO$I{s(>wM2j!jU;=-}W`tSocqx4}yj)}Nmpm}3mB5`8 z%l2O+h`)AaCJ3s)(8`(%cH-{DJCmq+O@DAcv>9qR& zZKp86;qWj4Y+&G~w+MiZCBPe!q6E$;)XtPZ81>djD34c-4J0A0q zJ)tU)+EK+Uo)AYT)zY!;OyN~Mtx8lpu@ZP%y_{ty#m6z>9W!I;^Cu7F3wwvE<#`hb zw;$~62OT}pD6I>MA$S=YJQeG-?6k#dt=V`_btmav>opV^m*)kksHj1ij@f@3Ec`s4= zT!DhTk)&`Q8O}{P3eK0g39RV?VipS6$c02#CF6_2)EGu78+v+5Q7IrVPO-{}_!((s zeg2X#UwrsfnK8ckQX}Fypj30g=Yp^;NS*d{c&yqHCys#b zRuE(vm&m8oSS6xZ0F6RQJRbuIo7yy?Fk>Pz}$K@8Af1O zp(H!U`XR_in9psLck>kL5N@Fa9Tdh=cWwykZ0hlRfI)b$_5xG1zyXu?Yg)y@!Uom^ zQKvH&o}Q7O)+aXBU$33TjI|bk{$g~qTZP0@$K9&nE)&MuWFj36Iwj}@lw2ECD?6Bj zF%9C7LX%O*9jXe+Va+^wQ{>5qD^K1muQx8HLp{Qo>Xra6jE<1mD)KUL-y?%0Lb*%( zqe5mtZ3w+0SX>!9I?>*a5kxWRYGkrKWqc`@JT^dq-GcZlYBk5ZYW4)VQKH`wpk*x6 z)aDZ+mwYS^S>%3=e6rqWtd5M*nQ14G15?{$iwKZNSLA}-O3CbhC32B!BcK$6$Q=uM zajk)a^#<-!FOwLk_D8HTaP}*84>&^|G#(Je3sW`D&dtU!ZN3y`emcB*>18xja<)L6qDKggs!UFf$gZa%W!ve?d z>A>WqYAX%jpf0pcc0AJtC8ALWY0dVEV=ON1mKHJ2(WNBlVidsF!P>#OY>SxE9>jB_ zG`43$R}0HepQ}m{D=-;V#|4$Y^~or)YY|s_3q1oK_l4mFA}6@}T|#3y*^GXJE}!wG zk>=9j&}3jHY;q)_;8{+=jVs^T4b(6yydxf|Hzo`>v{VufYUBtT6FDy9V><_% zoH5K(75k7>__)M6)Fw%wixp@C=9t2=$wy^Vdw46ExH*haUaKQK!Y8zTU#F@yTRcbF zqH?{}XQGvAdSvL4Dp#&P9OccVc(JW8A6Sw$JWS0@5apOqP+U%L_jxyX9NQ>~k85Vz z$49pP1ZCShxGp?F`chCtb~bbG6PGC5vi@a%5dQq=V^cg*fmwd3`S7fRykCrkz!m+gDVY z;IB#Qz|-#H0t>dZIemD@#RV*mq0bNpg(k32^Cz1bEW|>v7OHA8>U@068TYsv{fetJ z1O^G5Fs?ZViVU6$iiSSb5mmW)&fL417gwRMIy%#D24>yqsJOtHUmV3$UAWrvC0AXS!j+~O0ruPrw9uzqd}o&W&0}vc_EWsSs7%l zBv+Yil1C_2S4XvBjnk;|eb?;))%}Z<_WT?+3pT#CnD2)2b$EnENLyMNyi+VyTco{# zg29aS=rfj7DWN)5Z&VR=uv-?h?2tgVuraEnIV@6W!GeNSXBcoP=w#)nFtkN#j5ZEL z%2J=}2q3GYj4-43AX%L&{&zXCcgHE>vY!2VbtYLzjo5We`@CH=?>*R38WPK%QVEP_(Zcb2lGAk9I#Hw@P z9!JeuBcmuT5_$tRk}&#obp3(~J`QluIQ~K9P|5rc`Fy)-jpv{;^haJb(t5^&^-CIy z)o1&l(Mm0dn9+!jFbOv<(#9iFOej)JDpEANavC{J^ydhUOa-zrs;N8D%y(wKz>x<0 zGUXM@Towk1f=?DJM9{Ys#6F2wkOfHu3J>>A*a4@o!3yXR*ilzw*Snp4w-;6%T%QRa z5LCnJOy<;@p%t|!GS`~VZm)yEaimeoD%c0(Bngs_9yJM(We)9z1pf~D~|O< zTSr)F2_0Y`g^wVm3&#PNq$y|P2#sz;*h17Al%yAAi*uDl&+|4@0-P!ghZ+c?yOktu z6f}972}CT9O1Lz8CLO?Yz!fLudX0`75xxtZUV$mzj7WJapK+YSf(v+ncGT5}MQ1wn zbjhVEhznfn-K&Lk!gSScME{a z-g*rqaQ08$gQb{5fW1k@?g27ozR-qC(5s?_hN|lw@S*n`0udxSOb_1(fn7A{iSd&_ zJ~%uR2nT|@ne7}J+0J3gZ#GfeIo$QX&CN@T)a;qMq_#R!IWnk|m^*g!C{TH@%E8EK z)MBMa2Nr`2PC*K|V*)Q?L)$|t%dmt3v}GOC_|S2Y+aE7zW2+?s<_X1B&kidWV8_Lk zC3mK!hI1#TkPZH%lR~b{kvOT@Cks-QsN%}*^))5(7u$l^mkPmc4 z2~EdR06EJl4J@kablDST3%DqD$y$+qlppjecl4Etyc32j#p{I84KxuBj_FZHZj`f5 zh4tBqrZghJ28|SQXQ@D+-Hf{KQ-WD@Oi_2-v38;vYbSj{40)Pj?Oc}%^BjFskCC9S zo2Nv+G~${%j7;>{Zvpj}2L%8^P*Z%#sHl+hF*Wxm8jv8lM| zi`H@=5=WqdzNfWX(|Z1!C!jC#4!4-=Jh;7Fph7AbN{~W88Xb$0Z1yYqtVA2RPtRB* zyU?+Whin_7a}mhW$PS;JHz_&vWHv-_cI>a$MCUmgYq1>5S72WmhN5cuM0c zrLo^SoZ$PFrwp%PVhE~KRBOC_AxW#*mtYU$m_+8KO$Ebqno09QtchtWJc$jUJ}V>E zX$8cLElOuOIuSfB7!lv(t<>Kk@fy~}QJ2Y7h2jzc=4S8|q#ed!dU0uE5<+3*&S6m{ znyngh0!vmA2p26##gG+goB1O`fx<2ahA*8*@0jM$V$bdjx%G;5C{%8GOGzEsL5pd z^^J2}uZk*6TvgawM&b|uTP#koaN0Ghh0Pk(BG;&93=K>tB%@1AR!dU;qPEhd&f%6> znLMdw%ZF8QoW*9;W` zQ%f^}^@<;9W_u#35eFT}A{a2GvS@N*3G<0lPnDA)5g@f<`kPXj?cB{#*Bb5(T(D31 ztBX_c*M6sd;=7=>J=7Z21)6c`3L}|wcJk)5uR()PJLnt^MvcTS7R`pEg`RV|qK0Q| zNoi)>r))x%HaH(>w4Q_h*dQAFGq-`bERZF!lhEM7z0Hh)7O`5?XzJ1EJ>Ov^V}lpE zap^r>WQGe9WLuOl)0r6ZxL9vt5pj^}^qUD{siUC1ahY=z^*;EF5Mq%&w1p-Dp+E;& z@V^!&5Ecj&_H&W3zjHG;c2K9Fr9!7-ol8Q&DyP!ucmO^{u?d%h8h+LnhZ$@w8yo{{ zpa(g|N!n-+ZU|??VbTs(R(nVh!y#f0)g0S;!1dQ*&Tuz*JA(5B-r)gDh|5Wk{br2` zK7!@3h=(I=DUO768l26lve+CRDYTE592G_X$ym!W?sr=h{YNXeJ%*a(_C~>fY{L0P zJRVmR{KsRkg{A%zoW1TSGUgNPIgFe=-f7PXAPA!q(a#XQ++bL_PTWo>6R9Pr_lW|5 zmX%Hk*v_|{npQk{w(r_F&9X7YDmI@alWtr*%bpyyPsBLKgnO5Yv#6SGy2h5)RIBe* z$LrqsbigXBM>mF>LoKT_Fw?M@12r5JEc@MYa)~FLT=yBnI8jwB?Q!z0Ig#}SsV4N2 za?WF;`fM^ZGXN+hzZwFua$MpUlHj5xc=V#ou}0}8hJ zl;Q)1zC))Y&K!1hDMlA;LySBZAzAQEswu*kKtNhY{gm;lZ^bK7fsdH%b1MCH;oxYE zM+A8tOT;EYEcuh0-&Qz1hx3w7Ji}q;HsXJZ4fBMiTn|;2q}~_L1BNtAcbvjfM0)ZM zcaejCBuZ(bNm3V$x|`t|7uZdiZcr4)VO=4+511kX0J>;IqV7a}Ev%=VGe-5TN1p^2OOCskLp7T+z|O$G}=NVm+_RlTsNn9sE824A(dfxH@DytYERj2x*{-sAGt=A4hK%ECk7T zmz%9cYon29og+Z~t~~KOKyB5>U31e-d#<^yZ$bUBh_C)cU$jq0+)BNPeHQ43p2k1LWVY8>z6 z-CkHpJi)U`$?5E=!ioKOzCrAS$RIf;GD)1N8?El~~T!Wym?H9+J-LxxT)VEqB;&mueIj#6+X&ER$#C)FST3)r=&BGIBZC zG{{mZLL%~5Thuk0G9}iB!aucVIP21l+pc0jlmw1fU?z{lVhgfpWilRcA@v82#tK+3 zNYNZP?@V>VSq4_w*w+nwfz={aFO<+#y|A@hF}a z9tKLs9K>@37D|jxgMJ>j!8a(S|1*AS=bVq*Hw~CINfLEd>RLhBTMIw+SRya>VZ~Pt z6a#3RdC9mNPHDkU7AcAxkc4Wv2bRQbjhK1Pk_Xeu45vC05JG4%nR<=$@eI{R(|tZb z2sWvKK3>8=c;15&(GO)3{vq)YR%sp!t8aNaqVHqp~egeaWU-haRJCN#@=7W!f5&s zbTEZ;IO>!V8LCcEA{$M~vzqZ`cEp!a#TSoQA-(`35PqLBf}&jUct1gSuH2%-Cu(U^ zE?H!S3DgfPg3@B!L znG^KTiD25RMXuKCLyd+E0hQ<+aX~x~)lR;4uz6GntPrvk9X>t?tm3gM9$S&j9|0X= zjXBtema_{&Tw*E=NkfDw#xu#Dv^YuzxEH!d#XuLRb+PqMMu6+{F`mM!Vtpo6=PFL) zd-myUfT*UN^E;CY`kAXC99rU9sV@nT2b(f1u8&aCS!Bj^in2h?W@1|uiET!?GPZL7 z9}^KFf=@$E6SBiDzx2qQ<)-5YvRmPjDV{XrM7O#%eXR+a8yZoCPR^Do(qamW{cU)< zAW({2D`+N+KR{<)ArIshoiFyPv{krZoFkwG)RyySJeFfaq*Pxc$V?hBljAg7A&Xf4 z-`D>lmPJa~s~s{h81@N)i-k3(?87BK^(Hf|8l6OZv&iGriWU}b34mUhL@}lTYud!H zE1M&Z(}5xzCnN01R3FzGdxeA;gbf#=91_-679OHw0-lY_Kw-bwF zd*r!cPl*tr2_LLFgDjy#MpXLrEza?sVOP~^hXZbtx>#lw(j*IE@rV-%`fh`s0IVBG zj4?%{Bpbl70AzuU#tc2FjN1pSFyi`1)FoPZ%F}V%1`;Tku9}W}i)l54JG#w8PX%*1 zgN23gftaShqNzvoiliMCPn&yPt7_y>GEy~G%vWQD>zGM-%8#al)F~KV7rN@zTFXmL zSE-LHA9KVky6uUPJ_}Y+v=L^CRu}ecEu{%>naZt18C>o(z9K4ED+`iVoavnLW~*iW z6_ zB=5oGLt;6{pwWgVLBUua8?%ev5o2@xgohp+6&EK7>oXTfg0?X}R|Q%*Jt9rd9R&U- z$EM9T{iJ~^M!mP~RIKC7jTL|Zjgr&@c0;vwGU1DJwC7l;G~;Y~7>VL+U2%4PBDK{p z(TJ`*h$KO0KkY$)PlaHSW9f`zsmI&`|2Qd2ib%V(HGVD&il+=+`5Lz$1=`5MxeqFJ zK-VkN{qYPyV`V7?ix7kg$u&;#+}mtN$a&1IZ?}in!g=^Qr-lQZ>pzw@_s3V@8kxr4 zH<}cv#gH6(ZyUCr_d4(GQ}ocSoB?`zcp26fEtED#&-$T>-i56OVRfyl+=#1k2otbn zw{6>#iGs{ZiF~vfhsPq{9arSVmQ9d=D_?V(m9M$3d}$0Wuyx-?`RIQeKN7~%aBhzsm%WqE6T5h>#LTQ1{K znvRw>Bl9vx=H-Ea1V*|*1|Hz-1EXr=ysMH%00EA8uS@2FrAlc4T2tyRag$*@9ic!P z*Xa=nDM#jVIdEiSJqO5?7-O+K-Ip0mFUtGZQ_Sh{b3vdt@4S5cG*;BF6aN}eDQt$O zQ}K_o*-yD_JQq#ygh9hatZu!FD4`PNt&g?YZ#3b`Ayl}Y=I+CEs=Rj0+3a|NREB30 zj+qFTPc5o(FNCWG*fd{6(WZ5d^4aS^0nk!jQr{j4l+6(VNrHU(5)kp#{NKk-_ zclA2m`o2+!`u;Fis(}sol&E&+ss)}ND$3tso7FIqGzL^zQ9Mlr7H~aq(a}`~80Xi0 zGJ$7kXH2vYq-VFbIR7ZYW|Jbp%#z~0Y&*0H8Y&pL0oD(y4W?dbwcs@zxk~?SI9+vs zo&Y&rb%8*mZu2HbH3WHC?}-Bq1{T(&3slAHqE?&p0sem^lK*)D0c&ya2 zW?gxC)Rk8#!LD>3y~@dMbr2)Q#@7@#>0@;i1*1kyHHiMbAB>ceAp^kd$beu-Z>a^i z{)8P10gl2CTqaO~p+*oGi%o1evOTQrSQF>9Q|a~7^MDl^^IkYPFYV}|#rLK~ho!K< zmoJKo)wJf}#YT$plrY#7BM@P%i5WrUB?Q>aOp3h2T>yb18t2o3|-R9E|x}iu}s;;axSe`G^=|54K@PcB`2y0 z89q+NVMHf5L96dxyluMjpnaxsu^}gL3fBlYDDO|f(Qy1)TC_1L4TTlF+P(E~w8R=3 z)rAldp3aR&gck`oJ*eKLzMUzN*ckVDri5ciL9wqX2Ndno{L_8SliwdFe?W42W8FT*72(AJkpPgY z!>Xrow8~P_n26>{bViJw3+`>zN&Ewx3gUC8vx%;{rCI>&9DU3dNtUVcl5v)W?us*~ zr)?Ey$-k(R5>E4mAD=&)Q_N^$2d}#xF+Y-T5Un2WO6*K`t^rtjd;tPqXtGfi-D~Tq zh*&@Q79jwkGo5H*#Km1c&2!|jFv;#ToukwoD|Ds4^dv|Ek#;Ge={^pF9ETD=F5#1< z=aZtJe!N~XFQfrKTrbIv;VvQ5xgkhdIE=m0u$jlJ1%<-$ObGK8(YcU7gme1V2$gDv*R_-v~iu&RSE)CQNmfB)Ei*%+@FzFYp7@nvqhe~9v z0S#4Er8G0_lS=8hwN&&8P9Kj-tgA85`k<>iIug6mW4B@(VV83FdRemq)H)E&xGt85 z&F@0Y0+~WMMOga^jURAL#WiEF*Q8+KUMex- zg!i{PpsOg4_U1LQTGA3i6dd^w`z+{+d;)o%laB^Ef>`TD^Pvk()|+&a7R0rK1imxM zRrXH7Z>aJZ!HhC0=PUqRA+&|xqDq1q3woG6BfQ*ooS64sm8)soMVQMv_}DGs6u_v!A71mPNkqoE@@epydj96OVUPoBgabPj4KaWU^l z>jsPfj_{!S$omoM5;RswCGLGiv?X-^xO%}cGn838T_#cqFssQ)t7cb^gN`8(a1l@h zz(Jti<9Kvjk;y{G_nxVBQg(7e_we^7xlJoMMGdauWcVsoT=fv}KnO)2Ys0y^S}_<~ z>i47+I^P?ue=*Hg{0UZIZNk4S)hgLM}qbs96x=Iz%)ucmw&?JO)i1^+wn0P@PoXq`;+UQ!0 zw_wj(=i2B2t`Hn3IbccADl6n$q){Fw6OAtziJm6oZeC=-8mpF0cH>|U7+ZB4SGuX{ zfHUr6-w2$74VD@4E1G;xcX_h)!{fS7)rHM5BP;~}f88OFM|yC6P#y}4**{V3!FAD8 zp45RRrfWaSK)8USKe!oqixV2 z3%RWpno$V(zBxkc5N7=peL|NA1fazg_IVTck+)h9DQB(^Zvrpy$j zDqq}j?ZV!7!G(oFQOZLjWcN7%&jmEhpE0!B77eBkfr9lHSIlxnF*c&dntsJLQaz5jc z4H3y(f1|i*86op9;?a?`p%IYSu9Yqh!s&xyx0a1`)R|H|6Rc4;ZT)c$SDpO?Act5= z=i%r{$%MDS8J|`HN`@sw`q+HM#0GhzD;8aJim}j$aG?`X@<|S^e2OGQV^=bxc=cjF zr%rfmmP~EMOH*KC!;FKn*`ld&@(pX66^;jClq-uZ6nnNRh+otvHLq3y)2VUUSX~H{ z^dZye<^iIT*F4t!6C21{kFj4@=oNgrhP;Lc2kJ|b0|eH)P>~IjM5f|N0`T8Tv4)hmSB;mump<|rtS zP_DLxTy1M2nFZiS`cmsC3&gRB*d{PB=2ojd9g8vGOKPS)MyASETb!BgVP%C;&Wr_r z`>{eE$+GOY#4f_Gc7)X+zS$vzHsPZ&xjXQ9aQNW69clCm2fvfEj3X^Wqs%-hU45M# z6K=|Nm#BxjNG}?2xlv^g9JKWoqsY9lQ!()2!ue{Ed)rZaw*P?8lhf7Mb!uortwKU@ z02rJl$(oeK2|26c91gfv4AacVcl5vGroAs7Z|&GD-h@V`GRwKR>`SNOJ6dSS1c#oK zQUlvm@*-x~NT1vG&Jr$CrSXz#HD=v!k4@2NX)pYJ+9 z_QHyxLT8zErXv^haJdGxr*P6*9cOT*bQ-KP5)#``h!cM)+tMgHiO%ylIzejb3cnjx zWV8*R30mGv-5y^TiF{R&d>caURes~PBF(qLk;NB zs*qQlFIFR7B50o?%0r`qAZgz21oqw>XW?Yl_>J$PncceW(bERV5STA=0U;Pojn%)O z$>ED(4W5!kP?@c{B9F)FOL338)0-%+xH}3Q-svtCQSqn|S7`)y6@!#jH!*&1r~ZZ1 z#@<4~-irn`-(`b-91$?L;78K{Va$o#z*uF9fsBHTt4lbOP5OwADAEVJQKPJ>_md`; z_(M<_hy6rI- z_F>m*_!46sX0H7L;^;)-M3`}oD;&wGD+H5oYn!>(I^|yJPWGZ2zCN-i-PK+cX|7xc zfuY<pqwCx#(NK~wz*b*b-3{>0iqGt<}@$M5J{Z| zji+$cvpKD=X~EV;PIY9=05gA_yF_%w;?7%OaKU-#bIK_ZBSGLe9uMlmv-tI@z$d}W zoSwCX^eo5NI#;18J{!#eMZmat>BXawaO}}|VoNiIbrZwzMOUg)`1p?c1no1O5PZp6 z4^|{^S{H?eh%g_)a8v{r)-rMFPw4YW!ZIEe=P`up4RzyK*b-9TnAo@f-!Lr|7v;*H z1er7dSkG_4w71v@A3!hItV9Aew3X)P>?e>^im1yB*Hdw@ylAmn5mDC{Rs!jyr@p`{ zYE6F#+jjtw8WyRD1@(f+t*hnSlQR>4is3LS66Ba!7Rr`L8VF{Z*@erJRLDY< zxsnqFOmv(0JKI$(6b;=bQ-Y?RVrnparpRMTJhe1vf#HTIcI4UUfejzQygA>hP6!YDID+;L4iyfAH>pJNJ!OtxM49^+(%w(UY}n6nBJ zM4WU(aYj3fE|sE~ZzOOm&^2r#j#HPSE=s!ta)-0{(iW=?N|`CqJ#ldxJj(hQtrgybPZ?|?hEKAZcn)AAQ79zT#0gl)QKaHC(t^E(pgC*dO0(HHvy?qIlkKvEu;fdC zezdNhfwqqTG?m_~bxElqXi1z0LeNs5*8Yup!dhn;JS-$wI`vxt5`?C%G2h*%h>8i~ z2ipS$>+WQ14L)j!%I}6MN%|l|NTCIqEem?L!tP)~!93P`iAK9l&NbmjB!WsS4xzYj zm$MgR(PVvVr|33(UO1h_9esiX_Dv_@K-3RK`(5Uh$}vy1boUz19Q z7tY>QBvI8TSWLq)Mq<=TPk+``gnp_VR6ECc1vdc;?@8NhI7C2Sg3x9klWzRC6)zQc zYk?6y;0VpOm<7>)08pyDL^^-zLN4#Rxc}ibnh>!L5eD5 zQ$gsKlLXn5xc`XTG4kIrDs81&6j&ktzcs(CB|LJk!4#31v<}HJ>q%m#j|LPUX#KYm z8VvJr$XP=RN8Fu@+U#RX(E5*4yDemBk*z72RP986P7yzN4D$6U z0{e;d;`SRczFPkfzU@R{jCjg%g+7dU5V=TD&8a_95;ORGRf`a@O_Tj}Gwm#lw6jQ= zgr7Z~EER=r8bd1@xt@Oh8wj_YmQM~e9;+qDWBgVH6OS5f5I!*8itxc8MV~ZS7{*iC z^=OHZ#ka&0HSnOTBWr_HVwH7`d{k|s*bFaykmpj})(83k@e=8C#Xu8Sc#DN*;}tY9FlX836iZBS9zjH) zZoefe=YM=-S0n2Dk6SusCkD{4QY{+0oCMR^N|o@qxc?sw^lZcoRni(9kd93Z_!Wy6 z<;&x+dHuKaDdl!akZro{^pAlFt!8e(SEK1}W7T4Y+?*Ptd=04e#skyoWNcDz=wyuA znaTWk1a>nE9*->8j|QV7e2(idbDMRTd9K4SR3up{)Ln*A@!w|7*Jf|Eu? z;b2i;=E237?gW+7<2)35;u)>30|(WD*dq#}(qzyo>ir4#2@CT=EcJ&1oFHY`<|>%_YGj&(d(r&$OkH_68;l(A9eobK938NuuPDekflo!vv z3~MkLrR0n%-x;fKY;1i>Sdtt)ug=Bj29UWhdFb?%0&Rg4QYzv#3LcD9Pwftefq~gA z!r(KUHmBgldIIZHs#c4ggrG5l3Y0x--|^`dPYX1cX>W%-K-d?UGPZhq9GtS}716A& z=XPs79KZ-;trHPrAQJh5PI{!A%tvF~-+s*kY5ypY)+%dQN7f*5##}`m80Um+VSO{F z*x;NZp5+fnv+~BIGT_XtgNu<7h12{I@n8sO&mR_61u~r^EjdgsI)u}2j1|5>Dsyhs zN2MORFqo=kh)yh!^NbEV<)2+VG7xkN_H~rClcLSGLLOdrv>=x;8@~ORsDLX=ax{vH z`hx+kU%A~NU5oi9>}Zo1a$SI5PLAC*@g71a6cn7jh8+}US%O?HuGA@zljhJ{ zFfz>n?`iNAwf=%h(0h`z@#=lSmBbIC#Wzp_%q5>S>nT2jBL;Ejk5QR(_{jId$P|=| z`L+s#bzra|ZXa9tz^)whij^45qGm#$DayIUk?xl$-7h8GFDq(6ad|J`Pf!}CXQv_c zzQk8VMGqRnczqS3Vqr}bthK7pz* zslDnsK_F$bdPTcglX4%ZseW573?BrgNS~O`d;+^&FA66or7R-(2uC2Op5{67^W|~m zmB)7!nNdnC{jH10U=;P-gGyaJAgh0~9p~D*%gre(?QlF&sE9WFl#yU{TDibF93a8{ zW_Bw?-N}LYsUy1BPYB*#hhm?T0S6J$J=h5g-=_*LIBZ7K@57lxVIN=8RlIMGK2Air z2rZnZnL1_El%D94W3Ez-OZPF3=#d6NL?U)~nmii?z7j?8JSmWn9418t4O$R@9v)If zSA#~a7l=YGiWq@x(nVja6=(Ag5zK@)1!^Ui`+e*?uKhZ)7nzTS6Q@fsJwGf}vRVW) zr7NFim{1QXL)^&xZA7TK{(vMo#u?h%b zLG|ScQib!43%NLGM@<=E$W(R1Y-|Y$Xi~@eMfkJW4}V3J;1Ie;6ur(n?K3?$c*vUXwPN z#-T?AwMSK;gdCeKK|%Y_2!p;N<;hlb0xm`p1dPCZu%zMeM8g1IS&TH%QR3=|3)ICltJQz1O&yg8bCTz%^)sBcaBkD}%rukCBX;%XrT78H)y2$2Pa+^J?Vt4A`MuVnUz zHg{?qenM}c$|7OuI17P8Er^S|VM0ugNqZPMi|#*{9-LUGM&)<1>Iz|aiudwyU;l>g z-lfZ4)j~&e+fr6h;}s*ZQpAt=Y=^YT7qwF6ubg7vQpIULE*jc7(84amMu;eI!#3iS z`P%^vY$ALIH7y^Cn5WDBBIdOSwsGO+Q8teH@wm4psW;w?lM@jqClx2>aP>XcX-q%- zlQ?@)-4LRY1FFlbaa7R!B135Wa&7>Zh*SmELh2fe5SwDkC5?)Rm~-sIM>FIVi$%pU z3DDwT(T1}=gqQt3i;g08`IAJhXdpb-0ZOKcs(rB9S-C>rMXPxORAnM*`YOFy9VnDMr&ym_Tm(}3>fhu0 z4NiR?$R(Y%5SGN0jcn52YNtO0ai)IR{oDwE(*ui|nV%mh?GkDYu_}tg2aq7l7Najl~O@b&XJ*M_m6lM6vdrDanlo0V>{Xro8R1zo+MHe(^&AIAQ z5|$nwssys!ml}HZkI5g(h?T|Jw16u3mIGVHq9?*AMSihuo0We*8@*s$jOvs=DOA@Q z4GK#DLr6)IUvLTP9J=nVY?qoE{^?Q^#NB8bNV?k*><6i6b^K4iY^1zBAu;=28azHu zK+;*$PyH-5=|;q`AY0SAeaQkj7zeZhU@n_wMN^A38;{==juffN&);pJR(O9}vCY{& zoyDl;AX?RZRMh=~Xbl}~jE%MpCB?%63@nWM47`=BUiVA{RSKV6A>m^Y!~b|rZ?a$n zFbaDZm2Q-jM8HNxVnM=_r;E6xI$kO;{5%UiLSBeJwxl97OrxUb65*+aVyUeG+KM>n zm$~i}zeev`Lb`H^s?Dk}6+WW|M(!~R5EDN(*=Bb8h5G(Zx8 zpD9Wxe>jkY73o(wNjU2zVhJn>(k(Pte@}V_F@iy*0eMriUbe9?p>TUQF98mTOMpXl z32+#f0GpiAAKu7HHybk%d{mS;qFF21;#$epA}c+z$V!hY0)UF@wtz|`!lR4xR6R}9 zhhrLZW#i8F;<*LKhDu`Kkq3`6L<+=od^+7mG*7S*s(@A=y<=~D_Jp)D&eEbI^u!cI zI7&$FlpUx}zb1m~; zgrCw&_;Wjd-pQZ4`11w)l-X4InPvRk&%5uj;3xR$`Y+yfmhdy|KjrzjE3aa5BY%#> zPxlVqoxvaWrF0SRp2eT*_`^#{BiD z+jGnn=b=J*;Csx$)5qMx)R`{?NUM_`f#!9kD>*vRw zk$2>$qL2SMG~C{Wr@K4e-agrJaCQkz)mv}9_13%9d%9Z&J!{}u zK8lxUc-Lp#w4TVlQWdw*eA#B7xv5Q{)%mih;Byyqy@Sh)j|DNP@Ty|SFG}x9g({!oyFg-&g0h3?#QiEJ9Pk2MMF!|&I*MB^HAj{ zKkX;nq~7anct5=|3{P0>3nSXul+g%)e*-k*%9|titxX4*YhQMz&)mqnuhta;1|Q?b zFi0;Wqws&P=NTwe9|_ieUek}YnyAj#uB4@n2_g-Fbbn*qNB=e8?;E2vg8;CMDHH&V z&*lvRk9PT-tB4OWoN;cuW~+{A+rF)2TvT$ekIFCdd+ge6RJTI@zi*&lU-k#wZaxr}cA-w>WHS&?VXXHgoxYqb zp+m|2!@h}E^LP1}Wx7aMSzYf789zzOFXGin(H=kHAlLJ`>^wy`k-cH}+FeLb+e8&iE?B>}p#j zVC>CtZ-Tyzo8J++Uv54qgcaFM5mzu@sI{JlaSxXdCs_K+$32bKMUVR>ZN3gF9F-mI zq62<`{bqc#e%pML-LCT0`pwvlFuK+teSqmo*I2TS59XLb<3zx?A5+Hb1+~FX-0A0(O*WwZZh^p)h^E z6sAr6lvWB;Eif(X5(b}x1V1|mv84M3-vQE`NN?Ogf{1Wer6YC z@d;6PbSU8h88@jrau41XHv?JO2ek&fefb5xEMD!)-wVv0zMLoewdJUdB_T24?JtuB z8LulMX0dqJSuMV*%b(TktAxxHYips+f-kJ{lUnuHclb%2cF_`Dk`nv%ae(+;g1)RL z0ad)Bf{mgtIxINQL3wAC80hkTQXsg`!8Qjif{(E6Xc6?-vdOw@57XpmYcl@iUZdnn z#<-m>T0{GiM|Kz+uXeYZ{yG!no@c*eW?C%JR<1>| z$TDj!7r&dEx7Z$6Eu!6GRP~u#hQR#S`E=n{yX$%+FkNRY(A4+sq9)4DtTzcM6F(Zc zcizHk>FV%RPV3=+X?mvtB1~p>`Puw9<2O+c!ih02#lr-pSqOycSPOYU{Eddd~;typbG{eiH*k4�ab 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Ð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 z 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

-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%0A&cqZyXl(-yqIZFH-<$+s?m!rNj;>dokex!5z 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

xfD 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 mA9Y)``=j`T4(RcWC*?Y{`33X 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.11/site-packages/PIL/_imagingtk.cpython-311-x86_64-linux-gnu.so b/venv/lib/python3.11/site-packages/PIL/_imagingtk.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..0b483fee1a4c815237e15a181945619965e07bf4 GIT binary patch literal 39968 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.11/site-packages/PIL/features.py b/venv/lib/python3.11/site-packages/PIL/features.py new file mode 100644 index 00000000..da0ca557 --- /dev/null +++ b/venv/lib/python3.11/site-packages/PIL/features.py @@ -0,0 +1,313 @@ +import collections +import os +import sys +import warnings + +import PIL + +from . import Image + +modules = { + "pil": ("PIL._imaging", "PILLOW_VERSION"), + "tkinter": ("PIL._tkinter_finder", None), + "freetype2": ("PIL._imagingft", "freetype2_version"), + "littlecms2": ("PIL._imagingcms", "littlecms_version"), + "webp": ("PIL._webp", "webpdecoder_version"), +} + + +def check_module(feature): + """ + Checks if a module is available. + + :param feature: The module to check for. + :returns: ``True`` if available, ``False`` otherwise. + :raises ValueError: If the module is not defined in this version of Pillow. + """ + if not (feature in modules): + raise ValueError(f"Unknown module {feature}") + + module, ver = modules[feature] + + try: + __import__(module) + return True + except ImportError: + return False + + +def version_module(feature): + """ + :param feature: The module to check for. + :returns: + The loaded version number as a string, or ``None`` if unknown or not available. + :raises ValueError: If the module is not defined in this version of Pillow. + """ + if not check_module(feature): + return None + + module, ver = modules[feature] + + if ver is None: + return None + + return getattr(__import__(module, fromlist=[ver]), ver) + + +def get_supported_modules(): + """ + :returns: A list of all supported modules. + """ + return [f for f in modules if check_module(f)] + + +codecs = { + "jpg": ("jpeg", "jpeglib"), + "jpg_2000": ("jpeg2k", "jp2klib"), + "zlib": ("zip", "zlib"), + "libtiff": ("libtiff", "libtiff"), +} + + +def check_codec(feature): + """ + Checks if a codec is available. + + :param feature: The codec to check for. + :returns: ``True`` if available, ``False`` otherwise. + :raises ValueError: If the codec is not defined in this version of Pillow. + """ + if feature not in codecs: + raise ValueError(f"Unknown codec {feature}") + + codec, lib = codecs[feature] + + return codec + "_encoder" in dir(Image.core) + + +def version_codec(feature): + """ + :param feature: The codec to check for. + :returns: + The version number as a string, or ``None`` if not available. + Checked at compile time for ``jpg``, run-time otherwise. + :raises ValueError: If the codec is not defined in this version of Pillow. + """ + if not check_codec(feature): + return None + + codec, lib = codecs[feature] + + version = getattr(Image.core, lib + "_version") + + if feature == "libtiff": + return version.split("\n")[0].split("Version ")[1] + + return version + + +def get_supported_codecs(): + """ + :returns: A list of all supported codecs. + """ + return [f for f in codecs if check_codec(f)] + + +features = { + "webp_anim": ("PIL._webp", "HAVE_WEBPANIM", None), + "webp_mux": ("PIL._webp", "HAVE_WEBPMUX", None), + "transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY", None), + "raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"), + "libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO", "libjpeg_turbo_version"), + "libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT", "imagequant_version"), + "xcb": ("PIL._imaging", "HAVE_XCB", None), +} + + +def check_feature(feature): + """ + Checks if a feature is available. + + :param feature: The feature to check for. + :returns: ``True`` if available, ``False`` if unavailable, ``None`` if unknown. + :raises ValueError: If the feature is not defined in this version of Pillow. + """ + if feature not in features: + raise ValueError(f"Unknown feature {feature}") + + module, flag, ver = features[feature] + + try: + imported_module = __import__(module, fromlist=["PIL"]) + return getattr(imported_module, flag) + except ImportError: + return None + + +def version_feature(feature): + """ + :param feature: The feature to check for. + :returns: The version number as a string, or ``None`` if not available. + :raises ValueError: If the feature is not defined in this version of Pillow. + """ + if not check_feature(feature): + return None + + module, flag, ver = features[feature] + + if ver is None: + return None + + return getattr(__import__(module, fromlist=[ver]), ver) + + +def get_supported_features(): + """ + :returns: A list of all supported features. + """ + return [f for f in features if check_feature(f)] + + +def check(feature): + """ + :param feature: A module, codec, or feature name. + :returns: + ``True`` if the module, codec, or feature is available, + ``False`` or ``None`` otherwise. + """ + + if feature in modules: + return check_module(feature) + if feature in codecs: + return check_codec(feature) + if feature in features: + return check_feature(feature) + warnings.warn(f"Unknown feature '{feature}'.", stacklevel=2) + return False + + +def version(feature): + """ + :param feature: + The module, codec, or feature to check for. + :returns: + The version number as a string, or ``None`` if unknown or not available. + """ + if feature in modules: + return version_module(feature) + if feature in codecs: + return version_codec(feature) + if feature in features: + return version_feature(feature) + return None + + +def get_supported(): + """ + :returns: A list of all supported modules, features, and codecs. + """ + + ret = get_supported_modules() + ret.extend(get_supported_features()) + ret.extend(get_supported_codecs()) + return ret + + +def pilinfo(out=None, supported_formats=True): + """ + Prints information about this installation of Pillow. + This function can be called with ``python -m PIL``. + + :param out: + The output stream to print to. Defaults to ``sys.stdout`` if ``None``. + :param supported_formats: + If ``True``, a list of all supported image file formats will be printed. + """ + + if out is None: + out = sys.stdout + + Image.init() + + print("-" * 68, file=out) + print(f"Pillow {PIL.__version__}", file=out) + py_version = sys.version.splitlines() + print(f"Python {py_version[0].strip()}", file=out) + for py_version in py_version[1:]: + print(f" {py_version.strip()}", file=out) + print("-" * 68, file=out) + print( + f"Python modules loaded from {os.path.dirname(Image.__file__)}", + file=out, + ) + print( + f"Binary modules loaded from {os.path.dirname(Image.core.__file__)}", + file=out, + ) + print("-" * 68, file=out) + + for name, feature in [ + ("pil", "PIL CORE"), + ("tkinter", "TKINTER"), + ("freetype2", "FREETYPE2"), + ("littlecms2", "LITTLECMS2"), + ("webp", "WEBP"), + ("transp_webp", "WEBP Transparency"), + ("webp_mux", "WEBPMUX"), + ("webp_anim", "WEBP Animation"), + ("jpg", "JPEG"), + ("jpg_2000", "OPENJPEG (JPEG2000)"), + ("zlib", "ZLIB (PNG/ZIP)"), + ("libtiff", "LIBTIFF"), + ("raqm", "RAQM (Bidirectional Text)"), + ("libimagequant", "LIBIMAGEQUANT (Quantization method)"), + ("xcb", "XCB (X protocol)"), + ]: + if check(name): + if name == "jpg" and check_feature("libjpeg_turbo"): + v = "libjpeg-turbo " + version_feature("libjpeg_turbo") + else: + v = version(name) + if v is not None: + version_static = name in ("pil", "jpg") + if name == "littlecms2": + # this check is also in src/_imagingcms.c:setup_module() + version_static = tuple(int(x) for x in v.split(".")) < (2, 7) + t = "compiled for" if version_static else "loaded" + print("---", feature, "support ok,", t, v, file=out) + else: + print("---", feature, "support ok", file=out) + else: + print("***", feature, "support not installed", file=out) + print("-" * 68, file=out) + + if supported_formats: + extensions = collections.defaultdict(list) + for ext, i in Image.EXTENSION.items(): + extensions[i].append(ext) + + for i in sorted(Image.ID): + line = f"{i}" + if i in Image.MIME: + line = f"{line} {Image.MIME[i]}" + print(line, file=out) + + if i in extensions: + print( + "Extensions: {}".format(", ".join(sorted(extensions[i]))), file=out + ) + + features = [] + if i in Image.OPEN: + features.append("open") + if i in Image.SAVE: + features.append("save") + if i in Image.SAVE_ALL: + features.append("save_all") + if i in Image.DECODERS: + features.append("decode") + if i in Image.ENCODERS: + features.append("encode") + + print("Features: {}".format(", ".join(features)), file=out) + print("-" * 68, file=out) diff --git a/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/PKG-INFO b/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/PKG-INFO new file mode 100644 index 00000000..26bd1b56 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/PKG-INFO @@ -0,0 +1,135 @@ +Metadata-Version: 2.1 +Name: Pillow +Version: 8.1.0 +Summary: Python Imaging Library (Fork) +Home-page: https://python-pillow.org +Author: Alex Clark (PIL Fork Author) +Author-email: aclark@python-pillow.org +License: HPND +Project-URL: Documentation, https://pillow.readthedocs.io +Project-URL: Source, https://github.com/python-pillow/Pillow +Project-URL: Funding, https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=pypi +Project-URL: Release notes, https://pillow.readthedocs.io/en/stable/releasenotes/index.html +Project-URL: Changelog, https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst +Keywords: Imaging +Classifier: Development Status :: 6 - Mature +Classifier: License :: OSI Approved :: Historical Permission Notice and Disclaimer (HPND) +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Multimedia :: Graphics +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Digital Camera +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture +Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion +Classifier: Topic :: Multimedia :: Graphics :: Viewers +Requires-Python: >=3.6 +Description-Content-Type: text/markdown +License-File: LICENSE + +

+ Pillow logo +

+ +# Pillow + +## Python Imaging Library (Fork) + +Pillow is the friendly PIL fork by [Alex Clark and +Contributors](https://github.com/python-pillow/Pillow/graphs/contributors). +PIL is the Python Imaging Library by Fredrik Lundh and Contributors. +As of 2019, Pillow development is +[supported by Tidelift](https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=readme&utm_campaign=enterprise). + + + + + + + + + + + + + + + + + + +
docs + Documentation Status +
tests + GitHub Actions build status (Lint) + GitHub Actions build status (Test Linux and macOS) + GitHub Actions build status (Test Windows) + GitHub Actions build status (Test Docker) + AppVeyor CI build status (Windows) + Travis CI build status (macOS) + Code coverage +
package + Zenodo + Tidelift + Newest PyPI version + Number of PyPI downloads +
social + Join the chat at https://gitter.im/python-pillow/Pillow + Follow on https://twitter.com/PythonPillow +
+ +## Overview + +The Python Imaging Library adds image processing capabilities to your Python interpreter. + +This library provides extensive file format support, an efficient internal representation, and fairly powerful image processing capabilities. + +The core image library is designed for fast access to data stored in a few basic pixel formats. It should provide a solid foundation for a general image processing tool. + +## More Information + +- [Documentation](https://pillow.readthedocs.io/) + - [Installation](https://pillow.readthedocs.io/en/latest/installation.html) + - [Handbook](https://pillow.readthedocs.io/en/latest/handbook/index.html) +- [Contribute](https://github.com/python-pillow/Pillow/blob/master/.github/CONTRIBUTING.md) + - [Issues](https://github.com/python-pillow/Pillow/issues) + - [Pull requests](https://github.com/python-pillow/Pillow/pulls) +- [Release notes](https://pillow.readthedocs.io/en/stable/releasenotes/index.html) +- [Changelog](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst) + - [Pre-fork](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst#pre-fork) + +## Report a Vulnerability + +To report a security vulnerability, please follow the procedure described in the [Tidelift security policy](https://tidelift.com/docs/security). diff --git a/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/SOURCES.txt b/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/SOURCES.txt new file mode 100644 index 00000000..e19015d6 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/SOURCES.txt @@ -0,0 +1,1321 @@ +.pre-commit-config.yaml +CHANGES.rst +LICENSE +MANIFEST.in +Makefile +README.md +RELEASING.md +conftest.py +requirements.txt +selftest.py +setup.cfg +setup.py +tox.ini +Tests/32bit_segfault_check.py +Tests/README.rst +Tests/__init__.py +Tests/bench_cffi_access.py +Tests/check_fli_oob.py +Tests/check_fli_overflow.py +Tests/check_icns_dos.py +Tests/check_imaging_leaks.py +Tests/check_j2k_dos.py +Tests/check_j2k_leaks.py +Tests/check_j2k_overflow.py +Tests/check_jp2_overflow.py +Tests/check_jpeg_leaks.py +Tests/check_large_memory.py +Tests/check_large_memory_numpy.py +Tests/check_libtiff_segfault.py +Tests/check_png_dos.py +Tests/conftest.py +Tests/createfontdatachunk.py +Tests/helper.py +Tests/test_000_sanity.py +Tests/test_binary.py +Tests/test_bmp_reference.py +Tests/test_box_blur.py +Tests/test_color_lut.py +Tests/test_core_resources.py +Tests/test_decompression_bomb.py +Tests/test_features.py +Tests/test_file_apng.py +Tests/test_file_blp.py +Tests/test_file_bmp.py +Tests/test_file_bufrstub.py +Tests/test_file_container.py +Tests/test_file_cur.py +Tests/test_file_dcx.py +Tests/test_file_dds.py +Tests/test_file_eps.py +Tests/test_file_fitsstub.py +Tests/test_file_fli.py +Tests/test_file_fpx.py +Tests/test_file_ftex.py +Tests/test_file_gbr.py +Tests/test_file_gd.py +Tests/test_file_gif.py +Tests/test_file_gimpgradient.py +Tests/test_file_gimppalette.py +Tests/test_file_gribstub.py +Tests/test_file_hdf5stub.py +Tests/test_file_icns.py +Tests/test_file_ico.py +Tests/test_file_im.py +Tests/test_file_iptc.py +Tests/test_file_jpeg.py +Tests/test_file_jpeg2k.py +Tests/test_file_libtiff.py +Tests/test_file_libtiff_small.py +Tests/test_file_mcidas.py +Tests/test_file_mic.py +Tests/test_file_mpo.py +Tests/test_file_msp.py +Tests/test_file_palm.py +Tests/test_file_pcd.py +Tests/test_file_pcx.py +Tests/test_file_pdf.py +Tests/test_file_pixar.py +Tests/test_file_png.py +Tests/test_file_ppm.py +Tests/test_file_psd.py +Tests/test_file_sgi.py +Tests/test_file_spider.py +Tests/test_file_sun.py +Tests/test_file_tar.py +Tests/test_file_tga.py +Tests/test_file_tiff.py +Tests/test_file_tiff_metadata.py +Tests/test_file_wal.py +Tests/test_file_webp.py +Tests/test_file_webp_alpha.py +Tests/test_file_webp_animated.py +Tests/test_file_webp_lossless.py +Tests/test_file_webp_metadata.py +Tests/test_file_wmf.py +Tests/test_file_xbm.py +Tests/test_file_xpm.py +Tests/test_file_xvthumb.py +Tests/test_font_bdf.py +Tests/test_font_leaks.py +Tests/test_font_pcf.py +Tests/test_font_pcf_charsets.py +Tests/test_format_hsv.py +Tests/test_format_lab.py +Tests/test_image.py +Tests/test_image_access.py +Tests/test_image_array.py +Tests/test_image_convert.py +Tests/test_image_copy.py +Tests/test_image_crop.py +Tests/test_image_draft.py +Tests/test_image_entropy.py +Tests/test_image_filter.py +Tests/test_image_frombytes.py +Tests/test_image_fromqimage.py +Tests/test_image_getbands.py +Tests/test_image_getbbox.py +Tests/test_image_getcolors.py +Tests/test_image_getdata.py +Tests/test_image_getextrema.py +Tests/test_image_getim.py +Tests/test_image_getpalette.py +Tests/test_image_getprojection.py +Tests/test_image_histogram.py +Tests/test_image_load.py +Tests/test_image_mode.py +Tests/test_image_paste.py +Tests/test_image_point.py +Tests/test_image_putalpha.py +Tests/test_image_putdata.py +Tests/test_image_putpalette.py +Tests/test_image_quantize.py +Tests/test_image_reduce.py +Tests/test_image_resample.py +Tests/test_image_resize.py +Tests/test_image_rotate.py +Tests/test_image_split.py +Tests/test_image_thumbnail.py +Tests/test_image_tobitmap.py +Tests/test_image_tobytes.py +Tests/test_image_transform.py +Tests/test_image_transpose.py +Tests/test_imagechops.py +Tests/test_imagecms.py +Tests/test_imagecolor.py +Tests/test_imagedraw.py +Tests/test_imagedraw2.py +Tests/test_imageenhance.py +Tests/test_imagefile.py +Tests/test_imagefont.py +Tests/test_imagefontctl.py +Tests/test_imagegrab.py +Tests/test_imagemath.py +Tests/test_imagemorph.py +Tests/test_imageops.py +Tests/test_imageops_usm.py +Tests/test_imagepalette.py +Tests/test_imagepath.py +Tests/test_imageqt.py +Tests/test_imagesequence.py +Tests/test_imageshow.py +Tests/test_imagestat.py +Tests/test_imagetk.py +Tests/test_imagewin.py +Tests/test_imagewin_pointers.py +Tests/test_lib_image.py +Tests/test_lib_pack.py +Tests/test_locale.py +Tests/test_main.py +Tests/test_map.py +Tests/test_mode_i16.py +Tests/test_numpy.py +Tests/test_pdfparser.py +Tests/test_pickle.py +Tests/test_psdraw.py +Tests/test_pyroma.py +Tests/test_qt_image_qapplication.py +Tests/test_qt_image_toqimage.py +Tests/test_sgi_crash.py +Tests/test_shell_injection.py +Tests/test_tiff_crashes.py +Tests/test_tiff_ifdrational.py +Tests/test_uploader.py +Tests/test_util.py +Tests/test_webp_leaks.py +Tests/fonts/10x20-ISO8859-1.pcf +Tests/fonts/10x20.pbm +Tests/fonts/10x20.pil +Tests/fonts/AdobeVFPrototype.ttf +Tests/fonts/ArefRuqaa-Regular.ttf +Tests/fonts/BungeeColor-Regular_colr_Windows.ttf +Tests/fonts/DejaVuSans-24-1-stripped.ttf +Tests/fonts/DejaVuSans-24-2-stripped.ttf +Tests/fonts/DejaVuSans-24-4-stripped.ttf +Tests/fonts/DejaVuSans-24-8-stripped.ttf +Tests/fonts/DejaVuSans.ttf +Tests/fonts/FreeMono.ttf +Tests/fonts/KhmerOSBattambang-Regular.ttf +Tests/fonts/LICENSE.txt +Tests/fonts/NotoColorEmoji.ttf +Tests/fonts/NotoNastaliqUrdu-Regular.ttf +Tests/fonts/NotoSans-Regular.ttf +Tests/fonts/NotoSansJP-Regular.otf +Tests/fonts/NotoSansSymbols-Regular.ttf +Tests/fonts/OpenSansCondensed-LightItalic.ttf +Tests/fonts/TINY5x3GX.ttf +Tests/fonts/ter-x20b-cp1250.pbm +Tests/fonts/ter-x20b-cp1250.pil +Tests/fonts/ter-x20b-iso8859-1.pbm +Tests/fonts/ter-x20b-iso8859-1.pil +Tests/fonts/ter-x20b-iso8859-2.pbm +Tests/fonts/ter-x20b-iso8859-2.pil +Tests/fonts/ter-x20b.pcf +Tests/icc/LICENSE.txt +Tests/icc/sRGB_IEC61966-2-1_black_scaled.icc +Tests/icc/sRGB_v4_ICC_preference.icc +Tests/images/00r0_gray_l.jp2 +Tests/images/00r1_graya_la.jp2 +Tests/images/01r_00.pcx +Tests/images/10ct_32bit_128.tiff +Tests/images/12bit.cropped.tif +Tests/images/12in16bit.tif +Tests/images/16_bit_binary.pgm +Tests/images/16_bit_binary_pgm.png +Tests/images/16_bit_noise.tif +Tests/images/16bit.MM.cropped.tif +Tests/images/16bit.MM.deflate.tif +Tests/images/16bit.cropped.j2k +Tests/images/16bit.cropped.jp2 +Tests/images/16bit.cropped.tif +Tests/images/16bit.deflate.tif +Tests/images/16bit.s.tif +Tests/images/1_trns.png +Tests/images/7x13.png +Tests/images/DXGI_FORMAT_BC7_UNORM_SRGB.dds +Tests/images/DXGI_FORMAT_BC7_UNORM_SRGB.png +Tests/images/DXGI_FORMAT_R8G8B8A8_UNORM_SRGB.dds +Tests/images/DXGI_FORMAT_R8G8B8A8_UNORM_SRGB.png +Tests/images/WAlaska.wind.7days.grb +Tests/images/a.fli +Tests/images/a_fli.png +Tests/images/anim_frame1.webp +Tests/images/anim_frame2.webp +Tests/images/app13-multiple.jpg +Tests/images/app13.jpg +Tests/images/argb-32bpp_MipMaps-1.dds +Tests/images/argb-32bpp_MipMaps-1.png +Tests/images/bad_palette_entry.gpl +Tests/images/bad_palette_file.gpl +Tests/images/balloon.jpf +Tests/images/bc7-argb-8bpp_MipMaps-1.dds +Tests/images/bc7-argb-8bpp_MipMaps-1.png +Tests/images/binary_preview_map.eps +Tests/images/bitmap_font_1_basic.png +Tests/images/bitmap_font_1_raqm.png +Tests/images/bitmap_font_2_basic.png +Tests/images/bitmap_font_2_raqm.png +Tests/images/bitmap_font_4_basic.png +Tests/images/bitmap_font_4_raqm.png +Tests/images/bitmap_font_8_basic.png +Tests/images/bitmap_font_8_raqm.png +Tests/images/broken.png +Tests/images/broken_data_stream.png +Tests/images/bw_gradient.png +Tests/images/caption_6_33_22.png +Tests/images/cbdt_notocoloremoji.png +Tests/images/cbdt_notocoloremoji_mask.png +Tests/images/chi.gif +Tests/images/clipboard.dib +Tests/images/clipboard_target.png +Tests/images/cmx3g8_wv_1998.260_0745_mcidas.ara +Tests/images/cmx3g8_wv_1998.260_0745_mcidas.png +Tests/images/color_snakes.png +Tests/images/colr_bungee.png +Tests/images/colr_bungee_mask.png +Tests/images/combined_larger_than_size.psd +Tests/images/compression.tif +Tests/images/copyleft.png +Tests/images/copyleft.tiff +Tests/images/corner.lut +Tests/images/courB08.bdf +Tests/images/courB08.pbm +Tests/images/courB08.pil +Tests/images/crash-2020-10-test.tif +Tests/images/crash-6b7f2244da6d0ae297ee0754a424213444e92778.sgi +Tests/images/create_eps.gnuplot +Tests/images/custom_gimp_palette.gpl +Tests/images/decompression_bomb.gif +Tests/images/decompression_bomb.ico +Tests/images/deerstalker.cur +Tests/images/default_font.png +Tests/images/dilation4.lut +Tests/images/dilation8.lut +Tests/images/dispose_bgnd.gif +Tests/images/dispose_none.gif +Tests/images/dispose_none_load_end.gif +Tests/images/dispose_none_load_end_second.gif +Tests/images/dispose_prev.gif +Tests/images/drawing.emf +Tests/images/drawing.wmf +Tests/images/drawing_emf_ref.png +Tests/images/drawing_roundDown.emf +Tests/images/drawing_wmf_ref.png +Tests/images/drawing_wmf_ref_144.png +Tests/images/dummy.container +Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds +Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.png +Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds +Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.png +Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds +Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.png +Tests/images/edge.lut +Tests/images/effect_mandelbrot.png +Tests/images/effect_spread.png +Tests/images/empty_gps_ifd.jpg +Tests/images/erosion4.lut +Tests/images/erosion8.lut +Tests/images/exif-200dpcm.jpg +Tests/images/exif-72dpi-int.jpg +Tests/images/exif-dpi-zerodivision.jpg +Tests/images/exif-ifd-offset.jpg +Tests/images/exif.png +Tests/images/exif_gps.jpg +Tests/images/exif_gps_typeerror.jpg +Tests/images/exif_imagemagick.png +Tests/images/exif_text.png +Tests/images/exif_typeerror.jpg +Tests/images/fli_overflow.fli +Tests/images/fli_overrun.bin +Tests/images/fli_overrun2.bin +Tests/images/flower.jpg +Tests/images/flower.webp +Tests/images/flower2.jpg +Tests/images/flower2.webp +Tests/images/frozenpond.mpo +Tests/images/ftex_dxt1.ftc +Tests/images/ftex_dxt1.png +Tests/images/ftex_uncompressed.ftu +Tests/images/ftex_uncompressed.png +Tests/images/fujifilm.mpo +Tests/images/g4-fillorder-test.png +Tests/images/g4-fillorder-test.tif +Tests/images/g4-multi.tiff +Tests/images/g4_orientation_1.tif +Tests/images/g4_orientation_2.tif +Tests/images/g4_orientation_3.tif +Tests/images/g4_orientation_4.tif +Tests/images/g4_orientation_5.tif +Tests/images/g4_orientation_6.tif +Tests/images/g4_orientation_7.tif +Tests/images/g4_orientation_8.tif +Tests/images/gbr.gbr +Tests/images/gbr.png +Tests/images/gfs.t06z.rassda.tm00.bufr_d +Tests/images/gif_header_data.pkl +Tests/images/gimp_gradient.ggr +Tests/images/gimp_gradient_with_name.ggr +Tests/images/hdf5.h5 +Tests/images/high_ascii_chars.png +Tests/images/hopper-XYZ.png +Tests/images/hopper.Lab.tif +Tests/images/hopper.bmp +Tests/images/hopper.bw +Tests/images/hopper.dcx +Tests/images/hopper.fits +Tests/images/hopper.fli +Tests/images/hopper.gd +Tests/images/hopper.gif +Tests/images/hopper.iccprofile.tif +Tests/images/hopper.iccprofile_binary.tif +Tests/images/hopper.ico +Tests/images/hopper.im +Tests/images/hopper.jpg +Tests/images/hopper.mic +Tests/images/hopper.msp +Tests/images/hopper.p7 +Tests/images/hopper.pcd +Tests/images/hopper.png +Tests/images/hopper.pnm +Tests/images/hopper.ppm +Tests/images/hopper.psd +Tests/images/hopper.pxr +Tests/images/hopper.ras +Tests/images/hopper.rgb +Tests/images/hopper.sgi +Tests/images/hopper.spider +Tests/images/hopper.tar +Tests/images/hopper.tif +Tests/images/hopper.wal +Tests/images/hopper.webp +Tests/images/hopper.xbm +Tests/images/hopper.xpm +Tests/images/hopper16.rgb +Tests/images/hopper_16bit_qtables.jpg +Tests/images/hopper_256x256.ico +Tests/images/hopper_45.png +Tests/images/hopper_bad.p7 +Tests/images/hopper_bad_checksum.msp +Tests/images/hopper_bad_exif.jpg +Tests/images/hopper_bw_500.png +Tests/images/hopper_draw.ico +Tests/images/hopper_emboss.bmp +Tests/images/hopper_emboss_more.bmp +Tests/images/hopper_g4.tif +Tests/images/hopper_g4_500.tif +Tests/images/hopper_gray.jpg +Tests/images/hopper_gray_4bpp.tif +Tests/images/hopper_idat_after_image_end.png +Tests/images/hopper_jpg.tif +Tests/images/hopper_long_name.im +Tests/images/hopper_lzw.tif +Tests/images/hopper_merged.psd +Tests/images/hopper_orientation_2.jpg +Tests/images/hopper_orientation_2.webp +Tests/images/hopper_orientation_3.jpg +Tests/images/hopper_orientation_3.webp +Tests/images/hopper_orientation_4.jpg +Tests/images/hopper_orientation_4.webp +Tests/images/hopper_orientation_5.jpg +Tests/images/hopper_orientation_5.webp +Tests/images/hopper_orientation_6.jpg +Tests/images/hopper_orientation_6.webp +Tests/images/hopper_orientation_7.jpg +Tests/images/hopper_orientation_7.webp +Tests/images/hopper_orientation_8.jpg +Tests/images/hopper_orientation_8.webp +Tests/images/hopper_roundDown.bmp +Tests/images/hopper_roundDown_2.tif +Tests/images/hopper_roundDown_3.tif +Tests/images/hopper_roundDown_None.tif +Tests/images/hopper_roundUp_2.tif +Tests/images/hopper_roundUp_3.tif +Tests/images/hopper_roundUp_None.tif +Tests/images/hopper_underscore.xbm +Tests/images/hopper_unexpected.ico +Tests/images/hopper_unknown_pixel_mode.tif +Tests/images/hopper_webp_bits.ppm +Tests/images/hopper_webp_write.ppm +Tests/images/hopper_zero_comment_subblocks.gif +Tests/images/i_trns.png +Tests/images/icc-after-SOF.jpg +Tests/images/icc_profile.png +Tests/images/icc_profile_big.jpg +Tests/images/icc_profile_none.png +Tests/images/ifd_tag_type.tiff +Tests/images/illu10_no_preview.eps +Tests/images/illu10_preview.eps +Tests/images/illuCS6_no_preview.eps +Tests/images/illuCS6_preview.eps +Tests/images/imagedraw2_text.png +Tests/images/imagedraw_arc.png +Tests/images/imagedraw_arc_end_le_start.png +Tests/images/imagedraw_arc_high.png +Tests/images/imagedraw_arc_no_loops.png +Tests/images/imagedraw_arc_width.png +Tests/images/imagedraw_arc_width_fill.png +Tests/images/imagedraw_arc_width_non_whole_angle.png +Tests/images/imagedraw_arc_width_pieslice.png +Tests/images/imagedraw_big_rectangle.png +Tests/images/imagedraw_bitmap.png +Tests/images/imagedraw_chord_L.png +Tests/images/imagedraw_chord_RGB.png +Tests/images/imagedraw_chord_too_fat.png +Tests/images/imagedraw_chord_width.png +Tests/images/imagedraw_chord_width_fill.png +Tests/images/imagedraw_chord_zero_width.png +Tests/images/imagedraw_ellipse_L.png +Tests/images/imagedraw_ellipse_RGB.png +Tests/images/imagedraw_ellipse_edge.png +Tests/images/imagedraw_ellipse_translucent.png +Tests/images/imagedraw_ellipse_various_sizes.png +Tests/images/imagedraw_ellipse_various_sizes_filled.png +Tests/images/imagedraw_ellipse_width.png +Tests/images/imagedraw_ellipse_width_fill.png +Tests/images/imagedraw_ellipse_width_large.png +Tests/images/imagedraw_ellipse_zero_width.png +Tests/images/imagedraw_floodfill2.png +Tests/images/imagedraw_floodfill_L.png +Tests/images/imagedraw_floodfill_RGB.png +Tests/images/imagedraw_floodfill_RGBA.png +Tests/images/imagedraw_floodfill_not_negative.png +Tests/images/imagedraw_line.png +Tests/images/imagedraw_line_joint_curve.png +Tests/images/imagedraw_outline_chord_L.png +Tests/images/imagedraw_outline_chord_RGB.png +Tests/images/imagedraw_outline_ellipse_L.png +Tests/images/imagedraw_outline_ellipse_RGB.png +Tests/images/imagedraw_outline_pieslice_L.png +Tests/images/imagedraw_outline_pieslice_RGB.png +Tests/images/imagedraw_outline_polygon_L.png +Tests/images/imagedraw_outline_polygon_RGB.png +Tests/images/imagedraw_outline_rectangle_L.png +Tests/images/imagedraw_outline_rectangle_RGB.png +Tests/images/imagedraw_outline_shape_L.png +Tests/images/imagedraw_outline_shape_RGB.png +Tests/images/imagedraw_pieslice.png +Tests/images/imagedraw_pieslice_wide.png +Tests/images/imagedraw_pieslice_width.png +Tests/images/imagedraw_pieslice_width_fill.png +Tests/images/imagedraw_pieslice_zero_width.png +Tests/images/imagedraw_point.png +Tests/images/imagedraw_polygon.png +Tests/images/imagedraw_polygon_1px_high.png +Tests/images/imagedraw_polygon_kite_L.png +Tests/images/imagedraw_polygon_kite_RGB.png +Tests/images/imagedraw_rectangle.png +Tests/images/imagedraw_rectangle_I.png +Tests/images/imagedraw_rectangle_width.png +Tests/images/imagedraw_rectangle_width_fill.png +Tests/images/imagedraw_rectangle_zero_width.png +Tests/images/imagedraw_regular_octagon.png +Tests/images/imagedraw_shape1.png +Tests/images/imagedraw_shape2.png +Tests/images/imagedraw_square.png +Tests/images/imagedraw_square_rotate_45.png +Tests/images/imagedraw_stroke_descender.png +Tests/images/imagedraw_stroke_different.png +Tests/images/imagedraw_stroke_multiline.png +Tests/images/imagedraw_stroke_same.png +Tests/images/imagedraw_wide_line_dot.png +Tests/images/imagedraw_wide_line_larger_than_int.png +Tests/images/imageops_pad_h_0.jpg +Tests/images/imageops_pad_h_1.jpg +Tests/images/imageops_pad_h_2.jpg +Tests/images/imageops_pad_v_0.jpg +Tests/images/imageops_pad_v_1.jpg +Tests/images/imageops_pad_v_2.jpg +Tests/images/input_bw_five_bands.fpx +Tests/images/invalid-exif-without-x-resolution.jpg +Tests/images/invalid-exif.jpg +Tests/images/invalid.spider +Tests/images/iptc.jpg +Tests/images/iptc_roundDown.jpg +Tests/images/iptc_roundUp.jpg +Tests/images/iss634.apng +Tests/images/iss634.gif +Tests/images/iss634.webp +Tests/images/issue_2278.tif +Tests/images/issue_2811.gif +Tests/images/itxt_chunks.png +Tests/images/jpeg_ff00_header.jpg +Tests/images/junk_jpeg_header.jpg +Tests/images/l2rgb_read.bmp +Tests/images/l_trns.png +Tests/images/la.tga +Tests/images/lab-green.tif +Tests/images/lab-red.tif +Tests/images/lab.tif +Tests/images/libtiff_segfault.tif +Tests/images/linear_gradient.png +Tests/images/morph_a.png +Tests/images/multiline_text.png +Tests/images/multiline_text_center.png +Tests/images/multiline_text_right.png +Tests/images/multiline_text_spacing.png +Tests/images/multipage-lastframe.tif +Tests/images/multipage-mmap.tiff +Tests/images/multipage.tiff +Tests/images/negative_size.ppm +Tests/images/no-dpi-in-exif.jpg +Tests/images/no_cursors.cur +Tests/images/no_rows_per_strip.tif +Tests/images/non_zero_bb.eps +Tests/images/non_zero_bb.png +Tests/images/non_zero_bb_scale2.png +Tests/images/old-style-jpeg-compression.png +Tests/images/old-style-jpeg-compression.tif +Tests/images/ossfuzz-4836216264589312.pcx +Tests/images/ossfuzz-5730089102868480.sgi +Tests/images/p_trns_single.png +Tests/images/pcx_overrun.bin +Tests/images/pcx_overrun2.bin +Tests/images/photoshop-200dpi-broken.jpg +Tests/images/photoshop-200dpi.jpg +Tests/images/pil123p.png +Tests/images/pil123rgba.png +Tests/images/pil136.png +Tests/images/pil136.tiff +Tests/images/pil168.png +Tests/images/pil168.tif +Tests/images/pil184.pcx +Tests/images/pil_sample_cmyk.eps +Tests/images/pil_sample_cmyk.jpg +Tests/images/pil_sample_rgb.jpg +Tests/images/pillow.icns +Tests/images/pillow.ico +Tests/images/pillow2.icns +Tests/images/pillow3.icns +Tests/images/png_decompression_dos.png +Tests/images/pngtest_bad.png.bin +Tests/images/pport_g4.tif +Tests/images/python.ico +Tests/images/radial_gradient.png +Tests/images/radial_gradients.png +Tests/images/raw_negative_stride.bin +Tests/images/rdf.tif +Tests/images/rectangle_surrounding_text.png +Tests/images/reqd_showpage.eps +Tests/images/reqd_showpage.png +Tests/images/rgb.jpg +Tests/images/rgb32bf-rgba.bmp +Tests/images/rgb32rle.tga +Tests/images/rgb_trns.png +Tests/images/rgb_trns_ycbc.j2k +Tests/images/rgb_trns_ycbc.jp2 +Tests/images/rotate_45_no_fill.png +Tests/images/rotate_45_with_fill.png +Tests/images/sgi_crash.bin +Tests/images/sgi_overrun.bin +Tests/images/sgi_overrun_expandrow.bin +Tests/images/sgi_overrun_expandrow2.bin +Tests/images/sgi_overrun_expandrowF04.bin +Tests/images/standard_embedded.png +Tests/images/sugarshack.mpo +Tests/images/sugarshack_bad_mpo_header.jpg +Tests/images/sugarshack_frame_size.mpo +Tests/images/sugarshack_ifd_offset.mpo +Tests/images/sugarshack_no_data.mpo +Tests/images/sunraster.im1 +Tests/images/sunraster.im1.png +Tests/images/tRNS_null_1x1.png +Tests/images/test-card-lossless.jp2 +Tests/images/test-card-lossy-tiled.jp2 +Tests/images/test-card.png +Tests/images/test-ole-file.doc +Tests/images/test.colors.gif +Tests/images/test.gpl +Tests/images/test_Nastalifont_text.png +Tests/images/test_anchor_multiline_lm_center.png +Tests/images/test_anchor_multiline_lm_left.png +Tests/images/test_anchor_multiline_lm_right.png +Tests/images/test_anchor_multiline_ma_center.png +Tests/images/test_anchor_multiline_md_center.png +Tests/images/test_anchor_multiline_mm_center.png +Tests/images/test_anchor_multiline_mm_left.png +Tests/images/test_anchor_multiline_mm_right.png +Tests/images/test_anchor_multiline_rm_center.png +Tests/images/test_anchor_multiline_rm_left.png +Tests/images/test_anchor_multiline_rm_right.png +Tests/images/test_anchor_quick_ls.png +Tests/images/test_anchor_quick_ma.png +Tests/images/test_anchor_quick_mb.png +Tests/images/test_anchor_quick_md.png +Tests/images/test_anchor_quick_mm.png +Tests/images/test_anchor_quick_ms.png +Tests/images/test_anchor_quick_mt.png +Tests/images/test_anchor_quick_rs.png +Tests/images/test_anchor_ttb_f_lt.png +Tests/images/test_anchor_ttb_f_mm.png +Tests/images/test_anchor_ttb_f_rb.png +Tests/images/test_anchor_ttb_f_sm.png +Tests/images/test_arabictext_features.png +Tests/images/test_combine_caron.png +Tests/images/test_combine_caron_below.png +Tests/images/test_combine_caron_below_lb.png +Tests/images/test_combine_caron_below_ld.png +Tests/images/test_combine_caron_below_ls.png +Tests/images/test_combine_caron_below_ttb.png +Tests/images/test_combine_caron_below_ttb_lb.png +Tests/images/test_combine_caron_la.png +Tests/images/test_combine_caron_ls.png +Tests/images/test_combine_caron_lt.png +Tests/images/test_combine_caron_ttb.png +Tests/images/test_combine_caron_ttb_lt.png +Tests/images/test_combine_double_breve_below.png +Tests/images/test_combine_double_breve_below_ma.png +Tests/images/test_combine_double_breve_below_ra.png +Tests/images/test_combine_double_breve_below_ttb.png +Tests/images/test_combine_double_breve_below_ttb_mt.png +Tests/images/test_combine_double_breve_below_ttb_rt.png +Tests/images/test_combine_double_breve_below_ttb_st.png +Tests/images/test_combine_multiline_lm_center.png +Tests/images/test_combine_multiline_lm_left.png +Tests/images/test_combine_multiline_lm_right.png +Tests/images/test_combine_multiline_mm_center.png +Tests/images/test_combine_multiline_mm_left.png +Tests/images/test_combine_multiline_mm_right.png +Tests/images/test_combine_multiline_rm_center.png +Tests/images/test_combine_multiline_rm_left.png +Tests/images/test_combine_multiline_rm_right.png +Tests/images/test_combine_overline.png +Tests/images/test_combine_overline_la.png +Tests/images/test_combine_overline_ra.png +Tests/images/test_combine_overline_ttb.png +Tests/images/test_combine_overline_ttb_mt.png +Tests/images/test_combine_overline_ttb_rt.png +Tests/images/test_combine_overline_ttb_st.png +Tests/images/test_complex_unicode_text.png +Tests/images/test_complex_unicode_text2.png +Tests/images/test_direction_ltr.png +Tests/images/test_direction_rtl.png +Tests/images/test_direction_ttb.png +Tests/images/test_direction_ttb_stroke.png +Tests/images/test_draw_pbm_target.png +Tests/images/test_draw_pbm_ter_en_target.png +Tests/images/test_draw_pbm_ter_pl_target.png +Tests/images/test_extents.gif +Tests/images/test_kerning_features.png +Tests/images/test_language.png +Tests/images/test_ligature_features.png +Tests/images/test_text.png +Tests/images/test_x_max_and_y_offset.png +Tests/images/test_y_offset.png +Tests/images/text_mono.gif +Tests/images/tga_id_field.tga +Tests/images/tiff_16bit_RGB.tiff +Tests/images/tiff_16bit_RGB_target.png +Tests/images/tiff_16bit_RGBa.tiff +Tests/images/tiff_16bit_RGBa_target.png +Tests/images/tiff_adobe_deflate.png +Tests/images/tiff_adobe_deflate.tif +Tests/images/tiff_overflow_rows_per_strip.tif +Tests/images/tiff_strip_cmyk_16l_jpeg.tif +Tests/images/tiff_strip_cmyk_jpeg.tif +Tests/images/tiff_strip_planar_raw.tif +Tests/images/tiff_strip_planar_raw_with_overviews.tif +Tests/images/tiff_strip_raw.tif +Tests/images/tiff_strip_ycbcr_jpeg_1x1_sampling.tif +Tests/images/tiff_strip_ycbcr_jpeg_2x2_sampling.tif +Tests/images/tiff_tiled_cmyk_jpeg.tif +Tests/images/tiff_tiled_planar_raw.tif +Tests/images/tiff_tiled_raw.tif +Tests/images/tiff_tiled_ycbcr_jpeg_1x1_sampling.tif +Tests/images/tiff_tiled_ycbcr_jpeg_2x2_sampling.tif +Tests/images/tiff_wrong_bits_per_sample.tiff +Tests/images/total-pages-zero.tif +Tests/images/transparent.gif +Tests/images/transparent.png +Tests/images/transparent.sgi +Tests/images/transparent.webp +Tests/images/transparent_background_text.png +Tests/images/truncated_image.png +Tests/images/truncated_jpeg.jpg +Tests/images/tv.rgb +Tests/images/tv16.sgi +Tests/images/uint16_1_4660.tif +Tests/images/unbound_variable.jp2 +Tests/images/uncompressed_rgb.dds +Tests/images/uncompressed_rgb.png +Tests/images/unicode_extended.png +Tests/images/unimplemented_dxgi_format.dds +Tests/images/unimplemented_pixel_format.dds +Tests/images/variation_adobe.png +Tests/images/variation_adobe_axes.png +Tests/images/variation_adobe_name.png +Tests/images/variation_adobe_older_harfbuzz.png +Tests/images/variation_adobe_older_harfbuzz_axes.png +Tests/images/variation_adobe_older_harfbuzz_name.png +Tests/images/variation_tiny.png +Tests/images/variation_tiny_axes.png +Tests/images/variation_tiny_name.png +Tests/images/xmp_tags_orientation.png +Tests/images/zero_bb.eps +Tests/images/zero_bb.png +Tests/images/zero_bb_emptyline.eps +Tests/images/zero_bb_scale2.png +Tests/images/apng/blend_op_over.png +Tests/images/apng/blend_op_over_near_transparent.png +Tests/images/apng/blend_op_source_near_transparent.png +Tests/images/apng/blend_op_source_solid.png +Tests/images/apng/blend_op_source_transparent.png +Tests/images/apng/chunk_actl_after_idat.png +Tests/images/apng/chunk_multi_actl.png +Tests/images/apng/chunk_no_actl.png +Tests/images/apng/chunk_no_fctl.png +Tests/images/apng/chunk_no_fdat.png +Tests/images/apng/chunk_repeat_fctl.png +Tests/images/apng/delay.png +Tests/images/apng/delay_round.png +Tests/images/apng/delay_short_max.png +Tests/images/apng/delay_zero_denom.png +Tests/images/apng/delay_zero_numer.png +Tests/images/apng/dispose_op_background.png +Tests/images/apng/dispose_op_background_before_region.png +Tests/images/apng/dispose_op_background_final.png +Tests/images/apng/dispose_op_background_p_mode.png +Tests/images/apng/dispose_op_background_region.png +Tests/images/apng/dispose_op_none.png +Tests/images/apng/dispose_op_none_region.png +Tests/images/apng/dispose_op_previous.png +Tests/images/apng/dispose_op_previous_final.png +Tests/images/apng/dispose_op_previous_first.png +Tests/images/apng/dispose_op_previous_frame.png +Tests/images/apng/dispose_op_previous_region.png +Tests/images/apng/fctl_actl.png +Tests/images/apng/mode_16bit.png +Tests/images/apng/mode_greyscale.png +Tests/images/apng/mode_greyscale_alpha.png +Tests/images/apng/mode_palette.png +Tests/images/apng/mode_palette_1bit_alpha.png +Tests/images/apng/mode_palette_alpha.png +Tests/images/apng/num_plays.png +Tests/images/apng/num_plays_1.png +Tests/images/apng/sequence_fdat_fctl.png +Tests/images/apng/sequence_gap.png +Tests/images/apng/sequence_reorder.png +Tests/images/apng/sequence_reorder_chunk.png +Tests/images/apng/sequence_repeat.png +Tests/images/apng/sequence_repeat_chunk.png +Tests/images/apng/sequence_start.png +Tests/images/apng/single_frame.png +Tests/images/apng/single_frame_default.png +Tests/images/apng/split_fdat.png +Tests/images/apng/split_fdat_zero_chunk.png +Tests/images/apng/syntax_num_frames_high.png +Tests/images/apng/syntax_num_frames_invalid.png +Tests/images/apng/syntax_num_frames_low.png +Tests/images/apng/syntax_num_frames_zero.png +Tests/images/apng/syntax_num_frames_zero_default.png +Tests/images/blp/blp1_jpeg.blp +Tests/images/blp/blp2_dxt1.blp +Tests/images/blp/blp2_dxt1.png +Tests/images/blp/blp2_dxt1a.blp +Tests/images/blp/blp2_dxt1a.png +Tests/images/blp/blp2_raw.blp +Tests/images/blp/blp2_raw.png +Tests/images/bmp/README.txt +Tests/images/bmp/b/badbitcount.bmp +Tests/images/bmp/b/badbitssize.bmp +Tests/images/bmp/b/baddens1.bmp +Tests/images/bmp/b/baddens2.bmp +Tests/images/bmp/b/badfilesize.bmp +Tests/images/bmp/b/badheadersize.bmp +Tests/images/bmp/b/badpalettesize.bmp +Tests/images/bmp/b/badplanes.bmp +Tests/images/bmp/b/badrle.bmp +Tests/images/bmp/b/badwidth.bmp +Tests/images/bmp/b/pal8badindex.bmp +Tests/images/bmp/b/reallybig.bmp +Tests/images/bmp/b/rletopdown.bmp +Tests/images/bmp/b/shortfile.bmp +Tests/images/bmp/g/pal1.bmp +Tests/images/bmp/g/pal1bg.bmp +Tests/images/bmp/g/pal1wb.bmp +Tests/images/bmp/g/pal4.bmp +Tests/images/bmp/g/pal4rle.bmp +Tests/images/bmp/g/pal8-0.bmp +Tests/images/bmp/g/pal8.bmp +Tests/images/bmp/g/pal8nonsquare.bmp +Tests/images/bmp/g/pal8os2.bmp +Tests/images/bmp/g/pal8rle.bmp +Tests/images/bmp/g/pal8topdown.bmp +Tests/images/bmp/g/pal8v4.bmp +Tests/images/bmp/g/pal8v5.bmp +Tests/images/bmp/g/pal8w124.bmp +Tests/images/bmp/g/pal8w125.bmp +Tests/images/bmp/g/pal8w126.bmp +Tests/images/bmp/g/rgb16-565.bmp +Tests/images/bmp/g/rgb16-565pal.bmp +Tests/images/bmp/g/rgb16.bmp +Tests/images/bmp/g/rgb24.bmp +Tests/images/bmp/g/rgb24pal.bmp +Tests/images/bmp/g/rgb32.bmp +Tests/images/bmp/g/rgb32bf.bmp +Tests/images/bmp/html/bkgd.png +Tests/images/bmp/html/bmpsuite.html +Tests/images/bmp/html/fakealpha.png +Tests/images/bmp/html/pal1.png +Tests/images/bmp/html/pal1bg.png +Tests/images/bmp/html/pal1p1.png +Tests/images/bmp/html/pal2.png +Tests/images/bmp/html/pal4.png +Tests/images/bmp/html/pal4rletrns-0.png +Tests/images/bmp/html/pal4rletrns-b.png +Tests/images/bmp/html/pal4rletrns.png +Tests/images/bmp/html/pal8.png +Tests/images/bmp/html/pal8nonsquare-e.png +Tests/images/bmp/html/pal8nonsquare-v.png +Tests/images/bmp/html/pal8rletrns-0.png +Tests/images/bmp/html/pal8rletrns-b.png +Tests/images/bmp/html/pal8rletrns.png +Tests/images/bmp/html/pal8w124.png +Tests/images/bmp/html/pal8w125.png +Tests/images/bmp/html/pal8w126.png +Tests/images/bmp/html/rgb16-231.png +Tests/images/bmp/html/rgb16-565.png +Tests/images/bmp/html/rgb16.png +Tests/images/bmp/html/rgb24.jpg +Tests/images/bmp/html/rgb24.png +Tests/images/bmp/html/rgba16-4444.png +Tests/images/bmp/html/rgba32.png +Tests/images/bmp/q/pal1p1.bmp +Tests/images/bmp/q/pal2.bmp +Tests/images/bmp/q/pal4rletrns.bmp +Tests/images/bmp/q/pal8offs.bmp +Tests/images/bmp/q/pal8os2sp.bmp +Tests/images/bmp/q/pal8os2v2-16.bmp +Tests/images/bmp/q/pal8os2v2.bmp +Tests/images/bmp/q/pal8oversizepal.bmp +Tests/images/bmp/q/pal8rletrns.bmp +Tests/images/bmp/q/rgb16-231.bmp +Tests/images/bmp/q/rgb24jpeg.bmp +Tests/images/bmp/q/rgb24largepal.bmp +Tests/images/bmp/q/rgb24lprof.bmp +Tests/images/bmp/q/rgb24png.bmp +Tests/images/bmp/q/rgb24prof.bmp +Tests/images/bmp/q/rgb32-111110.bmp +Tests/images/bmp/q/rgb32bf-xbgr.bmp +Tests/images/bmp/q/rgb32fakealpha.bmp +Tests/images/bmp/q/rgba16-4444.bmp +Tests/images/bmp/q/rgba32.bmp +Tests/images/bmp/q/rgba32abf.bmp +Tests/images/fli_oob/02r/02r00.fli +Tests/images/fli_oob/02r/notes +Tests/images/fli_oob/02r/reproducing +Tests/images/fli_oob/02r/others/02r01.fli +Tests/images/fli_oob/02r/others/02r02.fli +Tests/images/fli_oob/02r/others/02r03.fli +Tests/images/fli_oob/02r/others/02r04.fli +Tests/images/fli_oob/03r/03r00.fli +Tests/images/fli_oob/03r/notes +Tests/images/fli_oob/03r/reproducing +Tests/images/fli_oob/03r/others/03r01.fli +Tests/images/fli_oob/03r/others/03r02.fli +Tests/images/fli_oob/03r/others/03r03.fli +Tests/images/fli_oob/03r/others/03r04.fli +Tests/images/fli_oob/03r/others/03r05.fli +Tests/images/fli_oob/03r/others/03r06.fli +Tests/images/fli_oob/03r/others/03r07.fli +Tests/images/fli_oob/03r/others/03r08.fli +Tests/images/fli_oob/03r/others/03r09.fli +Tests/images/fli_oob/03r/others/03r10.fli +Tests/images/fli_oob/03r/others/03r11.fli +Tests/images/fli_oob/04r/04r00.fli +Tests/images/fli_oob/04r/initial.fli +Tests/images/fli_oob/04r/notes +Tests/images/fli_oob/04r/reproducing +Tests/images/fli_oob/04r/others/04r01.fli +Tests/images/fli_oob/04r/others/04r02.fli +Tests/images/fli_oob/04r/others/04r03.fli +Tests/images/fli_oob/04r/others/04r04.fli +Tests/images/fli_oob/04r/others/04r05.fli +Tests/images/fli_oob/05r/05r00.fli +Tests/images/fli_oob/05r/notes +Tests/images/fli_oob/05r/reproducing +Tests/images/fli_oob/05r/others/05r01.fli +Tests/images/fli_oob/05r/others/05r02.fli +Tests/images/fli_oob/05r/others/05r03.fli +Tests/images/fli_oob/05r/others/05r04.fli +Tests/images/fli_oob/05r/others/05r05.fli +Tests/images/fli_oob/05r/others/05r06.fli +Tests/images/fli_oob/05r/others/05r07.fli +Tests/images/fli_oob/06r/06r00.fli +Tests/images/fli_oob/06r/notes +Tests/images/fli_oob/06r/reproducing +Tests/images/fli_oob/06r/others/06r01.fli +Tests/images/fli_oob/06r/others/06r02.fli +Tests/images/fli_oob/06r/others/06r03.fli +Tests/images/fli_oob/06r/others/06r04.fli +Tests/images/fli_oob/patch0/000000 +Tests/images/fli_oob/patch0/000001 +Tests/images/fli_oob/patch0/000002 +Tests/images/fli_oob/patch0/000003 +Tests/images/imagedraw/line_horizontal_slope1px_w2px.png +Tests/images/imagedraw/line_horizontal_w101px.png +Tests/images/imagedraw/line_horizontal_w2px_inverted.png +Tests/images/imagedraw/line_horizontal_w2px_normal.png +Tests/images/imagedraw/line_horizontal_w3px.png +Tests/images/imagedraw/line_oblique_45_w3px_a.png +Tests/images/imagedraw/line_oblique_45_w3px_b.png +Tests/images/imagedraw/line_vertical_slope1px_w2px.png +Tests/images/imagedraw/line_vertical_w101px.png +Tests/images/imagedraw/line_vertical_w2px_inverted.png +Tests/images/imagedraw/line_vertical_w2px_normal.png +Tests/images/imagedraw/line_vertical_w3px.png +Tests/images/imagedraw/square.png +Tests/images/imagedraw/triangle_right.png +Tests/images/tga/common/1x1_l.png +Tests/images/tga/common/1x1_l_bl_raw.tga +Tests/images/tga/common/1x1_l_bl_rle.tga +Tests/images/tga/common/1x1_l_tl_raw.tga +Tests/images/tga/common/1x1_l_tl_rle.tga +Tests/images/tga/common/200x32_l.png +Tests/images/tga/common/200x32_l_bl_raw.tga +Tests/images/tga/common/200x32_l_bl_rle.tga +Tests/images/tga/common/200x32_l_tl_raw.tga +Tests/images/tga/common/200x32_l_tl_rle.tga +Tests/images/tga/common/200x32_la.png +Tests/images/tga/common/200x32_la_bl_raw.tga +Tests/images/tga/common/200x32_la_bl_rle.tga +Tests/images/tga/common/200x32_la_tl_raw.tga +Tests/images/tga/common/200x32_la_tl_rle.tga +Tests/images/tga/common/200x32_p.png +Tests/images/tga/common/200x32_p_bl_raw.tga +Tests/images/tga/common/200x32_p_bl_rle.tga +Tests/images/tga/common/200x32_p_tl_raw.tga +Tests/images/tga/common/200x32_p_tl_rle.tga +Tests/images/tga/common/200x32_rgb.png +Tests/images/tga/common/200x32_rgb_bl_raw.tga +Tests/images/tga/common/200x32_rgb_bl_rle.tga +Tests/images/tga/common/200x32_rgb_tl_raw.tga +Tests/images/tga/common/200x32_rgb_tl_rle.tga +Tests/images/tga/common/200x32_rgba.png +Tests/images/tga/common/200x32_rgba_bl_raw.tga +Tests/images/tga/common/200x32_rgba_bl_rle.tga +Tests/images/tga/common/200x32_rgba_tl_raw.tga +Tests/images/tga/common/200x32_rgba_tl_rle.tga +Tests/images/tga/common/readme.txt +Tests/images/tiff_gray_2_4_bpp/hopper2.tif +Tests/images/tiff_gray_2_4_bpp/hopper2I.tif +Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif +Tests/images/tiff_gray_2_4_bpp/hopper2R.tif +Tests/images/tiff_gray_2_4_bpp/hopper4.tif +Tests/images/tiff_gray_2_4_bpp/hopper4I.tif +Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif +Tests/images/tiff_gray_2_4_bpp/hopper4R.tif +depends/README.rst +depends/download-and-extract.sh +depends/install_extra_test_images.sh +depends/install_imagequant.sh +depends/install_openjpeg.sh +depends/install_raqm.sh +depends/install_raqm_cmake.sh +depends/install_webp.sh +depends/termux.sh +docs/COPYING +docs/Guardfile +docs/Makefile +docs/PIL.rst +docs/about.rst +docs/conf.py +docs/deprecations.rst +docs/index.rst +docs/installation.rst +docs/make.bat +docs/porting.rst +docs/example/DdsImagePlugin.py +docs/example/anchors.png +docs/example/anchors.py +docs/handbook/appendices.rst +docs/handbook/concepts.rst +docs/handbook/image-file-formats.rst +docs/handbook/index.rst +docs/handbook/overview.rst +docs/handbook/text-anchors.rst +docs/handbook/tutorial.rst +docs/handbook/writing-your-own-file-decoder.rst +docs/reference/ExifTags.rst +docs/reference/Image.rst +docs/reference/ImageChops.rst +docs/reference/ImageCms.rst +docs/reference/ImageColor.rst +docs/reference/ImageDraw.rst +docs/reference/ImageEnhance.rst +docs/reference/ImageFile.rst +docs/reference/ImageFilter.rst +docs/reference/ImageFont.rst +docs/reference/ImageGrab.rst +docs/reference/ImageMath.rst +docs/reference/ImageMorph.rst +docs/reference/ImageOps.rst +docs/reference/ImagePalette.rst +docs/reference/ImagePath.rst +docs/reference/ImageQt.rst +docs/reference/ImageSequence.rst +docs/reference/ImageShow.rst +docs/reference/ImageStat.rst +docs/reference/ImageTk.rst +docs/reference/ImageWin.rst +docs/reference/JpegPresets.rst +docs/reference/PSDraw.rst +docs/reference/PixelAccess.rst +docs/reference/PyAccess.rst +docs/reference/TiffTags.rst +docs/reference/block_allocator.rst +docs/reference/features.rst +docs/reference/index.rst +docs/reference/internal_design.rst +docs/reference/internal_modules.rst +docs/reference/limits.rst +docs/reference/open_files.rst +docs/reference/plugins.rst +docs/releasenotes/2.7.0.rst +docs/releasenotes/2.8.0.rst +docs/releasenotes/3.0.0.rst +docs/releasenotes/3.1.0.rst +docs/releasenotes/3.1.1.rst +docs/releasenotes/3.1.2.rst +docs/releasenotes/3.2.0.rst +docs/releasenotes/3.3.0.rst +docs/releasenotes/3.3.2.rst +docs/releasenotes/3.4.0.rst +docs/releasenotes/4.0.0.rst +docs/releasenotes/4.1.0.rst +docs/releasenotes/4.1.1.rst +docs/releasenotes/4.2.0.rst +docs/releasenotes/4.2.1.rst +docs/releasenotes/4.3.0.rst +docs/releasenotes/5.0.0.rst +docs/releasenotes/5.1.0.rst +docs/releasenotes/5.2.0.rst +docs/releasenotes/5.3.0.rst +docs/releasenotes/5.4.0.rst +docs/releasenotes/5.4.1.rst +docs/releasenotes/6.0.0.rst +docs/releasenotes/6.1.0.rst +docs/releasenotes/6.2.0.rst +docs/releasenotes/6.2.1.rst +docs/releasenotes/6.2.2.rst +docs/releasenotes/7.0.0.rst +docs/releasenotes/7.1.0.rst +docs/releasenotes/7.1.1.rst +docs/releasenotes/7.1.2.rst +docs/releasenotes/7.2.0.rst +docs/releasenotes/8.0.0.rst +docs/releasenotes/8.0.1.rst +docs/releasenotes/8.1.0.rst +docs/releasenotes/index.rst +docs/releasenotes/template.rst +docs/releasenotes/versioning.rst +docs/resources/anchor_horizontal.svg +docs/resources/anchor_vertical.svg +docs/resources/favicon.ico +docs/resources/pillow-logo.png +docs/resources/css/dark.css +docs/resources/css/light.css +docs/resources/js/script.js +src/_imaging.c +src/_imagingcms.c +src/_imagingft.c +src/_imagingmath.c +src/_imagingmorph.c +src/_imagingtk.c +src/_webp.c +src/decode.c +src/display.c +src/encode.c +src/map.c +src/outline.c +src/path.c +src/PIL/BdfFontFile.py +src/PIL/BlpImagePlugin.py +src/PIL/BmpImagePlugin.py +src/PIL/BufrStubImagePlugin.py +src/PIL/ContainerIO.py +src/PIL/CurImagePlugin.py +src/PIL/DcxImagePlugin.py +src/PIL/DdsImagePlugin.py +src/PIL/EpsImagePlugin.py +src/PIL/ExifTags.py +src/PIL/FitsStubImagePlugin.py +src/PIL/FliImagePlugin.py +src/PIL/FontFile.py +src/PIL/FpxImagePlugin.py +src/PIL/FtexImagePlugin.py +src/PIL/GbrImagePlugin.py +src/PIL/GdImageFile.py +src/PIL/GifImagePlugin.py +src/PIL/GimpGradientFile.py +src/PIL/GimpPaletteFile.py +src/PIL/GribStubImagePlugin.py +src/PIL/Hdf5StubImagePlugin.py +src/PIL/IcnsImagePlugin.py +src/PIL/IcoImagePlugin.py +src/PIL/ImImagePlugin.py +src/PIL/Image.py +src/PIL/ImageChops.py +src/PIL/ImageCms.py +src/PIL/ImageColor.py +src/PIL/ImageDraw.py +src/PIL/ImageDraw2.py +src/PIL/ImageEnhance.py +src/PIL/ImageFile.py +src/PIL/ImageFilter.py +src/PIL/ImageFont.py +src/PIL/ImageGrab.py +src/PIL/ImageMath.py +src/PIL/ImageMode.py +src/PIL/ImageMorph.py +src/PIL/ImageOps.py +src/PIL/ImagePalette.py +src/PIL/ImagePath.py +src/PIL/ImageQt.py +src/PIL/ImageSequence.py +src/PIL/ImageShow.py +src/PIL/ImageStat.py +src/PIL/ImageTk.py +src/PIL/ImageTransform.py +src/PIL/ImageWin.py +src/PIL/ImtImagePlugin.py +src/PIL/IptcImagePlugin.py +src/PIL/Jpeg2KImagePlugin.py +src/PIL/JpegImagePlugin.py +src/PIL/JpegPresets.py +src/PIL/McIdasImagePlugin.py +src/PIL/MicImagePlugin.py +src/PIL/MpegImagePlugin.py +src/PIL/MpoImagePlugin.py +src/PIL/MspImagePlugin.py +src/PIL/PSDraw.py +src/PIL/PaletteFile.py +src/PIL/PalmImagePlugin.py +src/PIL/PcdImagePlugin.py +src/PIL/PcfFontFile.py +src/PIL/PcxImagePlugin.py +src/PIL/PdfImagePlugin.py +src/PIL/PdfParser.py +src/PIL/PixarImagePlugin.py +src/PIL/PngImagePlugin.py +src/PIL/PpmImagePlugin.py +src/PIL/PsdImagePlugin.py +src/PIL/PyAccess.py +src/PIL/SgiImagePlugin.py +src/PIL/SpiderImagePlugin.py +src/PIL/SunImagePlugin.py +src/PIL/TarIO.py +src/PIL/TgaImagePlugin.py +src/PIL/TiffImagePlugin.py +src/PIL/TiffTags.py +src/PIL/WalImageFile.py +src/PIL/WebPImagePlugin.py +src/PIL/WmfImagePlugin.py +src/PIL/XVThumbImagePlugin.py +src/PIL/XbmImagePlugin.py +src/PIL/XpmImagePlugin.py +src/PIL/__init__.py +src/PIL/__main__.py +src/PIL/_binary.py +src/PIL/_tkinter_finder.py +src/PIL/_util.py +src/PIL/_version.py +src/PIL/features.py +src/Pillow.egg-info/PKG-INFO +src/Pillow.egg-info/SOURCES.txt +src/Pillow.egg-info/dependency_links.txt +src/Pillow.egg-info/top_level.txt +src/Pillow.egg-info/zip-safe +src/Tk/_tkmini.h +src/Tk/tkImaging.c +src/libImaging/Access.c +src/libImaging/AlphaComposite.c +src/libImaging/Bands.c +src/libImaging/BcnDecode.c +src/libImaging/Bit.h +src/libImaging/BitDecode.c +src/libImaging/Blend.c +src/libImaging/BoxBlur.c +src/libImaging/Chops.c +src/libImaging/ColorLUT.c +src/libImaging/Convert.c +src/libImaging/ConvertYCbCr.c +src/libImaging/Copy.c +src/libImaging/Crop.c +src/libImaging/Dib.c +src/libImaging/Draw.c +src/libImaging/Effects.c +src/libImaging/EpsEncode.c +src/libImaging/Except.c +src/libImaging/File.c +src/libImaging/Fill.c +src/libImaging/Filter.c +src/libImaging/FliDecode.c +src/libImaging/Geometry.c +src/libImaging/GetBBox.c +src/libImaging/Gif.h +src/libImaging/GifDecode.c +src/libImaging/GifEncode.c +src/libImaging/HexDecode.c +src/libImaging/Histo.c +src/libImaging/ImDib.h +src/libImaging/ImPlatform.h +src/libImaging/Imaging.h +src/libImaging/ImagingUtils.h +src/libImaging/Jpeg.h +src/libImaging/Jpeg2K.h +src/libImaging/Jpeg2KDecode.c +src/libImaging/Jpeg2KEncode.c +src/libImaging/JpegDecode.c +src/libImaging/JpegEncode.c +src/libImaging/Matrix.c +src/libImaging/ModeFilter.c +src/libImaging/Negative.c +src/libImaging/Offset.c +src/libImaging/Pack.c +src/libImaging/PackDecode.c +src/libImaging/Palette.c +src/libImaging/Paste.c +src/libImaging/PcdDecode.c +src/libImaging/PcxDecode.c +src/libImaging/PcxEncode.c +src/libImaging/Point.c +src/libImaging/Quant.c +src/libImaging/QuantHash.c +src/libImaging/QuantHash.h +src/libImaging/QuantHeap.c +src/libImaging/QuantHeap.h +src/libImaging/QuantOctree.c +src/libImaging/QuantOctree.h +src/libImaging/QuantPngQuant.c +src/libImaging/QuantPngQuant.h +src/libImaging/QuantTypes.h +src/libImaging/RankFilter.c +src/libImaging/Raw.h +src/libImaging/RawDecode.c +src/libImaging/RawEncode.c +src/libImaging/Reduce.c +src/libImaging/Resample.c +src/libImaging/Sgi.h +src/libImaging/SgiRleDecode.c +src/libImaging/Storage.c +src/libImaging/SunRleDecode.c +src/libImaging/TgaRleDecode.c +src/libImaging/TgaRleEncode.c +src/libImaging/TiffDecode.c +src/libImaging/TiffDecode.h +src/libImaging/Unpack.c +src/libImaging/UnpackYCC.c +src/libImaging/UnsharpMask.c +src/libImaging/XbmDecode.c +src/libImaging/XbmEncode.c +src/libImaging/ZipCodecs.h +src/libImaging/ZipDecode.c +src/libImaging/ZipEncode.c +src/libImaging/codec_fd.c +src/libImaging/raqm.h +winbuild/README.md +winbuild/build.rst +winbuild/build_prepare.py +winbuild/fribidi.cmake +winbuild/raqm.cmake +winbuild/tiff.opt \ No newline at end of file diff --git a/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/dependency_links.txt b/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/installed-files.txt b/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/installed-files.txt new file mode 100644 index 00000000..e30fca3b --- /dev/null +++ b/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/installed-files.txt @@ -0,0 +1,195 @@ +../PIL/BdfFontFile.py +../PIL/BlpImagePlugin.py +../PIL/BmpImagePlugin.py +../PIL/BufrStubImagePlugin.py +../PIL/ContainerIO.py +../PIL/CurImagePlugin.py +../PIL/DcxImagePlugin.py +../PIL/DdsImagePlugin.py +../PIL/EpsImagePlugin.py +../PIL/ExifTags.py +../PIL/FitsStubImagePlugin.py +../PIL/FliImagePlugin.py +../PIL/FontFile.py +../PIL/FpxImagePlugin.py +../PIL/FtexImagePlugin.py +../PIL/GbrImagePlugin.py +../PIL/GdImageFile.py +../PIL/GifImagePlugin.py +../PIL/GimpGradientFile.py +../PIL/GimpPaletteFile.py +../PIL/GribStubImagePlugin.py +../PIL/Hdf5StubImagePlugin.py +../PIL/IcnsImagePlugin.py +../PIL/IcoImagePlugin.py +../PIL/ImImagePlugin.py +../PIL/Image.py +../PIL/ImageChops.py +../PIL/ImageCms.py +../PIL/ImageColor.py +../PIL/ImageDraw.py +../PIL/ImageDraw2.py +../PIL/ImageEnhance.py +../PIL/ImageFile.py +../PIL/ImageFilter.py +../PIL/ImageFont.py +../PIL/ImageGrab.py +../PIL/ImageMath.py +../PIL/ImageMode.py +../PIL/ImageMorph.py +../PIL/ImageOps.py +../PIL/ImagePalette.py +../PIL/ImagePath.py +../PIL/ImageQt.py +../PIL/ImageSequence.py +../PIL/ImageShow.py +../PIL/ImageStat.py +../PIL/ImageTk.py +../PIL/ImageTransform.py +../PIL/ImageWin.py +../PIL/ImtImagePlugin.py +../PIL/IptcImagePlugin.py +../PIL/Jpeg2KImagePlugin.py +../PIL/JpegImagePlugin.py +../PIL/JpegPresets.py +../PIL/McIdasImagePlugin.py +../PIL/MicImagePlugin.py +../PIL/MpegImagePlugin.py +../PIL/MpoImagePlugin.py +../PIL/MspImagePlugin.py +../PIL/PSDraw.py +../PIL/PaletteFile.py +../PIL/PalmImagePlugin.py +../PIL/PcdImagePlugin.py +../PIL/PcfFontFile.py +../PIL/PcxImagePlugin.py +../PIL/PdfImagePlugin.py +../PIL/PdfParser.py +../PIL/PixarImagePlugin.py +../PIL/PngImagePlugin.py +../PIL/PpmImagePlugin.py +../PIL/PsdImagePlugin.py +../PIL/PyAccess.py +../PIL/SgiImagePlugin.py +../PIL/SpiderImagePlugin.py +../PIL/SunImagePlugin.py +../PIL/TarIO.py +../PIL/TgaImagePlugin.py +../PIL/TiffImagePlugin.py +../PIL/TiffTags.py +../PIL/WalImageFile.py +../PIL/WebPImagePlugin.py +../PIL/WmfImagePlugin.py +../PIL/XVThumbImagePlugin.py +../PIL/XbmImagePlugin.py +../PIL/XpmImagePlugin.py +../PIL/__init__.py +../PIL/__main__.py +../PIL/__pycache__/BdfFontFile.cpython-311.pyc +../PIL/__pycache__/BlpImagePlugin.cpython-311.pyc +../PIL/__pycache__/BmpImagePlugin.cpython-311.pyc +../PIL/__pycache__/BufrStubImagePlugin.cpython-311.pyc +../PIL/__pycache__/ContainerIO.cpython-311.pyc +../PIL/__pycache__/CurImagePlugin.cpython-311.pyc +../PIL/__pycache__/DcxImagePlugin.cpython-311.pyc +../PIL/__pycache__/DdsImagePlugin.cpython-311.pyc +../PIL/__pycache__/EpsImagePlugin.cpython-311.pyc +../PIL/__pycache__/ExifTags.cpython-311.pyc +../PIL/__pycache__/FitsStubImagePlugin.cpython-311.pyc +../PIL/__pycache__/FliImagePlugin.cpython-311.pyc +../PIL/__pycache__/FontFile.cpython-311.pyc +../PIL/__pycache__/FpxImagePlugin.cpython-311.pyc +../PIL/__pycache__/FtexImagePlugin.cpython-311.pyc +../PIL/__pycache__/GbrImagePlugin.cpython-311.pyc +../PIL/__pycache__/GdImageFile.cpython-311.pyc +../PIL/__pycache__/GifImagePlugin.cpython-311.pyc +../PIL/__pycache__/GimpGradientFile.cpython-311.pyc +../PIL/__pycache__/GimpPaletteFile.cpython-311.pyc +../PIL/__pycache__/GribStubImagePlugin.cpython-311.pyc +../PIL/__pycache__/Hdf5StubImagePlugin.cpython-311.pyc +../PIL/__pycache__/IcnsImagePlugin.cpython-311.pyc +../PIL/__pycache__/IcoImagePlugin.cpython-311.pyc +../PIL/__pycache__/ImImagePlugin.cpython-311.pyc +../PIL/__pycache__/Image.cpython-311.pyc +../PIL/__pycache__/ImageChops.cpython-311.pyc +../PIL/__pycache__/ImageCms.cpython-311.pyc +../PIL/__pycache__/ImageColor.cpython-311.pyc +../PIL/__pycache__/ImageDraw.cpython-311.pyc +../PIL/__pycache__/ImageDraw2.cpython-311.pyc +../PIL/__pycache__/ImageEnhance.cpython-311.pyc +../PIL/__pycache__/ImageFile.cpython-311.pyc +../PIL/__pycache__/ImageFilter.cpython-311.pyc +../PIL/__pycache__/ImageFont.cpython-311.pyc +../PIL/__pycache__/ImageGrab.cpython-311.pyc +../PIL/__pycache__/ImageMath.cpython-311.pyc +../PIL/__pycache__/ImageMode.cpython-311.pyc +../PIL/__pycache__/ImageMorph.cpython-311.pyc +../PIL/__pycache__/ImageOps.cpython-311.pyc +../PIL/__pycache__/ImagePalette.cpython-311.pyc +../PIL/__pycache__/ImagePath.cpython-311.pyc +../PIL/__pycache__/ImageQt.cpython-311.pyc +../PIL/__pycache__/ImageSequence.cpython-311.pyc +../PIL/__pycache__/ImageShow.cpython-311.pyc +../PIL/__pycache__/ImageStat.cpython-311.pyc +../PIL/__pycache__/ImageTk.cpython-311.pyc +../PIL/__pycache__/ImageTransform.cpython-311.pyc +../PIL/__pycache__/ImageWin.cpython-311.pyc +../PIL/__pycache__/ImtImagePlugin.cpython-311.pyc +../PIL/__pycache__/IptcImagePlugin.cpython-311.pyc +../PIL/__pycache__/Jpeg2KImagePlugin.cpython-311.pyc +../PIL/__pycache__/JpegImagePlugin.cpython-311.pyc +../PIL/__pycache__/JpegPresets.cpython-311.pyc +../PIL/__pycache__/McIdasImagePlugin.cpython-311.pyc +../PIL/__pycache__/MicImagePlugin.cpython-311.pyc +../PIL/__pycache__/MpegImagePlugin.cpython-311.pyc +../PIL/__pycache__/MpoImagePlugin.cpython-311.pyc +../PIL/__pycache__/MspImagePlugin.cpython-311.pyc +../PIL/__pycache__/PSDraw.cpython-311.pyc +../PIL/__pycache__/PaletteFile.cpython-311.pyc +../PIL/__pycache__/PalmImagePlugin.cpython-311.pyc +../PIL/__pycache__/PcdImagePlugin.cpython-311.pyc +../PIL/__pycache__/PcfFontFile.cpython-311.pyc +../PIL/__pycache__/PcxImagePlugin.cpython-311.pyc +../PIL/__pycache__/PdfImagePlugin.cpython-311.pyc +../PIL/__pycache__/PdfParser.cpython-311.pyc +../PIL/__pycache__/PixarImagePlugin.cpython-311.pyc +../PIL/__pycache__/PngImagePlugin.cpython-311.pyc +../PIL/__pycache__/PpmImagePlugin.cpython-311.pyc +../PIL/__pycache__/PsdImagePlugin.cpython-311.pyc +../PIL/__pycache__/PyAccess.cpython-311.pyc +../PIL/__pycache__/SgiImagePlugin.cpython-311.pyc +../PIL/__pycache__/SpiderImagePlugin.cpython-311.pyc +../PIL/__pycache__/SunImagePlugin.cpython-311.pyc +../PIL/__pycache__/TarIO.cpython-311.pyc +../PIL/__pycache__/TgaImagePlugin.cpython-311.pyc +../PIL/__pycache__/TiffImagePlugin.cpython-311.pyc +../PIL/__pycache__/TiffTags.cpython-311.pyc +../PIL/__pycache__/WalImageFile.cpython-311.pyc +../PIL/__pycache__/WebPImagePlugin.cpython-311.pyc +../PIL/__pycache__/WmfImagePlugin.cpython-311.pyc +../PIL/__pycache__/XVThumbImagePlugin.cpython-311.pyc +../PIL/__pycache__/XbmImagePlugin.cpython-311.pyc +../PIL/__pycache__/XpmImagePlugin.cpython-311.pyc +../PIL/__pycache__/__init__.cpython-311.pyc +../PIL/__pycache__/__main__.cpython-311.pyc +../PIL/__pycache__/_binary.cpython-311.pyc +../PIL/__pycache__/_tkinter_finder.cpython-311.pyc +../PIL/__pycache__/_util.cpython-311.pyc +../PIL/__pycache__/_version.cpython-311.pyc +../PIL/__pycache__/features.cpython-311.pyc +../PIL/_binary.py +../PIL/_imaging.cpython-311-x86_64-linux-gnu.so +../PIL/_imagingft.cpython-311-x86_64-linux-gnu.so +../PIL/_imagingmath.cpython-311-x86_64-linux-gnu.so +../PIL/_imagingmorph.cpython-311-x86_64-linux-gnu.so +../PIL/_imagingtk.cpython-311-x86_64-linux-gnu.so +../PIL/_tkinter_finder.py +../PIL/_util.py +../PIL/_version.py +../PIL/_webp.cpython-311-x86_64-linux-gnu.so +../PIL/features.py +PKG-INFO +SOURCES.txt +dependency_links.txt +top_level.txt +zip-safe diff --git a/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/top_level.txt b/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/top_level.txt new file mode 100644 index 00000000..b338169c --- /dev/null +++ b/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/top_level.txt @@ -0,0 +1 @@ +PIL diff --git a/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/zip-safe b/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/zip-safe new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Pillow-8.1.0.egg-info/zip-safe @@ -0,0 +1 @@ + diff --git a/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/AUTHORS b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/AUTHORS new file mode 100644 index 00000000..3dc22341 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/AUTHORS @@ -0,0 +1,231 @@ +Pygments is written and maintained by Georg Brandl . + +Major developers are Tim Hatch and Armin Ronacher +. + +Other contributors, listed alphabetically, are: + +* Sam Aaron -- Ioke lexer +* Ali Afshar -- image formatter +* Thomas Aglassinger -- Easytrieve, JCL, Rexx, Transact-SQL and VBScript + lexers +* Muthiah Annamalai -- Ezhil lexer +* Kumar Appaiah -- Debian control lexer +* Andreas Amann -- AppleScript lexer +* Timothy Armstrong -- Dart lexer fixes +* Jeffrey Arnold -- R/S, Rd, BUGS, Jags, and Stan lexers +* Jeremy Ashkenas -- CoffeeScript lexer +* José Joaquín Atria -- Praat lexer +* Stefan Matthias Aust -- Smalltalk lexer +* Lucas Bajolet -- Nit lexer +* Ben Bangert -- Mako lexers +* Max Battcher -- Darcs patch lexer +* Thomas Baruchel -- APL lexer +* Tim Baumann -- (Literate) Agda lexer +* Paul Baumgart, 280 North, Inc. -- Objective-J lexer +* Michael Bayer -- Myghty lexers +* Thomas Beale -- Archetype lexers +* John Benediktsson -- Factor lexer +* Trevor Bergeron -- mIRC formatter +* Vincent Bernat -- LessCSS lexer +* Christopher Bertels -- Fancy lexer +* Sébastien Bigaret -- QVT Operational lexer +* Jarrett Billingsley -- MiniD lexer +* Adam Blinkinsop -- Haskell, Redcode lexers +* Stéphane Blondon -- SGF and Sieve lexers +* Frits van Bommel -- assembler lexers +* Pierre Bourdon -- bugfixes +* Martijn Braam -- Kernel log lexer +* Matthias Bussonnier -- ANSI style handling for terminal-256 formatter +* chebee7i -- Python traceback lexer improvements +* Hiram Chirino -- Scaml and Jade lexers +* Mauricio Caceres -- SAS and Stata lexers. +* Ian Cooper -- VGL lexer +* David Corbett -- Inform, Jasmin, JSGF, Snowball, and TADS 3 lexers +* Leaf Corcoran -- MoonScript lexer +* Christopher Creutzig -- MuPAD lexer +* Daniël W. Crompton -- Pike lexer +* Pete Curry -- bugfixes +* Bryan Davis -- EBNF lexer +* Bruno Deferrari -- Shen lexer +* Giedrius Dubinskas -- HTML formatter improvements +* Owen Durni -- Haxe lexer +* Alexander Dutton, Oxford University Computing Services -- SPARQL lexer +* James Edwards -- Terraform lexer +* Nick Efford -- Python 3 lexer +* Sven Efftinge -- Xtend lexer +* Artem Egorkine -- terminal256 formatter +* Matthew Fernandez -- CAmkES lexer +* Michael Ficarra -- CPSA lexer +* James H. Fisher -- PostScript lexer +* William S. Fulton -- SWIG lexer +* Carlos Galdino -- Elixir and Elixir Console lexers +* Michael Galloy -- IDL lexer +* Naveen Garg -- Autohotkey lexer +* Simon Garnotel -- FreeFem++ lexer +* Laurent Gautier -- R/S lexer +* Alex Gaynor -- PyPy log lexer +* Richard Gerkin -- Igor Pro lexer +* Alain Gilbert -- TypeScript lexer +* Alex Gilding -- BlitzBasic lexer +* GitHub, Inc -- DASM16, Augeas, TOML, and Slash lexers +* Bertrand Goetzmann -- Groovy lexer +* Krzysiek Goj -- Scala lexer +* Rostyslav Golda -- FloScript lexer +* Andrey Golovizin -- BibTeX lexers +* Matt Good -- Genshi, Cheetah lexers +* Michał Górny -- vim modeline support +* Alex Gosse -- TrafficScript lexer +* Patrick Gotthardt -- PHP namespaces support +* Olivier Guibe -- Asymptote lexer +* Phil Hagelberg -- Fennel lexer +* Florian Hahn -- Boogie lexer +* Martin Harriman -- SNOBOL lexer +* Matthew Harrison -- SVG formatter +* Steven Hazel -- Tcl lexer +* Dan Michael Heggø -- Turtle lexer +* Aslak Hellesøy -- Gherkin lexer +* Greg Hendershott -- Racket lexer +* Justin Hendrick -- ParaSail lexer +* Jordi Gutiérrez Hermoso -- Octave lexer +* David Hess, Fish Software, Inc. -- Objective-J lexer +* Varun Hiremath -- Debian control lexer +* Rob Hoelz -- Perl 6 lexer +* Doug Hogan -- Mscgen lexer +* Ben Hollis -- Mason lexer +* Max Horn -- GAP lexer +* Alastair Houghton -- Lexer inheritance facility +* Tim Howard -- BlitzMax lexer +* Dustin Howett -- Logos lexer +* Ivan Inozemtsev -- Fantom lexer +* Hiroaki Itoh -- Shell console rewrite, Lexers for PowerShell session, + MSDOS session, BC, WDiff +* Brian R. Jackson -- Tea lexer +* Christian Jann -- ShellSession lexer +* Dennis Kaarsemaker -- sources.list lexer +* Dmitri Kabak -- Inferno Limbo lexer +* Igor Kalnitsky -- vhdl lexer +* Colin Kennedy - USD lexer +* Alexander Kit -- MaskJS lexer +* Pekka Klärck -- Robot Framework lexer +* Gerwin Klein -- Isabelle lexer +* Eric Knibbe -- Lasso lexer +* Stepan Koltsov -- Clay lexer +* Adam Koprowski -- Opa lexer +* Benjamin Kowarsch -- Modula-2 lexer +* Domen Kožar -- Nix lexer +* Oleh Krekel -- Emacs Lisp lexer +* Alexander Kriegisch -- Kconfig and AspectJ lexers +* Marek Kubica -- Scheme lexer +* Jochen Kupperschmidt -- Markdown processor +* Gerd Kurzbach -- Modelica lexer +* Jon Larimer, Google Inc. -- Smali lexer +* Olov Lassus -- Dart lexer +* Matt Layman -- TAP lexer +* Kristian Lyngstøl -- Varnish lexers +* Sylvestre Ledru -- Scilab lexer +* Chee Sing Lee -- Flatline lexer +* Mark Lee -- Vala lexer +* Valentin Lorentz -- C++ lexer improvements +* Ben Mabey -- Gherkin lexer +* Angus MacArthur -- QML lexer +* Louis Mandel -- X10 lexer +* Louis Marchand -- Eiffel lexer +* Simone Margaritelli -- Hybris lexer +* Kirk McDonald -- D lexer +* Gordon McGregor -- SystemVerilog lexer +* Stephen McKamey -- Duel/JBST lexer +* Brian McKenna -- F# lexer +* Charles McLaughlin -- Puppet lexer +* Kurt McKee -- Tera Term macro lexer +* Lukas Meuser -- BBCode formatter, Lua lexer +* Cat Miller -- Pig lexer +* Paul Miller -- LiveScript lexer +* Hong Minhee -- HTTP lexer +* Michael Mior -- Awk lexer +* Bruce Mitchener -- Dylan lexer rewrite +* Reuben Morais -- SourcePawn lexer +* Jon Morton -- Rust lexer +* Paulo Moura -- Logtalk lexer +* Mher Movsisyan -- DTD lexer +* Dejan Muhamedagic -- Crmsh lexer +* Ana Nelson -- Ragel, ANTLR, R console lexers +* Kurt Neufeld -- Markdown lexer +* Nam T. Nguyen -- Monokai style +* Jesper Noehr -- HTML formatter "anchorlinenos" +* Mike Nolta -- Julia lexer +* Jonas Obrist -- BBCode lexer +* Edward O'Callaghan -- Cryptol lexer +* David Oliva -- Rebol lexer +* Pat Pannuto -- nesC lexer +* Jon Parise -- Protocol buffers and Thrift lexers +* Benjamin Peterson -- Test suite refactoring +* Ronny Pfannschmidt -- BBCode lexer +* Dominik Picheta -- Nimrod lexer +* Andrew Pinkham -- RTF Formatter Refactoring +* Clément Prévost -- UrbiScript lexer +* Tanner Prynn -- cmdline -x option and loading lexers from files +* Oleh Prypin -- Crystal lexer (based on Ruby lexer) +* Xidorn Quan -- Web IDL lexer +* Elias Rabel -- Fortran fixed form lexer +* raichoo -- Idris lexer +* Kashif Rasul -- CUDA lexer +* Nathan Reed -- HLSL lexer +* Justin Reidy -- MXML lexer +* Norman Richards -- JSON lexer +* Corey Richardson -- Rust lexer updates +* Lubomir Rintel -- GoodData MAQL and CL lexers +* Andre Roberge -- Tango style +* Georg Rollinger -- HSAIL lexer +* Michiel Roos -- TypoScript lexer +* Konrad Rudolph -- LaTeX formatter enhancements +* Mario Ruggier -- Evoque lexers +* Miikka Salminen -- Lovelace style, Hexdump lexer, lexer enhancements +* Stou Sandalski -- NumPy, FORTRAN, tcsh and XSLT lexers +* Matteo Sasso -- Common Lisp lexer +* Joe Schafer -- Ada lexer +* Ken Schutte -- Matlab lexers +* René Schwaiger -- Rainbow Dash style +* Sebastian Schweizer -- Whiley lexer +* Tassilo Schweyer -- Io, MOOCode lexers +* Ted Shaw -- AutoIt lexer +* Joerg Sieker -- ABAP lexer +* Robert Simmons -- Standard ML lexer +* Kirill Simonov -- YAML lexer +* Corbin Simpson -- Monte lexer +* Alexander Smishlajev -- Visual FoxPro lexer +* Steve Spigarelli -- XQuery lexer +* Jerome St-Louis -- eC lexer +* Camil Staps -- Clean and NuSMV lexers; Solarized style +* James Strachan -- Kotlin lexer +* Tom Stuart -- Treetop lexer +* Colin Sullivan -- SuperCollider lexer +* Ben Swift -- Extempore lexer +* Edoardo Tenani -- Arduino lexer +* Tiberius Teng -- default style overhaul +* Jeremy Thurgood -- Erlang, Squid config lexers +* Brian Tiffin -- OpenCOBOL lexer +* Bob Tolbert -- Hy lexer +* Matthias Trute -- Forth lexer +* Erick Tryzelaar -- Felix lexer +* Alexander Udalov -- Kotlin lexer improvements +* Thomas Van Doren -- Chapel lexer +* Daniele Varrazzo -- PostgreSQL lexers +* Abe Voelker -- OpenEdge ABL lexer +* Pepijn de Vos -- HTML formatter CTags support +* Matthias Vallentin -- Bro lexer +* Benoît Vinot -- AMPL lexer +* Linh Vu Hong -- RSL lexer +* Nathan Weizenbaum -- Haml and Sass lexers +* Nathan Whetsell -- Csound lexers +* Dietmar Winkler -- Modelica lexer +* Nils Winter -- Smalltalk lexer +* Davy Wybiral -- Clojure lexer +* Whitney Young -- ObjectiveC lexer +* Diego Zamboni -- CFengine3 lexer +* Enrique Zamudio -- Ceylon lexer +* Alex Zimin -- Nemerle lexer +* Rob Zimmerman -- Kal lexer +* Vincent Zurczak -- Roboconf lexer + +Many thanks for all contributions! diff --git a/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/INSTALLER b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/LICENSE b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/LICENSE new file mode 100644 index 00000000..13d1c74b --- /dev/null +++ b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2006-2019 by the respective authors (see AUTHORS file). +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/METADATA b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/METADATA new file mode 100644 index 00000000..ff19f5d7 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/METADATA @@ -0,0 +1,48 @@ +Metadata-Version: 2.1 +Name: Pygments +Version: 2.6.1 +Summary: Pygments is a syntax highlighting package written in Python. +Home-page: https://pygments.org/ +Author: Georg Brandl +Author-email: georg@python.org +License: BSD License +Keywords: syntax highlighting +Platform: any +Classifier: License :: OSI Approved :: BSD License +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: End Users/Desktop +Classifier: Intended Audience :: System Administrators +Classifier: Development Status :: 6 - Mature +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Operating System :: OS Independent +Classifier: Topic :: Text Processing :: Filters +Classifier: Topic :: Utilities +Requires-Python: >=3.5 + + +Pygments +~~~~~~~~ + +Pygments is a syntax highlighting package written in Python. + +It is a generic syntax highlighter suitable for use in code hosting, forums, +wikis or other applications that need to prettify source code. Highlights +are: + +* a wide range of over 500 languages and other text formats is supported +* special attention is paid to details, increasing quality by a fair amount +* support for new languages and formats are added easily +* a number of output formats, presently HTML, LaTeX, RTF, SVG, all image formats that PIL supports and ANSI sequences +* it is usable as a command-line tool and as a library + +:copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS. +:license: BSD, see LICENSE for details. + + diff --git a/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/RECORD b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/RECORD new file mode 100644 index 00000000..f1318407 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/RECORD @@ -0,0 +1,463 @@ +../../../bin/pygmentize,sha256=WeS3f8iUVFEI7U4w7BX44JIurdztRox8Kbopp-I_yRc,262 +Pygments-2.6.1.dist-info/AUTHORS,sha256=PVpa2_Oku6BGuiUvutvuPnWGpzxqFy2I8-NIrqCvqUY,8449 +Pygments-2.6.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Pygments-2.6.1.dist-info/LICENSE,sha256=RbiNNEnDeAZZR1i_PEhNnZixKx7MFj9lQx_gf-pgJfA,1331 +Pygments-2.6.1.dist-info/METADATA,sha256=nrRK1IyAspMOw3-uN_SPDbJo1XIALm7ZbRV9aui-FtM,1833 +Pygments-2.6.1.dist-info/RECORD,, +Pygments-2.6.1.dist-info/WHEEL,sha256=p46_5Uhzqz6AzeSosiOnxK-zmFja1i22CrQCjmYe8ec,92 +Pygments-2.6.1.dist-info/entry_points.txt,sha256=NXt9BRDRv6tAfDwqKM0bDHrrxaIt2f1nxH9CwjyjSKc,54 +Pygments-2.6.1.dist-info/top_level.txt,sha256=RjKKqrVIStoebLHdbs0yZ2Lk4rS7cxGguXsLCYvZ2Ak,9 +pygments/__init__.py,sha256=Hmd0jgKuzYTHVCXGJrUr5E7R0tAINEf8WuhKUmC7ITY,3036 +pygments/__main__.py,sha256=JV6RSKzbYgMQHLf0nZGzfq1IXxns2iGunsfkY3jxFKo,372 +pygments/__pycache__/__init__.cpython-311.pyc,, +pygments/__pycache__/__main__.cpython-311.pyc,, +pygments/__pycache__/cmdline.cpython-311.pyc,, +pygments/__pycache__/console.cpython-311.pyc,, +pygments/__pycache__/filter.cpython-311.pyc,, +pygments/__pycache__/formatter.cpython-311.pyc,, +pygments/__pycache__/lexer.cpython-311.pyc,, +pygments/__pycache__/modeline.cpython-311.pyc,, +pygments/__pycache__/plugin.cpython-311.pyc,, +pygments/__pycache__/regexopt.cpython-311.pyc,, +pygments/__pycache__/scanner.cpython-311.pyc,, +pygments/__pycache__/sphinxext.cpython-311.pyc,, +pygments/__pycache__/style.cpython-311.pyc,, +pygments/__pycache__/token.cpython-311.pyc,, +pygments/__pycache__/unistring.cpython-311.pyc,, +pygments/__pycache__/util.cpython-311.pyc,, +pygments/cmdline.py,sha256=-mJqcK1Cic8Z-z-ITdj0yjN9exJdPew8m9BwHvtesJY,19479 +pygments/console.py,sha256=QF0bQHbGeFRSetc3g5JsmGziVHQqIZCprEwNlZFtiRg,1721 +pygments/filter.py,sha256=hu4Qo6zdyMcIprEL3xmZGb-inVe1_vUKvgY9vdAV5JU,2030 +pygments/filters/__init__.py,sha256=L_K0aapWqkqDPBkMVGoXvp17zsv7ddl0bNQdMsK43tg,11534 +pygments/filters/__pycache__/__init__.cpython-311.pyc,, +pygments/formatter.py,sha256=Zyz1t_dRczxwuuQkgkwOIOd2TRZpHMbjVHOL_ch37JQ,2917 +pygments/formatters/__init__.py,sha256=d8AnTX9J39ZKoh2YJIiFcOk79h28T0cJ7Yn6rs4G3UI,5107 +pygments/formatters/__pycache__/__init__.cpython-311.pyc,, +pygments/formatters/__pycache__/_mapping.cpython-311.pyc,, +pygments/formatters/__pycache__/bbcode.cpython-311.pyc,, +pygments/formatters/__pycache__/html.cpython-311.pyc,, +pygments/formatters/__pycache__/img.cpython-311.pyc,, +pygments/formatters/__pycache__/irc.cpython-311.pyc,, +pygments/formatters/__pycache__/latex.cpython-311.pyc,, +pygments/formatters/__pycache__/other.cpython-311.pyc,, +pygments/formatters/__pycache__/rtf.cpython-311.pyc,, +pygments/formatters/__pycache__/svg.cpython-311.pyc,, +pygments/formatters/__pycache__/terminal.cpython-311.pyc,, +pygments/formatters/__pycache__/terminal256.cpython-311.pyc,, +pygments/formatters/_mapping.py,sha256=QvLAVzGeldQ6iE8xeGOYtclUBMV0KclGiINcsCaK538,6175 +pygments/formatters/bbcode.py,sha256=_K7UzwyT70snOYAiT3UkItbXRwQYVuTHpr1AZtRHL6Y,3314 +pygments/formatters/html.py,sha256=Eoa4EJxTmE3g0kgMiNN7Ihh5A9lNWUnBlZ1eXFC-yl4,32625 +pygments/formatters/img.py,sha256=iajPfAvg5cB79wS0Mu3pv5Vy2TgghWQcl1OWIz1NcKg,20701 +pygments/formatters/irc.py,sha256=nU9jSjARuRaZpCuCey7bnRwGTGKeCTEhm_yDDYxzKQ8,5869 +pygments/formatters/latex.py,sha256=IOqv1C-LyWs6v2cgecfZ-CkfNNF6VQqcNkPUs3aHUjU,17711 +pygments/formatters/other.py,sha256=Qfc5OixOxM7YEy0d0NJBT750ukj-uPyhxKtHGTm0Vlc,5140 +pygments/formatters/rtf.py,sha256=z8LTTuEXwx3hpLaG0qeJumZCkUfseLIBsxhZE-0tEKg,5050 +pygments/formatters/svg.py,sha256=QUPMQIhXN4JA6auaUTj6z6JaeffyF7OpVoQ8IENptCo,7279 +pygments/formatters/terminal.py,sha256=q0QuanTWnUr4fuNuxnSnjLwjlyUJSMXqBK58MZCAk8Q,4662 +pygments/formatters/terminal256.py,sha256=x9n-YSOwDZhOaLGmYKLO259ZNBCqSydm_KxZJh2Q-Eg,11126 +pygments/lexer.py,sha256=FBy1KBXYiwf1TYtXN25OSrnLGbm9oAWXCsq6ReqtvNA,31559 +pygments/lexers/__init__.py,sha256=3dDjsioYkLz3fRbX3gV9xoi-SkpRRCtYurrWrylAZCo,11310 +pygments/lexers/__pycache__/__init__.cpython-311.pyc,, +pygments/lexers/__pycache__/_asy_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_cl_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_cocoa_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_csound_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_lasso_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_lua_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_mapping.cpython-311.pyc,, +pygments/lexers/__pycache__/_mql_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_openedge_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_php_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_postgres_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_scilab_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_sourcemod_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_stan_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_stata_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_tsql_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_usd_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_vbscript_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_vim_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/actionscript.cpython-311.pyc,, +pygments/lexers/__pycache__/agile.cpython-311.pyc,, +pygments/lexers/__pycache__/algebra.cpython-311.pyc,, +pygments/lexers/__pycache__/ambient.cpython-311.pyc,, +pygments/lexers/__pycache__/ampl.cpython-311.pyc,, +pygments/lexers/__pycache__/apl.cpython-311.pyc,, +pygments/lexers/__pycache__/archetype.cpython-311.pyc,, +pygments/lexers/__pycache__/asm.cpython-311.pyc,, +pygments/lexers/__pycache__/automation.cpython-311.pyc,, +pygments/lexers/__pycache__/basic.cpython-311.pyc,, +pygments/lexers/__pycache__/bibtex.cpython-311.pyc,, +pygments/lexers/__pycache__/boa.cpython-311.pyc,, +pygments/lexers/__pycache__/business.cpython-311.pyc,, +pygments/lexers/__pycache__/c_cpp.cpython-311.pyc,, +pygments/lexers/__pycache__/c_like.cpython-311.pyc,, +pygments/lexers/__pycache__/capnproto.cpython-311.pyc,, +pygments/lexers/__pycache__/chapel.cpython-311.pyc,, +pygments/lexers/__pycache__/clean.cpython-311.pyc,, +pygments/lexers/__pycache__/compiled.cpython-311.pyc,, +pygments/lexers/__pycache__/configs.cpython-311.pyc,, +pygments/lexers/__pycache__/console.cpython-311.pyc,, +pygments/lexers/__pycache__/crystal.cpython-311.pyc,, +pygments/lexers/__pycache__/csound.cpython-311.pyc,, +pygments/lexers/__pycache__/css.cpython-311.pyc,, +pygments/lexers/__pycache__/d.cpython-311.pyc,, +pygments/lexers/__pycache__/dalvik.cpython-311.pyc,, +pygments/lexers/__pycache__/data.cpython-311.pyc,, +pygments/lexers/__pycache__/diff.cpython-311.pyc,, +pygments/lexers/__pycache__/dotnet.cpython-311.pyc,, +pygments/lexers/__pycache__/dsls.cpython-311.pyc,, +pygments/lexers/__pycache__/dylan.cpython-311.pyc,, +pygments/lexers/__pycache__/ecl.cpython-311.pyc,, +pygments/lexers/__pycache__/eiffel.cpython-311.pyc,, +pygments/lexers/__pycache__/elm.cpython-311.pyc,, +pygments/lexers/__pycache__/email.cpython-311.pyc,, +pygments/lexers/__pycache__/erlang.cpython-311.pyc,, +pygments/lexers/__pycache__/esoteric.cpython-311.pyc,, +pygments/lexers/__pycache__/ezhil.cpython-311.pyc,, +pygments/lexers/__pycache__/factor.cpython-311.pyc,, +pygments/lexers/__pycache__/fantom.cpython-311.pyc,, +pygments/lexers/__pycache__/felix.cpython-311.pyc,, +pygments/lexers/__pycache__/floscript.cpython-311.pyc,, +pygments/lexers/__pycache__/forth.cpython-311.pyc,, +pygments/lexers/__pycache__/fortran.cpython-311.pyc,, +pygments/lexers/__pycache__/foxpro.cpython-311.pyc,, +pygments/lexers/__pycache__/freefem.cpython-311.pyc,, +pygments/lexers/__pycache__/functional.cpython-311.pyc,, +pygments/lexers/__pycache__/go.cpython-311.pyc,, +pygments/lexers/__pycache__/grammar_notation.cpython-311.pyc,, +pygments/lexers/__pycache__/graph.cpython-311.pyc,, +pygments/lexers/__pycache__/graphics.cpython-311.pyc,, +pygments/lexers/__pycache__/haskell.cpython-311.pyc,, +pygments/lexers/__pycache__/haxe.cpython-311.pyc,, +pygments/lexers/__pycache__/hdl.cpython-311.pyc,, +pygments/lexers/__pycache__/hexdump.cpython-311.pyc,, +pygments/lexers/__pycache__/html.cpython-311.pyc,, +pygments/lexers/__pycache__/idl.cpython-311.pyc,, +pygments/lexers/__pycache__/igor.cpython-311.pyc,, +pygments/lexers/__pycache__/inferno.cpython-311.pyc,, +pygments/lexers/__pycache__/installers.cpython-311.pyc,, +pygments/lexers/__pycache__/int_fiction.cpython-311.pyc,, +pygments/lexers/__pycache__/iolang.cpython-311.pyc,, +pygments/lexers/__pycache__/j.cpython-311.pyc,, +pygments/lexers/__pycache__/javascript.cpython-311.pyc,, +pygments/lexers/__pycache__/julia.cpython-311.pyc,, +pygments/lexers/__pycache__/jvm.cpython-311.pyc,, +pygments/lexers/__pycache__/lisp.cpython-311.pyc,, +pygments/lexers/__pycache__/make.cpython-311.pyc,, +pygments/lexers/__pycache__/markup.cpython-311.pyc,, +pygments/lexers/__pycache__/math.cpython-311.pyc,, +pygments/lexers/__pycache__/matlab.cpython-311.pyc,, +pygments/lexers/__pycache__/mime.cpython-311.pyc,, +pygments/lexers/__pycache__/ml.cpython-311.pyc,, +pygments/lexers/__pycache__/modeling.cpython-311.pyc,, +pygments/lexers/__pycache__/modula2.cpython-311.pyc,, +pygments/lexers/__pycache__/monte.cpython-311.pyc,, +pygments/lexers/__pycache__/mosel.cpython-311.pyc,, +pygments/lexers/__pycache__/ncl.cpython-311.pyc,, +pygments/lexers/__pycache__/nimrod.cpython-311.pyc,, +pygments/lexers/__pycache__/nit.cpython-311.pyc,, +pygments/lexers/__pycache__/nix.cpython-311.pyc,, +pygments/lexers/__pycache__/oberon.cpython-311.pyc,, +pygments/lexers/__pycache__/objective.cpython-311.pyc,, +pygments/lexers/__pycache__/ooc.cpython-311.pyc,, +pygments/lexers/__pycache__/other.cpython-311.pyc,, +pygments/lexers/__pycache__/parasail.cpython-311.pyc,, +pygments/lexers/__pycache__/parsers.cpython-311.pyc,, +pygments/lexers/__pycache__/pascal.cpython-311.pyc,, +pygments/lexers/__pycache__/pawn.cpython-311.pyc,, +pygments/lexers/__pycache__/perl.cpython-311.pyc,, +pygments/lexers/__pycache__/php.cpython-311.pyc,, +pygments/lexers/__pycache__/pony.cpython-311.pyc,, +pygments/lexers/__pycache__/praat.cpython-311.pyc,, +pygments/lexers/__pycache__/prolog.cpython-311.pyc,, +pygments/lexers/__pycache__/python.cpython-311.pyc,, +pygments/lexers/__pycache__/qvt.cpython-311.pyc,, +pygments/lexers/__pycache__/r.cpython-311.pyc,, +pygments/lexers/__pycache__/rdf.cpython-311.pyc,, +pygments/lexers/__pycache__/rebol.cpython-311.pyc,, +pygments/lexers/__pycache__/resource.cpython-311.pyc,, +pygments/lexers/__pycache__/ride.cpython-311.pyc,, +pygments/lexers/__pycache__/rnc.cpython-311.pyc,, +pygments/lexers/__pycache__/roboconf.cpython-311.pyc,, +pygments/lexers/__pycache__/robotframework.cpython-311.pyc,, +pygments/lexers/__pycache__/ruby.cpython-311.pyc,, +pygments/lexers/__pycache__/rust.cpython-311.pyc,, +pygments/lexers/__pycache__/sas.cpython-311.pyc,, +pygments/lexers/__pycache__/scdoc.cpython-311.pyc,, +pygments/lexers/__pycache__/scripting.cpython-311.pyc,, +pygments/lexers/__pycache__/sgf.cpython-311.pyc,, +pygments/lexers/__pycache__/shell.cpython-311.pyc,, +pygments/lexers/__pycache__/sieve.cpython-311.pyc,, +pygments/lexers/__pycache__/slash.cpython-311.pyc,, +pygments/lexers/__pycache__/smalltalk.cpython-311.pyc,, +pygments/lexers/__pycache__/smv.cpython-311.pyc,, +pygments/lexers/__pycache__/snobol.cpython-311.pyc,, +pygments/lexers/__pycache__/solidity.cpython-311.pyc,, +pygments/lexers/__pycache__/special.cpython-311.pyc,, +pygments/lexers/__pycache__/sql.cpython-311.pyc,, +pygments/lexers/__pycache__/stata.cpython-311.pyc,, +pygments/lexers/__pycache__/supercollider.cpython-311.pyc,, +pygments/lexers/__pycache__/tcl.cpython-311.pyc,, +pygments/lexers/__pycache__/templates.cpython-311.pyc,, +pygments/lexers/__pycache__/teraterm.cpython-311.pyc,, +pygments/lexers/__pycache__/testing.cpython-311.pyc,, +pygments/lexers/__pycache__/text.cpython-311.pyc,, +pygments/lexers/__pycache__/textedit.cpython-311.pyc,, +pygments/lexers/__pycache__/textfmts.cpython-311.pyc,, +pygments/lexers/__pycache__/theorem.cpython-311.pyc,, +pygments/lexers/__pycache__/trafficscript.cpython-311.pyc,, +pygments/lexers/__pycache__/typoscript.cpython-311.pyc,, +pygments/lexers/__pycache__/unicon.cpython-311.pyc,, +pygments/lexers/__pycache__/urbi.cpython-311.pyc,, +pygments/lexers/__pycache__/usd.cpython-311.pyc,, +pygments/lexers/__pycache__/varnish.cpython-311.pyc,, +pygments/lexers/__pycache__/verification.cpython-311.pyc,, +pygments/lexers/__pycache__/web.cpython-311.pyc,, +pygments/lexers/__pycache__/webidl.cpython-311.pyc,, +pygments/lexers/__pycache__/webmisc.cpython-311.pyc,, +pygments/lexers/__pycache__/whiley.cpython-311.pyc,, +pygments/lexers/__pycache__/x10.cpython-311.pyc,, +pygments/lexers/__pycache__/xorg.cpython-311.pyc,, +pygments/lexers/__pycache__/zig.cpython-311.pyc,, +pygments/lexers/_asy_builtins.py,sha256=zO_y8v-bp6kjlIwvbmse79qY8P7qAUhoVObaX9Qy3S8,27311 +pygments/lexers/_cl_builtins.py,sha256=x-mRhM6ukZv0pxYtqCq7SlsezhL8L9fpcCQ-gou0Z9w,14018 +pygments/lexers/_cocoa_builtins.py,sha256=h8CT9tpHtyg7PF1tQsLj0NCoN6o9TzzIOBA5UTadEQs,39962 +pygments/lexers/_csound_builtins.py,sha256=6OzL4rwy_qgAenQ8uN0n2u39NlfoVMSZEOy4tdLNHOE,17619 +pygments/lexers/_lasso_builtins.py,sha256=1jR-3eDhf1CUcPSSEXgbJMymAkQaJqpWIPjYM4rL6Sk,134534 +pygments/lexers/_lua_builtins.py,sha256=VkZNUZW9_lTBBcTFOl8NDepWTBfwha6tkV8BXR1EzaM,8297 +pygments/lexers/_mapping.py,sha256=KQJCLXu0jKEF8g9N8Ysg3giPGOFByjdgiOIEMA1yoqw,58920 +pygments/lexers/_mql_builtins.py,sha256=MS7566jpdiud7gEa_y4iJpHLkqjpo-7Y8WwB9MyMUhY,24737 +pygments/lexers/_openedge_builtins.py,sha256=hCqbIZd_qcBTlLyQGME8mqijUDCIm5P9HtIsv8JCEG8,48362 +pygments/lexers/_php_builtins.py,sha256=6-lirHeDcuo-5U5Yh9D4Fgx0sGmN2gq_dmCzjY3wEQg,154390 +pygments/lexers/_postgres_builtins.py,sha256=OI0j7i72gKoNGJomATjK_P00D7cVT6bpPqeeSB4k0aM,11210 +pygments/lexers/_scilab_builtins.py,sha256=ffgN-8Jj5KLRIwdD9j51zvScUbim2DaYv9WmF8_JCNA,52401 +pygments/lexers/_sourcemod_builtins.py,sha256=BSnleYNoGBZ0IrTJz17r-Z9yz7OHwgJVy1oRvvqXg38,27074 +pygments/lexers/_stan_builtins.py,sha256=BfSr_PiG5QE0-7hUfX4g_jdwugKf1zWtGE2w33FotvA,10481 +pygments/lexers/_stata_builtins.py,sha256=rZ8lopR_vKuDCBeCF9oPf71sHkD6n-tN6T5QpyOVEg4,25228 +pygments/lexers/_tsql_builtins.py,sha256=5qrkZJHk_m1SgTnhCrKp5jXJxexjCaf4GtRkY5_PTEA,15484 +pygments/lexers/_usd_builtins.py,sha256=eiB5M8wMXpllmheEzX3BuId6zXGQWaMcJs92E114P7U,1514 +pygments/lexers/_vbscript_builtins.py,sha256=chotaveFeFC-A6qcRAghQC7fAwrDmV-BKE_TW-hrZwk,4249 +pygments/lexers/_vim_builtins.py,sha256=Il_pjrP0PWUjMLCRPOZPoLgd_3jauvv9SGtBOkzmU2A,57090 +pygments/lexers/actionscript.py,sha256=jQTpfKe0OwRQTknMs132_WhqEDIW7lQbLW0HU5D0cOs,11181 +pygments/lexers/agile.py,sha256=0yI_Bq_-ekqFCiMzkcnJfNQ12iyA4QmPk70RCfl1Xa0,900 +pygments/lexers/algebra.py,sha256=vMjSoC9CgSWUMoaNu7gysQDdAc46t_Y6U4dX2mEzNCc,7201 +pygments/lexers/ambient.py,sha256=1_B2JkmFVgGq-JuEhmrXIu-q5WP2e7Ir5DSpO7qXN9E,2557 +pygments/lexers/ampl.py,sha256=HWeNZxYsNhPuGmW1lgNUxMe5zMtbMQ-xNFoj9oVOvq8,4123 +pygments/lexers/apl.py,sha256=gzIuS7p2Qz-pN5M0i45uvDow_gsNNus5k6zrwe19M9c,3174 +pygments/lexers/archetype.py,sha256=luJBCChBsH6fdJOboz5pVTSNCHh7miLd1xtLnI7TH88,11136 +pygments/lexers/asm.py,sha256=0gX4w_cbEbWWUp0kBla41r0ZMAY8NUxaaeW-faRRiqE,39356 +pygments/lexers/automation.py,sha256=9oR495kiyEbl-ev7PWF4Mw-jvtuSbOkmKJRmOvUzQb8,19640 +pygments/lexers/basic.py,sha256=siXk3fQfTEfJNeSW2sI-rfssoUpyj7drMdMrs5csYrs,27576 +pygments/lexers/bibtex.py,sha256=fxbIyhfV1yrFfd7oyAp-fyss27T0Bfv8VqRdVnLg63Y,4725 +pygments/lexers/boa.py,sha256=OB_W242mpr2vwbhg0MO4BpZcdhjaXuM6ffQ54zn3-ZI,3942 +pygments/lexers/business.py,sha256=onAZDADHM7atSEFKsmODBvPc8GlOTDwPgfsh_hVGXNI,27666 +pygments/lexers/c_cpp.py,sha256=dfPzKNoZeqoprqM4a7pTqWin7scz6VYg5_q9MzbnjH0,10638 +pygments/lexers/c_like.py,sha256=UusXq2S5d0v0CpGsxkmVludmu58WsLZQHWnsb0YwhK4,25080 +pygments/lexers/capnproto.py,sha256=pC3zXFSfYFHEIBq3OqLPGKl71K5HtdWnAEqMz6n8KFY,2194 +pygments/lexers/chapel.py,sha256=qzglX8OKh7aaUgXqAVXFkerNjTMIJKdMbihP_o2VFWk,3908 +pygments/lexers/clean.py,sha256=XG0_2KVyxbRFp-_U5HgT1wN9srL522kOe_9T51HeQmA,6362 +pygments/lexers/compiled.py,sha256=iGwVkCJ-SXoovHegOBSnOG518hHkDudegb9_qS-8vW0,1385 +pygments/lexers/configs.py,sha256=YO1NWPpNENDBN4fr-yBYdBWwIqotSquTeaKfY24a7mk,32127 +pygments/lexers/console.py,sha256=tj_ARAplXlqt2sGb2ycxsOy8xIL4NCAMOd3bZ0Zjojg,4120 +pygments/lexers/crystal.py,sha256=hTz20yWrjuam9JVG9Xxr6I7x50M_sIlfdBs0_gg5hKQ,16845 +pygments/lexers/csound.py,sha256=_OqMoEegHcp0NV7zuiLt6h_aY17adQRlJe1DG-pQP4M,16739 +pygments/lexers/css.py,sha256=EvJYfYeaT-TkDHpqXc4ssz0BoXSnBfWT3Vb9MKCeNQg,31467 +pygments/lexers/d.py,sha256=ZWc365C_vRjee-ATfen2OmHCFW3QMoMNE9JEeTplk94,9686 +pygments/lexers/dalvik.py,sha256=tAoPPa_iRXhWG_MzslSvBE99NlGnkx0WKnwdDQ3XU9o,4420 +pygments/lexers/data.py,sha256=_-dqdUSm7Edmh-zaljABFodHjwMCECW1Y-IlyjnWibM,19072 +pygments/lexers/diff.py,sha256=8jKEVtSA2YKprutpONqFvMKBhK1U_IFdxaScTuRNeU4,4873 +pygments/lexers/dotnet.py,sha256=oGL8kWok74zOLn92iwjOYX6Do9tnk96-YTFlJSdkBaQ,27582 +pygments/lexers/dsls.py,sha256=qv0kHmfQOHkHMnXyFbgYrDjUpsCkuXPLig1hma2zcJ0,35837 +pygments/lexers/dylan.py,sha256=LkWTiLsU561_VQL-PUirryEt7ewbseLRJfN-H1twmiA,10402 +pygments/lexers/ecl.py,sha256=5ivxyk5lzMottCuIxyE7DBvWYJV5KTuaHNRkvOtgM7c,5875 +pygments/lexers/eiffel.py,sha256=He2DwoUqWqMt8_PDzoP3NuBl9AZ9K3_SmpGkIgSzWuI,2482 +pygments/lexers/elm.py,sha256=91CM_h3PPoBLLm2stJqNZi3lgjhZH7NvzNKWdXAe8CA,2997 +pygments/lexers/email.py,sha256=ap9imSi6jbbP7gPBAyc3rcNurVDSNmRKIWv0ByR6VOQ,5207 +pygments/lexers/erlang.py,sha256=bZBqAKFa-EsRpvai0EpwZkKMPCd2q6pDfZapq9gh9Qg,18985 +pygments/lexers/esoteric.py,sha256=I7YEPnQdftxEOasCec8_dxVr7zgypMtoYtds0v2srNQ,9489 +pygments/lexers/ezhil.py,sha256=R26b4iXSpdMkgXewJN2INhJXL0ICXhW2o9fu3bn078U,3020 +pygments/lexers/factor.py,sha256=nBYhJoNLkSxtshGrF08tSQKUq_TtgVp1ukKX4Zromm8,17864 +pygments/lexers/fantom.py,sha256=3OTJDka8qeNRykM1Ki1Lyek6gd-jqOa-l5IgRbX8kSg,9982 +pygments/lexers/felix.py,sha256=DoSGdEntZgG3JUbeBA9fqUtg3lODbqwY3_XS6EIfXt4,9408 +pygments/lexers/floscript.py,sha256=eza4Rw3RI3mFjIIAA2czmi2SlgbcSI1T8pNr7vUd0eY,2667 +pygments/lexers/forth.py,sha256=Yqm9z-PjymjQjaleCW-SNJdCCc_NWeFXMz_XvjtAStI,7179 +pygments/lexers/fortran.py,sha256=XqwbZg25atjNDN8yUnqkxm1nfqbzSgZDqmKUIFNQSHk,9841 +pygments/lexers/foxpro.py,sha256=i1B6wX4U5oY8FJO5BGtTR0RaVWbO6P45PXxndi5HcpE,26236 +pygments/lexers/freefem.py,sha256=bYEPIZ1mysE2Ub9WO8NPHefz-CaGqPiE0WbHZeMHPsQ,27086 +pygments/lexers/functional.py,sha256=gJqzgp1ujTa8Zk5hjzXdutz8vvSJpRxhqTVCkK03Ij0,698 +pygments/lexers/go.py,sha256=aRdc0lsKbF7xxTcUnu35m-_e3SD7s2eBAllq1y7_qY8,3701 +pygments/lexers/grammar_notation.py,sha256=0INMOPjLnMohU0QCUIvBaJdty7A7i1hS4ZItB4ehPnA,7941 +pygments/lexers/graph.py,sha256=v013Gzn_RIuLrEz_DJuUah_vCpv6aVSMZpHGov19BMY,2756 +pygments/lexers/graphics.py,sha256=xfr7jZ_JF81kh-RFxIFSKOa06W4z0YxWzOxXAmrLwMA,38259 +pygments/lexers/haskell.py,sha256=VXzPclm0SiawKT2E4L4ZO8uPKcNYMVDWKY4NPcLKFsg,32245 +pygments/lexers/haxe.py,sha256=uWeORmR1BBCtA_HKRJIhzl26GfkzxzVd7c8or-REw7s,30959 +pygments/lexers/hdl.py,sha256=Xpf_1SJ-Uwf94J6MK_C5wR7JyXQkDKtlNdJ7MLL6uzs,18179 +pygments/lexers/hexdump.py,sha256=7y6XhpOGaVfbtWPSzFxgen8u4sr9sWCbnRUTmvnW1KI,3507 +pygments/lexers/html.py,sha256=B-XSH62dR1GZSJ6E3rDOoF6WO-FcKAnrCqTYvvm8jow,19280 +pygments/lexers/idl.py,sha256=hg7CnizaVt7br6ydWkt4VU9UMNax7gg4ToA3_rnqM1M,14986 +pygments/lexers/igor.py,sha256=FP_3Uz06p1emRB1BqpJ_11KY5k38D5nBLP9nFLnXsHA,30917 +pygments/lexers/inferno.py,sha256=iB07whrTd_qnsABOUalv999QhFYB2nhIHfTp_ECsTxM,3117 +pygments/lexers/installers.py,sha256=QVPOqFLmDydPhBJYmQcyjq6XQvcPb1Hxhpbv5JvgL-M,12866 +pygments/lexers/int_fiction.py,sha256=-jBktm0onIUz_hzsP0lUd3g9aLXJ4KLls0gjIwSB46o,55779 +pygments/lexers/iolang.py,sha256=Sv9qzhNgvVz1xmStZOLm3KTvlcI2A1zywAWQTo6ahs0,1905 +pygments/lexers/j.py,sha256=2wqBgvkxF99yBTdyslEsaeweZuqNO_yNZPjTKRwNTdo,4527 +pygments/lexers/javascript.py,sha256=cOcijZB6rFr1aclYt94LHInEKs1KgZZ4Xg4i2zDvW28,60194 +pygments/lexers/julia.py,sha256=ObRU-RjNe_N6zcQZgq5nws526X_j_4c4KPUFwwROFns,14179 +pygments/lexers/jvm.py,sha256=Qsg2PugXHCD55g_w4GVI4FDFCfOBICYW70xKhWMfNiQ,70347 +pygments/lexers/lisp.py,sha256=oUWEXl8czd_ovmKgkROzATeDjy01jPXAne18zXtEYRY,143609 +pygments/lexers/make.py,sha256=dbnhkZWxESvkvV69TrQEZYdo4yiUGoBBIE-VpXX1uBM,7326 +pygments/lexers/markup.py,sha256=6ACdRUnjI6CGRwes8szHfUjZU-nR7C42y2dbP5EdJeI,20704 +pygments/lexers/math.py,sha256=74YS-Z0zpBP6JYk1fsauYbW7XeZ-XPDTqKakbkX0v1Y,700 +pygments/lexers/matlab.py,sha256=23FUS7UgeE9c0gPr9xnyIBz_0Qr7f8ks8DCumF0fGdU,30403 +pygments/lexers/mime.py,sha256=hf-dShZ8AUSIzTELUEnlel7gnZLZpiOd-OFehEDSba0,7975 +pygments/lexers/ml.py,sha256=SV44RnHSqsCQX7wZHZe_bJtzl0hTFrlY5UF8nhO9ozU,31376 +pygments/lexers/modeling.py,sha256=n4gorBPf3gttlsITHGYeOnrUjUWz3nCh5oLYkDMOnrM,13409 +pygments/lexers/modula2.py,sha256=zenAwJk17hVa1FnOTZHJAwLrDrmcurxu4yw7pUoa_Qk,52561 +pygments/lexers/monte.py,sha256=tIn0lsLdG0iHRX_01KI9OkR4iazyiV5F8H3OlkKdFZQ,6307 +pygments/lexers/mosel.py,sha256=N8J6mCnzTUd4KADnhMAAQ2X5OZGxXI-i2Xvq8HfzjNA,9211 +pygments/lexers/ncl.py,sha256=0U8xDdO0guIlnQKCHKmKQPXv91Jqy1YvrkNoMonaYp4,63986 +pygments/lexers/nimrod.py,sha256=ERUF4NVMUlbirF_FvN8EIXXFRv6RJqchq4rr9vugHPI,5174 +pygments/lexers/nit.py,sha256=FSQCdLNjKUrw_pisiCH-m15EQcz30lv6wvvbTgkrB-Y,2743 +pygments/lexers/nix.py,sha256=RTgXFxL2niA9iG1zLHRWdNZy70he_vE1D0-FcoU1cfw,4031 +pygments/lexers/oberon.py,sha256=HMOnehgSbLaTV6l1e5b44aZttyE2YIfA2hzyj6MW5xU,3733 +pygments/lexers/objective.py,sha256=FA7gniip1eEDC9x1UIvdI8flRtFxehTHId0MlqB0llo,22789 +pygments/lexers/ooc.py,sha256=lP6KSoWFrq9Q7w5F_aRSaLYUryh4nuBcPfnUkwyBQsU,2999 +pygments/lexers/other.py,sha256=0xuOYQ0uI9eLONFTNBv2e-hltZhQcN531NVi7e2AcQQ,1768 +pygments/lexers/parasail.py,sha256=YEgpP3B62qHYOBFcoChOfgzATczrSPj1WyovIgqW3gg,2737 +pygments/lexers/parsers.py,sha256=fhTyqwzifEpFFfW8emQ9WYYBwlUs48Sv_qykCUQoWHE,27590 +pygments/lexers/pascal.py,sha256=YpIQHj54lSJrBFdWSo_nkV8M_dYHfJyJMjLk6W6UNZY,32624 +pygments/lexers/pawn.py,sha256=LN0m73AC00wHyvBlbTPU1k2ihBdmDkfIFq24uAWvsF0,8021 +pygments/lexers/perl.py,sha256=Plh4ovtDulyq5oxJTIijQlJ8Td5ga7s3uQ0sbV7uES8,39155 +pygments/lexers/php.py,sha256=yU7DdvXBQlnEvX6WBb7c9kgSw9COwYp6psvzGmCebs4,10834 +pygments/lexers/pony.py,sha256=h6S-MGKN7q7sk869oWjC1OcgV7zwXloYnGFshhTFxHk,3269 +pygments/lexers/praat.py,sha256=aFOD7K8wEVjcr4Jb3DAGn5AmjhMDSHY8pVC4WQfjGlc,12292 +pygments/lexers/prolog.py,sha256=TNj3F1ossufZ_XKVVrWJlRtPDRU1ExGO6NS0-TBq7gw,12405 +pygments/lexers/python.py,sha256=7VkiN5v5IAIL9bDQGdwtmt2__plhedbEi3rzh397Nec,51187 +pygments/lexers/qvt.py,sha256=_lXPT5SdDEqhCmuq4TcO9JRrP703kIT3a1Y_ZW9NTCY,6097 +pygments/lexers/r.py,sha256=VGb5x89r844B-a_V49wAwu8i0245tbdyLKZWq_wRG74,6276 +pygments/lexers/rdf.py,sha256=RAerwJHNjrtXXtua4UXRfUQkMQ36uqfQZlSj63yoQA8,14608 +pygments/lexers/rebol.py,sha256=3bhOFMMneP38O9aJFjPZlNTS6cwbcnDlJaDbfvF4x1g,18624 +pygments/lexers/resource.py,sha256=xbAErtO3-d4LQJJPnLfhD7Kxz_NVQp4WiYrFu52UX-o,2926 +pygments/lexers/ride.py,sha256=lMlEAtdFILb1vd2WC17UaNwFJqOKb1du7EPG5jwX3Xk,5074 +pygments/lexers/rnc.py,sha256=OxpGllFDAM6Vn_alGiaEKMzQDoqRCrl82ocOO4s6L_k,1990 +pygments/lexers/roboconf.py,sha256=9eZkX5xkajimTV1F5wr0Y8QHPfuEB659Lde8H5AzFfM,2070 +pygments/lexers/robotframework.py,sha256=R0x05_jTPu9bErGS4v_mh-9kyCOG4g4GC-KUvxYkSKo,18646 +pygments/lexers/ruby.py,sha256=rqBelW7OJZIP-J3MVPgQzhXTh3Ey41MjMmpbGQDv390,22168 +pygments/lexers/rust.py,sha256=auhHzIX7VaYzgkj26USy9ZH5DZbPQ1LJYW7YDQB8Wgs,7844 +pygments/lexers/sas.py,sha256=guELd_4GLI1fhZr3Sxtn80Gt6s6ViYFf4jWnK23zzDc,9449 +pygments/lexers/scdoc.py,sha256=raoQeCR0E6sjvT56Lar0Wxc_1u6fB-gFjptjT0jE56g,1983 +pygments/lexers/scripting.py,sha256=kH-Kezddx8HzQMgA2z1ZRB-lcvc9qVyEvZnVjJ_YUBU,69759 +pygments/lexers/sgf.py,sha256=R5Zqd5oVOyUd-NewEXMmACaEO5RX_F7eYUZaJXGTY4g,2024 +pygments/lexers/shell.py,sha256=00dGjndFJ6ZWZzsfKW3nKjIKG-CBwTHH-VYQQs57700,33870 +pygments/lexers/sieve.py,sha256=79MOyJl8iAuvzhphpK-Qu_oybyFTdgzrP6d1Hj9-Lqc,2313 +pygments/lexers/slash.py,sha256=WN2f0VirklPe6djATJtbNMkFGRiuIykKZjqG19Rlgk8,8522 +pygments/lexers/smalltalk.py,sha256=xwRETRB2O_cKHZU9w18QXZpiz87WOw0lULDhMxc9xnA,7215 +pygments/lexers/smv.py,sha256=rbXxBG2rGtm7oPP_Il6v3BbUH3i5q4RtiDaweeN7fLA,2793 +pygments/lexers/snobol.py,sha256=YFOOuPk4yBxg6stlIm6R3UiUgzkMjz06ac7dW3LRxNk,2756 +pygments/lexers/solidity.py,sha256=fW_aQc_HyRawyStUxllYhUn-NYJPCqzDH-ABWTeKcOI,3255 +pygments/lexers/special.py,sha256=N0msqSMphQf0_7Vx9T7kABoHx_KkYLHUxP2FcyYmshg,3149 +pygments/lexers/sql.py,sha256=7evoMDWdBz0kXPIt1jy0YXrQ9KJFYnjN2cslkDrfB88,31823 +pygments/lexers/stata.py,sha256=E46GbEy8ET3yBw1l3KQLSknKW3_qS6Sq3V_hkpVotn0,6459 +pygments/lexers/supercollider.py,sha256=llVW-HUi7m4MNGy4wEp8bF2BJGTXdwF0oNfJfJ_sI8M,3516 +pygments/lexers/tcl.py,sha256=ORf0CBXHwC2MFBpZpcK2sPBCCTyJ3rcwcYOIhN9s0AI,5398 +pygments/lexers/templates.py,sha256=AE6yF5ohbqy52-rn8xUJ5A6OZCkoIs72j7wUnwp25vE,73612 +pygments/lexers/teraterm.py,sha256=2DdFVGyKIF85efcB5QdqqQQNGjqRHoWzVc5psdhSD7c,6310 +pygments/lexers/testing.py,sha256=JfFVWAh_8zaqaiPrewb3CGTmGNuHu6hFR4dtvcFCYRE,10753 +pygments/lexers/text.py,sha256=7cwhjV2GwLRH0CPjlOb7PLVa6XEiRQhDNFU1VO3KNjE,1030 +pygments/lexers/textedit.py,sha256=7F9f0-pAsorZpaFalHOZz5124fsdHCLTAWX_YuwA9XE,6092 +pygments/lexers/textfmts.py,sha256=Ctq-u_o2HVb-xvvsKfpuwkgNzVCNxXJHkirqhpsC8lE,15184 +pygments/lexers/theorem.py,sha256=c51ir2FdsyczFRu059z9wKFZplBETdhwWuWX0Y9wMtM,18908 +pygments/lexers/trafficscript.py,sha256=BYTyTAlD4oDVZ9D1aRrmy4zIC4VJ_n2Lgkgq92DxeJM,1546 +pygments/lexers/typoscript.py,sha256=Leb81-51KKuK9FHoo1xKWJGPqTIsyVoeZkGcsK5tQzU,8224 +pygments/lexers/unicon.py,sha256=xo0E3hnBW0gbdszL6n96Cdzume3l1DI7scgkIQ8koaw,18001 +pygments/lexers/urbi.py,sha256=Zq3PCTC-KI7QYuLZ7NSdikm9-MrAhrYH9DGXVSTT89I,5750 +pygments/lexers/usd.py,sha256=uEPjTqbXu0Ag_qJOB9IEwAGj4-R8_5yBbNRlUPtSlbY,3487 +pygments/lexers/varnish.py,sha256=Y2t_JY7uVz6pH3UvlpIvuaxurH4gRiQrP4Esqw5jPnk,7265 +pygments/lexers/verification.py,sha256=rN6OD2ary21XXvnzUTjknibiM9oF9YjxmLyC7iG6kuo,3932 +pygments/lexers/web.py,sha256=4thoq-m_kGixnDR2baWwN5eEqpFAeH3aRaOMK4J_GOE,918 +pygments/lexers/webidl.py,sha256=TTHSlvRlmdpMPNCMvrrUULY6Y6Q7l53HMR9CGyisq9I,10473 +pygments/lexers/webmisc.py,sha256=pJUyS7bcr77iHQshVzllZmIaZQoVkdGZi1D3FqlJEg0,40054 +pygments/lexers/whiley.py,sha256=J9ZuO8Yv9DYl9Mb6IHyZz2zguGxZXBKxTSwDcxaii8o,4012 +pygments/lexers/x10.py,sha256=Lu35QT0l-objbi6mCm-rxZU_7gO1rZQhjA6JnZ-EBRI,1965 +pygments/lexers/xorg.py,sha256=FDN0czbxMD6YDOqwL6ltspElwMoxxNVKW11OL--keQY,887 +pygments/lexers/zig.py,sha256=C3kbdZ_rJUb0hMK61UiFsjzJVvC_QIPJZ6glZDNPi78,4147 +pygments/modeline.py,sha256=ctgJHLjLF23gklYyo7Nz6P3I3Z8ArewlT5R2n2KNatQ,1010 +pygments/plugin.py,sha256=QFSBZcOqSJqAVQnydwDg8_LG7GzkxUgWjb0FzqoQHEM,1734 +pygments/regexopt.py,sha256=yMZBB3DRudP4AjPGAUpIF__o_NWOK4HrNfFV6h04V1w,3094 +pygments/scanner.py,sha256=tATA_g4QYMfFS2Tb-WIJtr_abdUetPb4tC1k7b0e97w,3115 +pygments/sphinxext.py,sha256=OVWeIgj0mvRslxw5boeo0tBykJHuSi5jSjIWXgAmqgk,4618 +pygments/style.py,sha256=DhwzS-OOt088Zkk-SWY6lBVy-Eh_2AcP2R_FdTYO9oI,5705 +pygments/styles/__init__.py,sha256=TBRYkROPEACN-kE1nQ1ygrhU4efWVShENqI6aqjk5cE,2894 +pygments/styles/__pycache__/__init__.cpython-311.pyc,, +pygments/styles/__pycache__/abap.cpython-311.pyc,, +pygments/styles/__pycache__/algol.cpython-311.pyc,, +pygments/styles/__pycache__/algol_nu.cpython-311.pyc,, +pygments/styles/__pycache__/arduino.cpython-311.pyc,, +pygments/styles/__pycache__/autumn.cpython-311.pyc,, +pygments/styles/__pycache__/borland.cpython-311.pyc,, +pygments/styles/__pycache__/bw.cpython-311.pyc,, +pygments/styles/__pycache__/colorful.cpython-311.pyc,, +pygments/styles/__pycache__/default.cpython-311.pyc,, +pygments/styles/__pycache__/emacs.cpython-311.pyc,, +pygments/styles/__pycache__/friendly.cpython-311.pyc,, +pygments/styles/__pycache__/fruity.cpython-311.pyc,, +pygments/styles/__pycache__/igor.cpython-311.pyc,, +pygments/styles/__pycache__/inkpot.cpython-311.pyc,, +pygments/styles/__pycache__/lovelace.cpython-311.pyc,, +pygments/styles/__pycache__/manni.cpython-311.pyc,, +pygments/styles/__pycache__/monokai.cpython-311.pyc,, +pygments/styles/__pycache__/murphy.cpython-311.pyc,, +pygments/styles/__pycache__/native.cpython-311.pyc,, +pygments/styles/__pycache__/paraiso_dark.cpython-311.pyc,, +pygments/styles/__pycache__/paraiso_light.cpython-311.pyc,, +pygments/styles/__pycache__/pastie.cpython-311.pyc,, +pygments/styles/__pycache__/perldoc.cpython-311.pyc,, +pygments/styles/__pycache__/rainbow_dash.cpython-311.pyc,, +pygments/styles/__pycache__/rrt.cpython-311.pyc,, +pygments/styles/__pycache__/sas.cpython-311.pyc,, +pygments/styles/__pycache__/solarized.cpython-311.pyc,, +pygments/styles/__pycache__/stata_dark.cpython-311.pyc,, +pygments/styles/__pycache__/stata_light.cpython-311.pyc,, +pygments/styles/__pycache__/tango.cpython-311.pyc,, +pygments/styles/__pycache__/trac.cpython-311.pyc,, +pygments/styles/__pycache__/vim.cpython-311.pyc,, +pygments/styles/__pycache__/vs.cpython-311.pyc,, +pygments/styles/__pycache__/xcode.cpython-311.pyc,, +pygments/styles/abap.py,sha256=weNa2ATjBDbWN-EJp36KuapOv_161OYudM6ilzp_5tU,751 +pygments/styles/algol.py,sha256=aVMDywxJ1VRTQ-eYd7CZVQ1BFIWehw2G9OcGg5KmfFI,2263 +pygments/styles/algol_nu.py,sha256=xgZhMlsdR8RppCyaGliUKBWVvianjxt5KrIcWCJDVMM,2278 +pygments/styles/arduino.py,sha256=MtP75GT5SqaAX2PfaC116iPETAPOaD6re6cZ1d9xehQ,4492 +pygments/styles/autumn.py,sha256=setTunOOFJAmdVHab3wmv5OkZmjP6-NVoZjMAyQ2rYY,2144 +pygments/styles/borland.py,sha256=UOFktPmmU_TK6prVMETvVm6FhT01oqsd9_HcG1NZq_Y,1562 +pygments/styles/bw.py,sha256=t0kQytwvh_0SMBcOcmM5foPcc3JWiSd8VWBIXkoP17s,1355 +pygments/styles/colorful.py,sha256=NV-MuEX61J0HH1M0dmurc0RNinp5eA9qIHTjhZ3M6ek,2778 +pygments/styles/default.py,sha256=j124bQ-0TFJaQ2U3ZICWq8_KUOQdjUSxFVknFcpSF40,2532 +pygments/styles/emacs.py,sha256=zNGOC_fHnCZxVphHkieHr7f-zxKkSg_PrFEwWGfQw2U,2486 +pygments/styles/friendly.py,sha256=55qszHEliWiT8h1dW5GjnEA47CpXpJ0BX0C-x6EmZsQ,2515 +pygments/styles/fruity.py,sha256=zkSwyKzmWDs9Jtzgq3rG4DathCH6Pq2JVLuUW8auKXI,1298 +pygments/styles/igor.py,sha256=6GFYt43btx70XZoVDSAqljc1G7UJb6_r9euz0b5nWpY,739 +pygments/styles/inkpot.py,sha256=ecGBxZQw0UhueDHZA06wvgWizu2JzXg9YkYCoLYJuh4,2347 +pygments/styles/lovelace.py,sha256=PBObIz9_gAjMJ8YgNrm-_z2P_wG7moQ1BosKLThJl20,3173 +pygments/styles/manni.py,sha256=EmN6YSp-U-ccxqLqjfnIPg-qkIhUAlSb78tIBvwFCsA,2374 +pygments/styles/monokai.py,sha256=hT5jhhqRQoOmjdK1lZ56hspKke4UDCCiUc3B8m5osLY,5086 +pygments/styles/murphy.py,sha256=ppT--IJLWtcbxKCNRBuusP4zdSmbR8YShosCdd3hpXs,2751 +pygments/styles/native.py,sha256=xkphXXv8PvfbgawNSTR28LcEe1TQxFtdrk_sQcGeo2E,1938 +pygments/styles/paraiso_dark.py,sha256=3a4BSgZQMfB8E2bUMi1WAWkDr98oFUfaPygcsl9B9ZM,5641 +pygments/styles/paraiso_light.py,sha256=QsZyh5oPQb6wYgnoQAkH2MRBkJjRPqAu5De77diOeN8,5645 +pygments/styles/pastie.py,sha256=duELGPs_LEzLbesA39vu0MzxtwkPJ2wnV2rS_clTu2E,2473 +pygments/styles/perldoc.py,sha256=Wf54Io76npBZEsVt8HuM-x7mpzJ7iwPgj5PP_hOf91w,2175 +pygments/styles/rainbow_dash.py,sha256=IlLrIcl76wy4aIiZIRWxMzUILOI9ms7YEX0o6UL9ROc,2480 +pygments/styles/rrt.py,sha256=xQp_B5sDo4BJ4Mzx4PWVK6AW_pZs_XmIoM8zLwpfVTs,852 +pygments/styles/sas.py,sha256=jC6iVFl7-xp0MKwFkPM9QbEInzxVlnhsluPR69iqMZE,1441 +pygments/styles/solarized.py,sha256=f_E9bd-THUcJUJR36hQgbu9BVIjLi6yiI_n07oRu2u4,3747 +pygments/styles/stata_dark.py,sha256=K1AKYh93Jd9E_eWXhDw7-tM6fJbIuFeJcAR5jVE1Nkc,1245 +pygments/styles/stata_light.py,sha256=cN0ulhqteDqKkGnOqAL1aNHy3AvYbmu-fS35XaMptKM,1274 +pygments/styles/tango.py,sha256=1VtAeshYeFh4jWITdb5_wf-7avl1DwtGWrQkvSKqJJo,7096 +pygments/styles/trac.py,sha256=wWJokrY8EWWxJTChPxxYsH_cB-CNN7coa1ZBihzbiG4,1933 +pygments/styles/vim.py,sha256=9PtHne1K4TmKIFcPoM4NY_HRV3naKXRIeEvMC437t7U,1976 +pygments/styles/vs.py,sha256=-mK8_RJJk12gbR-TXP1zedQpflKS2zc9xQQzHbZTB1E,1073 +pygments/styles/xcode.py,sha256=s3NuWSoZ8dRCuU0PU0-aDop4xqgAXP4rVefg5yFgQVg,1501 +pygments/token.py,sha256=J1LOX6vjhiN3pTShN9Mj0MfbWPzhypuPQYZuw29E8As,6167 +pygments/unistring.py,sha256=V4LPrb9dhNBGR-AdEnopDNmwpxFSodqPBuHOlqG9b0g,64569 +pygments/util.py,sha256=586xXHiJGGZxqk5PMBu3vBhE68DLuAe5MBARWrSPGxA,10778 diff --git a/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/WHEEL b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/WHEEL new file mode 100644 index 00000000..3b5c4038 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.6) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/entry_points.txt b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/entry_points.txt new file mode 100644 index 00000000..756d801b --- /dev/null +++ b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +pygmentize = pygments.cmdline:main + diff --git a/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/top_level.txt b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/top_level.txt new file mode 100644 index 00000000..a9f49e01 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Pygments-2.6.1.dist-info/top_level.txt @@ -0,0 +1 @@ +pygments diff --git a/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/INSTALLER b/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/LICENSE b/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/LICENSE new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/METADATA b/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/METADATA new file mode 100644 index 00000000..cd770ded --- /dev/null +++ b/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/METADATA @@ -0,0 +1,313 @@ +Metadata-Version: 2.1 +Name: Unidecode +Version: 1.3.8 +Summary: ASCII transliterations of Unicode text +Home-page: UNKNOWN +Author: Tomaz Solc +Author-email: tomaz.solc@tablix.org +License: GPL +Platform: UNKNOWN +Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Text Processing +Classifier: Topic :: Text Processing :: Filters +Requires-Python: >=3.5 + +Unidecode, lossy ASCII transliterations of Unicode text +======================================================= + +It often happens that you have text data in Unicode, but you need to +represent it in ASCII. For example when integrating with legacy code that +doesn't support Unicode, or for ease of entry of non-Roman names on a US +keyboard, or when constructing ASCII machine identifiers from human-readable +Unicode strings that should still be somewhat intelligible. A popular example +of this is when making an URL slug from an article title. + +**Unidecode is not a replacement for fully supporting Unicode for strings in +your program. There are a number of caveats that come with its use, +especially when its output is directly visible to users. Please read the rest +of this README before using Unidecode in your project.** + +In most of examples listed above you could represent Unicode characters as +``???`` or ``\\15BA\\15A0\\1610``, to mention two extreme cases. But that's +nearly useless to someone who actually wants to read what the text says. + +What Unidecode provides is a middle road: the function ``unidecode()`` takes +Unicode data and tries to represent it in ASCII characters (i.e., the +universally displayable characters between 0x00 and 0x7F), where the +compromises taken when mapping between two character sets are chosen to be +near what a human with a US keyboard would choose. + +The quality of resulting ASCII representation varies. For languages of +western origin it should be between perfect and good. On the other hand +transliteration (i.e., conveying, in Roman letters, the pronunciation +expressed by the text in some other writing system) of languages like +Chinese, Japanese or Korean is a very complex issue and this library does +not even attempt to address it. It draws the line at context-free +character-by-character mapping. So a good rule of thumb is that the further +the script you are transliterating is from Latin alphabet, the worse the +transliteration will be. + +Generally Unidecode produces better results than simply stripping accents from +characters (which can be done in Python with built-in functions). It is based +on hand-tuned character mappings that for example also contain ASCII +approximations for symbols and non-Latin alphabets. + +**Note that some people might find certain transliterations offending.** Most +common examples include characters that are used in multiple languages. A user +expects a character to be transliterated in their language but Unidecode uses a +transliteration for a different language. It's best to not use Unidecode for +strings that are directly visible to users of your application. See also the +*Frequently Asked Questions* section for more info on common problems. + +This is a Python port of ``Text::Unidecode`` Perl module by Sean M. Burke +. + + +Module content +-------------- + +This library contains a function that takes a string object, possibly +containing non-ASCII characters, and returns a string that can be safely +encoded to ASCII:: + + >>> from unidecode import unidecode + >>> unidecode('kožušček') + 'kozuscek' + >>> unidecode('30 \U0001d5c4\U0001d5c6/\U0001d5c1') + '30 km/h' + >>> unidecode('\u5317\u4EB0') + 'Bei Jing ' + +You can also specify an *errors* argument to ``unidecode()`` that determines +what Unidecode does with characters that are not present in its transliteration +tables. The default is ``'ignore'`` meaning that Unidecode will ignore those +characters (replace them with an empty string). ``'strict'`` will raise a +``UnidecodeError``. The exception object will contain an *index* attribute that +can be used to find the offending character. ``'replace'`` will replace them +with ``'?'`` (or another string, specified in the *replace_str* argument). +``'preserve'`` will keep the original, non-ASCII character in the string. Note +that if ``'preserve'`` is used the string returned by ``unidecode()`` will not +be ASCII-encodable!:: + + >>> unidecode('\ue000') # unidecode does not have replacements for Private Use Area characters + '' + >>> unidecode('\ue000', errors='strict') + Traceback (most recent call last): + ... + unidecode.UnidecodeError: no replacement found for character '\ue000' in position 0 + +A utility is also included that allows you to transliterate text from the +command line in several ways. Reading from standard input:: + + $ echo hello | unidecode + hello + +from a command line argument:: + + $ unidecode -c hello + hello + +or from a file:: + + $ unidecode hello.txt + hello + +The default encoding used by the utility depends on your system locale. You can +specify another encoding with the ``-e`` argument. See ``unidecode --help`` for +a full list of available options. + +Requirements +------------ + +Nothing except Python itself. Unidecode supports Python 3.5 or later. + +You need a Python build with "wide" Unicode characters (also called "UCS-4 +build") in order for Unidecode to work correctly with characters outside of +Basic Multilingual Plane (BMP). Common characters outside BMP are bold, italic, +script, etc. variants of the Latin alphabet intended for mathematical notation. +Surrogate pair encoding of "narrow" builds is not supported in Unidecode. + +If your Python build supports "wide" Unicode the following expression will +return True:: + + >>> import sys + >>> sys.maxunicode > 0xffff + True + +See `PEP 261 `_ for details +regarding support for "wide" Unicode characters in Python. + + +Installation +------------ + +To install the latest version of Unidecode from the Python package index, use +these commands:: + + $ pip install unidecode + +To install Unidecode from the source distribution and run unit tests, use:: + + $ python setup.py install + $ python setup.py test + +Frequently asked questions +-------------------------- + +German umlauts are transliterated incorrectly + Latin letters "a", "o" and "u" with diaeresis are transliterated by + Unidecode as "a", "o", "u", *not* according to German rules "ae", "oe", + "ue". This is intentional and will not be changed. Rationale is that these + letters are used in languages other than German (for example, Finnish and + Turkish). German text transliterated without the extra "e" is much more + readable than other languages transliterated using German rules. A + workaround is to do your own replacements of these characters before + passing the string to ``unidecode()``. + +Japanese Kanji is transliterated as Chinese + Same as with Latin letters with accents discussed in the answer above, the + Unicode standard encodes letters, not letters in a certain language or + their meaning. With Japanese and Chinese this is even more evident because + the same letter can have very different transliterations depending on the + language it is used in. Since Unidecode does not do language-specific + transliteration (see next question), it must decide on one. For certain + characters that are used in both Japanese and Chinese the decision was to + use Chinese transliterations. If you intend to transliterate Japanese, + Chinese or Korean text please consider using other libraries which do + language-specific transliteration, such as `Unihandecode + `_. + +Unidecode should support localization (e.g. a language or country parameter, inspecting system locale, etc.) + Language-specific transliteration is a complicated problem and beyond the + scope of this library. Changes related to this will not be accepted. Please + consider using other libraries which do provide this capability, such as + `Unihandecode `_. + +Unidecode should automatically detect the language of the text being transliterated + Language detection is a completely separate problem and beyond the scope of + this library. + +Unidecode should use a permissive license such as MIT or the BSD license. + The maintainer of Unidecode believes that providing access to source code + on redistribution is a fair and reasonable request when basing products on + voluntary work of many contributors. If the license is not suitable for + you, please consider using other libraries, such as `text-unidecode + `_. + +Unidecode produces completely wrong results (e.g. "u" with diaeresis transliterating as "A 1/4 ") + The strings you are passing to Unidecode have been wrongly decoded + somewhere in your program. For example, you might be decoding utf-8 encoded + strings as latin1. With a misconfigured terminal, locale and/or a text + editor this might not be immediately apparent. Inspect your strings with + ``repr()`` and consult the + `Unicode HOWTO `_. + +Why does Unidecode not replace \\u and \\U backslash escapes in my strings? + Unidecode knows nothing about escape sequences. Interpreting these sequences + and replacing them with actual Unicode characters in string literals is the + task of the Python interpreter. If you are asking this question you are + very likely misunderstanding the purpose of this library. Consult the + `Unicode HOWTO `_ and possibly + the ``unicode_escape`` encoding in the standard library. + +I've upgraded Unidecode and now some URLs on my website return 404 Not Found. + This is an issue with the software that is running your website, not + Unidecode. Occasionally, new versions of Unidecode library are released + which contain improvements to the transliteration tables. This means that + you cannot rely that ``unidecode()`` output will not change across + different versions of Unidecode library. If you use ``unidecode()`` to + generate URLs for your website, either generate the URL slug once and store + it in the database or lock your dependency of Unidecode to one specific + version. + +Some of the issues in this section are discussed in more detail in `this blog +post `_. + + +Performance notes +----------------- + +By default, ``unidecode()`` optimizes for the use case where most of the strings +passed to it are already ASCII-only and no transliteration is necessary (this +default might change in future versions). + +For performance critical applications, two additional functions are exposed: + +``unidecode_expect_ascii()`` is optimized for ASCII-only inputs (approximately +5 times faster than ``unidecode_expect_nonascii()`` on 10 character strings, +more on longer strings), but slightly slower for non-ASCII inputs. + +``unidecode_expect_nonascii()`` takes approximately the same amount of time on +ASCII and non-ASCII inputs, but is slightly faster for non-ASCII inputs than +``unidecode_expect_ascii()``. + +Apart from differences in run time, both functions produce identical results. +For most users of Unidecode, the difference in performance should be +negligible. + + +Source +------ + +You can get the latest development version of Unidecode with:: + + $ git clone https://www.tablix.org/~avian/git/unidecode.git + +There is also an official mirror of this repository on GitHub at +https://github.com/avian2/unidecode + + +Contact +------- + +Please make sure to read the `Frequently asked questions`_ section above before +contacting the maintainer. + +Bug reports, patches and suggestions for Unidecode can be sent to +tomaz.solc@tablix.org. + +Alternatively, you can also open a ticket or pull request at +https://github.com/avian2/unidecode + + +Copyright +--------- + +Original character transliteration tables: + +Copyright 2001, Sean M. Burke , all rights reserved. + +Python code and later additions: + +Copyright 2024, Tomaž Šolc + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your option) +any later version. + +This program 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 General Public License for +more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., 51 +Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. The programs and +documentation in this dist are distributed in the hope that they will be +useful, but without any warranty; without even the implied warranty of +merchantability or fitness for a particular purpose. + +.. + vim: set filetype=rst: + + diff --git a/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/RECORD b/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/RECORD new file mode 100644 index 00000000..a7121c26 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/RECORD @@ -0,0 +1,395 @@ +../../../bin/unidecode,sha256=JEq_YekuebhC5n8aQBENjADF4MCqfWZyJxGC6IAx9Kk,260 +Unidecode-1.3.8.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Unidecode-1.3.8.dist-info/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092 +Unidecode-1.3.8.dist-info/METADATA,sha256=CRKkssuy2vZTZZF2UrckNre6Nahve8U5B_H1m_9kP4o,13632 +Unidecode-1.3.8.dist-info/RECORD,, +Unidecode-1.3.8.dist-info/WHEEL,sha256=_NOXIqFgOaYmlm9RJLPQZ13BJuEIrp5jx5ptRD5uh3Y,92 +Unidecode-1.3.8.dist-info/entry_points.txt,sha256=ItDp7W6CoSJQAKqdiUkzmecugvKvWzjfN7MMK52GM10,51 +Unidecode-1.3.8.dist-info/top_level.txt,sha256=4uYNG2l04s0dm0mEQmPLo2zrjLbhLPKUesLr2dOTdpo,10 +unidecode/__init__.py,sha256=uUP370Iden1EsQtgglNd57DMKOG5mXh9UxIMm8yhDfQ,4230 +unidecode/__main__.py,sha256=VWYWCclyJsdhtNMQtryMFbgsCZtNUsWcEuS7ZOlH1Jc,40 +unidecode/__pycache__/__init__.cpython-311.pyc,, +unidecode/__pycache__/__main__.cpython-311.pyc,, +unidecode/__pycache__/util.cpython-311.pyc,, +unidecode/__pycache__/x000.cpython-311.pyc,, +unidecode/__pycache__/x001.cpython-311.pyc,, +unidecode/__pycache__/x002.cpython-311.pyc,, +unidecode/__pycache__/x003.cpython-311.pyc,, +unidecode/__pycache__/x004.cpython-311.pyc,, +unidecode/__pycache__/x005.cpython-311.pyc,, +unidecode/__pycache__/x006.cpython-311.pyc,, +unidecode/__pycache__/x007.cpython-311.pyc,, +unidecode/__pycache__/x009.cpython-311.pyc,, +unidecode/__pycache__/x00a.cpython-311.pyc,, +unidecode/__pycache__/x00b.cpython-311.pyc,, +unidecode/__pycache__/x00c.cpython-311.pyc,, +unidecode/__pycache__/x00d.cpython-311.pyc,, +unidecode/__pycache__/x00e.cpython-311.pyc,, +unidecode/__pycache__/x00f.cpython-311.pyc,, +unidecode/__pycache__/x010.cpython-311.pyc,, +unidecode/__pycache__/x011.cpython-311.pyc,, +unidecode/__pycache__/x012.cpython-311.pyc,, +unidecode/__pycache__/x013.cpython-311.pyc,, +unidecode/__pycache__/x014.cpython-311.pyc,, +unidecode/__pycache__/x015.cpython-311.pyc,, +unidecode/__pycache__/x016.cpython-311.pyc,, +unidecode/__pycache__/x017.cpython-311.pyc,, +unidecode/__pycache__/x018.cpython-311.pyc,, +unidecode/__pycache__/x01d.cpython-311.pyc,, +unidecode/__pycache__/x01e.cpython-311.pyc,, +unidecode/__pycache__/x01f.cpython-311.pyc,, +unidecode/__pycache__/x020.cpython-311.pyc,, +unidecode/__pycache__/x021.cpython-311.pyc,, +unidecode/__pycache__/x022.cpython-311.pyc,, +unidecode/__pycache__/x023.cpython-311.pyc,, +unidecode/__pycache__/x024.cpython-311.pyc,, +unidecode/__pycache__/x025.cpython-311.pyc,, +unidecode/__pycache__/x026.cpython-311.pyc,, +unidecode/__pycache__/x027.cpython-311.pyc,, +unidecode/__pycache__/x028.cpython-311.pyc,, +unidecode/__pycache__/x029.cpython-311.pyc,, +unidecode/__pycache__/x02a.cpython-311.pyc,, +unidecode/__pycache__/x02c.cpython-311.pyc,, +unidecode/__pycache__/x02e.cpython-311.pyc,, +unidecode/__pycache__/x02f.cpython-311.pyc,, +unidecode/__pycache__/x030.cpython-311.pyc,, +unidecode/__pycache__/x031.cpython-311.pyc,, +unidecode/__pycache__/x032.cpython-311.pyc,, +unidecode/__pycache__/x033.cpython-311.pyc,, +unidecode/__pycache__/x04d.cpython-311.pyc,, +unidecode/__pycache__/x04e.cpython-311.pyc,, +unidecode/__pycache__/x04f.cpython-311.pyc,, +unidecode/__pycache__/x050.cpython-311.pyc,, +unidecode/__pycache__/x051.cpython-311.pyc,, +unidecode/__pycache__/x052.cpython-311.pyc,, +unidecode/__pycache__/x053.cpython-311.pyc,, +unidecode/__pycache__/x054.cpython-311.pyc,, +unidecode/__pycache__/x055.cpython-311.pyc,, +unidecode/__pycache__/x056.cpython-311.pyc,, +unidecode/__pycache__/x057.cpython-311.pyc,, +unidecode/__pycache__/x058.cpython-311.pyc,, +unidecode/__pycache__/x059.cpython-311.pyc,, +unidecode/__pycache__/x05a.cpython-311.pyc,, +unidecode/__pycache__/x05b.cpython-311.pyc,, +unidecode/__pycache__/x05c.cpython-311.pyc,, +unidecode/__pycache__/x05d.cpython-311.pyc,, +unidecode/__pycache__/x05e.cpython-311.pyc,, +unidecode/__pycache__/x05f.cpython-311.pyc,, +unidecode/__pycache__/x060.cpython-311.pyc,, +unidecode/__pycache__/x061.cpython-311.pyc,, +unidecode/__pycache__/x062.cpython-311.pyc,, +unidecode/__pycache__/x063.cpython-311.pyc,, +unidecode/__pycache__/x064.cpython-311.pyc,, +unidecode/__pycache__/x065.cpython-311.pyc,, +unidecode/__pycache__/x066.cpython-311.pyc,, +unidecode/__pycache__/x067.cpython-311.pyc,, +unidecode/__pycache__/x068.cpython-311.pyc,, +unidecode/__pycache__/x069.cpython-311.pyc,, +unidecode/__pycache__/x06a.cpython-311.pyc,, +unidecode/__pycache__/x06b.cpython-311.pyc,, +unidecode/__pycache__/x06c.cpython-311.pyc,, +unidecode/__pycache__/x06d.cpython-311.pyc,, +unidecode/__pycache__/x06e.cpython-311.pyc,, +unidecode/__pycache__/x06f.cpython-311.pyc,, +unidecode/__pycache__/x070.cpython-311.pyc,, +unidecode/__pycache__/x071.cpython-311.pyc,, +unidecode/__pycache__/x072.cpython-311.pyc,, +unidecode/__pycache__/x073.cpython-311.pyc,, +unidecode/__pycache__/x074.cpython-311.pyc,, +unidecode/__pycache__/x075.cpython-311.pyc,, +unidecode/__pycache__/x076.cpython-311.pyc,, +unidecode/__pycache__/x077.cpython-311.pyc,, +unidecode/__pycache__/x078.cpython-311.pyc,, +unidecode/__pycache__/x079.cpython-311.pyc,, +unidecode/__pycache__/x07a.cpython-311.pyc,, +unidecode/__pycache__/x07b.cpython-311.pyc,, +unidecode/__pycache__/x07c.cpython-311.pyc,, +unidecode/__pycache__/x07d.cpython-311.pyc,, +unidecode/__pycache__/x07e.cpython-311.pyc,, +unidecode/__pycache__/x07f.cpython-311.pyc,, +unidecode/__pycache__/x080.cpython-311.pyc,, +unidecode/__pycache__/x081.cpython-311.pyc,, +unidecode/__pycache__/x082.cpython-311.pyc,, +unidecode/__pycache__/x083.cpython-311.pyc,, +unidecode/__pycache__/x084.cpython-311.pyc,, +unidecode/__pycache__/x085.cpython-311.pyc,, +unidecode/__pycache__/x086.cpython-311.pyc,, +unidecode/__pycache__/x087.cpython-311.pyc,, +unidecode/__pycache__/x088.cpython-311.pyc,, +unidecode/__pycache__/x089.cpython-311.pyc,, +unidecode/__pycache__/x08a.cpython-311.pyc,, +unidecode/__pycache__/x08b.cpython-311.pyc,, +unidecode/__pycache__/x08c.cpython-311.pyc,, +unidecode/__pycache__/x08d.cpython-311.pyc,, +unidecode/__pycache__/x08e.cpython-311.pyc,, +unidecode/__pycache__/x08f.cpython-311.pyc,, +unidecode/__pycache__/x090.cpython-311.pyc,, +unidecode/__pycache__/x091.cpython-311.pyc,, +unidecode/__pycache__/x092.cpython-311.pyc,, +unidecode/__pycache__/x093.cpython-311.pyc,, +unidecode/__pycache__/x094.cpython-311.pyc,, +unidecode/__pycache__/x095.cpython-311.pyc,, +unidecode/__pycache__/x096.cpython-311.pyc,, +unidecode/__pycache__/x097.cpython-311.pyc,, +unidecode/__pycache__/x098.cpython-311.pyc,, +unidecode/__pycache__/x099.cpython-311.pyc,, +unidecode/__pycache__/x09a.cpython-311.pyc,, +unidecode/__pycache__/x09b.cpython-311.pyc,, +unidecode/__pycache__/x09c.cpython-311.pyc,, +unidecode/__pycache__/x09d.cpython-311.pyc,, +unidecode/__pycache__/x09e.cpython-311.pyc,, +unidecode/__pycache__/x09f.cpython-311.pyc,, +unidecode/__pycache__/x0a0.cpython-311.pyc,, +unidecode/__pycache__/x0a1.cpython-311.pyc,, +unidecode/__pycache__/x0a2.cpython-311.pyc,, +unidecode/__pycache__/x0a3.cpython-311.pyc,, +unidecode/__pycache__/x0a4.cpython-311.pyc,, +unidecode/__pycache__/x0ac.cpython-311.pyc,, +unidecode/__pycache__/x0ad.cpython-311.pyc,, +unidecode/__pycache__/x0ae.cpython-311.pyc,, +unidecode/__pycache__/x0af.cpython-311.pyc,, +unidecode/__pycache__/x0b0.cpython-311.pyc,, +unidecode/__pycache__/x0b1.cpython-311.pyc,, +unidecode/__pycache__/x0b2.cpython-311.pyc,, +unidecode/__pycache__/x0b3.cpython-311.pyc,, +unidecode/__pycache__/x0b4.cpython-311.pyc,, +unidecode/__pycache__/x0b5.cpython-311.pyc,, +unidecode/__pycache__/x0b6.cpython-311.pyc,, +unidecode/__pycache__/x0b7.cpython-311.pyc,, +unidecode/__pycache__/x0b8.cpython-311.pyc,, +unidecode/__pycache__/x0b9.cpython-311.pyc,, +unidecode/__pycache__/x0ba.cpython-311.pyc,, +unidecode/__pycache__/x0bb.cpython-311.pyc,, +unidecode/__pycache__/x0bc.cpython-311.pyc,, +unidecode/__pycache__/x0bd.cpython-311.pyc,, +unidecode/__pycache__/x0be.cpython-311.pyc,, +unidecode/__pycache__/x0bf.cpython-311.pyc,, +unidecode/__pycache__/x0c0.cpython-311.pyc,, +unidecode/__pycache__/x0c1.cpython-311.pyc,, +unidecode/__pycache__/x0c2.cpython-311.pyc,, +unidecode/__pycache__/x0c3.cpython-311.pyc,, +unidecode/__pycache__/x0c4.cpython-311.pyc,, +unidecode/__pycache__/x0c5.cpython-311.pyc,, +unidecode/__pycache__/x0c6.cpython-311.pyc,, +unidecode/__pycache__/x0c7.cpython-311.pyc,, +unidecode/__pycache__/x0c8.cpython-311.pyc,, +unidecode/__pycache__/x0c9.cpython-311.pyc,, +unidecode/__pycache__/x0ca.cpython-311.pyc,, +unidecode/__pycache__/x0cb.cpython-311.pyc,, +unidecode/__pycache__/x0cc.cpython-311.pyc,, +unidecode/__pycache__/x0cd.cpython-311.pyc,, +unidecode/__pycache__/x0ce.cpython-311.pyc,, +unidecode/__pycache__/x0cf.cpython-311.pyc,, +unidecode/__pycache__/x0d0.cpython-311.pyc,, +unidecode/__pycache__/x0d1.cpython-311.pyc,, +unidecode/__pycache__/x0d2.cpython-311.pyc,, +unidecode/__pycache__/x0d3.cpython-311.pyc,, +unidecode/__pycache__/x0d4.cpython-311.pyc,, +unidecode/__pycache__/x0d5.cpython-311.pyc,, +unidecode/__pycache__/x0d6.cpython-311.pyc,, +unidecode/__pycache__/x0d7.cpython-311.pyc,, +unidecode/__pycache__/x0f9.cpython-311.pyc,, +unidecode/__pycache__/x0fa.cpython-311.pyc,, +unidecode/__pycache__/x0fb.cpython-311.pyc,, +unidecode/__pycache__/x0fc.cpython-311.pyc,, +unidecode/__pycache__/x0fd.cpython-311.pyc,, +unidecode/__pycache__/x0fe.cpython-311.pyc,, +unidecode/__pycache__/x0ff.cpython-311.pyc,, +unidecode/__pycache__/x1d4.cpython-311.pyc,, +unidecode/__pycache__/x1d5.cpython-311.pyc,, +unidecode/__pycache__/x1d6.cpython-311.pyc,, +unidecode/__pycache__/x1d7.cpython-311.pyc,, +unidecode/__pycache__/x1f1.cpython-311.pyc,, +unidecode/__pycache__/x1f6.cpython-311.pyc,, +unidecode/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +unidecode/util.py,sha256=ZxssZFzbZlAf6oiDIu2HZjrAQckbOD2VPD9uy-wZgCI,1652 +unidecode/x000.py,sha256=DaoVzSCvFzhzHbFtzFOE8uS9CgWD7K3JuhmACpFbivY,3038 +unidecode/x001.py,sha256=ylHh3UVaPtibVuUEEWvdSeDFK0OXrWt4-LnxAgYD6qo,3891 +unidecode/x002.py,sha256=NWord8myi2jYV4YwlNZFbKu6HgbbolWLNCOlseR3WsY,3871 +unidecode/x003.py,sha256=5gZS5aXbQ4Z8aH08EehKx4SqAgUNBcTz_x-I3o5qvVg,3825 +unidecode/x004.py,sha256=KAeJjKgkdzMU1MK9J9JqmPeKBDgjhG5UcfyAa594Hk8,4054 +unidecode/x005.py,sha256=7ezPyF52iKiK5LPf6TA5zVUZ7RbIjz7EVLS42aXG9ug,3920 +unidecode/x006.py,sha256=Jye83eXYQqtpowxsQ01jQSDlhAjWbmGNFRdmbojvgyE,3912 +unidecode/x007.py,sha256=6lnnnArEmvi3XeZLFwrCZGStdDKDAHt7alIpdo8S7rk,3987 +unidecode/x009.py,sha256=xNz8qrO1PDseMjOwA0rjsiAhNZTO_uFgjpmbp7qcH_c,4013 +unidecode/x00a.py,sha256=2xksKrrMWF9xLbs8OPfTxT7g86ciwdK9QZ8AQeecmus,4019 +unidecode/x00b.py,sha256=Y7GlfYE2v-D3BkZd3ctfo6L21VG-aR2OFESRb8_WRH4,4019 +unidecode/x00c.py,sha256=jOGpNU7vxghp3jwUuUATiSrDwvgZuOe8nlkcjJYTHco,4007 +unidecode/x00d.py,sha256=lkFf8d_oXN8IZop6CFlYpKdWuJqWGnH0WQMwir4_WgI,4025 +unidecode/x00e.py,sha256=ARKK__sIXUXL4h2Egac2f9ng2Z_YCGD5kYP2oj-ptlI,3989 +unidecode/x00f.py,sha256=TdSmr755Jw1TRtxk5Z4UPZIp1CVhXii8S0zSAcQ2vWk,3998 +unidecode/x010.py,sha256=YhXX8s1dP7YJMzaaV9CMBCOraExb6QrQQWbkFT3d2Jo,4011 +unidecode/x011.py,sha256=bc5lAse0haio2pceaADqkjzTh8MdgNTwTh04W2FJO-Q,4120 +unidecode/x012.py,sha256=XoiRFvNtHV29Q76KcpPBSrC4sLd6faTz4tKZEMIQ45M,4293 +unidecode/x013.py,sha256=UkxSb2Q4xq7dydCZNg_f0Nu90slVSmAckq-btDZ7uAA,4190 +unidecode/x014.py,sha256=4R3w_Dgg9yCw-9KkpqHfWFzyQZZfdb444fMIh240l-Q,4298 +unidecode/x015.py,sha256=TB6O4l2qPxbmF2dejlxXLqX5tTfjl95cMYx1770GHs0,4329 +unidecode/x016.py,sha256=Tx3P-DjDqCLuKbmiG-0cMzw2xFVuojQg3o5yyt4506E,4114 +unidecode/x017.py,sha256=Ks_t-4BgOrTqmqYC6BpqXePI-YyStE7p3P27lzBefSA,4038 +unidecode/x018.py,sha256=C1jpnsK3YO27xpiWJ2DXSAkV9dsPUwKqWtkgePtzp3g,3998 +unidecode/x01d.py,sha256=EwAYkMVHAFvbKRzsQ-e4cRcvS_eia3kYCM2GcaqkBWY,3701 +unidecode/x01e.py,sha256=rG1jtL0dpL-RNsvG-AxX1izkyWkbgwe0BWhATDJtmgg,3845 +unidecode/x01f.py,sha256=NUC2rlFE9YpODdDn4e5uzV7uIqEBNvKw486nOD7UQpQ,3877 +unidecode/x020.py,sha256=lXj8wkWMbD2Iuw3OCrEqZofJjJccnvY3ro5SpyotCq8,4080 +unidecode/x021.py,sha256=QQGWXFmQhQ9ei6rVCx2y-pbx_-7n8bv9DGJpdK_Q8jc,3987 +unidecode/x022.py,sha256=wX6BUR7yKGgSICIzY_B15mqgnjvRbSlepM6aqb2tnGY,4085 +unidecode/x023.py,sha256=weebXXqY3E8OhqS0ziAKHo58lCl3dkkyD0w2aKHqv7Q,4089 +unidecode/x024.py,sha256=JmCTFnYtmMHQvfYP-4f5uDiCxlwhNk7LZLyxLWWGjK8,4003 +unidecode/x025.py,sha256=DAMdCakIv0m21AWcRUNK9QWReCYXPSwVDmbFdriM4qc,3854 +unidecode/x026.py,sha256=TKU0cwRXL8vLAmZ26R8E2dpkmXmRKx4wTU0VEbuTAnM,3874 +unidecode/x027.py,sha256=qZacxfhS5nWgBhbrIT6-wm9yGP_OlAVRJ-GcmUhPl14,3718 +unidecode/x028.py,sha256=FZPCZ9w3N3WOI42h2gHEQgVOAlLBNTZjMu_KQQkIMdk,5069 +unidecode/x029.py,sha256=b8afmG-DjZmHHy0XdjcZlSXtlnwjScIcPBGbMv_YSUQ,4090 +unidecode/x02a.py,sha256=QHAyHnegV0OVOTQ5OnfJKzkaHQIFbWmmMjiFcHGUZi0,4093 +unidecode/x02c.py,sha256=ZkmMztaYT7d81E9qtUU9ayG9hBi5XqWY_ta-X5Hsaqc,4076 +unidecode/x02e.py,sha256=VCGlK7123S2wDzfkggEARyGZKi-0ElepSYECGGluf7E,4072 +unidecode/x02f.py,sha256=hcUTlkw_6Hjnxsk0e28RTd-HWpSK0IGq5hkrwA1fJFk,4091 +unidecode/x030.py,sha256=wdodiC_N7bMsh8vSmVF0STHGZnOAsZnVN-_RPiqupRA,4028 +unidecode/x031.py,sha256=jed0xoqQmUnnOqATVe7z9F2zigAZVAJX6BrWtXFPWbs,4044 +unidecode/x032.py,sha256=lj4IwokKA0IdIJiJJTfmBUGVYmWvLowFtPLwLzhfokU,4466 +unidecode/x033.py,sha256=ImTd4BRRPgCqWmrvJPoikoL0dJMKH8eQgd48vksi60A,4513 +unidecode/x04d.py,sha256=hcUTlkw_6Hjnxsk0e28RTd-HWpSK0IGq5hkrwA1fJFk,4091 +unidecode/x04e.py,sha256=X-Pzl5_QGkYexzNTY04C_tq3RvbyAUYemf0C4mIl5-U,4630 +unidecode/x04f.py,sha256=BM29-2OTb6aR7CN7NMN3nnC9BGxgediLEHGMcIB5ENU,4597 +unidecode/x050.py,sha256=SPmkA-PD39V8eO4DByxVa8HyqanGcw54xW51kLnaieY,4676 +unidecode/x051.py,sha256=GGJT-fiYxTk_FAAW6eTobT3pOGI-Qq1M3eCxN7c7f5E,4681 +unidecode/x052.py,sha256=a09eo_5pL6jpU9TW-zG2w2iXTYp6awtQ4OxGnLdcwKg,4654 +unidecode/x053.py,sha256=4x8X4Hrf56DOAINYi8JxStXW4m7FGJNiH-51JzCxE64,4608 +unidecode/x054.py,sha256=N8hO8YrlNoepnrYLUZ_EcTVRqI1lekqq3h-i-UNlTJw,4577 +unidecode/x055.py,sha256=_PK65HLpk7puojAFGeOm5Cdk-PDevHHI6NR8sHuo0Ko,4595 +unidecode/x056.py,sha256=mlNNouWFIjpZdjuBWhxFGSB_UDh0OItlsShjHQRjhxc,4607 +unidecode/x057.py,sha256=uivN7P3d-kkonqBATLKOM0ni4jVvsSzA9SOEFhbOuP4,4627 +unidecode/x058.py,sha256=lPNpdrFLFfaBoQz8Cwm2Ess8m4m_45ylIHspOUpDrLk,4664 +unidecode/x059.py,sha256=BdA_NFQqr-aGpuyo9he6uxDwm9facV-ql5axiKqgByk,4640 +unidecode/x05a.py,sha256=9UFNWH8FpkHUArS2-Td3VYOo21VQkoqYW7A0Slk0YhQ,4632 +unidecode/x05b.py,sha256=yfWnRe6mtnqY3b3Ac2_IJBA5vBYb64PYF9XM4HSZygU,4666 +unidecode/x05c.py,sha256=6iZj6HHnJ4lF3k1i68-9Dgge2H3KAlyZtNxW0BIu66o,4602 +unidecode/x05d.py,sha256=Wudbb7xOtWry4Xu5xm9j80vFkigCedGq5uHcYAYl0o8,4660 +unidecode/x05e.py,sha256=wKqvr0lkEy1yfXbYj2OtXHBxw5FxVz_MzJULXWrGvA0,4662 +unidecode/x05f.py,sha256=NnSIJOl_9CC4IRwBIQ6CEhTfvvzZ2PXhZSLJuC6sgHY,4656 +unidecode/x060.py,sha256=-Ajr6Q7RP_fdetvZ2hWflxNiaOokB3q5oeRCt7CqcDc,4640 +unidecode/x061.py,sha256=aqOY7Jt--4JhdktU2RB1bf5J0fH27fRDLhV55aR3gO0,4656 +unidecode/x062.py,sha256=wxQkvAGrppx4Y5E-hAVCps0I9bz_fbG1YSqs1E8k9sU,4616 +unidecode/x063.py,sha256=wAcyLr9CJ35G4sNTfvYb7DtFjeRlyo585JC2_-aBuQM,4648 +unidecode/x064.py,sha256=8e775dKt12GedypWT9jPXeqWLkW5-AsVG106FlfiTvA,4651 +unidecode/x065.py,sha256=fPak6ADqEOBFPfP2u7pAIZ_ObbgtdGFa4enmjVBpsVE,4634 +unidecode/x066.py,sha256=K6g6XTVEFEAppiln64vxgA2V1FMWl0YdbhDJgihQsTA,4675 +unidecode/x067.py,sha256=5d8zLxoh2vS76uBWQckXGbeyjzEUJ5aJMAMvNA-YxLs,4627 +unidecode/x068.py,sha256=-UhVYRQGQtxQJbgwyHAox-JHizu_RvO7Lb5I1F9mpvY,4670 +unidecode/x069.py,sha256=cRQZP6ZGJQsx5l2qSfpe9XmiDfxlGh7rEh30_u9oTSo,4665 +unidecode/x06a.py,sha256=iXZkuxRRsgUuNlVlNliR7gio4M4WUN0JNCPdINrzYlY,4662 +unidecode/x06b.py,sha256=5GRxv36m9zR163UNrGb_c64-uueKrpqyeeRWG9ZDme0,4600 +unidecode/x06c.py,sha256=RNKzdImtimBIuLtvbsUAzYSV7iZmVvPWyV8dj91KJlw,4637 +unidecode/x06d.py,sha256=jFvmxRU4VHSeoahyFtHIHqpvfqvJbNzvsrDn4Kd7WAQ,4647 +unidecode/x06e.py,sha256=1esJUSaQ4QotdjhxG6vtvC3CDWjY2rTr4EVLD4if8CU,4630 +unidecode/x06f.py,sha256=s7JTw6eW_6pqjCc1DEMDQ178vtllhHiejtvb360vDVc,4638 +unidecode/x070.py,sha256=oLeIanQmBbyz8OU_l5VQ-POF8mY5XbLL3rfEjr3XkUw,4677 +unidecode/x071.py,sha256=v1S9E-H06WC0rr10gP27Dqev2nxRlymECJ681BSs9Y4,4644 +unidecode/x072.py,sha256=veZOktQoJQ2wmKKLjq17UM5hAa3xo3nRLdFgSHjv8rI,4645 +unidecode/x073.py,sha256=NWkyVIbNgSu_U9katu1LRaLkL7iHx4bSuRtfsqRG4yk,4642 +unidecode/x074.py,sha256=AocniPNZMcBTeiDWA6OLzQilcWMc_ZHh7pCGXTzqMSg,4686 +unidecode/x075.py,sha256=P3SrhI5BQ5sJ66hyu_LWDONpuzLZJBKsl7f-A37sJXc,4675 +unidecode/x076.py,sha256=9rwfe41pej250BneHHO663PU9vVWyrnHRnP11VUqxEc,4635 +unidecode/x077.py,sha256=ugbmqiry2-tBstXW0Q9o7XEZQimpagZK1EttvBCK1sE,4673 +unidecode/x078.py,sha256=NxeTS_dXa6jmc7iDVUve6_SqO4AhjULng_Gei7pqbRE,4630 +unidecode/x079.py,sha256=ucPPGrgm-AnnWdVFd__unqiSMtdEpZQF6E8ta6IzdiQ,4590 +unidecode/x07a.py,sha256=fjyeO--0F5Kd80F0yOvFIIuiDW7lFKWaVIUqQRIwR9k,4659 +unidecode/x07b.py,sha256=3g39Yw2ZMs7_tcC3OT2e4nGxaWMY6V8iJ2Z6PsnhPS4,4667 +unidecode/x07c.py,sha256=Cbs98r7vdJD_YxpXgAAYoPdA7RDYR82MXN44TQJxoN8,4647 +unidecode/x07d.py,sha256=EKFrTQTNFLGnsm3qI76ALxrxGCcDuyEbapi9j9jy1B4,4678 +unidecode/x07e.py,sha256=r96YBkHoCO8GAvO0j3BdY45RdlNkqpiFWl-Q6mieVcc,4680 +unidecode/x07f.py,sha256=MNRU4aNOE2dKl4p0_WPy-oga_cx7wZ6w4Mlk-RN3PeU,4658 +unidecode/x080.py,sha256=9feIVoCdOFolKgZfRCpdL80l9kRvjbl0z9sV4FAk2Qg,4643 +unidecode/x081.py,sha256=ffvplClKTCDre83MhO7-X3tgdUWfjvkUMxQCPEnRj_U,4671 +unidecode/x082.py,sha256=XTFSjZO8LO3SFcYh9h-Oqby6a67hFDx4B-AQRyptlJU,4641 +unidecode/x083.py,sha256=wXP1lZZAravJZm1f1bCT1cumocFGRG0ZQmgFMVCOSDQ,4635 +unidecode/x084.py,sha256=inA5ODar8zAherLeTyX9-KtCUOrTigxDwb3ei2Kl1CE,4630 +unidecode/x085.py,sha256=QDKK-wbb04nCFc91pSGhyHsxcS_MhdeQLsRqqXhV9h8,4628 +unidecode/x086.py,sha256=DcXhJemXKgrGwPBRFCbINxfxatqjpy7jFgM9jbN8eEk,4608 +unidecode/x087.py,sha256=nddqMqheth-n7kHCyjRNvVPO82UI_PdOic1kQer_iF0,4641 +unidecode/x088.py,sha256=0PVL160fpQ-Kkw29X-bLviyfs4TKIAwp_-SwEWsvemM,4639 +unidecode/x089.py,sha256=RrIGIX6dojryaYh6Da4ysaM_-yREbNZ-HasFX2O_SQc,4624 +unidecode/x08a.py,sha256=NjMp9ck824PXG2gcJXfi_9oQCFgXhhiallO3bYCtXCE,4647 +unidecode/x08b.py,sha256=vUwkG_IOBIhB8gQAaVbgD5EAIA1wY4BBPk5kXwAcPg0,4639 +unidecode/x08c.py,sha256=0sHcCXB9YzXE9oJcwzPtPUltCn6Oo-itdY5vk6MbtdA,4628 +unidecode/x08d.py,sha256=SWD7xSIR-1P30S5-yuNDHpVjWlpfxmUxuJr7f178WsA,4630 +unidecode/x08e.py,sha256=Ym0RQUdsgZJdVmOI56nzSmzfxuKjuS5MUbPSOeyv2Ws,4655 +unidecode/x08f.py,sha256=tNFpnEzNLIY4xHbcR0rZqaoNUKinj-XO2XfSnh6c4u4,4649 +unidecode/x090.py,sha256=XGomJNriNZsHQRUDy3vKwFc4W38uxeqWpn5SHM4G4j8,4627 +unidecode/x091.py,sha256=u8tRZhaVNa2mbsDSYIKqRZ3u4Npj-kiz55rC9izadnM,4653 +unidecode/x092.py,sha256=NvNce8y3YFlPI20pN1a4LY68sid5ApetXs9bo9cxb7w,4644 +unidecode/x093.py,sha256=O2e1p58RB1TS2Au-JSjft3FgPBx1YRAGxnviqSsfnYE,4646 +unidecode/x094.py,sha256=k8ZwNc9qCSzU2b8wMrWUeGSg39tPMiwiKHCiKw6zteM,4653 +unidecode/x095.py,sha256=H2O3xJDE3cAOecyYMUTl6fLs9shETPFwZshtIIK5V3E,4667 +unidecode/x096.py,sha256=sev3zRm46EBQgEtkR4T-Ah0cHYEM-9CM2pFCCc21BFI,4608 +unidecode/x097.py,sha256=S1nZBdt-MHySCAgV9MDdNSQTCSaD62iAhz8EjikfS5A,4633 +unidecode/x098.py,sha256=w0KMxUF7XyG9gdfTJylYsG_clvm3RD_LIM5XHR0xsdY,4643 +unidecode/x099.py,sha256=nlaWb2nRTSnFfDjseDRJ1a3Y0okOHbNA1-htKo9SkAM,4627 +unidecode/x09a.py,sha256=Z8pQsTc62CWgm0JPnj3kokKKf9_qfzRpo0u5iH61CaE,4623 +unidecode/x09b.py,sha256=njA75MlCgC-5UuS1Hvm-mdgsVwW9r801odfBTJg-BFE,4653 +unidecode/x09c.py,sha256=NveMhN85_Cm4H1cnfHDTcnSj675MOVBq9Lkjpw3YxA0,4659 +unidecode/x09d.py,sha256=_0fAaUhK3axhhWLA4QPNJf_J9YSs1MCKx2xR-vl5QYI,4630 +unidecode/x09e.py,sha256=wreETFCeKf9bVvLc3E7MUAvlu2CN5xKeebf3ESuV13s,4613 +unidecode/x09f.py,sha256=pNAdX7-9yMEPXtozjCuXxzc74eCVft9meOTxCtU7vJw,4420 +unidecode/x0a0.py,sha256=EpopPuuocybgCcpX19Ii-udqsPXJjSces3360lqJ8vs,4428 +unidecode/x0a1.py,sha256=0hvF77d5E640SujjdHVqy5gMUH85gEdOv80eRvCEAGM,4469 +unidecode/x0a2.py,sha256=9Icpfk_ElebYd_xN09OMziFrpAGPXEUNVmawpnhbBaQ,4503 +unidecode/x0a3.py,sha256=G1lPrnCqYz0s4wsSa1qM0WgrZBWO_beRk3AgK0iVZLA,4521 +unidecode/x0a4.py,sha256=nWPXzCragW0rsDQPa6ooo9KOc-SOjVCP8KIOuGc7WpU,4373 +unidecode/x0ac.py,sha256=wj7hl88VlCdc_eGpOL4m4CBJILyQqd9atObC5Xvd0aA,4709 +unidecode/x0ad.py,sha256=Rz5rn0fM-CqRjaN4TvSq_1StAQdyAF2WX3cUvcQHaWU,4766 +unidecode/x0ae.py,sha256=jNIBVB-Pw2ZNihAeyWbDIEq9Yt9zlhdfGylfvAaxUks,4875 +unidecode/x0af.py,sha256=Am5YC8Zfrun5NUKxU6LrU2-d5GgkGSBs7fZt2rqSi74,5012 +unidecode/x0b0.py,sha256=1bgHerCDAqIcJHYeGddJjJfRWiHCKtU2B0J-XGvcbbc,4853 +unidecode/x0b1.py,sha256=Six-lzGdvgJx4YsIa0lTusnBEV1zbCKQCquq17TDJoQ,4746 +unidecode/x0b2.py,sha256=HQDbmglNi4QfiRSGucUclgq_4FGpRjbJkWU1JTLAFGc,4680 +unidecode/x0b3.py,sha256=1lqxghVZiiStOAx1IG_vc1zZTXrAa7Z__QY6ZWvo2aA,4741 +unidecode/x0b4.py,sha256=V6BNSTxpyP8VuqF7x5z7bpF3MQAkwZfKtEu6NFr_vSg,4762 +unidecode/x0b5.py,sha256=9NVd2hNLyRlLceVlznba1dreqBGeKU_0gzmkgAw0gyg,4919 +unidecode/x0b6.py,sha256=V_vRsB0GICu9hqhO4pnbPWreDSevJ3bbmLRJkuQUxnE,4996 +unidecode/x0b7.py,sha256=CwBaCBICyVagnFjUpkwabuDvBJw7gAeqkSRpfBAVv8s,4833 +unidecode/x0b8.py,sha256=xYp-xy2LIwq95OWyS9vYMc_Z5od9dud0W1dxeg4P_Jk,4714 +unidecode/x0b9.py,sha256=z3hKNzBq_MeK9V3AyQzaY58cgi0-VGOsLk3-UFmszLQ,4704 +unidecode/x0ba.py,sha256=4gubifoBeJUUrwXEI4litJygekufEycmWDLrJ-Qvs14,4765 +unidecode/x0bb.py,sha256=bsCTABUdC6yTn8_0vhYe5jRP1z_BoAdificB8Y1c1hA,4730 +unidecode/x0bc.py,sha256=AhQvAz7yHlbQ_4c2KOIisq07eZJ5JQn6cV8I31oT9kg,4707 +unidecode/x0bd.py,sha256=IGtyVxIUr1mU3hokn6iUDJhXZezQozVvfWOyf4Pa5dI,4752 +unidecode/x0be.py,sha256=1D-hXu3p3wvOnGVMjEqVsrltYe7UuSwit2yqN5eFizc,4849 +unidecode/x0bf.py,sha256=NkEXqr2ER3BNFkTasDV9CHnkRBuX_Ao5OHGv_NgKAew,5010 +unidecode/x0c0.py,sha256=zDlHpyM0omza5TqGLb8Rhl7Wd-LlV1AjvH_xdnEnNFw,4856 +unidecode/x0c1.py,sha256=AC6xJyx9UblKAGNqGN7AH2Idb3_3vbc-I5U0Myig5fA,4765 +unidecode/x0c2.py,sha256=siRYLA8Cv9Z8XsRp3WQOBdRrPkjJOuEh8z1-3SMXOzQ,4710 +unidecode/x0c3.py,sha256=hlAFe6lsz0aLMixlpeFjV4I-WTIiA3B2BU58yGlTwRg,4975 +unidecode/x0c4.py,sha256=z3xZwSkf5ru1FCdBMHOr5fyglzVdyPhQVtWjq9xInsQ,5024 +unidecode/x0c5.py,sha256=F-DR0eVMRkemOnNXOtDjI5i6gW9136XLmWM_yMVvc84,4581 +unidecode/x0c6.py,sha256=7p_jMrHf3WUa_zANms-RGVN1bAeshgWLkC16_VcSawA,4490 +unidecode/x0c7.py,sha256=5eOAq4jFsPZ-zKO7lHzAGj_EvXdaMC4Kud7gvE-B7Tg,4564 +unidecode/x0c8.py,sha256=wltKvhBgn51jULzwUnEbmyDuK9JvQpQee0uTKK42-20,4733 +unidecode/x0c9.py,sha256=GoARON07wCoHN2wRHb5fvzqE9L3Yme2hKeciynUIAIk,4722 +unidecode/x0ca.py,sha256=BsBZTNj3npIkdo3L9pSEX7XvDT68KV7wFtOOwyEb2So,5007 +unidecode/x0cb.py,sha256=8T7vnJMRmYGyySYthMWz0bgN-MremktGImjejodFeMo,5012 +unidecode/x0cc.py,sha256=GKoHN-4vL4Y3EL42G0xbN74Tgspew1oMvxQtsIa3ess,4749 +unidecode/x0cd.py,sha256=7sZ05OjugbaombMRDYOVxgstZbXMcuX5kHFheKv4W2E,4738 +unidecode/x0ce.py,sha256=mOEHFrsAwIvcTnh7OKVK5qbuXUXHfJOR7D4FtXsQmao,4708 +unidecode/x0cf.py,sha256=H9PeYcbOG68F_yc7zsELUuN05ANfFNOUX-e3-gzx7Ow,4713 +unidecode/x0d0.py,sha256=eULqcGHPmaoEdl0EwRB5wWSu8M43bp4HoFo5gGljacg,4706 +unidecode/x0d1.py,sha256=BClLDAjPgsAX6MJCsuHfmfuhH9qfzUy_vb-d9zBs3Oc,4767 +unidecode/x0d2.py,sha256=e74nqGo4E4sF1sy8qBFu2ecWoRfJdoXI1xRFRPqYEz8,4724 +unidecode/x0d3.py,sha256=8-UmvJ3-ILXo9d3GA-ReOE4OfUenL3tVUJYldZ9gHu0,4705 +unidecode/x0d4.py,sha256=fwUmzksoddTKB8fH2rZMxRK3pJtLrxhcrYpHfBauAwE,4758 +unidecode/x0d5.py,sha256=rANSL5ndzLgSgYJQNEw57AfXpicRe7pvHRlKTPb4-QQ,4680 +unidecode/x0d6.py,sha256=fT8_cRzp7y60IIhn87kM9lLehKGAg5wYmfFOwgGp6e0,4765 +unidecode/x0d7.py,sha256=40-m7uKNvylWCcVBuTXrbiP6Lrj_4d4PWgLcX8670Kk,4468 +unidecode/x0f9.py,sha256=2PD0_fpDnaFO9ftICjYSOhnjAfBppjsj1TcLIuYjnCI,4567 +unidecode/x0fa.py,sha256=XHxCfXOhHDqzjG0Nw6n1sT5Q_MKLCovPFe-22IQxVXU,4172 +unidecode/x0fb.py,sha256=n_5urRXj6Ecf0MKMnuwNY0UK6TJtUW2hKcNLQqa2Gf8,3787 +unidecode/x0fc.py,sha256=KcyQnyv7gxNeVcAnRwQrm4NlabZE3CrnmtLqXj_7te8,3595 +unidecode/x0fd.py,sha256=mVHMrX8AhRzwCkMNA4sJkhwirK3BqmNv6YZfyCpE9Is,3703 +unidecode/x0fe.py,sha256=CrdwUOf0sl8yUfOFnXOXFZ8U662dQThpGMwGBkY8cJ4,3769 +unidecode/x0ff.py,sha256=Ijfv5VVDCTWRzRqwMYSp0fSycy176gn7P8ut8x3bv-w,3957 +unidecode/x1d4.py,sha256=xzL0OicR95IWq6LiApIPEgPoST8dyVgYuIUGxkz1b28,3863 +unidecode/x1d5.py,sha256=bmTSTgWnsLP7yUDZq2Irtz84Zm7bmLzYzurY0eI0uIU,3863 +unidecode/x1d6.py,sha256=8H0RmEfbY82X1iQwr0vcsgQGCvGKv19_773K_T2NI2A,4052 +unidecode/x1d7.py,sha256=yyHV2dCo1p_m_QVgz1H9S6XqeiN9GpGxB-ZqAW7l5ts,4057 +unidecode/x1f1.py,sha256=URX9F6UPgUo4-tpr7bhPm4G5ruFDoScW5bZLwzR88Yg,4308 +unidecode/x1f6.py,sha256=Ji4t-EFmJmo3CDeZ0yD7pX58hj5fQQc99TOrD-yad9k,4103 diff --git a/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/WHEEL b/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/WHEEL new file mode 100644 index 00000000..4eeaea1f --- /dev/null +++ b/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.32.3) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/entry_points.txt b/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/entry_points.txt new file mode 100644 index 00000000..3016afe6 --- /dev/null +++ b/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +unidecode = unidecode.util:main + diff --git a/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/top_level.txt b/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/top_level.txt new file mode 100644 index 00000000..051b14ce --- /dev/null +++ b/venv/lib/python3.11/site-packages/Unidecode-1.3.8.dist-info/top_level.txt @@ -0,0 +1 @@ +unidecode diff --git a/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/INSTALLER b/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/LICENSE b/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/LICENSE new file mode 100644 index 00000000..104eebf5 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2018 Alex Grönholm + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/METADATA b/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/METADATA new file mode 100644 index 00000000..10d7aafc --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/METADATA @@ -0,0 +1,105 @@ +Metadata-Version: 2.1 +Name: anyio +Version: 4.6.2.post1 +Summary: High level compatibility layer for multiple asynchronous event loop implementations +Author-email: Alex Grönholm +License: MIT +Project-URL: Documentation, https://anyio.readthedocs.io/en/latest/ +Project-URL: Changelog, https://anyio.readthedocs.io/en/stable/versionhistory.html +Project-URL: Source code, https://github.com/agronholm/anyio +Project-URL: Issue tracker, https://github.com/agronholm/anyio/issues +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Framework :: AnyIO +Classifier: Typing :: Typed +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: idna >=2.8 +Requires-Dist: sniffio >=1.1 +Requires-Dist: exceptiongroup >=1.0.2 ; python_version < "3.11" +Requires-Dist: typing-extensions >=4.1 ; python_version < "3.11" +Provides-Extra: doc +Requires-Dist: packaging ; extra == 'doc' +Requires-Dist: Sphinx ~=7.4 ; extra == 'doc' +Requires-Dist: sphinx-rtd-theme ; extra == 'doc' +Requires-Dist: sphinx-autodoc-typehints >=1.2.0 ; extra == 'doc' +Provides-Extra: test +Requires-Dist: anyio[trio] ; extra == 'test' +Requires-Dist: coverage[toml] >=7 ; extra == 'test' +Requires-Dist: exceptiongroup >=1.2.0 ; extra == 'test' +Requires-Dist: hypothesis >=4.0 ; extra == 'test' +Requires-Dist: psutil >=5.9 ; extra == 'test' +Requires-Dist: pytest >=7.0 ; extra == 'test' +Requires-Dist: pytest-mock >=3.6.1 ; extra == 'test' +Requires-Dist: trustme ; extra == 'test' +Requires-Dist: uvloop >=0.21.0b1 ; (platform_python_implementation == "CPython" and platform_system != "Windows") and extra == 'test' +Requires-Dist: truststore >=0.9.1 ; (python_version >= "3.10") and extra == 'test' +Provides-Extra: trio +Requires-Dist: trio >=0.26.1 ; extra == 'trio' + +.. image:: https://github.com/agronholm/anyio/actions/workflows/test.yml/badge.svg + :target: https://github.com/agronholm/anyio/actions/workflows/test.yml + :alt: Build Status +.. image:: https://coveralls.io/repos/github/agronholm/anyio/badge.svg?branch=master + :target: https://coveralls.io/github/agronholm/anyio?branch=master + :alt: Code Coverage +.. image:: https://readthedocs.org/projects/anyio/badge/?version=latest + :target: https://anyio.readthedocs.io/en/latest/?badge=latest + :alt: Documentation +.. image:: https://badges.gitter.im/gitterHQ/gitter.svg + :target: https://gitter.im/python-trio/AnyIO + :alt: Gitter chat + +AnyIO is an asynchronous networking and concurrency library that works on top of either asyncio_ or +trio_. It implements trio-like `structured concurrency`_ (SC) on top of asyncio and works in harmony +with the native SC of trio itself. + +Applications and libraries written against AnyIO's API will run unmodified on either asyncio_ or +trio_. AnyIO can also be adopted into a library or application incrementally – bit by bit, no full +refactoring necessary. It will blend in with the native libraries of your chosen backend. + +Documentation +------------- + +View full documentation at: https://anyio.readthedocs.io/ + +Features +-------- + +AnyIO offers the following functionality: + +* Task groups (nurseries_ in trio terminology) +* High-level networking (TCP, UDP and UNIX sockets) + + * `Happy eyeballs`_ algorithm for TCP connections (more robust than that of asyncio on Python + 3.8) + * async/await style UDP sockets (unlike asyncio where you still have to use Transports and + Protocols) + +* A versatile API for byte streams and object streams +* Inter-task synchronization and communication (locks, conditions, events, semaphores, object + streams) +* Worker threads +* Subprocesses +* Asynchronous file I/O (using worker threads) +* Signal handling + +AnyIO also comes with its own pytest_ plugin which also supports asynchronous fixtures. +It even works with the popular Hypothesis_ library. + +.. _asyncio: https://docs.python.org/3/library/asyncio.html +.. _trio: https://github.com/python-trio/trio +.. _structured concurrency: https://en.wikipedia.org/wiki/Structured_concurrency +.. _nurseries: https://trio.readthedocs.io/en/stable/reference-core.html#nurseries-and-spawning +.. _Happy eyeballs: https://en.wikipedia.org/wiki/Happy_Eyeballs +.. _pytest: https://docs.pytest.org/en/latest/ +.. _Hypothesis: https://hypothesis.works/ diff --git a/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/RECORD b/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/RECORD new file mode 100644 index 00000000..8f346c17 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/RECORD @@ -0,0 +1,82 @@ +anyio-4.6.2.post1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +anyio-4.6.2.post1.dist-info/LICENSE,sha256=U2GsncWPLvX9LpsJxoKXwX8ElQkJu8gCO9uC6s8iwrA,1081 +anyio-4.6.2.post1.dist-info/METADATA,sha256=-tUagL58CG66oT2eLY1593L_yXsIb6xW0oouVCQsE5c,4697 +anyio-4.6.2.post1.dist-info/RECORD,, +anyio-4.6.2.post1.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91 +anyio-4.6.2.post1.dist-info/entry_points.txt,sha256=_d6Yu6uiaZmNe0CydowirE9Cmg7zUL2g08tQpoS3Qvc,39 +anyio-4.6.2.post1.dist-info/top_level.txt,sha256=QglSMiWX8_5dpoVAEIHdEYzvqFMdSYWmCj6tYw2ITkQ,6 +anyio/__init__.py,sha256=myTIdg75VPwA-9L7BpislRQplJUPMeleUBHa4MyIruw,4315 +anyio/__pycache__/__init__.cpython-311.pyc,, +anyio/__pycache__/from_thread.cpython-311.pyc,, +anyio/__pycache__/lowlevel.cpython-311.pyc,, +anyio/__pycache__/pytest_plugin.cpython-311.pyc,, +anyio/__pycache__/to_process.cpython-311.pyc,, +anyio/__pycache__/to_thread.cpython-311.pyc,, +anyio/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/_backends/__pycache__/__init__.cpython-311.pyc,, +anyio/_backends/__pycache__/_asyncio.cpython-311.pyc,, +anyio/_backends/__pycache__/_trio.cpython-311.pyc,, +anyio/_backends/_asyncio.py,sha256=H3rMz2wquSxPnV4KIXpXGtBFWXk3jkFljrzvk7KWk4E,91497 +anyio/_backends/_trio.py,sha256=wfgvQ2ut2CAxOjcuDLAdrucfEgc02XXRN9aC3IEBHdY,40311 +anyio/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/_core/__pycache__/__init__.cpython-311.pyc,, +anyio/_core/__pycache__/_eventloop.cpython-311.pyc,, +anyio/_core/__pycache__/_exceptions.cpython-311.pyc,, +anyio/_core/__pycache__/_fileio.cpython-311.pyc,, +anyio/_core/__pycache__/_resources.cpython-311.pyc,, +anyio/_core/__pycache__/_signals.cpython-311.pyc,, +anyio/_core/__pycache__/_sockets.cpython-311.pyc,, +anyio/_core/__pycache__/_streams.cpython-311.pyc,, +anyio/_core/__pycache__/_subprocesses.cpython-311.pyc,, +anyio/_core/__pycache__/_synchronization.cpython-311.pyc,, +anyio/_core/__pycache__/_tasks.cpython-311.pyc,, +anyio/_core/__pycache__/_testing.cpython-311.pyc,, +anyio/_core/__pycache__/_typedattr.cpython-311.pyc,, +anyio/_core/_eventloop.py,sha256=t_tAwBFPjF8jrZGjlJ6bbYy6KA3bjsbZxV9mvh9t1i0,4695 +anyio/_core/_exceptions.py,sha256=NPxECdXkG4nk3NOCUeFmBEAgPhmj7Bzs4vFAKaW_vqw,2481 +anyio/_core/_fileio.py,sha256=lbGk3xq_6DhvbEI8ykdFf2NjYnhuyc8hjXKZTLYkW4k,20961 +anyio/_core/_resources.py,sha256=NbmU5O5UX3xEyACnkmYX28Fmwdl-f-ny0tHym26e0w0,435 +anyio/_core/_signals.py,sha256=vulT1M1xdLYtAR-eY5TamIgaf1WTlOwOrMGwswlTTr8,905 +anyio/_core/_sockets.py,sha256=iM3UeMU68n0PlQjl2U9HyiOpV26rnjqV4KBr_Fo2z1I,24293 +anyio/_core/_streams.py,sha256=OnaKgoDD-FcMSwLvkoAUGP51sG2ZdRvMpxt9q2w1gYA,1804 +anyio/_core/_subprocesses.py,sha256=WquR6sHrnaZofaeqnL8U4Yv___msVW_WqivleLHK4zI,7760 +anyio/_core/_synchronization.py,sha256=UDsbG5f8jWsWkRxYUOKp_WOBWCI9-vBO6wBrsR6WNjA,20121 +anyio/_core/_tasks.py,sha256=pvVEX2Fw159sf0ypAPerukKsZgRRwvFFedVW52nR2Vk,4764 +anyio/_core/_testing.py,sha256=YUGwA5cgFFbUTv4WFd7cv_BSVr4ryTtPp8owQA3JdWE,2118 +anyio/_core/_typedattr.py,sha256=P4ozZikn3-DbpoYcvyghS_FOYAgbmUxeoU8-L_07pZM,2508 +anyio/abc/__init__.py,sha256=U44_s3BglL8BojWQiq0KuokvCqkunIp-ySH3GyRXxAc,2681 +anyio/abc/__pycache__/__init__.cpython-311.pyc,, +anyio/abc/__pycache__/_eventloop.cpython-311.pyc,, +anyio/abc/__pycache__/_resources.cpython-311.pyc,, +anyio/abc/__pycache__/_sockets.cpython-311.pyc,, +anyio/abc/__pycache__/_streams.cpython-311.pyc,, +anyio/abc/__pycache__/_subprocesses.cpython-311.pyc,, +anyio/abc/__pycache__/_tasks.cpython-311.pyc,, +anyio/abc/__pycache__/_testing.cpython-311.pyc,, +anyio/abc/_eventloop.py,sha256=kdkLSnizMk3tPq61K109iPUQ6uXpvp1uNsj5aP1s0N8,9619 +anyio/abc/_resources.py,sha256=DrYvkNN1hH6Uvv5_5uKySvDsnknGVDe8FCKfko0VtN8,783 +anyio/abc/_sockets.py,sha256=KhWtJxan8jpBXKwPaFeQzI4iRXdFaOIn0HXtDZnaO7U,6262 +anyio/abc/_streams.py,sha256=GzST5Q2zQmxVzdrAqtbSyHNxkPlIC9AzeZJg_YyPAXw,6598 +anyio/abc/_subprocesses.py,sha256=cumAPJTktOQtw63IqG0lDpyZqu_l1EElvQHMiwJgL08,2067 +anyio/abc/_tasks.py,sha256=0Jc6oIwUjMIVReehF6knOZyAqlgwDt4TP1NQkx4IQGw,2731 +anyio/abc/_testing.py,sha256=tBJUzkSfOXJw23fe8qSJ03kJlShOYjjaEyFB6k6MYT8,1821 +anyio/from_thread.py,sha256=dbi5TUH45_Sg_jZ8Vv1NJWVohe0WeQ_OaCvXIKveAGg,17478 +anyio/lowlevel.py,sha256=nkgmW--SdxGVp0cmLUYazjkigveRm5HY7-gW8Bpp9oY,4169 +anyio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/pytest_plugin.py,sha256=vjGhGRHD31OyMgJRFQrMvExhx3Ea8KbyDqYKmiSDdXA,6712 +anyio/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/streams/__pycache__/__init__.cpython-311.pyc,, +anyio/streams/__pycache__/buffered.cpython-311.pyc,, +anyio/streams/__pycache__/file.cpython-311.pyc,, +anyio/streams/__pycache__/memory.cpython-311.pyc,, +anyio/streams/__pycache__/stapled.cpython-311.pyc,, +anyio/streams/__pycache__/text.cpython-311.pyc,, +anyio/streams/__pycache__/tls.cpython-311.pyc,, +anyio/streams/buffered.py,sha256=UCldKC168YuLvT7n3HtNPnQ2iWAMSTYQWbZvzLwMwkM,4500 +anyio/streams/file.py,sha256=6uoTNb5KbMoj-6gS3_xrrL8uZN8Q4iIvOS1WtGyFfKw,4383 +anyio/streams/memory.py,sha256=j8AyOExK4-UPaon_Xbhwax25Vqs0DwFg3ZXc-EIiHjY,10550 +anyio/streams/stapled.py,sha256=U09pCrmOw9kkNhe6tKopsm1QIMT1lFTFvtb-A7SIe4k,4302 +anyio/streams/text.py,sha256=6x8w8xlfCZKTUWQoJiMPoMhSSJFUBRKgoBNSBtbd9yg,5094 +anyio/streams/tls.py,sha256=m3AE2LVSpoRHSIwSoSCupiOVL54EvOFoY3CcwTxcZfg,12742 +anyio/to_process.py,sha256=cR4n7TssbbJowE_9cWme49zaeuoBuMzqgZ6cBIs0YIs,9571 +anyio/to_thread.py,sha256=WM2JQ2MbVsd5D5CM08bQiTwzZIvpsGjfH1Fy247KoDQ,2396 diff --git a/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/WHEEL b/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/WHEEL new file mode 100644 index 00000000..dcfdc6e3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.1.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/entry_points.txt b/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/entry_points.txt new file mode 100644 index 00000000..44dd9bdc --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[pytest11] +anyio = anyio.pytest_plugin diff --git a/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/top_level.txt b/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/top_level.txt new file mode 100644 index 00000000..c77c069e --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio-4.6.2.post1.dist-info/top_level.txt @@ -0,0 +1 @@ +anyio diff --git a/venv/lib/python3.11/site-packages/anyio/__init__.py b/venv/lib/python3.11/site-packages/anyio/__init__.py new file mode 100644 index 00000000..fd9fe06b --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/__init__.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from ._core._eventloop import current_time as current_time +from ._core._eventloop import get_all_backends as get_all_backends +from ._core._eventloop import get_cancelled_exc_class as get_cancelled_exc_class +from ._core._eventloop import run as run +from ._core._eventloop import sleep as sleep +from ._core._eventloop import sleep_forever as sleep_forever +from ._core._eventloop import sleep_until as sleep_until +from ._core._exceptions import BrokenResourceError as BrokenResourceError +from ._core._exceptions import BrokenWorkerProcess as BrokenWorkerProcess +from ._core._exceptions import BusyResourceError as BusyResourceError +from ._core._exceptions import ClosedResourceError as ClosedResourceError +from ._core._exceptions import DelimiterNotFound as DelimiterNotFound +from ._core._exceptions import EndOfStream as EndOfStream +from ._core._exceptions import IncompleteRead as IncompleteRead +from ._core._exceptions import TypedAttributeLookupError as TypedAttributeLookupError +from ._core._exceptions import WouldBlock as WouldBlock +from ._core._fileio import AsyncFile as AsyncFile +from ._core._fileio import Path as Path +from ._core._fileio import open_file as open_file +from ._core._fileio import wrap_file as wrap_file +from ._core._resources import aclose_forcefully as aclose_forcefully +from ._core._signals import open_signal_receiver as open_signal_receiver +from ._core._sockets import connect_tcp as connect_tcp +from ._core._sockets import connect_unix as connect_unix +from ._core._sockets import create_connected_udp_socket as create_connected_udp_socket +from ._core._sockets import ( + create_connected_unix_datagram_socket as create_connected_unix_datagram_socket, +) +from ._core._sockets import create_tcp_listener as create_tcp_listener +from ._core._sockets import create_udp_socket as create_udp_socket +from ._core._sockets import create_unix_datagram_socket as create_unix_datagram_socket +from ._core._sockets import create_unix_listener as create_unix_listener +from ._core._sockets import getaddrinfo as getaddrinfo +from ._core._sockets import getnameinfo as getnameinfo +from ._core._sockets import wait_socket_readable as wait_socket_readable +from ._core._sockets import wait_socket_writable as wait_socket_writable +from ._core._streams import create_memory_object_stream as create_memory_object_stream +from ._core._subprocesses import open_process as open_process +from ._core._subprocesses import run_process as run_process +from ._core._synchronization import CapacityLimiter as CapacityLimiter +from ._core._synchronization import ( + CapacityLimiterStatistics as CapacityLimiterStatistics, +) +from ._core._synchronization import Condition as Condition +from ._core._synchronization import ConditionStatistics as ConditionStatistics +from ._core._synchronization import Event as Event +from ._core._synchronization import EventStatistics as EventStatistics +from ._core._synchronization import Lock as Lock +from ._core._synchronization import LockStatistics as LockStatistics +from ._core._synchronization import ResourceGuard as ResourceGuard +from ._core._synchronization import Semaphore as Semaphore +from ._core._synchronization import SemaphoreStatistics as SemaphoreStatistics +from ._core._tasks import TASK_STATUS_IGNORED as TASK_STATUS_IGNORED +from ._core._tasks import CancelScope as CancelScope +from ._core._tasks import create_task_group as create_task_group +from ._core._tasks import current_effective_deadline as current_effective_deadline +from ._core._tasks import fail_after as fail_after +from ._core._tasks import move_on_after as move_on_after +from ._core._testing import TaskInfo as TaskInfo +from ._core._testing import get_current_task as get_current_task +from ._core._testing import get_running_tasks as get_running_tasks +from ._core._testing import wait_all_tasks_blocked as wait_all_tasks_blocked +from ._core._typedattr import TypedAttributeProvider as TypedAttributeProvider +from ._core._typedattr import TypedAttributeSet as TypedAttributeSet +from ._core._typedattr import typed_attribute as typed_attribute + +# Re-export imports so they look like they live directly in this package +for __value in list(locals().values()): + if getattr(__value, "__module__", "").startswith("anyio."): + __value.__module__ = __name__ + +del __value diff --git a/venv/lib/python3.11/site-packages/anyio/_backends/__init__.py b/venv/lib/python3.11/site-packages/anyio/_backends/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.11/site-packages/anyio/_backends/_asyncio.py b/venv/lib/python3.11/site-packages/anyio/_backends/_asyncio.py new file mode 100644 index 00000000..0a69e7ac --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/_backends/_asyncio.py @@ -0,0 +1,2771 @@ +from __future__ import annotations + +import array +import asyncio +import concurrent.futures +import math +import os +import socket +import sys +import threading +import weakref +from asyncio import ( + AbstractEventLoop, + CancelledError, + all_tasks, + create_task, + current_task, + get_running_loop, + sleep, +) +from asyncio.base_events import _run_until_complete_cb # type: ignore[attr-defined] +from collections import OrderedDict, deque +from collections.abc import ( + AsyncGenerator, + AsyncIterator, + Awaitable, + Callable, + Collection, + Coroutine, + Iterable, + Sequence, +) +from concurrent.futures import Future +from contextlib import AbstractContextManager, suppress +from contextvars import Context, copy_context +from dataclasses import dataclass +from functools import partial, wraps +from inspect import ( + CORO_RUNNING, + CORO_SUSPENDED, + getcoroutinestate, + iscoroutine, +) +from io import IOBase +from os import PathLike +from queue import Queue +from signal import Signals +from socket import AddressFamily, SocketKind +from threading import Thread +from types import TracebackType +from typing import ( + IO, + Any, + Optional, + TypeVar, + cast, +) +from weakref import WeakKeyDictionary + +import sniffio + +from .. import ( + CapacityLimiterStatistics, + EventStatistics, + LockStatistics, + TaskInfo, + abc, +) +from .._core._eventloop import claim_worker_thread, threadlocals +from .._core._exceptions import ( + BrokenResourceError, + BusyResourceError, + ClosedResourceError, + EndOfStream, + WouldBlock, + iterate_exceptions, +) +from .._core._sockets import convert_ipv6_sockaddr +from .._core._streams import create_memory_object_stream +from .._core._synchronization import ( + CapacityLimiter as BaseCapacityLimiter, +) +from .._core._synchronization import Event as BaseEvent +from .._core._synchronization import Lock as BaseLock +from .._core._synchronization import ( + ResourceGuard, + SemaphoreStatistics, +) +from .._core._synchronization import Semaphore as BaseSemaphore +from .._core._tasks import CancelScope as BaseCancelScope +from ..abc import ( + AsyncBackend, + IPSockAddrType, + SocketListener, + UDPPacketType, + UNIXDatagramPacketType, +) +from ..abc._eventloop import StrOrBytesPath +from ..lowlevel import RunVar +from ..streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream + +if sys.version_info >= (3, 10): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + +if sys.version_info >= (3, 11): + from asyncio import Runner + from typing import TypeVarTuple, Unpack +else: + import contextvars + import enum + import signal + from asyncio import coroutines, events, exceptions, tasks + + from exceptiongroup import BaseExceptionGroup + from typing_extensions import TypeVarTuple, Unpack + + class _State(enum.Enum): + CREATED = "created" + INITIALIZED = "initialized" + CLOSED = "closed" + + class Runner: + # Copied from CPython 3.11 + def __init__( + self, + *, + debug: bool | None = None, + loop_factory: Callable[[], AbstractEventLoop] | None = None, + ): + self._state = _State.CREATED + self._debug = debug + self._loop_factory = loop_factory + self._loop: AbstractEventLoop | None = None + self._context = None + self._interrupt_count = 0 + self._set_event_loop = False + + def __enter__(self) -> Runner: + self._lazy_init() + return self + + def __exit__( + self, + exc_type: type[BaseException], + exc_val: BaseException, + exc_tb: TracebackType, + ) -> None: + self.close() + + def close(self) -> None: + """Shutdown and close event loop.""" + if self._state is not _State.INITIALIZED: + return + try: + loop = self._loop + _cancel_all_tasks(loop) + loop.run_until_complete(loop.shutdown_asyncgens()) + if hasattr(loop, "shutdown_default_executor"): + loop.run_until_complete(loop.shutdown_default_executor()) + else: + loop.run_until_complete(_shutdown_default_executor(loop)) + finally: + if self._set_event_loop: + events.set_event_loop(None) + loop.close() + self._loop = None + self._state = _State.CLOSED + + def get_loop(self) -> AbstractEventLoop: + """Return embedded event loop.""" + self._lazy_init() + return self._loop + + def run(self, coro: Coroutine[T_Retval], *, context=None) -> T_Retval: + """Run a coroutine inside the embedded event loop.""" + if not coroutines.iscoroutine(coro): + raise ValueError(f"a coroutine was expected, got {coro!r}") + + if events._get_running_loop() is not None: + # fail fast with short traceback + raise RuntimeError( + "Runner.run() cannot be called from a running event loop" + ) + + self._lazy_init() + + if context is None: + context = self._context + task = context.run(self._loop.create_task, coro) + + if ( + threading.current_thread() is threading.main_thread() + and signal.getsignal(signal.SIGINT) is signal.default_int_handler + ): + sigint_handler = partial(self._on_sigint, main_task=task) + try: + signal.signal(signal.SIGINT, sigint_handler) + except ValueError: + # `signal.signal` may throw if `threading.main_thread` does + # not support signals (e.g. embedded interpreter with signals + # not registered - see gh-91880) + sigint_handler = None + else: + sigint_handler = None + + self._interrupt_count = 0 + try: + return self._loop.run_until_complete(task) + except exceptions.CancelledError: + if self._interrupt_count > 0: + uncancel = getattr(task, "uncancel", None) + if uncancel is not None and uncancel() == 0: + raise KeyboardInterrupt() + raise # CancelledError + finally: + if ( + sigint_handler is not None + and signal.getsignal(signal.SIGINT) is sigint_handler + ): + signal.signal(signal.SIGINT, signal.default_int_handler) + + def _lazy_init(self) -> None: + if self._state is _State.CLOSED: + raise RuntimeError("Runner is closed") + if self._state is _State.INITIALIZED: + return + if self._loop_factory is None: + self._loop = events.new_event_loop() + if not self._set_event_loop: + # Call set_event_loop only once to avoid calling + # attach_loop multiple times on child watchers + events.set_event_loop(self._loop) + self._set_event_loop = True + else: + self._loop = self._loop_factory() + if self._debug is not None: + self._loop.set_debug(self._debug) + self._context = contextvars.copy_context() + self._state = _State.INITIALIZED + + def _on_sigint(self, signum, frame, main_task: asyncio.Task) -> None: + self._interrupt_count += 1 + if self._interrupt_count == 1 and not main_task.done(): + main_task.cancel() + # wakeup loop if it is blocked by select() with long timeout + self._loop.call_soon_threadsafe(lambda: None) + return + raise KeyboardInterrupt() + + def _cancel_all_tasks(loop: AbstractEventLoop) -> None: + to_cancel = tasks.all_tasks(loop) + if not to_cancel: + return + + for task in to_cancel: + task.cancel() + + loop.run_until_complete(tasks.gather(*to_cancel, return_exceptions=True)) + + for task in to_cancel: + if task.cancelled(): + continue + if task.exception() is not None: + loop.call_exception_handler( + { + "message": "unhandled exception during asyncio.run() shutdown", + "exception": task.exception(), + "task": task, + } + ) + + async def _shutdown_default_executor(loop: AbstractEventLoop) -> None: + """Schedule the shutdown of the default executor.""" + + def _do_shutdown(future: asyncio.futures.Future) -> None: + try: + loop._default_executor.shutdown(wait=True) # type: ignore[attr-defined] + loop.call_soon_threadsafe(future.set_result, None) + except Exception as ex: + loop.call_soon_threadsafe(future.set_exception, ex) + + loop._executor_shutdown_called = True + if loop._default_executor is None: + return + future = loop.create_future() + thread = threading.Thread(target=_do_shutdown, args=(future,)) + thread.start() + try: + await future + finally: + thread.join() + + +T_Retval = TypeVar("T_Retval") +T_contra = TypeVar("T_contra", contravariant=True) +PosArgsT = TypeVarTuple("PosArgsT") +P = ParamSpec("P") + +_root_task: RunVar[asyncio.Task | None] = RunVar("_root_task") + + +def find_root_task() -> asyncio.Task: + root_task = _root_task.get(None) + if root_task is not None and not root_task.done(): + return root_task + + # Look for a task that has been started via run_until_complete() + for task in all_tasks(): + if task._callbacks and not task.done(): + callbacks = [cb for cb, context in task._callbacks] + for cb in callbacks: + if ( + cb is _run_until_complete_cb + or getattr(cb, "__module__", None) == "uvloop.loop" + ): + _root_task.set(task) + return task + + # Look up the topmost task in the AnyIO task tree, if possible + task = cast(asyncio.Task, current_task()) + state = _task_states.get(task) + if state: + cancel_scope = state.cancel_scope + while cancel_scope and cancel_scope._parent_scope is not None: + cancel_scope = cancel_scope._parent_scope + + if cancel_scope is not None: + return cast(asyncio.Task, cancel_scope._host_task) + + return task + + +def get_callable_name(func: Callable) -> str: + module = getattr(func, "__module__", None) + qualname = getattr(func, "__qualname__", None) + return ".".join([x for x in (module, qualname) if x]) + + +# +# Event loop +# + +_run_vars: WeakKeyDictionary[asyncio.AbstractEventLoop, Any] = WeakKeyDictionary() + + +def _task_started(task: asyncio.Task) -> bool: + """Return ``True`` if the task has been started and has not finished.""" + try: + return getcoroutinestate(task.get_coro()) in (CORO_RUNNING, CORO_SUSPENDED) + except AttributeError: + # task coro is async_genenerator_asend https://bugs.python.org/issue37771 + raise Exception(f"Cannot determine if task {task} has started or not") from None + + +# +# Timeouts and cancellation +# + + +def is_anyio_cancellation(exc: CancelledError) -> bool: + return ( + bool(exc.args) + and isinstance(exc.args[0], str) + and exc.args[0].startswith("Cancelled by cancel scope ") + ) + + +class CancelScope(BaseCancelScope): + def __new__( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + return object.__new__(cls) + + def __init__(self, deadline: float = math.inf, shield: bool = False): + self._deadline = deadline + self._shield = shield + self._parent_scope: CancelScope | None = None + self._child_scopes: set[CancelScope] = set() + self._cancel_called = False + self._cancelled_caught = False + self._active = False + self._timeout_handle: asyncio.TimerHandle | None = None + self._cancel_handle: asyncio.Handle | None = None + self._tasks: set[asyncio.Task] = set() + self._host_task: asyncio.Task | None = None + self._cancel_calls: int = 0 + self._cancelling: int | None = None + + def __enter__(self) -> CancelScope: + if self._active: + raise RuntimeError( + "Each CancelScope may only be used for a single 'with' block" + ) + + self._host_task = host_task = cast(asyncio.Task, current_task()) + self._tasks.add(host_task) + try: + task_state = _task_states[host_task] + except KeyError: + task_state = TaskState(None, self) + _task_states[host_task] = task_state + else: + self._parent_scope = task_state.cancel_scope + task_state.cancel_scope = self + if self._parent_scope is not None: + self._parent_scope._child_scopes.add(self) + self._parent_scope._tasks.remove(host_task) + + self._timeout() + self._active = True + if sys.version_info >= (3, 11): + self._cancelling = self._host_task.cancelling() + + # Start cancelling the host task if the scope was cancelled before entering + if self._cancel_called: + self._deliver_cancellation(self) + + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + del exc_tb + + if not self._active: + raise RuntimeError("This cancel scope is not active") + if current_task() is not self._host_task: + raise RuntimeError( + "Attempted to exit cancel scope in a different task than it was " + "entered in" + ) + + assert self._host_task is not None + host_task_state = _task_states.get(self._host_task) + if host_task_state is None or host_task_state.cancel_scope is not self: + raise RuntimeError( + "Attempted to exit a cancel scope that isn't the current tasks's " + "current cancel scope" + ) + + try: + self._active = False + if self._timeout_handle: + self._timeout_handle.cancel() + self._timeout_handle = None + + self._tasks.remove(self._host_task) + if self._parent_scope is not None: + self._parent_scope._child_scopes.remove(self) + self._parent_scope._tasks.add(self._host_task) + + host_task_state.cancel_scope = self._parent_scope + + # Undo all cancellations done by this scope + if self._cancelling is not None: + while self._cancel_calls: + self._cancel_calls -= 1 + if self._host_task.uncancel() <= self._cancelling: + break + + # We only swallow the exception iff it was an AnyIO CancelledError, either + # directly as exc_val or inside an exception group and there are no cancelled + # parent cancel scopes visible to us here + not_swallowed_exceptions = 0 + swallow_exception = False + if exc_val is not None: + for exc in iterate_exceptions(exc_val): + if self._cancel_called and isinstance(exc, CancelledError): + if not (swallow_exception := self._uncancel(exc)): + not_swallowed_exceptions += 1 + else: + not_swallowed_exceptions += 1 + + # Restart the cancellation effort in the closest visible, cancelled parent + # scope if necessary + self._restart_cancellation_in_parent() + return swallow_exception and not not_swallowed_exceptions + finally: + self._host_task = None + del exc_val + + @property + def _effectively_cancelled(self) -> bool: + cancel_scope: CancelScope | None = self + while cancel_scope is not None: + if cancel_scope._cancel_called: + return True + + if cancel_scope.shield: + return False + + cancel_scope = cancel_scope._parent_scope + + return False + + @property + def _parent_cancellation_is_visible_to_us(self) -> bool: + return ( + self._parent_scope is not None + and not self.shield + and self._parent_scope._effectively_cancelled + ) + + def _uncancel(self, cancelled_exc: CancelledError) -> bool: + if self._host_task is None: + self._cancel_calls = 0 + return True + + while True: + if is_anyio_cancellation(cancelled_exc): + # Only swallow the cancellation exception if it's an AnyIO cancel + # exception and there are no other cancel scopes down the line pending + # cancellation + self._cancelled_caught = ( + self._effectively_cancelled + and not self._parent_cancellation_is_visible_to_us + ) + return self._cancelled_caught + + # Sometimes third party frameworks catch a CancelledError and raise a new + # one, so as a workaround we have to look at the previous ones in + # __context__ too for a matching cancel message + if isinstance(cancelled_exc.__context__, CancelledError): + cancelled_exc = cancelled_exc.__context__ + continue + + return False + + def _timeout(self) -> None: + if self._deadline != math.inf: + loop = get_running_loop() + if loop.time() >= self._deadline: + self.cancel() + else: + self._timeout_handle = loop.call_at(self._deadline, self._timeout) + + def _deliver_cancellation(self, origin: CancelScope) -> bool: + """ + Deliver cancellation to directly contained tasks and nested cancel scopes. + + Schedule another run at the end if we still have tasks eligible for + cancellation. + + :param origin: the cancel scope that originated the cancellation + :return: ``True`` if the delivery needs to be retried on the next cycle + + """ + should_retry = False + current = current_task() + for task in self._tasks: + should_retry = True + if task._must_cancel: # type: ignore[attr-defined] + continue + + # The task is eligible for cancellation if it has started + if task is not current and (task is self._host_task or _task_started(task)): + waiter = task._fut_waiter # type: ignore[attr-defined] + if not isinstance(waiter, asyncio.Future) or not waiter.done(): + task.cancel(f"Cancelled by cancel scope {id(origin):x}") + if task is origin._host_task: + origin._cancel_calls += 1 + + # Deliver cancellation to child scopes that aren't shielded or running their own + # cancellation callbacks + for scope in self._child_scopes: + if not scope._shield and not scope.cancel_called: + should_retry = scope._deliver_cancellation(origin) or should_retry + + # Schedule another callback if there are still tasks left + if origin is self: + if should_retry: + self._cancel_handle = get_running_loop().call_soon( + self._deliver_cancellation, origin + ) + else: + self._cancel_handle = None + + return should_retry + + def _restart_cancellation_in_parent(self) -> None: + """ + Restart the cancellation effort in the closest directly cancelled parent scope. + + """ + scope = self._parent_scope + while scope is not None: + if scope._cancel_called: + if scope._cancel_handle is None: + scope._deliver_cancellation(scope) + + break + + # No point in looking beyond any shielded scope + if scope._shield: + break + + scope = scope._parent_scope + + def cancel(self) -> None: + if not self._cancel_called: + if self._timeout_handle: + self._timeout_handle.cancel() + self._timeout_handle = None + + self._cancel_called = True + if self._host_task is not None: + self._deliver_cancellation(self) + + @property + def deadline(self) -> float: + return self._deadline + + @deadline.setter + def deadline(self, value: float) -> None: + self._deadline = float(value) + if self._timeout_handle is not None: + self._timeout_handle.cancel() + self._timeout_handle = None + + if self._active and not self._cancel_called: + self._timeout() + + @property + def cancel_called(self) -> bool: + return self._cancel_called + + @property + def cancelled_caught(self) -> bool: + return self._cancelled_caught + + @property + def shield(self) -> bool: + return self._shield + + @shield.setter + def shield(self, value: bool) -> None: + if self._shield != value: + self._shield = value + if not value: + self._restart_cancellation_in_parent() + + +# +# Task states +# + + +class TaskState: + """ + Encapsulates auxiliary task information that cannot be added to the Task instance + itself because there are no guarantees about its implementation. + """ + + __slots__ = "parent_id", "cancel_scope", "__weakref__" + + def __init__(self, parent_id: int | None, cancel_scope: CancelScope | None): + self.parent_id = parent_id + self.cancel_scope = cancel_scope + + +_task_states: WeakKeyDictionary[asyncio.Task, TaskState] = WeakKeyDictionary() + + +# +# Task groups +# + + +class _AsyncioTaskStatus(abc.TaskStatus): + def __init__(self, future: asyncio.Future, parent_id: int): + self._future = future + self._parent_id = parent_id + + def started(self, value: T_contra | None = None) -> None: + try: + self._future.set_result(value) + except asyncio.InvalidStateError: + if not self._future.cancelled(): + raise RuntimeError( + "called 'started' twice on the same task status" + ) from None + + task = cast(asyncio.Task, current_task()) + _task_states[task].parent_id = self._parent_id + + +async def _wait(tasks: Iterable[asyncio.Task[object]]) -> None: + tasks = set(tasks) + waiter = get_running_loop().create_future() + + def on_completion(task: asyncio.Task[object]) -> None: + tasks.discard(task) + if not tasks and not waiter.done(): + waiter.set_result(None) + + for task in tasks: + task.add_done_callback(on_completion) + del task + + try: + await waiter + finally: + while tasks: + tasks.pop().remove_done_callback(on_completion) + + +class TaskGroup(abc.TaskGroup): + def __init__(self) -> None: + self.cancel_scope: CancelScope = CancelScope() + self._active = False + self._exceptions: list[BaseException] = [] + self._tasks: set[asyncio.Task] = set() + + async def __aenter__(self) -> TaskGroup: + self.cancel_scope.__enter__() + self._active = True + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + try: + if exc_val is not None: + self.cancel_scope.cancel() + if not isinstance(exc_val, CancelledError): + self._exceptions.append(exc_val) + + try: + if self._tasks: + with CancelScope() as wait_scope: + while self._tasks: + try: + await _wait(self._tasks) + except CancelledError as exc: + # Shield the scope against further cancellation attempts, + # as they're not productive (#695) + wait_scope.shield = True + self.cancel_scope.cancel() + + # Set exc_val from the cancellation exception if it was + # previously unset. However, we should not replace a native + # cancellation exception with one raise by a cancel scope. + if exc_val is None or ( + isinstance(exc_val, CancelledError) + and not is_anyio_cancellation(exc) + ): + exc_val = exc + else: + # If there are no child tasks to wait on, run at least one checkpoint + # anyway + await AsyncIOBackend.cancel_shielded_checkpoint() + + self._active = False + if self._exceptions: + raise BaseExceptionGroup( + "unhandled errors in a TaskGroup", self._exceptions + ) + elif exc_val: + raise exc_val + except BaseException as exc: + if self.cancel_scope.__exit__(type(exc), exc, exc.__traceback__): + return True + + raise + + return self.cancel_scope.__exit__(exc_type, exc_val, exc_tb) + finally: + del exc_val, exc_tb, self._exceptions + + def _spawn( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], + args: tuple[Unpack[PosArgsT]], + name: object, + task_status_future: asyncio.Future | None = None, + ) -> asyncio.Task: + def task_done(_task: asyncio.Task) -> None: + task_state = _task_states[_task] + assert task_state.cancel_scope is not None + assert _task in task_state.cancel_scope._tasks + task_state.cancel_scope._tasks.remove(_task) + self._tasks.remove(task) + del _task_states[_task] + + try: + exc = _task.exception() + except CancelledError as e: + while isinstance(e.__context__, CancelledError): + e = e.__context__ + + exc = e + + if exc is not None: + # The future can only be in the cancelled state if the host task was + # cancelled, so return immediately instead of adding one more + # CancelledError to the exceptions list + if task_status_future is not None and task_status_future.cancelled(): + return + + if task_status_future is None or task_status_future.done(): + if not isinstance(exc, CancelledError): + self._exceptions.append(exc) + + if not self.cancel_scope._effectively_cancelled: + self.cancel_scope.cancel() + else: + task_status_future.set_exception(exc) + elif task_status_future is not None and not task_status_future.done(): + task_status_future.set_exception( + RuntimeError("Child exited without calling task_status.started()") + ) + + if not self._active: + raise RuntimeError( + "This task group is not active; no new tasks can be started." + ) + + kwargs = {} + if task_status_future: + parent_id = id(current_task()) + kwargs["task_status"] = _AsyncioTaskStatus( + task_status_future, id(self.cancel_scope._host_task) + ) + else: + parent_id = id(self.cancel_scope._host_task) + + coro = func(*args, **kwargs) + if not iscoroutine(coro): + prefix = f"{func.__module__}." if hasattr(func, "__module__") else "" + raise TypeError( + f"Expected {prefix}{func.__qualname__}() to return a coroutine, but " + f"the return value ({coro!r}) is not a coroutine object" + ) + + name = get_callable_name(func) if name is None else str(name) + task = create_task(coro, name=name) + task.add_done_callback(task_done) + + # Make the spawned task inherit the task group's cancel scope + _task_states[task] = TaskState( + parent_id=parent_id, cancel_scope=self.cancel_scope + ) + self.cancel_scope._tasks.add(task) + self._tasks.add(task) + return task + + def start_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], + *args: Unpack[PosArgsT], + name: object = None, + ) -> None: + self._spawn(func, args, name) + + async def start( + self, func: Callable[..., Awaitable[Any]], *args: object, name: object = None + ) -> Any: + future: asyncio.Future = asyncio.Future() + task = self._spawn(func, args, name, future) + + # If the task raises an exception after sending a start value without a switch + # point between, the task group is cancelled and this method never proceeds to + # process the completed future. That's why we have to have a shielded cancel + # scope here. + try: + return await future + except CancelledError: + # Cancel the task and wait for it to exit before returning + task.cancel() + with CancelScope(shield=True), suppress(CancelledError): + await task + + raise + + +# +# Threads +# + +_Retval_Queue_Type = tuple[Optional[T_Retval], Optional[BaseException]] + + +class WorkerThread(Thread): + MAX_IDLE_TIME = 10 # seconds + + def __init__( + self, + root_task: asyncio.Task, + workers: set[WorkerThread], + idle_workers: deque[WorkerThread], + ): + super().__init__(name="AnyIO worker thread") + self.root_task = root_task + self.workers = workers + self.idle_workers = idle_workers + self.loop = root_task._loop + self.queue: Queue[ + tuple[Context, Callable, tuple, asyncio.Future, CancelScope] | None + ] = Queue(2) + self.idle_since = AsyncIOBackend.current_time() + self.stopping = False + + def _report_result( + self, future: asyncio.Future, result: Any, exc: BaseException | None + ) -> None: + self.idle_since = AsyncIOBackend.current_time() + if not self.stopping: + self.idle_workers.append(self) + + if not future.cancelled(): + if exc is not None: + if isinstance(exc, StopIteration): + new_exc = RuntimeError("coroutine raised StopIteration") + new_exc.__cause__ = exc + exc = new_exc + + future.set_exception(exc) + else: + future.set_result(result) + + def run(self) -> None: + with claim_worker_thread(AsyncIOBackend, self.loop): + while True: + item = self.queue.get() + if item is None: + # Shutdown command received + return + + context, func, args, future, cancel_scope = item + if not future.cancelled(): + result = None + exception: BaseException | None = None + threadlocals.current_cancel_scope = cancel_scope + try: + result = context.run(func, *args) + except BaseException as exc: + exception = exc + finally: + del threadlocals.current_cancel_scope + + if not self.loop.is_closed(): + self.loop.call_soon_threadsafe( + self._report_result, future, result, exception + ) + + self.queue.task_done() + + def stop(self, f: asyncio.Task | None = None) -> None: + self.stopping = True + self.queue.put_nowait(None) + self.workers.discard(self) + try: + self.idle_workers.remove(self) + except ValueError: + pass + + +_threadpool_idle_workers: RunVar[deque[WorkerThread]] = RunVar( + "_threadpool_idle_workers" +) +_threadpool_workers: RunVar[set[WorkerThread]] = RunVar("_threadpool_workers") + + +class BlockingPortal(abc.BlockingPortal): + def __new__(cls) -> BlockingPortal: + return object.__new__(cls) + + def __init__(self) -> None: + super().__init__() + self._loop = get_running_loop() + + def _spawn_task_from_thread( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + name: object, + future: Future[T_Retval], + ) -> None: + AsyncIOBackend.run_sync_from_thread( + partial(self._task_group.start_soon, name=name), + (self._call_func, func, args, kwargs, future), + self._loop, + ) + + +# +# Subprocesses +# + + +@dataclass(eq=False) +class StreamReaderWrapper(abc.ByteReceiveStream): + _stream: asyncio.StreamReader + + async def receive(self, max_bytes: int = 65536) -> bytes: + data = await self._stream.read(max_bytes) + if data: + return data + else: + raise EndOfStream + + async def aclose(self) -> None: + self._stream.set_exception(ClosedResourceError()) + await AsyncIOBackend.checkpoint() + + +@dataclass(eq=False) +class StreamWriterWrapper(abc.ByteSendStream): + _stream: asyncio.StreamWriter + + async def send(self, item: bytes) -> None: + self._stream.write(item) + await self._stream.drain() + + async def aclose(self) -> None: + self._stream.close() + await AsyncIOBackend.checkpoint() + + +@dataclass(eq=False) +class Process(abc.Process): + _process: asyncio.subprocess.Process + _stdin: StreamWriterWrapper | None + _stdout: StreamReaderWrapper | None + _stderr: StreamReaderWrapper | None + + async def aclose(self) -> None: + with CancelScope(shield=True) as scope: + if self._stdin: + await self._stdin.aclose() + if self._stdout: + await self._stdout.aclose() + if self._stderr: + await self._stderr.aclose() + + scope.shield = False + try: + await self.wait() + except BaseException: + scope.shield = True + self.kill() + await self.wait() + raise + + async def wait(self) -> int: + return await self._process.wait() + + def terminate(self) -> None: + self._process.terminate() + + def kill(self) -> None: + self._process.kill() + + def send_signal(self, signal: int) -> None: + self._process.send_signal(signal) + + @property + def pid(self) -> int: + return self._process.pid + + @property + def returncode(self) -> int | None: + return self._process.returncode + + @property + def stdin(self) -> abc.ByteSendStream | None: + return self._stdin + + @property + def stdout(self) -> abc.ByteReceiveStream | None: + return self._stdout + + @property + def stderr(self) -> abc.ByteReceiveStream | None: + return self._stderr + + +def _forcibly_shutdown_process_pool_on_exit( + workers: set[Process], _task: object +) -> None: + """ + Forcibly shuts down worker processes belonging to this event loop.""" + child_watcher: asyncio.AbstractChildWatcher | None = None + if sys.version_info < (3, 12): + try: + child_watcher = asyncio.get_event_loop_policy().get_child_watcher() + except NotImplementedError: + pass + + # Close as much as possible (w/o async/await) to avoid warnings + for process in workers: + if process.returncode is None: + continue + + process._stdin._stream._transport.close() # type: ignore[union-attr] + process._stdout._stream._transport.close() # type: ignore[union-attr] + process._stderr._stream._transport.close() # type: ignore[union-attr] + process.kill() + if child_watcher: + child_watcher.remove_child_handler(process.pid) + + +async def _shutdown_process_pool_on_exit(workers: set[abc.Process]) -> None: + """ + Shuts down worker processes belonging to this event loop. + + NOTE: this only works when the event loop was started using asyncio.run() or + anyio.run(). + + """ + process: abc.Process + try: + await sleep(math.inf) + except asyncio.CancelledError: + for process in workers: + if process.returncode is None: + process.kill() + + for process in workers: + await process.aclose() + + +# +# Sockets and networking +# + + +class StreamProtocol(asyncio.Protocol): + read_queue: deque[bytes] + read_event: asyncio.Event + write_event: asyncio.Event + exception: Exception | None = None + is_at_eof: bool = False + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + self.read_queue = deque() + self.read_event = asyncio.Event() + self.write_event = asyncio.Event() + self.write_event.set() + cast(asyncio.Transport, transport).set_write_buffer_limits(0) + + def connection_lost(self, exc: Exception | None) -> None: + if exc: + self.exception = BrokenResourceError() + self.exception.__cause__ = exc + + self.read_event.set() + self.write_event.set() + + def data_received(self, data: bytes) -> None: + # ProactorEventloop sometimes sends bytearray instead of bytes + self.read_queue.append(bytes(data)) + self.read_event.set() + + def eof_received(self) -> bool | None: + self.is_at_eof = True + self.read_event.set() + return True + + def pause_writing(self) -> None: + self.write_event = asyncio.Event() + + def resume_writing(self) -> None: + self.write_event.set() + + +class DatagramProtocol(asyncio.DatagramProtocol): + read_queue: deque[tuple[bytes, IPSockAddrType]] + read_event: asyncio.Event + write_event: asyncio.Event + exception: Exception | None = None + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + self.read_queue = deque(maxlen=100) # arbitrary value + self.read_event = asyncio.Event() + self.write_event = asyncio.Event() + self.write_event.set() + + def connection_lost(self, exc: Exception | None) -> None: + self.read_event.set() + self.write_event.set() + + def datagram_received(self, data: bytes, addr: IPSockAddrType) -> None: + addr = convert_ipv6_sockaddr(addr) + self.read_queue.append((data, addr)) + self.read_event.set() + + def error_received(self, exc: Exception) -> None: + self.exception = exc + + def pause_writing(self) -> None: + self.write_event.clear() + + def resume_writing(self) -> None: + self.write_event.set() + + +class SocketStream(abc.SocketStream): + def __init__(self, transport: asyncio.Transport, protocol: StreamProtocol): + self._transport = transport + self._protocol = protocol + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + self._closed = False + + @property + def _raw_socket(self) -> socket.socket: + return self._transport.get_extra_info("socket") + + async def receive(self, max_bytes: int = 65536) -> bytes: + with self._receive_guard: + if ( + not self._protocol.read_event.is_set() + and not self._transport.is_closing() + and not self._protocol.is_at_eof + ): + self._transport.resume_reading() + await self._protocol.read_event.wait() + self._transport.pause_reading() + else: + await AsyncIOBackend.checkpoint() + + try: + chunk = self._protocol.read_queue.popleft() + except IndexError: + if self._closed: + raise ClosedResourceError from None + elif self._protocol.exception: + raise self._protocol.exception from None + else: + raise EndOfStream from None + + if len(chunk) > max_bytes: + # Split the oversized chunk + chunk, leftover = chunk[:max_bytes], chunk[max_bytes:] + self._protocol.read_queue.appendleft(leftover) + + # If the read queue is empty, clear the flag so that the next call will + # block until data is available + if not self._protocol.read_queue: + self._protocol.read_event.clear() + + return chunk + + async def send(self, item: bytes) -> None: + with self._send_guard: + await AsyncIOBackend.checkpoint() + + if self._closed: + raise ClosedResourceError + elif self._protocol.exception is not None: + raise self._protocol.exception + + try: + self._transport.write(item) + except RuntimeError as exc: + if self._transport.is_closing(): + raise BrokenResourceError from exc + else: + raise + + await self._protocol.write_event.wait() + + async def send_eof(self) -> None: + try: + self._transport.write_eof() + except OSError: + pass + + async def aclose(self) -> None: + if not self._transport.is_closing(): + self._closed = True + try: + self._transport.write_eof() + except OSError: + pass + + self._transport.close() + await sleep(0) + self._transport.abort() + + +class _RawSocketMixin: + _receive_future: asyncio.Future | None = None + _send_future: asyncio.Future | None = None + _closing = False + + def __init__(self, raw_socket: socket.socket): + self.__raw_socket = raw_socket + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + @property + def _raw_socket(self) -> socket.socket: + return self.__raw_socket + + def _wait_until_readable(self, loop: asyncio.AbstractEventLoop) -> asyncio.Future: + def callback(f: object) -> None: + del self._receive_future + loop.remove_reader(self.__raw_socket) + + f = self._receive_future = asyncio.Future() + loop.add_reader(self.__raw_socket, f.set_result, None) + f.add_done_callback(callback) + return f + + def _wait_until_writable(self, loop: asyncio.AbstractEventLoop) -> asyncio.Future: + def callback(f: object) -> None: + del self._send_future + loop.remove_writer(self.__raw_socket) + + f = self._send_future = asyncio.Future() + loop.add_writer(self.__raw_socket, f.set_result, None) + f.add_done_callback(callback) + return f + + async def aclose(self) -> None: + if not self._closing: + self._closing = True + if self.__raw_socket.fileno() != -1: + self.__raw_socket.close() + + if self._receive_future: + self._receive_future.set_result(None) + if self._send_future: + self._send_future.set_result(None) + + +class UNIXSocketStream(_RawSocketMixin, abc.UNIXSocketStream): + async def send_eof(self) -> None: + with self._send_guard: + self._raw_socket.shutdown(socket.SHUT_WR) + + async def receive(self, max_bytes: int = 65536) -> bytes: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + data = self._raw_socket.recv(max_bytes) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + if not data: + raise EndOfStream + + return data + + async def send(self, item: bytes) -> None: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._send_guard: + view = memoryview(item) + while view: + try: + bytes_sent = self._raw_socket.send(view) + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + view = view[bytes_sent:] + + async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: + if not isinstance(msglen, int) or msglen < 0: + raise ValueError("msglen must be a non-negative integer") + if not isinstance(maxfds, int) or maxfds < 1: + raise ValueError("maxfds must be a positive integer") + + loop = get_running_loop() + fds = array.array("i") + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + message, ancdata, flags, addr = self._raw_socket.recvmsg( + msglen, socket.CMSG_LEN(maxfds * fds.itemsize) + ) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + if not message and not ancdata: + raise EndOfStream + + break + + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS: + raise RuntimeError( + f"Received unexpected ancillary data; message = {message!r}, " + f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}" + ) + + fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) + + return message, list(fds) + + async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: + if not message: + raise ValueError("message must not be empty") + if not fds: + raise ValueError("fds must not be empty") + + loop = get_running_loop() + filenos: list[int] = [] + for fd in fds: + if isinstance(fd, int): + filenos.append(fd) + elif isinstance(fd, IOBase): + filenos.append(fd.fileno()) + + fdarray = array.array("i", filenos) + await AsyncIOBackend.checkpoint() + with self._send_guard: + while True: + try: + # The ignore can be removed after mypy picks up + # https://github.com/python/typeshed/pull/5545 + self._raw_socket.sendmsg( + [message], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, fdarray)] + ) + break + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + + +class TCPSocketListener(abc.SocketListener): + _accept_scope: CancelScope | None = None + _closed = False + + def __init__(self, raw_socket: socket.socket): + self.__raw_socket = raw_socket + self._loop = cast(asyncio.BaseEventLoop, get_running_loop()) + self._accept_guard = ResourceGuard("accepting connections from") + + @property + def _raw_socket(self) -> socket.socket: + return self.__raw_socket + + async def accept(self) -> abc.SocketStream: + if self._closed: + raise ClosedResourceError + + with self._accept_guard: + await AsyncIOBackend.checkpoint() + with CancelScope() as self._accept_scope: + try: + client_sock, _addr = await self._loop.sock_accept(self._raw_socket) + except asyncio.CancelledError: + # Workaround for https://bugs.python.org/issue41317 + try: + self._loop.remove_reader(self._raw_socket) + except (ValueError, NotImplementedError): + pass + + if self._closed: + raise ClosedResourceError from None + + raise + finally: + self._accept_scope = None + + client_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + transport, protocol = await self._loop.connect_accepted_socket( + StreamProtocol, client_sock + ) + return SocketStream(transport, protocol) + + async def aclose(self) -> None: + if self._closed: + return + + self._closed = True + if self._accept_scope: + # Workaround for https://bugs.python.org/issue41317 + try: + self._loop.remove_reader(self._raw_socket) + except (ValueError, NotImplementedError): + pass + + self._accept_scope.cancel() + await sleep(0) + + self._raw_socket.close() + + +class UNIXSocketListener(abc.SocketListener): + def __init__(self, raw_socket: socket.socket): + self.__raw_socket = raw_socket + self._loop = get_running_loop() + self._accept_guard = ResourceGuard("accepting connections from") + self._closed = False + + async def accept(self) -> abc.SocketStream: + await AsyncIOBackend.checkpoint() + with self._accept_guard: + while True: + try: + client_sock, _ = self.__raw_socket.accept() + client_sock.setblocking(False) + return UNIXSocketStream(client_sock) + except BlockingIOError: + f: asyncio.Future = asyncio.Future() + self._loop.add_reader(self.__raw_socket, f.set_result, None) + f.add_done_callback( + lambda _: self._loop.remove_reader(self.__raw_socket) + ) + await f + except OSError as exc: + if self._closed: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + + async def aclose(self) -> None: + self._closed = True + self.__raw_socket.close() + + @property + def _raw_socket(self) -> socket.socket: + return self.__raw_socket + + +class UDPSocket(abc.UDPSocket): + def __init__( + self, transport: asyncio.DatagramTransport, protocol: DatagramProtocol + ): + self._transport = transport + self._protocol = protocol + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + self._closed = False + + @property + def _raw_socket(self) -> socket.socket: + return self._transport.get_extra_info("socket") + + async def aclose(self) -> None: + if not self._transport.is_closing(): + self._closed = True + self._transport.close() + + async def receive(self) -> tuple[bytes, IPSockAddrType]: + with self._receive_guard: + await AsyncIOBackend.checkpoint() + + # If the buffer is empty, ask for more data + if not self._protocol.read_queue and not self._transport.is_closing(): + self._protocol.read_event.clear() + await self._protocol.read_event.wait() + + try: + return self._protocol.read_queue.popleft() + except IndexError: + if self._closed: + raise ClosedResourceError from None + else: + raise BrokenResourceError from None + + async def send(self, item: UDPPacketType) -> None: + with self._send_guard: + await AsyncIOBackend.checkpoint() + await self._protocol.write_event.wait() + if self._closed: + raise ClosedResourceError + elif self._transport.is_closing(): + raise BrokenResourceError + else: + self._transport.sendto(*item) + + +class ConnectedUDPSocket(abc.ConnectedUDPSocket): + def __init__( + self, transport: asyncio.DatagramTransport, protocol: DatagramProtocol + ): + self._transport = transport + self._protocol = protocol + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + self._closed = False + + @property + def _raw_socket(self) -> socket.socket: + return self._transport.get_extra_info("socket") + + async def aclose(self) -> None: + if not self._transport.is_closing(): + self._closed = True + self._transport.close() + + async def receive(self) -> bytes: + with self._receive_guard: + await AsyncIOBackend.checkpoint() + + # If the buffer is empty, ask for more data + if not self._protocol.read_queue and not self._transport.is_closing(): + self._protocol.read_event.clear() + await self._protocol.read_event.wait() + + try: + packet = self._protocol.read_queue.popleft() + except IndexError: + if self._closed: + raise ClosedResourceError from None + else: + raise BrokenResourceError from None + + return packet[0] + + async def send(self, item: bytes) -> None: + with self._send_guard: + await AsyncIOBackend.checkpoint() + await self._protocol.write_event.wait() + if self._closed: + raise ClosedResourceError + elif self._transport.is_closing(): + raise BrokenResourceError + else: + self._transport.sendto(item) + + +class UNIXDatagramSocket(_RawSocketMixin, abc.UNIXDatagramSocket): + async def receive(self) -> UNIXDatagramPacketType: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + data = self._raw_socket.recvfrom(65536) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return data + + async def send(self, item: UNIXDatagramPacketType) -> None: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._send_guard: + while True: + try: + self._raw_socket.sendto(*item) + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return + + +class ConnectedUNIXDatagramSocket(_RawSocketMixin, abc.ConnectedUNIXDatagramSocket): + async def receive(self) -> bytes: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + data = self._raw_socket.recv(65536) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return data + + async def send(self, item: bytes) -> None: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._send_guard: + while True: + try: + self._raw_socket.send(item) + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return + + +_read_events: RunVar[dict[Any, asyncio.Event]] = RunVar("read_events") +_write_events: RunVar[dict[Any, asyncio.Event]] = RunVar("write_events") + + +# +# Synchronization +# + + +class Event(BaseEvent): + def __new__(cls) -> Event: + return object.__new__(cls) + + def __init__(self) -> None: + self._event = asyncio.Event() + + def set(self) -> None: + self._event.set() + + def is_set(self) -> bool: + return self._event.is_set() + + async def wait(self) -> None: + if self.is_set(): + await AsyncIOBackend.checkpoint() + else: + await self._event.wait() + + def statistics(self) -> EventStatistics: + return EventStatistics(len(self._event._waiters)) + + +class Lock(BaseLock): + def __new__(cls, *, fast_acquire: bool = False) -> Lock: + return object.__new__(cls) + + def __init__(self, *, fast_acquire: bool = False) -> None: + self._fast_acquire = fast_acquire + self._owner_task: asyncio.Task | None = None + self._waiters: deque[tuple[asyncio.Task, asyncio.Future]] = deque() + + async def acquire(self) -> None: + task = cast(asyncio.Task, current_task()) + if self._owner_task is None and not self._waiters: + await AsyncIOBackend.checkpoint_if_cancelled() + self._owner_task = task + + # Unless on the "fast path", yield control of the event loop so that other + # tasks can run too + if not self._fast_acquire: + try: + await AsyncIOBackend.cancel_shielded_checkpoint() + except CancelledError: + self.release() + raise + + return + + if self._owner_task == task: + raise RuntimeError("Attempted to acquire an already held Lock") + + fut: asyncio.Future[None] = asyncio.Future() + item = task, fut + self._waiters.append(item) + try: + await fut + except CancelledError: + self._waiters.remove(item) + if self._owner_task is task: + self.release() + + raise + + self._waiters.remove(item) + + def acquire_nowait(self) -> None: + task = cast(asyncio.Task, current_task()) + if self._owner_task is None and not self._waiters: + self._owner_task = task + return + + if self._owner_task is task: + raise RuntimeError("Attempted to acquire an already held Lock") + + raise WouldBlock + + def locked(self) -> bool: + return self._owner_task is not None + + def release(self) -> None: + if self._owner_task != current_task(): + raise RuntimeError("The current task is not holding this lock") + + for task, fut in self._waiters: + if not fut.cancelled(): + self._owner_task = task + fut.set_result(None) + return + + self._owner_task = None + + def statistics(self) -> LockStatistics: + task_info = AsyncIOTaskInfo(self._owner_task) if self._owner_task else None + return LockStatistics(self.locked(), task_info, len(self._waiters)) + + +class Semaphore(BaseSemaphore): + def __new__( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> Semaphore: + return object.__new__(cls) + + def __init__( + self, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ): + super().__init__(initial_value, max_value=max_value) + self._value = initial_value + self._max_value = max_value + self._fast_acquire = fast_acquire + self._waiters: deque[asyncio.Future[None]] = deque() + + async def acquire(self) -> None: + if self._value > 0 and not self._waiters: + await AsyncIOBackend.checkpoint_if_cancelled() + self._value -= 1 + + # Unless on the "fast path", yield control of the event loop so that other + # tasks can run too + if not self._fast_acquire: + try: + await AsyncIOBackend.cancel_shielded_checkpoint() + except CancelledError: + self.release() + raise + + return + + fut: asyncio.Future[None] = asyncio.Future() + self._waiters.append(fut) + try: + await fut + except CancelledError: + try: + self._waiters.remove(fut) + except ValueError: + self.release() + + raise + + def acquire_nowait(self) -> None: + if self._value == 0: + raise WouldBlock + + self._value -= 1 + + def release(self) -> None: + if self._max_value is not None and self._value == self._max_value: + raise ValueError("semaphore released too many times") + + for fut in self._waiters: + if not fut.cancelled(): + fut.set_result(None) + self._waiters.remove(fut) + return + + self._value += 1 + + @property + def value(self) -> int: + return self._value + + @property + def max_value(self) -> int | None: + return self._max_value + + def statistics(self) -> SemaphoreStatistics: + return SemaphoreStatistics(len(self._waiters)) + + +class CapacityLimiter(BaseCapacityLimiter): + _total_tokens: float = 0 + + def __new__(cls, total_tokens: float) -> CapacityLimiter: + return object.__new__(cls) + + def __init__(self, total_tokens: float): + self._borrowers: set[Any] = set() + self._wait_queue: OrderedDict[Any, asyncio.Event] = OrderedDict() + self.total_tokens = total_tokens + + async def __aenter__(self) -> None: + await self.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + @property + def total_tokens(self) -> float: + return self._total_tokens + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + if not isinstance(value, int) and not math.isinf(value): + raise TypeError("total_tokens must be an int or math.inf") + if value < 1: + raise ValueError("total_tokens must be >= 1") + + waiters_to_notify = max(value - self._total_tokens, 0) + self._total_tokens = value + + # Notify waiting tasks that they have acquired the limiter + while self._wait_queue and waiters_to_notify: + event = self._wait_queue.popitem(last=False)[1] + event.set() + waiters_to_notify -= 1 + + @property + def borrowed_tokens(self) -> int: + return len(self._borrowers) + + @property + def available_tokens(self) -> float: + return self._total_tokens - len(self._borrowers) + + def acquire_nowait(self) -> None: + self.acquire_on_behalf_of_nowait(current_task()) + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + if borrower in self._borrowers: + raise RuntimeError( + "this borrower is already holding one of this CapacityLimiter's " + "tokens" + ) + + if self._wait_queue or len(self._borrowers) >= self._total_tokens: + raise WouldBlock + + self._borrowers.add(borrower) + + async def acquire(self) -> None: + return await self.acquire_on_behalf_of(current_task()) + + async def acquire_on_behalf_of(self, borrower: object) -> None: + await AsyncIOBackend.checkpoint_if_cancelled() + try: + self.acquire_on_behalf_of_nowait(borrower) + except WouldBlock: + event = asyncio.Event() + self._wait_queue[borrower] = event + try: + await event.wait() + except BaseException: + self._wait_queue.pop(borrower, None) + raise + + self._borrowers.add(borrower) + else: + try: + await AsyncIOBackend.cancel_shielded_checkpoint() + except BaseException: + self.release() + raise + + def release(self) -> None: + self.release_on_behalf_of(current_task()) + + def release_on_behalf_of(self, borrower: object) -> None: + try: + self._borrowers.remove(borrower) + except KeyError: + raise RuntimeError( + "this borrower isn't holding any of this CapacityLimiter's tokens" + ) from None + + # Notify the next task in line if this limiter has free capacity now + if self._wait_queue and len(self._borrowers) < self._total_tokens: + event = self._wait_queue.popitem(last=False)[1] + event.set() + + def statistics(self) -> CapacityLimiterStatistics: + return CapacityLimiterStatistics( + self.borrowed_tokens, + self.total_tokens, + tuple(self._borrowers), + len(self._wait_queue), + ) + + +_default_thread_limiter: RunVar[CapacityLimiter] = RunVar("_default_thread_limiter") + + +# +# Operating system signals +# + + +class _SignalReceiver: + def __init__(self, signals: tuple[Signals, ...]): + self._signals = signals + self._loop = get_running_loop() + self._signal_queue: deque[Signals] = deque() + self._future: asyncio.Future = asyncio.Future() + self._handled_signals: set[Signals] = set() + + def _deliver(self, signum: Signals) -> None: + self._signal_queue.append(signum) + if not self._future.done(): + self._future.set_result(None) + + def __enter__(self) -> _SignalReceiver: + for sig in set(self._signals): + self._loop.add_signal_handler(sig, self._deliver, sig) + self._handled_signals.add(sig) + + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + for sig in self._handled_signals: + self._loop.remove_signal_handler(sig) + return None + + def __aiter__(self) -> _SignalReceiver: + return self + + async def __anext__(self) -> Signals: + await AsyncIOBackend.checkpoint() + if not self._signal_queue: + self._future = asyncio.Future() + await self._future + + return self._signal_queue.popleft() + + +# +# Testing and debugging +# + + +class AsyncIOTaskInfo(TaskInfo): + def __init__(self, task: asyncio.Task): + task_state = _task_states.get(task) + if task_state is None: + parent_id = None + else: + parent_id = task_state.parent_id + + super().__init__(id(task), parent_id, task.get_name(), task.get_coro()) + self._task = weakref.ref(task) + + def has_pending_cancellation(self) -> bool: + if not (task := self._task()): + # If the task isn't around anymore, it won't have a pending cancellation + return False + + if sys.version_info >= (3, 11): + if task.cancelling(): + return True + elif ( + isinstance(task._fut_waiter, asyncio.Future) + and task._fut_waiter.cancelled() + ): + return True + + if task_state := _task_states.get(task): + if cancel_scope := task_state.cancel_scope: + return cancel_scope._effectively_cancelled + + return False + + +class TestRunner(abc.TestRunner): + _send_stream: MemoryObjectSendStream[tuple[Awaitable[Any], asyncio.Future[Any]]] + + def __init__( + self, + *, + debug: bool | None = None, + use_uvloop: bool = False, + loop_factory: Callable[[], AbstractEventLoop] | None = None, + ) -> None: + if use_uvloop and loop_factory is None: + import uvloop + + loop_factory = uvloop.new_event_loop + + self._runner = Runner(debug=debug, loop_factory=loop_factory) + self._exceptions: list[BaseException] = [] + self._runner_task: asyncio.Task | None = None + + def __enter__(self) -> TestRunner: + self._runner.__enter__() + self.get_loop().set_exception_handler(self._exception_handler) + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self._runner.__exit__(exc_type, exc_val, exc_tb) + + def get_loop(self) -> AbstractEventLoop: + return self._runner.get_loop() + + def _exception_handler( + self, loop: asyncio.AbstractEventLoop, context: dict[str, Any] + ) -> None: + if isinstance(context.get("exception"), Exception): + self._exceptions.append(context["exception"]) + else: + loop.default_exception_handler(context) + + def _raise_async_exceptions(self) -> None: + # Re-raise any exceptions raised in asynchronous callbacks + if self._exceptions: + exceptions, self._exceptions = self._exceptions, [] + if len(exceptions) == 1: + raise exceptions[0] + elif exceptions: + raise BaseExceptionGroup( + "Multiple exceptions occurred in asynchronous callbacks", exceptions + ) + + async def _run_tests_and_fixtures( + self, + receive_stream: MemoryObjectReceiveStream[ + tuple[Awaitable[T_Retval], asyncio.Future[T_Retval]] + ], + ) -> None: + from _pytest.outcomes import OutcomeException + + with receive_stream, self._send_stream: + async for coro, future in receive_stream: + try: + retval = await coro + except CancelledError as exc: + if not future.cancelled(): + future.cancel(*exc.args) + + raise + except BaseException as exc: + if not future.cancelled(): + future.set_exception(exc) + + if not isinstance(exc, (Exception, OutcomeException)): + raise + else: + if not future.cancelled(): + future.set_result(retval) + + async def _call_in_runner_task( + self, + func: Callable[P, Awaitable[T_Retval]], + *args: P.args, + **kwargs: P.kwargs, + ) -> T_Retval: + if not self._runner_task: + self._send_stream, receive_stream = create_memory_object_stream[ + tuple[Awaitable[Any], asyncio.Future] + ](1) + self._runner_task = self.get_loop().create_task( + self._run_tests_and_fixtures(receive_stream) + ) + + coro = func(*args, **kwargs) + future: asyncio.Future[T_Retval] = self.get_loop().create_future() + self._send_stream.send_nowait((coro, future)) + return await future + + def run_asyncgen_fixture( + self, + fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]], + kwargs: dict[str, Any], + ) -> Iterable[T_Retval]: + asyncgen = fixture_func(**kwargs) + fixturevalue: T_Retval = self.get_loop().run_until_complete( + self._call_in_runner_task(asyncgen.asend, None) + ) + self._raise_async_exceptions() + + yield fixturevalue + + try: + self.get_loop().run_until_complete( + self._call_in_runner_task(asyncgen.asend, None) + ) + except StopAsyncIteration: + self._raise_async_exceptions() + else: + self.get_loop().run_until_complete(asyncgen.aclose()) + raise RuntimeError("Async generator fixture did not stop") + + def run_fixture( + self, + fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]], + kwargs: dict[str, Any], + ) -> T_Retval: + retval = self.get_loop().run_until_complete( + self._call_in_runner_task(fixture_func, **kwargs) + ) + self._raise_async_exceptions() + return retval + + def run_test( + self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] + ) -> None: + try: + self.get_loop().run_until_complete( + self._call_in_runner_task(test_func, **kwargs) + ) + except Exception as exc: + self._exceptions.append(exc) + + self._raise_async_exceptions() + + +class AsyncIOBackend(AsyncBackend): + @classmethod + def run( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + options: dict[str, Any], + ) -> T_Retval: + @wraps(func) + async def wrapper() -> T_Retval: + task = cast(asyncio.Task, current_task()) + task.set_name(get_callable_name(func)) + _task_states[task] = TaskState(None, None) + + try: + return await func(*args) + finally: + del _task_states[task] + + debug = options.get("debug", None) + loop_factory = options.get("loop_factory", None) + if loop_factory is None and options.get("use_uvloop", False): + import uvloop + + loop_factory = uvloop.new_event_loop + + with Runner(debug=debug, loop_factory=loop_factory) as runner: + return runner.run(wrapper()) + + @classmethod + def current_token(cls) -> object: + return get_running_loop() + + @classmethod + def current_time(cls) -> float: + return get_running_loop().time() + + @classmethod + def cancelled_exception_class(cls) -> type[BaseException]: + return CancelledError + + @classmethod + async def checkpoint(cls) -> None: + await sleep(0) + + @classmethod + async def checkpoint_if_cancelled(cls) -> None: + task = current_task() + if task is None: + return + + try: + cancel_scope = _task_states[task].cancel_scope + except KeyError: + return + + while cancel_scope: + if cancel_scope.cancel_called: + await sleep(0) + elif cancel_scope.shield: + break + else: + cancel_scope = cancel_scope._parent_scope + + @classmethod + async def cancel_shielded_checkpoint(cls) -> None: + with CancelScope(shield=True): + await sleep(0) + + @classmethod + async def sleep(cls, delay: float) -> None: + await sleep(delay) + + @classmethod + def create_cancel_scope( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + return CancelScope(deadline=deadline, shield=shield) + + @classmethod + def current_effective_deadline(cls) -> float: + try: + cancel_scope = _task_states[ + current_task() # type: ignore[index] + ].cancel_scope + except KeyError: + return math.inf + + deadline = math.inf + while cancel_scope: + deadline = min(deadline, cancel_scope.deadline) + if cancel_scope._cancel_called: + deadline = -math.inf + break + elif cancel_scope.shield: + break + else: + cancel_scope = cancel_scope._parent_scope + + return deadline + + @classmethod + def create_task_group(cls) -> abc.TaskGroup: + return TaskGroup() + + @classmethod + def create_event(cls) -> abc.Event: + return Event() + + @classmethod + def create_lock(cls, *, fast_acquire: bool) -> abc.Lock: + return Lock(fast_acquire=fast_acquire) + + @classmethod + def create_semaphore( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> abc.Semaphore: + return Semaphore(initial_value, max_value=max_value, fast_acquire=fast_acquire) + + @classmethod + def create_capacity_limiter(cls, total_tokens: float) -> abc.CapacityLimiter: + return CapacityLimiter(total_tokens) + + @classmethod + async def run_sync_in_worker_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + abandon_on_cancel: bool = False, + limiter: abc.CapacityLimiter | None = None, + ) -> T_Retval: + await cls.checkpoint() + + # If this is the first run in this event loop thread, set up the necessary + # variables + try: + idle_workers = _threadpool_idle_workers.get() + workers = _threadpool_workers.get() + except LookupError: + idle_workers = deque() + workers = set() + _threadpool_idle_workers.set(idle_workers) + _threadpool_workers.set(workers) + + async with limiter or cls.current_default_thread_limiter(): + with CancelScope(shield=not abandon_on_cancel) as scope: + future: asyncio.Future = asyncio.Future() + root_task = find_root_task() + if not idle_workers: + worker = WorkerThread(root_task, workers, idle_workers) + worker.start() + workers.add(worker) + root_task.add_done_callback(worker.stop) + else: + worker = idle_workers.pop() + + # Prune any other workers that have been idle for MAX_IDLE_TIME + # seconds or longer + now = cls.current_time() + while idle_workers: + if ( + now - idle_workers[0].idle_since + < WorkerThread.MAX_IDLE_TIME + ): + break + + expired_worker = idle_workers.popleft() + expired_worker.root_task.remove_done_callback( + expired_worker.stop + ) + expired_worker.stop() + + context = copy_context() + context.run(sniffio.current_async_library_cvar.set, None) + if abandon_on_cancel or scope._parent_scope is None: + worker_scope = scope + else: + worker_scope = scope._parent_scope + + worker.queue.put_nowait((context, func, args, future, worker_scope)) + return await future + + @classmethod + def check_cancelled(cls) -> None: + scope: CancelScope | None = threadlocals.current_cancel_scope + while scope is not None: + if scope.cancel_called: + raise CancelledError(f"Cancelled by cancel scope {id(scope):x}") + + if scope.shield: + return + + scope = scope._parent_scope + + @classmethod + def run_async_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + async def task_wrapper(scope: CancelScope) -> T_Retval: + __tracebackhide__ = True + task = cast(asyncio.Task, current_task()) + _task_states[task] = TaskState(None, scope) + scope._tasks.add(task) + try: + return await func(*args) + except CancelledError as exc: + raise concurrent.futures.CancelledError(str(exc)) from None + finally: + scope._tasks.discard(task) + + loop = cast(AbstractEventLoop, token) + context = copy_context() + context.run(sniffio.current_async_library_cvar.set, "asyncio") + wrapper = task_wrapper(threadlocals.current_cancel_scope) + f: concurrent.futures.Future[T_Retval] = context.run( + asyncio.run_coroutine_threadsafe, wrapper, loop + ) + return f.result() + + @classmethod + def run_sync_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + @wraps(func) + def wrapper() -> None: + try: + sniffio.current_async_library_cvar.set("asyncio") + f.set_result(func(*args)) + except BaseException as exc: + f.set_exception(exc) + if not isinstance(exc, Exception): + raise + + f: concurrent.futures.Future[T_Retval] = Future() + loop = cast(AbstractEventLoop, token) + loop.call_soon_threadsafe(wrapper) + return f.result() + + @classmethod + def create_blocking_portal(cls) -> abc.BlockingPortal: + return BlockingPortal() + + @classmethod + async def open_process( + cls, + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + stdin: int | IO[Any] | None, + stdout: int | IO[Any] | None, + stderr: int | IO[Any] | None, + **kwargs: Any, + ) -> Process: + await cls.checkpoint() + if isinstance(command, PathLike): + command = os.fspath(command) + + if isinstance(command, (str, bytes)): + process = await asyncio.create_subprocess_shell( + command, + stdin=stdin, + stdout=stdout, + stderr=stderr, + **kwargs, + ) + else: + process = await asyncio.create_subprocess_exec( + *command, + stdin=stdin, + stdout=stdout, + stderr=stderr, + **kwargs, + ) + + stdin_stream = StreamWriterWrapper(process.stdin) if process.stdin else None + stdout_stream = StreamReaderWrapper(process.stdout) if process.stdout else None + stderr_stream = StreamReaderWrapper(process.stderr) if process.stderr else None + return Process(process, stdin_stream, stdout_stream, stderr_stream) + + @classmethod + def setup_process_pool_exit_at_shutdown(cls, workers: set[abc.Process]) -> None: + create_task( + _shutdown_process_pool_on_exit(workers), + name="AnyIO process pool shutdown task", + ) + find_root_task().add_done_callback( + partial(_forcibly_shutdown_process_pool_on_exit, workers) # type:ignore[arg-type] + ) + + @classmethod + async def connect_tcp( + cls, host: str, port: int, local_address: IPSockAddrType | None = None + ) -> abc.SocketStream: + transport, protocol = cast( + tuple[asyncio.Transport, StreamProtocol], + await get_running_loop().create_connection( + StreamProtocol, host, port, local_addr=local_address + ), + ) + transport.pause_reading() + return SocketStream(transport, protocol) + + @classmethod + async def connect_unix(cls, path: str | bytes) -> abc.UNIXSocketStream: + await cls.checkpoint() + loop = get_running_loop() + raw_socket = socket.socket(socket.AF_UNIX) + raw_socket.setblocking(False) + while True: + try: + raw_socket.connect(path) + except BlockingIOError: + f: asyncio.Future = asyncio.Future() + loop.add_writer(raw_socket, f.set_result, None) + f.add_done_callback(lambda _: loop.remove_writer(raw_socket)) + await f + except BaseException: + raw_socket.close() + raise + else: + return UNIXSocketStream(raw_socket) + + @classmethod + def create_tcp_listener(cls, sock: socket.socket) -> SocketListener: + return TCPSocketListener(sock) + + @classmethod + def create_unix_listener(cls, sock: socket.socket) -> SocketListener: + return UNIXSocketListener(sock) + + @classmethod + async def create_udp_socket( + cls, + family: AddressFamily, + local_address: IPSockAddrType | None, + remote_address: IPSockAddrType | None, + reuse_port: bool, + ) -> UDPSocket | ConnectedUDPSocket: + transport, protocol = await get_running_loop().create_datagram_endpoint( + DatagramProtocol, + local_addr=local_address, + remote_addr=remote_address, + family=family, + reuse_port=reuse_port, + ) + if protocol.exception: + transport.close() + raise protocol.exception + + if not remote_address: + return UDPSocket(transport, protocol) + else: + return ConnectedUDPSocket(transport, protocol) + + @classmethod + async def create_unix_datagram_socket( # type: ignore[override] + cls, raw_socket: socket.socket, remote_path: str | bytes | None + ) -> abc.UNIXDatagramSocket | abc.ConnectedUNIXDatagramSocket: + await cls.checkpoint() + loop = get_running_loop() + + if remote_path: + while True: + try: + raw_socket.connect(remote_path) + except BlockingIOError: + f: asyncio.Future = asyncio.Future() + loop.add_writer(raw_socket, f.set_result, None) + f.add_done_callback(lambda _: loop.remove_writer(raw_socket)) + await f + except BaseException: + raw_socket.close() + raise + else: + return ConnectedUNIXDatagramSocket(raw_socket) + else: + return UNIXDatagramSocket(raw_socket) + + @classmethod + async def getaddrinfo( + cls, + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, + ) -> list[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int], + ] + ]: + return await get_running_loop().getaddrinfo( + host, port, family=family, type=type, proto=proto, flags=flags + ) + + @classmethod + async def getnameinfo( + cls, sockaddr: IPSockAddrType, flags: int = 0 + ) -> tuple[str, str]: + return await get_running_loop().getnameinfo(sockaddr, flags) + + @classmethod + async def wait_socket_readable(cls, sock: socket.socket) -> None: + await cls.checkpoint() + try: + read_events = _read_events.get() + except LookupError: + read_events = {} + _read_events.set(read_events) + + if read_events.get(sock): + raise BusyResourceError("reading from") from None + + loop = get_running_loop() + event = read_events[sock] = asyncio.Event() + loop.add_reader(sock, event.set) + try: + await event.wait() + finally: + if read_events.pop(sock, None) is not None: + loop.remove_reader(sock) + readable = True + else: + readable = False + + if not readable: + raise ClosedResourceError + + @classmethod + async def wait_socket_writable(cls, sock: socket.socket) -> None: + await cls.checkpoint() + try: + write_events = _write_events.get() + except LookupError: + write_events = {} + _write_events.set(write_events) + + if write_events.get(sock): + raise BusyResourceError("writing to") from None + + loop = get_running_loop() + event = write_events[sock] = asyncio.Event() + loop.add_writer(sock.fileno(), event.set) + try: + await event.wait() + finally: + if write_events.pop(sock, None) is not None: + loop.remove_writer(sock) + writable = True + else: + writable = False + + if not writable: + raise ClosedResourceError + + @classmethod + def current_default_thread_limiter(cls) -> CapacityLimiter: + try: + return _default_thread_limiter.get() + except LookupError: + limiter = CapacityLimiter(40) + _default_thread_limiter.set(limiter) + return limiter + + @classmethod + def open_signal_receiver( + cls, *signals: Signals + ) -> AbstractContextManager[AsyncIterator[Signals]]: + return _SignalReceiver(signals) + + @classmethod + def get_current_task(cls) -> TaskInfo: + return AsyncIOTaskInfo(current_task()) # type: ignore[arg-type] + + @classmethod + def get_running_tasks(cls) -> Sequence[TaskInfo]: + return [AsyncIOTaskInfo(task) for task in all_tasks() if not task.done()] + + @classmethod + async def wait_all_tasks_blocked(cls) -> None: + await cls.checkpoint() + this_task = current_task() + while True: + for task in all_tasks(): + if task is this_task: + continue + + waiter = task._fut_waiter # type: ignore[attr-defined] + if waiter is None or waiter.done(): + await sleep(0.1) + break + else: + return + + @classmethod + def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: + return TestRunner(**options) + + +backend_class = AsyncIOBackend diff --git a/venv/lib/python3.11/site-packages/anyio/_backends/_trio.py b/venv/lib/python3.11/site-packages/anyio/_backends/_trio.py new file mode 100644 index 00000000..24dcd744 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/_backends/_trio.py @@ -0,0 +1,1330 @@ +from __future__ import annotations + +import array +import math +import os +import socket +import sys +import types +import weakref +from collections.abc import ( + AsyncGenerator, + AsyncIterator, + Awaitable, + Callable, + Collection, + Coroutine, + Iterable, + Sequence, +) +from concurrent.futures import Future +from contextlib import AbstractContextManager +from dataclasses import dataclass +from functools import partial +from io import IOBase +from os import PathLike +from signal import Signals +from socket import AddressFamily, SocketKind +from types import TracebackType +from typing import ( + IO, + Any, + Generic, + NoReturn, + TypeVar, + cast, + overload, +) + +import trio.from_thread +import trio.lowlevel +from outcome import Error, Outcome, Value +from trio.lowlevel import ( + current_root_task, + current_task, + wait_readable, + wait_writable, +) +from trio.socket import SocketType as TrioSocketType +from trio.to_thread import run_sync + +from .. import ( + CapacityLimiterStatistics, + EventStatistics, + LockStatistics, + TaskInfo, + WouldBlock, + abc, +) +from .._core._eventloop import claim_worker_thread +from .._core._exceptions import ( + BrokenResourceError, + BusyResourceError, + ClosedResourceError, + EndOfStream, +) +from .._core._sockets import convert_ipv6_sockaddr +from .._core._streams import create_memory_object_stream +from .._core._synchronization import ( + CapacityLimiter as BaseCapacityLimiter, +) +from .._core._synchronization import Event as BaseEvent +from .._core._synchronization import Lock as BaseLock +from .._core._synchronization import ( + ResourceGuard, + SemaphoreStatistics, +) +from .._core._synchronization import Semaphore as BaseSemaphore +from .._core._tasks import CancelScope as BaseCancelScope +from ..abc import IPSockAddrType, UDPPacketType, UNIXDatagramPacketType +from ..abc._eventloop import AsyncBackend, StrOrBytesPath +from ..streams.memory import MemoryObjectSendStream + +if sys.version_info >= (3, 10): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from exceptiongroup import BaseExceptionGroup + from typing_extensions import TypeVarTuple, Unpack + +T = TypeVar("T") +T_Retval = TypeVar("T_Retval") +T_SockAddr = TypeVar("T_SockAddr", str, IPSockAddrType) +PosArgsT = TypeVarTuple("PosArgsT") +P = ParamSpec("P") + + +# +# Event loop +# + +RunVar = trio.lowlevel.RunVar + + +# +# Timeouts and cancellation +# + + +class CancelScope(BaseCancelScope): + def __new__( + cls, original: trio.CancelScope | None = None, **kwargs: object + ) -> CancelScope: + return object.__new__(cls) + + def __init__(self, original: trio.CancelScope | None = None, **kwargs: Any) -> None: + self.__original = original or trio.CancelScope(**kwargs) + + def __enter__(self) -> CancelScope: + self.__original.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + # https://github.com/python-trio/trio-typing/pull/79 + return self.__original.__exit__(exc_type, exc_val, exc_tb) + + def cancel(self) -> None: + self.__original.cancel() + + @property + def deadline(self) -> float: + return self.__original.deadline + + @deadline.setter + def deadline(self, value: float) -> None: + self.__original.deadline = value + + @property + def cancel_called(self) -> bool: + return self.__original.cancel_called + + @property + def cancelled_caught(self) -> bool: + return self.__original.cancelled_caught + + @property + def shield(self) -> bool: + return self.__original.shield + + @shield.setter + def shield(self, value: bool) -> None: + self.__original.shield = value + + +# +# Task groups +# + + +class TaskGroup(abc.TaskGroup): + def __init__(self) -> None: + self._active = False + self._nursery_manager = trio.open_nursery(strict_exception_groups=True) + self.cancel_scope = None # type: ignore[assignment] + + async def __aenter__(self) -> TaskGroup: + self._active = True + self._nursery = await self._nursery_manager.__aenter__() + self.cancel_scope = CancelScope(self._nursery.cancel_scope) + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + try: + return await self._nursery_manager.__aexit__(exc_type, exc_val, exc_tb) + except BaseExceptionGroup as exc: + if not exc.split(trio.Cancelled)[1]: + raise trio.Cancelled._create() from exc + + raise + finally: + del exc_val, exc_tb + self._active = False + + def start_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], + *args: Unpack[PosArgsT], + name: object = None, + ) -> None: + if not self._active: + raise RuntimeError( + "This task group is not active; no new tasks can be started." + ) + + self._nursery.start_soon(func, *args, name=name) + + async def start( + self, func: Callable[..., Awaitable[Any]], *args: object, name: object = None + ) -> Any: + if not self._active: + raise RuntimeError( + "This task group is not active; no new tasks can be started." + ) + + return await self._nursery.start(func, *args, name=name) + + +# +# Threads +# + + +class BlockingPortal(abc.BlockingPortal): + def __new__(cls) -> BlockingPortal: + return object.__new__(cls) + + def __init__(self) -> None: + super().__init__() + self._token = trio.lowlevel.current_trio_token() + + def _spawn_task_from_thread( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + name: object, + future: Future[T_Retval], + ) -> None: + trio.from_thread.run_sync( + partial(self._task_group.start_soon, name=name), + self._call_func, + func, + args, + kwargs, + future, + trio_token=self._token, + ) + + +# +# Subprocesses +# + + +@dataclass(eq=False) +class ReceiveStreamWrapper(abc.ByteReceiveStream): + _stream: trio.abc.ReceiveStream + + async def receive(self, max_bytes: int | None = None) -> bytes: + try: + data = await self._stream.receive_some(max_bytes) + except trio.ClosedResourceError as exc: + raise ClosedResourceError from exc.__cause__ + except trio.BrokenResourceError as exc: + raise BrokenResourceError from exc.__cause__ + + if data: + return data + else: + raise EndOfStream + + async def aclose(self) -> None: + await self._stream.aclose() + + +@dataclass(eq=False) +class SendStreamWrapper(abc.ByteSendStream): + _stream: trio.abc.SendStream + + async def send(self, item: bytes) -> None: + try: + await self._stream.send_all(item) + except trio.ClosedResourceError as exc: + raise ClosedResourceError from exc.__cause__ + except trio.BrokenResourceError as exc: + raise BrokenResourceError from exc.__cause__ + + async def aclose(self) -> None: + await self._stream.aclose() + + +@dataclass(eq=False) +class Process(abc.Process): + _process: trio.Process + _stdin: abc.ByteSendStream | None + _stdout: abc.ByteReceiveStream | None + _stderr: abc.ByteReceiveStream | None + + async def aclose(self) -> None: + with CancelScope(shield=True): + if self._stdin: + await self._stdin.aclose() + if self._stdout: + await self._stdout.aclose() + if self._stderr: + await self._stderr.aclose() + + try: + await self.wait() + except BaseException: + self.kill() + with CancelScope(shield=True): + await self.wait() + raise + + async def wait(self) -> int: + return await self._process.wait() + + def terminate(self) -> None: + self._process.terminate() + + def kill(self) -> None: + self._process.kill() + + def send_signal(self, signal: Signals) -> None: + self._process.send_signal(signal) + + @property + def pid(self) -> int: + return self._process.pid + + @property + def returncode(self) -> int | None: + return self._process.returncode + + @property + def stdin(self) -> abc.ByteSendStream | None: + return self._stdin + + @property + def stdout(self) -> abc.ByteReceiveStream | None: + return self._stdout + + @property + def stderr(self) -> abc.ByteReceiveStream | None: + return self._stderr + + +class _ProcessPoolShutdownInstrument(trio.abc.Instrument): + def after_run(self) -> None: + super().after_run() + + +current_default_worker_process_limiter: trio.lowlevel.RunVar = RunVar( + "current_default_worker_process_limiter" +) + + +async def _shutdown_process_pool(workers: set[abc.Process]) -> None: + try: + await trio.sleep(math.inf) + except trio.Cancelled: + for process in workers: + if process.returncode is None: + process.kill() + + with CancelScope(shield=True): + for process in workers: + await process.aclose() + + +# +# Sockets and networking +# + + +class _TrioSocketMixin(Generic[T_SockAddr]): + def __init__(self, trio_socket: TrioSocketType) -> None: + self._trio_socket = trio_socket + self._closed = False + + def _check_closed(self) -> None: + if self._closed: + raise ClosedResourceError + if self._trio_socket.fileno() < 0: + raise BrokenResourceError + + @property + def _raw_socket(self) -> socket.socket: + return self._trio_socket._sock # type: ignore[attr-defined] + + async def aclose(self) -> None: + if self._trio_socket.fileno() >= 0: + self._closed = True + self._trio_socket.close() + + def _convert_socket_error(self, exc: BaseException) -> NoReturn: + if isinstance(exc, trio.ClosedResourceError): + raise ClosedResourceError from exc + elif self._trio_socket.fileno() < 0 and self._closed: + raise ClosedResourceError from None + elif isinstance(exc, OSError): + raise BrokenResourceError from exc + else: + raise exc + + +class SocketStream(_TrioSocketMixin, abc.SocketStream): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self, max_bytes: int = 65536) -> bytes: + with self._receive_guard: + try: + data = await self._trio_socket.recv(max_bytes) + except BaseException as exc: + self._convert_socket_error(exc) + + if data: + return data + else: + raise EndOfStream + + async def send(self, item: bytes) -> None: + with self._send_guard: + view = memoryview(item) + while view: + try: + bytes_sent = await self._trio_socket.send(view) + except BaseException as exc: + self._convert_socket_error(exc) + + view = view[bytes_sent:] + + async def send_eof(self) -> None: + self._trio_socket.shutdown(socket.SHUT_WR) + + +class UNIXSocketStream(SocketStream, abc.UNIXSocketStream): + async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: + if not isinstance(msglen, int) or msglen < 0: + raise ValueError("msglen must be a non-negative integer") + if not isinstance(maxfds, int) or maxfds < 1: + raise ValueError("maxfds must be a positive integer") + + fds = array.array("i") + await trio.lowlevel.checkpoint() + with self._receive_guard: + while True: + try: + message, ancdata, flags, addr = await self._trio_socket.recvmsg( + msglen, socket.CMSG_LEN(maxfds * fds.itemsize) + ) + except BaseException as exc: + self._convert_socket_error(exc) + else: + if not message and not ancdata: + raise EndOfStream + + break + + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS: + raise RuntimeError( + f"Received unexpected ancillary data; message = {message!r}, " + f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}" + ) + + fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) + + return message, list(fds) + + async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: + if not message: + raise ValueError("message must not be empty") + if not fds: + raise ValueError("fds must not be empty") + + filenos: list[int] = [] + for fd in fds: + if isinstance(fd, int): + filenos.append(fd) + elif isinstance(fd, IOBase): + filenos.append(fd.fileno()) + + fdarray = array.array("i", filenos) + await trio.lowlevel.checkpoint() + with self._send_guard: + while True: + try: + await self._trio_socket.sendmsg( + [message], + [ + ( + socket.SOL_SOCKET, + socket.SCM_RIGHTS, + fdarray, + ) + ], + ) + break + except BaseException as exc: + self._convert_socket_error(exc) + + +class TCPSocketListener(_TrioSocketMixin, abc.SocketListener): + def __init__(self, raw_socket: socket.socket): + super().__init__(trio.socket.from_stdlib_socket(raw_socket)) + self._accept_guard = ResourceGuard("accepting connections from") + + async def accept(self) -> SocketStream: + with self._accept_guard: + try: + trio_socket, _addr = await self._trio_socket.accept() + except BaseException as exc: + self._convert_socket_error(exc) + + trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + return SocketStream(trio_socket) + + +class UNIXSocketListener(_TrioSocketMixin, abc.SocketListener): + def __init__(self, raw_socket: socket.socket): + super().__init__(trio.socket.from_stdlib_socket(raw_socket)) + self._accept_guard = ResourceGuard("accepting connections from") + + async def accept(self) -> UNIXSocketStream: + with self._accept_guard: + try: + trio_socket, _addr = await self._trio_socket.accept() + except BaseException as exc: + self._convert_socket_error(exc) + + return UNIXSocketStream(trio_socket) + + +class UDPSocket(_TrioSocketMixin[IPSockAddrType], abc.UDPSocket): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> tuple[bytes, IPSockAddrType]: + with self._receive_guard: + try: + data, addr = await self._trio_socket.recvfrom(65536) + return data, convert_ipv6_sockaddr(addr) + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: UDPPacketType) -> None: + with self._send_guard: + try: + await self._trio_socket.sendto(*item) + except BaseException as exc: + self._convert_socket_error(exc) + + +class ConnectedUDPSocket(_TrioSocketMixin[IPSockAddrType], abc.ConnectedUDPSocket): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> bytes: + with self._receive_guard: + try: + return await self._trio_socket.recv(65536) + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: bytes) -> None: + with self._send_guard: + try: + await self._trio_socket.send(item) + except BaseException as exc: + self._convert_socket_error(exc) + + +class UNIXDatagramSocket(_TrioSocketMixin[str], abc.UNIXDatagramSocket): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> UNIXDatagramPacketType: + with self._receive_guard: + try: + data, addr = await self._trio_socket.recvfrom(65536) + return data, addr + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: UNIXDatagramPacketType) -> None: + with self._send_guard: + try: + await self._trio_socket.sendto(*item) + except BaseException as exc: + self._convert_socket_error(exc) + + +class ConnectedUNIXDatagramSocket( + _TrioSocketMixin[str], abc.ConnectedUNIXDatagramSocket +): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> bytes: + with self._receive_guard: + try: + return await self._trio_socket.recv(65536) + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: bytes) -> None: + with self._send_guard: + try: + await self._trio_socket.send(item) + except BaseException as exc: + self._convert_socket_error(exc) + + +# +# Synchronization +# + + +class Event(BaseEvent): + def __new__(cls) -> Event: + return object.__new__(cls) + + def __init__(self) -> None: + self.__original = trio.Event() + + def is_set(self) -> bool: + return self.__original.is_set() + + async def wait(self) -> None: + return await self.__original.wait() + + def statistics(self) -> EventStatistics: + orig_statistics = self.__original.statistics() + return EventStatistics(tasks_waiting=orig_statistics.tasks_waiting) + + def set(self) -> None: + self.__original.set() + + +class Lock(BaseLock): + def __new__(cls, *, fast_acquire: bool = False) -> Lock: + return object.__new__(cls) + + def __init__(self, *, fast_acquire: bool = False) -> None: + self._fast_acquire = fast_acquire + self.__original = trio.Lock() + + @staticmethod + def _convert_runtime_error_msg(exc: RuntimeError) -> None: + if exc.args == ("attempt to re-acquire an already held Lock",): + exc.args = ("Attempted to acquire an already held Lock",) + + async def acquire(self) -> None: + if not self._fast_acquire: + try: + await self.__original.acquire() + except RuntimeError as exc: + self._convert_runtime_error_msg(exc) + raise + + return + + # This is the "fast path" where we don't let other tasks run + await trio.lowlevel.checkpoint_if_cancelled() + try: + self.__original.acquire_nowait() + except trio.WouldBlock: + await self.__original._lot.park() + except RuntimeError as exc: + self._convert_runtime_error_msg(exc) + raise + + def acquire_nowait(self) -> None: + try: + self.__original.acquire_nowait() + except trio.WouldBlock: + raise WouldBlock from None + except RuntimeError as exc: + self._convert_runtime_error_msg(exc) + raise + + def locked(self) -> bool: + return self.__original.locked() + + def release(self) -> None: + self.__original.release() + + def statistics(self) -> LockStatistics: + orig_statistics = self.__original.statistics() + owner = TrioTaskInfo(orig_statistics.owner) if orig_statistics.owner else None + return LockStatistics( + orig_statistics.locked, owner, orig_statistics.tasks_waiting + ) + + +class Semaphore(BaseSemaphore): + def __new__( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> Semaphore: + return object.__new__(cls) + + def __init__( + self, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> None: + super().__init__(initial_value, max_value=max_value, fast_acquire=fast_acquire) + self.__original = trio.Semaphore(initial_value, max_value=max_value) + + async def acquire(self) -> None: + if not self._fast_acquire: + await self.__original.acquire() + return + + # This is the "fast path" where we don't let other tasks run + await trio.lowlevel.checkpoint_if_cancelled() + try: + self.__original.acquire_nowait() + except trio.WouldBlock: + await self.__original._lot.park() + + def acquire_nowait(self) -> None: + try: + self.__original.acquire_nowait() + except trio.WouldBlock: + raise WouldBlock from None + + @property + def max_value(self) -> int | None: + return self.__original.max_value + + @property + def value(self) -> int: + return self.__original.value + + def release(self) -> None: + self.__original.release() + + def statistics(self) -> SemaphoreStatistics: + orig_statistics = self.__original.statistics() + return SemaphoreStatistics(orig_statistics.tasks_waiting) + + +class CapacityLimiter(BaseCapacityLimiter): + def __new__( + cls, + total_tokens: float | None = None, + *, + original: trio.CapacityLimiter | None = None, + ) -> CapacityLimiter: + return object.__new__(cls) + + def __init__( + self, + total_tokens: float | None = None, + *, + original: trio.CapacityLimiter | None = None, + ) -> None: + if original is not None: + self.__original = original + else: + assert total_tokens is not None + self.__original = trio.CapacityLimiter(total_tokens) + + async def __aenter__(self) -> None: + return await self.__original.__aenter__() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.__original.__aexit__(exc_type, exc_val, exc_tb) + + @property + def total_tokens(self) -> float: + return self.__original.total_tokens + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + self.__original.total_tokens = value + + @property + def borrowed_tokens(self) -> int: + return self.__original.borrowed_tokens + + @property + def available_tokens(self) -> float: + return self.__original.available_tokens + + def acquire_nowait(self) -> None: + self.__original.acquire_nowait() + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + self.__original.acquire_on_behalf_of_nowait(borrower) + + async def acquire(self) -> None: + await self.__original.acquire() + + async def acquire_on_behalf_of(self, borrower: object) -> None: + await self.__original.acquire_on_behalf_of(borrower) + + def release(self) -> None: + return self.__original.release() + + def release_on_behalf_of(self, borrower: object) -> None: + return self.__original.release_on_behalf_of(borrower) + + def statistics(self) -> CapacityLimiterStatistics: + orig = self.__original.statistics() + return CapacityLimiterStatistics( + borrowed_tokens=orig.borrowed_tokens, + total_tokens=orig.total_tokens, + borrowers=tuple(orig.borrowers), + tasks_waiting=orig.tasks_waiting, + ) + + +_capacity_limiter_wrapper: trio.lowlevel.RunVar = RunVar("_capacity_limiter_wrapper") + + +# +# Signal handling +# + + +class _SignalReceiver: + _iterator: AsyncIterator[int] + + def __init__(self, signals: tuple[Signals, ...]): + self._signals = signals + + def __enter__(self) -> _SignalReceiver: + self._cm = trio.open_signal_receiver(*self._signals) + self._iterator = self._cm.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + return self._cm.__exit__(exc_type, exc_val, exc_tb) + + def __aiter__(self) -> _SignalReceiver: + return self + + async def __anext__(self) -> Signals: + signum = await self._iterator.__anext__() + return Signals(signum) + + +# +# Testing and debugging +# + + +class TestRunner(abc.TestRunner): + def __init__(self, **options: Any) -> None: + from queue import Queue + + self._call_queue: Queue[Callable[[], object]] = Queue() + self._send_stream: MemoryObjectSendStream | None = None + self._options = options + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> None: + if self._send_stream: + self._send_stream.close() + while self._send_stream is not None: + self._call_queue.get()() + + async def _run_tests_and_fixtures(self) -> None: + self._send_stream, receive_stream = create_memory_object_stream(1) + with receive_stream: + async for coro, outcome_holder in receive_stream: + try: + retval = await coro + except BaseException as exc: + outcome_holder.append(Error(exc)) + else: + outcome_holder.append(Value(retval)) + + def _main_task_finished(self, outcome: object) -> None: + self._send_stream = None + + def _call_in_runner_task( + self, + func: Callable[P, Awaitable[T_Retval]], + *args: P.args, + **kwargs: P.kwargs, + ) -> T_Retval: + if self._send_stream is None: + trio.lowlevel.start_guest_run( + self._run_tests_and_fixtures, + run_sync_soon_threadsafe=self._call_queue.put, + done_callback=self._main_task_finished, + **self._options, + ) + while self._send_stream is None: + self._call_queue.get()() + + outcome_holder: list[Outcome] = [] + self._send_stream.send_nowait((func(*args, **kwargs), outcome_holder)) + while not outcome_holder: + self._call_queue.get()() + + return outcome_holder[0].unwrap() + + def run_asyncgen_fixture( + self, + fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]], + kwargs: dict[str, Any], + ) -> Iterable[T_Retval]: + asyncgen = fixture_func(**kwargs) + fixturevalue: T_Retval = self._call_in_runner_task(asyncgen.asend, None) + + yield fixturevalue + + try: + self._call_in_runner_task(asyncgen.asend, None) + except StopAsyncIteration: + pass + else: + self._call_in_runner_task(asyncgen.aclose) + raise RuntimeError("Async generator fixture did not stop") + + def run_fixture( + self, + fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]], + kwargs: dict[str, Any], + ) -> T_Retval: + return self._call_in_runner_task(fixture_func, **kwargs) + + def run_test( + self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] + ) -> None: + self._call_in_runner_task(test_func, **kwargs) + + +class TrioTaskInfo(TaskInfo): + def __init__(self, task: trio.lowlevel.Task): + parent_id = None + if task.parent_nursery and task.parent_nursery.parent_task: + parent_id = id(task.parent_nursery.parent_task) + + super().__init__(id(task), parent_id, task.name, task.coro) + self._task = weakref.proxy(task) + + def has_pending_cancellation(self) -> bool: + try: + return self._task._cancel_status.effectively_cancelled + except ReferenceError: + # If the task is no longer around, it surely doesn't have a cancellation + # pending + return False + + +class TrioBackend(AsyncBackend): + @classmethod + def run( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + options: dict[str, Any], + ) -> T_Retval: + return trio.run(func, *args) + + @classmethod + def current_token(cls) -> object: + return trio.lowlevel.current_trio_token() + + @classmethod + def current_time(cls) -> float: + return trio.current_time() + + @classmethod + def cancelled_exception_class(cls) -> type[BaseException]: + return trio.Cancelled + + @classmethod + async def checkpoint(cls) -> None: + await trio.lowlevel.checkpoint() + + @classmethod + async def checkpoint_if_cancelled(cls) -> None: + await trio.lowlevel.checkpoint_if_cancelled() + + @classmethod + async def cancel_shielded_checkpoint(cls) -> None: + await trio.lowlevel.cancel_shielded_checkpoint() + + @classmethod + async def sleep(cls, delay: float) -> None: + await trio.sleep(delay) + + @classmethod + def create_cancel_scope( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> abc.CancelScope: + return CancelScope(deadline=deadline, shield=shield) + + @classmethod + def current_effective_deadline(cls) -> float: + return trio.current_effective_deadline() + + @classmethod + def create_task_group(cls) -> abc.TaskGroup: + return TaskGroup() + + @classmethod + def create_event(cls) -> abc.Event: + return Event() + + @classmethod + def create_lock(cls, *, fast_acquire: bool) -> Lock: + return Lock(fast_acquire=fast_acquire) + + @classmethod + def create_semaphore( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> abc.Semaphore: + return Semaphore(initial_value, max_value=max_value, fast_acquire=fast_acquire) + + @classmethod + def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter: + return CapacityLimiter(total_tokens) + + @classmethod + async def run_sync_in_worker_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + abandon_on_cancel: bool = False, + limiter: abc.CapacityLimiter | None = None, + ) -> T_Retval: + def wrapper() -> T_Retval: + with claim_worker_thread(TrioBackend, token): + return func(*args) + + token = TrioBackend.current_token() + return await run_sync( + wrapper, + abandon_on_cancel=abandon_on_cancel, + limiter=cast(trio.CapacityLimiter, limiter), + ) + + @classmethod + def check_cancelled(cls) -> None: + trio.from_thread.check_cancelled() + + @classmethod + def run_async_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + return trio.from_thread.run(func, *args) + + @classmethod + def run_sync_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + return trio.from_thread.run_sync(func, *args) + + @classmethod + def create_blocking_portal(cls) -> abc.BlockingPortal: + return BlockingPortal() + + @classmethod + async def open_process( + cls, + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + stdin: int | IO[Any] | None, + stdout: int | IO[Any] | None, + stderr: int | IO[Any] | None, + **kwargs: Any, + ) -> Process: + def convert_item(item: StrOrBytesPath) -> str: + str_or_bytes = os.fspath(item) + if isinstance(str_or_bytes, str): + return str_or_bytes + else: + return os.fsdecode(str_or_bytes) + + if isinstance(command, (str, bytes, PathLike)): + process = await trio.lowlevel.open_process( + convert_item(command), + stdin=stdin, + stdout=stdout, + stderr=stderr, + shell=True, + **kwargs, + ) + else: + process = await trio.lowlevel.open_process( + [convert_item(item) for item in command], + stdin=stdin, + stdout=stdout, + stderr=stderr, + shell=False, + **kwargs, + ) + + stdin_stream = SendStreamWrapper(process.stdin) if process.stdin else None + stdout_stream = ReceiveStreamWrapper(process.stdout) if process.stdout else None + stderr_stream = ReceiveStreamWrapper(process.stderr) if process.stderr else None + return Process(process, stdin_stream, stdout_stream, stderr_stream) + + @classmethod + def setup_process_pool_exit_at_shutdown(cls, workers: set[abc.Process]) -> None: + trio.lowlevel.spawn_system_task(_shutdown_process_pool, workers) + + @classmethod + async def connect_tcp( + cls, host: str, port: int, local_address: IPSockAddrType | None = None + ) -> SocketStream: + family = socket.AF_INET6 if ":" in host else socket.AF_INET + trio_socket = trio.socket.socket(family) + trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + if local_address: + await trio_socket.bind(local_address) + + try: + await trio_socket.connect((host, port)) + except BaseException: + trio_socket.close() + raise + + return SocketStream(trio_socket) + + @classmethod + async def connect_unix(cls, path: str | bytes) -> abc.UNIXSocketStream: + trio_socket = trio.socket.socket(socket.AF_UNIX) + try: + await trio_socket.connect(path) + except BaseException: + trio_socket.close() + raise + + return UNIXSocketStream(trio_socket) + + @classmethod + def create_tcp_listener(cls, sock: socket.socket) -> abc.SocketListener: + return TCPSocketListener(sock) + + @classmethod + def create_unix_listener(cls, sock: socket.socket) -> abc.SocketListener: + return UNIXSocketListener(sock) + + @classmethod + async def create_udp_socket( + cls, + family: socket.AddressFamily, + local_address: IPSockAddrType | None, + remote_address: IPSockAddrType | None, + reuse_port: bool, + ) -> UDPSocket | ConnectedUDPSocket: + trio_socket = trio.socket.socket(family=family, type=socket.SOCK_DGRAM) + + if reuse_port: + trio_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + + if local_address: + await trio_socket.bind(local_address) + + if remote_address: + await trio_socket.connect(remote_address) + return ConnectedUDPSocket(trio_socket) + else: + return UDPSocket(trio_socket) + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket.socket, remote_path: None + ) -> abc.UNIXDatagramSocket: ... + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket.socket, remote_path: str | bytes + ) -> abc.ConnectedUNIXDatagramSocket: ... + + @classmethod + async def create_unix_datagram_socket( + cls, raw_socket: socket.socket, remote_path: str | bytes | None + ) -> abc.UNIXDatagramSocket | abc.ConnectedUNIXDatagramSocket: + trio_socket = trio.socket.from_stdlib_socket(raw_socket) + + if remote_path: + await trio_socket.connect(remote_path) + return ConnectedUNIXDatagramSocket(trio_socket) + else: + return UNIXDatagramSocket(trio_socket) + + @classmethod + async def getaddrinfo( + cls, + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, + ) -> list[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int], + ] + ]: + return await trio.socket.getaddrinfo(host, port, family, type, proto, flags) + + @classmethod + async def getnameinfo( + cls, sockaddr: IPSockAddrType, flags: int = 0 + ) -> tuple[str, str]: + return await trio.socket.getnameinfo(sockaddr, flags) + + @classmethod + async def wait_socket_readable(cls, sock: socket.socket) -> None: + try: + await wait_readable(sock) + except trio.ClosedResourceError as exc: + raise ClosedResourceError().with_traceback(exc.__traceback__) from None + except trio.BusyResourceError: + raise BusyResourceError("reading from") from None + + @classmethod + async def wait_socket_writable(cls, sock: socket.socket) -> None: + try: + await wait_writable(sock) + except trio.ClosedResourceError as exc: + raise ClosedResourceError().with_traceback(exc.__traceback__) from None + except trio.BusyResourceError: + raise BusyResourceError("writing to") from None + + @classmethod + def current_default_thread_limiter(cls) -> CapacityLimiter: + try: + return _capacity_limiter_wrapper.get() + except LookupError: + limiter = CapacityLimiter( + original=trio.to_thread.current_default_thread_limiter() + ) + _capacity_limiter_wrapper.set(limiter) + return limiter + + @classmethod + def open_signal_receiver( + cls, *signals: Signals + ) -> AbstractContextManager[AsyncIterator[Signals]]: + return _SignalReceiver(signals) + + @classmethod + def get_current_task(cls) -> TaskInfo: + task = current_task() + return TrioTaskInfo(task) + + @classmethod + def get_running_tasks(cls) -> Sequence[TaskInfo]: + root_task = current_root_task() + assert root_task + task_infos = [TrioTaskInfo(root_task)] + nurseries = root_task.child_nurseries + while nurseries: + new_nurseries: list[trio.Nursery] = [] + for nursery in nurseries: + for task in nursery.child_tasks: + task_infos.append(TrioTaskInfo(task)) + new_nurseries.extend(task.child_nurseries) + + nurseries = new_nurseries + + return task_infos + + @classmethod + async def wait_all_tasks_blocked(cls) -> None: + from trio.testing import wait_all_tasks_blocked + + await wait_all_tasks_blocked() + + @classmethod + def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: + return TestRunner(**options) + + +backend_class = TrioBackend diff --git a/venv/lib/python3.11/site-packages/anyio/_core/__init__.py b/venv/lib/python3.11/site-packages/anyio/_core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.11/site-packages/anyio/_core/_eventloop.py b/venv/lib/python3.11/site-packages/anyio/_core/_eventloop.py new file mode 100644 index 00000000..6dcb4589 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/_core/_eventloop.py @@ -0,0 +1,166 @@ +from __future__ import annotations + +import math +import sys +import threading +from collections.abc import Awaitable, Callable, Generator +from contextlib import contextmanager +from importlib import import_module +from typing import TYPE_CHECKING, Any, TypeVar + +import sniffio + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if TYPE_CHECKING: + from ..abc import AsyncBackend + +# This must be updated when new backends are introduced +BACKENDS = "asyncio", "trio" + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + +threadlocals = threading.local() +loaded_backends: dict[str, type[AsyncBackend]] = {} + + +def run( + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + *args: Unpack[PosArgsT], + backend: str = "asyncio", + backend_options: dict[str, Any] | None = None, +) -> T_Retval: + """ + Run the given coroutine function in an asynchronous event loop. + + The current thread must not be already running an event loop. + + :param func: a coroutine function + :param args: positional arguments to ``func`` + :param backend: name of the asynchronous event loop implementation – currently + either ``asyncio`` or ``trio`` + :param backend_options: keyword arguments to call the backend ``run()`` + implementation with (documented :ref:`here `) + :return: the return value of the coroutine function + :raises RuntimeError: if an asynchronous event loop is already running in this + thread + :raises LookupError: if the named backend is not found + + """ + try: + asynclib_name = sniffio.current_async_library() + except sniffio.AsyncLibraryNotFoundError: + pass + else: + raise RuntimeError(f"Already running {asynclib_name} in this thread") + + try: + async_backend = get_async_backend(backend) + except ImportError as exc: + raise LookupError(f"No such backend: {backend}") from exc + + token = None + if sniffio.current_async_library_cvar.get(None) is None: + # Since we're in control of the event loop, we can cache the name of the async + # library + token = sniffio.current_async_library_cvar.set(backend) + + try: + backend_options = backend_options or {} + return async_backend.run(func, args, {}, backend_options) + finally: + if token: + sniffio.current_async_library_cvar.reset(token) + + +async def sleep(delay: float) -> None: + """ + Pause the current task for the specified duration. + + :param delay: the duration, in seconds + + """ + return await get_async_backend().sleep(delay) + + +async def sleep_forever() -> None: + """ + Pause the current task until it's cancelled. + + This is a shortcut for ``sleep(math.inf)``. + + .. versionadded:: 3.1 + + """ + await sleep(math.inf) + + +async def sleep_until(deadline: float) -> None: + """ + Pause the current task until the given time. + + :param deadline: the absolute time to wake up at (according to the internal + monotonic clock of the event loop) + + .. versionadded:: 3.1 + + """ + now = current_time() + await sleep(max(deadline - now, 0)) + + +def current_time() -> float: + """ + Return the current value of the event loop's internal clock. + + :return: the clock value (seconds) + + """ + return get_async_backend().current_time() + + +def get_all_backends() -> tuple[str, ...]: + """Return a tuple of the names of all built-in backends.""" + return BACKENDS + + +def get_cancelled_exc_class() -> type[BaseException]: + """Return the current async library's cancellation exception class.""" + return get_async_backend().cancelled_exception_class() + + +# +# Private API +# + + +@contextmanager +def claim_worker_thread( + backend_class: type[AsyncBackend], token: object +) -> Generator[Any, None, None]: + threadlocals.current_async_backend = backend_class + threadlocals.current_token = token + try: + yield + finally: + del threadlocals.current_async_backend + del threadlocals.current_token + + +def get_async_backend(asynclib_name: str | None = None) -> type[AsyncBackend]: + if asynclib_name is None: + asynclib_name = sniffio.current_async_library() + + # We use our own dict instead of sys.modules to get the already imported back-end + # class because the appropriate modules in sys.modules could potentially be only + # partially initialized + try: + return loaded_backends[asynclib_name] + except KeyError: + module = import_module(f"anyio._backends._{asynclib_name}") + loaded_backends[asynclib_name] = module.backend_class + return module.backend_class diff --git a/venv/lib/python3.11/site-packages/anyio/_core/_exceptions.py b/venv/lib/python3.11/site-packages/anyio/_core/_exceptions.py new file mode 100644 index 00000000..6e3f8ccc --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/_core/_exceptions.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +import sys +from collections.abc import Generator + +if sys.version_info < (3, 11): + from exceptiongroup import BaseExceptionGroup + + +class BrokenResourceError(Exception): + """ + Raised when trying to use a resource that has been rendered unusable due to external + causes (e.g. a send stream whose peer has disconnected). + """ + + +class BrokenWorkerProcess(Exception): + """ + Raised by :func:`run_sync_in_process` if the worker process terminates abruptly or + otherwise misbehaves. + """ + + +class BusyResourceError(Exception): + """ + Raised when two tasks are trying to read from or write to the same resource + concurrently. + """ + + def __init__(self, action: str): + super().__init__(f"Another task is already {action} this resource") + + +class ClosedResourceError(Exception): + """Raised when trying to use a resource that has been closed.""" + + +class DelimiterNotFound(Exception): + """ + Raised during + :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the + maximum number of bytes has been read without the delimiter being found. + """ + + def __init__(self, max_bytes: int) -> None: + super().__init__( + f"The delimiter was not found among the first {max_bytes} bytes" + ) + + +class EndOfStream(Exception): + """ + Raised when trying to read from a stream that has been closed from the other end. + """ + + +class IncompleteRead(Exception): + """ + Raised during + :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_exactly` or + :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the + connection is closed before the requested amount of bytes has been read. + """ + + def __init__(self) -> None: + super().__init__( + "The stream was closed before the read operation could be completed" + ) + + +class TypedAttributeLookupError(LookupError): + """ + Raised by :meth:`~anyio.TypedAttributeProvider.extra` when the given typed attribute + is not found and no default value has been given. + """ + + +class WouldBlock(Exception): + """Raised by ``X_nowait`` functions if ``X()`` would block.""" + + +def iterate_exceptions( + exception: BaseException, +) -> Generator[BaseException, None, None]: + if isinstance(exception, BaseExceptionGroup): + for exc in exception.exceptions: + yield from iterate_exceptions(exc) + else: + yield exception diff --git a/venv/lib/python3.11/site-packages/anyio/_core/_fileio.py b/venv/lib/python3.11/site-packages/anyio/_core/_fileio.py new file mode 100644 index 00000000..53d3288c --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/_core/_fileio.py @@ -0,0 +1,674 @@ +from __future__ import annotations + +import os +import pathlib +import sys +from collections.abc import AsyncIterator, Callable, Iterable, Iterator, Sequence +from dataclasses import dataclass +from functools import partial +from os import PathLike +from typing import ( + IO, + TYPE_CHECKING, + Any, + AnyStr, + Final, + Generic, + overload, +) + +from .. import to_thread +from ..abc import AsyncResource + +if TYPE_CHECKING: + from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer +else: + ReadableBuffer = OpenBinaryMode = OpenTextMode = WriteableBuffer = object + + +class AsyncFile(AsyncResource, Generic[AnyStr]): + """ + An asynchronous file object. + + This class wraps a standard file object and provides async friendly versions of the + following blocking methods (where available on the original file object): + + * read + * read1 + * readline + * readlines + * readinto + * readinto1 + * write + * writelines + * truncate + * seek + * tell + * flush + + All other methods are directly passed through. + + This class supports the asynchronous context manager protocol which closes the + underlying file at the end of the context block. + + This class also supports asynchronous iteration:: + + async with await open_file(...) as f: + async for line in f: + print(line) + """ + + def __init__(self, fp: IO[AnyStr]) -> None: + self._fp: Any = fp + + def __getattr__(self, name: str) -> object: + return getattr(self._fp, name) + + @property + def wrapped(self) -> IO[AnyStr]: + """The wrapped file object.""" + return self._fp + + async def __aiter__(self) -> AsyncIterator[AnyStr]: + while True: + line = await self.readline() + if line: + yield line + else: + break + + async def aclose(self) -> None: + return await to_thread.run_sync(self._fp.close) + + async def read(self, size: int = -1) -> AnyStr: + return await to_thread.run_sync(self._fp.read, size) + + async def read1(self: AsyncFile[bytes], size: int = -1) -> bytes: + return await to_thread.run_sync(self._fp.read1, size) + + async def readline(self) -> AnyStr: + return await to_thread.run_sync(self._fp.readline) + + async def readlines(self) -> list[AnyStr]: + return await to_thread.run_sync(self._fp.readlines) + + async def readinto(self: AsyncFile[bytes], b: WriteableBuffer) -> bytes: + return await to_thread.run_sync(self._fp.readinto, b) + + async def readinto1(self: AsyncFile[bytes], b: WriteableBuffer) -> bytes: + return await to_thread.run_sync(self._fp.readinto1, b) + + @overload + async def write(self: AsyncFile[bytes], b: ReadableBuffer) -> int: ... + + @overload + async def write(self: AsyncFile[str], b: str) -> int: ... + + async def write(self, b: ReadableBuffer | str) -> int: + return await to_thread.run_sync(self._fp.write, b) + + @overload + async def writelines( + self: AsyncFile[bytes], lines: Iterable[ReadableBuffer] + ) -> None: ... + + @overload + async def writelines(self: AsyncFile[str], lines: Iterable[str]) -> None: ... + + async def writelines(self, lines: Iterable[ReadableBuffer] | Iterable[str]) -> None: + return await to_thread.run_sync(self._fp.writelines, lines) + + async def truncate(self, size: int | None = None) -> int: + return await to_thread.run_sync(self._fp.truncate, size) + + async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int: + return await to_thread.run_sync(self._fp.seek, offset, whence) + + async def tell(self) -> int: + return await to_thread.run_sync(self._fp.tell) + + async def flush(self) -> None: + return await to_thread.run_sync(self._fp.flush) + + +@overload +async def open_file( + file: str | PathLike[str] | int, + mode: OpenBinaryMode, + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + closefd: bool = ..., + opener: Callable[[str, int], int] | None = ..., +) -> AsyncFile[bytes]: ... + + +@overload +async def open_file( + file: str | PathLike[str] | int, + mode: OpenTextMode = ..., + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + closefd: bool = ..., + opener: Callable[[str, int], int] | None = ..., +) -> AsyncFile[str]: ... + + +async def open_file( + file: str | PathLike[str] | int, + mode: str = "r", + buffering: int = -1, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + closefd: bool = True, + opener: Callable[[str, int], int] | None = None, +) -> AsyncFile[Any]: + """ + Open a file asynchronously. + + The arguments are exactly the same as for the builtin :func:`open`. + + :return: an asynchronous file object + + """ + fp = await to_thread.run_sync( + open, file, mode, buffering, encoding, errors, newline, closefd, opener + ) + return AsyncFile(fp) + + +def wrap_file(file: IO[AnyStr]) -> AsyncFile[AnyStr]: + """ + Wrap an existing file as an asynchronous file. + + :param file: an existing file-like object + :return: an asynchronous file object + + """ + return AsyncFile(file) + + +@dataclass(eq=False) +class _PathIterator(AsyncIterator["Path"]): + iterator: Iterator[PathLike[str]] + + async def __anext__(self) -> Path: + nextval = await to_thread.run_sync( + next, self.iterator, None, abandon_on_cancel=True + ) + if nextval is None: + raise StopAsyncIteration from None + + return Path(nextval) + + +class Path: + """ + An asynchronous version of :class:`pathlib.Path`. + + This class cannot be substituted for :class:`pathlib.Path` or + :class:`pathlib.PurePath`, but it is compatible with the :class:`os.PathLike` + interface. + + It implements the Python 3.10 version of :class:`pathlib.Path` interface, except for + the deprecated :meth:`~pathlib.Path.link_to` method. + + Some methods may be unavailable or have limited functionality, based on the Python + version: + + * :meth:`~pathlib.Path.from_uri` (available on Python 3.13 or later) + * :meth:`~pathlib.Path.full_match` (available on Python 3.13 or later) + * :meth:`~pathlib.Path.is_junction` (available on Python 3.12 or later) + * :meth:`~pathlib.Path.match` (the ``case_sensitive`` paramater is only available on + Python 3.13 or later) + * :meth:`~pathlib.Path.relative_to` (the ``walk_up`` parameter is only available on + Python 3.12 or later) + * :meth:`~pathlib.Path.walk` (available on Python 3.12 or later) + + Any methods that do disk I/O need to be awaited on. These methods are: + + * :meth:`~pathlib.Path.absolute` + * :meth:`~pathlib.Path.chmod` + * :meth:`~pathlib.Path.cwd` + * :meth:`~pathlib.Path.exists` + * :meth:`~pathlib.Path.expanduser` + * :meth:`~pathlib.Path.group` + * :meth:`~pathlib.Path.hardlink_to` + * :meth:`~pathlib.Path.home` + * :meth:`~pathlib.Path.is_block_device` + * :meth:`~pathlib.Path.is_char_device` + * :meth:`~pathlib.Path.is_dir` + * :meth:`~pathlib.Path.is_fifo` + * :meth:`~pathlib.Path.is_file` + * :meth:`~pathlib.Path.is_junction` + * :meth:`~pathlib.Path.is_mount` + * :meth:`~pathlib.Path.is_socket` + * :meth:`~pathlib.Path.is_symlink` + * :meth:`~pathlib.Path.lchmod` + * :meth:`~pathlib.Path.lstat` + * :meth:`~pathlib.Path.mkdir` + * :meth:`~pathlib.Path.open` + * :meth:`~pathlib.Path.owner` + * :meth:`~pathlib.Path.read_bytes` + * :meth:`~pathlib.Path.read_text` + * :meth:`~pathlib.Path.readlink` + * :meth:`~pathlib.Path.rename` + * :meth:`~pathlib.Path.replace` + * :meth:`~pathlib.Path.resolve` + * :meth:`~pathlib.Path.rmdir` + * :meth:`~pathlib.Path.samefile` + * :meth:`~pathlib.Path.stat` + * :meth:`~pathlib.Path.symlink_to` + * :meth:`~pathlib.Path.touch` + * :meth:`~pathlib.Path.unlink` + * :meth:`~pathlib.Path.walk` + * :meth:`~pathlib.Path.write_bytes` + * :meth:`~pathlib.Path.write_text` + + Additionally, the following methods return an async iterator yielding + :class:`~.Path` objects: + + * :meth:`~pathlib.Path.glob` + * :meth:`~pathlib.Path.iterdir` + * :meth:`~pathlib.Path.rglob` + """ + + __slots__ = "_path", "__weakref__" + + __weakref__: Any + + def __init__(self, *args: str | PathLike[str]) -> None: + self._path: Final[pathlib.Path] = pathlib.Path(*args) + + def __fspath__(self) -> str: + return self._path.__fspath__() + + def __str__(self) -> str: + return self._path.__str__() + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.as_posix()!r})" + + def __bytes__(self) -> bytes: + return self._path.__bytes__() + + def __hash__(self) -> int: + return self._path.__hash__() + + def __eq__(self, other: object) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__eq__(target) + + def __lt__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__lt__(target) + + def __le__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__le__(target) + + def __gt__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__gt__(target) + + def __ge__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__ge__(target) + + def __truediv__(self, other: str | PathLike[str]) -> Path: + return Path(self._path / other) + + def __rtruediv__(self, other: str | PathLike[str]) -> Path: + return Path(other) / self + + @property + def parts(self) -> tuple[str, ...]: + return self._path.parts + + @property + def drive(self) -> str: + return self._path.drive + + @property + def root(self) -> str: + return self._path.root + + @property + def anchor(self) -> str: + return self._path.anchor + + @property + def parents(self) -> Sequence[Path]: + return tuple(Path(p) for p in self._path.parents) + + @property + def parent(self) -> Path: + return Path(self._path.parent) + + @property + def name(self) -> str: + return self._path.name + + @property + def suffix(self) -> str: + return self._path.suffix + + @property + def suffixes(self) -> list[str]: + return self._path.suffixes + + @property + def stem(self) -> str: + return self._path.stem + + async def absolute(self) -> Path: + path = await to_thread.run_sync(self._path.absolute) + return Path(path) + + def as_posix(self) -> str: + return self._path.as_posix() + + def as_uri(self) -> str: + return self._path.as_uri() + + if sys.version_info >= (3, 13): + parser = pathlib.Path.parser + + @classmethod + def from_uri(cls, uri: str) -> Path: + return Path(pathlib.Path.from_uri(uri)) + + def full_match( + self, path_pattern: str, *, case_sensitive: bool | None = None + ) -> bool: + return self._path.full_match(path_pattern, case_sensitive=case_sensitive) + + def match( + self, path_pattern: str, *, case_sensitive: bool | None = None + ) -> bool: + return self._path.match(path_pattern, case_sensitive=case_sensitive) + else: + + def match(self, path_pattern: str) -> bool: + return self._path.match(path_pattern) + + def is_relative_to(self, other: str | PathLike[str]) -> bool: + try: + self.relative_to(other) + return True + except ValueError: + return False + + async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None: + func = partial(os.chmod, follow_symlinks=follow_symlinks) + return await to_thread.run_sync(func, self._path, mode) + + @classmethod + async def cwd(cls) -> Path: + path = await to_thread.run_sync(pathlib.Path.cwd) + return cls(path) + + async def exists(self) -> bool: + return await to_thread.run_sync(self._path.exists, abandon_on_cancel=True) + + async def expanduser(self) -> Path: + return Path( + await to_thread.run_sync(self._path.expanduser, abandon_on_cancel=True) + ) + + def glob(self, pattern: str) -> AsyncIterator[Path]: + gen = self._path.glob(pattern) + return _PathIterator(gen) + + async def group(self) -> str: + return await to_thread.run_sync(self._path.group, abandon_on_cancel=True) + + async def hardlink_to( + self, target: str | bytes | PathLike[str] | PathLike[bytes] + ) -> None: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(os.link, target, self) + + @classmethod + async def home(cls) -> Path: + home_path = await to_thread.run_sync(pathlib.Path.home) + return cls(home_path) + + def is_absolute(self) -> bool: + return self._path.is_absolute() + + async def is_block_device(self) -> bool: + return await to_thread.run_sync( + self._path.is_block_device, abandon_on_cancel=True + ) + + async def is_char_device(self) -> bool: + return await to_thread.run_sync( + self._path.is_char_device, abandon_on_cancel=True + ) + + async def is_dir(self) -> bool: + return await to_thread.run_sync(self._path.is_dir, abandon_on_cancel=True) + + async def is_fifo(self) -> bool: + return await to_thread.run_sync(self._path.is_fifo, abandon_on_cancel=True) + + async def is_file(self) -> bool: + return await to_thread.run_sync(self._path.is_file, abandon_on_cancel=True) + + if sys.version_info >= (3, 12): + + async def is_junction(self) -> bool: + return await to_thread.run_sync(self._path.is_junction) + + async def is_mount(self) -> bool: + return await to_thread.run_sync( + os.path.ismount, self._path, abandon_on_cancel=True + ) + + def is_reserved(self) -> bool: + return self._path.is_reserved() + + async def is_socket(self) -> bool: + return await to_thread.run_sync(self._path.is_socket, abandon_on_cancel=True) + + async def is_symlink(self) -> bool: + return await to_thread.run_sync(self._path.is_symlink, abandon_on_cancel=True) + + def iterdir(self) -> AsyncIterator[Path]: + gen = self._path.iterdir() + return _PathIterator(gen) + + def joinpath(self, *args: str | PathLike[str]) -> Path: + return Path(self._path.joinpath(*args)) + + async def lchmod(self, mode: int) -> None: + await to_thread.run_sync(self._path.lchmod, mode) + + async def lstat(self) -> os.stat_result: + return await to_thread.run_sync(self._path.lstat, abandon_on_cancel=True) + + async def mkdir( + self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False + ) -> None: + await to_thread.run_sync(self._path.mkdir, mode, parents, exist_ok) + + @overload + async def open( + self, + mode: OpenBinaryMode, + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + ) -> AsyncFile[bytes]: ... + + @overload + async def open( + self, + mode: OpenTextMode = ..., + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + ) -> AsyncFile[str]: ... + + async def open( + self, + mode: str = "r", + buffering: int = -1, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + ) -> AsyncFile[Any]: + fp = await to_thread.run_sync( + self._path.open, mode, buffering, encoding, errors, newline + ) + return AsyncFile(fp) + + async def owner(self) -> str: + return await to_thread.run_sync(self._path.owner, abandon_on_cancel=True) + + async def read_bytes(self) -> bytes: + return await to_thread.run_sync(self._path.read_bytes) + + async def read_text( + self, encoding: str | None = None, errors: str | None = None + ) -> str: + return await to_thread.run_sync(self._path.read_text, encoding, errors) + + if sys.version_info >= (3, 12): + + def relative_to( + self, *other: str | PathLike[str], walk_up: bool = False + ) -> Path: + return Path(self._path.relative_to(*other, walk_up=walk_up)) + + else: + + def relative_to(self, *other: str | PathLike[str]) -> Path: + return Path(self._path.relative_to(*other)) + + async def readlink(self) -> Path: + target = await to_thread.run_sync(os.readlink, self._path) + return Path(target) + + async def rename(self, target: str | pathlib.PurePath | Path) -> Path: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(self._path.rename, target) + return Path(target) + + async def replace(self, target: str | pathlib.PurePath | Path) -> Path: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(self._path.replace, target) + return Path(target) + + async def resolve(self, strict: bool = False) -> Path: + func = partial(self._path.resolve, strict=strict) + return Path(await to_thread.run_sync(func, abandon_on_cancel=True)) + + def rglob(self, pattern: str) -> AsyncIterator[Path]: + gen = self._path.rglob(pattern) + return _PathIterator(gen) + + async def rmdir(self) -> None: + await to_thread.run_sync(self._path.rmdir) + + async def samefile(self, other_path: str | PathLike[str]) -> bool: + if isinstance(other_path, Path): + other_path = other_path._path + + return await to_thread.run_sync( + self._path.samefile, other_path, abandon_on_cancel=True + ) + + async def stat(self, *, follow_symlinks: bool = True) -> os.stat_result: + func = partial(os.stat, follow_symlinks=follow_symlinks) + return await to_thread.run_sync(func, self._path, abandon_on_cancel=True) + + async def symlink_to( + self, + target: str | bytes | PathLike[str] | PathLike[bytes], + target_is_directory: bool = False, + ) -> None: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(self._path.symlink_to, target, target_is_directory) + + async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None: + await to_thread.run_sync(self._path.touch, mode, exist_ok) + + async def unlink(self, missing_ok: bool = False) -> None: + try: + await to_thread.run_sync(self._path.unlink) + except FileNotFoundError: + if not missing_ok: + raise + + if sys.version_info >= (3, 12): + + async def walk( + self, + top_down: bool = True, + on_error: Callable[[OSError], object] | None = None, + follow_symlinks: bool = False, + ) -> AsyncIterator[tuple[Path, list[str], list[str]]]: + def get_next_value() -> tuple[pathlib.Path, list[str], list[str]] | None: + try: + return next(gen) + except StopIteration: + return None + + gen = self._path.walk(top_down, on_error, follow_symlinks) + while True: + value = await to_thread.run_sync(get_next_value) + if value is None: + return + + root, dirs, paths = value + yield Path(root), dirs, paths + + def with_name(self, name: str) -> Path: + return Path(self._path.with_name(name)) + + def with_stem(self, stem: str) -> Path: + return Path(self._path.with_name(stem + self._path.suffix)) + + def with_suffix(self, suffix: str) -> Path: + return Path(self._path.with_suffix(suffix)) + + def with_segments(self, *pathsegments: str | PathLike[str]) -> Path: + return Path(*pathsegments) + + async def write_bytes(self, data: bytes) -> int: + return await to_thread.run_sync(self._path.write_bytes, data) + + async def write_text( + self, + data: str, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + ) -> int: + # Path.write_text() does not support the "newline" parameter before Python 3.10 + def sync_write_text() -> int: + with self._path.open( + "w", encoding=encoding, errors=errors, newline=newline + ) as fp: + return fp.write(data) + + return await to_thread.run_sync(sync_write_text) + + +PathLike.register(Path) diff --git a/venv/lib/python3.11/site-packages/anyio/_core/_resources.py b/venv/lib/python3.11/site-packages/anyio/_core/_resources.py new file mode 100644 index 00000000..b9a5344a --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/_core/_resources.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from ..abc import AsyncResource +from ._tasks import CancelScope + + +async def aclose_forcefully(resource: AsyncResource) -> None: + """ + Close an asynchronous resource in a cancelled scope. + + Doing this closes the resource without waiting on anything. + + :param resource: the resource to close + + """ + with CancelScope() as scope: + scope.cancel() + await resource.aclose() diff --git a/venv/lib/python3.11/site-packages/anyio/_core/_signals.py b/venv/lib/python3.11/site-packages/anyio/_core/_signals.py new file mode 100644 index 00000000..f3451d30 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/_core/_signals.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +from collections.abc import AsyncIterator +from contextlib import AbstractContextManager +from signal import Signals + +from ._eventloop import get_async_backend + + +def open_signal_receiver( + *signals: Signals, +) -> AbstractContextManager[AsyncIterator[Signals]]: + """ + Start receiving operating system signals. + + :param signals: signals to receive (e.g. ``signal.SIGINT``) + :return: an asynchronous context manager for an asynchronous iterator which yields + signal numbers + + .. warning:: Windows does not support signals natively so it is best to avoid + relying on this in cross-platform applications. + + .. warning:: On asyncio, this permanently replaces any previous signal handler for + the given signals, as set via :meth:`~asyncio.loop.add_signal_handler`. + + """ + return get_async_backend().open_signal_receiver(*signals) diff --git a/venv/lib/python3.11/site-packages/anyio/_core/_sockets.py b/venv/lib/python3.11/site-packages/anyio/_core/_sockets.py new file mode 100644 index 00000000..6070c647 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/_core/_sockets.py @@ -0,0 +1,718 @@ +from __future__ import annotations + +import errno +import os +import socket +import ssl +import stat +import sys +from collections.abc import Awaitable +from ipaddress import IPv6Address, ip_address +from os import PathLike, chmod +from socket import AddressFamily, SocketKind +from typing import Any, Literal, cast, overload + +from .. import to_thread +from ..abc import ( + ConnectedUDPSocket, + ConnectedUNIXDatagramSocket, + IPAddressType, + IPSockAddrType, + SocketListener, + SocketStream, + UDPSocket, + UNIXDatagramSocket, + UNIXSocketStream, +) +from ..streams.stapled import MultiListener +from ..streams.tls import TLSStream +from ._eventloop import get_async_backend +from ._resources import aclose_forcefully +from ._synchronization import Event +from ._tasks import create_task_group, move_on_after + +if sys.version_info < (3, 11): + from exceptiongroup import ExceptionGroup + +IPPROTO_IPV6 = getattr(socket, "IPPROTO_IPV6", 41) # https://bugs.python.org/issue29515 + +AnyIPAddressFamily = Literal[ + AddressFamily.AF_UNSPEC, AddressFamily.AF_INET, AddressFamily.AF_INET6 +] +IPAddressFamily = Literal[AddressFamily.AF_INET, AddressFamily.AF_INET6] + + +# tls_hostname given +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + ssl_context: ssl.SSLContext | None = ..., + tls_standard_compatible: bool = ..., + tls_hostname: str, + happy_eyeballs_delay: float = ..., +) -> TLSStream: ... + + +# ssl_context given +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + ssl_context: ssl.SSLContext, + tls_standard_compatible: bool = ..., + tls_hostname: str | None = ..., + happy_eyeballs_delay: float = ..., +) -> TLSStream: ... + + +# tls=True +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + tls: Literal[True], + ssl_context: ssl.SSLContext | None = ..., + tls_standard_compatible: bool = ..., + tls_hostname: str | None = ..., + happy_eyeballs_delay: float = ..., +) -> TLSStream: ... + + +# tls=False +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + tls: Literal[False], + ssl_context: ssl.SSLContext | None = ..., + tls_standard_compatible: bool = ..., + tls_hostname: str | None = ..., + happy_eyeballs_delay: float = ..., +) -> SocketStream: ... + + +# No TLS arguments +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + happy_eyeballs_delay: float = ..., +) -> SocketStream: ... + + +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = None, + tls: bool = False, + ssl_context: ssl.SSLContext | None = None, + tls_standard_compatible: bool = True, + tls_hostname: str | None = None, + happy_eyeballs_delay: float = 0.25, +) -> SocketStream | TLSStream: + """ + Connect to a host using the TCP protocol. + + This function implements the stateless version of the Happy Eyeballs algorithm (RFC + 6555). If ``remote_host`` is a host name that resolves to multiple IP addresses, + each one is tried until one connection attempt succeeds. If the first attempt does + not connected within 250 milliseconds, a second attempt is started using the next + address in the list, and so on. On IPv6 enabled systems, an IPv6 address (if + available) is tried first. + + When the connection has been established, a TLS handshake will be done if either + ``ssl_context`` or ``tls_hostname`` is not ``None``, or if ``tls`` is ``True``. + + :param remote_host: the IP address or host name to connect to + :param remote_port: port on the target host to connect to + :param local_host: the interface address or name to bind the socket to before + connecting + :param tls: ``True`` to do a TLS handshake with the connected stream and return a + :class:`~anyio.streams.tls.TLSStream` instead + :param ssl_context: the SSL context object to use (if omitted, a default context is + created) + :param tls_standard_compatible: If ``True``, performs the TLS shutdown handshake + before closing the stream and requires that the server does this as well. + Otherwise, :exc:`~ssl.SSLEOFError` may be raised during reads from the stream. + Some protocols, such as HTTP, require this option to be ``False``. + See :meth:`~ssl.SSLContext.wrap_socket` for details. + :param tls_hostname: host name to check the server certificate against (defaults to + the value of ``remote_host``) + :param happy_eyeballs_delay: delay (in seconds) before starting the next connection + attempt + :return: a socket stream object if no TLS handshake was done, otherwise a TLS stream + :raises OSError: if the connection attempt fails + + """ + # Placed here due to https://github.com/python/mypy/issues/7057 + connected_stream: SocketStream | None = None + + async def try_connect(remote_host: str, event: Event) -> None: + nonlocal connected_stream + try: + stream = await asynclib.connect_tcp(remote_host, remote_port, local_address) + except OSError as exc: + oserrors.append(exc) + return + else: + if connected_stream is None: + connected_stream = stream + tg.cancel_scope.cancel() + else: + await stream.aclose() + finally: + event.set() + + asynclib = get_async_backend() + local_address: IPSockAddrType | None = None + family = socket.AF_UNSPEC + if local_host: + gai_res = await getaddrinfo(str(local_host), None) + family, *_, local_address = gai_res[0] + + target_host = str(remote_host) + try: + addr_obj = ip_address(remote_host) + except ValueError: + # getaddrinfo() will raise an exception if name resolution fails + gai_res = await getaddrinfo( + target_host, remote_port, family=family, type=socket.SOCK_STREAM + ) + + # Organize the list so that the first address is an IPv6 address (if available) + # and the second one is an IPv4 addresses. The rest can be in whatever order. + v6_found = v4_found = False + target_addrs: list[tuple[socket.AddressFamily, str]] = [] + for af, *rest, sa in gai_res: + if af == socket.AF_INET6 and not v6_found: + v6_found = True + target_addrs.insert(0, (af, sa[0])) + elif af == socket.AF_INET and not v4_found and v6_found: + v4_found = True + target_addrs.insert(1, (af, sa[0])) + else: + target_addrs.append((af, sa[0])) + else: + if isinstance(addr_obj, IPv6Address): + target_addrs = [(socket.AF_INET6, addr_obj.compressed)] + else: + target_addrs = [(socket.AF_INET, addr_obj.compressed)] + + oserrors: list[OSError] = [] + async with create_task_group() as tg: + for i, (af, addr) in enumerate(target_addrs): + event = Event() + tg.start_soon(try_connect, addr, event) + with move_on_after(happy_eyeballs_delay): + await event.wait() + + if connected_stream is None: + cause = ( + oserrors[0] + if len(oserrors) == 1 + else ExceptionGroup("multiple connection attempts failed", oserrors) + ) + raise OSError("All connection attempts failed") from cause + + if tls or tls_hostname or ssl_context: + try: + return await TLSStream.wrap( + connected_stream, + server_side=False, + hostname=tls_hostname or str(remote_host), + ssl_context=ssl_context, + standard_compatible=tls_standard_compatible, + ) + except BaseException: + await aclose_forcefully(connected_stream) + raise + + return connected_stream + + +async def connect_unix(path: str | bytes | PathLike[Any]) -> UNIXSocketStream: + """ + Connect to the given UNIX socket. + + Not available on Windows. + + :param path: path to the socket + :return: a socket stream object + + """ + path = os.fspath(path) + return await get_async_backend().connect_unix(path) + + +async def create_tcp_listener( + *, + local_host: IPAddressType | None = None, + local_port: int = 0, + family: AnyIPAddressFamily = socket.AddressFamily.AF_UNSPEC, + backlog: int = 65536, + reuse_port: bool = False, +) -> MultiListener[SocketStream]: + """ + Create a TCP socket listener. + + :param local_port: port number to listen on + :param local_host: IP address of the interface to listen on. If omitted, listen on + all IPv4 and IPv6 interfaces. To listen on all interfaces on a specific address + family, use ``0.0.0.0`` for IPv4 or ``::`` for IPv6. + :param family: address family (used if ``local_host`` was omitted) + :param backlog: maximum number of queued incoming connections (up to a maximum of + 2**16, or 65536) + :param reuse_port: ``True`` to allow multiple sockets to bind to the same + address/port (not supported on Windows) + :return: a list of listener objects + + """ + asynclib = get_async_backend() + backlog = min(backlog, 65536) + local_host = str(local_host) if local_host is not None else None + gai_res = await getaddrinfo( + local_host, + local_port, + family=family, + type=socket.SocketKind.SOCK_STREAM if sys.platform == "win32" else 0, + flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, + ) + listeners: list[SocketListener] = [] + try: + # The set() is here to work around a glibc bug: + # https://sourceware.org/bugzilla/show_bug.cgi?id=14969 + sockaddr: tuple[str, int] | tuple[str, int, int, int] + for fam, kind, *_, sockaddr in sorted(set(gai_res)): + # Workaround for an uvloop bug where we don't get the correct scope ID for + # IPv6 link-local addresses when passing type=socket.SOCK_STREAM to + # getaddrinfo(): https://github.com/MagicStack/uvloop/issues/539 + if sys.platform != "win32" and kind is not SocketKind.SOCK_STREAM: + continue + + raw_socket = socket.socket(fam) + raw_socket.setblocking(False) + + # For Windows, enable exclusive address use. For others, enable address + # reuse. + if sys.platform == "win32": + raw_socket.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) + else: + raw_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + if reuse_port: + raw_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + + # If only IPv6 was requested, disable dual stack operation + if fam == socket.AF_INET6: + raw_socket.setsockopt(IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) + + # Workaround for #554 + if "%" in sockaddr[0]: + addr, scope_id = sockaddr[0].split("%", 1) + sockaddr = (addr, sockaddr[1], 0, int(scope_id)) + + raw_socket.bind(sockaddr) + raw_socket.listen(backlog) + listener = asynclib.create_tcp_listener(raw_socket) + listeners.append(listener) + except BaseException: + for listener in listeners: + await listener.aclose() + + raise + + return MultiListener(listeners) + + +async def create_unix_listener( + path: str | bytes | PathLike[Any], + *, + mode: int | None = None, + backlog: int = 65536, +) -> SocketListener: + """ + Create a UNIX socket listener. + + Not available on Windows. + + :param path: path of the socket + :param mode: permissions to set on the socket + :param backlog: maximum number of queued incoming connections (up to a maximum of + 2**16, or 65536) + :return: a listener object + + .. versionchanged:: 3.0 + If a socket already exists on the file system in the given path, it will be + removed first. + + """ + backlog = min(backlog, 65536) + raw_socket = await setup_unix_local_socket(path, mode, socket.SOCK_STREAM) + try: + raw_socket.listen(backlog) + return get_async_backend().create_unix_listener(raw_socket) + except BaseException: + raw_socket.close() + raise + + +async def create_udp_socket( + family: AnyIPAddressFamily = AddressFamily.AF_UNSPEC, + *, + local_host: IPAddressType | None = None, + local_port: int = 0, + reuse_port: bool = False, +) -> UDPSocket: + """ + Create a UDP socket. + + If ``port`` has been given, the socket will be bound to this port on the local + machine, making this socket suitable for providing UDP based services. + + :param family: address family (``AF_INET`` or ``AF_INET6``) – automatically + determined from ``local_host`` if omitted + :param local_host: IP address or host name of the local interface to bind to + :param local_port: local port to bind to + :param reuse_port: ``True`` to allow multiple sockets to bind to the same + address/port (not supported on Windows) + :return: a UDP socket + + """ + if family is AddressFamily.AF_UNSPEC and not local_host: + raise ValueError('Either "family" or "local_host" must be given') + + if local_host: + gai_res = await getaddrinfo( + str(local_host), + local_port, + family=family, + type=socket.SOCK_DGRAM, + flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, + ) + family = cast(AnyIPAddressFamily, gai_res[0][0]) + local_address = gai_res[0][-1] + elif family is AddressFamily.AF_INET6: + local_address = ("::", 0) + else: + local_address = ("0.0.0.0", 0) + + sock = await get_async_backend().create_udp_socket( + family, local_address, None, reuse_port + ) + return cast(UDPSocket, sock) + + +async def create_connected_udp_socket( + remote_host: IPAddressType, + remote_port: int, + *, + family: AnyIPAddressFamily = AddressFamily.AF_UNSPEC, + local_host: IPAddressType | None = None, + local_port: int = 0, + reuse_port: bool = False, +) -> ConnectedUDPSocket: + """ + Create a connected UDP socket. + + Connected UDP sockets can only communicate with the specified remote host/port, an + any packets sent from other sources are dropped. + + :param remote_host: remote host to set as the default target + :param remote_port: port on the remote host to set as the default target + :param family: address family (``AF_INET`` or ``AF_INET6``) – automatically + determined from ``local_host`` or ``remote_host`` if omitted + :param local_host: IP address or host name of the local interface to bind to + :param local_port: local port to bind to + :param reuse_port: ``True`` to allow multiple sockets to bind to the same + address/port (not supported on Windows) + :return: a connected UDP socket + + """ + local_address = None + if local_host: + gai_res = await getaddrinfo( + str(local_host), + local_port, + family=family, + type=socket.SOCK_DGRAM, + flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, + ) + family = cast(AnyIPAddressFamily, gai_res[0][0]) + local_address = gai_res[0][-1] + + gai_res = await getaddrinfo( + str(remote_host), remote_port, family=family, type=socket.SOCK_DGRAM + ) + family = cast(AnyIPAddressFamily, gai_res[0][0]) + remote_address = gai_res[0][-1] + + sock = await get_async_backend().create_udp_socket( + family, local_address, remote_address, reuse_port + ) + return cast(ConnectedUDPSocket, sock) + + +async def create_unix_datagram_socket( + *, + local_path: None | str | bytes | PathLike[Any] = None, + local_mode: int | None = None, +) -> UNIXDatagramSocket: + """ + Create a UNIX datagram socket. + + Not available on Windows. + + If ``local_path`` has been given, the socket will be bound to this path, making this + socket suitable for receiving datagrams from other processes. Other processes can + send datagrams to this socket only if ``local_path`` is set. + + If a socket already exists on the file system in the ``local_path``, it will be + removed first. + + :param local_path: the path on which to bind to + :param local_mode: permissions to set on the local socket + :return: a UNIX datagram socket + + """ + raw_socket = await setup_unix_local_socket( + local_path, local_mode, socket.SOCK_DGRAM + ) + return await get_async_backend().create_unix_datagram_socket(raw_socket, None) + + +async def create_connected_unix_datagram_socket( + remote_path: str | bytes | PathLike[Any], + *, + local_path: None | str | bytes | PathLike[Any] = None, + local_mode: int | None = None, +) -> ConnectedUNIXDatagramSocket: + """ + Create a connected UNIX datagram socket. + + Connected datagram sockets can only communicate with the specified remote path. + + If ``local_path`` has been given, the socket will be bound to this path, making + this socket suitable for receiving datagrams from other processes. Other processes + can send datagrams to this socket only if ``local_path`` is set. + + If a socket already exists on the file system in the ``local_path``, it will be + removed first. + + :param remote_path: the path to set as the default target + :param local_path: the path on which to bind to + :param local_mode: permissions to set on the local socket + :return: a connected UNIX datagram socket + + """ + remote_path = os.fspath(remote_path) + raw_socket = await setup_unix_local_socket( + local_path, local_mode, socket.SOCK_DGRAM + ) + return await get_async_backend().create_unix_datagram_socket( + raw_socket, remote_path + ) + + +async def getaddrinfo( + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, +) -> list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int]]]: + """ + Look up a numeric IP address given a host name. + + Internationalized domain names are translated according to the (non-transitional) + IDNA 2008 standard. + + .. note:: 4-tuple IPv6 socket addresses are automatically converted to 2-tuples of + (host, port), unlike what :func:`socket.getaddrinfo` does. + + :param host: host name + :param port: port number + :param family: socket family (`'AF_INET``, ...) + :param type: socket type (``SOCK_STREAM``, ...) + :param proto: protocol number + :param flags: flags to pass to upstream ``getaddrinfo()`` + :return: list of tuples containing (family, type, proto, canonname, sockaddr) + + .. seealso:: :func:`socket.getaddrinfo` + + """ + # Handle unicode hostnames + if isinstance(host, str): + try: + encoded_host: bytes | None = host.encode("ascii") + except UnicodeEncodeError: + import idna + + encoded_host = idna.encode(host, uts46=True) + else: + encoded_host = host + + gai_res = await get_async_backend().getaddrinfo( + encoded_host, port, family=family, type=type, proto=proto, flags=flags + ) + return [ + (family, type, proto, canonname, convert_ipv6_sockaddr(sockaddr)) + for family, type, proto, canonname, sockaddr in gai_res + ] + + +def getnameinfo(sockaddr: IPSockAddrType, flags: int = 0) -> Awaitable[tuple[str, str]]: + """ + Look up the host name of an IP address. + + :param sockaddr: socket address (e.g. (ipaddress, port) for IPv4) + :param flags: flags to pass to upstream ``getnameinfo()`` + :return: a tuple of (host name, service name) + + .. seealso:: :func:`socket.getnameinfo` + + """ + return get_async_backend().getnameinfo(sockaddr, flags) + + +def wait_socket_readable(sock: socket.socket) -> Awaitable[None]: + """ + Wait until the given socket has data to be read. + + This does **NOT** work on Windows when using the asyncio backend with a proactor + event loop (default on py3.8+). + + .. warning:: Only use this on raw sockets that have not been wrapped by any higher + level constructs like socket streams! + + :param sock: a socket object + :raises ~anyio.ClosedResourceError: if the socket was closed while waiting for the + socket to become readable + :raises ~anyio.BusyResourceError: if another task is already waiting for the socket + to become readable + + """ + return get_async_backend().wait_socket_readable(sock) + + +def wait_socket_writable(sock: socket.socket) -> Awaitable[None]: + """ + Wait until the given socket can be written to. + + This does **NOT** work on Windows when using the asyncio backend with a proactor + event loop (default on py3.8+). + + .. warning:: Only use this on raw sockets that have not been wrapped by any higher + level constructs like socket streams! + + :param sock: a socket object + :raises ~anyio.ClosedResourceError: if the socket was closed while waiting for the + socket to become writable + :raises ~anyio.BusyResourceError: if another task is already waiting for the socket + to become writable + + """ + return get_async_backend().wait_socket_writable(sock) + + +# +# Private API +# + + +def convert_ipv6_sockaddr( + sockaddr: tuple[str, int, int, int] | tuple[str, int], +) -> tuple[str, int]: + """ + Convert a 4-tuple IPv6 socket address to a 2-tuple (address, port) format. + + If the scope ID is nonzero, it is added to the address, separated with ``%``. + Otherwise the flow id and scope id are simply cut off from the tuple. + Any other kinds of socket addresses are returned as-is. + + :param sockaddr: the result of :meth:`~socket.socket.getsockname` + :return: the converted socket address + + """ + # This is more complicated than it should be because of MyPy + if isinstance(sockaddr, tuple) and len(sockaddr) == 4: + host, port, flowinfo, scope_id = sockaddr + if scope_id: + # PyPy (as of v7.3.11) leaves the interface name in the result, so + # we discard it and only get the scope ID from the end + # (https://foss.heptapod.net/pypy/pypy/-/issues/3938) + host = host.split("%")[0] + + # Add scope_id to the address + return f"{host}%{scope_id}", port + else: + return host, port + else: + return sockaddr + + +async def setup_unix_local_socket( + path: None | str | bytes | PathLike[Any], + mode: int | None, + socktype: int, +) -> socket.socket: + """ + Create a UNIX local socket object, deleting the socket at the given path if it + exists. + + Not available on Windows. + + :param path: path of the socket + :param mode: permissions to set on the socket + :param socktype: socket.SOCK_STREAM or socket.SOCK_DGRAM + + """ + path_str: str | None + if path is not None: + path_str = os.fsdecode(path) + + # Linux abstract namespace sockets aren't backed by a concrete file so skip stat call + if not path_str.startswith("\0"): + # Copied from pathlib... + try: + stat_result = os.stat(path) + except OSError as e: + if e.errno not in ( + errno.ENOENT, + errno.ENOTDIR, + errno.EBADF, + errno.ELOOP, + ): + raise + else: + if stat.S_ISSOCK(stat_result.st_mode): + os.unlink(path) + else: + path_str = None + + raw_socket = socket.socket(socket.AF_UNIX, socktype) + raw_socket.setblocking(False) + + if path_str is not None: + try: + await to_thread.run_sync(raw_socket.bind, path_str, abandon_on_cancel=True) + if mode is not None: + await to_thread.run_sync(chmod, path_str, mode, abandon_on_cancel=True) + except BaseException: + raw_socket.close() + raise + + return raw_socket diff --git a/venv/lib/python3.11/site-packages/anyio/_core/_streams.py b/venv/lib/python3.11/site-packages/anyio/_core/_streams.py new file mode 100644 index 00000000..6a9814e5 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/_core/_streams.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +import math +from typing import TypeVar +from warnings import warn + +from ..streams.memory import ( + MemoryObjectReceiveStream, + MemoryObjectSendStream, + MemoryObjectStreamState, +) + +T_Item = TypeVar("T_Item") + + +class create_memory_object_stream( + tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]], +): + """ + Create a memory object stream. + + The stream's item type can be annotated like + :func:`create_memory_object_stream[T_Item]`. + + :param max_buffer_size: number of items held in the buffer until ``send()`` starts + blocking + :param item_type: old way of marking the streams with the right generic type for + static typing (does nothing on AnyIO 4) + + .. deprecated:: 4.0 + Use ``create_memory_object_stream[YourItemType](...)`` instead. + :return: a tuple of (send stream, receive stream) + + """ + + def __new__( # type: ignore[misc] + cls, max_buffer_size: float = 0, item_type: object = None + ) -> tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]]: + if max_buffer_size != math.inf and not isinstance(max_buffer_size, int): + raise ValueError("max_buffer_size must be either an integer or math.inf") + if max_buffer_size < 0: + raise ValueError("max_buffer_size cannot be negative") + if item_type is not None: + warn( + "The item_type argument has been deprecated in AnyIO 4.0. " + "Use create_memory_object_stream[YourItemType](...) instead.", + DeprecationWarning, + stacklevel=2, + ) + + state = MemoryObjectStreamState[T_Item](max_buffer_size) + return (MemoryObjectSendStream(state), MemoryObjectReceiveStream(state)) diff --git a/venv/lib/python3.11/site-packages/anyio/_core/_subprocesses.py b/venv/lib/python3.11/site-packages/anyio/_core/_subprocesses.py new file mode 100644 index 00000000..7ba41a5b --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/_core/_subprocesses.py @@ -0,0 +1,196 @@ +from __future__ import annotations + +import sys +from collections.abc import AsyncIterable, Iterable, Mapping, Sequence +from io import BytesIO +from os import PathLike +from subprocess import DEVNULL, PIPE, CalledProcessError, CompletedProcess +from typing import IO, Any, Union, cast + +from ..abc import Process +from ._eventloop import get_async_backend +from ._tasks import create_task_group + +if sys.version_info >= (3, 10): + from typing import TypeAlias +else: + from typing_extensions import TypeAlias + +StrOrBytesPath: TypeAlias = Union[str, bytes, "PathLike[str]", "PathLike[bytes]"] + + +async def run_process( + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + input: bytes | None = None, + stdout: int | IO[Any] | None = PIPE, + stderr: int | IO[Any] | None = PIPE, + check: bool = True, + cwd: StrOrBytesPath | None = None, + env: Mapping[str, str] | None = None, + startupinfo: Any = None, + creationflags: int = 0, + start_new_session: bool = False, + pass_fds: Sequence[int] = (), + user: str | int | None = None, + group: str | int | None = None, + extra_groups: Iterable[str | int] | None = None, + umask: int = -1, +) -> CompletedProcess[bytes]: + """ + Run an external command in a subprocess and wait until it completes. + + .. seealso:: :func:`subprocess.run` + + :param command: either a string to pass to the shell, or an iterable of strings + containing the executable name or path and its arguments + :param input: bytes passed to the standard input of the subprocess + :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + a file-like object, or `None` + :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + :data:`subprocess.STDOUT`, a file-like object, or `None` + :param check: if ``True``, raise :exc:`~subprocess.CalledProcessError` if the + process terminates with a return code other than 0 + :param cwd: If not ``None``, change the working directory to this before running the + command + :param env: if not ``None``, this mapping replaces the inherited environment + variables from the parent process + :param startupinfo: an instance of :class:`subprocess.STARTUPINFO` that can be used + to specify process startup parameters (Windows only) + :param creationflags: flags that can be used to control the creation of the + subprocess (see :class:`subprocess.Popen` for the specifics) + :param start_new_session: if ``true`` the setsid() system call will be made in the + child process prior to the execution of the subprocess. (POSIX only) + :param pass_fds: sequence of file descriptors to keep open between the parent and + child processes. (POSIX only) + :param user: effective user to run the process as (Python >= 3.9, POSIX only) + :param group: effective group to run the process as (Python >= 3.9, POSIX only) + :param extra_groups: supplementary groups to set in the subprocess (Python >= 3.9, + POSIX only) + :param umask: if not negative, this umask is applied in the child process before + running the given command (Python >= 3.9, POSIX only) + :return: an object representing the completed process + :raises ~subprocess.CalledProcessError: if ``check`` is ``True`` and the process + exits with a nonzero return code + + """ + + async def drain_stream(stream: AsyncIterable[bytes], index: int) -> None: + buffer = BytesIO() + async for chunk in stream: + buffer.write(chunk) + + stream_contents[index] = buffer.getvalue() + + async with await open_process( + command, + stdin=PIPE if input else DEVNULL, + stdout=stdout, + stderr=stderr, + cwd=cwd, + env=env, + startupinfo=startupinfo, + creationflags=creationflags, + start_new_session=start_new_session, + pass_fds=pass_fds, + user=user, + group=group, + extra_groups=extra_groups, + umask=umask, + ) as process: + stream_contents: list[bytes | None] = [None, None] + async with create_task_group() as tg: + if process.stdout: + tg.start_soon(drain_stream, process.stdout, 0) + + if process.stderr: + tg.start_soon(drain_stream, process.stderr, 1) + + if process.stdin and input: + await process.stdin.send(input) + await process.stdin.aclose() + + await process.wait() + + output, errors = stream_contents + if check and process.returncode != 0: + raise CalledProcessError(cast(int, process.returncode), command, output, errors) + + return CompletedProcess(command, cast(int, process.returncode), output, errors) + + +async def open_process( + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + stdin: int | IO[Any] | None = PIPE, + stdout: int | IO[Any] | None = PIPE, + stderr: int | IO[Any] | None = PIPE, + cwd: StrOrBytesPath | None = None, + env: Mapping[str, str] | None = None, + startupinfo: Any = None, + creationflags: int = 0, + start_new_session: bool = False, + pass_fds: Sequence[int] = (), + user: str | int | None = None, + group: str | int | None = None, + extra_groups: Iterable[str | int] | None = None, + umask: int = -1, +) -> Process: + """ + Start an external command in a subprocess. + + .. seealso:: :class:`subprocess.Popen` + + :param command: either a string to pass to the shell, or an iterable of strings + containing the executable name or path and its arguments + :param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, a + file-like object, or ``None`` + :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + a file-like object, or ``None`` + :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + :data:`subprocess.STDOUT`, a file-like object, or ``None`` + :param cwd: If not ``None``, the working directory is changed before executing + :param env: If env is not ``None``, it must be a mapping that defines the + environment variables for the new process + :param creationflags: flags that can be used to control the creation of the + subprocess (see :class:`subprocess.Popen` for the specifics) + :param startupinfo: an instance of :class:`subprocess.STARTUPINFO` that can be used + to specify process startup parameters (Windows only) + :param start_new_session: if ``true`` the setsid() system call will be made in the + child process prior to the execution of the subprocess. (POSIX only) + :param pass_fds: sequence of file descriptors to keep open between the parent and + child processes. (POSIX only) + :param user: effective user to run the process as (POSIX only) + :param group: effective group to run the process as (POSIX only) + :param extra_groups: supplementary groups to set in the subprocess (POSIX only) + :param umask: if not negative, this umask is applied in the child process before + running the given command (POSIX only) + :return: an asynchronous process object + + """ + kwargs: dict[str, Any] = {} + if user is not None: + kwargs["user"] = user + + if group is not None: + kwargs["group"] = group + + if extra_groups is not None: + kwargs["extra_groups"] = group + + if umask >= 0: + kwargs["umask"] = umask + + return await get_async_backend().open_process( + command, + stdin=stdin, + stdout=stdout, + stderr=stderr, + cwd=cwd, + env=env, + startupinfo=startupinfo, + creationflags=creationflags, + start_new_session=start_new_session, + pass_fds=pass_fds, + **kwargs, + ) diff --git a/venv/lib/python3.11/site-packages/anyio/_core/_synchronization.py b/venv/lib/python3.11/site-packages/anyio/_core/_synchronization.py new file mode 100644 index 00000000..023ab733 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/_core/_synchronization.py @@ -0,0 +1,724 @@ +from __future__ import annotations + +import math +from collections import deque +from dataclasses import dataclass +from types import TracebackType + +from sniffio import AsyncLibraryNotFoundError + +from ..lowlevel import checkpoint +from ._eventloop import get_async_backend +from ._exceptions import BusyResourceError +from ._tasks import CancelScope +from ._testing import TaskInfo, get_current_task + + +@dataclass(frozen=True) +class EventStatistics: + """ + :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Event.wait` + """ + + tasks_waiting: int + + +@dataclass(frozen=True) +class CapacityLimiterStatistics: + """ + :ivar int borrowed_tokens: number of tokens currently borrowed by tasks + :ivar float total_tokens: total number of available tokens + :ivar tuple borrowers: tasks or other objects currently holding tokens borrowed from + this limiter + :ivar int tasks_waiting: number of tasks waiting on + :meth:`~.CapacityLimiter.acquire` or + :meth:`~.CapacityLimiter.acquire_on_behalf_of` + """ + + borrowed_tokens: int + total_tokens: float + borrowers: tuple[object, ...] + tasks_waiting: int + + +@dataclass(frozen=True) +class LockStatistics: + """ + :ivar bool locked: flag indicating if this lock is locked or not + :ivar ~anyio.TaskInfo owner: task currently holding the lock (or ``None`` if the + lock is not held by any task) + :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Lock.acquire` + """ + + locked: bool + owner: TaskInfo | None + tasks_waiting: int + + +@dataclass(frozen=True) +class ConditionStatistics: + """ + :ivar int tasks_waiting: number of tasks blocked on :meth:`~.Condition.wait` + :ivar ~anyio.LockStatistics lock_statistics: statistics of the underlying + :class:`~.Lock` + """ + + tasks_waiting: int + lock_statistics: LockStatistics + + +@dataclass(frozen=True) +class SemaphoreStatistics: + """ + :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Semaphore.acquire` + + """ + + tasks_waiting: int + + +class Event: + def __new__(cls) -> Event: + try: + return get_async_backend().create_event() + except AsyncLibraryNotFoundError: + return EventAdapter() + + def set(self) -> None: + """Set the flag, notifying all listeners.""" + raise NotImplementedError + + def is_set(self) -> bool: + """Return ``True`` if the flag is set, ``False`` if not.""" + raise NotImplementedError + + async def wait(self) -> None: + """ + Wait until the flag has been set. + + If the flag has already been set when this method is called, it returns + immediately. + + """ + raise NotImplementedError + + def statistics(self) -> EventStatistics: + """Return statistics about the current state of this event.""" + raise NotImplementedError + + +class EventAdapter(Event): + _internal_event: Event | None = None + + def __new__(cls) -> EventAdapter: + return object.__new__(cls) + + @property + def _event(self) -> Event: + if self._internal_event is None: + self._internal_event = get_async_backend().create_event() + + return self._internal_event + + def set(self) -> None: + self._event.set() + + def is_set(self) -> bool: + return self._internal_event is not None and self._internal_event.is_set() + + async def wait(self) -> None: + await self._event.wait() + + def statistics(self) -> EventStatistics: + if self._internal_event is None: + return EventStatistics(tasks_waiting=0) + + return self._internal_event.statistics() + + +class Lock: + def __new__(cls, *, fast_acquire: bool = False) -> Lock: + try: + return get_async_backend().create_lock(fast_acquire=fast_acquire) + except AsyncLibraryNotFoundError: + return LockAdapter(fast_acquire=fast_acquire) + + async def __aenter__(self) -> None: + await self.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + async def acquire(self) -> None: + """Acquire the lock.""" + raise NotImplementedError + + def acquire_nowait(self) -> None: + """ + Acquire the lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + raise NotImplementedError + + def release(self) -> None: + """Release the lock.""" + raise NotImplementedError + + def locked(self) -> bool: + """Return True if the lock is currently held.""" + raise NotImplementedError + + def statistics(self) -> LockStatistics: + """ + Return statistics about the current state of this lock. + + .. versionadded:: 3.0 + """ + raise NotImplementedError + + +class LockAdapter(Lock): + _internal_lock: Lock | None = None + + def __new__(cls, *, fast_acquire: bool = False) -> LockAdapter: + return object.__new__(cls) + + def __init__(self, *, fast_acquire: bool = False): + self._fast_acquire = fast_acquire + + @property + def _lock(self) -> Lock: + if self._internal_lock is None: + self._internal_lock = get_async_backend().create_lock( + fast_acquire=self._fast_acquire + ) + + return self._internal_lock + + async def __aenter__(self) -> None: + await self._lock.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self._internal_lock is not None: + self._internal_lock.release() + + async def acquire(self) -> None: + """Acquire the lock.""" + await self._lock.acquire() + + def acquire_nowait(self) -> None: + """ + Acquire the lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + self._lock.acquire_nowait() + + def release(self) -> None: + """Release the lock.""" + self._lock.release() + + def locked(self) -> bool: + """Return True if the lock is currently held.""" + return self._lock.locked() + + def statistics(self) -> LockStatistics: + """ + Return statistics about the current state of this lock. + + .. versionadded:: 3.0 + + """ + if self._internal_lock is None: + return LockStatistics(False, None, 0) + + return self._internal_lock.statistics() + + +class Condition: + _owner_task: TaskInfo | None = None + + def __init__(self, lock: Lock | None = None): + self._lock = lock or Lock() + self._waiters: deque[Event] = deque() + + async def __aenter__(self) -> None: + await self.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + def _check_acquired(self) -> None: + if self._owner_task != get_current_task(): + raise RuntimeError("The current task is not holding the underlying lock") + + async def acquire(self) -> None: + """Acquire the underlying lock.""" + await self._lock.acquire() + self._owner_task = get_current_task() + + def acquire_nowait(self) -> None: + """ + Acquire the underlying lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + self._lock.acquire_nowait() + self._owner_task = get_current_task() + + def release(self) -> None: + """Release the underlying lock.""" + self._lock.release() + + def locked(self) -> bool: + """Return True if the lock is set.""" + return self._lock.locked() + + def notify(self, n: int = 1) -> None: + """Notify exactly n listeners.""" + self._check_acquired() + for _ in range(n): + try: + event = self._waiters.popleft() + except IndexError: + break + + event.set() + + def notify_all(self) -> None: + """Notify all the listeners.""" + self._check_acquired() + for event in self._waiters: + event.set() + + self._waiters.clear() + + async def wait(self) -> None: + """Wait for a notification.""" + await checkpoint() + event = Event() + self._waiters.append(event) + self.release() + try: + await event.wait() + except BaseException: + if not event.is_set(): + self._waiters.remove(event) + + raise + finally: + with CancelScope(shield=True): + await self.acquire() + + def statistics(self) -> ConditionStatistics: + """ + Return statistics about the current state of this condition. + + .. versionadded:: 3.0 + """ + return ConditionStatistics(len(self._waiters), self._lock.statistics()) + + +class Semaphore: + def __new__( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> Semaphore: + try: + return get_async_backend().create_semaphore( + initial_value, max_value=max_value, fast_acquire=fast_acquire + ) + except AsyncLibraryNotFoundError: + return SemaphoreAdapter(initial_value, max_value=max_value) + + def __init__( + self, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ): + if not isinstance(initial_value, int): + raise TypeError("initial_value must be an integer") + if initial_value < 0: + raise ValueError("initial_value must be >= 0") + if max_value is not None: + if not isinstance(max_value, int): + raise TypeError("max_value must be an integer or None") + if max_value < initial_value: + raise ValueError( + "max_value must be equal to or higher than initial_value" + ) + + self._fast_acquire = fast_acquire + + async def __aenter__(self) -> Semaphore: + await self.acquire() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + async def acquire(self) -> None: + """Decrement the semaphore value, blocking if necessary.""" + raise NotImplementedError + + def acquire_nowait(self) -> None: + """ + Acquire the underlying lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + raise NotImplementedError + + def release(self) -> None: + """Increment the semaphore value.""" + raise NotImplementedError + + @property + def value(self) -> int: + """The current value of the semaphore.""" + raise NotImplementedError + + @property + def max_value(self) -> int | None: + """The maximum value of the semaphore.""" + raise NotImplementedError + + def statistics(self) -> SemaphoreStatistics: + """ + Return statistics about the current state of this semaphore. + + .. versionadded:: 3.0 + """ + raise NotImplementedError + + +class SemaphoreAdapter(Semaphore): + _internal_semaphore: Semaphore | None = None + + def __new__( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> SemaphoreAdapter: + return object.__new__(cls) + + def __init__( + self, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> None: + super().__init__(initial_value, max_value=max_value, fast_acquire=fast_acquire) + self._initial_value = initial_value + self._max_value = max_value + + @property + def _semaphore(self) -> Semaphore: + if self._internal_semaphore is None: + self._internal_semaphore = get_async_backend().create_semaphore( + self._initial_value, max_value=self._max_value + ) + + return self._internal_semaphore + + async def acquire(self) -> None: + await self._semaphore.acquire() + + def acquire_nowait(self) -> None: + self._semaphore.acquire_nowait() + + def release(self) -> None: + self._semaphore.release() + + @property + def value(self) -> int: + if self._internal_semaphore is None: + return self._initial_value + + return self._semaphore.value + + @property + def max_value(self) -> int | None: + return self._max_value + + def statistics(self) -> SemaphoreStatistics: + if self._internal_semaphore is None: + return SemaphoreStatistics(tasks_waiting=0) + + return self._semaphore.statistics() + + +class CapacityLimiter: + def __new__(cls, total_tokens: float) -> CapacityLimiter: + try: + return get_async_backend().create_capacity_limiter(total_tokens) + except AsyncLibraryNotFoundError: + return CapacityLimiterAdapter(total_tokens) + + async def __aenter__(self) -> None: + raise NotImplementedError + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + raise NotImplementedError + + @property + def total_tokens(self) -> float: + """ + The total number of tokens available for borrowing. + + This is a read-write property. If the total number of tokens is increased, the + proportionate number of tasks waiting on this limiter will be granted their + tokens. + + .. versionchanged:: 3.0 + The property is now writable. + + """ + raise NotImplementedError + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + raise NotImplementedError + + @property + def borrowed_tokens(self) -> int: + """The number of tokens that have currently been borrowed.""" + raise NotImplementedError + + @property + def available_tokens(self) -> float: + """The number of tokens currently available to be borrowed""" + raise NotImplementedError + + def acquire_nowait(self) -> None: + """ + Acquire a token for the current task without waiting for one to become + available. + + :raises ~anyio.WouldBlock: if there are no tokens available for borrowing + + """ + raise NotImplementedError + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + """ + Acquire a token without waiting for one to become available. + + :param borrower: the entity borrowing a token + :raises ~anyio.WouldBlock: if there are no tokens available for borrowing + + """ + raise NotImplementedError + + async def acquire(self) -> None: + """ + Acquire a token for the current task, waiting if necessary for one to become + available. + + """ + raise NotImplementedError + + async def acquire_on_behalf_of(self, borrower: object) -> None: + """ + Acquire a token, waiting if necessary for one to become available. + + :param borrower: the entity borrowing a token + + """ + raise NotImplementedError + + def release(self) -> None: + """ + Release the token held by the current task. + + :raises RuntimeError: if the current task has not borrowed a token from this + limiter. + + """ + raise NotImplementedError + + def release_on_behalf_of(self, borrower: object) -> None: + """ + Release the token held by the given borrower. + + :raises RuntimeError: if the borrower has not borrowed a token from this + limiter. + + """ + raise NotImplementedError + + def statistics(self) -> CapacityLimiterStatistics: + """ + Return statistics about the current state of this limiter. + + .. versionadded:: 3.0 + + """ + raise NotImplementedError + + +class CapacityLimiterAdapter(CapacityLimiter): + _internal_limiter: CapacityLimiter | None = None + + def __new__(cls, total_tokens: float) -> CapacityLimiterAdapter: + return object.__new__(cls) + + def __init__(self, total_tokens: float) -> None: + self.total_tokens = total_tokens + + @property + def _limiter(self) -> CapacityLimiter: + if self._internal_limiter is None: + self._internal_limiter = get_async_backend().create_capacity_limiter( + self._total_tokens + ) + + return self._internal_limiter + + async def __aenter__(self) -> None: + await self._limiter.__aenter__() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + return await self._limiter.__aexit__(exc_type, exc_val, exc_tb) + + @property + def total_tokens(self) -> float: + if self._internal_limiter is None: + return self._total_tokens + + return self._internal_limiter.total_tokens + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + if not isinstance(value, int) and value is not math.inf: + raise TypeError("total_tokens must be an int or math.inf") + elif value < 1: + raise ValueError("total_tokens must be >= 1") + + if self._internal_limiter is None: + self._total_tokens = value + return + + self._limiter.total_tokens = value + + @property + def borrowed_tokens(self) -> int: + if self._internal_limiter is None: + return 0 + + return self._internal_limiter.borrowed_tokens + + @property + def available_tokens(self) -> float: + if self._internal_limiter is None: + return self._total_tokens + + return self._internal_limiter.available_tokens + + def acquire_nowait(self) -> None: + self._limiter.acquire_nowait() + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + self._limiter.acquire_on_behalf_of_nowait(borrower) + + async def acquire(self) -> None: + await self._limiter.acquire() + + async def acquire_on_behalf_of(self, borrower: object) -> None: + await self._limiter.acquire_on_behalf_of(borrower) + + def release(self) -> None: + self._limiter.release() + + def release_on_behalf_of(self, borrower: object) -> None: + self._limiter.release_on_behalf_of(borrower) + + def statistics(self) -> CapacityLimiterStatistics: + if self._internal_limiter is None: + return CapacityLimiterStatistics( + borrowed_tokens=0, + total_tokens=self.total_tokens, + borrowers=(), + tasks_waiting=0, + ) + + return self._internal_limiter.statistics() + + +class ResourceGuard: + """ + A context manager for ensuring that a resource is only used by a single task at a + time. + + Entering this context manager while the previous has not exited it yet will trigger + :exc:`BusyResourceError`. + + :param action: the action to guard against (visible in the :exc:`BusyResourceError` + when triggered, e.g. "Another task is already {action} this resource") + + .. versionadded:: 4.1 + """ + + __slots__ = "action", "_guarded" + + def __init__(self, action: str = "using"): + self.action: str = action + self._guarded = False + + def __enter__(self) -> None: + if self._guarded: + raise BusyResourceError(self.action) + + self._guarded = True + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + self._guarded = False + return None diff --git a/venv/lib/python3.11/site-packages/anyio/_core/_tasks.py b/venv/lib/python3.11/site-packages/anyio/_core/_tasks.py new file mode 100644 index 00000000..2f21ea20 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/_core/_tasks.py @@ -0,0 +1,158 @@ +from __future__ import annotations + +import math +from collections.abc import Generator +from contextlib import contextmanager +from types import TracebackType + +from ..abc._tasks import TaskGroup, TaskStatus +from ._eventloop import get_async_backend + + +class _IgnoredTaskStatus(TaskStatus[object]): + def started(self, value: object = None) -> None: + pass + + +TASK_STATUS_IGNORED = _IgnoredTaskStatus() + + +class CancelScope: + """ + Wraps a unit of work that can be made separately cancellable. + + :param deadline: The time (clock value) when this scope is cancelled automatically + :param shield: ``True`` to shield the cancel scope from external cancellation + """ + + def __new__( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + return get_async_backend().create_cancel_scope(shield=shield, deadline=deadline) + + def cancel(self) -> None: + """Cancel this scope immediately.""" + raise NotImplementedError + + @property + def deadline(self) -> float: + """ + The time (clock value) when this scope is cancelled automatically. + + Will be ``float('inf')`` if no timeout has been set. + + """ + raise NotImplementedError + + @deadline.setter + def deadline(self, value: float) -> None: + raise NotImplementedError + + @property + def cancel_called(self) -> bool: + """``True`` if :meth:`cancel` has been called.""" + raise NotImplementedError + + @property + def cancelled_caught(self) -> bool: + """ + ``True`` if this scope suppressed a cancellation exception it itself raised. + + This is typically used to check if any work was interrupted, or to see if the + scope was cancelled due to its deadline being reached. The value will, however, + only be ``True`` if the cancellation was triggered by the scope itself (and not + an outer scope). + + """ + raise NotImplementedError + + @property + def shield(self) -> bool: + """ + ``True`` if this scope is shielded from external cancellation. + + While a scope is shielded, it will not receive cancellations from outside. + + """ + raise NotImplementedError + + @shield.setter + def shield(self, value: bool) -> None: + raise NotImplementedError + + def __enter__(self) -> CancelScope: + raise NotImplementedError + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + raise NotImplementedError + + +@contextmanager +def fail_after( + delay: float | None, shield: bool = False +) -> Generator[CancelScope, None, None]: + """ + Create a context manager which raises a :class:`TimeoutError` if does not finish in + time. + + :param delay: maximum allowed time (in seconds) before raising the exception, or + ``None`` to disable the timeout + :param shield: ``True`` to shield the cancel scope from external cancellation + :return: a context manager that yields a cancel scope + :rtype: :class:`~typing.ContextManager`\\[:class:`~anyio.CancelScope`\\] + + """ + current_time = get_async_backend().current_time + deadline = (current_time() + delay) if delay is not None else math.inf + with get_async_backend().create_cancel_scope( + deadline=deadline, shield=shield + ) as cancel_scope: + yield cancel_scope + + if cancel_scope.cancelled_caught and current_time() >= cancel_scope.deadline: + raise TimeoutError + + +def move_on_after(delay: float | None, shield: bool = False) -> CancelScope: + """ + Create a cancel scope with a deadline that expires after the given delay. + + :param delay: maximum allowed time (in seconds) before exiting the context block, or + ``None`` to disable the timeout + :param shield: ``True`` to shield the cancel scope from external cancellation + :return: a cancel scope + + """ + deadline = ( + (get_async_backend().current_time() + delay) if delay is not None else math.inf + ) + return get_async_backend().create_cancel_scope(deadline=deadline, shield=shield) + + +def current_effective_deadline() -> float: + """ + Return the nearest deadline among all the cancel scopes effective for the current + task. + + :return: a clock value from the event loop's internal clock (or ``float('inf')`` if + there is no deadline in effect, or ``float('-inf')`` if the current scope has + been cancelled) + :rtype: float + + """ + return get_async_backend().current_effective_deadline() + + +def create_task_group() -> TaskGroup: + """ + Create a task group. + + :return: a task group + + """ + return get_async_backend().create_task_group() diff --git a/venv/lib/python3.11/site-packages/anyio/_core/_testing.py b/venv/lib/python3.11/site-packages/anyio/_core/_testing.py new file mode 100644 index 00000000..9e28b227 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/_core/_testing.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +from collections.abc import Awaitable, Generator +from typing import Any, cast + +from ._eventloop import get_async_backend + + +class TaskInfo: + """ + Represents an asynchronous task. + + :ivar int id: the unique identifier of the task + :ivar parent_id: the identifier of the parent task, if any + :vartype parent_id: Optional[int] + :ivar str name: the description of the task (if any) + :ivar ~collections.abc.Coroutine coro: the coroutine object of the task + """ + + __slots__ = "_name", "id", "parent_id", "name", "coro" + + def __init__( + self, + id: int, + parent_id: int | None, + name: str | None, + coro: Generator[Any, Any, Any] | Awaitable[Any], + ): + func = get_current_task + self._name = f"{func.__module__}.{func.__qualname__}" + self.id: int = id + self.parent_id: int | None = parent_id + self.name: str | None = name + self.coro: Generator[Any, Any, Any] | Awaitable[Any] = coro + + def __eq__(self, other: object) -> bool: + if isinstance(other, TaskInfo): + return self.id == other.id + + return NotImplemented + + def __hash__(self) -> int: + return hash(self.id) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}(id={self.id!r}, name={self.name!r})" + + def has_pending_cancellation(self) -> bool: + """ + Return ``True`` if the task has a cancellation pending, ``False`` otherwise. + + """ + return False + + +def get_current_task() -> TaskInfo: + """ + Return the current task. + + :return: a representation of the current task + + """ + return get_async_backend().get_current_task() + + +def get_running_tasks() -> list[TaskInfo]: + """ + Return a list of running tasks in the current event loop. + + :return: a list of task info objects + + """ + return cast("list[TaskInfo]", get_async_backend().get_running_tasks()) + + +async def wait_all_tasks_blocked() -> None: + """Wait until all other tasks are waiting for something.""" + await get_async_backend().wait_all_tasks_blocked() diff --git a/venv/lib/python3.11/site-packages/anyio/_core/_typedattr.py b/venv/lib/python3.11/site-packages/anyio/_core/_typedattr.py new file mode 100644 index 00000000..f358a448 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/_core/_typedattr.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +from collections.abc import Callable, Mapping +from typing import Any, TypeVar, final, overload + +from ._exceptions import TypedAttributeLookupError + +T_Attr = TypeVar("T_Attr") +T_Default = TypeVar("T_Default") +undefined = object() + + +def typed_attribute() -> Any: + """Return a unique object, used to mark typed attributes.""" + return object() + + +class TypedAttributeSet: + """ + Superclass for typed attribute collections. + + Checks that every public attribute of every subclass has a type annotation. + """ + + def __init_subclass__(cls) -> None: + annotations: dict[str, Any] = getattr(cls, "__annotations__", {}) + for attrname in dir(cls): + if not attrname.startswith("_") and attrname not in annotations: + raise TypeError( + f"Attribute {attrname!r} is missing its type annotation" + ) + + super().__init_subclass__() + + +class TypedAttributeProvider: + """Base class for classes that wish to provide typed extra attributes.""" + + @property + def extra_attributes(self) -> Mapping[T_Attr, Callable[[], T_Attr]]: + """ + A mapping of the extra attributes to callables that return the corresponding + values. + + If the provider wraps another provider, the attributes from that wrapper should + also be included in the returned mapping (but the wrapper may override the + callables from the wrapped instance). + + """ + return {} + + @overload + def extra(self, attribute: T_Attr) -> T_Attr: ... + + @overload + def extra(self, attribute: T_Attr, default: T_Default) -> T_Attr | T_Default: ... + + @final + def extra(self, attribute: Any, default: object = undefined) -> object: + """ + extra(attribute, default=undefined) + + Return the value of the given typed extra attribute. + + :param attribute: the attribute (member of a :class:`~TypedAttributeSet`) to + look for + :param default: the value that should be returned if no value is found for the + attribute + :raises ~anyio.TypedAttributeLookupError: if the search failed and no default + value was given + + """ + try: + getter = self.extra_attributes[attribute] + except KeyError: + if default is undefined: + raise TypedAttributeLookupError("Attribute not found") from None + else: + return default + + return getter() diff --git a/venv/lib/python3.11/site-packages/anyio/abc/__init__.py b/venv/lib/python3.11/site-packages/anyio/abc/__init__.py new file mode 100644 index 00000000..1ca0fcf7 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/abc/__init__.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from typing import Any + +from ._eventloop import AsyncBackend as AsyncBackend +from ._resources import AsyncResource as AsyncResource +from ._sockets import ConnectedUDPSocket as ConnectedUDPSocket +from ._sockets import ConnectedUNIXDatagramSocket as ConnectedUNIXDatagramSocket +from ._sockets import IPAddressType as IPAddressType +from ._sockets import IPSockAddrType as IPSockAddrType +from ._sockets import SocketAttribute as SocketAttribute +from ._sockets import SocketListener as SocketListener +from ._sockets import SocketStream as SocketStream +from ._sockets import UDPPacketType as UDPPacketType +from ._sockets import UDPSocket as UDPSocket +from ._sockets import UNIXDatagramPacketType as UNIXDatagramPacketType +from ._sockets import UNIXDatagramSocket as UNIXDatagramSocket +from ._sockets import UNIXSocketStream as UNIXSocketStream +from ._streams import AnyByteReceiveStream as AnyByteReceiveStream +from ._streams import AnyByteSendStream as AnyByteSendStream +from ._streams import AnyByteStream as AnyByteStream +from ._streams import AnyUnreliableByteReceiveStream as AnyUnreliableByteReceiveStream +from ._streams import AnyUnreliableByteSendStream as AnyUnreliableByteSendStream +from ._streams import AnyUnreliableByteStream as AnyUnreliableByteStream +from ._streams import ByteReceiveStream as ByteReceiveStream +from ._streams import ByteSendStream as ByteSendStream +from ._streams import ByteStream as ByteStream +from ._streams import Listener as Listener +from ._streams import ObjectReceiveStream as ObjectReceiveStream +from ._streams import ObjectSendStream as ObjectSendStream +from ._streams import ObjectStream as ObjectStream +from ._streams import UnreliableObjectReceiveStream as UnreliableObjectReceiveStream +from ._streams import UnreliableObjectSendStream as UnreliableObjectSendStream +from ._streams import UnreliableObjectStream as UnreliableObjectStream +from ._subprocesses import Process as Process +from ._tasks import TaskGroup as TaskGroup +from ._tasks import TaskStatus as TaskStatus +from ._testing import TestRunner as TestRunner + +# Re-exported here, for backwards compatibility +# isort: off +from .._core._synchronization import ( + CapacityLimiter as CapacityLimiter, + Condition as Condition, + Event as Event, + Lock as Lock, + Semaphore as Semaphore, +) +from .._core._tasks import CancelScope as CancelScope +from ..from_thread import BlockingPortal as BlockingPortal + +# Re-export imports so they look like they live directly in this package +key: str +value: Any +for key, value in list(locals().items()): + if getattr(value, "__module__", "").startswith("anyio.abc."): + value.__module__ = __name__ diff --git a/venv/lib/python3.11/site-packages/anyio/abc/_eventloop.py b/venv/lib/python3.11/site-packages/anyio/abc/_eventloop.py new file mode 100644 index 00000000..93d0e9d2 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/abc/_eventloop.py @@ -0,0 +1,374 @@ +from __future__ import annotations + +import math +import sys +from abc import ABCMeta, abstractmethod +from collections.abc import AsyncIterator, Awaitable, Callable, Sequence +from contextlib import AbstractContextManager +from os import PathLike +from signal import Signals +from socket import AddressFamily, SocketKind, socket +from typing import ( + IO, + TYPE_CHECKING, + Any, + TypeVar, + Union, + overload, +) + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if sys.version_info >= (3, 10): + from typing import TypeAlias +else: + from typing_extensions import TypeAlias + +if TYPE_CHECKING: + from .._core._synchronization import CapacityLimiter, Event, Lock, Semaphore + from .._core._tasks import CancelScope + from .._core._testing import TaskInfo + from ..from_thread import BlockingPortal + from ._sockets import ( + ConnectedUDPSocket, + ConnectedUNIXDatagramSocket, + IPSockAddrType, + SocketListener, + SocketStream, + UDPSocket, + UNIXDatagramSocket, + UNIXSocketStream, + ) + from ._subprocesses import Process + from ._tasks import TaskGroup + from ._testing import TestRunner + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") +StrOrBytesPath: TypeAlias = Union[str, bytes, "PathLike[str]", "PathLike[bytes]"] + + +class AsyncBackend(metaclass=ABCMeta): + @classmethod + @abstractmethod + def run( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + options: dict[str, Any], + ) -> T_Retval: + """ + Run the given coroutine function in an asynchronous event loop. + + The current thread must not be already running an event loop. + + :param func: a coroutine function + :param args: positional arguments to ``func`` + :param kwargs: positional arguments to ``func`` + :param options: keyword arguments to call the backend ``run()`` implementation + with + :return: the return value of the coroutine function + """ + + @classmethod + @abstractmethod + def current_token(cls) -> object: + """ + + :return: + """ + + @classmethod + @abstractmethod + def current_time(cls) -> float: + """ + Return the current value of the event loop's internal clock. + + :return: the clock value (seconds) + """ + + @classmethod + @abstractmethod + def cancelled_exception_class(cls) -> type[BaseException]: + """Return the exception class that is raised in a task if it's cancelled.""" + + @classmethod + @abstractmethod + async def checkpoint(cls) -> None: + """ + Check if the task has been cancelled, and allow rescheduling of other tasks. + + This is effectively the same as running :meth:`checkpoint_if_cancelled` and then + :meth:`cancel_shielded_checkpoint`. + """ + + @classmethod + async def checkpoint_if_cancelled(cls) -> None: + """ + Check if the current task group has been cancelled. + + This will check if the task has been cancelled, but will not allow other tasks + to be scheduled if not. + + """ + if cls.current_effective_deadline() == -math.inf: + await cls.checkpoint() + + @classmethod + async def cancel_shielded_checkpoint(cls) -> None: + """ + Allow the rescheduling of other tasks. + + This will give other tasks the opportunity to run, but without checking if the + current task group has been cancelled, unlike with :meth:`checkpoint`. + + """ + with cls.create_cancel_scope(shield=True): + await cls.sleep(0) + + @classmethod + @abstractmethod + async def sleep(cls, delay: float) -> None: + """ + Pause the current task for the specified duration. + + :param delay: the duration, in seconds + """ + + @classmethod + @abstractmethod + def create_cancel_scope( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + pass + + @classmethod + @abstractmethod + def current_effective_deadline(cls) -> float: + """ + Return the nearest deadline among all the cancel scopes effective for the + current task. + + :return: + - a clock value from the event loop's internal clock + - ``inf`` if there is no deadline in effect + - ``-inf`` if the current scope has been cancelled + :rtype: float + """ + + @classmethod + @abstractmethod + def create_task_group(cls) -> TaskGroup: + pass + + @classmethod + @abstractmethod + def create_event(cls) -> Event: + pass + + @classmethod + @abstractmethod + def create_lock(cls, *, fast_acquire: bool) -> Lock: + pass + + @classmethod + @abstractmethod + def create_semaphore( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> Semaphore: + pass + + @classmethod + @abstractmethod + def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter: + pass + + @classmethod + @abstractmethod + async def run_sync_in_worker_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + abandon_on_cancel: bool = False, + limiter: CapacityLimiter | None = None, + ) -> T_Retval: + pass + + @classmethod + @abstractmethod + def check_cancelled(cls) -> None: + pass + + @classmethod + @abstractmethod + def run_async_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + pass + + @classmethod + @abstractmethod + def run_sync_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + pass + + @classmethod + @abstractmethod + def create_blocking_portal(cls) -> BlockingPortal: + pass + + @classmethod + @abstractmethod + async def open_process( + cls, + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + stdin: int | IO[Any] | None, + stdout: int | IO[Any] | None, + stderr: int | IO[Any] | None, + **kwargs: Any, + ) -> Process: + pass + + @classmethod + @abstractmethod + def setup_process_pool_exit_at_shutdown(cls, workers: set[Process]) -> None: + pass + + @classmethod + @abstractmethod + async def connect_tcp( + cls, host: str, port: int, local_address: IPSockAddrType | None = None + ) -> SocketStream: + pass + + @classmethod + @abstractmethod + async def connect_unix(cls, path: str | bytes) -> UNIXSocketStream: + pass + + @classmethod + @abstractmethod + def create_tcp_listener(cls, sock: socket) -> SocketListener: + pass + + @classmethod + @abstractmethod + def create_unix_listener(cls, sock: socket) -> SocketListener: + pass + + @classmethod + @abstractmethod + async def create_udp_socket( + cls, + family: AddressFamily, + local_address: IPSockAddrType | None, + remote_address: IPSockAddrType | None, + reuse_port: bool, + ) -> UDPSocket | ConnectedUDPSocket: + pass + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket, remote_path: None + ) -> UNIXDatagramSocket: ... + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket, remote_path: str | bytes + ) -> ConnectedUNIXDatagramSocket: ... + + @classmethod + @abstractmethod + async def create_unix_datagram_socket( + cls, raw_socket: socket, remote_path: str | bytes | None + ) -> UNIXDatagramSocket | ConnectedUNIXDatagramSocket: + pass + + @classmethod + @abstractmethod + async def getaddrinfo( + cls, + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, + ) -> list[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int], + ] + ]: + pass + + @classmethod + @abstractmethod + async def getnameinfo( + cls, sockaddr: IPSockAddrType, flags: int = 0 + ) -> tuple[str, str]: + pass + + @classmethod + @abstractmethod + async def wait_socket_readable(cls, sock: socket) -> None: + pass + + @classmethod + @abstractmethod + async def wait_socket_writable(cls, sock: socket) -> None: + pass + + @classmethod + @abstractmethod + def current_default_thread_limiter(cls) -> CapacityLimiter: + pass + + @classmethod + @abstractmethod + def open_signal_receiver( + cls, *signals: Signals + ) -> AbstractContextManager[AsyncIterator[Signals]]: + pass + + @classmethod + @abstractmethod + def get_current_task(cls) -> TaskInfo: + pass + + @classmethod + @abstractmethod + def get_running_tasks(cls) -> Sequence[TaskInfo]: + pass + + @classmethod + @abstractmethod + async def wait_all_tasks_blocked(cls) -> None: + pass + + @classmethod + @abstractmethod + def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: + pass diff --git a/venv/lib/python3.11/site-packages/anyio/abc/_resources.py b/venv/lib/python3.11/site-packages/anyio/abc/_resources.py new file mode 100644 index 00000000..10df115a --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/abc/_resources.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +from abc import ABCMeta, abstractmethod +from types import TracebackType +from typing import TypeVar + +T = TypeVar("T") + + +class AsyncResource(metaclass=ABCMeta): + """ + Abstract base class for all closeable asynchronous resources. + + Works as an asynchronous context manager which returns the instance itself on enter, + and calls :meth:`aclose` on exit. + """ + + __slots__ = () + + async def __aenter__(self: T) -> T: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.aclose() + + @abstractmethod + async def aclose(self) -> None: + """Close the resource.""" diff --git a/venv/lib/python3.11/site-packages/anyio/abc/_sockets.py b/venv/lib/python3.11/site-packages/anyio/abc/_sockets.py new file mode 100644 index 00000000..1c6a450c --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/abc/_sockets.py @@ -0,0 +1,194 @@ +from __future__ import annotations + +import socket +from abc import abstractmethod +from collections.abc import Callable, Collection, Mapping +from contextlib import AsyncExitStack +from io import IOBase +from ipaddress import IPv4Address, IPv6Address +from socket import AddressFamily +from types import TracebackType +from typing import Any, TypeVar, Union + +from .._core._typedattr import ( + TypedAttributeProvider, + TypedAttributeSet, + typed_attribute, +) +from ._streams import ByteStream, Listener, UnreliableObjectStream +from ._tasks import TaskGroup + +IPAddressType = Union[str, IPv4Address, IPv6Address] +IPSockAddrType = tuple[str, int] +SockAddrType = Union[IPSockAddrType, str] +UDPPacketType = tuple[bytes, IPSockAddrType] +UNIXDatagramPacketType = tuple[bytes, str] +T_Retval = TypeVar("T_Retval") + + +class _NullAsyncContextManager: + async def __aenter__(self) -> None: + pass + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + return None + + +class SocketAttribute(TypedAttributeSet): + #: the address family of the underlying socket + family: AddressFamily = typed_attribute() + #: the local socket address of the underlying socket + local_address: SockAddrType = typed_attribute() + #: for IP addresses, the local port the underlying socket is bound to + local_port: int = typed_attribute() + #: the underlying stdlib socket object + raw_socket: socket.socket = typed_attribute() + #: the remote address the underlying socket is connected to + remote_address: SockAddrType = typed_attribute() + #: for IP addresses, the remote port the underlying socket is connected to + remote_port: int = typed_attribute() + + +class _SocketProvider(TypedAttributeProvider): + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + from .._core._sockets import convert_ipv6_sockaddr as convert + + attributes: dict[Any, Callable[[], Any]] = { + SocketAttribute.family: lambda: self._raw_socket.family, + SocketAttribute.local_address: lambda: convert( + self._raw_socket.getsockname() + ), + SocketAttribute.raw_socket: lambda: self._raw_socket, + } + try: + peername: tuple[str, int] | None = convert(self._raw_socket.getpeername()) + except OSError: + peername = None + + # Provide the remote address for connected sockets + if peername is not None: + attributes[SocketAttribute.remote_address] = lambda: peername + + # Provide local and remote ports for IP based sockets + if self._raw_socket.family in (AddressFamily.AF_INET, AddressFamily.AF_INET6): + attributes[SocketAttribute.local_port] = ( + lambda: self._raw_socket.getsockname()[1] + ) + if peername is not None: + remote_port = peername[1] + attributes[SocketAttribute.remote_port] = lambda: remote_port + + return attributes + + @property + @abstractmethod + def _raw_socket(self) -> socket.socket: + pass + + +class SocketStream(ByteStream, _SocketProvider): + """ + Transports bytes over a socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + +class UNIXSocketStream(SocketStream): + @abstractmethod + async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: + """ + Send file descriptors along with a message to the peer. + + :param message: a non-empty bytestring + :param fds: a collection of files (either numeric file descriptors or open file + or socket objects) + """ + + @abstractmethod + async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: + """ + Receive file descriptors along with a message from the peer. + + :param msglen: length of the message to expect from the peer + :param maxfds: maximum number of file descriptors to expect from the peer + :return: a tuple of (message, file descriptors) + """ + + +class SocketListener(Listener[SocketStream], _SocketProvider): + """ + Listens to incoming socket connections. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @abstractmethod + async def accept(self) -> SocketStream: + """Accept an incoming connection.""" + + async def serve( + self, + handler: Callable[[SocketStream], Any], + task_group: TaskGroup | None = None, + ) -> None: + from .. import create_task_group + + async with AsyncExitStack() as stack: + if task_group is None: + task_group = await stack.enter_async_context(create_task_group()) + + while True: + stream = await self.accept() + task_group.start_soon(handler, stream) + + +class UDPSocket(UnreliableObjectStream[UDPPacketType], _SocketProvider): + """ + Represents an unconnected UDP socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + async def sendto(self, data: bytes, host: str, port: int) -> None: + """ + Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, (host, port))). + + """ + return await self.send((data, (host, port))) + + +class ConnectedUDPSocket(UnreliableObjectStream[bytes], _SocketProvider): + """ + Represents an connected UDP socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + +class UNIXDatagramSocket( + UnreliableObjectStream[UNIXDatagramPacketType], _SocketProvider +): + """ + Represents an unconnected Unix datagram socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + async def sendto(self, data: bytes, path: str) -> None: + """Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, path)).""" + return await self.send((data, path)) + + +class ConnectedUNIXDatagramSocket(UnreliableObjectStream[bytes], _SocketProvider): + """ + Represents a connected Unix datagram socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ diff --git a/venv/lib/python3.11/site-packages/anyio/abc/_streams.py b/venv/lib/python3.11/site-packages/anyio/abc/_streams.py new file mode 100644 index 00000000..8c638683 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/abc/_streams.py @@ -0,0 +1,203 @@ +from __future__ import annotations + +from abc import abstractmethod +from collections.abc import Callable +from typing import Any, Generic, TypeVar, Union + +from .._core._exceptions import EndOfStream +from .._core._typedattr import TypedAttributeProvider +from ._resources import AsyncResource +from ._tasks import TaskGroup + +T_Item = TypeVar("T_Item") +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class UnreliableObjectReceiveStream( + Generic[T_co], AsyncResource, TypedAttributeProvider +): + """ + An interface for receiving objects. + + This interface makes no guarantees that the received messages arrive in the order in + which they were sent, or that no messages are missed. + + Asynchronously iterating over objects of this type will yield objects matching the + given type parameter. + """ + + def __aiter__(self) -> UnreliableObjectReceiveStream[T_co]: + return self + + async def __anext__(self) -> T_co: + try: + return await self.receive() + except EndOfStream: + raise StopAsyncIteration + + @abstractmethod + async def receive(self) -> T_co: + """ + Receive the next item. + + :raises ~anyio.ClosedResourceError: if the receive stream has been explicitly + closed + :raises ~anyio.EndOfStream: if this stream has been closed from the other end + :raises ~anyio.BrokenResourceError: if this stream has been rendered unusable + due to external causes + """ + + +class UnreliableObjectSendStream( + Generic[T_contra], AsyncResource, TypedAttributeProvider +): + """ + An interface for sending objects. + + This interface makes no guarantees that the messages sent will reach the + recipient(s) in the same order in which they were sent, or at all. + """ + + @abstractmethod + async def send(self, item: T_contra) -> None: + """ + Send an item to the peer(s). + + :param item: the item to send + :raises ~anyio.ClosedResourceError: if the send stream has been explicitly + closed + :raises ~anyio.BrokenResourceError: if this stream has been rendered unusable + due to external causes + """ + + +class UnreliableObjectStream( + UnreliableObjectReceiveStream[T_Item], UnreliableObjectSendStream[T_Item] +): + """ + A bidirectional message stream which does not guarantee the order or reliability of + message delivery. + """ + + +class ObjectReceiveStream(UnreliableObjectReceiveStream[T_co]): + """ + A receive message stream which guarantees that messages are received in the same + order in which they were sent, and that no messages are missed. + """ + + +class ObjectSendStream(UnreliableObjectSendStream[T_contra]): + """ + A send message stream which guarantees that messages are delivered in the same order + in which they were sent, without missing any messages in the middle. + """ + + +class ObjectStream( + ObjectReceiveStream[T_Item], + ObjectSendStream[T_Item], + UnreliableObjectStream[T_Item], +): + """ + A bidirectional message stream which guarantees the order and reliability of message + delivery. + """ + + @abstractmethod + async def send_eof(self) -> None: + """ + Send an end-of-file indication to the peer. + + You should not try to send any further data to this stream after calling this + method. This method is idempotent (does nothing on successive calls). + """ + + +class ByteReceiveStream(AsyncResource, TypedAttributeProvider): + """ + An interface for receiving bytes from a single peer. + + Iterating this byte stream will yield a byte string of arbitrary length, but no more + than 65536 bytes. + """ + + def __aiter__(self) -> ByteReceiveStream: + return self + + async def __anext__(self) -> bytes: + try: + return await self.receive() + except EndOfStream: + raise StopAsyncIteration + + @abstractmethod + async def receive(self, max_bytes: int = 65536) -> bytes: + """ + Receive at most ``max_bytes`` bytes from the peer. + + .. note:: Implementors of this interface should not return an empty + :class:`bytes` object, and users should ignore them. + + :param max_bytes: maximum number of bytes to receive + :return: the received bytes + :raises ~anyio.EndOfStream: if this stream has been closed from the other end + """ + + +class ByteSendStream(AsyncResource, TypedAttributeProvider): + """An interface for sending bytes to a single peer.""" + + @abstractmethod + async def send(self, item: bytes) -> None: + """ + Send the given bytes to the peer. + + :param item: the bytes to send + """ + + +class ByteStream(ByteReceiveStream, ByteSendStream): + """A bidirectional byte stream.""" + + @abstractmethod + async def send_eof(self) -> None: + """ + Send an end-of-file indication to the peer. + + You should not try to send any further data to this stream after calling this + method. This method is idempotent (does nothing on successive calls). + """ + + +#: Type alias for all unreliable bytes-oriented receive streams. +AnyUnreliableByteReceiveStream = Union[ + UnreliableObjectReceiveStream[bytes], ByteReceiveStream +] +#: Type alias for all unreliable bytes-oriented send streams. +AnyUnreliableByteSendStream = Union[UnreliableObjectSendStream[bytes], ByteSendStream] +#: Type alias for all unreliable bytes-oriented streams. +AnyUnreliableByteStream = Union[UnreliableObjectStream[bytes], ByteStream] +#: Type alias for all bytes-oriented receive streams. +AnyByteReceiveStream = Union[ObjectReceiveStream[bytes], ByteReceiveStream] +#: Type alias for all bytes-oriented send streams. +AnyByteSendStream = Union[ObjectSendStream[bytes], ByteSendStream] +#: Type alias for all bytes-oriented streams. +AnyByteStream = Union[ObjectStream[bytes], ByteStream] + + +class Listener(Generic[T_co], AsyncResource, TypedAttributeProvider): + """An interface for objects that let you accept incoming connections.""" + + @abstractmethod + async def serve( + self, handler: Callable[[T_co], Any], task_group: TaskGroup | None = None + ) -> None: + """ + Accept incoming connections as they come in and start tasks to handle them. + + :param handler: a callable that will be used to handle each accepted connection + :param task_group: the task group that will be used to start tasks for handling + each accepted connection (if omitted, an ad-hoc task group will be created) + """ diff --git a/venv/lib/python3.11/site-packages/anyio/abc/_subprocesses.py b/venv/lib/python3.11/site-packages/anyio/abc/_subprocesses.py new file mode 100644 index 00000000..ce0564ce --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/abc/_subprocesses.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +from abc import abstractmethod +from signal import Signals + +from ._resources import AsyncResource +from ._streams import ByteReceiveStream, ByteSendStream + + +class Process(AsyncResource): + """An asynchronous version of :class:`subprocess.Popen`.""" + + @abstractmethod + async def wait(self) -> int: + """ + Wait until the process exits. + + :return: the exit code of the process + """ + + @abstractmethod + def terminate(self) -> None: + """ + Terminates the process, gracefully if possible. + + On Windows, this calls ``TerminateProcess()``. + On POSIX systems, this sends ``SIGTERM`` to the process. + + .. seealso:: :meth:`subprocess.Popen.terminate` + """ + + @abstractmethod + def kill(self) -> None: + """ + Kills the process. + + On Windows, this calls ``TerminateProcess()``. + On POSIX systems, this sends ``SIGKILL`` to the process. + + .. seealso:: :meth:`subprocess.Popen.kill` + """ + + @abstractmethod + def send_signal(self, signal: Signals) -> None: + """ + Send a signal to the subprocess. + + .. seealso:: :meth:`subprocess.Popen.send_signal` + + :param signal: the signal number (e.g. :data:`signal.SIGHUP`) + """ + + @property + @abstractmethod + def pid(self) -> int: + """The process ID of the process.""" + + @property + @abstractmethod + def returncode(self) -> int | None: + """ + The return code of the process. If the process has not yet terminated, this will + be ``None``. + """ + + @property + @abstractmethod + def stdin(self) -> ByteSendStream | None: + """The stream for the standard input of the process.""" + + @property + @abstractmethod + def stdout(self) -> ByteReceiveStream | None: + """The stream for the standard output of the process.""" + + @property + @abstractmethod + def stderr(self) -> ByteReceiveStream | None: + """The stream for the standard error output of the process.""" diff --git a/venv/lib/python3.11/site-packages/anyio/abc/_tasks.py b/venv/lib/python3.11/site-packages/anyio/abc/_tasks.py new file mode 100644 index 00000000..88aecf38 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/abc/_tasks.py @@ -0,0 +1,95 @@ +from __future__ import annotations + +import sys +from abc import ABCMeta, abstractmethod +from collections.abc import Awaitable, Callable +from types import TracebackType +from typing import TYPE_CHECKING, Any, Protocol, TypeVar, overload + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if TYPE_CHECKING: + from .._core._tasks import CancelScope + +T_Retval = TypeVar("T_Retval") +T_contra = TypeVar("T_contra", contravariant=True) +PosArgsT = TypeVarTuple("PosArgsT") + + +class TaskStatus(Protocol[T_contra]): + @overload + def started(self: TaskStatus[None]) -> None: ... + + @overload + def started(self, value: T_contra) -> None: ... + + def started(self, value: T_contra | None = None) -> None: + """ + Signal that the task has started. + + :param value: object passed back to the starter of the task + """ + + +class TaskGroup(metaclass=ABCMeta): + """ + Groups several asynchronous tasks together. + + :ivar cancel_scope: the cancel scope inherited by all child tasks + :vartype cancel_scope: CancelScope + """ + + cancel_scope: CancelScope + + @abstractmethod + def start_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], + *args: Unpack[PosArgsT], + name: object = None, + ) -> None: + """ + Start a new task in this task group. + + :param func: a coroutine function + :param args: positional arguments to call the function with + :param name: name of the task, for the purposes of introspection and debugging + + .. versionadded:: 3.0 + """ + + @abstractmethod + async def start( + self, + func: Callable[..., Awaitable[Any]], + *args: object, + name: object = None, + ) -> Any: + """ + Start a new task and wait until it signals for readiness. + + :param func: a coroutine function + :param args: positional arguments to call the function with + :param name: name of the task, for the purposes of introspection and debugging + :return: the value passed to ``task_status.started()`` + :raises RuntimeError: if the task finishes without calling + ``task_status.started()`` + + .. versionadded:: 3.0 + """ + + @abstractmethod + async def __aenter__(self) -> TaskGroup: + """Enter the task group context and allow starting new tasks.""" + + @abstractmethod + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + """Exit the task group context waiting for all tasks to finish.""" diff --git a/venv/lib/python3.11/site-packages/anyio/abc/_testing.py b/venv/lib/python3.11/site-packages/anyio/abc/_testing.py new file mode 100644 index 00000000..7c50ed76 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/abc/_testing.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import types +from abc import ABCMeta, abstractmethod +from collections.abc import AsyncGenerator, Callable, Coroutine, Iterable +from typing import Any, TypeVar + +_T = TypeVar("_T") + + +class TestRunner(metaclass=ABCMeta): + """ + Encapsulates a running event loop. Every call made through this object will use the + same event loop. + """ + + def __enter__(self) -> TestRunner: + return self + + @abstractmethod + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> bool | None: ... + + @abstractmethod + def run_asyncgen_fixture( + self, + fixture_func: Callable[..., AsyncGenerator[_T, Any]], + kwargs: dict[str, Any], + ) -> Iterable[_T]: + """ + Run an async generator fixture. + + :param fixture_func: the fixture function + :param kwargs: keyword arguments to call the fixture function with + :return: an iterator yielding the value yielded from the async generator + """ + + @abstractmethod + def run_fixture( + self, + fixture_func: Callable[..., Coroutine[Any, Any, _T]], + kwargs: dict[str, Any], + ) -> _T: + """ + Run an async fixture. + + :param fixture_func: the fixture function + :param kwargs: keyword arguments to call the fixture function with + :return: the return value of the fixture function + """ + + @abstractmethod + def run_test( + self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] + ) -> None: + """ + Run an async test function. + + :param test_func: the test function + :param kwargs: keyword arguments to call the test function with + """ diff --git a/venv/lib/python3.11/site-packages/anyio/from_thread.py b/venv/lib/python3.11/site-packages/anyio/from_thread.py new file mode 100644 index 00000000..93a4cfe8 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/from_thread.py @@ -0,0 +1,527 @@ +from __future__ import annotations + +import sys +from collections.abc import Awaitable, Callable, Generator +from concurrent.futures import Future +from contextlib import ( + AbstractAsyncContextManager, + AbstractContextManager, + contextmanager, +) +from dataclasses import dataclass, field +from inspect import isawaitable +from threading import Lock, Thread, get_ident +from types import TracebackType +from typing import ( + Any, + Generic, + TypeVar, + cast, + overload, +) + +from ._core import _eventloop +from ._core._eventloop import get_async_backend, get_cancelled_exc_class, threadlocals +from ._core._synchronization import Event +from ._core._tasks import CancelScope, create_task_group +from .abc import AsyncBackend +from .abc._tasks import TaskStatus + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +T_Retval = TypeVar("T_Retval") +T_co = TypeVar("T_co", covariant=True) +PosArgsT = TypeVarTuple("PosArgsT") + + +def run( + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], *args: Unpack[PosArgsT] +) -> T_Retval: + """ + Call a coroutine function from a worker thread. + + :param func: a coroutine function + :param args: positional arguments for the callable + :return: the return value of the coroutine function + + """ + try: + async_backend = threadlocals.current_async_backend + token = threadlocals.current_token + except AttributeError: + raise RuntimeError( + "This function can only be run from an AnyIO worker thread" + ) from None + + return async_backend.run_async_from_thread(func, args, token=token) + + +def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] +) -> T_Retval: + """ + Call a function in the event loop thread from a worker thread. + + :param func: a callable + :param args: positional arguments for the callable + :return: the return value of the callable + + """ + try: + async_backend = threadlocals.current_async_backend + token = threadlocals.current_token + except AttributeError: + raise RuntimeError( + "This function can only be run from an AnyIO worker thread" + ) from None + + return async_backend.run_sync_from_thread(func, args, token=token) + + +class _BlockingAsyncContextManager(Generic[T_co], AbstractContextManager): + _enter_future: Future[T_co] + _exit_future: Future[bool | None] + _exit_event: Event + _exit_exc_info: tuple[ + type[BaseException] | None, BaseException | None, TracebackType | None + ] = (None, None, None) + + def __init__( + self, async_cm: AbstractAsyncContextManager[T_co], portal: BlockingPortal + ): + self._async_cm = async_cm + self._portal = portal + + async def run_async_cm(self) -> bool | None: + try: + self._exit_event = Event() + value = await self._async_cm.__aenter__() + except BaseException as exc: + self._enter_future.set_exception(exc) + raise + else: + self._enter_future.set_result(value) + + try: + # Wait for the sync context manager to exit. + # This next statement can raise `get_cancelled_exc_class()` if + # something went wrong in a task group in this async context + # manager. + await self._exit_event.wait() + finally: + # In case of cancellation, it could be that we end up here before + # `_BlockingAsyncContextManager.__exit__` is called, and an + # `_exit_exc_info` has been set. + result = await self._async_cm.__aexit__(*self._exit_exc_info) + return result + + def __enter__(self) -> T_co: + self._enter_future = Future() + self._exit_future = self._portal.start_task_soon(self.run_async_cm) + return self._enter_future.result() + + def __exit__( + self, + __exc_type: type[BaseException] | None, + __exc_value: BaseException | None, + __traceback: TracebackType | None, + ) -> bool | None: + self._exit_exc_info = __exc_type, __exc_value, __traceback + self._portal.call(self._exit_event.set) + return self._exit_future.result() + + +class _BlockingPortalTaskStatus(TaskStatus): + def __init__(self, future: Future): + self._future = future + + def started(self, value: object = None) -> None: + self._future.set_result(value) + + +class BlockingPortal: + """An object that lets external threads run code in an asynchronous event loop.""" + + def __new__(cls) -> BlockingPortal: + return get_async_backend().create_blocking_portal() + + def __init__(self) -> None: + self._event_loop_thread_id: int | None = get_ident() + self._stop_event = Event() + self._task_group = create_task_group() + self._cancelled_exc_class = get_cancelled_exc_class() + + async def __aenter__(self) -> BlockingPortal: + await self._task_group.__aenter__() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + await self.stop() + return await self._task_group.__aexit__(exc_type, exc_val, exc_tb) + + def _check_running(self) -> None: + if self._event_loop_thread_id is None: + raise RuntimeError("This portal is not running") + if self._event_loop_thread_id == get_ident(): + raise RuntimeError( + "This method cannot be called from the event loop thread" + ) + + async def sleep_until_stopped(self) -> None: + """Sleep until :meth:`stop` is called.""" + await self._stop_event.wait() + + async def stop(self, cancel_remaining: bool = False) -> None: + """ + Signal the portal to shut down. + + This marks the portal as no longer accepting new calls and exits from + :meth:`sleep_until_stopped`. + + :param cancel_remaining: ``True`` to cancel all the remaining tasks, ``False`` + to let them finish before returning + + """ + self._event_loop_thread_id = None + self._stop_event.set() + if cancel_remaining: + self._task_group.cancel_scope.cancel() + + async def _call_func( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + future: Future[T_Retval], + ) -> None: + def callback(f: Future[T_Retval]) -> None: + if f.cancelled() and self._event_loop_thread_id not in ( + None, + get_ident(), + ): + self.call(scope.cancel) + + try: + retval_or_awaitable = func(*args, **kwargs) + if isawaitable(retval_or_awaitable): + with CancelScope() as scope: + if future.cancelled(): + scope.cancel() + else: + future.add_done_callback(callback) + + retval = await retval_or_awaitable + else: + retval = retval_or_awaitable + except self._cancelled_exc_class: + future.cancel() + future.set_running_or_notify_cancel() + except BaseException as exc: + if not future.cancelled(): + future.set_exception(exc) + + # Let base exceptions fall through + if not isinstance(exc, Exception): + raise + else: + if not future.cancelled(): + future.set_result(retval) + finally: + scope = None # type: ignore[assignment] + + def _spawn_task_from_thread( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + name: object, + future: Future[T_Retval], + ) -> None: + """ + Spawn a new task using the given callable. + + Implementors must ensure that the future is resolved when the task finishes. + + :param func: a callable + :param args: positional arguments to be passed to the callable + :param kwargs: keyword arguments to be passed to the callable + :param name: name of the task (will be coerced to a string if not ``None``) + :param future: a future that will resolve to the return value of the callable, + or the exception raised during its execution + + """ + raise NotImplementedError + + @overload + def call( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + *args: Unpack[PosArgsT], + ) -> T_Retval: ... + + @overload + def call( + self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] + ) -> T_Retval: ... + + def call( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + *args: Unpack[PosArgsT], + ) -> T_Retval: + """ + Call the given function in the event loop thread. + + If the callable returns a coroutine object, it is awaited on. + + :param func: any callable + :raises RuntimeError: if the portal is not running or if this method is called + from within the event loop thread + + """ + return cast(T_Retval, self.start_task_soon(func, *args).result()) + + @overload + def start_task_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + *args: Unpack[PosArgsT], + name: object = None, + ) -> Future[T_Retval]: ... + + @overload + def start_task_soon( + self, + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + name: object = None, + ) -> Future[T_Retval]: ... + + def start_task_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + *args: Unpack[PosArgsT], + name: object = None, + ) -> Future[T_Retval]: + """ + Start a task in the portal's task group. + + The task will be run inside a cancel scope which can be cancelled by cancelling + the returned future. + + :param func: the target function + :param args: positional arguments passed to ``func`` + :param name: name of the task (will be coerced to a string if not ``None``) + :return: a future that resolves with the return value of the callable if the + task completes successfully, or with the exception raised in the task + :raises RuntimeError: if the portal is not running or if this method is called + from within the event loop thread + :rtype: concurrent.futures.Future[T_Retval] + + .. versionadded:: 3.0 + + """ + self._check_running() + f: Future[T_Retval] = Future() + self._spawn_task_from_thread(func, args, {}, name, f) + return f + + def start_task( + self, + func: Callable[..., Awaitable[T_Retval]], + *args: object, + name: object = None, + ) -> tuple[Future[T_Retval], Any]: + """ + Start a task in the portal's task group and wait until it signals for readiness. + + This method works the same way as :meth:`.abc.TaskGroup.start`. + + :param func: the target function + :param args: positional arguments passed to ``func`` + :param name: name of the task (will be coerced to a string if not ``None``) + :return: a tuple of (future, task_status_value) where the ``task_status_value`` + is the value passed to ``task_status.started()`` from within the target + function + :rtype: tuple[concurrent.futures.Future[T_Retval], Any] + + .. versionadded:: 3.0 + + """ + + def task_done(future: Future[T_Retval]) -> None: + if not task_status_future.done(): + if future.cancelled(): + task_status_future.cancel() + elif future.exception(): + task_status_future.set_exception(future.exception()) + else: + exc = RuntimeError( + "Task exited without calling task_status.started()" + ) + task_status_future.set_exception(exc) + + self._check_running() + task_status_future: Future = Future() + task_status = _BlockingPortalTaskStatus(task_status_future) + f: Future = Future() + f.add_done_callback(task_done) + self._spawn_task_from_thread(func, args, {"task_status": task_status}, name, f) + return f, task_status_future.result() + + def wrap_async_context_manager( + self, cm: AbstractAsyncContextManager[T_co] + ) -> AbstractContextManager[T_co]: + """ + Wrap an async context manager as a synchronous context manager via this portal. + + Spawns a task that will call both ``__aenter__()`` and ``__aexit__()``, stopping + in the middle until the synchronous context manager exits. + + :param cm: an asynchronous context manager + :return: a synchronous context manager + + .. versionadded:: 2.1 + + """ + return _BlockingAsyncContextManager(cm, self) + + +@dataclass +class BlockingPortalProvider: + """ + A manager for a blocking portal. Used as a context manager. The first thread to + enter this context manager causes a blocking portal to be started with the specific + parameters, and the last thread to exit causes the portal to be shut down. Thus, + there will be exactly one blocking portal running in this context as long as at + least one thread has entered this context manager. + + The parameters are the same as for :func:`~anyio.run`. + + :param backend: name of the backend + :param backend_options: backend options + + .. versionadded:: 4.4 + """ + + backend: str = "asyncio" + backend_options: dict[str, Any] | None = None + _lock: Lock = field(init=False, default_factory=Lock) + _leases: int = field(init=False, default=0) + _portal: BlockingPortal = field(init=False) + _portal_cm: AbstractContextManager[BlockingPortal] | None = field( + init=False, default=None + ) + + def __enter__(self) -> BlockingPortal: + with self._lock: + if self._portal_cm is None: + self._portal_cm = start_blocking_portal( + self.backend, self.backend_options + ) + self._portal = self._portal_cm.__enter__() + + self._leases += 1 + return self._portal + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + portal_cm: AbstractContextManager[BlockingPortal] | None = None + with self._lock: + assert self._portal_cm + assert self._leases > 0 + self._leases -= 1 + if not self._leases: + portal_cm = self._portal_cm + self._portal_cm = None + del self._portal + + if portal_cm: + portal_cm.__exit__(None, None, None) + + +@contextmanager +def start_blocking_portal( + backend: str = "asyncio", backend_options: dict[str, Any] | None = None +) -> Generator[BlockingPortal, Any, None]: + """ + Start a new event loop in a new thread and run a blocking portal in its main task. + + The parameters are the same as for :func:`~anyio.run`. + + :param backend: name of the backend + :param backend_options: backend options + :return: a context manager that yields a blocking portal + + .. versionchanged:: 3.0 + Usage as a context manager is now required. + + """ + + async def run_portal() -> None: + async with BlockingPortal() as portal_: + future.set_result(portal_) + await portal_.sleep_until_stopped() + + def run_blocking_portal() -> None: + if future.set_running_or_notify_cancel(): + try: + _eventloop.run( + run_portal, backend=backend, backend_options=backend_options + ) + except BaseException as exc: + if not future.done(): + future.set_exception(exc) + + future: Future[BlockingPortal] = Future() + thread = Thread(target=run_blocking_portal, daemon=True) + thread.start() + try: + cancel_remaining_tasks = False + portal = future.result() + try: + yield portal + except BaseException: + cancel_remaining_tasks = True + raise + finally: + try: + portal.call(portal.stop, cancel_remaining_tasks) + except RuntimeError: + pass + finally: + thread.join() + + +def check_cancelled() -> None: + """ + Check if the cancel scope of the host task's running the current worker thread has + been cancelled. + + If the host task's current cancel scope has indeed been cancelled, the + backend-specific cancellation exception will be raised. + + :raises RuntimeError: if the current thread was not spawned by + :func:`.to_thread.run_sync` + + """ + try: + async_backend: AsyncBackend = threadlocals.current_async_backend + except AttributeError: + raise RuntimeError( + "This function can only be run from an AnyIO worker thread" + ) from None + + async_backend.check_cancelled() diff --git a/venv/lib/python3.11/site-packages/anyio/lowlevel.py b/venv/lib/python3.11/site-packages/anyio/lowlevel.py new file mode 100644 index 00000000..14c7668c --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/lowlevel.py @@ -0,0 +1,161 @@ +from __future__ import annotations + +import enum +from dataclasses import dataclass +from typing import Any, Generic, Literal, TypeVar, overload +from weakref import WeakKeyDictionary + +from ._core._eventloop import get_async_backend + +T = TypeVar("T") +D = TypeVar("D") + + +async def checkpoint() -> None: + """ + Check for cancellation and allow the scheduler to switch to another task. + + Equivalent to (but more efficient than):: + + await checkpoint_if_cancelled() + await cancel_shielded_checkpoint() + + + .. versionadded:: 3.0 + + """ + await get_async_backend().checkpoint() + + +async def checkpoint_if_cancelled() -> None: + """ + Enter a checkpoint if the enclosing cancel scope has been cancelled. + + This does not allow the scheduler to switch to a different task. + + .. versionadded:: 3.0 + + """ + await get_async_backend().checkpoint_if_cancelled() + + +async def cancel_shielded_checkpoint() -> None: + """ + Allow the scheduler to switch to another task but without checking for cancellation. + + Equivalent to (but potentially more efficient than):: + + with CancelScope(shield=True): + await checkpoint() + + + .. versionadded:: 3.0 + + """ + await get_async_backend().cancel_shielded_checkpoint() + + +def current_token() -> object: + """ + Return a backend specific token object that can be used to get back to the event + loop. + + """ + return get_async_backend().current_token() + + +_run_vars: WeakKeyDictionary[Any, dict[str, Any]] = WeakKeyDictionary() +_token_wrappers: dict[Any, _TokenWrapper] = {} + + +@dataclass(frozen=True) +class _TokenWrapper: + __slots__ = "_token", "__weakref__" + _token: object + + +class _NoValueSet(enum.Enum): + NO_VALUE_SET = enum.auto() + + +class RunvarToken(Generic[T]): + __slots__ = "_var", "_value", "_redeemed" + + def __init__(self, var: RunVar[T], value: T | Literal[_NoValueSet.NO_VALUE_SET]): + self._var = var + self._value: T | Literal[_NoValueSet.NO_VALUE_SET] = value + self._redeemed = False + + +class RunVar(Generic[T]): + """ + Like a :class:`~contextvars.ContextVar`, except scoped to the running event loop. + """ + + __slots__ = "_name", "_default" + + NO_VALUE_SET: Literal[_NoValueSet.NO_VALUE_SET] = _NoValueSet.NO_VALUE_SET + + _token_wrappers: set[_TokenWrapper] = set() + + def __init__( + self, name: str, default: T | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET + ): + self._name = name + self._default = default + + @property + def _current_vars(self) -> dict[str, T]: + token = current_token() + try: + return _run_vars[token] + except KeyError: + run_vars = _run_vars[token] = {} + return run_vars + + @overload + def get(self, default: D) -> T | D: ... + + @overload + def get(self) -> T: ... + + def get( + self, default: D | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET + ) -> T | D: + try: + return self._current_vars[self._name] + except KeyError: + if default is not RunVar.NO_VALUE_SET: + return default + elif self._default is not RunVar.NO_VALUE_SET: + return self._default + + raise LookupError( + f'Run variable "{self._name}" has no value and no default set' + ) + + def set(self, value: T) -> RunvarToken[T]: + current_vars = self._current_vars + token = RunvarToken(self, current_vars.get(self._name, RunVar.NO_VALUE_SET)) + current_vars[self._name] = value + return token + + def reset(self, token: RunvarToken[T]) -> None: + if token._var is not self: + raise ValueError("This token does not belong to this RunVar") + + if token._redeemed: + raise ValueError("This token has already been used") + + if token._value is _NoValueSet.NO_VALUE_SET: + try: + del self._current_vars[self._name] + except KeyError: + pass + else: + self._current_vars[self._name] = token._value + + token._redeemed = True + + def __repr__(self) -> str: + return f"" diff --git a/venv/lib/python3.11/site-packages/anyio/py.typed b/venv/lib/python3.11/site-packages/anyio/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.11/site-packages/anyio/pytest_plugin.py b/venv/lib/python3.11/site-packages/anyio/pytest_plugin.py new file mode 100644 index 00000000..4a0d59dd --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/pytest_plugin.py @@ -0,0 +1,191 @@ +from __future__ import annotations + +import sys +from collections.abc import Generator, Iterator +from contextlib import ExitStack, contextmanager +from inspect import isasyncgenfunction, iscoroutinefunction, ismethod +from typing import Any, cast + +import pytest +import sniffio +from _pytest.fixtures import SubRequest +from _pytest.outcomes import Exit + +from ._core._eventloop import get_all_backends, get_async_backend +from ._core._exceptions import iterate_exceptions +from .abc import TestRunner + +if sys.version_info < (3, 11): + from exceptiongroup import ExceptionGroup + +_current_runner: TestRunner | None = None +_runner_stack: ExitStack | None = None +_runner_leases = 0 + + +def extract_backend_and_options(backend: object) -> tuple[str, dict[str, Any]]: + if isinstance(backend, str): + return backend, {} + elif isinstance(backend, tuple) and len(backend) == 2: + if isinstance(backend[0], str) and isinstance(backend[1], dict): + return cast(tuple[str, dict[str, Any]], backend) + + raise TypeError("anyio_backend must be either a string or tuple of (string, dict)") + + +@contextmanager +def get_runner( + backend_name: str, backend_options: dict[str, Any] +) -> Iterator[TestRunner]: + global _current_runner, _runner_leases, _runner_stack + if _current_runner is None: + asynclib = get_async_backend(backend_name) + _runner_stack = ExitStack() + if sniffio.current_async_library_cvar.get(None) is None: + # Since we're in control of the event loop, we can cache the name of the + # async library + token = sniffio.current_async_library_cvar.set(backend_name) + _runner_stack.callback(sniffio.current_async_library_cvar.reset, token) + + backend_options = backend_options or {} + _current_runner = _runner_stack.enter_context( + asynclib.create_test_runner(backend_options) + ) + + _runner_leases += 1 + try: + yield _current_runner + finally: + _runner_leases -= 1 + if not _runner_leases: + assert _runner_stack is not None + _runner_stack.close() + _runner_stack = _current_runner = None + + +def pytest_configure(config: Any) -> None: + config.addinivalue_line( + "markers", + "anyio: mark the (coroutine function) test to be run " + "asynchronously via anyio.", + ) + + +@pytest.hookimpl(hookwrapper=True) +def pytest_fixture_setup(fixturedef: Any, request: Any) -> Generator[Any]: + def wrapper( + *args: Any, anyio_backend: Any, request: SubRequest, **kwargs: Any + ) -> Any: + # Rebind any fixture methods to the request instance + if ( + request.instance + and ismethod(func) + and type(func.__self__) is type(request.instance) + ): + local_func = func.__func__.__get__(request.instance) + else: + local_func = func + + backend_name, backend_options = extract_backend_and_options(anyio_backend) + if has_backend_arg: + kwargs["anyio_backend"] = anyio_backend + + if has_request_arg: + kwargs["request"] = request + + with get_runner(backend_name, backend_options) as runner: + if isasyncgenfunction(local_func): + yield from runner.run_asyncgen_fixture(local_func, kwargs) + else: + yield runner.run_fixture(local_func, kwargs) + + # Only apply this to coroutine functions and async generator functions in requests + # that involve the anyio_backend fixture + func = fixturedef.func + if isasyncgenfunction(func) or iscoroutinefunction(func): + if "anyio_backend" in request.fixturenames: + fixturedef.func = wrapper + original_argname = fixturedef.argnames + + if not (has_backend_arg := "anyio_backend" in fixturedef.argnames): + fixturedef.argnames += ("anyio_backend",) + + if not (has_request_arg := "request" in fixturedef.argnames): + fixturedef.argnames += ("request",) + + try: + return (yield) + finally: + fixturedef.func = func + fixturedef.argnames = original_argname + + return (yield) + + +@pytest.hookimpl(tryfirst=True) +def pytest_pycollect_makeitem(collector: Any, name: Any, obj: Any) -> None: + if collector.istestfunction(obj, name): + inner_func = obj.hypothesis.inner_test if hasattr(obj, "hypothesis") else obj + if iscoroutinefunction(inner_func): + marker = collector.get_closest_marker("anyio") + own_markers = getattr(obj, "pytestmark", ()) + if marker or any(marker.name == "anyio" for marker in own_markers): + pytest.mark.usefixtures("anyio_backend")(obj) + + +@pytest.hookimpl(tryfirst=True) +def pytest_pyfunc_call(pyfuncitem: Any) -> bool | None: + def run_with_hypothesis(**kwargs: Any) -> None: + with get_runner(backend_name, backend_options) as runner: + runner.run_test(original_func, kwargs) + + backend = pyfuncitem.funcargs.get("anyio_backend") + if backend: + backend_name, backend_options = extract_backend_and_options(backend) + + if hasattr(pyfuncitem.obj, "hypothesis"): + # Wrap the inner test function unless it's already wrapped + original_func = pyfuncitem.obj.hypothesis.inner_test + if original_func.__qualname__ != run_with_hypothesis.__qualname__: + if iscoroutinefunction(original_func): + pyfuncitem.obj.hypothesis.inner_test = run_with_hypothesis + + return None + + if iscoroutinefunction(pyfuncitem.obj): + funcargs = pyfuncitem.funcargs + testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} + with get_runner(backend_name, backend_options) as runner: + try: + runner.run_test(pyfuncitem.obj, testargs) + except ExceptionGroup as excgrp: + for exc in iterate_exceptions(excgrp): + if isinstance(exc, (Exit, KeyboardInterrupt, SystemExit)): + raise exc from excgrp + + raise + + return True + + return None + + +@pytest.fixture(scope="module", params=get_all_backends()) +def anyio_backend(request: Any) -> Any: + return request.param + + +@pytest.fixture +def anyio_backend_name(anyio_backend: Any) -> str: + if isinstance(anyio_backend, str): + return anyio_backend + else: + return anyio_backend[0] + + +@pytest.fixture +def anyio_backend_options(anyio_backend: Any) -> dict[str, Any]: + if isinstance(anyio_backend, str): + return {} + else: + return anyio_backend[1] diff --git a/venv/lib/python3.11/site-packages/anyio/streams/__init__.py b/venv/lib/python3.11/site-packages/anyio/streams/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.11/site-packages/anyio/streams/buffered.py b/venv/lib/python3.11/site-packages/anyio/streams/buffered.py new file mode 100644 index 00000000..f5d5e836 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/streams/buffered.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +from collections.abc import Callable, Mapping +from dataclasses import dataclass, field +from typing import Any + +from .. import ClosedResourceError, DelimiterNotFound, EndOfStream, IncompleteRead +from ..abc import AnyByteReceiveStream, ByteReceiveStream + + +@dataclass(eq=False) +class BufferedByteReceiveStream(ByteReceiveStream): + """ + Wraps any bytes-based receive stream and uses a buffer to provide sophisticated + receiving capabilities in the form of a byte stream. + """ + + receive_stream: AnyByteReceiveStream + _buffer: bytearray = field(init=False, default_factory=bytearray) + _closed: bool = field(init=False, default=False) + + async def aclose(self) -> None: + await self.receive_stream.aclose() + self._closed = True + + @property + def buffer(self) -> bytes: + """The bytes currently in the buffer.""" + return bytes(self._buffer) + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return self.receive_stream.extra_attributes + + async def receive(self, max_bytes: int = 65536) -> bytes: + if self._closed: + raise ClosedResourceError + + if self._buffer: + chunk = bytes(self._buffer[:max_bytes]) + del self._buffer[:max_bytes] + return chunk + elif isinstance(self.receive_stream, ByteReceiveStream): + return await self.receive_stream.receive(max_bytes) + else: + # With a bytes-oriented object stream, we need to handle any surplus bytes + # we get from the receive() call + chunk = await self.receive_stream.receive() + if len(chunk) > max_bytes: + # Save the surplus bytes in the buffer + self._buffer.extend(chunk[max_bytes:]) + return chunk[:max_bytes] + else: + return chunk + + async def receive_exactly(self, nbytes: int) -> bytes: + """ + Read exactly the given amount of bytes from the stream. + + :param nbytes: the number of bytes to read + :return: the bytes read + :raises ~anyio.IncompleteRead: if the stream was closed before the requested + amount of bytes could be read from the stream + + """ + while True: + remaining = nbytes - len(self._buffer) + if remaining <= 0: + retval = self._buffer[:nbytes] + del self._buffer[:nbytes] + return bytes(retval) + + try: + if isinstance(self.receive_stream, ByteReceiveStream): + chunk = await self.receive_stream.receive(remaining) + else: + chunk = await self.receive_stream.receive() + except EndOfStream as exc: + raise IncompleteRead from exc + + self._buffer.extend(chunk) + + async def receive_until(self, delimiter: bytes, max_bytes: int) -> bytes: + """ + Read from the stream until the delimiter is found or max_bytes have been read. + + :param delimiter: the marker to look for in the stream + :param max_bytes: maximum number of bytes that will be read before raising + :exc:`~anyio.DelimiterNotFound` + :return: the bytes read (not including the delimiter) + :raises ~anyio.IncompleteRead: if the stream was closed before the delimiter + was found + :raises ~anyio.DelimiterNotFound: if the delimiter is not found within the + bytes read up to the maximum allowed + + """ + delimiter_size = len(delimiter) + offset = 0 + while True: + # Check if the delimiter can be found in the current buffer + index = self._buffer.find(delimiter, offset) + if index >= 0: + found = self._buffer[:index] + del self._buffer[: index + len(delimiter) :] + return bytes(found) + + # Check if the buffer is already at or over the limit + if len(self._buffer) >= max_bytes: + raise DelimiterNotFound(max_bytes) + + # Read more data into the buffer from the socket + try: + data = await self.receive_stream.receive() + except EndOfStream as exc: + raise IncompleteRead from exc + + # Move the offset forward and add the new data to the buffer + offset = max(len(self._buffer) - delimiter_size + 1, 0) + self._buffer.extend(data) diff --git a/venv/lib/python3.11/site-packages/anyio/streams/file.py b/venv/lib/python3.11/site-packages/anyio/streams/file.py new file mode 100644 index 00000000..f4924642 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/streams/file.py @@ -0,0 +1,148 @@ +from __future__ import annotations + +from collections.abc import Callable, Mapping +from io import SEEK_SET, UnsupportedOperation +from os import PathLike +from pathlib import Path +from typing import Any, BinaryIO, cast + +from .. import ( + BrokenResourceError, + ClosedResourceError, + EndOfStream, + TypedAttributeSet, + to_thread, + typed_attribute, +) +from ..abc import ByteReceiveStream, ByteSendStream + + +class FileStreamAttribute(TypedAttributeSet): + #: the open file descriptor + file: BinaryIO = typed_attribute() + #: the path of the file on the file system, if available (file must be a real file) + path: Path = typed_attribute() + #: the file number, if available (file must be a real file or a TTY) + fileno: int = typed_attribute() + + +class _BaseFileStream: + def __init__(self, file: BinaryIO): + self._file = file + + async def aclose(self) -> None: + await to_thread.run_sync(self._file.close) + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + attributes: dict[Any, Callable[[], Any]] = { + FileStreamAttribute.file: lambda: self._file, + } + + if hasattr(self._file, "name"): + attributes[FileStreamAttribute.path] = lambda: Path(self._file.name) + + try: + self._file.fileno() + except UnsupportedOperation: + pass + else: + attributes[FileStreamAttribute.fileno] = lambda: self._file.fileno() + + return attributes + + +class FileReadStream(_BaseFileStream, ByteReceiveStream): + """ + A byte stream that reads from a file in the file system. + + :param file: a file that has been opened for reading in binary mode + + .. versionadded:: 3.0 + """ + + @classmethod + async def from_path(cls, path: str | PathLike[str]) -> FileReadStream: + """ + Create a file read stream by opening the given file. + + :param path: path of the file to read from + + """ + file = await to_thread.run_sync(Path(path).open, "rb") + return cls(cast(BinaryIO, file)) + + async def receive(self, max_bytes: int = 65536) -> bytes: + try: + data = await to_thread.run_sync(self._file.read, max_bytes) + except ValueError: + raise ClosedResourceError from None + except OSError as exc: + raise BrokenResourceError from exc + + if data: + return data + else: + raise EndOfStream + + async def seek(self, position: int, whence: int = SEEK_SET) -> int: + """ + Seek the file to the given position. + + .. seealso:: :meth:`io.IOBase.seek` + + .. note:: Not all file descriptors are seekable. + + :param position: position to seek the file to + :param whence: controls how ``position`` is interpreted + :return: the new absolute position + :raises OSError: if the file is not seekable + + """ + return await to_thread.run_sync(self._file.seek, position, whence) + + async def tell(self) -> int: + """ + Return the current stream position. + + .. note:: Not all file descriptors are seekable. + + :return: the current absolute position + :raises OSError: if the file is not seekable + + """ + return await to_thread.run_sync(self._file.tell) + + +class FileWriteStream(_BaseFileStream, ByteSendStream): + """ + A byte stream that writes to a file in the file system. + + :param file: a file that has been opened for writing in binary mode + + .. versionadded:: 3.0 + """ + + @classmethod + async def from_path( + cls, path: str | PathLike[str], append: bool = False + ) -> FileWriteStream: + """ + Create a file write stream by opening the given file for writing. + + :param path: path of the file to write to + :param append: if ``True``, open the file for appending; if ``False``, any + existing file at the given path will be truncated + + """ + mode = "ab" if append else "wb" + file = await to_thread.run_sync(Path(path).open, mode) + return cls(cast(BinaryIO, file)) + + async def send(self, item: bytes) -> None: + try: + await to_thread.run_sync(self._file.write, item) + except ValueError: + raise ClosedResourceError from None + except OSError as exc: + raise BrokenResourceError from exc diff --git a/venv/lib/python3.11/site-packages/anyio/streams/memory.py b/venv/lib/python3.11/site-packages/anyio/streams/memory.py new file mode 100644 index 00000000..b547aa6a --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/streams/memory.py @@ -0,0 +1,317 @@ +from __future__ import annotations + +import warnings +from collections import OrderedDict, deque +from dataclasses import dataclass, field +from types import TracebackType +from typing import Generic, NamedTuple, TypeVar + +from .. import ( + BrokenResourceError, + ClosedResourceError, + EndOfStream, + WouldBlock, +) +from .._core._testing import TaskInfo, get_current_task +from ..abc import Event, ObjectReceiveStream, ObjectSendStream +from ..lowlevel import checkpoint + +T_Item = TypeVar("T_Item") +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class MemoryObjectStreamStatistics(NamedTuple): + current_buffer_used: int #: number of items stored in the buffer + #: maximum number of items that can be stored on this stream (or :data:`math.inf`) + max_buffer_size: float + open_send_streams: int #: number of unclosed clones of the send stream + open_receive_streams: int #: number of unclosed clones of the receive stream + #: number of tasks blocked on :meth:`MemoryObjectSendStream.send` + tasks_waiting_send: int + #: number of tasks blocked on :meth:`MemoryObjectReceiveStream.receive` + tasks_waiting_receive: int + + +@dataclass(eq=False) +class MemoryObjectItemReceiver(Generic[T_Item]): + task_info: TaskInfo = field(init=False, default_factory=get_current_task) + item: T_Item = field(init=False) + + def __repr__(self) -> str: + # When item is not defined, we get following error with default __repr__: + # AttributeError: 'MemoryObjectItemReceiver' object has no attribute 'item' + item = getattr(self, "item", None) + return f"{self.__class__.__name__}(task_info={self.task_info}, item={item!r})" + + +@dataclass(eq=False) +class MemoryObjectStreamState(Generic[T_Item]): + max_buffer_size: float = field() + buffer: deque[T_Item] = field(init=False, default_factory=deque) + open_send_channels: int = field(init=False, default=0) + open_receive_channels: int = field(init=False, default=0) + waiting_receivers: OrderedDict[Event, MemoryObjectItemReceiver[T_Item]] = field( + init=False, default_factory=OrderedDict + ) + waiting_senders: OrderedDict[Event, T_Item] = field( + init=False, default_factory=OrderedDict + ) + + def statistics(self) -> MemoryObjectStreamStatistics: + return MemoryObjectStreamStatistics( + len(self.buffer), + self.max_buffer_size, + self.open_send_channels, + self.open_receive_channels, + len(self.waiting_senders), + len(self.waiting_receivers), + ) + + +@dataclass(eq=False) +class MemoryObjectReceiveStream(Generic[T_co], ObjectReceiveStream[T_co]): + _state: MemoryObjectStreamState[T_co] + _closed: bool = field(init=False, default=False) + + def __post_init__(self) -> None: + self._state.open_receive_channels += 1 + + def receive_nowait(self) -> T_co: + """ + Receive the next item if it can be done without waiting. + + :return: the received item + :raises ~anyio.ClosedResourceError: if this send stream has been closed + :raises ~anyio.EndOfStream: if the buffer is empty and this stream has been + closed from the sending end + :raises ~anyio.WouldBlock: if there are no items in the buffer and no tasks + waiting to send + + """ + if self._closed: + raise ClosedResourceError + + if self._state.waiting_senders: + # Get the item from the next sender + send_event, item = self._state.waiting_senders.popitem(last=False) + self._state.buffer.append(item) + send_event.set() + + if self._state.buffer: + return self._state.buffer.popleft() + elif not self._state.open_send_channels: + raise EndOfStream + + raise WouldBlock + + async def receive(self) -> T_co: + await checkpoint() + try: + return self.receive_nowait() + except WouldBlock: + # Add ourselves in the queue + receive_event = Event() + receiver = MemoryObjectItemReceiver[T_co]() + self._state.waiting_receivers[receive_event] = receiver + + try: + await receive_event.wait() + finally: + self._state.waiting_receivers.pop(receive_event, None) + + try: + return receiver.item + except AttributeError: + raise EndOfStream + + def clone(self) -> MemoryObjectReceiveStream[T_co]: + """ + Create a clone of this receive stream. + + Each clone can be closed separately. Only when all clones have been closed will + the receiving end of the memory stream be considered closed by the sending ends. + + :return: the cloned stream + + """ + if self._closed: + raise ClosedResourceError + + return MemoryObjectReceiveStream(_state=self._state) + + def close(self) -> None: + """ + Close the stream. + + This works the exact same way as :meth:`aclose`, but is provided as a special + case for the benefit of synchronous callbacks. + + """ + if not self._closed: + self._closed = True + self._state.open_receive_channels -= 1 + if self._state.open_receive_channels == 0: + send_events = list(self._state.waiting_senders.keys()) + for event in send_events: + event.set() + + async def aclose(self) -> None: + self.close() + + def statistics(self) -> MemoryObjectStreamStatistics: + """ + Return statistics about the current state of this stream. + + .. versionadded:: 3.0 + """ + return self._state.statistics() + + def __enter__(self) -> MemoryObjectReceiveStream[T_co]: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def __del__(self) -> None: + if not self._closed: + warnings.warn( + f"Unclosed <{self.__class__.__name__} at {id(self):x}>", + ResourceWarning, + source=self, + ) + + +@dataclass(eq=False) +class MemoryObjectSendStream(Generic[T_contra], ObjectSendStream[T_contra]): + _state: MemoryObjectStreamState[T_contra] + _closed: bool = field(init=False, default=False) + + def __post_init__(self) -> None: + self._state.open_send_channels += 1 + + def send_nowait(self, item: T_contra) -> None: + """ + Send an item immediately if it can be done without waiting. + + :param item: the item to send + :raises ~anyio.ClosedResourceError: if this send stream has been closed + :raises ~anyio.BrokenResourceError: if the stream has been closed from the + receiving end + :raises ~anyio.WouldBlock: if the buffer is full and there are no tasks waiting + to receive + + """ + if self._closed: + raise ClosedResourceError + if not self._state.open_receive_channels: + raise BrokenResourceError + + while self._state.waiting_receivers: + receive_event, receiver = self._state.waiting_receivers.popitem(last=False) + if not receiver.task_info.has_pending_cancellation(): + receiver.item = item + receive_event.set() + return + + if len(self._state.buffer) < self._state.max_buffer_size: + self._state.buffer.append(item) + else: + raise WouldBlock + + async def send(self, item: T_contra) -> None: + """ + Send an item to the stream. + + If the buffer is full, this method blocks until there is again room in the + buffer or the item can be sent directly to a receiver. + + :param item: the item to send + :raises ~anyio.ClosedResourceError: if this send stream has been closed + :raises ~anyio.BrokenResourceError: if the stream has been closed from the + receiving end + + """ + await checkpoint() + try: + self.send_nowait(item) + except WouldBlock: + # Wait until there's someone on the receiving end + send_event = Event() + self._state.waiting_senders[send_event] = item + try: + await send_event.wait() + except BaseException: + self._state.waiting_senders.pop(send_event, None) + raise + + if send_event in self._state.waiting_senders: + del self._state.waiting_senders[send_event] + raise BrokenResourceError from None + + def clone(self) -> MemoryObjectSendStream[T_contra]: + """ + Create a clone of this send stream. + + Each clone can be closed separately. Only when all clones have been closed will + the sending end of the memory stream be considered closed by the receiving ends. + + :return: the cloned stream + + """ + if self._closed: + raise ClosedResourceError + + return MemoryObjectSendStream(_state=self._state) + + def close(self) -> None: + """ + Close the stream. + + This works the exact same way as :meth:`aclose`, but is provided as a special + case for the benefit of synchronous callbacks. + + """ + if not self._closed: + self._closed = True + self._state.open_send_channels -= 1 + if self._state.open_send_channels == 0: + receive_events = list(self._state.waiting_receivers.keys()) + self._state.waiting_receivers.clear() + for event in receive_events: + event.set() + + async def aclose(self) -> None: + self.close() + + def statistics(self) -> MemoryObjectStreamStatistics: + """ + Return statistics about the current state of this stream. + + .. versionadded:: 3.0 + """ + return self._state.statistics() + + def __enter__(self) -> MemoryObjectSendStream[T_contra]: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def __del__(self) -> None: + if not self._closed: + warnings.warn( + f"Unclosed <{self.__class__.__name__} at {id(self):x}>", + ResourceWarning, + source=self, + ) diff --git a/venv/lib/python3.11/site-packages/anyio/streams/stapled.py b/venv/lib/python3.11/site-packages/anyio/streams/stapled.py new file mode 100644 index 00000000..80f64a2e --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/streams/stapled.py @@ -0,0 +1,141 @@ +from __future__ import annotations + +from collections.abc import Callable, Mapping, Sequence +from dataclasses import dataclass +from typing import Any, Generic, TypeVar + +from ..abc import ( + ByteReceiveStream, + ByteSendStream, + ByteStream, + Listener, + ObjectReceiveStream, + ObjectSendStream, + ObjectStream, + TaskGroup, +) + +T_Item = TypeVar("T_Item") +T_Stream = TypeVar("T_Stream") + + +@dataclass(eq=False) +class StapledByteStream(ByteStream): + """ + Combines two byte streams into a single, bidirectional byte stream. + + Extra attributes will be provided from both streams, with the receive stream + providing the values in case of a conflict. + + :param ByteSendStream send_stream: the sending byte stream + :param ByteReceiveStream receive_stream: the receiving byte stream + """ + + send_stream: ByteSendStream + receive_stream: ByteReceiveStream + + async def receive(self, max_bytes: int = 65536) -> bytes: + return await self.receive_stream.receive(max_bytes) + + async def send(self, item: bytes) -> None: + await self.send_stream.send(item) + + async def send_eof(self) -> None: + await self.send_stream.aclose() + + async def aclose(self) -> None: + await self.send_stream.aclose() + await self.receive_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self.send_stream.extra_attributes, + **self.receive_stream.extra_attributes, + } + + +@dataclass(eq=False) +class StapledObjectStream(Generic[T_Item], ObjectStream[T_Item]): + """ + Combines two object streams into a single, bidirectional object stream. + + Extra attributes will be provided from both streams, with the receive stream + providing the values in case of a conflict. + + :param ObjectSendStream send_stream: the sending object stream + :param ObjectReceiveStream receive_stream: the receiving object stream + """ + + send_stream: ObjectSendStream[T_Item] + receive_stream: ObjectReceiveStream[T_Item] + + async def receive(self) -> T_Item: + return await self.receive_stream.receive() + + async def send(self, item: T_Item) -> None: + await self.send_stream.send(item) + + async def send_eof(self) -> None: + await self.send_stream.aclose() + + async def aclose(self) -> None: + await self.send_stream.aclose() + await self.receive_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self.send_stream.extra_attributes, + **self.receive_stream.extra_attributes, + } + + +@dataclass(eq=False) +class MultiListener(Generic[T_Stream], Listener[T_Stream]): + """ + Combines multiple listeners into one, serving connections from all of them at once. + + Any MultiListeners in the given collection of listeners will have their listeners + moved into this one. + + Extra attributes are provided from each listener, with each successive listener + overriding any conflicting attributes from the previous one. + + :param listeners: listeners to serve + :type listeners: Sequence[Listener[T_Stream]] + """ + + listeners: Sequence[Listener[T_Stream]] + + def __post_init__(self) -> None: + listeners: list[Listener[T_Stream]] = [] + for listener in self.listeners: + if isinstance(listener, MultiListener): + listeners.extend(listener.listeners) + del listener.listeners[:] # type: ignore[attr-defined] + else: + listeners.append(listener) + + self.listeners = listeners + + async def serve( + self, handler: Callable[[T_Stream], Any], task_group: TaskGroup | None = None + ) -> None: + from .. import create_task_group + + async with create_task_group() as tg: + for listener in self.listeners: + tg.start_soon(listener.serve, handler, task_group) + + async def aclose(self) -> None: + for listener in self.listeners: + await listener.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + attributes: dict = {} + for listener in self.listeners: + attributes.update(listener.extra_attributes) + + return attributes diff --git a/venv/lib/python3.11/site-packages/anyio/streams/text.py b/venv/lib/python3.11/site-packages/anyio/streams/text.py new file mode 100644 index 00000000..f1a11278 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/streams/text.py @@ -0,0 +1,147 @@ +from __future__ import annotations + +import codecs +from collections.abc import Callable, Mapping +from dataclasses import InitVar, dataclass, field +from typing import Any + +from ..abc import ( + AnyByteReceiveStream, + AnyByteSendStream, + AnyByteStream, + ObjectReceiveStream, + ObjectSendStream, + ObjectStream, +) + + +@dataclass(eq=False) +class TextReceiveStream(ObjectReceiveStream[str]): + """ + Stream wrapper that decodes bytes to strings using the given encoding. + + Decoding is done using :class:`~codecs.IncrementalDecoder` which returns any + completely received unicode characters as soon as they come in. + + :param transport_stream: any bytes-based receive stream + :param encoding: character encoding to use for decoding bytes to strings (defaults + to ``utf-8``) + :param errors: handling scheme for decoding errors (defaults to ``strict``; see the + `codecs module documentation`_ for a comprehensive list of options) + + .. _codecs module documentation: + https://docs.python.org/3/library/codecs.html#codec-objects + """ + + transport_stream: AnyByteReceiveStream + encoding: InitVar[str] = "utf-8" + errors: InitVar[str] = "strict" + _decoder: codecs.IncrementalDecoder = field(init=False) + + def __post_init__(self, encoding: str, errors: str) -> None: + decoder_class = codecs.getincrementaldecoder(encoding) + self._decoder = decoder_class(errors=errors) + + async def receive(self) -> str: + while True: + chunk = await self.transport_stream.receive() + decoded = self._decoder.decode(chunk) + if decoded: + return decoded + + async def aclose(self) -> None: + await self.transport_stream.aclose() + self._decoder.reset() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return self.transport_stream.extra_attributes + + +@dataclass(eq=False) +class TextSendStream(ObjectSendStream[str]): + """ + Sends strings to the wrapped stream as bytes using the given encoding. + + :param AnyByteSendStream transport_stream: any bytes-based send stream + :param str encoding: character encoding to use for encoding strings to bytes + (defaults to ``utf-8``) + :param str errors: handling scheme for encoding errors (defaults to ``strict``; see + the `codecs module documentation`_ for a comprehensive list of options) + + .. _codecs module documentation: + https://docs.python.org/3/library/codecs.html#codec-objects + """ + + transport_stream: AnyByteSendStream + encoding: InitVar[str] = "utf-8" + errors: str = "strict" + _encoder: Callable[..., tuple[bytes, int]] = field(init=False) + + def __post_init__(self, encoding: str) -> None: + self._encoder = codecs.getencoder(encoding) + + async def send(self, item: str) -> None: + encoded = self._encoder(item, self.errors)[0] + await self.transport_stream.send(encoded) + + async def aclose(self) -> None: + await self.transport_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return self.transport_stream.extra_attributes + + +@dataclass(eq=False) +class TextStream(ObjectStream[str]): + """ + A bidirectional stream that decodes bytes to strings on receive and encodes strings + to bytes on send. + + Extra attributes will be provided from both streams, with the receive stream + providing the values in case of a conflict. + + :param AnyByteStream transport_stream: any bytes-based stream + :param str encoding: character encoding to use for encoding/decoding strings to/from + bytes (defaults to ``utf-8``) + :param str errors: handling scheme for encoding errors (defaults to ``strict``; see + the `codecs module documentation`_ for a comprehensive list of options) + + .. _codecs module documentation: + https://docs.python.org/3/library/codecs.html#codec-objects + """ + + transport_stream: AnyByteStream + encoding: InitVar[str] = "utf-8" + errors: InitVar[str] = "strict" + _receive_stream: TextReceiveStream = field(init=False) + _send_stream: TextSendStream = field(init=False) + + def __post_init__(self, encoding: str, errors: str) -> None: + self._receive_stream = TextReceiveStream( + self.transport_stream, encoding=encoding, errors=errors + ) + self._send_stream = TextSendStream( + self.transport_stream, encoding=encoding, errors=errors + ) + + async def receive(self) -> str: + return await self._receive_stream.receive() + + async def send(self, item: str) -> None: + await self._send_stream.send(item) + + async def send_eof(self) -> None: + await self.transport_stream.send_eof() + + async def aclose(self) -> None: + await self._send_stream.aclose() + await self._receive_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self._send_stream.extra_attributes, + **self._receive_stream.extra_attributes, + } diff --git a/venv/lib/python3.11/site-packages/anyio/streams/tls.py b/venv/lib/python3.11/site-packages/anyio/streams/tls.py new file mode 100644 index 00000000..b6961bee --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/streams/tls.py @@ -0,0 +1,337 @@ +from __future__ import annotations + +import logging +import re +import ssl +import sys +from collections.abc import Callable, Mapping +from dataclasses import dataclass +from functools import wraps +from typing import Any, TypeVar + +from .. import ( + BrokenResourceError, + EndOfStream, + aclose_forcefully, + get_cancelled_exc_class, +) +from .._core._typedattr import TypedAttributeSet, typed_attribute +from ..abc import AnyByteStream, ByteStream, Listener, TaskGroup + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") +_PCTRTT = tuple[tuple[str, str], ...] +_PCTRTTT = tuple[_PCTRTT, ...] + + +class TLSAttribute(TypedAttributeSet): + """Contains Transport Layer Security related attributes.""" + + #: the selected ALPN protocol + alpn_protocol: str | None = typed_attribute() + #: the channel binding for type ``tls-unique`` + channel_binding_tls_unique: bytes = typed_attribute() + #: the selected cipher + cipher: tuple[str, str, int] = typed_attribute() + #: the peer certificate in dictionary form (see :meth:`ssl.SSLSocket.getpeercert` + # for more information) + peer_certificate: None | (dict[str, str | _PCTRTTT | _PCTRTT]) = typed_attribute() + #: the peer certificate in binary form + peer_certificate_binary: bytes | None = typed_attribute() + #: ``True`` if this is the server side of the connection + server_side: bool = typed_attribute() + #: ciphers shared by the client during the TLS handshake (``None`` if this is the + #: client side) + shared_ciphers: list[tuple[str, str, int]] | None = typed_attribute() + #: the :class:`~ssl.SSLObject` used for encryption + ssl_object: ssl.SSLObject = typed_attribute() + #: ``True`` if this stream does (and expects) a closing TLS handshake when the + #: stream is being closed + standard_compatible: bool = typed_attribute() + #: the TLS protocol version (e.g. ``TLSv1.2``) + tls_version: str = typed_attribute() + + +@dataclass(eq=False) +class TLSStream(ByteStream): + """ + A stream wrapper that encrypts all sent data and decrypts received data. + + This class has no public initializer; use :meth:`wrap` instead. + All extra attributes from :class:`~TLSAttribute` are supported. + + :var AnyByteStream transport_stream: the wrapped stream + + """ + + transport_stream: AnyByteStream + standard_compatible: bool + _ssl_object: ssl.SSLObject + _read_bio: ssl.MemoryBIO + _write_bio: ssl.MemoryBIO + + @classmethod + async def wrap( + cls, + transport_stream: AnyByteStream, + *, + server_side: bool | None = None, + hostname: str | None = None, + ssl_context: ssl.SSLContext | None = None, + standard_compatible: bool = True, + ) -> TLSStream: + """ + Wrap an existing stream with Transport Layer Security. + + This performs a TLS handshake with the peer. + + :param transport_stream: a bytes-transporting stream to wrap + :param server_side: ``True`` if this is the server side of the connection, + ``False`` if this is the client side (if omitted, will be set to ``False`` + if ``hostname`` has been provided, ``False`` otherwise). Used only to create + a default context when an explicit context has not been provided. + :param hostname: host name of the peer (if host name checking is desired) + :param ssl_context: the SSLContext object to use (if not provided, a secure + default will be created) + :param standard_compatible: if ``False``, skip the closing handshake when + closing the connection, and don't raise an exception if the peer does the + same + :raises ~ssl.SSLError: if the TLS handshake fails + + """ + if server_side is None: + server_side = not hostname + + if not ssl_context: + purpose = ( + ssl.Purpose.CLIENT_AUTH if server_side else ssl.Purpose.SERVER_AUTH + ) + ssl_context = ssl.create_default_context(purpose) + + # Re-enable detection of unexpected EOFs if it was disabled by Python + if hasattr(ssl, "OP_IGNORE_UNEXPECTED_EOF"): + ssl_context.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF + + bio_in = ssl.MemoryBIO() + bio_out = ssl.MemoryBIO() + ssl_object = ssl_context.wrap_bio( + bio_in, bio_out, server_side=server_side, server_hostname=hostname + ) + wrapper = cls( + transport_stream=transport_stream, + standard_compatible=standard_compatible, + _ssl_object=ssl_object, + _read_bio=bio_in, + _write_bio=bio_out, + ) + await wrapper._call_sslobject_method(ssl_object.do_handshake) + return wrapper + + async def _call_sslobject_method( + self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] + ) -> T_Retval: + while True: + try: + result = func(*args) + except ssl.SSLWantReadError: + try: + # Flush any pending writes first + if self._write_bio.pending: + await self.transport_stream.send(self._write_bio.read()) + + data = await self.transport_stream.receive() + except EndOfStream: + self._read_bio.write_eof() + except OSError as exc: + self._read_bio.write_eof() + self._write_bio.write_eof() + raise BrokenResourceError from exc + else: + self._read_bio.write(data) + except ssl.SSLWantWriteError: + await self.transport_stream.send(self._write_bio.read()) + except ssl.SSLSyscallError as exc: + self._read_bio.write_eof() + self._write_bio.write_eof() + raise BrokenResourceError from exc + except ssl.SSLError as exc: + self._read_bio.write_eof() + self._write_bio.write_eof() + if isinstance(exc, ssl.SSLEOFError) or ( + exc.strerror and "UNEXPECTED_EOF_WHILE_READING" in exc.strerror + ): + if self.standard_compatible: + raise BrokenResourceError from exc + else: + raise EndOfStream from None + + raise + else: + # Flush any pending writes first + if self._write_bio.pending: + await self.transport_stream.send(self._write_bio.read()) + + return result + + async def unwrap(self) -> tuple[AnyByteStream, bytes]: + """ + Does the TLS closing handshake. + + :return: a tuple of (wrapped byte stream, bytes left in the read buffer) + + """ + await self._call_sslobject_method(self._ssl_object.unwrap) + self._read_bio.write_eof() + self._write_bio.write_eof() + return self.transport_stream, self._read_bio.read() + + async def aclose(self) -> None: + if self.standard_compatible: + try: + await self.unwrap() + except BaseException: + await aclose_forcefully(self.transport_stream) + raise + + await self.transport_stream.aclose() + + async def receive(self, max_bytes: int = 65536) -> bytes: + data = await self._call_sslobject_method(self._ssl_object.read, max_bytes) + if not data: + raise EndOfStream + + return data + + async def send(self, item: bytes) -> None: + await self._call_sslobject_method(self._ssl_object.write, item) + + async def send_eof(self) -> None: + tls_version = self.extra(TLSAttribute.tls_version) + match = re.match(r"TLSv(\d+)(?:\.(\d+))?", tls_version) + if match: + major, minor = int(match.group(1)), int(match.group(2) or 0) + if (major, minor) < (1, 3): + raise NotImplementedError( + f"send_eof() requires at least TLSv1.3; current " + f"session uses {tls_version}" + ) + + raise NotImplementedError( + "send_eof() has not yet been implemented for TLS streams" + ) + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self.transport_stream.extra_attributes, + TLSAttribute.alpn_protocol: self._ssl_object.selected_alpn_protocol, + TLSAttribute.channel_binding_tls_unique: ( + self._ssl_object.get_channel_binding + ), + TLSAttribute.cipher: self._ssl_object.cipher, + TLSAttribute.peer_certificate: lambda: self._ssl_object.getpeercert(False), + TLSAttribute.peer_certificate_binary: lambda: self._ssl_object.getpeercert( + True + ), + TLSAttribute.server_side: lambda: self._ssl_object.server_side, + TLSAttribute.shared_ciphers: lambda: self._ssl_object.shared_ciphers() + if self._ssl_object.server_side + else None, + TLSAttribute.standard_compatible: lambda: self.standard_compatible, + TLSAttribute.ssl_object: lambda: self._ssl_object, + TLSAttribute.tls_version: self._ssl_object.version, + } + + +@dataclass(eq=False) +class TLSListener(Listener[TLSStream]): + """ + A convenience listener that wraps another listener and auto-negotiates a TLS session + on every accepted connection. + + If the TLS handshake times out or raises an exception, + :meth:`handle_handshake_error` is called to do whatever post-mortem processing is + deemed necessary. + + Supports only the :attr:`~TLSAttribute.standard_compatible` extra attribute. + + :param Listener listener: the listener to wrap + :param ssl_context: the SSL context object + :param standard_compatible: a flag passed through to :meth:`TLSStream.wrap` + :param handshake_timeout: time limit for the TLS handshake + (passed to :func:`~anyio.fail_after`) + """ + + listener: Listener[Any] + ssl_context: ssl.SSLContext + standard_compatible: bool = True + handshake_timeout: float = 30 + + @staticmethod + async def handle_handshake_error(exc: BaseException, stream: AnyByteStream) -> None: + """ + Handle an exception raised during the TLS handshake. + + This method does 3 things: + + #. Forcefully closes the original stream + #. Logs the exception (unless it was a cancellation exception) using the + ``anyio.streams.tls`` logger + #. Reraises the exception if it was a base exception or a cancellation exception + + :param exc: the exception + :param stream: the original stream + + """ + await aclose_forcefully(stream) + + # Log all except cancellation exceptions + if not isinstance(exc, get_cancelled_exc_class()): + # CPython (as of 3.11.5) returns incorrect `sys.exc_info()` here when using + # any asyncio implementation, so we explicitly pass the exception to log + # (https://github.com/python/cpython/issues/108668). Trio does not have this + # issue because it works around the CPython bug. + logging.getLogger(__name__).exception( + "Error during TLS handshake", exc_info=exc + ) + + # Only reraise base exceptions and cancellation exceptions + if not isinstance(exc, Exception) or isinstance(exc, get_cancelled_exc_class()): + raise + + async def serve( + self, + handler: Callable[[TLSStream], Any], + task_group: TaskGroup | None = None, + ) -> None: + @wraps(handler) + async def handler_wrapper(stream: AnyByteStream) -> None: + from .. import fail_after + + try: + with fail_after(self.handshake_timeout): + wrapped_stream = await TLSStream.wrap( + stream, + ssl_context=self.ssl_context, + standard_compatible=self.standard_compatible, + ) + except BaseException as exc: + await self.handle_handshake_error(exc, stream) + else: + await handler(wrapped_stream) + + await self.listener.serve(handler_wrapper, task_group) + + async def aclose(self) -> None: + await self.listener.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + TLSAttribute.standard_compatible: lambda: self.standard_compatible, + } diff --git a/venv/lib/python3.11/site-packages/anyio/to_process.py b/venv/lib/python3.11/site-packages/anyio/to_process.py new file mode 100644 index 00000000..5050dee2 --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/to_process.py @@ -0,0 +1,258 @@ +from __future__ import annotations + +import os +import pickle +import subprocess +import sys +from collections import deque +from collections.abc import Callable +from importlib.util import module_from_spec, spec_from_file_location +from typing import TypeVar, cast + +from ._core._eventloop import current_time, get_async_backend, get_cancelled_exc_class +from ._core._exceptions import BrokenWorkerProcess +from ._core._subprocesses import open_process +from ._core._synchronization import CapacityLimiter +from ._core._tasks import CancelScope, fail_after +from .abc import ByteReceiveStream, ByteSendStream, Process +from .lowlevel import RunVar, checkpoint_if_cancelled +from .streams.buffered import BufferedByteReceiveStream + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +WORKER_MAX_IDLE_TIME = 300 # 5 minutes + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + +_process_pool_workers: RunVar[set[Process]] = RunVar("_process_pool_workers") +_process_pool_idle_workers: RunVar[deque[tuple[Process, float]]] = RunVar( + "_process_pool_idle_workers" +) +_default_process_limiter: RunVar[CapacityLimiter] = RunVar("_default_process_limiter") + + +async def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + cancellable: bool = False, + limiter: CapacityLimiter | None = None, +) -> T_Retval: + """ + Call the given function with the given arguments in a worker process. + + If the ``cancellable`` option is enabled and the task waiting for its completion is + cancelled, the worker process running it will be abruptly terminated using SIGKILL + (or ``terminateProcess()`` on Windows). + + :param func: a callable + :param args: positional arguments for the callable + :param cancellable: ``True`` to allow cancellation of the operation while it's + running + :param limiter: capacity limiter to use to limit the total amount of processes + running (if omitted, the default limiter is used) + :return: an awaitable that yields the return value of the function. + + """ + + async def send_raw_command(pickled_cmd: bytes) -> object: + try: + await stdin.send(pickled_cmd) + response = await buffered.receive_until(b"\n", 50) + status, length = response.split(b" ") + if status not in (b"RETURN", b"EXCEPTION"): + raise RuntimeError( + f"Worker process returned unexpected response: {response!r}" + ) + + pickled_response = await buffered.receive_exactly(int(length)) + except BaseException as exc: + workers.discard(process) + try: + process.kill() + with CancelScope(shield=True): + await process.aclose() + except ProcessLookupError: + pass + + if isinstance(exc, get_cancelled_exc_class()): + raise + else: + raise BrokenWorkerProcess from exc + + retval = pickle.loads(pickled_response) + if status == b"EXCEPTION": + assert isinstance(retval, BaseException) + raise retval + else: + return retval + + # First pickle the request before trying to reserve a worker process + await checkpoint_if_cancelled() + request = pickle.dumps(("run", func, args), protocol=pickle.HIGHEST_PROTOCOL) + + # If this is the first run in this event loop thread, set up the necessary variables + try: + workers = _process_pool_workers.get() + idle_workers = _process_pool_idle_workers.get() + except LookupError: + workers = set() + idle_workers = deque() + _process_pool_workers.set(workers) + _process_pool_idle_workers.set(idle_workers) + get_async_backend().setup_process_pool_exit_at_shutdown(workers) + + async with limiter or current_default_process_limiter(): + # Pop processes from the pool (starting from the most recently used) until we + # find one that hasn't exited yet + process: Process + while idle_workers: + process, idle_since = idle_workers.pop() + if process.returncode is None: + stdin = cast(ByteSendStream, process.stdin) + buffered = BufferedByteReceiveStream( + cast(ByteReceiveStream, process.stdout) + ) + + # Prune any other workers that have been idle for WORKER_MAX_IDLE_TIME + # seconds or longer + now = current_time() + killed_processes: list[Process] = [] + while idle_workers: + if now - idle_workers[0][1] < WORKER_MAX_IDLE_TIME: + break + + process_to_kill, idle_since = idle_workers.popleft() + process_to_kill.kill() + workers.remove(process_to_kill) + killed_processes.append(process_to_kill) + + with CancelScope(shield=True): + for killed_process in killed_processes: + await killed_process.aclose() + + break + + workers.remove(process) + else: + command = [sys.executable, "-u", "-m", __name__] + process = await open_process( + command, stdin=subprocess.PIPE, stdout=subprocess.PIPE + ) + try: + stdin = cast(ByteSendStream, process.stdin) + buffered = BufferedByteReceiveStream( + cast(ByteReceiveStream, process.stdout) + ) + with fail_after(20): + message = await buffered.receive(6) + + if message != b"READY\n": + raise BrokenWorkerProcess( + f"Worker process returned unexpected response: {message!r}" + ) + + main_module_path = getattr(sys.modules["__main__"], "__file__", None) + pickled = pickle.dumps( + ("init", sys.path, main_module_path), + protocol=pickle.HIGHEST_PROTOCOL, + ) + await send_raw_command(pickled) + except (BrokenWorkerProcess, get_cancelled_exc_class()): + raise + except BaseException as exc: + process.kill() + raise BrokenWorkerProcess( + "Error during worker process initialization" + ) from exc + + workers.add(process) + + with CancelScope(shield=not cancellable): + try: + return cast(T_Retval, await send_raw_command(request)) + finally: + if process in workers: + idle_workers.append((process, current_time())) + + +def current_default_process_limiter() -> CapacityLimiter: + """ + Return the capacity limiter that is used by default to limit the number of worker + processes. + + :return: a capacity limiter object + + """ + try: + return _default_process_limiter.get() + except LookupError: + limiter = CapacityLimiter(os.cpu_count() or 2) + _default_process_limiter.set(limiter) + return limiter + + +def process_worker() -> None: + # Redirect standard streams to os.devnull so that user code won't interfere with the + # parent-worker communication + stdin = sys.stdin + stdout = sys.stdout + sys.stdin = open(os.devnull) + sys.stdout = open(os.devnull, "w") + + stdout.buffer.write(b"READY\n") + while True: + retval = exception = None + try: + command, *args = pickle.load(stdin.buffer) + except EOFError: + return + except BaseException as exc: + exception = exc + else: + if command == "run": + func, args = args + try: + retval = func(*args) + except BaseException as exc: + exception = exc + elif command == "init": + main_module_path: str | None + sys.path, main_module_path = args + del sys.modules["__main__"] + if main_module_path and os.path.isfile(main_module_path): + # Load the parent's main module but as __mp_main__ instead of + # __main__ (like multiprocessing does) to avoid infinite recursion + try: + spec = spec_from_file_location("__mp_main__", main_module_path) + if spec and spec.loader: + main = module_from_spec(spec) + spec.loader.exec_module(main) + sys.modules["__main__"] = main + except BaseException as exc: + exception = exc + try: + if exception is not None: + status = b"EXCEPTION" + pickled = pickle.dumps(exception, pickle.HIGHEST_PROTOCOL) + else: + status = b"RETURN" + pickled = pickle.dumps(retval, pickle.HIGHEST_PROTOCOL) + except BaseException as exc: + exception = exc + status = b"EXCEPTION" + pickled = pickle.dumps(exc, pickle.HIGHEST_PROTOCOL) + + stdout.buffer.write(b"%s %d\n" % (status, len(pickled))) + stdout.buffer.write(pickled) + + # Respect SIGTERM + if isinstance(exception, SystemExit): + raise exception + + +if __name__ == "__main__": + process_worker() diff --git a/venv/lib/python3.11/site-packages/anyio/to_thread.py b/venv/lib/python3.11/site-packages/anyio/to_thread.py new file mode 100644 index 00000000..5070516e --- /dev/null +++ b/venv/lib/python3.11/site-packages/anyio/to_thread.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +import sys +from collections.abc import Callable +from typing import TypeVar +from warnings import warn + +from ._core._eventloop import get_async_backend +from .abc import CapacityLimiter + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + + +async def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + abandon_on_cancel: bool = False, + cancellable: bool | None = None, + limiter: CapacityLimiter | None = None, +) -> T_Retval: + """ + Call the given function with the given arguments in a worker thread. + + If the ``cancellable`` option is enabled and the task waiting for its completion is + cancelled, the thread will still run its course but its return value (or any raised + exception) will be ignored. + + :param func: a callable + :param args: positional arguments for the callable + :param abandon_on_cancel: ``True`` to abandon the thread (leaving it to run + unchecked on own) if the host task is cancelled, ``False`` to ignore + cancellations in the host task until the operation has completed in the worker + thread + :param cancellable: deprecated alias of ``abandon_on_cancel``; will override + ``abandon_on_cancel`` if both parameters are passed + :param limiter: capacity limiter to use to limit the total amount of threads running + (if omitted, the default limiter is used) + :return: an awaitable that yields the return value of the function. + + """ + if cancellable is not None: + abandon_on_cancel = cancellable + warn( + "The `cancellable=` keyword argument to `anyio.to_thread.run_sync` is " + "deprecated since AnyIO 4.1.0; use `abandon_on_cancel=` instead", + DeprecationWarning, + stacklevel=2, + ) + + return await get_async_backend().run_sync_in_worker_thread( + func, args, abandon_on_cancel=abandon_on_cancel, limiter=limiter + ) + + +def current_default_thread_limiter() -> CapacityLimiter: + """ + Return the capacity limiter that is used by default to limit the number of + concurrent threads. + + :return: a capacity limiter object + + """ + return get_async_backend().current_default_thread_limiter() diff --git a/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/AUTHORS b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/AUTHORS new file mode 100644 index 00000000..1f14fe07 --- /dev/null +++ b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/AUTHORS @@ -0,0 +1,49 @@ +Behold, mortal, the origins of Beautiful Soup... +================================================ + +Leonard Richardson is the primary maintainer. + +Aaron DeVore and Isaac Muse have made significant contributions to the +code base. + +Mark Pilgrim provided the encoding detection code that forms the base +of UnicodeDammit. + +Thomas Kluyver and Ezio Melotti finished the work of getting Beautiful +Soup 4 working under Python 3. + +Simon Willison wrote soupselect, which was used to make Beautiful Soup +support CSS selectors. Isaac Muse wrote SoupSieve, which made it +possible to _remove_ the CSS selector code from Beautiful Soup. + +Sam Ruby helped with a lot of edge cases. + +Jonathan Ellis was awarded the prestigious Beau Potage D'Or for his +work in solving the nestable tags conundrum. + +An incomplete list of people have contributed patches to Beautiful +Soup: + + Istvan Albert, Andrew Lin, Anthony Baxter, Oliver Beattie, Andrew +Boyko, Tony Chang, Francisco Canas, "Delong", Zephyr Fang, Fuzzy, +Roman Gaufman, Yoni Gilad, Richie Hindle, Toshihiro Kamiya, Peteris +Krumins, Kent Johnson, Marek Kapolka, Andreas Kostyrka, Roel Kramer, +Ben Last, Robert Leftwich, Stefaan Lippens, "liquider", Staffan +Malmgren, Ksenia Marasanova, JP Moins, Adam Monsen, John Nagle, "Jon", +Ed Oskiewicz, Martijn Peters, Greg Phillips, Giles Radford, Stefano +Revera, Arthur Rudolph, Marko Samastur, James Salter, Jouni Seppnen, +Alexander Schmolck, Tim Shirley, Geoffrey Sneddon, Ville Skytt, +"Vikas", Jens Svalgaard, Andy Theyers, Eric Weiser, Glyn Webster, John +Wiseman, Paul Wright, Danny Yoo + +An incomplete list of people who made suggestions or found bugs or +found ways to break Beautiful Soup: + + Hanno Bck, Matteo Bertini, Chris Curvey, Simon Cusack, Bruce Eckel, + Matt Ernst, Michael Foord, Tom Harris, Bill de hOra, Donald Howes, + Matt Patterson, Scott Roberts, Steve Strassmann, Mike Williams, + warchild at redho dot com, Sami Kuisma, Carlos Rocha, Bob Hutchison, + Joren Mc, Michal Migurski, John Kleven, Tim Heaney, Tripp Lilley, Ed + Summers, Dennis Sutch, Chris Smith, Aaron Swartz, Stuart + Turner, Greg Edwards, Kevin J Kalupson, Nikos Kouremenos, Artur de + Sousa Rocha, Yichun Wei, Per Vognsen diff --git a/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/COPYING.txt b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/COPYING.txt new file mode 100644 index 00000000..fb6ae69c --- /dev/null +++ b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/COPYING.txt @@ -0,0 +1,27 @@ +Beautiful Soup is made available under the MIT license: + + Copyright (c) 2004-2017 Leonard Richardson + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +Beautiful Soup incorporates code from the html5lib library, which is +also made available under the MIT license. Copyright (c) 2006-2013 +James Graham and other contributors diff --git a/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/INSTALLER b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/LICENSE b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/LICENSE new file mode 100644 index 00000000..4c068bab --- /dev/null +++ b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/LICENSE @@ -0,0 +1,30 @@ +Beautiful Soup is made available under the MIT license: + + Copyright (c) 2004-2019 Leonard Richardson + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +Beautiful Soup incorporates code from the html5lib library, which is +also made available under the MIT license. Copyright (c) 2006-2013 +James Graham and other contributors + +Beautiful Soup depends on the soupsieve library, which is also made +available under the MIT license. Copyright (c) 2018 Isaac Muse diff --git a/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/METADATA b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/METADATA new file mode 100644 index 00000000..88c03b6f --- /dev/null +++ b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/METADATA @@ -0,0 +1,132 @@ +Metadata-Version: 2.1 +Name: beautifulsoup4 +Version: 4.9.3 +Summary: Screen-scraping library +Home-page: http://www.crummy.com/software/BeautifulSoup/bs4/ +Author: Leonard Richardson +Author-email: leonardr@segfault.org +License: MIT +Download-URL: http://www.crummy.com/software/BeautifulSoup/bs4/download/ +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Topic :: Text Processing :: Markup :: XML +Classifier: Topic :: Text Processing :: Markup :: SGML +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Description-Content-Type: text/markdown +Requires-Dist: soupsieve (<2.0,>1.2) ; python_version < "3.0" +Requires-Dist: soupsieve (>1.2) ; python_version >= "3.0" +Provides-Extra: html5lib +Requires-Dist: html5lib ; extra == 'html5lib' +Provides-Extra: lxml +Requires-Dist: lxml ; extra == 'lxml' + +Beautiful Soup is a library that makes it easy to scrape information +from web pages. It sits atop an HTML or XML parser, providing Pythonic +idioms for iterating, searching, and modifying the parse tree. + +# Quick start + +``` +>>> from bs4 import BeautifulSoup +>>> soup = BeautifulSoup("

SomebadHTML") +>>> print(soup.prettify()) + + +

+ Some + + bad + + HTML + + +

+ + +>>> soup.find(text="bad") +'bad' +>>> soup.i +HTML +# +>>> soup = BeautifulSoup("SomebadXML", "xml") +# +>>> print(soup.prettify()) + + + Some + + bad + + XML + + +``` + +To go beyond the basics, [comprehensive documentation is available](http://www.crummy.com/software/BeautifulSoup/bs4/doc/). + +# Links + +* [Homepage](http://www.crummy.com/software/BeautifulSoup/bs4/) +* [Documentation](http://www.crummy.com/software/BeautifulSoup/bs4/doc/) +* [Discussion group](http://groups.google.com/group/beautifulsoup/) +* [Development](https://code.launchpad.net/beautifulsoup/) +* [Bug tracker](https://bugs.launchpad.net/beautifulsoup/) +* [Complete changelog](https://bazaar.launchpad.net/~leonardr/beautifulsoup/bs4/view/head:/CHANGELOG) + +# Note on Python 2 sunsetting + +Since 2012, Beautiful Soup has been developed as a Python 2 library +which is automatically converted to Python 3 code as necessary. This +makes it impossible to take advantage of some features of Python +3. + +For this reason, I plan to discontinue Beautiful Soup's Python 2 +support at some point after December 31, 2020: one year after the +sunset date for Python 2 itself. Beyond that point, new Beautiful Soup +development will exclusively target Python 3. Of course, older +releases of Beautiful Soup, which support both versions, will continue +to be available. + +# Supporting the project + +If you use Beautiful Soup as part of your professional work, please consider a +[Tidelift subscription](https://tidelift.com/subscription/pkg/pypi-beautifulsoup4?utm_source=pypi-beautifulsoup4&utm_medium=referral&utm_campaign=readme). +This will support many of the free software projects your organization +depends on, not just Beautiful Soup. + +If you use Beautiful Soup for personal projects, the best way to say +thank you is to read +[Tool Safety](https://www.crummy.com/software/BeautifulSoup/zine/), a zine I +wrote about what Beautiful Soup has taught me about software +development. + +# Building the documentation + +The bs4/doc/ directory contains full documentation in Sphinx +format. Run `make html` in that directory to create HTML +documentation. + +# Running the unit tests + +Beautiful Soup supports unit test discovery from the project root directory: + +``` +$ nosetests +``` + +``` +$ python -m unittest discover -s bs4 +``` + +If you checked out the source tree, you should see a script in the +home directory called test-all-versions. This script will run the unit +tests under Python 2, then create a temporary Python 3 conversion of +the source and run the unit tests again under Python 3. + + diff --git a/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/RECORD b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/RECORD new file mode 100644 index 00000000..ac22e873 --- /dev/null +++ b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/RECORD @@ -0,0 +1,45 @@ +beautifulsoup4-4.9.3.dist-info/AUTHORS,sha256=uSIdbrBb1sobdXl7VrlUvuvim2dN9kF3MH4Edn0WKGE,2176 +beautifulsoup4-4.9.3.dist-info/COPYING.txt,sha256=pH6lEjYJhGT-C09Vl0NZC1MwVtngD0nsv4Apn6tH4jE,1315 +beautifulsoup4-4.9.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +beautifulsoup4-4.9.3.dist-info/LICENSE,sha256=ynIn3bnu1syAnhV_Z7Ag543eBjJAAB0RhW-FxJy25CM,1447 +beautifulsoup4-4.9.3.dist-info/METADATA,sha256=iY3LTmChfV6eWiLC4MPfy_FZL9pllucV7IzOVrF117Q,4190 +beautifulsoup4-4.9.3.dist-info/RECORD,, +beautifulsoup4-4.9.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +beautifulsoup4-4.9.3.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92 +beautifulsoup4-4.9.3.dist-info/top_level.txt,sha256=H8VT-IuPWLzQqwG9_eChjXDJ1z0H9RRebdSR90Bjnkw,4 +bs4/__init__.py,sha256=Xg4iRoWU7bD9HQo8OCHh6BP9g74T8n37QjQFhT3_GLA,32102 +bs4/__pycache__/__init__.cpython-311.pyc,, +bs4/__pycache__/dammit.cpython-311.pyc,, +bs4/__pycache__/diagnose.cpython-311.pyc,, +bs4/__pycache__/element.cpython-311.pyc,, +bs4/__pycache__/formatter.cpython-311.pyc,, +bs4/__pycache__/testing.cpython-311.pyc,, +bs4/builder/__init__.py,sha256=dJcyrx6CdAuKQhg5KfpKTAEQeDKvIW3PWHo-0aKDaLg,19777 +bs4/builder/__pycache__/__init__.cpython-311.pyc,, +bs4/builder/__pycache__/_html5lib.cpython-311.pyc,, +bs4/builder/__pycache__/_htmlparser.cpython-311.pyc,, +bs4/builder/__pycache__/_lxml.cpython-311.pyc,, +bs4/builder/_html5lib.py,sha256=hDxlzVrAku_eU7zEt4gZ-sAXzG58GvkLfMz6P4zUqoA,18748 +bs4/builder/_htmlparser.py,sha256=80-Nb3QXS3PrSa-ReEBTh1X8-C2HkLFLLAbC0fSKuoU,18405 +bs4/builder/_lxml.py,sha256=e4w91RZi3NII_QYe2e1-EiN_BxQtgJPSRwQ8Xgz41ZA,12234 +bs4/dammit.py,sha256=k_XPB3kbZsHM01ckf9BxCUB2Eu2dIQ3d3DDt7UEv9RA,34130 +bs4/diagnose.py,sha256=WOzytCTkvqh_fGhqYlMyaYVjtH50w4jbdf1Fd0iundE,7755 +bs4/element.py,sha256=DbavvJfetuG3GWM_mOsNPVk7WuyQtJQ6qti8FFzwZvE,81650 +bs4/formatter.py,sha256=Wayv1d6fUc9BSCa2k9uhvWwm89xCukdtJhyi9Sxvkuc,5654 +bs4/testing.py,sha256=8C72bkPqP_zPzpP-f9i1qtlNuKonHD-VtamNxUKwIBE,45930 +bs4/tests/__init__.py,sha256=bdUBDE750n7qNEfue7-3a1fBaUxJlvZMkvJvZa-lbYs,27 +bs4/tests/__pycache__/__init__.cpython-311.pyc,, +bs4/tests/__pycache__/test_builder_registry.cpython-311.pyc,, +bs4/tests/__pycache__/test_docs.cpython-311.pyc,, +bs4/tests/__pycache__/test_html5lib.cpython-311.pyc,, +bs4/tests/__pycache__/test_htmlparser.cpython-311.pyc,, +bs4/tests/__pycache__/test_lxml.cpython-311.pyc,, +bs4/tests/__pycache__/test_soup.cpython-311.pyc,, +bs4/tests/__pycache__/test_tree.cpython-311.pyc,, +bs4/tests/test_builder_registry.py,sha256=pllfRpArh9TYhjjRUiu1wITr9Ryyv4hiaAtRjij-k4E,5582 +bs4/tests/test_docs.py,sha256=FXfz2bGL4Xe0q6duwpmg9hmFiZuU4DVJPNZ0hTb6aH4,1067 +bs4/tests/test_html5lib.py,sha256=eWnLGHek_RO_TMq0Ixpb1RF3BEDrvhenMf2eaEBjjsg,6754 +bs4/tests/test_htmlparser.py,sha256=3294XvFbWVe0AYoTlnLPEDW_a0Om0BKRcsrwlJbxUaI,3941 +bs4/tests/test_lxml.py,sha256=xJr8eDrtHSb_vQw88lYEKyfdM1Hel4-dBaz14vQq78M,4105 +bs4/tests/test_soup.py,sha256=EhE1dhHKyctNu0y2l0ql6FOHg9qliEt8Kh7jfCx1lDw,29303 +bs4/tests/test_tree.py,sha256=UsXGvnlTBKixno0sbnFllZo6pm-7EiSvOY1dOeWcqFg,89437 diff --git a/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/REQUESTED b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/WHEEL b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/WHEEL new file mode 100644 index 00000000..b552003f --- /dev/null +++ b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.34.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/top_level.txt b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/top_level.txt new file mode 100644 index 00000000..13154420 --- /dev/null +++ b/venv/lib/python3.11/site-packages/beautifulsoup4-4.9.3.dist-info/top_level.txt @@ -0,0 +1 @@ +bs4 diff --git a/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/INSTALLER b/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/LICENSE.txt b/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/LICENSE.txt new file mode 100644 index 00000000..79c9825a --- /dev/null +++ b/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright 2010 Jason Kirtland + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/METADATA b/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/METADATA new file mode 100644 index 00000000..6d343f57 --- /dev/null +++ b/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/METADATA @@ -0,0 +1,60 @@ +Metadata-Version: 2.3 +Name: blinker +Version: 1.9.0 +Summary: Fast, simple object-to-object and broadcast signaling +Author: Jason Kirtland +Maintainer-email: Pallets Ecosystem +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Typing :: Typed +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://blinker.readthedocs.io +Project-URL: Source, https://github.com/pallets-eco/blinker/ + +# Blinker + +Blinker provides a fast dispatching system that allows any number of +interested parties to subscribe to events, or "signals". + + +## Pallets Community Ecosystem + +> [!IMPORTANT]\ +> This project is part of the Pallets Community Ecosystem. Pallets is the open +> source organization that maintains Flask; Pallets-Eco enables community +> maintenance of related projects. If you are interested in helping maintain +> this project, please reach out on [the Pallets Discord server][discord]. +> +> [discord]: https://discord.gg/pallets + + +## Example + +Signal receivers can subscribe to specific senders or receive signals +sent by any sender. + +```pycon +>>> from blinker import signal +>>> started = signal('round-started') +>>> def each(round): +... print(f"Round {round}") +... +>>> started.connect(each) + +>>> def round_two(round): +... print("This is round two.") +... +>>> started.connect(round_two, sender=2) + +>>> for round in range(1, 4): +... started.send(round) +... +Round 1! +Round 2! +This is round two. +Round 3! +``` + diff --git a/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/RECORD b/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/RECORD new file mode 100644 index 00000000..52d1667f --- /dev/null +++ b/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/RECORD @@ -0,0 +1,12 @@ +blinker-1.9.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +blinker-1.9.0.dist-info/LICENSE.txt,sha256=nrc6HzhZekqhcCXSrhvjg5Ykx5XphdTw6Xac4p-spGc,1054 +blinker-1.9.0.dist-info/METADATA,sha256=uIRiM8wjjbHkCtbCyTvctU37IAZk0kEe5kxAld1dvzA,1633 +blinker-1.9.0.dist-info/RECORD,, +blinker-1.9.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82 +blinker/__init__.py,sha256=I2EdZqpy4LyjX17Hn1yzJGWCjeLaVaPzsMgHkLfj_cQ,317 +blinker/__pycache__/__init__.cpython-311.pyc,, +blinker/__pycache__/_utilities.cpython-311.pyc,, +blinker/__pycache__/base.cpython-311.pyc,, +blinker/_utilities.py,sha256=0J7eeXXTUx0Ivf8asfpx0ycVkp0Eqfqnj117x2mYX9E,1675 +blinker/base.py,sha256=QpDuvXXcwJF49lUBcH5BiST46Rz9wSG7VW_p7N_027M,19132 +blinker/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/WHEEL b/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/WHEEL new file mode 100644 index 00000000..e3c6feef --- /dev/null +++ b/venv/lib/python3.11/site-packages/blinker-1.9.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.10.1 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.11/site-packages/blinker/__init__.py b/venv/lib/python3.11/site-packages/blinker/__init__.py new file mode 100644 index 00000000..1772fa4a --- /dev/null +++ b/venv/lib/python3.11/site-packages/blinker/__init__.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from .base import ANY +from .base import default_namespace +from .base import NamedSignal +from .base import Namespace +from .base import Signal +from .base import signal + +__all__ = [ + "ANY", + "default_namespace", + "NamedSignal", + "Namespace", + "Signal", + "signal", +] diff --git a/venv/lib/python3.11/site-packages/blinker/_utilities.py b/venv/lib/python3.11/site-packages/blinker/_utilities.py new file mode 100644 index 00000000..000c902a --- /dev/null +++ b/venv/lib/python3.11/site-packages/blinker/_utilities.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +import collections.abc as c +import inspect +import typing as t +from weakref import ref +from weakref import WeakMethod + +T = t.TypeVar("T") + + +class Symbol: + """A constant symbol, nicer than ``object()``. Repeated calls return the + same instance. + + >>> Symbol('foo') is Symbol('foo') + True + >>> Symbol('foo') + foo + """ + + symbols: t.ClassVar[dict[str, Symbol]] = {} + + def __new__(cls, name: str) -> Symbol: + if name in cls.symbols: + return cls.symbols[name] + + obj = super().__new__(cls) + cls.symbols[name] = obj + return obj + + def __init__(self, name: str) -> None: + self.name = name + + def __repr__(self) -> str: + return self.name + + def __getnewargs__(self) -> tuple[t.Any, ...]: + return (self.name,) + + +def make_id(obj: object) -> c.Hashable: + """Get a stable identifier for a receiver or sender, to be used as a dict + key or in a set. + """ + if inspect.ismethod(obj): + # The id of a bound method is not stable, but the id of the unbound + # function and instance are. + return id(obj.__func__), id(obj.__self__) + + if isinstance(obj, (str, int)): + # Instances with the same value always compare equal and have the same + # hash, even if the id may change. + return obj + + # Assume other types are not hashable but will always be the same instance. + return id(obj) + + +def make_ref(obj: T, callback: c.Callable[[ref[T]], None] | None = None) -> ref[T]: + if inspect.ismethod(obj): + return WeakMethod(obj, callback) # type: ignore[arg-type, return-value] + + return ref(obj, callback) diff --git a/venv/lib/python3.11/site-packages/blinker/base.py b/venv/lib/python3.11/site-packages/blinker/base.py new file mode 100644 index 00000000..d051b94a --- /dev/null +++ b/venv/lib/python3.11/site-packages/blinker/base.py @@ -0,0 +1,512 @@ +from __future__ import annotations + +import collections.abc as c +import sys +import typing as t +import weakref +from collections import defaultdict +from contextlib import contextmanager +from functools import cached_property +from inspect import iscoroutinefunction + +from ._utilities import make_id +from ._utilities import make_ref +from ._utilities import Symbol + +F = t.TypeVar("F", bound=c.Callable[..., t.Any]) + +ANY = Symbol("ANY") +"""Symbol for "any sender".""" + +ANY_ID = 0 + + +class Signal: + """A notification emitter. + + :param doc: The docstring for the signal. + """ + + ANY = ANY + """An alias for the :data:`~blinker.ANY` sender symbol.""" + + set_class: type[set[t.Any]] = set + """The set class to use for tracking connected receivers and senders. + Python's ``set`` is unordered. If receivers must be dispatched in the order + they were connected, an ordered set implementation can be used. + + .. versionadded:: 1.7 + """ + + @cached_property + def receiver_connected(self) -> Signal: + """Emitted at the end of each :meth:`connect` call. + + The signal sender is the signal instance, and the :meth:`connect` + arguments are passed through: ``receiver``, ``sender``, and ``weak``. + + .. versionadded:: 1.2 + """ + return Signal(doc="Emitted after a receiver connects.") + + @cached_property + def receiver_disconnected(self) -> Signal: + """Emitted at the end of each :meth:`disconnect` call. + + The sender is the signal instance, and the :meth:`disconnect` arguments + are passed through: ``receiver`` and ``sender``. + + This signal is emitted **only** when :meth:`disconnect` is called + explicitly. This signal cannot be emitted by an automatic disconnect + when a weakly referenced receiver or sender goes out of scope, as the + instance is no longer be available to be used as the sender for this + signal. + + An alternative approach is available by subscribing to + :attr:`receiver_connected` and setting up a custom weakref cleanup + callback on weak receivers and senders. + + .. versionadded:: 1.2 + """ + return Signal(doc="Emitted after a receiver disconnects.") + + def __init__(self, doc: str | None = None) -> None: + if doc: + self.__doc__ = doc + + self.receivers: dict[ + t.Any, weakref.ref[c.Callable[..., t.Any]] | c.Callable[..., t.Any] + ] = {} + """The map of connected receivers. Useful to quickly check if any + receivers are connected to the signal: ``if s.receivers:``. The + structure and data is not part of the public API, but checking its + boolean value is. + """ + + self.is_muted: bool = False + self._by_receiver: dict[t.Any, set[t.Any]] = defaultdict(self.set_class) + self._by_sender: dict[t.Any, set[t.Any]] = defaultdict(self.set_class) + self._weak_senders: dict[t.Any, weakref.ref[t.Any]] = {} + + def connect(self, receiver: F, sender: t.Any = ANY, weak: bool = True) -> F: + """Connect ``receiver`` to be called when the signal is sent by + ``sender``. + + :param receiver: The callable to call when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument + along with any extra keyword arguments. + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. A receiver may be connected + to multiple senders by calling :meth:`connect` multiple times. + :param weak: Track the receiver with a :mod:`weakref`. The receiver will + be automatically disconnected when it is garbage collected. When + connecting a receiver defined within a function, set to ``False``, + otherwise it will be disconnected when the function scope ends. + """ + receiver_id = make_id(receiver) + sender_id = ANY_ID if sender is ANY else make_id(sender) + + if weak: + self.receivers[receiver_id] = make_ref( + receiver, self._make_cleanup_receiver(receiver_id) + ) + else: + self.receivers[receiver_id] = receiver + + self._by_sender[sender_id].add(receiver_id) + self._by_receiver[receiver_id].add(sender_id) + + if sender is not ANY and sender_id not in self._weak_senders: + # store a cleanup for weakref-able senders + try: + self._weak_senders[sender_id] = make_ref( + sender, self._make_cleanup_sender(sender_id) + ) + except TypeError: + pass + + if "receiver_connected" in self.__dict__ and self.receiver_connected.receivers: + try: + self.receiver_connected.send( + self, receiver=receiver, sender=sender, weak=weak + ) + except TypeError: + # TODO no explanation or test for this + self.disconnect(receiver, sender) + raise + + return receiver + + def connect_via(self, sender: t.Any, weak: bool = False) -> c.Callable[[F], F]: + """Connect the decorated function to be called when the signal is sent + by ``sender``. + + The decorated function will be called when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument along + with any extra keyword arguments. + + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. A receiver may be connected + to multiple senders by calling :meth:`connect` multiple times. + :param weak: Track the receiver with a :mod:`weakref`. The receiver will + be automatically disconnected when it is garbage collected. When + connecting a receiver defined within a function, set to ``False``, + otherwise it will be disconnected when the function scope ends.= + + .. versionadded:: 1.1 + """ + + def decorator(fn: F) -> F: + self.connect(fn, sender, weak) + return fn + + return decorator + + @contextmanager + def connected_to( + self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY + ) -> c.Generator[None, None, None]: + """A context manager that temporarily connects ``receiver`` to the + signal while a ``with`` block executes. When the block exits, the + receiver is disconnected. Useful for tests. + + :param receiver: The callable to call when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument + along with any extra keyword arguments. + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. + + .. versionadded:: 1.1 + """ + self.connect(receiver, sender=sender, weak=False) + + try: + yield None + finally: + self.disconnect(receiver) + + @contextmanager + def muted(self) -> c.Generator[None, None, None]: + """A context manager that temporarily disables the signal. No receivers + will be called if the signal is sent, until the ``with`` block exits. + Useful for tests. + """ + self.is_muted = True + + try: + yield None + finally: + self.is_muted = False + + def send( + self, + sender: t.Any | None = None, + /, + *, + _async_wrapper: c.Callable[ + [c.Callable[..., c.Coroutine[t.Any, t.Any, t.Any]]], c.Callable[..., t.Any] + ] + | None = None, + **kwargs: t.Any, + ) -> list[tuple[c.Callable[..., t.Any], t.Any]]: + """Call all receivers that are connected to the given ``sender`` + or :data:`ANY`. Each receiver is called with ``sender`` as a positional + argument along with any extra keyword arguments. Return a list of + ``(receiver, return value)`` tuples. + + The order receivers are called is undefined, but can be influenced by + setting :attr:`set_class`. + + If a receiver raises an exception, that exception will propagate up. + This makes debugging straightforward, with an assumption that correctly + implemented receivers will not raise. + + :param sender: Call receivers connected to this sender, in addition to + those connected to :data:`ANY`. + :param _async_wrapper: Will be called on any receivers that are async + coroutines to turn them into sync callables. For example, could run + the receiver with an event loop. + :param kwargs: Extra keyword arguments to pass to each receiver. + + .. versionchanged:: 1.7 + Added the ``_async_wrapper`` argument. + """ + if self.is_muted: + return [] + + results = [] + + for receiver in self.receivers_for(sender): + if iscoroutinefunction(receiver): + if _async_wrapper is None: + raise RuntimeError("Cannot send to a coroutine function.") + + result = _async_wrapper(receiver)(sender, **kwargs) + else: + result = receiver(sender, **kwargs) + + results.append((receiver, result)) + + return results + + async def send_async( + self, + sender: t.Any | None = None, + /, + *, + _sync_wrapper: c.Callable[ + [c.Callable[..., t.Any]], c.Callable[..., c.Coroutine[t.Any, t.Any, t.Any]] + ] + | None = None, + **kwargs: t.Any, + ) -> list[tuple[c.Callable[..., t.Any], t.Any]]: + """Await all receivers that are connected to the given ``sender`` + or :data:`ANY`. Each receiver is called with ``sender`` as a positional + argument along with any extra keyword arguments. Return a list of + ``(receiver, return value)`` tuples. + + The order receivers are called is undefined, but can be influenced by + setting :attr:`set_class`. + + If a receiver raises an exception, that exception will propagate up. + This makes debugging straightforward, with an assumption that correctly + implemented receivers will not raise. + + :param sender: Call receivers connected to this sender, in addition to + those connected to :data:`ANY`. + :param _sync_wrapper: Will be called on any receivers that are sync + callables to turn them into async coroutines. For example, + could call the receiver in a thread. + :param kwargs: Extra keyword arguments to pass to each receiver. + + .. versionadded:: 1.7 + """ + if self.is_muted: + return [] + + results = [] + + for receiver in self.receivers_for(sender): + if not iscoroutinefunction(receiver): + if _sync_wrapper is None: + raise RuntimeError("Cannot send to a non-coroutine function.") + + result = await _sync_wrapper(receiver)(sender, **kwargs) + else: + result = await receiver(sender, **kwargs) + + results.append((receiver, result)) + + return results + + def has_receivers_for(self, sender: t.Any) -> bool: + """Check if there is at least one receiver that will be called with the + given ``sender``. A receiver connected to :data:`ANY` will always be + called, regardless of sender. Does not check if weakly referenced + receivers are still live. See :meth:`receivers_for` for a stronger + search. + + :param sender: Check for receivers connected to this sender, in addition + to those connected to :data:`ANY`. + """ + if not self.receivers: + return False + + if self._by_sender[ANY_ID]: + return True + + if sender is ANY: + return False + + return make_id(sender) in self._by_sender + + def receivers_for( + self, sender: t.Any + ) -> c.Generator[c.Callable[..., t.Any], None, None]: + """Yield each receiver to be called for ``sender``, in addition to those + to be called for :data:`ANY`. Weakly referenced receivers that are not + live will be disconnected and skipped. + + :param sender: Yield receivers connected to this sender, in addition + to those connected to :data:`ANY`. + """ + # TODO: test receivers_for(ANY) + if not self.receivers: + return + + sender_id = make_id(sender) + + if sender_id in self._by_sender: + ids = self._by_sender[ANY_ID] | self._by_sender[sender_id] + else: + ids = self._by_sender[ANY_ID].copy() + + for receiver_id in ids: + receiver = self.receivers.get(receiver_id) + + if receiver is None: + continue + + if isinstance(receiver, weakref.ref): + strong = receiver() + + if strong is None: + self._disconnect(receiver_id, ANY_ID) + continue + + yield strong + else: + yield receiver + + def disconnect(self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY) -> None: + """Disconnect ``receiver`` from being called when the signal is sent by + ``sender``. + + :param receiver: A connected receiver callable. + :param sender: Disconnect from only this sender. By default, disconnect + from all senders. + """ + sender_id: c.Hashable + + if sender is ANY: + sender_id = ANY_ID + else: + sender_id = make_id(sender) + + receiver_id = make_id(receiver) + self._disconnect(receiver_id, sender_id) + + if ( + "receiver_disconnected" in self.__dict__ + and self.receiver_disconnected.receivers + ): + self.receiver_disconnected.send(self, receiver=receiver, sender=sender) + + def _disconnect(self, receiver_id: c.Hashable, sender_id: c.Hashable) -> None: + if sender_id == ANY_ID: + if self._by_receiver.pop(receiver_id, None) is not None: + for bucket in self._by_sender.values(): + bucket.discard(receiver_id) + + self.receivers.pop(receiver_id, None) + else: + self._by_sender[sender_id].discard(receiver_id) + self._by_receiver[receiver_id].discard(sender_id) + + def _make_cleanup_receiver( + self, receiver_id: c.Hashable + ) -> c.Callable[[weakref.ref[c.Callable[..., t.Any]]], None]: + """Create a callback function to disconnect a weakly referenced + receiver when it is garbage collected. + """ + + def cleanup(ref: weakref.ref[c.Callable[..., t.Any]]) -> None: + # If the interpreter is shutting down, disconnecting can result in a + # weird ignored exception. Don't call it in that case. + if not sys.is_finalizing(): + self._disconnect(receiver_id, ANY_ID) + + return cleanup + + def _make_cleanup_sender( + self, sender_id: c.Hashable + ) -> c.Callable[[weakref.ref[t.Any]], None]: + """Create a callback function to disconnect all receivers for a weakly + referenced sender when it is garbage collected. + """ + assert sender_id != ANY_ID + + def cleanup(ref: weakref.ref[t.Any]) -> None: + self._weak_senders.pop(sender_id, None) + + for receiver_id in self._by_sender.pop(sender_id, ()): + self._by_receiver[receiver_id].discard(sender_id) + + return cleanup + + def _cleanup_bookkeeping(self) -> None: + """Prune unused sender/receiver bookkeeping. Not threadsafe. + + Connecting & disconnecting leaves behind a small amount of bookkeeping + data. Typical workloads using Blinker, for example in most web apps, + Flask, CLI scripts, etc., are not adversely affected by this + bookkeeping. + + With a long-running process performing dynamic signal routing with high + volume, e.g. connecting to function closures, senders are all unique + object instances. Doing all of this over and over may cause memory usage + to grow due to extraneous bookkeeping. (An empty ``set`` for each stale + sender/receiver pair.) + + This method will prune that bookkeeping away, with the caveat that such + pruning is not threadsafe. The risk is that cleanup of a fully + disconnected receiver/sender pair occurs while another thread is + connecting that same pair. If you are in the highly dynamic, unique + receiver/sender situation that has lead you to this method, that failure + mode is perhaps not a big deal for you. + """ + for mapping in (self._by_sender, self._by_receiver): + for ident, bucket in list(mapping.items()): + if not bucket: + mapping.pop(ident, None) + + def _clear_state(self) -> None: + """Disconnect all receivers and senders. Useful for tests.""" + self._weak_senders.clear() + self.receivers.clear() + self._by_sender.clear() + self._by_receiver.clear() + + +class NamedSignal(Signal): + """A named generic notification emitter. The name is not used by the signal + itself, but matches the key in the :class:`Namespace` that it belongs to. + + :param name: The name of the signal within the namespace. + :param doc: The docstring for the signal. + """ + + def __init__(self, name: str, doc: str | None = None) -> None: + super().__init__(doc) + + #: The name of this signal. + self.name: str = name + + def __repr__(self) -> str: + base = super().__repr__() + return f"{base[:-1]}; {self.name!r}>" # noqa: E702 + + +class Namespace(dict[str, NamedSignal]): + """A dict mapping names to signals.""" + + def signal(self, name: str, doc: str | None = None) -> NamedSignal: + """Return the :class:`NamedSignal` for the given ``name``, creating it + if required. Repeated calls with the same name return the same signal. + + :param name: The name of the signal. + :param doc: The docstring of the signal. + """ + if name not in self: + self[name] = NamedSignal(name, doc) + + return self[name] + + +class _PNamespaceSignal(t.Protocol): + def __call__(self, name: str, doc: str | None = None) -> NamedSignal: ... + + +default_namespace: Namespace = Namespace() +"""A default :class:`Namespace` for creating named signals. :func:`signal` +creates a :class:`NamedSignal` in this namespace. +""" + +signal: _PNamespaceSignal = default_namespace.signal +"""Return a :class:`NamedSignal` in :data:`default_namespace` with the given +``name``, creating it if required. Repeated calls with the same name return the +same signal. +""" diff --git a/venv/lib/python3.11/site-packages/blinker/py.typed b/venv/lib/python3.11/site-packages/blinker/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.11/site-packages/bs4/__init__.py b/venv/lib/python3.11/site-packages/bs4/__init__.py new file mode 100644 index 00000000..74cab3f7 --- /dev/null +++ b/venv/lib/python3.11/site-packages/bs4/__init__.py @@ -0,0 +1,791 @@ +"""Beautiful Soup Elixir and Tonic - "The Screen-Scraper's Friend". + +http://www.crummy.com/software/BeautifulSoup/ + +Beautiful Soup uses a pluggable XML or HTML parser to parse a +(possibly invalid) document into a tree representation. Beautiful Soup +provides methods and Pythonic idioms that make it easy to navigate, +search, and modify the parse tree. + +Beautiful Soup works with Python 2.7 and up. It works better if lxml +and/or html5lib is installed. + +For more than you ever wanted to know about Beautiful Soup, see the +documentation: http://www.crummy.com/software/BeautifulSoup/bs4/doc/ +""" + +__author__ = "Leonard Richardson (leonardr@segfault.org)" +__version__ = "4.9.3" +__copyright__ = "Copyright (c) 2004-2020 Leonard Richardson" +# Use of this source code is governed by the MIT license. +__license__ = "MIT" + +__all__ = ['BeautifulSoup'] + +from collections import Counter +import os +import re +import sys +import traceback +import warnings + +from .builder import builder_registry, ParserRejectedMarkup +from .dammit import UnicodeDammit +from .element import ( + CData, + Comment, + DEFAULT_OUTPUT_ENCODING, + Declaration, + Doctype, + NavigableString, + PageElement, + ProcessingInstruction, + PYTHON_SPECIFIC_ENCODINGS, + ResultSet, + Script, + Stylesheet, + SoupStrainer, + Tag, + TemplateString, + ) + +# The very first thing we do is give a useful error if someone is +# running this code under Python 3 without converting it. +'You are trying to run the Python 2 version of Beautiful Soup under Python 3. This will not work.'!='You need to convert the code, either by installing it (`python setup.py install`) or by running 2to3 (`2to3 -w bs4`).' + +# Define some custom warnings. +class GuessedAtParserWarning(UserWarning): + """The warning issued when BeautifulSoup has to guess what parser to + use -- probably because no parser was specified in the constructor. + """ + +class MarkupResemblesLocatorWarning(UserWarning): + """The warning issued when BeautifulSoup is given 'markup' that + actually looks like a resource locator -- a URL or a path to a file + on disk. + """ + + +class BeautifulSoup(Tag): + """A data structure representing a parsed HTML or XML document. + + Most of the methods you'll call on a BeautifulSoup object are inherited from + PageElement or Tag. + + Internally, this class defines the basic interface called by the + tree builders when converting an HTML/XML document into a data + structure. The interface abstracts away the differences between + parsers. To write a new tree builder, you'll need to understand + these methods as a whole. + + These methods will be called by the BeautifulSoup constructor: + * reset() + * feed(markup) + + The tree builder may call these methods from its feed() implementation: + * handle_starttag(name, attrs) # See note about return value + * handle_endtag(name) + * handle_data(data) # Appends to the current data node + * endData(containerClass) # Ends the current data node + + No matter how complicated the underlying parser is, you should be + able to build a tree using 'start tag' events, 'end tag' events, + 'data' events, and "done with data" events. + + If you encounter an empty-element tag (aka a self-closing tag, + like HTML's
tag), call handle_starttag and then + handle_endtag. + """ + + # Since BeautifulSoup subclasses Tag, it's possible to treat it as + # a Tag with a .name. This name makes it clear the BeautifulSoup + # object isn't a real markup tag. + ROOT_TAG_NAME = '[document]' + + # If the end-user gives no indication which tree builder they + # want, look for one with these features. + DEFAULT_BUILDER_FEATURES = ['html', 'fast'] + + # A string containing all ASCII whitespace characters, used in + # endData() to detect data chunks that seem 'empty'. + ASCII_SPACES = '\x20\x0a\x09\x0c\x0d' + + NO_PARSER_SPECIFIED_WARNING = "No parser was explicitly specified, so I'm using the best available %(markup_type)s parser for this system (\"%(parser)s\"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.\n\nThe code that caused this warning is on line %(line_number)s of the file %(filename)s. To get rid of this warning, pass the additional argument 'features=\"%(parser)s\"' to the BeautifulSoup constructor.\n" + + def __init__(self, markup="", features=None, builder=None, + parse_only=None, from_encoding=None, exclude_encodings=None, + element_classes=None, **kwargs): + """Constructor. + + :param markup: A string or a file-like object representing + markup to be parsed. + + :param features: Desirable features of the parser to be + used. This may be the name of a specific parser ("lxml", + "lxml-xml", "html.parser", or "html5lib") or it may be the + type of markup to be used ("html", "html5", "xml"). It's + recommended that you name a specific parser, so that + Beautiful Soup gives you the same results across platforms + and virtual environments. + + :param builder: A TreeBuilder subclass to instantiate (or + instance to use) instead of looking one up based on + `features`. You only need to use this if you've implemented a + custom TreeBuilder. + + :param parse_only: A SoupStrainer. Only parts of the document + matching the SoupStrainer will be considered. This is useful + when parsing part of a document that would otherwise be too + large to fit into memory. + + :param from_encoding: A string indicating the encoding of the + document to be parsed. Pass this in if Beautiful Soup is + guessing wrongly about the document's encoding. + + :param exclude_encodings: A list of strings indicating + encodings known to be wrong. Pass this in if you don't know + the document's encoding but you know Beautiful Soup's guess is + wrong. + + :param element_classes: A dictionary mapping BeautifulSoup + classes like Tag and NavigableString, to other classes you'd + like to be instantiated instead as the parse tree is + built. This is useful for subclassing Tag or NavigableString + to modify default behavior. + + :param kwargs: For backwards compatibility purposes, the + constructor accepts certain keyword arguments used in + Beautiful Soup 3. None of these arguments do anything in + Beautiful Soup 4; they will result in a warning and then be + ignored. + + Apart from this, any keyword arguments passed into the + BeautifulSoup constructor are propagated to the TreeBuilder + constructor. This makes it possible to configure a + TreeBuilder by passing in arguments, not just by saying which + one to use. + """ + if 'convertEntities' in kwargs: + del kwargs['convertEntities'] + warnings.warn( + "BS4 does not respect the convertEntities argument to the " + "BeautifulSoup constructor. Entities are always converted " + "to Unicode characters.") + + if 'markupMassage' in kwargs: + del kwargs['markupMassage'] + warnings.warn( + "BS4 does not respect the markupMassage argument to the " + "BeautifulSoup constructor. The tree builder is responsible " + "for any necessary markup massage.") + + if 'smartQuotesTo' in kwargs: + del kwargs['smartQuotesTo'] + warnings.warn( + "BS4 does not respect the smartQuotesTo argument to the " + "BeautifulSoup constructor. Smart quotes are always converted " + "to Unicode characters.") + + if 'selfClosingTags' in kwargs: + del kwargs['selfClosingTags'] + warnings.warn( + "BS4 does not respect the selfClosingTags argument to the " + "BeautifulSoup constructor. The tree builder is responsible " + "for understanding self-closing tags.") + + if 'isHTML' in kwargs: + del kwargs['isHTML'] + warnings.warn( + "BS4 does not respect the isHTML argument to the " + "BeautifulSoup constructor. Suggest you use " + "features='lxml' for HTML and features='lxml-xml' for " + "XML.") + + def deprecated_argument(old_name, new_name): + if old_name in kwargs: + warnings.warn( + 'The "%s" argument to the BeautifulSoup constructor ' + 'has been renamed to "%s."' % (old_name, new_name)) + value = kwargs[old_name] + del kwargs[old_name] + return value + return None + + parse_only = parse_only or deprecated_argument( + "parseOnlyThese", "parse_only") + + from_encoding = from_encoding or deprecated_argument( + "fromEncoding", "from_encoding") + + if from_encoding and isinstance(markup, str): + warnings.warn("You provided Unicode markup but also provided a value for from_encoding. Your from_encoding will be ignored.") + from_encoding = None + + self.element_classes = element_classes or dict() + + # We need this information to track whether or not the builder + # was specified well enough that we can omit the 'you need to + # specify a parser' warning. + original_builder = builder + original_features = features + + if isinstance(builder, type): + # A builder class was passed in; it needs to be instantiated. + builder_class = builder + builder = None + elif builder is None: + if isinstance(features, str): + features = [features] + if features is None or len(features) == 0: + features = self.DEFAULT_BUILDER_FEATURES + builder_class = builder_registry.lookup(*features) + if builder_class is None: + raise FeatureNotFound( + "Couldn't find a tree builder with the features you " + "requested: %s. Do you need to install a parser library?" + % ",".join(features)) + + # At this point either we have a TreeBuilder instance in + # builder, or we have a builder_class that we can instantiate + # with the remaining **kwargs. + if builder is None: + builder = builder_class(**kwargs) + if not original_builder and not ( + original_features == builder.NAME or + original_features in builder.ALTERNATE_NAMES + ) and markup: + # The user did not tell us which TreeBuilder to use, + # and we had to guess. Issue a warning. + if builder.is_xml: + markup_type = "XML" + else: + markup_type = "HTML" + + # This code adapted from warnings.py so that we get the same line + # of code as our warnings.warn() call gets, even if the answer is wrong + # (as it may be in a multithreading situation). + caller = None + try: + caller = sys._getframe(1) + except ValueError: + pass + if caller: + globals = caller.f_globals + line_number = caller.f_lineno + else: + globals = sys.__dict__ + line_number= 1 + filename = globals.get('__file__') + if filename: + fnl = filename.lower() + if fnl.endswith((".pyc", ".pyo")): + filename = filename[:-1] + if filename: + # If there is no filename at all, the user is most likely in a REPL, + # and the warning is not necessary. + values = dict( + filename=filename, + line_number=line_number, + parser=builder.NAME, + markup_type=markup_type + ) + warnings.warn( + self.NO_PARSER_SPECIFIED_WARNING % values, + GuessedAtParserWarning, stacklevel=2 + ) + else: + if kwargs: + warnings.warn("Keyword arguments to the BeautifulSoup constructor will be ignored. These would normally be passed into the TreeBuilder constructor, but a TreeBuilder instance was passed in as `builder`.") + + self.builder = builder + self.is_xml = builder.is_xml + self.known_xml = self.is_xml + self._namespaces = dict() + self.parse_only = parse_only + + self.builder.initialize_soup(self) + + if hasattr(markup, 'read'): # It's a file-type object. + markup = markup.read() + elif len(markup) <= 256 and ( + (isinstance(markup, bytes) and not b'<' in markup) + or (isinstance(markup, str) and not '<' in markup) + ): + # Print out warnings for a couple beginner problems + # involving passing non-markup to Beautiful Soup. + # Beautiful Soup will still parse the input as markup, + # just in case that's what the user really wants. + if (isinstance(markup, str) + and not os.path.supports_unicode_filenames): + possible_filename = markup.encode("utf8") + else: + possible_filename = markup + is_file = False + try: + is_file = os.path.exists(possible_filename) + except Exception as e: + # This is almost certainly a problem involving + # characters not valid in filenames on this + # system. Just let it go. + pass + if is_file: + warnings.warn( + '"%s" looks like a filename, not markup. You should' + ' probably open this file and pass the filehandle into' + ' Beautiful Soup.' % self._decode_markup(markup), + MarkupResemblesLocatorWarning + ) + self._check_markup_is_url(markup) + + rejections = [] + success = False + for (self.markup, self.original_encoding, self.declared_html_encoding, + self.contains_replacement_characters) in ( + self.builder.prepare_markup( + markup, from_encoding, exclude_encodings=exclude_encodings)): + self.reset() + try: + self._feed() + success = True + break + except ParserRejectedMarkup as e: + rejections.append(e) + pass + + if not success: + other_exceptions = [str(e) for e in rejections] + raise ParserRejectedMarkup( + "The markup you provided was rejected by the parser. Trying a different parser or a different encoding may help.\n\nOriginal exception(s) from parser:\n " + "\n ".join(other_exceptions) + ) + + # Clear out the markup and remove the builder's circular + # reference to this object. + self.markup = None + self.builder.soup = None + + def __copy__(self): + """Copy a BeautifulSoup object by converting the document to a string and parsing it again.""" + copy = type(self)( + self.encode('utf-8'), builder=self.builder, from_encoding='utf-8' + ) + + # Although we encoded the tree to UTF-8, that may not have + # been the encoding of the original markup. Set the copy's + # .original_encoding to reflect the original object's + # .original_encoding. + copy.original_encoding = self.original_encoding + return copy + + def __getstate__(self): + # Frequently a tree builder can't be pickled. + d = dict(self.__dict__) + if 'builder' in d and not self.builder.picklable: + d['builder'] = None + return d + + @classmethod + def _decode_markup(cls, markup): + """Ensure `markup` is bytes so it's safe to send into warnings.warn. + + TODO: warnings.warn had this problem back in 2010 but it might not + anymore. + """ + if isinstance(markup, bytes): + decoded = markup.decode('utf-8', 'replace') + else: + decoded = markup + return decoded + + @classmethod + def _check_markup_is_url(cls, markup): + """Error-handling method to raise a warning if incoming markup looks + like a URL. + + :param markup: A string. + """ + if isinstance(markup, bytes): + space = b' ' + cant_start_with = (b"http:", b"https:") + elif isinstance(markup, str): + space = ' ' + cant_start_with = ("http:", "https:") + else: + return + + if any(markup.startswith(prefix) for prefix in cant_start_with): + if not space in markup: + warnings.warn( + '"%s" looks like a URL. Beautiful Soup is not an' + ' HTTP client. You should probably use an HTTP client like' + ' requests to get the document behind the URL, and feed' + ' that document to Beautiful Soup.' % cls._decode_markup( + markup + ), + MarkupResemblesLocatorWarning + ) + + def _feed(self): + """Internal method that parses previously set markup, creating a large + number of Tag and NavigableString objects. + """ + # Convert the document to Unicode. + self.builder.reset() + + self.builder.feed(self.markup) + # Close out any unfinished strings and close all the open tags. + self.endData() + while self.currentTag.name != self.ROOT_TAG_NAME: + self.popTag() + + def reset(self): + """Reset this object to a state as though it had never parsed any + markup. + """ + Tag.__init__(self, self, self.builder, self.ROOT_TAG_NAME) + self.hidden = 1 + self.builder.reset() + self.current_data = [] + self.currentTag = None + self.tagStack = [] + self.open_tag_counter = Counter() + self.preserve_whitespace_tag_stack = [] + self.string_container_stack = [] + self.pushTag(self) + + def new_tag(self, name, namespace=None, nsprefix=None, attrs={}, + sourceline=None, sourcepos=None, **kwattrs): + """Create a new Tag associated with this BeautifulSoup object. + + :param name: The name of the new Tag. + :param namespace: The URI of the new Tag's XML namespace, if any. + :param prefix: The prefix for the new Tag's XML namespace, if any. + :param attrs: A dictionary of this Tag's attribute values; can + be used instead of `kwattrs` for attributes like 'class' + that are reserved words in Python. + :param sourceline: The line number where this tag was + (purportedly) found in its source document. + :param sourcepos: The character position within `sourceline` where this + tag was (purportedly) found. + :param kwattrs: Keyword arguments for the new Tag's attribute values. + + """ + kwattrs.update(attrs) + return self.element_classes.get(Tag, Tag)( + None, self.builder, name, namespace, nsprefix, kwattrs, + sourceline=sourceline, sourcepos=sourcepos + ) + + def string_container(self, base_class=None): + container = base_class or NavigableString + + # There may be a general override of NavigableString. + container = self.element_classes.get( + container, container + ) + + # On top of that, we may be inside a tag that needs a special + # container class. + if self.string_container_stack: + container = self.builder.string_containers.get( + self.string_container_stack[-1].name, container + ) + return container + + def new_string(self, s, subclass=None): + """Create a new NavigableString associated with this BeautifulSoup + object. + """ + container = self.string_container(subclass) + return container(s) + + def insert_before(self, *args): + """This method is part of the PageElement API, but `BeautifulSoup` doesn't implement + it because there is nothing before or after it in the parse tree. + """ + raise NotImplementedError("BeautifulSoup objects don't support insert_before().") + + def insert_after(self, *args): + """This method is part of the PageElement API, but `BeautifulSoup` doesn't implement + it because there is nothing before or after it in the parse tree. + """ + raise NotImplementedError("BeautifulSoup objects don't support insert_after().") + + def popTag(self): + """Internal method called by _popToTag when a tag is closed.""" + tag = self.tagStack.pop() + if tag.name in self.open_tag_counter: + self.open_tag_counter[tag.name] -= 1 + if self.preserve_whitespace_tag_stack and tag == self.preserve_whitespace_tag_stack[-1]: + self.preserve_whitespace_tag_stack.pop() + if self.string_container_stack and tag == self.string_container_stack[-1]: + self.string_container_stack.pop() + #print("Pop", tag.name) + if self.tagStack: + self.currentTag = self.tagStack[-1] + return self.currentTag + + def pushTag(self, tag): + """Internal method called by handle_starttag when a tag is opened.""" + #print("Push", tag.name) + if self.currentTag is not None: + self.currentTag.contents.append(tag) + self.tagStack.append(tag) + self.currentTag = self.tagStack[-1] + if tag.name != self.ROOT_TAG_NAME: + self.open_tag_counter[tag.name] += 1 + if tag.name in self.builder.preserve_whitespace_tags: + self.preserve_whitespace_tag_stack.append(tag) + if tag.name in self.builder.string_containers: + self.string_container_stack.append(tag) + + def endData(self, containerClass=None): + """Method called by the TreeBuilder when the end of a data segment + occurs. + """ + containerClass = self.string_container(containerClass) + + if self.current_data: + current_data = ''.join(self.current_data) + # If whitespace is not preserved, and this string contains + # nothing but ASCII spaces, replace it with a single space + # or newline. + if not self.preserve_whitespace_tag_stack: + strippable = True + for i in current_data: + if i not in self.ASCII_SPACES: + strippable = False + break + if strippable: + if '\n' in current_data: + current_data = '\n' + else: + current_data = ' ' + + # Reset the data collector. + self.current_data = [] + + # Should we add this string to the tree at all? + if self.parse_only and len(self.tagStack) <= 1 and \ + (not self.parse_only.text or \ + not self.parse_only.search(current_data)): + return + + o = containerClass(current_data) + self.object_was_parsed(o) + + def object_was_parsed(self, o, parent=None, most_recent_element=None): + """Method called by the TreeBuilder to integrate an object into the parse tree.""" + if parent is None: + parent = self.currentTag + if most_recent_element is not None: + previous_element = most_recent_element + else: + previous_element = self._most_recent_element + + next_element = previous_sibling = next_sibling = None + if isinstance(o, Tag): + next_element = o.next_element + next_sibling = o.next_sibling + previous_sibling = o.previous_sibling + if previous_element is None: + previous_element = o.previous_element + + fix = parent.next_element is not None + + o.setup(parent, previous_element, next_element, previous_sibling, next_sibling) + + self._most_recent_element = o + parent.contents.append(o) + + # Check if we are inserting into an already parsed node. + if fix: + self._linkage_fixer(parent) + + def _linkage_fixer(self, el): + """Make sure linkage of this fragment is sound.""" + + first = el.contents[0] + child = el.contents[-1] + descendant = child + + if child is first and el.parent is not None: + # Parent should be linked to first child + el.next_element = child + # We are no longer linked to whatever this element is + prev_el = child.previous_element + if prev_el is not None and prev_el is not el: + prev_el.next_element = None + # First child should be linked to the parent, and no previous siblings. + child.previous_element = el + child.previous_sibling = None + + # We have no sibling as we've been appended as the last. + child.next_sibling = None + + # This index is a tag, dig deeper for a "last descendant" + if isinstance(child, Tag) and child.contents: + descendant = child._last_descendant(False) + + # As the final step, link last descendant. It should be linked + # to the parent's next sibling (if found), else walk up the chain + # and find a parent with a sibling. It should have no next sibling. + descendant.next_element = None + descendant.next_sibling = None + target = el + while True: + if target is None: + break + elif target.next_sibling is not None: + descendant.next_element = target.next_sibling + target.next_sibling.previous_element = child + break + target = target.parent + + def _popToTag(self, name, nsprefix=None, inclusivePop=True): + """Pops the tag stack up to and including the most recent + instance of the given tag. + + If there are no open tags with the given name, nothing will be + popped. + + :param name: Pop up to the most recent tag with this name. + :param nsprefix: The namespace prefix that goes with `name`. + :param inclusivePop: It this is false, pops the tag stack up + to but *not* including the most recent instqance of the + given tag. + + """ + #print("Popping to %s" % name) + if name == self.ROOT_TAG_NAME: + # The BeautifulSoup object itself can never be popped. + return + + most_recently_popped = None + + stack_size = len(self.tagStack) + for i in range(stack_size - 1, 0, -1): + if not self.open_tag_counter.get(name): + break + t = self.tagStack[i] + if (name == t.name and nsprefix == t.prefix): + if inclusivePop: + most_recently_popped = self.popTag() + break + most_recently_popped = self.popTag() + + return most_recently_popped + + def handle_starttag(self, name, namespace, nsprefix, attrs, sourceline=None, + sourcepos=None): + """Called by the tree builder when a new tag is encountered. + + :param name: Name of the tag. + :param nsprefix: Namespace prefix for the tag. + :param attrs: A dictionary of attribute values. + :param sourceline: The line number where this tag was found in its + source document. + :param sourcepos: The character position within `sourceline` where this + tag was found. + + If this method returns None, the tag was rejected by an active + SoupStrainer. You should proceed as if the tag had not occurred + in the document. For instance, if this was a self-closing tag, + don't call handle_endtag. + """ + # print("Start tag %s: %s" % (name, attrs)) + self.endData() + + if (self.parse_only and len(self.tagStack) <= 1 + and (self.parse_only.text + or not self.parse_only.search_tag(name, attrs))): + return None + + tag = self.element_classes.get(Tag, Tag)( + self, self.builder, name, namespace, nsprefix, attrs, + self.currentTag, self._most_recent_element, + sourceline=sourceline, sourcepos=sourcepos + ) + if tag is None: + return tag + if self._most_recent_element is not None: + self._most_recent_element.next_element = tag + self._most_recent_element = tag + self.pushTag(tag) + return tag + + def handle_endtag(self, name, nsprefix=None): + """Called by the tree builder when an ending tag is encountered. + + :param name: Name of the tag. + :param nsprefix: Namespace prefix for the tag. + """ + #print("End tag: " + name) + self.endData() + self._popToTag(name, nsprefix) + + def handle_data(self, data): + """Called by the tree builder when a chunk of textual data is encountered.""" + self.current_data.append(data) + + def decode(self, pretty_print=False, + eventual_encoding=DEFAULT_OUTPUT_ENCODING, + formatter="minimal"): + """Returns a string or Unicode representation of the parse tree + as an HTML or XML document. + + :param pretty_print: If this is True, indentation will be used to + make the document more readable. + :param eventual_encoding: The encoding of the final document. + If this is None, the document will be a Unicode string. + """ + if self.is_xml: + # Print the XML declaration + encoding_part = '' + if eventual_encoding in PYTHON_SPECIFIC_ENCODINGS: + # This is a special Python encoding; it can't actually + # go into an XML document because it means nothing + # outside of Python. + eventual_encoding = None + if eventual_encoding != None: + encoding_part = ' encoding="%s"' % eventual_encoding + prefix = '\n' % encoding_part + else: + prefix = '' + if not pretty_print: + indent_level = None + else: + indent_level = 0 + return prefix + super(BeautifulSoup, self).decode( + indent_level, eventual_encoding, formatter) + +# Aliases to make it easier to get started quickly, e.g. 'from bs4 import _soup' +_s = BeautifulSoup +_soup = BeautifulSoup + +class BeautifulStoneSoup(BeautifulSoup): + """Deprecated interface to an XML parser.""" + + def __init__(self, *args, **kwargs): + kwargs['features'] = 'xml' + warnings.warn( + 'The BeautifulStoneSoup class is deprecated. Instead of using ' + 'it, pass features="xml" into the BeautifulSoup constructor.') + super(BeautifulStoneSoup, self).__init__(*args, **kwargs) + + +class StopParsing(Exception): + """Exception raised by a TreeBuilder if it's unable to continue parsing.""" + pass + +class FeatureNotFound(ValueError): + """Exception raised by the BeautifulSoup constructor if no parser with the + requested features is found. + """ + pass + + +#If this file is run as a script, act as an HTML pretty-printer. +if __name__ == '__main__': + import sys + soup = BeautifulSoup(sys.stdin) + print((soup.prettify())) diff --git a/venv/lib/python3.11/site-packages/bs4/builder/__init__.py b/venv/lib/python3.11/site-packages/bs4/builder/__init__.py new file mode 100644 index 00000000..03fbd6a8 --- /dev/null +++ b/venv/lib/python3.11/site-packages/bs4/builder/__init__.py @@ -0,0 +1,519 @@ +# Use of this source code is governed by the MIT license. +__license__ = "MIT" + +from collections import defaultdict +import itertools +import sys +from bs4.element import ( + CharsetMetaAttributeValue, + ContentMetaAttributeValue, + Stylesheet, + Script, + TemplateString, + nonwhitespace_re +) + +__all__ = [ + 'HTMLTreeBuilder', + 'SAXTreeBuilder', + 'TreeBuilder', + 'TreeBuilderRegistry', + ] + +# Some useful features for a TreeBuilder to have. +FAST = 'fast' +PERMISSIVE = 'permissive' +STRICT = 'strict' +XML = 'xml' +HTML = 'html' +HTML_5 = 'html5' + + +class TreeBuilderRegistry(object): + """A way of looking up TreeBuilder subclasses by their name or by desired + features. + """ + + def __init__(self): + self.builders_for_feature = defaultdict(list) + self.builders = [] + + def register(self, treebuilder_class): + """Register a treebuilder based on its advertised features. + + :param treebuilder_class: A subclass of Treebuilder. its .features + attribute should list its features. + """ + for feature in treebuilder_class.features: + self.builders_for_feature[feature].insert(0, treebuilder_class) + self.builders.insert(0, treebuilder_class) + + def lookup(self, *features): + """Look up a TreeBuilder subclass with the desired features. + + :param features: A list of features to look for. If none are + provided, the most recently registered TreeBuilder subclass + will be used. + :return: A TreeBuilder subclass, or None if there's no + registered subclass with all the requested features. + """ + if len(self.builders) == 0: + # There are no builders at all. + return None + + if len(features) == 0: + # They didn't ask for any features. Give them the most + # recently registered builder. + return self.builders[0] + + # Go down the list of features in order, and eliminate any builders + # that don't match every feature. + features = list(features) + features.reverse() + candidates = None + candidate_set = None + while len(features) > 0: + feature = features.pop() + we_have_the_feature = self.builders_for_feature.get(feature, []) + if len(we_have_the_feature) > 0: + if candidates is None: + candidates = we_have_the_feature + candidate_set = set(candidates) + else: + # Eliminate any candidates that don't have this feature. + candidate_set = candidate_set.intersection( + set(we_have_the_feature)) + + # The only valid candidates are the ones in candidate_set. + # Go through the original list of candidates and pick the first one + # that's in candidate_set. + if candidate_set is None: + return None + for candidate in candidates: + if candidate in candidate_set: + return candidate + return None + +# The BeautifulSoup class will take feature lists from developers and use them +# to look up builders in this registry. +builder_registry = TreeBuilderRegistry() + +class TreeBuilder(object): + """Turn a textual document into a Beautiful Soup object tree.""" + + NAME = "[Unknown tree builder]" + ALTERNATE_NAMES = [] + features = [] + + is_xml = False + picklable = False + empty_element_tags = None # A tag will be considered an empty-element + # tag when and only when it has no contents. + + # A value for these tag/attribute combinations is a space- or + # comma-separated list of CDATA, rather than a single CDATA. + DEFAULT_CDATA_LIST_ATTRIBUTES = {} + + # Whitespace should be preserved inside these tags. + DEFAULT_PRESERVE_WHITESPACE_TAGS = set() + + # The textual contents of tags with these names should be + # instantiated with some class other than NavigableString. + DEFAULT_STRING_CONTAINERS = {} + + USE_DEFAULT = object() + + # Most parsers don't keep track of line numbers. + TRACKS_LINE_NUMBERS = False + + def __init__(self, multi_valued_attributes=USE_DEFAULT, + preserve_whitespace_tags=USE_DEFAULT, + store_line_numbers=USE_DEFAULT, + string_containers=USE_DEFAULT, + ): + """Constructor. + + :param multi_valued_attributes: If this is set to None, the + TreeBuilder will not turn any values for attributes like + 'class' into lists. Setting this to a dictionary will + customize this behavior; look at DEFAULT_CDATA_LIST_ATTRIBUTES + for an example. + + Internally, these are called "CDATA list attributes", but that + probably doesn't make sense to an end-user, so the argument name + is `multi_valued_attributes`. + + :param preserve_whitespace_tags: A list of tags to treat + the way
 tags are treated in HTML. Tags in this list
+         are immune from pretty-printing; their contents will always be
+         output as-is.
+
+        :param string_containers: A dictionary mapping tag names to
+        the classes that should be instantiated to contain the textual
+        contents of those tags. The default is to use NavigableString
+        for every tag, no matter what the name. You can override the
+        default by changing DEFAULT_STRING_CONTAINERS.
+
+        :param store_line_numbers: If the parser keeps track of the
+         line numbers and positions of the original markup, that
+         information will, by default, be stored in each corresponding
+         `Tag` object. You can turn this off by passing
+         store_line_numbers=False. If the parser you're using doesn't 
+         keep track of this information, then setting store_line_numbers=True
+         will do nothing.
+        """
+        self.soup = None
+        if multi_valued_attributes is self.USE_DEFAULT:
+            multi_valued_attributes = self.DEFAULT_CDATA_LIST_ATTRIBUTES
+        self.cdata_list_attributes = multi_valued_attributes
+        if preserve_whitespace_tags is self.USE_DEFAULT:
+            preserve_whitespace_tags = self.DEFAULT_PRESERVE_WHITESPACE_TAGS
+        self.preserve_whitespace_tags = preserve_whitespace_tags
+        if store_line_numbers == self.USE_DEFAULT:
+            store_line_numbers = self.TRACKS_LINE_NUMBERS
+        self.store_line_numbers = store_line_numbers 
+        if string_containers == self.USE_DEFAULT:
+            string_containers = self.DEFAULT_STRING_CONTAINERS
+        self.string_containers = string_containers
+        
+    def initialize_soup(self, soup):
+        """The BeautifulSoup object has been initialized and is now
+        being associated with the TreeBuilder.
+
+        :param soup: A BeautifulSoup object.
+        """
+        self.soup = soup
+        
+    def reset(self):
+        """Do any work necessary to reset the underlying parser
+        for a new document.
+
+        By default, this does nothing.
+        """
+        pass
+
+    def can_be_empty_element(self, tag_name):
+        """Might a tag with this name be an empty-element tag?
+
+        The final markup may or may not actually present this tag as
+        self-closing.
+
+        For instance: an HTMLBuilder does not consider a 

tag to be + an empty-element tag (it's not in + HTMLBuilder.empty_element_tags). This means an empty

tag + will be presented as "

", not "

" or "

". + + The default implementation has no opinion about which tags are + empty-element tags, so a tag will be presented as an + empty-element tag if and only if it has no children. + "" will become "", and "bar" will + be left alone. + + :param tag_name: The name of a markup tag. + """ + if self.empty_element_tags is None: + return True + return tag_name in self.empty_element_tags + + def feed(self, markup): + """Run some incoming markup through some parsing process, + populating the `BeautifulSoup` object in self.soup. + + This method is not implemented in TreeBuilder; it must be + implemented in subclasses. + + :return: None. + """ + raise NotImplementedError() + + def prepare_markup(self, markup, user_specified_encoding=None, + document_declared_encoding=None, exclude_encodings=None): + """Run any preliminary steps necessary to make incoming markup + acceptable to the parser. + + :param markup: Some markup -- probably a bytestring. + :param user_specified_encoding: The user asked to try this encoding. + :param document_declared_encoding: The markup itself claims to be + in this encoding. + :param exclude_encodings: The user asked _not_ to try any of + these encodings. + + :yield: A series of 4-tuples: + (markup, encoding, declared encoding, + has undergone character replacement) + + Each 4-tuple represents a strategy for converting the + document to Unicode and parsing it. Each strategy will be tried + in turn. + + By default, the only strategy is to parse the markup + as-is. See `LXMLTreeBuilderForXML` and + `HTMLParserTreeBuilder` for implementations that take into + account the quirks of particular parsers. + """ + yield markup, None, None, False + + def test_fragment_to_document(self, fragment): + """Wrap an HTML fragment to make it look like a document. + + Different parsers do this differently. For instance, lxml + introduces an empty tag, and html5lib + doesn't. Abstracting this away lets us write simple tests + which run HTML fragments through the parser and compare the + results against other HTML fragments. + + This method should not be used outside of tests. + + :param fragment: A string -- fragment of HTML. + :return: A string -- a full HTML document. + """ + return fragment + + def set_up_substitutions(self, tag): + """Set up any substitutions that will need to be performed on + a `Tag` when it's output as a string. + + By default, this does nothing. See `HTMLTreeBuilder` for a + case where this is used. + + :param tag: A `Tag` + :return: Whether or not a substitution was performed. + """ + return False + + def _replace_cdata_list_attribute_values(self, tag_name, attrs): + """When an attribute value is associated with a tag that can + have multiple values for that attribute, convert the string + value to a list of strings. + + Basically, replaces class="foo bar" with class=["foo", "bar"] + + NOTE: This method modifies its input in place. + + :param tag_name: The name of a tag. + :param attrs: A dictionary containing the tag's attributes. + Any appropriate attribute values will be modified in place. + """ + if not attrs: + return attrs + if self.cdata_list_attributes: + universal = self.cdata_list_attributes.get('*', []) + tag_specific = self.cdata_list_attributes.get( + tag_name.lower(), None) + for attr in list(attrs.keys()): + if attr in universal or (tag_specific and attr in tag_specific): + # We have a "class"-type attribute whose string + # value is a whitespace-separated list of + # values. Split it into a list. + value = attrs[attr] + if isinstance(value, str): + values = nonwhitespace_re.findall(value) + else: + # html5lib sometimes calls setAttributes twice + # for the same tag when rearranging the parse + # tree. On the second call the attribute value + # here is already a list. If this happens, + # leave the value alone rather than trying to + # split it again. + values = value + attrs[attr] = values + return attrs + +class SAXTreeBuilder(TreeBuilder): + """A Beautiful Soup treebuilder that listens for SAX events. + + This is not currently used for anything, but it demonstrates + how a simple TreeBuilder would work. + """ + + def feed(self, markup): + raise NotImplementedError() + + def close(self): + pass + + def startElement(self, name, attrs): + attrs = dict((key[1], value) for key, value in list(attrs.items())) + #print("Start %s, %r" % (name, attrs)) + self.soup.handle_starttag(name, attrs) + + def endElement(self, name): + #print("End %s" % name) + self.soup.handle_endtag(name) + + def startElementNS(self, nsTuple, nodeName, attrs): + # Throw away (ns, nodeName) for now. + self.startElement(nodeName, attrs) + + def endElementNS(self, nsTuple, nodeName): + # Throw away (ns, nodeName) for now. + self.endElement(nodeName) + #handler.endElementNS((ns, node.nodeName), node.nodeName) + + def startPrefixMapping(self, prefix, nodeValue): + # Ignore the prefix for now. + pass + + def endPrefixMapping(self, prefix): + # Ignore the prefix for now. + # handler.endPrefixMapping(prefix) + pass + + def characters(self, content): + self.soup.handle_data(content) + + def startDocument(self): + pass + + def endDocument(self): + pass + + +class HTMLTreeBuilder(TreeBuilder): + """This TreeBuilder knows facts about HTML. + + Such as which tags are empty-element tags. + """ + + empty_element_tags = set([ + # These are from HTML5. + 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr', + + # These are from earlier versions of HTML and are removed in HTML5. + 'basefont', 'bgsound', 'command', 'frame', 'image', 'isindex', 'nextid', 'spacer' + ]) + + # The HTML standard defines these as block-level elements. Beautiful + # Soup does not treat these elements differently from other elements, + # but it may do so eventually, and this information is available if + # you need to use it. + block_elements = set(["address", "article", "aside", "blockquote", "canvas", "dd", "div", "dl", "dt", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hr", "li", "main", "nav", "noscript", "ol", "output", "p", "pre", "section", "table", "tfoot", "ul", "video"]) + + # The HTML standard defines an unusual content model for these tags. + # We represent this by using a string class other than NavigableString + # inside these tags. + # + # I made this list by going through the HTML spec + # (https://html.spec.whatwg.org/#metadata-content) and looking for + # "metadata content" elements that can contain strings. + # + # TODO: Arguably

as a +# string. +# +# XXX This code can be removed once most Python 3 users are on 3.2.3. +if major == 3 and minor == 2 and not CONSTRUCTOR_TAKES_STRICT: + import re + attrfind_tolerant = re.compile( + r'\s*((?<=[\'"\s])[^\s/>][^\s/=>]*)(\s*=+\s*' + r'(\'[^\']*\'|"[^"]*"|(?![\'"])[^>\s]*))?') + HTMLParserTreeBuilder.attrfind_tolerant = attrfind_tolerant + + locatestarttagend = re.compile(r""" + <[a-zA-Z][-.a-zA-Z0-9:_]* # tag name + (?:\s+ # whitespace before attribute name + (?:[a-zA-Z_][-.:a-zA-Z0-9_]* # attribute name + (?:\s*=\s* # value indicator + (?:'[^']*' # LITA-enclosed value + |\"[^\"]*\" # LIT-enclosed value + |[^'\">\s]+ # bare value + ) + )? + ) + )* + \s* # trailing whitespace +""", re.VERBOSE) + BeautifulSoupHTMLParser.locatestarttagend = locatestarttagend + + from html.parser import tagfind, attrfind + + def parse_starttag(self, i): + self.__starttag_text = None + endpos = self.check_for_whole_start_tag(i) + if endpos < 0: + return endpos + rawdata = self.rawdata + self.__starttag_text = rawdata[i:endpos] + + # Now parse the data between i+1 and j into a tag and attrs + attrs = [] + match = tagfind.match(rawdata, i+1) + assert match, 'unexpected call to parse_starttag()' + k = match.end() + self.lasttag = tag = rawdata[i+1:k].lower() + while k < endpos: + if self.strict: + m = attrfind.match(rawdata, k) + else: + m = attrfind_tolerant.match(rawdata, k) + if not m: + break + attrname, rest, attrvalue = m.group(1, 2, 3) + if not rest: + attrvalue = None + elif attrvalue[:1] == '\'' == attrvalue[-1:] or \ + attrvalue[:1] == '"' == attrvalue[-1:]: + attrvalue = attrvalue[1:-1] + if attrvalue: + attrvalue = self.unescape(attrvalue) + attrs.append((attrname.lower(), attrvalue)) + k = m.end() + + end = rawdata[k:endpos].strip() + if end not in (">", "/>"): + lineno, offset = self.getpos() + if "\n" in self.__starttag_text: + lineno = lineno + self.__starttag_text.count("\n") + offset = len(self.__starttag_text) \ + - self.__starttag_text.rfind("\n") + else: + offset = offset + len(self.__starttag_text) + if self.strict: + self.error("junk characters in start tag: %r" + % (rawdata[k:endpos][:20],)) + self.handle_data(rawdata[i:endpos]) + return endpos + if end.endswith('/>'): + # XHTML-style empty tag: + self.handle_startendtag(tag, attrs) + else: + self.handle_starttag(tag, attrs) + if tag in self.CDATA_CONTENT_ELEMENTS: + self.set_cdata_mode(tag) + return endpos + + def set_cdata_mode(self, elem): + self.cdata_elem = elem.lower() + self.interesting = re.compile(r'' % self.cdata_elem, re.I) + + BeautifulSoupHTMLParser.parse_starttag = parse_starttag + BeautifulSoupHTMLParser.set_cdata_mode = set_cdata_mode + + CONSTRUCTOR_TAKES_STRICT = True diff --git a/venv/lib/python3.11/site-packages/bs4/builder/_lxml.py b/venv/lib/python3.11/site-packages/bs4/builder/_lxml.py new file mode 100644 index 00000000..432a2c86 --- /dev/null +++ b/venv/lib/python3.11/site-packages/bs4/builder/_lxml.py @@ -0,0 +1,332 @@ +# Use of this source code is governed by the MIT license. +__license__ = "MIT" + +__all__ = [ + 'LXMLTreeBuilderForXML', + 'LXMLTreeBuilder', + ] + +try: + from collections.abc import Callable # Python 3.6 +except ImportError as e: + from collections import Callable + +from io import BytesIO +from io import StringIO +from lxml import etree +from bs4.element import ( + Comment, + Doctype, + NamespacedAttribute, + ProcessingInstruction, + XMLProcessingInstruction, +) +from bs4.builder import ( + FAST, + HTML, + HTMLTreeBuilder, + PERMISSIVE, + ParserRejectedMarkup, + TreeBuilder, + XML) +from bs4.dammit import EncodingDetector + +LXML = 'lxml' + +def _invert(d): + "Invert a dictionary." + return dict((v,k) for k, v in list(d.items())) + +class LXMLTreeBuilderForXML(TreeBuilder): + DEFAULT_PARSER_CLASS = etree.XMLParser + + is_xml = True + processing_instruction_class = XMLProcessingInstruction + + NAME = "lxml-xml" + ALTERNATE_NAMES = ["xml"] + + # Well, it's permissive by XML parser standards. + features = [NAME, LXML, XML, FAST, PERMISSIVE] + + CHUNK_SIZE = 512 + + # This namespace mapping is specified in the XML Namespace + # standard. + DEFAULT_NSMAPS = dict(xml='http://www.w3.org/XML/1998/namespace') + + DEFAULT_NSMAPS_INVERTED = _invert(DEFAULT_NSMAPS) + + # NOTE: If we parsed Element objects and looked at .sourceline, + # we'd be able to see the line numbers from the original document. + # But instead we build an XMLParser or HTMLParser object to serve + # as the target of parse messages, and those messages don't include + # line numbers. + # See: https://bugs.launchpad.net/lxml/+bug/1846906 + + def initialize_soup(self, soup): + """Let the BeautifulSoup object know about the standard namespace + mapping. + + :param soup: A `BeautifulSoup`. + """ + super(LXMLTreeBuilderForXML, self).initialize_soup(soup) + self._register_namespaces(self.DEFAULT_NSMAPS) + + def _register_namespaces(self, mapping): + """Let the BeautifulSoup object know about namespaces encountered + while parsing the document. + + This might be useful later on when creating CSS selectors. + + :param mapping: A dictionary mapping namespace prefixes to URIs. + """ + for key, value in list(mapping.items()): + if key and key not in self.soup._namespaces: + # Let the BeautifulSoup object know about a new namespace. + # If there are multiple namespaces defined with the same + # prefix, the first one in the document takes precedence. + self.soup._namespaces[key] = value + + def default_parser(self, encoding): + """Find the default parser for the given encoding. + + :param encoding: A string. + :return: Either a parser object or a class, which + will be instantiated with default arguments. + """ + if self._default_parser is not None: + return self._default_parser + return etree.XMLParser( + target=self, strip_cdata=False, recover=True, encoding=encoding) + + def parser_for(self, encoding): + """Instantiate an appropriate parser for the given encoding. + + :param encoding: A string. + :return: A parser object such as an `etree.XMLParser`. + """ + # Use the default parser. + parser = self.default_parser(encoding) + + if isinstance(parser, Callable): + # Instantiate the parser with default arguments + parser = parser( + target=self, strip_cdata=False, recover=True, encoding=encoding + ) + return parser + + def __init__(self, parser=None, empty_element_tags=None, **kwargs): + # TODO: Issue a warning if parser is present but not a + # callable, since that means there's no way to create new + # parsers for different encodings. + self._default_parser = parser + if empty_element_tags is not None: + self.empty_element_tags = set(empty_element_tags) + self.soup = None + self.nsmaps = [self.DEFAULT_NSMAPS_INVERTED] + super(LXMLTreeBuilderForXML, self).__init__(**kwargs) + + def _getNsTag(self, tag): + # Split the namespace URL out of a fully-qualified lxml tag + # name. Copied from lxml's src/lxml/sax.py. + if tag[0] == '{': + return tuple(tag[1:].split('}', 1)) + else: + return (None, tag) + + def prepare_markup(self, markup, user_specified_encoding=None, + exclude_encodings=None, + document_declared_encoding=None): + """Run any preliminary steps necessary to make incoming markup + acceptable to the parser. + + lxml really wants to get a bytestring and convert it to + Unicode itself. So instead of using UnicodeDammit to convert + the bytestring to Unicode using different encodings, this + implementation uses EncodingDetector to iterate over the + encodings, and tell lxml to try to parse the document as each + one in turn. + + :param markup: Some markup -- hopefully a bytestring. + :param user_specified_encoding: The user asked to try this encoding. + :param document_declared_encoding: The markup itself claims to be + in this encoding. + :param exclude_encodings: The user asked _not_ to try any of + these encodings. + + :yield: A series of 4-tuples: + (markup, encoding, declared encoding, + has undergone character replacement) + + Each 4-tuple represents a strategy for converting the + document to Unicode and parsing it. Each strategy will be tried + in turn. + """ + is_html = not self.is_xml + if is_html: + self.processing_instruction_class = ProcessingInstruction + else: + self.processing_instruction_class = XMLProcessingInstruction + + if isinstance(markup, str): + # We were given Unicode. Maybe lxml can parse Unicode on + # this system? + yield markup, None, document_declared_encoding, False + + if isinstance(markup, str): + # No, apparently not. Convert the Unicode to UTF-8 and + # tell lxml to parse it as UTF-8. + yield (markup.encode("utf8"), "utf8", + document_declared_encoding, False) + + try_encodings = [user_specified_encoding, document_declared_encoding] + detector = EncodingDetector( + markup, try_encodings, is_html, exclude_encodings) + for encoding in detector.encodings: + yield (detector.markup, encoding, document_declared_encoding, False) + + def feed(self, markup): + if isinstance(markup, bytes): + markup = BytesIO(markup) + elif isinstance(markup, str): + markup = StringIO(markup) + + # Call feed() at least once, even if the markup is empty, + # or the parser won't be initialized. + data = markup.read(self.CHUNK_SIZE) + try: + self.parser = self.parser_for(self.soup.original_encoding) + self.parser.feed(data) + while len(data) != 0: + # Now call feed() on the rest of the data, chunk by chunk. + data = markup.read(self.CHUNK_SIZE) + if len(data) != 0: + self.parser.feed(data) + self.parser.close() + except (UnicodeDecodeError, LookupError, etree.ParserError) as e: + raise ParserRejectedMarkup(e) + + def close(self): + self.nsmaps = [self.DEFAULT_NSMAPS_INVERTED] + + def start(self, name, attrs, nsmap={}): + # Make sure attrs is a mutable dict--lxml may send an immutable dictproxy. + attrs = dict(attrs) + nsprefix = None + # Invert each namespace map as it comes in. + if len(nsmap) == 0 and len(self.nsmaps) > 1: + # There are no new namespaces for this tag, but + # non-default namespaces are in play, so we need a + # separate tag stack to know when they end. + self.nsmaps.append(None) + elif len(nsmap) > 0: + # A new namespace mapping has come into play. + + # First, Let the BeautifulSoup object know about it. + self._register_namespaces(nsmap) + + # Then, add it to our running list of inverted namespace + # mappings. + self.nsmaps.append(_invert(nsmap)) + + # Also treat the namespace mapping as a set of attributes on the + # tag, so we can recreate it later. + attrs = attrs.copy() + for prefix, namespace in list(nsmap.items()): + attribute = NamespacedAttribute( + "xmlns", prefix, "http://www.w3.org/2000/xmlns/") + attrs[attribute] = namespace + + # Namespaces are in play. Find any attributes that came in + # from lxml with namespaces attached to their names, and + # turn then into NamespacedAttribute objects. + new_attrs = {} + for attr, value in list(attrs.items()): + namespace, attr = self._getNsTag(attr) + if namespace is None: + new_attrs[attr] = value + else: + nsprefix = self._prefix_for_namespace(namespace) + attr = NamespacedAttribute(nsprefix, attr, namespace) + new_attrs[attr] = value + attrs = new_attrs + + namespace, name = self._getNsTag(name) + nsprefix = self._prefix_for_namespace(namespace) + self.soup.handle_starttag(name, namespace, nsprefix, attrs) + + def _prefix_for_namespace(self, namespace): + """Find the currently active prefix for the given namespace.""" + if namespace is None: + return None + for inverted_nsmap in reversed(self.nsmaps): + if inverted_nsmap is not None and namespace in inverted_nsmap: + return inverted_nsmap[namespace] + return None + + def end(self, name): + self.soup.endData() + completed_tag = self.soup.tagStack[-1] + namespace, name = self._getNsTag(name) + nsprefix = None + if namespace is not None: + for inverted_nsmap in reversed(self.nsmaps): + if inverted_nsmap is not None and namespace in inverted_nsmap: + nsprefix = inverted_nsmap[namespace] + break + self.soup.handle_endtag(name, nsprefix) + if len(self.nsmaps) > 1: + # This tag, or one of its parents, introduced a namespace + # mapping, so pop it off the stack. + self.nsmaps.pop() + + def pi(self, target, data): + self.soup.endData() + self.soup.handle_data(target + ' ' + data) + self.soup.endData(self.processing_instruction_class) + + def data(self, content): + self.soup.handle_data(content) + + def doctype(self, name, pubid, system): + self.soup.endData() + doctype = Doctype.for_name_and_ids(name, pubid, system) + self.soup.object_was_parsed(doctype) + + def comment(self, content): + "Handle comments as Comment objects." + self.soup.endData() + self.soup.handle_data(content) + self.soup.endData(Comment) + + def test_fragment_to_document(self, fragment): + """See `TreeBuilder`.""" + return '\n%s' % fragment + + +class LXMLTreeBuilder(HTMLTreeBuilder, LXMLTreeBuilderForXML): + + NAME = LXML + ALTERNATE_NAMES = ["lxml-html"] + + features = ALTERNATE_NAMES + [NAME, HTML, FAST, PERMISSIVE] + is_xml = False + processing_instruction_class = ProcessingInstruction + + def default_parser(self, encoding): + return etree.HTMLParser + + def feed(self, markup): + encoding = self.soup.original_encoding + try: + self.parser = self.parser_for(encoding) + self.parser.feed(markup) + self.parser.close() + except (UnicodeDecodeError, LookupError, etree.ParserError) as e: + raise ParserRejectedMarkup(e) + + + def test_fragment_to_document(self, fragment): + """See `TreeBuilder`.""" + return '%s' % fragment diff --git a/venv/lib/python3.11/site-packages/bs4/dammit.py b/venv/lib/python3.11/site-packages/bs4/dammit.py new file mode 100644 index 00000000..ee3708f5 --- /dev/null +++ b/venv/lib/python3.11/site-packages/bs4/dammit.py @@ -0,0 +1,939 @@ +# -*- coding: utf-8 -*- +"""Beautiful Soup bonus library: Unicode, Dammit + +This library converts a bytestream to Unicode through any means +necessary. It is heavily based on code from Mark Pilgrim's Universal +Feed Parser. It works best on XML and HTML, but it does not rewrite the +XML or HTML to reflect a new encoding; that's the tree builder's job. +""" +# Use of this source code is governed by the MIT license. +__license__ = "MIT" + +import codecs +from html.entities import codepoint2name +import re +import logging +import string + +# Import a library to autodetect character encodings. +chardet_type = None +try: + # First try the fast C implementation. + # PyPI package: cchardet + import cchardet + def chardet_dammit(s): + if isinstance(s, str): + return None + return cchardet.detect(s)['encoding'] +except ImportError: + try: + # Fall back to the pure Python implementation + # Debian package: python-chardet + # PyPI package: chardet + import chardet + def chardet_dammit(s): + if isinstance(s, str): + return None + return chardet.detect(s)['encoding'] + #import chardet.constants + #chardet.constants._debug = 1 + except ImportError: + # No chardet available. + def chardet_dammit(s): + return None + +# Available from http://cjkpython.i18n.org/. +# +# TODO: This doesn't work anymore and the closest thing, iconv_codecs, +# is GPL-licensed. Check whether this is still necessary. +try: + import iconv_codec +except ImportError: + pass + +# Build bytestring and Unicode versions of regular expressions for finding +# a declared encoding inside an XML or HTML document. +xml_encoding = '^\\s*<\\?.*encoding=[\'"](.*?)[\'"].*\\?>' +html_meta = '<\\s*meta[^>]+charset\\s*=\\s*["\']?([^>]*?)[ /;\'">]' +encoding_res = dict() +encoding_res[bytes] = { + 'html' : re.compile(html_meta.encode("ascii"), re.I), + 'xml' : re.compile(xml_encoding.encode("ascii"), re.I), +} +encoding_res[str] = { + 'html' : re.compile(html_meta, re.I), + 'xml' : re.compile(xml_encoding, re.I) +} + +class EntitySubstitution(object): + """The ability to substitute XML or HTML entities for certain characters.""" + + def _populate_class_variables(): + lookup = {} + reverse_lookup = {} + characters_for_re = [] + + # &apos is an XHTML entity and an HTML 5, but not an HTML 4 + # entity. We don't want to use it, but we want to recognize it on the way in. + # + # TODO: Ideally we would be able to recognize all HTML 5 named + # entities, but that's a little tricky. + extra = [(39, 'apos')] + for codepoint, name in list(codepoint2name.items()) + extra: + character = chr(codepoint) + if codepoint not in (34, 39): + # There's no point in turning the quotation mark into + # " or the single quote into ', unless it + # happens within an attribute value, which is handled + # elsewhere. + characters_for_re.append(character) + lookup[character] = name + # But we do want to recognize those entities on the way in and + # convert them to Unicode characters. + reverse_lookup[name] = character + re_definition = "[%s]" % "".join(characters_for_re) + return lookup, reverse_lookup, re.compile(re_definition) + (CHARACTER_TO_HTML_ENTITY, HTML_ENTITY_TO_CHARACTER, + CHARACTER_TO_HTML_ENTITY_RE) = _populate_class_variables() + + CHARACTER_TO_XML_ENTITY = { + "'": "apos", + '"': "quot", + "&": "amp", + "<": "lt", + ">": "gt", + } + + BARE_AMPERSAND_OR_BRACKET = re.compile("([<>]|" + "&(?!#\\d+;|#x[0-9a-fA-F]+;|\\w+;)" + ")") + + AMPERSAND_OR_BRACKET = re.compile("([<>&])") + + @classmethod + def _substitute_html_entity(cls, matchobj): + """Used with a regular expression to substitute the + appropriate HTML entity for a special character.""" + entity = cls.CHARACTER_TO_HTML_ENTITY.get(matchobj.group(0)) + return "&%s;" % entity + + @classmethod + def _substitute_xml_entity(cls, matchobj): + """Used with a regular expression to substitute the + appropriate XML entity for a special character.""" + entity = cls.CHARACTER_TO_XML_ENTITY[matchobj.group(0)] + return "&%s;" % entity + + @classmethod + def quoted_attribute_value(self, value): + """Make a value into a quoted XML attribute, possibly escaping it. + + Most strings will be quoted using double quotes. + + Bob's Bar -> "Bob's Bar" + + If a string contains double quotes, it will be quoted using + single quotes. + + Welcome to "my bar" -> 'Welcome to "my bar"' + + If a string contains both single and double quotes, the + double quotes will be escaped, and the string will be quoted + using double quotes. + + Welcome to "Bob's Bar" -> "Welcome to "Bob's bar" + """ + quote_with = '"' + if '"' in value: + if "'" in value: + # The string contains both single and double + # quotes. Turn the double quotes into + # entities. We quote the double quotes rather than + # the single quotes because the entity name is + # """ whether this is HTML or XML. If we + # quoted the single quotes, we'd have to decide + # between ' and &squot;. + replace_with = """ + value = value.replace('"', replace_with) + else: + # There are double quotes but no single quotes. + # We can use single quotes to quote the attribute. + quote_with = "'" + return quote_with + value + quote_with + + @classmethod + def substitute_xml(cls, value, make_quoted_attribute=False): + """Substitute XML entities for special XML characters. + + :param value: A string to be substituted. The less-than sign + will become <, the greater-than sign will become >, + and any ampersands will become &. If you want ampersands + that appear to be part of an entity definition to be left + alone, use substitute_xml_containing_entities() instead. + + :param make_quoted_attribute: If True, then the string will be + quoted, as befits an attribute value. + """ + # Escape angle brackets and ampersands. + value = cls.AMPERSAND_OR_BRACKET.sub( + cls._substitute_xml_entity, value) + + if make_quoted_attribute: + value = cls.quoted_attribute_value(value) + return value + + @classmethod + def substitute_xml_containing_entities( + cls, value, make_quoted_attribute=False): + """Substitute XML entities for special XML characters. + + :param value: A string to be substituted. The less-than sign will + become <, the greater-than sign will become >, and any + ampersands that are not part of an entity defition will + become &. + + :param make_quoted_attribute: If True, then the string will be + quoted, as befits an attribute value. + """ + # Escape angle brackets, and ampersands that aren't part of + # entities. + value = cls.BARE_AMPERSAND_OR_BRACKET.sub( + cls._substitute_xml_entity, value) + + if make_quoted_attribute: + value = cls.quoted_attribute_value(value) + return value + + @classmethod + def substitute_html(cls, s): + """Replace certain Unicode characters with named HTML entities. + + This differs from data.encode(encoding, 'xmlcharrefreplace') + in that the goal is to make the result more readable (to those + with ASCII displays) rather than to recover from + errors. There's absolutely nothing wrong with a UTF-8 string + containg a LATIN SMALL LETTER E WITH ACUTE, but replacing that + character with "é" will make it more readable to some + people. + + :param s: A Unicode string. + """ + return cls.CHARACTER_TO_HTML_ENTITY_RE.sub( + cls._substitute_html_entity, s) + + +class EncodingDetector: + """Suggests a number of possible encodings for a bytestring. + + Order of precedence: + + 1. Encodings you specifically tell EncodingDetector to try first + (the override_encodings argument to the constructor). + + 2. An encoding declared within the bytestring itself, either in an + XML declaration (if the bytestring is to be interpreted as an XML + document), or in a tag (if the bytestring is to be + interpreted as an HTML document.) + + 3. An encoding detected through textual analysis by chardet, + cchardet, or a similar external library. + + 4. UTF-8. + + 5. Windows-1252. + """ + def __init__(self, markup, override_encodings=None, is_html=False, + exclude_encodings=None): + """Constructor. + + :param markup: Some markup in an unknown encoding. + :param override_encodings: These encodings will be tried first. + :param is_html: If True, this markup is considered to be HTML. Otherwise + it's assumed to be XML. + :param exclude_encodings: These encodings will not be tried, even + if they otherwise would be. + """ + self.override_encodings = override_encodings or [] + exclude_encodings = exclude_encodings or [] + self.exclude_encodings = set([x.lower() for x in exclude_encodings]) + self.chardet_encoding = None + self.is_html = is_html + self.declared_encoding = None + + # First order of business: strip a byte-order mark. + self.markup, self.sniffed_encoding = self.strip_byte_order_mark(markup) + + def _usable(self, encoding, tried): + """Should we even bother to try this encoding? + + :param encoding: Name of an encoding. + :param tried: Encodings that have already been tried. This will be modified + as a side effect. + """ + if encoding is not None: + encoding = encoding.lower() + if encoding in self.exclude_encodings: + return False + if encoding not in tried: + tried.add(encoding) + return True + return False + + @property + def encodings(self): + """Yield a number of encodings that might work for this markup. + + :yield: A sequence of strings. + """ + tried = set() + for e in self.override_encodings: + if self._usable(e, tried): + yield e + + # Did the document originally start with a byte-order mark + # that indicated its encoding? + if self._usable(self.sniffed_encoding, tried): + yield self.sniffed_encoding + + # Look within the document for an XML or HTML encoding + # declaration. + if self.declared_encoding is None: + self.declared_encoding = self.find_declared_encoding( + self.markup, self.is_html) + if self._usable(self.declared_encoding, tried): + yield self.declared_encoding + + # Use third-party character set detection to guess at the + # encoding. + if self.chardet_encoding is None: + self.chardet_encoding = chardet_dammit(self.markup) + if self._usable(self.chardet_encoding, tried): + yield self.chardet_encoding + + # As a last-ditch effort, try utf-8 and windows-1252. + for e in ('utf-8', 'windows-1252'): + if self._usable(e, tried): + yield e + + @classmethod + def strip_byte_order_mark(cls, data): + """If a byte-order mark is present, strip it and return the encoding it implies. + + :param data: Some markup. + :return: A 2-tuple (modified data, implied encoding) + """ + encoding = None + if isinstance(data, str): + # Unicode data cannot have a byte-order mark. + return data, encoding + if (len(data) >= 4) and (data[:2] == b'\xfe\xff') \ + and (data[2:4] != '\x00\x00'): + encoding = 'utf-16be' + data = data[2:] + elif (len(data) >= 4) and (data[:2] == b'\xff\xfe') \ + and (data[2:4] != '\x00\x00'): + encoding = 'utf-16le' + data = data[2:] + elif data[:3] == b'\xef\xbb\xbf': + encoding = 'utf-8' + data = data[3:] + elif data[:4] == b'\x00\x00\xfe\xff': + encoding = 'utf-32be' + data = data[4:] + elif data[:4] == b'\xff\xfe\x00\x00': + encoding = 'utf-32le' + data = data[4:] + return data, encoding + + @classmethod + def find_declared_encoding(cls, markup, is_html=False, search_entire_document=False): + """Given a document, tries to find its declared encoding. + + An XML encoding is declared at the beginning of the document. + + An HTML encoding is declared in a tag, hopefully near the + beginning of the document. + + :param markup: Some markup. + :param is_html: If True, this markup is considered to be HTML. Otherwise + it's assumed to be XML. + :param search_entire_document: Since an encoding is supposed to declared near the beginning + of the document, most of the time it's only necessary to search a few kilobytes of data. + Set this to True to force this method to search the entire document. + """ + if search_entire_document: + xml_endpos = html_endpos = len(markup) + else: + xml_endpos = 1024 + html_endpos = max(2048, int(len(markup) * 0.05)) + + if isinstance(markup, bytes): + res = encoding_res[bytes] + else: + res = encoding_res[str] + + xml_re = res['xml'] + html_re = res['html'] + declared_encoding = None + declared_encoding_match = xml_re.search(markup, endpos=xml_endpos) + if not declared_encoding_match and is_html: + declared_encoding_match = html_re.search(markup, endpos=html_endpos) + if declared_encoding_match is not None: + declared_encoding = declared_encoding_match.groups()[0] + if declared_encoding: + if isinstance(declared_encoding, bytes): + declared_encoding = declared_encoding.decode('ascii', 'replace') + return declared_encoding.lower() + return None + +class UnicodeDammit: + """A class for detecting the encoding of a *ML document and + converting it to a Unicode string. If the source encoding is + windows-1252, can replace MS smart quotes with their HTML or XML + equivalents.""" + + # This dictionary maps commonly seen values for "charset" in HTML + # meta tags to the corresponding Python codec names. It only covers + # values that aren't in Python's aliases and can't be determined + # by the heuristics in find_codec. + CHARSET_ALIASES = {"macintosh": "mac-roman", + "x-sjis": "shift-jis"} + + ENCODINGS_WITH_SMART_QUOTES = [ + "windows-1252", + "iso-8859-1", + "iso-8859-2", + ] + + def __init__(self, markup, override_encodings=[], + smart_quotes_to=None, is_html=False, exclude_encodings=[]): + """Constructor. + + :param markup: A bytestring representing markup in an unknown encoding. + :param override_encodings: These encodings will be tried first, + before any sniffing code is run. + + :param smart_quotes_to: By default, Microsoft smart quotes will, like all other characters, be converted + to Unicode characters. Setting this to 'ascii' will convert them to ASCII quotes instead. + Setting it to 'xml' will convert them to XML entity references, and setting it to 'html' + will convert them to HTML entity references. + :param is_html: If True, this markup is considered to be HTML. Otherwise + it's assumed to be XML. + :param exclude_encodings: These encodings will not be considered, even + if the sniffing code thinks they might make sense. + """ + self.smart_quotes_to = smart_quotes_to + self.tried_encodings = [] + self.contains_replacement_characters = False + self.is_html = is_html + self.log = logging.getLogger(__name__) + self.detector = EncodingDetector( + markup, override_encodings, is_html, exclude_encodings) + + # Short-circuit if the data is in Unicode to begin with. + if isinstance(markup, str) or markup == '': + self.markup = markup + self.unicode_markup = str(markup) + self.original_encoding = None + return + + # The encoding detector may have stripped a byte-order mark. + # Use the stripped markup from this point on. + self.markup = self.detector.markup + + u = None + for encoding in self.detector.encodings: + markup = self.detector.markup + u = self._convert_from(encoding) + if u is not None: + break + + if not u: + # None of the encodings worked. As an absolute last resort, + # try them again with character replacement. + + for encoding in self.detector.encodings: + if encoding != "ascii": + u = self._convert_from(encoding, "replace") + if u is not None: + self.log.warning( + "Some characters could not be decoded, and were " + "replaced with REPLACEMENT CHARACTER." + ) + self.contains_replacement_characters = True + break + + # If none of that worked, we could at this point force it to + # ASCII, but that would destroy so much data that I think + # giving up is better. + self.unicode_markup = u + if not u: + self.original_encoding = None + + def _sub_ms_char(self, match): + """Changes a MS smart quote character to an XML or HTML + entity, or an ASCII character.""" + orig = match.group(1) + if self.smart_quotes_to == 'ascii': + sub = self.MS_CHARS_TO_ASCII.get(orig).encode() + else: + sub = self.MS_CHARS.get(orig) + if type(sub) == tuple: + if self.smart_quotes_to == 'xml': + sub = '&#x'.encode() + sub[1].encode() + ';'.encode() + else: + sub = '&'.encode() + sub[0].encode() + ';'.encode() + else: + sub = sub.encode() + return sub + + def _convert_from(self, proposed, errors="strict"): + """Attempt to convert the markup to the proposed encoding. + + :param proposed: The name of a character encoding. + """ + proposed = self.find_codec(proposed) + if not proposed or (proposed, errors) in self.tried_encodings: + return None + self.tried_encodings.append((proposed, errors)) + markup = self.markup + # Convert smart quotes to HTML if coming from an encoding + # that might have them. + if (self.smart_quotes_to is not None + and proposed in self.ENCODINGS_WITH_SMART_QUOTES): + smart_quotes_re = b"([\x80-\x9f])" + smart_quotes_compiled = re.compile(smart_quotes_re) + markup = smart_quotes_compiled.sub(self._sub_ms_char, markup) + + try: + #print("Trying to convert document to %s (errors=%s)" % ( + # proposed, errors)) + u = self._to_unicode(markup, proposed, errors) + self.markup = u + self.original_encoding = proposed + except Exception as e: + #print("That didn't work!") + #print(e) + return None + #print("Correct encoding: %s" % proposed) + return self.markup + + def _to_unicode(self, data, encoding, errors="strict"): + """Given a string and its encoding, decodes the string into Unicode. + + :param encoding: The name of an encoding. + """ + return str(data, encoding, errors) + + @property + def declared_html_encoding(self): + """If the markup is an HTML document, returns the encoding declared _within_ + the document. + """ + if not self.is_html: + return None + return self.detector.declared_encoding + + def find_codec(self, charset): + """Convert the name of a character set to a codec name. + + :param charset: The name of a character set. + :return: The name of a codec. + """ + value = (self._codec(self.CHARSET_ALIASES.get(charset, charset)) + or (charset and self._codec(charset.replace("-", ""))) + or (charset and self._codec(charset.replace("-", "_"))) + or (charset and charset.lower()) + or charset + ) + if value: + return value.lower() + return None + + def _codec(self, charset): + if not charset: + return charset + codec = None + try: + codecs.lookup(charset) + codec = charset + except (LookupError, ValueError): + pass + return codec + + + # A partial mapping of ISO-Latin-1 to HTML entities/XML numeric entities. + MS_CHARS = {b'\x80': ('euro', '20AC'), + b'\x81': ' ', + b'\x82': ('sbquo', '201A'), + b'\x83': ('fnof', '192'), + b'\x84': ('bdquo', '201E'), + b'\x85': ('hellip', '2026'), + b'\x86': ('dagger', '2020'), + b'\x87': ('Dagger', '2021'), + b'\x88': ('circ', '2C6'), + b'\x89': ('permil', '2030'), + b'\x8A': ('Scaron', '160'), + b'\x8B': ('lsaquo', '2039'), + b'\x8C': ('OElig', '152'), + b'\x8D': '?', + b'\x8E': ('#x17D', '17D'), + b'\x8F': '?', + b'\x90': '?', + b'\x91': ('lsquo', '2018'), + b'\x92': ('rsquo', '2019'), + b'\x93': ('ldquo', '201C'), + b'\x94': ('rdquo', '201D'), + b'\x95': ('bull', '2022'), + b'\x96': ('ndash', '2013'), + b'\x97': ('mdash', '2014'), + b'\x98': ('tilde', '2DC'), + b'\x99': ('trade', '2122'), + b'\x9a': ('scaron', '161'), + b'\x9b': ('rsaquo', '203A'), + b'\x9c': ('oelig', '153'), + b'\x9d': '?', + b'\x9e': ('#x17E', '17E'), + b'\x9f': ('Yuml', ''),} + + # A parochial partial mapping of ISO-Latin-1 to ASCII. Contains + # horrors like stripping diacritical marks to turn á into a, but also + # contains non-horrors like turning “ into ". + MS_CHARS_TO_ASCII = { + b'\x80' : 'EUR', + b'\x81' : ' ', + b'\x82' : ',', + b'\x83' : 'f', + b'\x84' : ',,', + b'\x85' : '...', + b'\x86' : '+', + b'\x87' : '++', + b'\x88' : '^', + b'\x89' : '%', + b'\x8a' : 'S', + b'\x8b' : '<', + b'\x8c' : 'OE', + b'\x8d' : '?', + b'\x8e' : 'Z', + b'\x8f' : '?', + b'\x90' : '?', + b'\x91' : "'", + b'\x92' : "'", + b'\x93' : '"', + b'\x94' : '"', + b'\x95' : '*', + b'\x96' : '-', + b'\x97' : '--', + b'\x98' : '~', + b'\x99' : '(TM)', + b'\x9a' : 's', + b'\x9b' : '>', + b'\x9c' : 'oe', + b'\x9d' : '?', + b'\x9e' : 'z', + b'\x9f' : 'Y', + b'\xa0' : ' ', + b'\xa1' : '!', + b'\xa2' : 'c', + b'\xa3' : 'GBP', + b'\xa4' : '$', #This approximation is especially parochial--this is the + #generic currency symbol. + b'\xa5' : 'YEN', + b'\xa6' : '|', + b'\xa7' : 'S', + b'\xa8' : '..', + b'\xa9' : '', + b'\xaa' : '(th)', + b'\xab' : '<<', + b'\xac' : '!', + b'\xad' : ' ', + b'\xae' : '(R)', + b'\xaf' : '-', + b'\xb0' : 'o', + b'\xb1' : '+-', + b'\xb2' : '2', + b'\xb3' : '3', + b'\xb4' : ("'", 'acute'), + b'\xb5' : 'u', + b'\xb6' : 'P', + b'\xb7' : '*', + b'\xb8' : ',', + b'\xb9' : '1', + b'\xba' : '(th)', + b'\xbb' : '>>', + b'\xbc' : '1/4', + b'\xbd' : '1/2', + b'\xbe' : '3/4', + b'\xbf' : '?', + b'\xc0' : 'A', + b'\xc1' : 'A', + b'\xc2' : 'A', + b'\xc3' : 'A', + b'\xc4' : 'A', + b'\xc5' : 'A', + b'\xc6' : 'AE', + b'\xc7' : 'C', + b'\xc8' : 'E', + b'\xc9' : 'E', + b'\xca' : 'E', + b'\xcb' : 'E', + b'\xcc' : 'I', + b'\xcd' : 'I', + b'\xce' : 'I', + b'\xcf' : 'I', + b'\xd0' : 'D', + b'\xd1' : 'N', + b'\xd2' : 'O', + b'\xd3' : 'O', + b'\xd4' : 'O', + b'\xd5' : 'O', + b'\xd6' : 'O', + b'\xd7' : '*', + b'\xd8' : 'O', + b'\xd9' : 'U', + b'\xda' : 'U', + b'\xdb' : 'U', + b'\xdc' : 'U', + b'\xdd' : 'Y', + b'\xde' : 'b', + b'\xdf' : 'B', + b'\xe0' : 'a', + b'\xe1' : 'a', + b'\xe2' : 'a', + b'\xe3' : 'a', + b'\xe4' : 'a', + b'\xe5' : 'a', + b'\xe6' : 'ae', + b'\xe7' : 'c', + b'\xe8' : 'e', + b'\xe9' : 'e', + b'\xea' : 'e', + b'\xeb' : 'e', + b'\xec' : 'i', + b'\xed' : 'i', + b'\xee' : 'i', + b'\xef' : 'i', + b'\xf0' : 'o', + b'\xf1' : 'n', + b'\xf2' : 'o', + b'\xf3' : 'o', + b'\xf4' : 'o', + b'\xf5' : 'o', + b'\xf6' : 'o', + b'\xf7' : '/', + b'\xf8' : 'o', + b'\xf9' : 'u', + b'\xfa' : 'u', + b'\xfb' : 'u', + b'\xfc' : 'u', + b'\xfd' : 'y', + b'\xfe' : 'b', + b'\xff' : 'y', + } + + # A map used when removing rogue Windows-1252/ISO-8859-1 + # characters in otherwise UTF-8 documents. + # + # Note that \x81, \x8d, \x8f, \x90, and \x9d are undefined in + # Windows-1252. + WINDOWS_1252_TO_UTF8 = { + 0x80 : b'\xe2\x82\xac', # € + 0x82 : b'\xe2\x80\x9a', # ‚ + 0x83 : b'\xc6\x92', # ƒ + 0x84 : b'\xe2\x80\x9e', # „ + 0x85 : b'\xe2\x80\xa6', # … + 0x86 : b'\xe2\x80\xa0', # † + 0x87 : b'\xe2\x80\xa1', # ‡ + 0x88 : b'\xcb\x86', # ˆ + 0x89 : b'\xe2\x80\xb0', # ‰ + 0x8a : b'\xc5\xa0', # Š + 0x8b : b'\xe2\x80\xb9', # ‹ + 0x8c : b'\xc5\x92', # Œ + 0x8e : b'\xc5\xbd', # Ž + 0x91 : b'\xe2\x80\x98', # ‘ + 0x92 : b'\xe2\x80\x99', # ’ + 0x93 : b'\xe2\x80\x9c', # “ + 0x94 : b'\xe2\x80\x9d', # ” + 0x95 : b'\xe2\x80\xa2', # • + 0x96 : b'\xe2\x80\x93', # – + 0x97 : b'\xe2\x80\x94', # — + 0x98 : b'\xcb\x9c', # ˜ + 0x99 : b'\xe2\x84\xa2', # ™ + 0x9a : b'\xc5\xa1', # š + 0x9b : b'\xe2\x80\xba', # › + 0x9c : b'\xc5\x93', # œ + 0x9e : b'\xc5\xbe', # ž + 0x9f : b'\xc5\xb8', # Ÿ + 0xa0 : b'\xc2\xa0', #   + 0xa1 : b'\xc2\xa1', # ¡ + 0xa2 : b'\xc2\xa2', # ¢ + 0xa3 : b'\xc2\xa3', # £ + 0xa4 : b'\xc2\xa4', # ¤ + 0xa5 : b'\xc2\xa5', # ¥ + 0xa6 : b'\xc2\xa6', # ¦ + 0xa7 : b'\xc2\xa7', # § + 0xa8 : b'\xc2\xa8', # ¨ + 0xa9 : b'\xc2\xa9', # © + 0xaa : b'\xc2\xaa', # ª + 0xab : b'\xc2\xab', # « + 0xac : b'\xc2\xac', # ¬ + 0xad : b'\xc2\xad', # ­ + 0xae : b'\xc2\xae', # ® + 0xaf : b'\xc2\xaf', # ¯ + 0xb0 : b'\xc2\xb0', # ° + 0xb1 : b'\xc2\xb1', # ± + 0xb2 : b'\xc2\xb2', # ² + 0xb3 : b'\xc2\xb3', # ³ + 0xb4 : b'\xc2\xb4', # ´ + 0xb5 : b'\xc2\xb5', # µ + 0xb6 : b'\xc2\xb6', # ¶ + 0xb7 : b'\xc2\xb7', # · + 0xb8 : b'\xc2\xb8', # ¸ + 0xb9 : b'\xc2\xb9', # ¹ + 0xba : b'\xc2\xba', # º + 0xbb : b'\xc2\xbb', # » + 0xbc : b'\xc2\xbc', # ¼ + 0xbd : b'\xc2\xbd', # ½ + 0xbe : b'\xc2\xbe', # ¾ + 0xbf : b'\xc2\xbf', # ¿ + 0xc0 : b'\xc3\x80', # À + 0xc1 : b'\xc3\x81', # Á + 0xc2 : b'\xc3\x82', #  + 0xc3 : b'\xc3\x83', # à + 0xc4 : b'\xc3\x84', # Ä + 0xc5 : b'\xc3\x85', # Å + 0xc6 : b'\xc3\x86', # Æ + 0xc7 : b'\xc3\x87', # Ç + 0xc8 : b'\xc3\x88', # È + 0xc9 : b'\xc3\x89', # É + 0xca : b'\xc3\x8a', # Ê + 0xcb : b'\xc3\x8b', # Ë + 0xcc : b'\xc3\x8c', # Ì + 0xcd : b'\xc3\x8d', # Í + 0xce : b'\xc3\x8e', # Î + 0xcf : b'\xc3\x8f', # Ï + 0xd0 : b'\xc3\x90', # Ð + 0xd1 : b'\xc3\x91', # Ñ + 0xd2 : b'\xc3\x92', # Ò + 0xd3 : b'\xc3\x93', # Ó + 0xd4 : b'\xc3\x94', # Ô + 0xd5 : b'\xc3\x95', # Õ + 0xd6 : b'\xc3\x96', # Ö + 0xd7 : b'\xc3\x97', # × + 0xd8 : b'\xc3\x98', # Ø + 0xd9 : b'\xc3\x99', # Ù + 0xda : b'\xc3\x9a', # Ú + 0xdb : b'\xc3\x9b', # Û + 0xdc : b'\xc3\x9c', # Ü + 0xdd : b'\xc3\x9d', # Ý + 0xde : b'\xc3\x9e', # Þ + 0xdf : b'\xc3\x9f', # ß + 0xe0 : b'\xc3\xa0', # à + 0xe1 : b'\xa1', # á + 0xe2 : b'\xc3\xa2', # â + 0xe3 : b'\xc3\xa3', # ã + 0xe4 : b'\xc3\xa4', # ä + 0xe5 : b'\xc3\xa5', # å + 0xe6 : b'\xc3\xa6', # æ + 0xe7 : b'\xc3\xa7', # ç + 0xe8 : b'\xc3\xa8', # è + 0xe9 : b'\xc3\xa9', # é + 0xea : b'\xc3\xaa', # ê + 0xeb : b'\xc3\xab', # ë + 0xec : b'\xc3\xac', # ì + 0xed : b'\xc3\xad', # í + 0xee : b'\xc3\xae', # î + 0xef : b'\xc3\xaf', # ï + 0xf0 : b'\xc3\xb0', # ð + 0xf1 : b'\xc3\xb1', # ñ + 0xf2 : b'\xc3\xb2', # ò + 0xf3 : b'\xc3\xb3', # ó + 0xf4 : b'\xc3\xb4', # ô + 0xf5 : b'\xc3\xb5', # õ + 0xf6 : b'\xc3\xb6', # ö + 0xf7 : b'\xc3\xb7', # ÷ + 0xf8 : b'\xc3\xb8', # ø + 0xf9 : b'\xc3\xb9', # ù + 0xfa : b'\xc3\xba', # ú + 0xfb : b'\xc3\xbb', # û + 0xfc : b'\xc3\xbc', # ü + 0xfd : b'\xc3\xbd', # ý + 0xfe : b'\xc3\xbe', # þ + } + + MULTIBYTE_MARKERS_AND_SIZES = [ + (0xc2, 0xdf, 2), # 2-byte characters start with a byte C2-DF + (0xe0, 0xef, 3), # 3-byte characters start with E0-EF + (0xf0, 0xf4, 4), # 4-byte characters start with F0-F4 + ] + + FIRST_MULTIBYTE_MARKER = MULTIBYTE_MARKERS_AND_SIZES[0][0] + LAST_MULTIBYTE_MARKER = MULTIBYTE_MARKERS_AND_SIZES[-1][1] + + @classmethod + def detwingle(cls, in_bytes, main_encoding="utf8", + embedded_encoding="windows-1252"): + """Fix characters from one encoding embedded in some other encoding. + + Currently the only situation supported is Windows-1252 (or its + subset ISO-8859-1), embedded in UTF-8. + + :param in_bytes: A bytestring that you suspect contains + characters from multiple encodings. Note that this _must_ + be a bytestring. If you've already converted the document + to Unicode, you're too late. + :param main_encoding: The primary encoding of `in_bytes`. + :param embedded_encoding: The encoding that was used to embed characters + in the main document. + :return: A bytestring in which `embedded_encoding` + characters have been converted to their `main_encoding` + equivalents. + """ + if embedded_encoding.replace('_', '-').lower() not in ( + 'windows-1252', 'windows_1252'): + raise NotImplementedError( + "Windows-1252 and ISO-8859-1 are the only currently supported " + "embedded encodings.") + + if main_encoding.lower() not in ('utf8', 'utf-8'): + raise NotImplementedError( + "UTF-8 is the only currently supported main encoding.") + + byte_chunks = [] + + chunk_start = 0 + pos = 0 + while pos < len(in_bytes): + byte = in_bytes[pos] + if not isinstance(byte, int): + # Python 2.x + byte = ord(byte) + if (byte >= cls.FIRST_MULTIBYTE_MARKER + and byte <= cls.LAST_MULTIBYTE_MARKER): + # This is the start of a UTF-8 multibyte character. Skip + # to the end. + for start, end, size in cls.MULTIBYTE_MARKERS_AND_SIZES: + if byte >= start and byte <= end: + pos += size + break + elif byte >= 0x80 and byte in cls.WINDOWS_1252_TO_UTF8: + # We found a Windows-1252 character! + # Save the string up to this point as a chunk. + byte_chunks.append(in_bytes[chunk_start:pos]) + + # Now translate the Windows-1252 character into UTF-8 + # and add it as another, one-byte chunk. + byte_chunks.append(cls.WINDOWS_1252_TO_UTF8[byte]) + pos += 1 + chunk_start = pos + else: + # Go on to the next character. + pos += 1 + if chunk_start == 0: + # The string is unchanged. + return in_bytes + else: + # Store the final chunk. + byte_chunks.append(in_bytes[chunk_start:]) + return b''.join(byte_chunks) + diff --git a/venv/lib/python3.11/site-packages/bs4/diagnose.py b/venv/lib/python3.11/site-packages/bs4/diagnose.py new file mode 100644 index 00000000..500e92df --- /dev/null +++ b/venv/lib/python3.11/site-packages/bs4/diagnose.py @@ -0,0 +1,242 @@ +"""Diagnostic functions, mainly for use when doing tech support.""" + +# Use of this source code is governed by the MIT license. +__license__ = "MIT" + +import cProfile +from io import StringIO +from html.parser import HTMLParser +import bs4 +from bs4 import BeautifulSoup, __version__ +from bs4.builder import builder_registry + +import os +import pstats +import random +import tempfile +import time +import traceback +import sys +import cProfile + +def diagnose(data): + """Diagnostic suite for isolating common problems. + + :param data: A string containing markup that needs to be explained. + :return: None; diagnostics are printed to standard output. + """ + print(("Diagnostic running on Beautiful Soup %s" % __version__)) + print(("Python version %s" % sys.version)) + + basic_parsers = ["html.parser", "html5lib", "lxml"] + for name in basic_parsers: + for builder in builder_registry.builders: + if name in builder.features: + break + else: + basic_parsers.remove(name) + print(( + "I noticed that %s is not installed. Installing it may help." % + name)) + + if 'lxml' in basic_parsers: + basic_parsers.append("lxml-xml") + try: + from lxml import etree + print(("Found lxml version %s" % ".".join(map(str,etree.LXML_VERSION)))) + except ImportError as e: + print( + "lxml is not installed or couldn't be imported.") + + + if 'html5lib' in basic_parsers: + try: + import html5lib + print(("Found html5lib version %s" % html5lib.__version__)) + except ImportError as e: + print( + "html5lib is not installed or couldn't be imported.") + + if hasattr(data, 'read'): + data = data.read() + elif data.startswith("http:") or data.startswith("https:"): + print(('"%s" looks like a URL. Beautiful Soup is not an HTTP client.' % data)) + print("You need to use some other library to get the document behind the URL, and feed that document to Beautiful Soup.") + return + else: + try: + if os.path.exists(data): + print(('"%s" looks like a filename. Reading data from the file.' % data)) + with open(data) as fp: + data = fp.read() + except ValueError: + # This can happen on some platforms when the 'filename' is + # too long. Assume it's data and not a filename. + pass + print("") + + for parser in basic_parsers: + print(("Trying to parse your markup with %s" % parser)) + success = False + try: + soup = BeautifulSoup(data, features=parser) + success = True + except Exception as e: + print(("%s could not parse the markup." % parser)) + traceback.print_exc() + if success: + print(("Here's what %s did with the markup:" % parser)) + print((soup.prettify())) + + print(("-" * 80)) + +def lxml_trace(data, html=True, **kwargs): + """Print out the lxml events that occur during parsing. + + This lets you see how lxml parses a document when no Beautiful + Soup code is running. You can use this to determine whether + an lxml-specific problem is in Beautiful Soup's lxml tree builders + or in lxml itself. + + :param data: Some markup. + :param html: If True, markup will be parsed with lxml's HTML parser. + if False, lxml's XML parser will be used. + """ + from lxml import etree + for event, element in etree.iterparse(StringIO(data), html=html, **kwargs): + print(("%s, %4s, %s" % (event, element.tag, element.text))) + +class AnnouncingParser(HTMLParser): + """Subclass of HTMLParser that announces parse events, without doing + anything else. + + You can use this to get a picture of how html.parser sees a given + document. The easiest way to do this is to call `htmlparser_trace`. + """ + + def _p(self, s): + print(s) + + def handle_starttag(self, name, attrs): + self._p("%s START" % name) + + def handle_endtag(self, name): + self._p("%s END" % name) + + def handle_data(self, data): + self._p("%s DATA" % data) + + def handle_charref(self, name): + self._p("%s CHARREF" % name) + + def handle_entityref(self, name): + self._p("%s ENTITYREF" % name) + + def handle_comment(self, data): + self._p("%s COMMENT" % data) + + def handle_decl(self, data): + self._p("%s DECL" % data) + + def unknown_decl(self, data): + self._p("%s UNKNOWN-DECL" % data) + + def handle_pi(self, data): + self._p("%s PI" % data) + +def htmlparser_trace(data): + """Print out the HTMLParser events that occur during parsing. + + This lets you see how HTMLParser parses a document when no + Beautiful Soup code is running. + + :param data: Some markup. + """ + parser = AnnouncingParser() + parser.feed(data) + +_vowels = "aeiou" +_consonants = "bcdfghjklmnpqrstvwxyz" + +def rword(length=5): + "Generate a random word-like string." + s = '' + for i in range(length): + if i % 2 == 0: + t = _consonants + else: + t = _vowels + s += random.choice(t) + return s + +def rsentence(length=4): + "Generate a random sentence-like string." + return " ".join(rword(random.randint(4,9)) for i in range(length)) + +def rdoc(num_elements=1000): + """Randomly generate an invalid HTML document.""" + tag_names = ['p', 'div', 'span', 'i', 'b', 'script', 'table'] + elements = [] + for i in range(num_elements): + choice = random.randint(0,3) + if choice == 0: + # New tag. + tag_name = random.choice(tag_names) + elements.append("<%s>" % tag_name) + elif choice == 1: + elements.append(rsentence(random.randint(1,4))) + elif choice == 2: + # Close a tag. + tag_name = random.choice(tag_names) + elements.append("" % tag_name) + return "" + "\n".join(elements) + "" + +def benchmark_parsers(num_elements=100000): + """Very basic head-to-head performance benchmark.""" + print(("Comparative parser benchmark on Beautiful Soup %s" % __version__)) + data = rdoc(num_elements) + print(("Generated a large invalid HTML document (%d bytes)." % len(data))) + + for parser in ["lxml", ["lxml", "html"], "html5lib", "html.parser"]: + success = False + try: + a = time.time() + soup = BeautifulSoup(data, parser) + b = time.time() + success = True + except Exception as e: + print(("%s could not parse the markup." % parser)) + traceback.print_exc() + if success: + print(("BS4+%s parsed the markup in %.2fs." % (parser, b-a))) + + from lxml import etree + a = time.time() + etree.HTML(data) + b = time.time() + print(("Raw lxml parsed the markup in %.2fs." % (b-a))) + + import html5lib + parser = html5lib.HTMLParser() + a = time.time() + parser.parse(data) + b = time.time() + print(("Raw html5lib parsed the markup in %.2fs." % (b-a))) + +def profile(num_elements=100000, parser="lxml"): + """Use Python's profiler on a randomly generated document.""" + filehandle = tempfile.NamedTemporaryFile() + filename = filehandle.name + + data = rdoc(num_elements) + vars = dict(bs4=bs4, data=data, parser=parser) + cProfile.runctx('bs4.BeautifulSoup(data, parser)' , vars, vars, filename) + + stats = pstats.Stats(filename) + # stats.strip_dirs() + stats.sort_stats("cumulative") + stats.print_stats('_html5lib|bs4', 50) + +# If this file is run as a script, standard input is diagnosed. +if __name__ == '__main__': + diagnose(sys.stdin.read()) diff --git a/venv/lib/python3.11/site-packages/bs4/element.py b/venv/lib/python3.11/site-packages/bs4/element.py new file mode 100644 index 00000000..81d9db90 --- /dev/null +++ b/venv/lib/python3.11/site-packages/bs4/element.py @@ -0,0 +1,2175 @@ +# Use of this source code is governed by the MIT license. +__license__ = "MIT" + +try: + from collections.abc import Callable # Python 3.6 +except ImportError as e: + from collections import Callable +import re +import sys +import warnings +try: + import soupsieve +except ImportError as e: + soupsieve = None + warnings.warn( + 'The soupsieve package is not installed. CSS selectors cannot be used.' + ) + +from bs4.formatter import ( + Formatter, + HTMLFormatter, + XMLFormatter, +) + +DEFAULT_OUTPUT_ENCODING = "utf-8" +PY3K = (sys.version_info[0] > 2) + +nonwhitespace_re = re.compile(r"\S+") + +# NOTE: This isn't used as of 4.7.0. I'm leaving it for a little bit on +# the off chance someone imported it for their own use. +whitespace_re = re.compile(r"\s+") + +def _alias(attr): + """Alias one attribute name to another for backward compatibility""" + @property + def alias(self): + return getattr(self, attr) + + @alias.setter + def alias(self): + return setattr(self, attr) + return alias + + +# These encodings are recognized by Python (so PageElement.encode +# could theoretically support them) but XML and HTML don't recognize +# them (so they should not show up in an XML or HTML document as that +# document's encoding). +# +# If an XML document is encoded in one of these encodings, no encoding +# will be mentioned in the XML declaration. If an HTML document is +# encoded in one of these encodings, and the HTML document has a +# tag that mentions an encoding, the encoding will be given as +# the empty string. +# +# Source: +# https://docs.python.org/3/library/codecs.html#python-specific-encodings +PYTHON_SPECIFIC_ENCODINGS = set([ + "idna", + "mbcs", + "oem", + "palmos", + "punycode", + "raw_unicode_escape", + "undefined", + "unicode_escape", + "raw-unicode-escape", + "unicode-escape", + "string-escape", + "string_escape", +]) + + +class NamespacedAttribute(str): + """A namespaced string (e.g. 'xml:lang') that remembers the namespace + ('xml') and the name ('lang') that were used to create it. + """ + + def __new__(cls, prefix, name=None, namespace=None): + if not name: + # This is the default namespace. Its name "has no value" + # per https://www.w3.org/TR/xml-names/#defaulting + name = None + + if name is None: + obj = str.__new__(cls, prefix) + elif prefix is None: + # Not really namespaced. + obj = str.__new__(cls, name) + else: + obj = str.__new__(cls, prefix + ":" + name) + obj.prefix = prefix + obj.name = name + obj.namespace = namespace + return obj + +class AttributeValueWithCharsetSubstitution(str): + """A stand-in object for a character encoding specified in HTML.""" + +class CharsetMetaAttributeValue(AttributeValueWithCharsetSubstitution): + """A generic stand-in for the value of a meta tag's 'charset' attribute. + + When Beautiful Soup parses the markup '', the + value of the 'charset' attribute will be one of these objects. + """ + + def __new__(cls, original_value): + obj = str.__new__(cls, original_value) + obj.original_value = original_value + return obj + + def encode(self, encoding): + """When an HTML document is being encoded to a given encoding, the + value of a meta tag's 'charset' is the name of the encoding. + """ + if encoding in PYTHON_SPECIFIC_ENCODINGS: + return '' + return encoding + + +class ContentMetaAttributeValue(AttributeValueWithCharsetSubstitution): + """A generic stand-in for the value of a meta tag's 'content' attribute. + + When Beautiful Soup parses the markup: + + + The value of the 'content' attribute will be one of these objects. + """ + + CHARSET_RE = re.compile(r"((^|;)\s*charset=)([^;]*)", re.M) + + def __new__(cls, original_value): + match = cls.CHARSET_RE.search(original_value) + if match is None: + # No substitution necessary. + return str.__new__(str, original_value) + + obj = str.__new__(cls, original_value) + obj.original_value = original_value + return obj + + def encode(self, encoding): + if encoding in PYTHON_SPECIFIC_ENCODINGS: + return '' + def rewrite(match): + return match.group(1) + encoding + return self.CHARSET_RE.sub(rewrite, self.original_value) + + +class PageElement(object): + """Contains the navigational information for some part of the page: + that is, its current location in the parse tree. + + NavigableString, Tag, etc. are all subclasses of PageElement. + """ + + def setup(self, parent=None, previous_element=None, next_element=None, + previous_sibling=None, next_sibling=None): + """Sets up the initial relations between this element and + other elements. + + :param parent: The parent of this element. + + :param previous_element: The element parsed immediately before + this one. + + :param next_element: The element parsed immediately before + this one. + + :param previous_sibling: The most recently encountered element + on the same level of the parse tree as this one. + + :param previous_sibling: The next element to be encountered + on the same level of the parse tree as this one. + """ + self.parent = parent + + self.previous_element = previous_element + if previous_element is not None: + self.previous_element.next_element = self + + self.next_element = next_element + if self.next_element is not None: + self.next_element.previous_element = self + + self.next_sibling = next_sibling + if self.next_sibling is not None: + self.next_sibling.previous_sibling = self + + if (previous_sibling is None + and self.parent is not None and self.parent.contents): + previous_sibling = self.parent.contents[-1] + + self.previous_sibling = previous_sibling + if previous_sibling is not None: + self.previous_sibling.next_sibling = self + + def format_string(self, s, formatter): + """Format the given string using the given formatter. + + :param s: A string. + :param formatter: A Formatter object, or a string naming one of the standard formatters. + """ + if formatter is None: + return s + if not isinstance(formatter, Formatter): + formatter = self.formatter_for_name(formatter) + output = formatter.substitute(s) + return output + + def formatter_for_name(self, formatter): + """Look up or create a Formatter for the given identifier, + if necessary. + + :param formatter: Can be a Formatter object (used as-is), a + function (used as the entity substitution hook for an + XMLFormatter or HTMLFormatter), or a string (used to look + up an XMLFormatter or HTMLFormatter in the appropriate + registry. + """ + if isinstance(formatter, Formatter): + return formatter + if self._is_xml: + c = XMLFormatter + else: + c = HTMLFormatter + if isinstance(formatter, Callable): + return c(entity_substitution=formatter) + return c.REGISTRY[formatter] + + @property + def _is_xml(self): + """Is this element part of an XML tree or an HTML tree? + + This is used in formatter_for_name, when deciding whether an + XMLFormatter or HTMLFormatter is more appropriate. It can be + inefficient, but it should be called very rarely. + """ + if self.known_xml is not None: + # Most of the time we will have determined this when the + # document is parsed. + return self.known_xml + + # Otherwise, it's likely that this element was created by + # direct invocation of the constructor from within the user's + # Python code. + if self.parent is None: + # This is the top-level object. It should have .known_xml set + # from tree creation. If not, take a guess--BS is usually + # used on HTML markup. + return getattr(self, 'is_xml', False) + return self.parent._is_xml + + nextSibling = _alias("next_sibling") # BS3 + previousSibling = _alias("previous_sibling") # BS3 + + def replace_with(self, replace_with): + """Replace this PageElement with another one, keeping the rest of the + tree the same. + + :param replace_with: A PageElement. + :return: `self`, no longer part of the tree. + """ + if self.parent is None: + raise ValueError( + "Cannot replace one element with another when the " + "element to be replaced is not part of a tree.") + if replace_with is self: + return + if replace_with is self.parent: + raise ValueError("Cannot replace a Tag with its parent.") + old_parent = self.parent + my_index = self.parent.index(self) + self.extract(_self_index=my_index) + old_parent.insert(my_index, replace_with) + return self + replaceWith = replace_with # BS3 + + def unwrap(self): + """Replace this PageElement with its contents. + + :return: `self`, no longer part of the tree. + """ + my_parent = self.parent + if self.parent is None: + raise ValueError( + "Cannot replace an element with its contents when that" + "element is not part of a tree.") + my_index = self.parent.index(self) + self.extract(_self_index=my_index) + for child in reversed(self.contents[:]): + my_parent.insert(my_index, child) + return self + replace_with_children = unwrap + replaceWithChildren = unwrap # BS3 + + def wrap(self, wrap_inside): + """Wrap this PageElement inside another one. + + :param wrap_inside: A PageElement. + :return: `wrap_inside`, occupying the position in the tree that used + to be occupied by `self`, and with `self` inside it. + """ + me = self.replace_with(wrap_inside) + wrap_inside.append(me) + return wrap_inside + + def extract(self, _self_index=None): + """Destructively rips this element out of the tree. + + :param _self_index: The location of this element in its parent's + .contents, if known. Passing this in allows for a performance + optimization. + + :return: `self`, no longer part of the tree. + """ + if self.parent is not None: + if _self_index is None: + _self_index = self.parent.index(self) + del self.parent.contents[_self_index] + + #Find the two elements that would be next to each other if + #this element (and any children) hadn't been parsed. Connect + #the two. + last_child = self._last_descendant() + next_element = last_child.next_element + + if (self.previous_element is not None and + self.previous_element is not next_element): + self.previous_element.next_element = next_element + if next_element is not None and next_element is not self.previous_element: + next_element.previous_element = self.previous_element + self.previous_element = None + last_child.next_element = None + + self.parent = None + if (self.previous_sibling is not None + and self.previous_sibling is not self.next_sibling): + self.previous_sibling.next_sibling = self.next_sibling + if (self.next_sibling is not None + and self.next_sibling is not self.previous_sibling): + self.next_sibling.previous_sibling = self.previous_sibling + self.previous_sibling = self.next_sibling = None + return self + + def _last_descendant(self, is_initialized=True, accept_self=True): + """Finds the last element beneath this object to be parsed. + + :param is_initialized: Has `setup` been called on this PageElement + yet? + :param accept_self: Is `self` an acceptable answer to the question? + """ + if is_initialized and self.next_sibling is not None: + last_child = self.next_sibling.previous_element + else: + last_child = self + while isinstance(last_child, Tag) and last_child.contents: + last_child = last_child.contents[-1] + if not accept_self and last_child is self: + last_child = None + return last_child + # BS3: Not part of the API! + _lastRecursiveChild = _last_descendant + + def insert(self, position, new_child): + """Insert a new PageElement in the list of this PageElement's children. + + This works the same way as `list.insert`. + + :param position: The numeric position that should be occupied + in `self.children` by the new PageElement. + :param new_child: A PageElement. + """ + if new_child is None: + raise ValueError("Cannot insert None into a tag.") + if new_child is self: + raise ValueError("Cannot insert a tag into itself.") + if (isinstance(new_child, str) + and not isinstance(new_child, NavigableString)): + new_child = NavigableString(new_child) + + from bs4 import BeautifulSoup + if isinstance(new_child, BeautifulSoup): + # We don't want to end up with a situation where one BeautifulSoup + # object contains another. Insert the children one at a time. + for subchild in list(new_child.contents): + self.insert(position, subchild) + position += 1 + return + position = min(position, len(self.contents)) + if hasattr(new_child, 'parent') and new_child.parent is not None: + # We're 'inserting' an element that's already one + # of this object's children. + if new_child.parent is self: + current_index = self.index(new_child) + if current_index < position: + # We're moving this element further down the list + # of this object's children. That means that when + # we extract this element, our target index will + # jump down one. + position -= 1 + new_child.extract() + + new_child.parent = self + previous_child = None + if position == 0: + new_child.previous_sibling = None + new_child.previous_element = self + else: + previous_child = self.contents[position - 1] + new_child.previous_sibling = previous_child + new_child.previous_sibling.next_sibling = new_child + new_child.previous_element = previous_child._last_descendant(False) + if new_child.previous_element is not None: + new_child.previous_element.next_element = new_child + + new_childs_last_element = new_child._last_descendant(False) + + if position >= len(self.contents): + new_child.next_sibling = None + + parent = self + parents_next_sibling = None + while parents_next_sibling is None and parent is not None: + parents_next_sibling = parent.next_sibling + parent = parent.parent + if parents_next_sibling is not None: + # We found the element that comes next in the document. + break + if parents_next_sibling is not None: + new_childs_last_element.next_element = parents_next_sibling + else: + # The last element of this tag is the last element in + # the document. + new_childs_last_element.next_element = None + else: + next_child = self.contents[position] + new_child.next_sibling = next_child + if new_child.next_sibling is not None: + new_child.next_sibling.previous_sibling = new_child + new_childs_last_element.next_element = next_child + + if new_childs_last_element.next_element is not None: + new_childs_last_element.next_element.previous_element = new_childs_last_element + self.contents.insert(position, new_child) + + def append(self, tag): + """Appends the given PageElement to the contents of this one. + + :param tag: A PageElement. + """ + self.insert(len(self.contents), tag) + + def extend(self, tags): + """Appends the given PageElements to this one's contents. + + :param tags: A list of PageElements. + """ + if isinstance(tags, Tag): + # Calling self.append() on another tag's contents will change + # the list we're iterating over. Make a list that won't + # change. + tags = list(tags.contents) + for tag in tags: + self.append(tag) + + def insert_before(self, *args): + """Makes the given element(s) the immediate predecessor of this one. + + All the elements will have the same parent, and the given elements + will be immediately before this one. + + :param args: One or more PageElements. + """ + parent = self.parent + if parent is None: + raise ValueError( + "Element has no parent, so 'before' has no meaning.") + if any(x is self for x in args): + raise ValueError("Can't insert an element before itself.") + for predecessor in args: + # Extract first so that the index won't be screwed up if they + # are siblings. + if isinstance(predecessor, PageElement): + predecessor.extract() + index = parent.index(self) + parent.insert(index, predecessor) + + def insert_after(self, *args): + """Makes the given element(s) the immediate successor of this one. + + The elements will have the same parent, and the given elements + will be immediately after this one. + + :param args: One or more PageElements. + """ + # Do all error checking before modifying the tree. + parent = self.parent + if parent is None: + raise ValueError( + "Element has no parent, so 'after' has no meaning.") + if any(x is self for x in args): + raise ValueError("Can't insert an element after itself.") + + offset = 0 + for successor in args: + # Extract first so that the index won't be screwed up if they + # are siblings. + if isinstance(successor, PageElement): + successor.extract() + index = parent.index(self) + parent.insert(index+1+offset, successor) + offset += 1 + + def find_next(self, name=None, attrs={}, text=None, **kwargs): + """Find the first PageElement that matches the given criteria and + appears later in the document than this PageElement. + + All find_* methods take a common set of arguments. See the online + documentation for detailed explanations. + + :param name: A filter on tag name. + :param attrs: A dictionary of filters on attribute values. + :param text: A filter for a NavigableString with specific text. + :kwargs: A dictionary of filters on attribute values. + :return: A PageElement. + :rtype: bs4.element.Tag | bs4.element.NavigableString + """ + return self._find_one(self.find_all_next, name, attrs, text, **kwargs) + findNext = find_next # BS3 + + def find_all_next(self, name=None, attrs={}, text=None, limit=None, + **kwargs): + """Find all PageElements that match the given criteria and appear + later in the document than this PageElement. + + All find_* methods take a common set of arguments. See the online + documentation for detailed explanations. + + :param name: A filter on tag name. + :param attrs: A dictionary of filters on attribute values. + :param text: A filter for a NavigableString with specific text. + :param limit: Stop looking after finding this many results. + :kwargs: A dictionary of filters on attribute values. + :return: A ResultSet containing PageElements. + """ + return self._find_all(name, attrs, text, limit, self.next_elements, + **kwargs) + findAllNext = find_all_next # BS3 + + def find_next_sibling(self, name=None, attrs={}, text=None, **kwargs): + """Find the closest sibling to this PageElement that matches the + given criteria and appears later in the document. + + All find_* methods take a common set of arguments. See the + online documentation for detailed explanations. + + :param name: A filter on tag name. + :param attrs: A dictionary of filters on attribute values. + :param text: A filter for a NavigableString with specific text. + :kwargs: A dictionary of filters on attribute values. + :return: A PageElement. + :rtype: bs4.element.Tag | bs4.element.NavigableString + """ + return self._find_one(self.find_next_siblings, name, attrs, text, + **kwargs) + findNextSibling = find_next_sibling # BS3 + + def find_next_siblings(self, name=None, attrs={}, text=None, limit=None, + **kwargs): + """Find all siblings of this PageElement that match the given criteria + and appear later in the document. + + All find_* methods take a common set of arguments. See the online + documentation for detailed explanations. + + :param name: A filter on tag name. + :param attrs: A dictionary of filters on attribute values. + :param text: A filter for a NavigableString with specific text. + :param limit: Stop looking after finding this many results. + :kwargs: A dictionary of filters on attribute values. + :return: A ResultSet of PageElements. + :rtype: bs4.element.ResultSet + """ + return self._find_all(name, attrs, text, limit, + self.next_siblings, **kwargs) + findNextSiblings = find_next_siblings # BS3 + fetchNextSiblings = find_next_siblings # BS2 + + def find_previous(self, name=None, attrs={}, text=None, **kwargs): + """Look backwards in the document from this PageElement and find the + first PageElement that matches the given criteria. + + All find_* methods take a common set of arguments. See the online + documentation for detailed explanations. + + :param name: A filter on tag name. + :param attrs: A dictionary of filters on attribute values. + :param text: A filter for a NavigableString with specific text. + :kwargs: A dictionary of filters on attribute values. + :return: A PageElement. + :rtype: bs4.element.Tag | bs4.element.NavigableString + """ + return self._find_one( + self.find_all_previous, name, attrs, text, **kwargs) + findPrevious = find_previous # BS3 + + def find_all_previous(self, name=None, attrs={}, text=None, limit=None, + **kwargs): + """Look backwards in the document from this PageElement and find all + PageElements that match the given criteria. + + All find_* methods take a common set of arguments. See the online + documentation for detailed explanations. + + :param name: A filter on tag name. + :param attrs: A dictionary of filters on attribute values. + :param text: A filter for a NavigableString with specific text. + :param limit: Stop looking after finding this many results. + :kwargs: A dictionary of filters on attribute values. + :return: A ResultSet of PageElements. + :rtype: bs4.element.ResultSet + """ + return self._find_all(name, attrs, text, limit, self.previous_elements, + **kwargs) + findAllPrevious = find_all_previous # BS3 + fetchPrevious = find_all_previous # BS2 + + def find_previous_sibling(self, name=None, attrs={}, text=None, **kwargs): + """Returns the closest sibling to this PageElement that matches the + given criteria and appears earlier in the document. + + All find_* methods take a common set of arguments. See the online + documentation for detailed explanations. + + :param name: A filter on tag name. + :param attrs: A dictionary of filters on attribute values. + :param text: A filter for a NavigableString with specific text. + :kwargs: A dictionary of filters on attribute values. + :return: A PageElement. + :rtype: bs4.element.Tag | bs4.element.NavigableString + """ + return self._find_one(self.find_previous_siblings, name, attrs, text, + **kwargs) + findPreviousSibling = find_previous_sibling # BS3 + + def find_previous_siblings(self, name=None, attrs={}, text=None, + limit=None, **kwargs): + """Returns all siblings to this PageElement that match the + given criteria and appear earlier in the document. + + All find_* methods take a common set of arguments. See the online + documentation for detailed explanations. + + :param name: A filter on tag name. + :param attrs: A dictionary of filters on attribute values. + :param text: A filter for a NavigableString with specific text. + :param limit: Stop looking after finding this many results. + :kwargs: A dictionary of filters on attribute values. + :return: A ResultSet of PageElements. + :rtype: bs4.element.ResultSet + """ + return self._find_all(name, attrs, text, limit, + self.previous_siblings, **kwargs) + findPreviousSiblings = find_previous_siblings # BS3 + fetchPreviousSiblings = find_previous_siblings # BS2 + + def find_parent(self, name=None, attrs={}, **kwargs): + """Find the closest parent of this PageElement that matches the given + criteria. + + All find_* methods take a common set of arguments. See the online + documentation for detailed explanations. + + :param name: A filter on tag name. + :param attrs: A dictionary of filters on attribute values. + :kwargs: A dictionary of filters on attribute values. + + :return: A PageElement. + :rtype: bs4.element.Tag | bs4.element.NavigableString + """ + # NOTE: We can't use _find_one because findParents takes a different + # set of arguments. + r = None + l = self.find_parents(name, attrs, 1, **kwargs) + if l: + r = l[0] + return r + findParent = find_parent # BS3 + + def find_parents(self, name=None, attrs={}, limit=None, **kwargs): + """Find all parents of this PageElement that match the given criteria. + + All find_* methods take a common set of arguments. See the online + documentation for detailed explanations. + + :param name: A filter on tag name. + :param attrs: A dictionary of filters on attribute values. + :param limit: Stop looking after finding this many results. + :kwargs: A dictionary of filters on attribute values. + + :return: A PageElement. + :rtype: bs4.element.Tag | bs4.element.NavigableString + """ + return self._find_all(name, attrs, None, limit, self.parents, + **kwargs) + findParents = find_parents # BS3 + fetchParents = find_parents # BS2 + + @property + def next(self): + """The PageElement, if any, that was parsed just after this one. + + :return: A PageElement. + :rtype: bs4.element.Tag | bs4.element.NavigableString + """ + return self.next_element + + @property + def previous(self): + """The PageElement, if any, that was parsed just before this one. + + :return: A PageElement. + :rtype: bs4.element.Tag | bs4.element.NavigableString + """ + return self.previous_element + + #These methods do the real heavy lifting. + + def _find_one(self, method, name, attrs, text, **kwargs): + r = None + l = method(name, attrs, text, 1, **kwargs) + if l: + r = l[0] + return r + + def _find_all(self, name, attrs, text, limit, generator, **kwargs): + "Iterates over a generator looking for things that match." + + if text is None and 'string' in kwargs: + text = kwargs['string'] + del kwargs['string'] + + if isinstance(name, SoupStrainer): + strainer = name + else: + strainer = SoupStrainer(name, attrs, text, **kwargs) + + if text is None and not limit and not attrs and not kwargs: + if name is True or name is None: + # Optimization to find all tags. + result = (element for element in generator + if isinstance(element, Tag)) + return ResultSet(strainer, result) + elif isinstance(name, str): + # Optimization to find all tags with a given name. + if name.count(':') == 1: + # This is a name with a prefix. If this is a namespace-aware document, + # we need to match the local name against tag.name. If not, + # we need to match the fully-qualified name against tag.name. + prefix, local_name = name.split(':', 1) + else: + prefix = None + local_name = name + result = (element for element in generator + if isinstance(element, Tag) + and ( + element.name == name + ) or ( + element.name == local_name + and (prefix is None or element.prefix == prefix) + ) + ) + return ResultSet(strainer, result) + results = ResultSet(strainer) + while True: + try: + i = next(generator) + except StopIteration: + break + if i: + found = strainer.search(i) + if found: + results.append(found) + if limit and len(results) >= limit: + break + return results + + #These generators can be used to navigate starting from both + #NavigableStrings and Tags. + @property + def next_elements(self): + """All PageElements that were parsed after this one. + + :yield: A sequence of PageElements. + """ + i = self.next_element + while i is not None: + yield i + i = i.next_element + + @property + def next_siblings(self): + """All PageElements that are siblings of this one but were parsed + later. + + :yield: A sequence of PageElements. + """ + i = self.next_sibling + while i is not None: + yield i + i = i.next_sibling + + @property + def previous_elements(self): + """All PageElements that were parsed before this one. + + :yield: A sequence of PageElements. + """ + i = self.previous_element + while i is not None: + yield i + i = i.previous_element + + @property + def previous_siblings(self): + """All PageElements that are siblings of this one but were parsed + earlier. + + :yield: A sequence of PageElements. + """ + i = self.previous_sibling + while i is not None: + yield i + i = i.previous_sibling + + @property + def parents(self): + """All PageElements that are parents of this PageElement. + + :yield: A sequence of PageElements. + """ + i = self.parent + while i is not None: + yield i + i = i.parent + + @property + def decomposed(self): + """Check whether a PageElement has been decomposed. + + :rtype: bool + """ + return getattr(self, '_decomposed', False) or False + + # Old non-property versions of the generators, for backwards + # compatibility with BS3. + def nextGenerator(self): + return self.next_elements + + def nextSiblingGenerator(self): + return self.next_siblings + + def previousGenerator(self): + return self.previous_elements + + def previousSiblingGenerator(self): + return self.previous_siblings + + def parentGenerator(self): + return self.parents + + +class NavigableString(str, PageElement): + """A Python Unicode string that is part of a parse tree. + + When Beautiful Soup parses the markup penguin, it will + create a NavigableString for the string "penguin". + """ + + PREFIX = '' + SUFFIX = '' + + # We can't tell just by looking at a string whether it's contained + # in an XML document or an HTML document. + + known_xml = None + + def __new__(cls, value): + """Create a new NavigableString. + + When unpickling a NavigableString, this method is called with + the string in DEFAULT_OUTPUT_ENCODING. That encoding needs to be + passed in to the superclass's __new__ or the superclass won't know + how to handle non-ASCII characters. + """ + if isinstance(value, str): + u = str.__new__(cls, value) + else: + u = str.__new__(cls, value, DEFAULT_OUTPUT_ENCODING) + u.setup() + return u + + def __copy__(self): + """A copy of a NavigableString has the same contents and class + as the original, but it is not connected to the parse tree. + """ + return type(self)(self) + + def __getnewargs__(self): + return (str(self),) + + def __getattr__(self, attr): + """text.string gives you text. This is for backwards + compatibility for Navigable*String, but for CData* it lets you + get the string without the CData wrapper.""" + if attr == 'string': + return self + else: + raise AttributeError( + "'%s' object has no attribute '%s'" % ( + self.__class__.__name__, attr)) + + def output_ready(self, formatter="minimal"): + """Run the string through the provided formatter. + + :param formatter: A Formatter object, or a string naming one of the standard formatters. + """ + output = self.format_string(self, formatter) + return self.PREFIX + output + self.SUFFIX + + @property + def name(self): + """Since a NavigableString is not a Tag, it has no .name. + + This property is implemented so that code like this doesn't crash + when run on a mixture of Tag and NavigableString objects: + [x.name for x in tag.children] + """ + return None + + @name.setter + def name(self, name): + """Prevent NavigableString.name from ever being set.""" + raise AttributeError("A NavigableString cannot be given a name.") + + +class PreformattedString(NavigableString): + """A NavigableString not subject to the normal formatting rules. + + This is an abstract class used for special kinds of strings such + as comments (the Comment class) and CDATA blocks (the CData + class). + """ + + PREFIX = '' + SUFFIX = '' + + def output_ready(self, formatter=None): + """Make this string ready for output by adding any subclass-specific + prefix or suffix. + + :param formatter: A Formatter object, or a string naming one + of the standard formatters. The string will be passed into the + Formatter, but only to trigger any side effects: the return + value is ignored. + + :return: The string, with any subclass-specific prefix and + suffix added on. + """ + if formatter is not None: + ignore = self.format_string(self, formatter) + return self.PREFIX + self + self.SUFFIX + +class CData(PreformattedString): + """A CDATA block.""" + PREFIX = '' + +class ProcessingInstruction(PreformattedString): + """A SGML processing instruction.""" + + PREFIX = '' + +class XMLProcessingInstruction(ProcessingInstruction): + """An XML processing instruction.""" + PREFIX = '' + +class Comment(PreformattedString): + """An HTML or XML comment.""" + PREFIX = '' + + +class Declaration(PreformattedString): + """An XML declaration.""" + PREFIX = '' + + +class Doctype(PreformattedString): + """A document type declaration.""" + @classmethod + def for_name_and_ids(cls, name, pub_id, system_id): + """Generate an appropriate document type declaration for a given + public ID and system ID. + + :param name: The name of the document's root element, e.g. 'html'. + :param pub_id: The Formal Public Identifier for this document type, + e.g. '-//W3C//DTD XHTML 1.1//EN' + :param system_id: The system identifier for this document type, + e.g. 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd' + + :return: A Doctype. + """ + value = name or '' + if pub_id is not None: + value += ' PUBLIC "%s"' % pub_id + if system_id is not None: + value += ' "%s"' % system_id + elif system_id is not None: + value += ' SYSTEM "%s"' % system_id + + return Doctype(value) + + PREFIX = '\n' + + +class Stylesheet(NavigableString): + """A NavigableString representing an stylesheet (probably + CSS). + + Used to distinguish embedded stylesheets from textual content. + """ + pass + + +class Script(NavigableString): + """A NavigableString representing an executable script (probably + Javascript). + + Used to distinguish executable code from textual content. + """ + pass + + +class TemplateString(NavigableString): + """A NavigableString representing a string found inside an HTML + template embedded in a larger document. + + Used to distinguish such strings from the main body of the document. + """ + pass + + +class Tag(PageElement): + """Represents an HTML or XML tag that is part of a parse tree, along + with its attributes and contents. + + When Beautiful Soup parses the markup penguin, it will + create a Tag object representing the tag. + """ + + def __init__(self, parser=None, builder=None, name=None, namespace=None, + prefix=None, attrs=None, parent=None, previous=None, + is_xml=None, sourceline=None, sourcepos=None, + can_be_empty_element=None, cdata_list_attributes=None, + preserve_whitespace_tags=None + ): + """Basic constructor. + + :param parser: A BeautifulSoup object. + :param builder: A TreeBuilder. + :param name: The name of the tag. + :param namespace: The URI of this Tag's XML namespace, if any. + :param prefix: The prefix for this Tag's XML namespace, if any. + :param attrs: A dictionary of this Tag's attribute values. + :param parent: The PageElement to use as this Tag's parent. + :param previous: The PageElement that was parsed immediately before + this tag. + :param is_xml: If True, this is an XML tag. Otherwise, this is an + HTML tag. + :param sourceline: The line number where this tag was found in its + source document. + :param sourcepos: The character position within `sourceline` where this + tag was found. + :param can_be_empty_element: If True, this tag should be + represented as . If False, this tag should be represented + as . + :param cdata_list_attributes: A list of attributes whose values should + be treated as CDATA if they ever show up on this tag. + :param preserve_whitespace_tags: A list of tag names whose contents + should have their whitespace preserved. + """ + if parser is None: + self.parser_class = None + else: + # We don't actually store the parser object: that lets extracted + # chunks be garbage-collected. + self.parser_class = parser.__class__ + if name is None: + raise ValueError("No value provided for new tag's name.") + self.name = name + self.namespace = namespace + self.prefix = prefix + if ((not builder or builder.store_line_numbers) + and (sourceline is not None or sourcepos is not None)): + self.sourceline = sourceline + self.sourcepos = sourcepos + if attrs is None: + attrs = {} + elif attrs: + if builder is not None and builder.cdata_list_attributes: + attrs = builder._replace_cdata_list_attribute_values( + self.name, attrs) + else: + attrs = dict(attrs) + else: + attrs = dict(attrs) + + # If possible, determine ahead of time whether this tag is an + # XML tag. + if builder: + self.known_xml = builder.is_xml + else: + self.known_xml = is_xml + self.attrs = attrs + self.contents = [] + self.setup(parent, previous) + self.hidden = False + + if builder is None: + # In the absence of a TreeBuilder, use whatever values were + # passed in here. They're probably None, unless this is a copy of some + # other tag. + self.can_be_empty_element = can_be_empty_element + self.cdata_list_attributes = cdata_list_attributes + self.preserve_whitespace_tags = preserve_whitespace_tags + else: + # Set up any substitutions for this tag, such as the charset in a META tag. + builder.set_up_substitutions(self) + + # Ask the TreeBuilder whether this tag might be an empty-element tag. + self.can_be_empty_element = builder.can_be_empty_element(name) + + # Keep track of the list of attributes of this tag that + # might need to be treated as a list. + # + # For performance reasons, we store the whole data structure + # rather than asking the question of every tag. Asking would + # require building a new data structure every time, and + # (unlike can_be_empty_element), we almost never need + # to check this. + self.cdata_list_attributes = builder.cdata_list_attributes + + # Keep track of the names that might cause this tag to be treated as a + # whitespace-preserved tag. + self.preserve_whitespace_tags = builder.preserve_whitespace_tags + + parserClass = _alias("parser_class") # BS3 + + def __copy__(self): + """A copy of a Tag is a new Tag, unconnected to the parse tree. + Its contents are a copy of the old Tag's contents. + """ + clone = type(self)( + None, self.builder, self.name, self.namespace, + self.prefix, self.attrs, is_xml=self._is_xml, + sourceline=self.sourceline, sourcepos=self.sourcepos, + can_be_empty_element=self.can_be_empty_element, + cdata_list_attributes=self.cdata_list_attributes, + preserve_whitespace_tags=self.preserve_whitespace_tags + ) + for attr in ('can_be_empty_element', 'hidden'): + setattr(clone, attr, getattr(self, attr)) + for child in self.contents: + clone.append(child.__copy__()) + return clone + + @property + def is_empty_element(self): + """Is this tag an empty-element tag? (aka a self-closing tag) + + A tag that has contents is never an empty-element tag. + + A tag that has no contents may or may not be an empty-element + tag. It depends on the builder used to create the tag. If the + builder has a designated list of empty-element tags, then only + a tag whose name shows up in that list is considered an + empty-element tag. + + If the builder has no designated list of empty-element tags, + then any tag with no contents is an empty-element tag. + """ + return len(self.contents) == 0 and self.can_be_empty_element + isSelfClosing = is_empty_element # BS3 + + @property + def string(self): + """Convenience property to get the single string within this + PageElement. + + TODO It might make sense to have NavigableString.string return + itself. + + :return: If this element has a single string child, return + value is that string. If this element has one child tag, + return value is the 'string' attribute of the child tag, + recursively. If this element is itself a string, has no + children, or has more than one child, return value is None. + """ + if len(self.contents) != 1: + return None + child = self.contents[0] + if isinstance(child, NavigableString): + return child + return child.string + + @string.setter + def string(self, string): + """Replace this PageElement's contents with `string`.""" + self.clear() + self.append(string.__class__(string)) + + def _all_strings(self, strip=False, types=(NavigableString, CData)): + """Yield all strings of certain classes, possibly stripping them. + + :param strip: If True, all strings will be stripped before being + yielded. + + :types: A tuple of NavigableString subclasses. Any strings of + a subclass not found in this list will be ignored. By + default, this means only NavigableString and CData objects + will be considered. So no comments, processing instructions, + etc. + + :yield: A sequence of strings. + """ + for descendant in self.descendants: + if ( + (types is None and not isinstance(descendant, NavigableString)) + or + (types is not None and type(descendant) not in types)): + continue + if strip: + descendant = descendant.strip() + if len(descendant) == 0: + continue + yield descendant + + strings = property(_all_strings) + + @property + def stripped_strings(self): + """Yield all strings in the document, stripping them first. + + :yield: A sequence of stripped strings. + """ + for string in self._all_strings(True): + yield string + + def get_text(self, separator="", strip=False, + types=(NavigableString, CData)): + """Get all child strings, concatenated using the given separator. + + :param separator: Strings will be concatenated using this separator. + + :param strip: If True, strings will be stripped before being + concatenated. + + :types: A tuple of NavigableString subclasses. Any strings of + a subclass not found in this list will be ignored. By + default, this means only NavigableString and CData objects + will be considered. So no comments, processing instructions, + stylesheets, etc. + + :return: A string. + """ + return separator.join([s for s in self._all_strings( + strip, types=types)]) + getText = get_text + text = property(get_text) + + def decompose(self): + """Recursively destroys this PageElement and its children. + + This element will be removed from the tree and wiped out; so + will everything beneath it. + + The behavior of a decomposed PageElement is undefined and you + should never use one for anything, but if you need to _check_ + whether an element has been decomposed, you can use the + `decomposed` property. + """ + self.extract() + i = self + while i is not None: + n = i.next_element + i.__dict__.clear() + i.contents = [] + i._decomposed = True + i = n + + def clear(self, decompose=False): + """Wipe out all children of this PageElement by calling extract() + on them. + + :param decompose: If this is True, decompose() (a more + destructive method) will be called instead of extract(). + """ + if decompose: + for element in self.contents[:]: + if isinstance(element, Tag): + element.decompose() + else: + element.extract() + else: + for element in self.contents[:]: + element.extract() + + def smooth(self): + """Smooth out this element's children by consolidating consecutive + strings. + + This makes pretty-printed output look more natural following a + lot of operations that modified the tree. + """ + # Mark the first position of every pair of children that need + # to be consolidated. Do this rather than making a copy of + # self.contents, since in most cases very few strings will be + # affected. + marked = [] + for i, a in enumerate(self.contents): + if isinstance(a, Tag): + # Recursively smooth children. + a.smooth() + if i == len(self.contents)-1: + # This is the last item in .contents, and it's not a + # tag. There's no chance it needs any work. + continue + b = self.contents[i+1] + if (isinstance(a, NavigableString) + and isinstance(b, NavigableString) + and not isinstance(a, PreformattedString) + and not isinstance(b, PreformattedString) + ): + marked.append(i) + + # Go over the marked positions in reverse order, so that + # removing items from .contents won't affect the remaining + # positions. + for i in reversed(marked): + a = self.contents[i] + b = self.contents[i+1] + b.extract() + n = NavigableString(a+b) + a.replace_with(n) + + def index(self, element): + """Find the index of a child by identity, not value. + + Avoids issues with tag.contents.index(element) getting the + index of equal elements. + + :param element: Look for this PageElement in `self.contents`. + """ + for i, child in enumerate(self.contents): + if child is element: + return i + raise ValueError("Tag.index: element not in tag") + + def get(self, key, default=None): + """Returns the value of the 'key' attribute for the tag, or + the value given for 'default' if it doesn't have that + attribute.""" + return self.attrs.get(key, default) + + def get_attribute_list(self, key, default=None): + """The same as get(), but always returns a list. + + :param key: The attribute to look for. + :param default: Use this value if the attribute is not present + on this PageElement. + :return: A list of values, probably containing only a single + value. + """ + value = self.get(key, default) + if not isinstance(value, list): + value = [value] + return value + + def has_attr(self, key): + """Does this PageElement have an attribute with the given name?""" + return key in self.attrs + + def __hash__(self): + return str(self).__hash__() + + def __getitem__(self, key): + """tag[key] returns the value of the 'key' attribute for the Tag, + and throws an exception if it's not there.""" + return self.attrs[key] + + def __iter__(self): + "Iterating over a Tag iterates over its contents." + return iter(self.contents) + + def __len__(self): + "The length of a Tag is the length of its list of contents." + return len(self.contents) + + def __contains__(self, x): + return x in self.contents + + def __bool__(self): + "A tag is non-None even if it has no contents." + return True + + def __setitem__(self, key, value): + """Setting tag[key] sets the value of the 'key' attribute for the + tag.""" + self.attrs[key] = value + + def __delitem__(self, key): + "Deleting tag[key] deletes all 'key' attributes for the tag." + self.attrs.pop(key, None) + + def __call__(self, *args, **kwargs): + """Calling a Tag like a function is the same as calling its + find_all() method. Eg. tag('a') returns a list of all the A tags + found within this tag.""" + return self.find_all(*args, **kwargs) + + def __getattr__(self, tag): + """Calling tag.subtag is the same as calling tag.find(name="subtag")""" + #print("Getattr %s.%s" % (self.__class__, tag)) + if len(tag) > 3 and tag.endswith('Tag'): + # BS3: soup.aTag -> "soup.find("a") + tag_name = tag[:-3] + warnings.warn( + '.%(name)sTag is deprecated, use .find("%(name)s") instead. If you really were looking for a tag called %(name)sTag, use .find("%(name)sTag")' % dict( + name=tag_name + ) + ) + return self.find(tag_name) + # We special case contents to avoid recursion. + elif not tag.startswith("__") and not tag == "contents": + return self.find(tag) + raise AttributeError( + "'%s' object has no attribute '%s'" % (self.__class__, tag)) + + def __eq__(self, other): + """Returns true iff this Tag has the same name, the same attributes, + and the same contents (recursively) as `other`.""" + if self is other: + return True + if (not hasattr(other, 'name') or + not hasattr(other, 'attrs') or + not hasattr(other, 'contents') or + self.name != other.name or + self.attrs != other.attrs or + len(self) != len(other)): + return False + for i, my_child in enumerate(self.contents): + if my_child != other.contents[i]: + return False + return True + + def __ne__(self, other): + """Returns true iff this Tag is not identical to `other`, + as defined in __eq__.""" + return not self == other + + def __repr__(self, encoding="unicode-escape"): + """Renders this PageElement as a string. + + :param encoding: The encoding to use (Python 2 only). + :return: Under Python 2, a bytestring; under Python 3, + a Unicode string. + """ + if PY3K: + # "The return value must be a string object", i.e. Unicode + return self.decode() + else: + # "The return value must be a string object", i.e. a bytestring. + # By convention, the return value of __repr__ should also be + # an ASCII string. + return self.encode(encoding) + + def __unicode__(self): + """Renders this PageElement as a Unicode string.""" + return self.decode() + + def __str__(self): + """Renders this PageElement as a generic string. + + :return: Under Python 2, a UTF-8 bytestring; under Python 3, + a Unicode string. + """ + if PY3K: + return self.decode() + else: + return self.encode() + + if PY3K: + __str__ = __repr__ = __unicode__ + + def encode(self, encoding=DEFAULT_OUTPUT_ENCODING, + indent_level=None, formatter="minimal", + errors="xmlcharrefreplace"): + """Render a bytestring representation of this PageElement and its + contents. + + :param encoding: The destination encoding. + :param indent_level: Each line of the rendering will be + indented this many spaces. Used internally in + recursive calls while pretty-printing. + :param formatter: A Formatter object, or a string naming one of + the standard formatters. + :param errors: An error handling strategy such as + 'xmlcharrefreplace'. This value is passed along into + encode() and its value should be one of the constants + defined by Python. + :return: A bytestring. + + """ + # Turn the data structure into Unicode, then encode the + # Unicode. + u = self.decode(indent_level, encoding, formatter) + return u.encode(encoding, errors) + + def decode(self, indent_level=None, + eventual_encoding=DEFAULT_OUTPUT_ENCODING, + formatter="minimal"): + """Render a Unicode representation of this PageElement and its + contents. + + :param indent_level: Each line of the rendering will be + indented this many spaces. Used internally in + recursive calls while pretty-printing. + :param eventual_encoding: The tag is destined to be + encoded into this encoding. This method is _not_ + responsible for performing that encoding. This information + is passed in so that it can be substituted in if the + document contains a tag that mentions the document's + encoding. + :param formatter: A Formatter object, or a string naming one of + the standard formatters. + """ + + # First off, turn a non-Formatter `formatter` into a Formatter + # object. This will stop the lookup from happening over and + # over again. + if not isinstance(formatter, Formatter): + formatter = self.formatter_for_name(formatter) + attributes = formatter.attributes(self) + attrs = [] + for key, val in attributes: + if val is None: + decoded = key + else: + if isinstance(val, list) or isinstance(val, tuple): + val = ' '.join(val) + elif not isinstance(val, str): + val = str(val) + elif ( + isinstance(val, AttributeValueWithCharsetSubstitution) + and eventual_encoding is not None + ): + val = val.encode(eventual_encoding) + + text = formatter.attribute_value(val) + decoded = ( + str(key) + '=' + + formatter.quoted_attribute_value(text)) + attrs.append(decoded) + close = '' + closeTag = '' + + prefix = '' + if self.prefix: + prefix = self.prefix + ":" + + if self.is_empty_element: + close = formatter.void_element_close_prefix or '' + else: + closeTag = '' % (prefix, self.name) + + pretty_print = self._should_pretty_print(indent_level) + space = '' + indent_space = '' + if indent_level is not None: + indent_space = (' ' * (indent_level - 1)) + if pretty_print: + space = indent_space + indent_contents = indent_level + 1 + else: + indent_contents = None + contents = self.decode_contents( + indent_contents, eventual_encoding, formatter + ) + + if self.hidden: + # This is the 'document root' object. + s = contents + else: + s = [] + attribute_string = '' + if attrs: + attribute_string = ' ' + ' '.join(attrs) + if indent_level is not None: + # Even if this particular tag is not pretty-printed, + # we should indent up to the start of the tag. + s.append(indent_space) + s.append('<%s%s%s%s>' % ( + prefix, self.name, attribute_string, close)) + if pretty_print: + s.append("\n") + s.append(contents) + if pretty_print and contents and contents[-1] != "\n": + s.append("\n") + if pretty_print and closeTag: + s.append(space) + s.append(closeTag) + if indent_level is not None and closeTag and self.next_sibling: + # Even if this particular tag is not pretty-printed, + # we're now done with the tag, and we should add a + # newline if appropriate. + s.append("\n") + s = ''.join(s) + return s + + def _should_pretty_print(self, indent_level): + """Should this tag be pretty-printed? + + Most of them should, but some (such as
 in HTML
+        documents) should not.
+        """
+        return (
+            indent_level is not None
+            and (
+                not self.preserve_whitespace_tags
+                or self.name not in self.preserve_whitespace_tags
+            )
+        )
+
+    def prettify(self, encoding=None, formatter="minimal"):
+        """Pretty-print this PageElement as a string.
+
+        :param encoding: The eventual encoding of the string. If this is None,
+            a Unicode string will be returned.
+        :param formatter: A Formatter object, or a string naming one of
+            the standard formatters.
+        :return: A Unicode string (if encoding==None) or a bytestring 
+            (otherwise).
+        """
+        if encoding is None:
+            return self.decode(True, formatter=formatter)
+        else:
+            return self.encode(encoding, True, formatter=formatter)
+
+    def decode_contents(self, indent_level=None,
+                       eventual_encoding=DEFAULT_OUTPUT_ENCODING,
+                       formatter="minimal"):
+        """Renders the contents of this tag as a Unicode string.
+
+        :param indent_level: Each line of the rendering will be
+           indented this many spaces. Used internally in
+           recursive calls while pretty-printing.
+
+        :param eventual_encoding: The tag is destined to be
+           encoded into this encoding. decode_contents() is _not_
+           responsible for performing that encoding. This information
+           is passed in so that it can be substituted in if the
+           document contains a  tag that mentions the document's
+           encoding.
+
+        :param formatter: A Formatter object, or a string naming one of
+            the standard Formatters.
+        """
+        # First off, turn a string formatter into a Formatter object. This
+        # will stop the lookup from happening over and over again.
+        if not isinstance(formatter, Formatter):
+            formatter = self.formatter_for_name(formatter)
+
+        pretty_print = (indent_level is not None)
+        s = []
+        for c in self:
+            text = None
+            if isinstance(c, NavigableString):
+                text = c.output_ready(formatter)
+            elif isinstance(c, Tag):
+                s.append(c.decode(indent_level, eventual_encoding,
+                                  formatter))
+            preserve_whitespace = (
+                self.preserve_whitespace_tags and self.name in self.preserve_whitespace_tags
+            )
+            if text and indent_level and not preserve_whitespace:
+                text = text.strip()
+            if text:
+                if pretty_print and not preserve_whitespace:
+                    s.append(" " * (indent_level - 1))
+                s.append(text)
+                if pretty_print and not preserve_whitespace:
+                    s.append("\n")
+        return ''.join(s)
+       
+    def encode_contents(
+        self, indent_level=None, encoding=DEFAULT_OUTPUT_ENCODING,
+        formatter="minimal"):
+        """Renders the contents of this PageElement as a bytestring.
+
+        :param indent_level: Each line of the rendering will be
+           indented this many spaces. Used internally in
+           recursive calls while pretty-printing.
+
+        :param eventual_encoding: The bytestring will be in this encoding.
+
+        :param formatter: A Formatter object, or a string naming one of
+            the standard Formatters.
+
+        :return: A bytestring.
+        """
+        contents = self.decode_contents(indent_level, encoding, formatter)
+        return contents.encode(encoding)
+
+    # Old method for BS3 compatibility
+    def renderContents(self, encoding=DEFAULT_OUTPUT_ENCODING,
+                       prettyPrint=False, indentLevel=0):
+        """Deprecated method for BS3 compatibility."""
+        if not prettyPrint:
+            indentLevel = None
+        return self.encode_contents(
+            indent_level=indentLevel, encoding=encoding)
+
+    #Soup methods
+
+    def find(self, name=None, attrs={}, recursive=True, text=None,
+             **kwargs):
+        """Look in the children of this PageElement and find the first
+        PageElement that matches the given criteria.
+
+        All find_* methods take a common set of arguments. See the online
+        documentation for detailed explanations.
+
+        :param name: A filter on tag name.
+        :param attrs: A dictionary of filters on attribute values.
+        :param recursive: If this is True, find() will perform a
+            recursive search of this PageElement's children. Otherwise,
+            only the direct children will be considered.
+        :param limit: Stop looking after finding this many results.
+        :kwargs: A dictionary of filters on attribute values.
+        :return: A PageElement.
+        :rtype: bs4.element.Tag | bs4.element.NavigableString
+        """
+        r = None
+        l = self.find_all(name, attrs, recursive, text, 1, **kwargs)
+        if l:
+            r = l[0]
+        return r
+    findChild = find #BS2
+
+    def find_all(self, name=None, attrs={}, recursive=True, text=None,
+                 limit=None, **kwargs):
+        """Look in the children of this PageElement and find all
+        PageElements that match the given criteria.
+
+        All find_* methods take a common set of arguments. See the online
+        documentation for detailed explanations.
+
+        :param name: A filter on tag name.
+        :param attrs: A dictionary of filters on attribute values.
+        :param recursive: If this is True, find_all() will perform a
+            recursive search of this PageElement's children. Otherwise,
+            only the direct children will be considered.
+        :param limit: Stop looking after finding this many results.
+        :kwargs: A dictionary of filters on attribute values.
+        :return: A ResultSet of PageElements.
+        :rtype: bs4.element.ResultSet
+        """
+        generator = self.descendants
+        if not recursive:
+            generator = self.children
+        return self._find_all(name, attrs, text, limit, generator, **kwargs)
+    findAll = find_all       # BS3
+    findChildren = find_all  # BS2
+
+    #Generator methods
+    @property
+    def children(self):
+        """Iterate over all direct children of this PageElement.
+
+        :yield: A sequence of PageElements.
+        """
+        # return iter() to make the purpose of the method clear
+        return iter(self.contents)  # XXX This seems to be untested.
+
+    @property
+    def descendants(self):
+        """Iterate over all children of this PageElement in a
+        breadth-first sequence.
+
+        :yield: A sequence of PageElements.
+        """
+        if not len(self.contents):
+            return
+        stopNode = self._last_descendant().next_element
+        current = self.contents[0]
+        while current is not stopNode:
+            yield current
+            current = current.next_element
+
+    # CSS selector code
+    def select_one(self, selector, namespaces=None, **kwargs):
+        """Perform a CSS selection operation on the current element.
+
+        :param selector: A CSS selector.
+
+        :param namespaces: A dictionary mapping namespace prefixes
+           used in the CSS selector to namespace URIs. By default,
+           Beautiful Soup will use the prefixes it encountered while
+           parsing the document.
+
+        :param kwargs: Keyword arguments to be passed into SoupSieve's 
+           soupsieve.select() method.
+
+        :return: A Tag.
+        :rtype: bs4.element.Tag
+        """
+        value = self.select(selector, namespaces, 1, **kwargs)
+        if value:
+            return value[0]
+        return None
+
+    def select(self, selector, namespaces=None, limit=None, **kwargs):
+        """Perform a CSS selection operation on the current element.
+
+        This uses the SoupSieve library.
+
+        :param selector: A string containing a CSS selector.
+
+        :param namespaces: A dictionary mapping namespace prefixes
+           used in the CSS selector to namespace URIs. By default,
+           Beautiful Soup will use the prefixes it encountered while
+           parsing the document.
+
+        :param limit: After finding this number of results, stop looking.
+
+        :param kwargs: Keyword arguments to be passed into SoupSieve's 
+           soupsieve.select() method.
+
+        :return: A ResultSet of Tags.
+        :rtype: bs4.element.ResultSet
+        """
+        if namespaces is None:
+            namespaces = self._namespaces
+        
+        if limit is None:
+            limit = 0
+        if soupsieve is None:
+            raise NotImplementedError(
+                "Cannot execute CSS selectors because the soupsieve package is not installed."
+            )
+            
+        results = soupsieve.select(selector, self, namespaces, limit, **kwargs)
+
+        # We do this because it's more consistent and because
+        # ResultSet.__getattr__ has a helpful error message.
+        return ResultSet(None, results)
+
+    # Old names for backwards compatibility
+    def childGenerator(self):
+        """Deprecated generator."""
+        return self.children
+
+    def recursiveChildGenerator(self):
+        """Deprecated generator."""
+        return self.descendants
+
+    def has_key(self, key):
+        """Deprecated method. This was kind of misleading because has_key()
+        (attributes) was different from __in__ (contents).
+
+        has_key() is gone in Python 3, anyway.
+        """
+        warnings.warn('has_key is deprecated. Use has_attr("%s") instead.' % (
+                key))
+        return self.has_attr(key)
+
+# Next, a couple classes to represent queries and their results.
+class SoupStrainer(object):
+    """Encapsulates a number of ways of matching a markup element (tag or
+    string).
+
+    This is primarily used to underpin the find_* methods, but you can
+    create one yourself and pass it in as `parse_only` to the
+    `BeautifulSoup` constructor, to parse a subset of a large
+    document.
+    """
+
+    def __init__(self, name=None, attrs={}, text=None, **kwargs):
+        """Constructor.
+
+        The SoupStrainer constructor takes the same arguments passed
+        into the find_* methods. See the online documentation for
+        detailed explanations.
+
+        :param name: A filter on tag name.
+        :param attrs: A dictionary of filters on attribute values.
+        :param text: A filter for a NavigableString with specific text.
+        :kwargs: A dictionary of filters on attribute values.
+        """        
+        self.name = self._normalize_search_value(name)
+        if not isinstance(attrs, dict):
+            # Treat a non-dict value for attrs as a search for the 'class'
+            # attribute.
+            kwargs['class'] = attrs
+            attrs = None
+
+        if 'class_' in kwargs:
+            # Treat class_="foo" as a search for the 'class'
+            # attribute, overriding any non-dict value for attrs.
+            kwargs['class'] = kwargs['class_']
+            del kwargs['class_']
+
+        if kwargs:
+            if attrs:
+                attrs = attrs.copy()
+                attrs.update(kwargs)
+            else:
+                attrs = kwargs
+        normalized_attrs = {}
+        for key, value in list(attrs.items()):
+            normalized_attrs[key] = self._normalize_search_value(value)
+
+        self.attrs = normalized_attrs
+        self.text = self._normalize_search_value(text)
+
+    def _normalize_search_value(self, value):
+        # Leave it alone if it's a Unicode string, a callable, a
+        # regular expression, a boolean, or None.
+        if (isinstance(value, str) or isinstance(value, Callable) or hasattr(value, 'match')
+            or isinstance(value, bool) or value is None):
+            return value
+
+        # If it's a bytestring, convert it to Unicode, treating it as UTF-8.
+        if isinstance(value, bytes):
+            return value.decode("utf8")
+
+        # If it's listlike, convert it into a list of strings.
+        if hasattr(value, '__iter__'):
+            new_value = []
+            for v in value:
+                if (hasattr(v, '__iter__') and not isinstance(v, bytes)
+                    and not isinstance(v, str)):
+                    # This is almost certainly the user's mistake. In the
+                    # interests of avoiding infinite loops, we'll let
+                    # it through as-is rather than doing a recursive call.
+                    new_value.append(v)
+                else:
+                    new_value.append(self._normalize_search_value(v))
+            return new_value
+
+        # Otherwise, convert it into a Unicode string.
+        # The unicode(str()) thing is so this will do the same thing on Python 2
+        # and Python 3.
+        return str(str(value))
+
+    def __str__(self):
+        """A human-readable representation of this SoupStrainer."""
+        if self.text:
+            return self.text
+        else:
+            return "%s|%s" % (self.name, self.attrs)
+
+    def search_tag(self, markup_name=None, markup_attrs={}):
+        """Check whether a Tag with the given name and attributes would
+        match this SoupStrainer.
+
+        Used prospectively to decide whether to even bother creating a Tag
+        object.
+
+        :param markup_name: A tag name as found in some markup.
+        :param markup_attrs: A dictionary of attributes as found in some markup.
+
+        :return: True if the prospective tag would match this SoupStrainer;
+            False otherwise.
+        """
+        found = None
+        markup = None
+        if isinstance(markup_name, Tag):
+            markup = markup_name
+            markup_attrs = markup
+
+        if isinstance(self.name, str):
+            # Optimization for a very common case where the user is
+            # searching for a tag with one specific name, and we're
+            # looking at a tag with a different name.
+            if markup and not markup.prefix and self.name != markup.name:
+                 return False
+            
+        call_function_with_tag_data = (
+            isinstance(self.name, Callable)
+            and not isinstance(markup_name, Tag))
+
+        if ((not self.name)
+            or call_function_with_tag_data
+            or (markup and self._matches(markup, self.name))
+            or (not markup and self._matches(markup_name, self.name))):
+            if call_function_with_tag_data:
+                match = self.name(markup_name, markup_attrs)
+            else:
+                match = True
+                markup_attr_map = None
+                for attr, match_against in list(self.attrs.items()):
+                    if not markup_attr_map:
+                        if hasattr(markup_attrs, 'get'):
+                            markup_attr_map = markup_attrs
+                        else:
+                            markup_attr_map = {}
+                            for k, v in markup_attrs:
+                                markup_attr_map[k] = v
+                    attr_value = markup_attr_map.get(attr)
+                    if not self._matches(attr_value, match_against):
+                        match = False
+                        break
+            if match:
+                if markup:
+                    found = markup
+                else:
+                    found = markup_name
+        if found and self.text and not self._matches(found.string, self.text):
+            found = None
+        return found
+
+    # For BS3 compatibility.
+    searchTag = search_tag
+
+    def search(self, markup):
+        """Find all items in `markup` that match this SoupStrainer.
+
+        Used by the core _find_all() method, which is ultimately
+        called by all find_* methods.
+
+        :param markup: A PageElement or a list of them.
+        """
+        # print('looking for %s in %s' % (self, markup))
+        found = None
+        # If given a list of items, scan it for a text element that
+        # matches.
+        if hasattr(markup, '__iter__') and not isinstance(markup, (Tag, str)):
+            for element in markup:
+                if isinstance(element, NavigableString) \
+                       and self.search(element):
+                    found = element
+                    break
+        # If it's a Tag, make sure its name or attributes match.
+        # Don't bother with Tags if we're searching for text.
+        elif isinstance(markup, Tag):
+            if not self.text or self.name or self.attrs:
+                found = self.search_tag(markup)
+        # If it's text, make sure the text matches.
+        elif isinstance(markup, NavigableString) or \
+                 isinstance(markup, str):
+            if not self.name and not self.attrs and self._matches(markup, self.text):
+                found = markup
+        else:
+            raise Exception(
+                "I don't know how to match against a %s" % markup.__class__)
+        return found
+
+    def _matches(self, markup, match_against, already_tried=None):
+        # print(u"Matching %s against %s" % (markup, match_against))
+        result = False
+        if isinstance(markup, list) or isinstance(markup, tuple):
+            # This should only happen when searching a multi-valued attribute
+            # like 'class'.
+            for item in markup:
+                if self._matches(item, match_against):
+                    return True
+            # We didn't match any particular value of the multivalue
+            # attribute, but maybe we match the attribute value when
+            # considered as a string.
+            if self._matches(' '.join(markup), match_against):
+                return True
+            return False
+        
+        if match_against is True:
+            # True matches any non-None value.
+            return markup is not None
+
+        if isinstance(match_against, Callable):
+            return match_against(markup)
+
+        # Custom callables take the tag as an argument, but all
+        # other ways of matching match the tag name as a string.
+        original_markup = markup
+        if isinstance(markup, Tag):
+            markup = markup.name
+
+        # Ensure that `markup` is either a Unicode string, or None.
+        markup = self._normalize_search_value(markup)
+
+        if markup is None:
+            # None matches None, False, an empty string, an empty list, and so on.
+            return not match_against
+
+        if (hasattr(match_against, '__iter__')
+            and not isinstance(match_against, str)):
+            # We're asked to match against an iterable of items.
+            # The markup must be match at least one item in the
+            # iterable. We'll try each one in turn.
+            #
+            # To avoid infinite recursion we need to keep track of
+            # items we've already seen.
+            if not already_tried:
+                already_tried = set()
+            for item in match_against:
+                if item.__hash__:
+                    key = item
+                else:
+                    key = id(item)
+                if key in already_tried:
+                    continue
+                else:
+                    already_tried.add(key)
+                    if self._matches(original_markup, item, already_tried):
+                        return True
+            else:
+                return False
+        
+        # Beyond this point we might need to run the test twice: once against
+        # the tag's name and once against its prefixed name.
+        match = False
+        
+        if not match and isinstance(match_against, str):
+            # Exact string match
+            match = markup == match_against
+
+        if not match and hasattr(match_against, 'search'):
+            # Regexp match
+            return match_against.search(markup)
+
+        if (not match
+            and isinstance(original_markup, Tag)
+            and original_markup.prefix):
+            # Try the whole thing again with the prefixed tag name.
+            return self._matches(
+                original_markup.prefix + ':' + original_markup.name, match_against
+            )
+
+        return match
+
+
+class ResultSet(list):
+    """A ResultSet is just a list that keeps track of the SoupStrainer
+    that created it."""
+    def __init__(self, source, result=()):
+        """Constructor.
+
+        :param source: A SoupStrainer.
+        :param result: A list of PageElements.
+        """
+        super(ResultSet, self).__init__(result)
+        self.source = source
+
+    def __getattr__(self, key):
+        """Raise a helpful exception to explain a common code fix."""
+        raise AttributeError(
+            "ResultSet object has no attribute '%s'. You're probably treating a list of elements like a single element. Did you call find_all() when you meant to call find()?" % key
+        )
diff --git a/venv/lib/python3.11/site-packages/bs4/formatter.py b/venv/lib/python3.11/site-packages/bs4/formatter.py
new file mode 100644
index 00000000..2cbab4c7
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/bs4/formatter.py
@@ -0,0 +1,152 @@
+from bs4.dammit import EntitySubstitution
+
+class Formatter(EntitySubstitution):
+    """Describes a strategy to use when outputting a parse tree to a string.
+
+    Some parts of this strategy come from the distinction between
+    HTML4, HTML5, and XML. Others are configurable by the user.
+
+    Formatters are passed in as the `formatter` argument to methods
+    like `PageElement.encode`. Most people won't need to think about
+    formatters, and most people who need to think about them can pass
+    in one of these predefined strings as `formatter` rather than
+    making a new Formatter object:
+
+    For HTML documents:
+     * 'html' - HTML entity substitution for generic HTML documents. (default)
+     * 'html5' - HTML entity substitution for HTML5 documents.
+     * 'minimal' - Only make the substitutions necessary to guarantee
+                   valid HTML.
+     * None - Do not perform any substitution. This will be faster
+              but may result in invalid markup.
+
+    For XML documents:
+     * 'html' - Entity substitution for XHTML documents.
+     * 'minimal' - Only make the substitutions necessary to guarantee
+                   valid XML. (default)
+     * None - Do not perform any substitution. This will be faster
+              but may result in invalid markup.
+    """
+    # Registries of XML and HTML formatters.
+    XML_FORMATTERS = {}
+    HTML_FORMATTERS = {}
+
+    HTML = 'html'
+    XML = 'xml'
+
+    HTML_DEFAULTS = dict(
+        cdata_containing_tags=set(["script", "style"]),
+    )
+
+    def _default(self, language, value, kwarg):
+        if value is not None:
+            return value
+        if language == self.XML:
+            return set()
+        return self.HTML_DEFAULTS[kwarg]
+
+    def __init__(
+            self, language=None, entity_substitution=None,
+            void_element_close_prefix='/', cdata_containing_tags=None,
+    ):
+        """Constructor.
+
+        :param language: This should be Formatter.XML if you are formatting
+           XML markup and Formatter.HTML if you are formatting HTML markup.
+
+        :param entity_substitution: A function to call to replace special
+           characters with XML/HTML entities. For examples, see 
+           bs4.dammit.EntitySubstitution.substitute_html and substitute_xml.
+        :param void_element_close_prefix: By default, void elements
+           are represented as  (XML rules) rather than 
+           (HTML rules). To get , pass in the empty string.
+        :param cdata_containing_tags: The list of tags that are defined
+           as containing CDATA in this dialect. For example, in HTML,
+           
+
This numeric entity is missing the final semicolon:
+ +
a
+
This document contains (do you see it?)
+
This document ends with That attribute value was bogus
+The doctype is invalid because it contains extra whitespace +
That boolean attribute had no value
+
Here's a nonexistent entity: &#foo; (do you see it?)
+
This document ends before the entity finishes: > +

Paragraphs shouldn't contain block display elements, but this one does:

you see?

+Multiple values for the same attribute. +
Here's a table
+
+
This tag contains nothing but whitespace:
+

This p tag is cut off by

the end of the blockquote tag
+
Here's a nested table:
foo
This table contains bare markup
+ +
This document contains a surprise doctype
+ +
Tag name contains Unicode characters
+ + +""" + + +class SoupTest(unittest.TestCase): + + @property + def default_builder(self): + return default_builder + + def soup(self, markup, **kwargs): + """Build a Beautiful Soup object from markup.""" + builder = kwargs.pop('builder', self.default_builder) + return BeautifulSoup(markup, builder=builder, **kwargs) + + def document_for(self, markup, **kwargs): + """Turn an HTML fragment into a document. + + The details depend on the builder. + """ + return self.default_builder(**kwargs).test_fragment_to_document(markup) + + def assertSoupEquals(self, to_parse, compare_parsed_to=None): + builder = self.default_builder + obj = BeautifulSoup(to_parse, builder=builder) + if compare_parsed_to is None: + compare_parsed_to = to_parse + + # Verify that the documents come out the same. + self.assertEqual(obj.decode(), self.document_for(compare_parsed_to)) + + # Also run some checks on the BeautifulSoup object itself: + + # Verify that every tag that was opened was eventually closed. + + # There are no tags in the open tag counter. + assert all(v==0 for v in list(obj.open_tag_counter.values())) + + # The only tag in the tag stack is the one for the root + # document. + self.assertEqual( + [obj.ROOT_TAG_NAME], [x.name for x in obj.tagStack] + ) + + def assertConnectedness(self, element): + """Ensure that next_element and previous_element are properly + set for all descendants of the given element. + """ + earlier = None + for e in element.descendants: + if earlier: + self.assertEqual(e, earlier.next_element) + self.assertEqual(earlier, e.previous_element) + earlier = e + + def linkage_validator(self, el, _recursive_call=False): + """Ensure proper linkage throughout the document.""" + descendant = None + # Document element should have no previous element or previous sibling. + # It also shouldn't have a next sibling. + if el.parent is None: + assert el.previous_element is None,\ + "Bad previous_element\nNODE: {}\nPREV: {}\nEXPECTED: {}".format( + el, el.previous_element, None + ) + assert el.previous_sibling is None,\ + "Bad previous_sibling\nNODE: {}\nPREV: {}\nEXPECTED: {}".format( + el, el.previous_sibling, None + ) + assert el.next_sibling is None,\ + "Bad next_sibling\nNODE: {}\nNEXT: {}\nEXPECTED: {}".format( + el, el.next_sibling, None + ) + + idx = 0 + child = None + last_child = None + last_idx = len(el.contents) - 1 + for child in el.contents: + descendant = None + + # Parent should link next element to their first child + # That child should have no previous sibling + if idx == 0: + if el.parent is not None: + assert el.next_element is child,\ + "Bad next_element\nNODE: {}\nNEXT: {}\nEXPECTED: {}".format( + el, el.next_element, child + ) + assert child.previous_element is el,\ + "Bad previous_element\nNODE: {}\nPREV: {}\nEXPECTED: {}".format( + child, child.previous_element, el + ) + assert child.previous_sibling is None,\ + "Bad previous_sibling\nNODE: {}\nPREV {}\nEXPECTED: {}".format( + child, child.previous_sibling, None + ) + + # If not the first child, previous index should link as sibling to this index + # Previous element should match the last index or the last bubbled up descendant + else: + assert child.previous_sibling is el.contents[idx - 1],\ + "Bad previous_sibling\nNODE: {}\nPREV {}\nEXPECTED {}".format( + child, child.previous_sibling, el.contents[idx - 1] + ) + assert el.contents[idx - 1].next_sibling is child,\ + "Bad next_sibling\nNODE: {}\nNEXT {}\nEXPECTED {}".format( + el.contents[idx - 1], el.contents[idx - 1].next_sibling, child + ) + + if last_child is not None: + assert child.previous_element is last_child,\ + "Bad previous_element\nNODE: {}\nPREV {}\nEXPECTED {}\nCONTENTS {}".format( + child, child.previous_element, last_child, child.parent.contents + ) + assert last_child.next_element is child,\ + "Bad next_element\nNODE: {}\nNEXT {}\nEXPECTED {}".format( + last_child, last_child.next_element, child + ) + + if isinstance(child, Tag) and child.contents: + descendant = self.linkage_validator(child, True) + # A bubbled up descendant should have no next siblings + assert descendant.next_sibling is None,\ + "Bad next_sibling\nNODE: {}\nNEXT {}\nEXPECTED {}".format( + descendant, descendant.next_sibling, None + ) + + # Mark last child as either the bubbled up descendant or the current child + if descendant is not None: + last_child = descendant + else: + last_child = child + + # If last child, there are non next siblings + if idx == last_idx: + assert child.next_sibling is None,\ + "Bad next_sibling\nNODE: {}\nNEXT {}\nEXPECTED {}".format( + child, child.next_sibling, None + ) + idx += 1 + + child = descendant if descendant is not None else child + if child is None: + child = el + + if not _recursive_call and child is not None: + target = el + while True: + if target is None: + assert child.next_element is None, \ + "Bad next_element\nNODE: {}\nNEXT {}\nEXPECTED {}".format( + child, child.next_element, None + ) + break + elif target.next_sibling is not None: + assert child.next_element is target.next_sibling, \ + "Bad next_element\nNODE: {}\nNEXT {}\nEXPECTED {}".format( + child, child.next_element, target.next_sibling + ) + break + target = target.parent + + # We are done, so nothing to return + return None + else: + # Return the child to the recursive caller + return child + + +class HTMLTreeBuilderSmokeTest(object): + + """A basic test of a treebuilder's competence. + + Any HTML treebuilder, present or future, should be able to pass + these tests. With invalid markup, there's room for interpretation, + and different parsers can handle it differently. But with the + markup in these tests, there's not much room for interpretation. + """ + + def test_empty_element_tags(self): + """Verify that all HTML4 and HTML5 empty element (aka void element) tags + are handled correctly. + """ + for name in [ + 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr', + 'spacer', 'frame' + ]: + soup = self.soup("") + new_tag = soup.new_tag(name) + self.assertEqual(True, new_tag.is_empty_element) + + def test_special_string_containers(self): + soup = self.soup( + "" + ) + assert isinstance(soup.style.string, Stylesheet) + assert isinstance(soup.script.string, Script) + + soup = self.soup( + "" + ) + assert isinstance(soup.style.string, Stylesheet) + # The contents of the style tag resemble an HTML comment, but + # it's not treated as a comment. + self.assertEqual("", soup.style.string) + assert isinstance(soup.style.string, Stylesheet) + + def test_pickle_and_unpickle_identity(self): + # Pickling a tree, then unpickling it, yields a tree identical + # to the original. + tree = self.soup("foo") + dumped = pickle.dumps(tree, 2) + loaded = pickle.loads(dumped) + self.assertEqual(loaded.__class__, BeautifulSoup) + self.assertEqual(loaded.decode(), tree.decode()) + + def assertDoctypeHandled(self, doctype_fragment): + """Assert that a given doctype string is handled correctly.""" + doctype_str, soup = self._document_with_doctype(doctype_fragment) + + # Make sure a Doctype object was created. + doctype = soup.contents[0] + self.assertEqual(doctype.__class__, Doctype) + self.assertEqual(doctype, doctype_fragment) + self.assertEqual( + soup.encode("utf8")[:len(doctype_str)], + doctype_str + ) + + # Make sure that the doctype was correctly associated with the + # parse tree and that the rest of the document parsed. + self.assertEqual(soup.p.contents[0], 'foo') + + def _document_with_doctype(self, doctype_fragment, doctype_string="DOCTYPE"): + """Generate and parse a document with the given doctype.""" + doctype = '' % (doctype_string, doctype_fragment) + markup = doctype + '\n

foo

' + soup = self.soup(markup) + return doctype.encode("utf8"), soup + + def test_normal_doctypes(self): + """Make sure normal, everyday HTML doctypes are handled correctly.""" + self.assertDoctypeHandled("html") + self.assertDoctypeHandled( + 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"') + + def test_empty_doctype(self): + soup = self.soup("") + doctype = soup.contents[0] + self.assertEqual("", doctype.strip()) + + def test_mixed_case_doctype(self): + # A lowercase or mixed-case doctype becomes a Doctype. + for doctype_fragment in ("doctype", "DocType"): + doctype_str, soup = self._document_with_doctype( + "html", doctype_fragment + ) + + # Make sure a Doctype object was created and that the DOCTYPE + # is uppercase. + doctype = soup.contents[0] + self.assertEqual(doctype.__class__, Doctype) + self.assertEqual(doctype, "html") + self.assertEqual( + soup.encode("utf8")[:len(doctype_str)], + b"" + ) + + # Make sure that the doctype was correctly associated with the + # parse tree and that the rest of the document parsed. + self.assertEqual(soup.p.contents[0], 'foo') + + def test_public_doctype_with_url(self): + doctype = 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"' + self.assertDoctypeHandled(doctype) + + def test_system_doctype(self): + self.assertDoctypeHandled('foo SYSTEM "http://www.example.com/"') + + def test_namespaced_system_doctype(self): + # We can handle a namespaced doctype with a system ID. + self.assertDoctypeHandled('xsl:stylesheet SYSTEM "htmlent.dtd"') + + def test_namespaced_public_doctype(self): + # Test a namespaced doctype with a public id. + self.assertDoctypeHandled('xsl:stylesheet PUBLIC "htmlent.dtd"') + + def test_real_xhtml_document(self): + """A real XHTML document should come out more or less the same as it went in.""" + markup = b""" + + +Hello. +Goodbye. +""" + soup = self.soup(markup) + self.assertEqual( + soup.encode("utf-8").replace(b"\n", b""), + markup.replace(b"\n", b"")) + + def test_namespaced_html(self): + """When a namespaced XML document is parsed as HTML it should + be treated as HTML with weird tag names. + """ + markup = b"""content""" + soup = self.soup(markup) + self.assertEqual(2, len(soup.find_all("ns1:foo"))) + + def test_processing_instruction(self): + # We test both Unicode and bytestring to verify that + # process_markup correctly sets processing_instruction_class + # even when the markup is already Unicode and there is no + # need to process anything. + markup = """""" + soup = self.soup(markup) + self.assertEqual(markup, soup.decode()) + + markup = b"""""" + soup = self.soup(markup) + self.assertEqual(markup, soup.encode("utf8")) + + def test_deepcopy(self): + """Make sure you can copy the tree builder. + + This is important because the builder is part of a + BeautifulSoup object, and we want to be able to copy that. + """ + copy.deepcopy(self.default_builder) + + def test_p_tag_is_never_empty_element(self): + """A

tag is never designated as an empty-element tag. + + Even if the markup shows it as an empty-element tag, it + shouldn't be presented that way. + """ + soup = self.soup("

") + self.assertFalse(soup.p.is_empty_element) + self.assertEqual(str(soup.p), "

") + + def test_unclosed_tags_get_closed(self): + """A tag that's not closed by the end of the document should be closed. + + This applies to all tags except empty-element tags. + """ + self.assertSoupEquals("

", "

") + self.assertSoupEquals("", "") + + self.assertSoupEquals("
", "
") + + def test_br_is_always_empty_element_tag(self): + """A
tag is designated as an empty-element tag. + + Some parsers treat

as one
tag, some parsers as + two tags, but it should always be an empty-element tag. + """ + soup = self.soup("

") + self.assertTrue(soup.br.is_empty_element) + self.assertEqual(str(soup.br), "
") + + def test_nested_formatting_elements(self): + self.assertSoupEquals("") + + def test_double_head(self): + html = ''' + + +Ordinary HEAD element test + + + +Hello, world! + + +''' + soup = self.soup(html) + self.assertEqual("text/javascript", soup.find('script')['type']) + + def test_comment(self): + # Comments are represented as Comment objects. + markup = "

foobaz

" + self.assertSoupEquals(markup) + + soup = self.soup(markup) + comment = soup.find(text="foobar") + self.assertEqual(comment.__class__, Comment) + + # The comment is properly integrated into the tree. + foo = soup.find(text="foo") + self.assertEqual(comment, foo.next_element) + baz = soup.find(text="baz") + self.assertEqual(comment, baz.previous_element) + + def test_preserved_whitespace_in_pre_and_textarea(self): + """Whitespace must be preserved in
 and "
+        self.assertSoupEquals(pre_markup)
+        self.assertSoupEquals(textarea_markup)
+
+        soup = self.soup(pre_markup)
+        self.assertEqual(soup.pre.prettify(), pre_markup)
+
+        soup = self.soup(textarea_markup)
+        self.assertEqual(soup.textarea.prettify(), textarea_markup)
+
+        soup = self.soup("")
+        self.assertEqual(soup.textarea.prettify(), "")
+
+    def test_nested_inline_elements(self):
+        """Inline elements can be nested indefinitely."""
+        b_tag = "Inside a B tag"
+        self.assertSoupEquals(b_tag)
+
+        nested_b_tag = "

A nested tag

" + self.assertSoupEquals(nested_b_tag) + + double_nested_b_tag = "

A doubly nested tag

" + self.assertSoupEquals(nested_b_tag) + + def test_nested_block_level_elements(self): + """Block elements can be nested.""" + soup = self.soup('

Foo

') + blockquote = soup.blockquote + self.assertEqual(blockquote.p.b.string, 'Foo') + self.assertEqual(blockquote.b.string, 'Foo') + + def test_correctly_nested_tables(self): + """One table can go inside another one.""" + markup = ('' + '' + "') + + self.assertSoupEquals( + markup, + '
Here's another table:" + '' + '' + '
foo
Here\'s another table:' + '
foo
' + '
') + + self.assertSoupEquals( + "" + "" + "
Foo
Bar
Baz
") + + def test_multivalued_attribute_with_whitespace(self): + # Whitespace separating the values of a multi-valued attribute + # should be ignored. + + markup = '
' + soup = self.soup(markup) + self.assertEqual(['foo', 'bar'], soup.div['class']) + + # If you search by the literal name of the class it's like the whitespace + # wasn't there. + self.assertEqual(soup.div, soup.find('div', class_="foo bar")) + + def test_deeply_nested_multivalued_attribute(self): + # html5lib can set the attributes of the same tag many times + # as it rearranges the tree. This has caused problems with + # multivalued attributes. + markup = '
' + soup = self.soup(markup) + self.assertEqual(["css"], soup.div.div['class']) + + def test_multivalued_attribute_on_html(self): + # html5lib uses a different API to set the attributes ot the + # tag. This has caused problems with multivalued + # attributes. + markup = '' + soup = self.soup(markup) + self.assertEqual(["a", "b"], soup.html['class']) + + def test_angle_brackets_in_attribute_values_are_escaped(self): + self.assertSoupEquals('', '') + + def test_strings_resembling_character_entity_references(self): + # "&T" and "&p" look like incomplete character entities, but they are + # not. + self.assertSoupEquals( + "

• AT&T is in the s&p 500

", + "

\u2022 AT&T is in the s&p 500

" + ) + + def test_apos_entity(self): + self.assertSoupEquals( + "

Bob's Bar

", + "

Bob's Bar

", + ) + + def test_entities_in_foreign_document_encoding(self): + # “ and ” are invalid numeric entities referencing + # Windows-1252 characters. - references a character common + # to Windows-1252 and Unicode, and ☃ references a + # character only found in Unicode. + # + # All of these entities should be converted to Unicode + # characters. + markup = "

“Hello” -☃

" + soup = self.soup(markup) + self.assertEqual("“Hello” -☃", soup.p.string) + + def test_entities_in_attributes_converted_to_unicode(self): + expect = '

' + self.assertSoupEquals('

', expect) + self.assertSoupEquals('

', expect) + self.assertSoupEquals('

', expect) + self.assertSoupEquals('

', expect) + + def test_entities_in_text_converted_to_unicode(self): + expect = '

pi\N{LATIN SMALL LETTER N WITH TILDE}ata

' + self.assertSoupEquals("

piñata

", expect) + self.assertSoupEquals("

piñata

", expect) + self.assertSoupEquals("

piñata

", expect) + self.assertSoupEquals("

piñata

", expect) + + def test_quot_entity_converted_to_quotation_mark(self): + self.assertSoupEquals("

I said "good day!"

", + '

I said "good day!"

') + + def test_out_of_range_entity(self): + expect = "\N{REPLACEMENT CHARACTER}" + self.assertSoupEquals("�", expect) + self.assertSoupEquals("�", expect) + self.assertSoupEquals("�", expect) + + def test_multipart_strings(self): + "Mostly to prevent a recurrence of a bug in the html5lib treebuilder." + soup = self.soup("

\nfoo

") + self.assertEqual("p", soup.h2.string.next_element.name) + self.assertEqual("p", soup.p.name) + self.assertConnectedness(soup) + + def test_empty_element_tags(self): + """Verify consistent handling of empty-element tags, + no matter how they come in through the markup. + """ + self.assertSoupEquals('


', "


") + self.assertSoupEquals('


', "


") + + def test_head_tag_between_head_and_body(self): + "Prevent recurrence of a bug in the html5lib treebuilder." + content = """ + + foo + +""" + soup = self.soup(content) + self.assertNotEqual(None, soup.html.body) + self.assertConnectedness(soup) + + def test_multiple_copies_of_a_tag(self): + "Prevent recurrence of a bug in the html5lib treebuilder." + content = """ + + + + + +""" + soup = self.soup(content) + self.assertConnectedness(soup.article) + + def test_basic_namespaces(self): + """Parsers don't need to *understand* namespaces, but at the + very least they should not choke on namespaces or lose + data.""" + + markup = b'4' + soup = self.soup(markup) + self.assertEqual(markup, soup.encode()) + html = soup.html + self.assertEqual('http://www.w3.org/1999/xhtml', soup.html['xmlns']) + self.assertEqual( + 'http://www.w3.org/1998/Math/MathML', soup.html['xmlns:mathml']) + self.assertEqual( + 'http://www.w3.org/2000/svg', soup.html['xmlns:svg']) + + def test_multivalued_attribute_value_becomes_list(self): + markup = b'' + soup = self.soup(markup) + self.assertEqual(['foo', 'bar'], soup.a['class']) + + # + # Generally speaking, tests below this point are more tests of + # Beautiful Soup than tests of the tree builders. But parsers are + # weird, so we run these tests separately for every tree builder + # to detect any differences between them. + # + + def test_can_parse_unicode_document(self): + # A seemingly innocuous document... but it's in Unicode! And + # it contains characters that can't be represented in the + # encoding found in the declaration! The horror! + markup = 'Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!' + soup = self.soup(markup) + self.assertEqual('Sacr\xe9 bleu!', soup.body.string) + + def test_soupstrainer(self): + """Parsers should be able to work with SoupStrainers.""" + strainer = SoupStrainer("b") + soup = self.soup("A bold statement", + parse_only=strainer) + self.assertEqual(soup.decode(), "bold") + + def test_single_quote_attribute_values_become_double_quotes(self): + self.assertSoupEquals("", + '') + + def test_attribute_values_with_nested_quotes_are_left_alone(self): + text = """a""" + self.assertSoupEquals(text) + + def test_attribute_values_with_double_nested_quotes_get_quoted(self): + text = """a""" + soup = self.soup(text) + soup.foo['attr'] = 'Brawls happen at "Bob\'s Bar"' + self.assertSoupEquals( + soup.foo.decode(), + """a""") + + def test_ampersand_in_attribute_value_gets_escaped(self): + self.assertSoupEquals('', + '') + + self.assertSoupEquals( + 'foo', + 'foo') + + def test_escaped_ampersand_in_attribute_value_is_left_alone(self): + self.assertSoupEquals('') + + def test_entities_in_strings_converted_during_parsing(self): + # Both XML and HTML entities are converted to Unicode characters + # during parsing. + text = "

<<sacré bleu!>>

" + expected = "

<<sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>>

" + self.assertSoupEquals(text, expected) + + def test_smart_quotes_converted_on_the_way_in(self): + # Microsoft smart quotes are converted to Unicode characters during + # parsing. + quote = b"

\x91Foo\x92

" + soup = self.soup(quote) + self.assertEqual( + soup.p.string, + "\N{LEFT SINGLE QUOTATION MARK}Foo\N{RIGHT SINGLE QUOTATION MARK}") + + def test_non_breaking_spaces_converted_on_the_way_in(self): + soup = self.soup("  ") + self.assertEqual(soup.a.string, "\N{NO-BREAK SPACE}" * 2) + + def test_entities_converted_on_the_way_out(self): + text = "

<<sacré bleu!>>

" + expected = "

<<sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>>

".encode("utf-8") + soup = self.soup(text) + self.assertEqual(soup.p.encode("utf-8"), expected) + + def test_real_iso_latin_document(self): + # Smoke test of interrelated functionality, using an + # easy-to-understand document. + + # Here it is in Unicode. Note that it claims to be in ISO-Latin-1. + unicode_html = '

Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!

' + + # That's because we're going to encode it into ISO-Latin-1, and use + # that to test. + iso_latin_html = unicode_html.encode("iso-8859-1") + + # Parse the ISO-Latin-1 HTML. + soup = self.soup(iso_latin_html) + # Encode it to UTF-8. + result = soup.encode("utf-8") + + # What do we expect the result to look like? Well, it would + # look like unicode_html, except that the META tag would say + # UTF-8 instead of ISO-Latin-1. + expected = unicode_html.replace("ISO-Latin-1", "utf-8") + + # And, of course, it would be in UTF-8, not Unicode. + expected = expected.encode("utf-8") + + # Ta-da! + self.assertEqual(result, expected) + + def test_real_shift_jis_document(self): + # Smoke test to make sure the parser can handle a document in + # Shift-JIS encoding, without choking. + shift_jis_html = ( + b'
'
+            b'\x82\xb1\x82\xea\x82\xcdShift-JIS\x82\xc5\x83R\x81[\x83f'
+            b'\x83B\x83\x93\x83O\x82\xb3\x82\xea\x82\xbd\x93\xfa\x96{\x8c'
+            b'\xea\x82\xcc\x83t\x83@\x83C\x83\x8b\x82\xc5\x82\xb7\x81B'
+            b'
') + unicode_html = shift_jis_html.decode("shift-jis") + soup = self.soup(unicode_html) + + # Make sure the parse tree is correctly encoded to various + # encodings. + self.assertEqual(soup.encode("utf-8"), unicode_html.encode("utf-8")) + self.assertEqual(soup.encode("euc_jp"), unicode_html.encode("euc_jp")) + + def test_real_hebrew_document(self): + # A real-world test to make sure we can convert ISO-8859-9 (a + # Hebrew encoding) to UTF-8. + hebrew_document = b'Hebrew (ISO 8859-8) in Visual Directionality

Hebrew (ISO 8859-8) in Visual Directionality

\xed\xe5\xec\xf9' + soup = self.soup( + hebrew_document, from_encoding="iso8859-8") + # Some tree builders call it iso8859-8, others call it iso-8859-9. + # That's not a difference we really care about. + assert soup.original_encoding in ('iso8859-8', 'iso-8859-8') + self.assertEqual( + soup.encode('utf-8'), + hebrew_document.decode("iso8859-8").encode("utf-8")) + + def test_meta_tag_reflects_current_encoding(self): + # Here's the tag saying that a document is + # encoded in Shift-JIS. + meta_tag = ('') + + # Here's a document incorporating that meta tag. + shift_jis_html = ( + '\n%s\n' + '' + 'Shift-JIS markup goes here.') % meta_tag + soup = self.soup(shift_jis_html) + + # Parse the document, and the charset is seemingly unaffected. + parsed_meta = soup.find('meta', {'http-equiv': 'Content-type'}) + content = parsed_meta['content'] + self.assertEqual('text/html; charset=x-sjis', content) + + # But that value is actually a ContentMetaAttributeValue object. + self.assertTrue(isinstance(content, ContentMetaAttributeValue)) + + # And it will take on a value that reflects its current + # encoding. + self.assertEqual('text/html; charset=utf8', content.encode("utf8")) + + # For the rest of the story, see TestSubstitutions in + # test_tree.py. + + def test_html5_style_meta_tag_reflects_current_encoding(self): + # Here's the tag saying that a document is + # encoded in Shift-JIS. + meta_tag = ('') + + # Here's a document incorporating that meta tag. + shift_jis_html = ( + '\n%s\n' + '' + 'Shift-JIS markup goes here.') % meta_tag + soup = self.soup(shift_jis_html) + + # Parse the document, and the charset is seemingly unaffected. + parsed_meta = soup.find('meta', id="encoding") + charset = parsed_meta['charset'] + self.assertEqual('x-sjis', charset) + + # But that value is actually a CharsetMetaAttributeValue object. + self.assertTrue(isinstance(charset, CharsetMetaAttributeValue)) + + # And it will take on a value that reflects its current + # encoding. + self.assertEqual('utf8', charset.encode("utf8")) + + def test_python_specific_encodings_not_used_in_charset(self): + # You can encode an HTML document using a Python-specific + # encoding, but that encoding won't be mentioned _inside_ the + # resulting document. Instead, the document will appear to + # have no encoding. + for markup in [ + b'' + b'' + ]: + soup = self.soup(markup) + for encoding in PYTHON_SPECIFIC_ENCODINGS: + if encoding in ( + 'idna', 'mbcs', 'oem', 'undefined', + 'string_escape', 'string-escape' + ): + # For one reason or another, these will raise an + # exception if we actually try to use them, so don't + # bother. + continue + encoded = soup.encode(encoding) + assert b'meta charset=""' in encoded + assert encoding.encode("ascii") not in encoded + + def test_tag_with_no_attributes_can_have_attributes_added(self): + data = self.soup("text") + data.a['foo'] = 'bar' + self.assertEqual('text', data.a.decode()) + + def test_closing_tag_with_no_opening_tag(self): + # Without BeautifulSoup.open_tag_counter, the tag will + # cause _popToTag to be called over and over again as we look + # for a tag that wasn't there. The result is that 'text2' + # will show up outside the body of the document. + soup = self.soup("

text1

text2
") + self.assertEqual( + "

text1

text2
", soup.body.decode() + ) + + def test_worst_case(self): + """Test the worst case (currently) for linking issues.""" + + soup = self.soup(BAD_DOCUMENT) + self.linkage_validator(soup) + + +class XMLTreeBuilderSmokeTest(object): + + def test_pickle_and_unpickle_identity(self): + # Pickling a tree, then unpickling it, yields a tree identical + # to the original. + tree = self.soup("foo") + dumped = pickle.dumps(tree, 2) + loaded = pickle.loads(dumped) + self.assertEqual(loaded.__class__, BeautifulSoup) + self.assertEqual(loaded.decode(), tree.decode()) + + def test_docstring_generated(self): + soup = self.soup("") + self.assertEqual( + soup.encode(), b'\n') + + def test_xml_declaration(self): + markup = b"""\n""" + soup = self.soup(markup) + self.assertEqual(markup, soup.encode("utf8")) + + def test_python_specific_encodings_not_used_in_xml_declaration(self): + # You can encode an XML document using a Python-specific + # encoding, but that encoding won't be mentioned _inside_ the + # resulting document. + markup = b"""\n""" + soup = self.soup(markup) + for encoding in PYTHON_SPECIFIC_ENCODINGS: + if encoding in ( + 'idna', 'mbcs', 'oem', 'undefined', + 'string_escape', 'string-escape' + ): + # For one reason or another, these will raise an + # exception if we actually try to use them, so don't + # bother. + continue + encoded = soup.encode(encoding) + assert b'' in encoded + assert encoding.encode("ascii") not in encoded + + def test_processing_instruction(self): + markup = b"""\n""" + soup = self.soup(markup) + self.assertEqual(markup, soup.encode("utf8")) + + def test_real_xhtml_document(self): + """A real XHTML document should come out *exactly* the same as it went in.""" + markup = b""" + + +Hello. +Goodbye. +""" + soup = self.soup(markup) + self.assertEqual( + soup.encode("utf-8"), markup) + + def test_nested_namespaces(self): + doc = b""" + + + + + +""" + soup = self.soup(doc) + self.assertEqual(doc, soup.encode()) + + def test_formatter_processes_script_tag_for_xml_documents(self): + doc = """ + +""" + soup = BeautifulSoup(doc, "lxml-xml") + # lxml would have stripped this while parsing, but we can add + # it later. + soup.script.string = 'console.log("< < hey > > ");' + encoded = soup.encode() + self.assertTrue(b"< < hey > >" in encoded) + + def test_can_parse_unicode_document(self): + markup = 'Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!' + soup = self.soup(markup) + self.assertEqual('Sacr\xe9 bleu!', soup.root.string) + + def test_popping_namespaced_tag(self): + markup = 'b2012-07-02T20:33:42Zcd' + soup = self.soup(markup) + self.assertEqual( + str(soup.rss), markup) + + def test_docstring_includes_correct_encoding(self): + soup = self.soup("") + self.assertEqual( + soup.encode("latin1"), + b'\n') + + def test_large_xml_document(self): + """A large XML document should come out the same as it went in.""" + markup = (b'\n' + + b'0' * (2**12) + + b'') + soup = self.soup(markup) + self.assertEqual(soup.encode("utf-8"), markup) + + + def test_tags_are_empty_element_if_and_only_if_they_are_empty(self): + self.assertSoupEquals("

", "

") + self.assertSoupEquals("

foo

") + + def test_namespaces_are_preserved(self): + markup = 'This tag is in the a namespaceThis tag is in the b namespace' + soup = self.soup(markup) + root = soup.root + self.assertEqual("http://example.com/", root['xmlns:a']) + self.assertEqual("http://example.net/", root['xmlns:b']) + + def test_closing_namespaced_tag(self): + markup = '

20010504

' + soup = self.soup(markup) + self.assertEqual(str(soup.p), markup) + + def test_namespaced_attributes(self): + markup = '' + soup = self.soup(markup) + self.assertEqual(str(soup.foo), markup) + + def test_namespaced_attributes_xml_namespace(self): + markup = 'bar' + soup = self.soup(markup) + self.assertEqual(str(soup.foo), markup) + + def test_find_by_prefixed_name(self): + doc = """ +foo + bar + baz + +""" + soup = self.soup(doc) + + # There are three tags. + self.assertEqual(3, len(soup.find_all('tag'))) + + # But two of them are ns1:tag and one of them is ns2:tag. + self.assertEqual(2, len(soup.find_all('ns1:tag'))) + self.assertEqual(1, len(soup.find_all('ns2:tag'))) + + self.assertEqual(1, len(soup.find_all('ns2:tag', key='value'))) + self.assertEqual(3, len(soup.find_all(['ns1:tag', 'ns2:tag']))) + + def test_copy_tag_preserves_namespace(self): + xml = """ +""" + + soup = self.soup(xml) + tag = soup.document + duplicate = copy.copy(tag) + + # The two tags have the same namespace prefix. + self.assertEqual(tag.prefix, duplicate.prefix) + + def test_worst_case(self): + """Test the worst case (currently) for linking issues.""" + + soup = self.soup(BAD_DOCUMENT) + self.linkage_validator(soup) + + +class HTML5TreeBuilderSmokeTest(HTMLTreeBuilderSmokeTest): + """Smoke test for a tree builder that supports HTML5.""" + + def test_real_xhtml_document(self): + # Since XHTML is not HTML5, HTML5 parsers are not tested to handle + # XHTML documents in any particular way. + pass + + def test_html_tags_have_namespace(self): + markup = "" + soup = self.soup(markup) + self.assertEqual("http://www.w3.org/1999/xhtml", soup.a.namespace) + + def test_svg_tags_have_namespace(self): + markup = '' + soup = self.soup(markup) + namespace = "http://www.w3.org/2000/svg" + self.assertEqual(namespace, soup.svg.namespace) + self.assertEqual(namespace, soup.circle.namespace) + + + def test_mathml_tags_have_namespace(self): + markup = '5' + soup = self.soup(markup) + namespace = 'http://www.w3.org/1998/Math/MathML' + self.assertEqual(namespace, soup.math.namespace) + self.assertEqual(namespace, soup.msqrt.namespace) + + def test_xml_declaration_becomes_comment(self): + markup = '' + soup = self.soup(markup) + self.assertTrue(isinstance(soup.contents[0], Comment)) + self.assertEqual(soup.contents[0], '?xml version="1.0" encoding="utf-8"?') + self.assertEqual("html", soup.contents[0].next_element.name) + +def skipIf(condition, reason): + def nothing(test, *args, **kwargs): + return None + + def decorator(test_item): + if condition: + return nothing + else: + return test_item + + return decorator diff --git a/venv/lib/python3.11/site-packages/bs4/tests/__init__.py b/venv/lib/python3.11/site-packages/bs4/tests/__init__.py new file mode 100644 index 00000000..142c8cc3 --- /dev/null +++ b/venv/lib/python3.11/site-packages/bs4/tests/__init__.py @@ -0,0 +1 @@ +"The beautifulsoup tests." diff --git a/venv/lib/python3.11/site-packages/bs4/tests/test_builder_registry.py b/venv/lib/python3.11/site-packages/bs4/tests/test_builder_registry.py new file mode 100644 index 00000000..90cad829 --- /dev/null +++ b/venv/lib/python3.11/site-packages/bs4/tests/test_builder_registry.py @@ -0,0 +1,147 @@ +"""Tests of the builder registry.""" + +import unittest +import warnings + +from bs4 import BeautifulSoup +from bs4.builder import ( + builder_registry as registry, + HTMLParserTreeBuilder, + TreeBuilderRegistry, +) + +try: + from bs4.builder import HTML5TreeBuilder + HTML5LIB_PRESENT = True +except ImportError: + HTML5LIB_PRESENT = False + +try: + from bs4.builder import ( + LXMLTreeBuilderForXML, + LXMLTreeBuilder, + ) + LXML_PRESENT = True +except ImportError: + LXML_PRESENT = False + + +class BuiltInRegistryTest(unittest.TestCase): + """Test the built-in registry with the default builders registered.""" + + def test_combination(self): + if LXML_PRESENT: + self.assertEqual(registry.lookup('fast', 'html'), + LXMLTreeBuilder) + + if LXML_PRESENT: + self.assertEqual(registry.lookup('permissive', 'xml'), + LXMLTreeBuilderForXML) + self.assertEqual(registry.lookup('strict', 'html'), + HTMLParserTreeBuilder) + if HTML5LIB_PRESENT: + self.assertEqual(registry.lookup('html5lib', 'html'), + HTML5TreeBuilder) + + def test_lookup_by_markup_type(self): + if LXML_PRESENT: + self.assertEqual(registry.lookup('html'), LXMLTreeBuilder) + self.assertEqual(registry.lookup('xml'), LXMLTreeBuilderForXML) + else: + self.assertEqual(registry.lookup('xml'), None) + if HTML5LIB_PRESENT: + self.assertEqual(registry.lookup('html'), HTML5TreeBuilder) + else: + self.assertEqual(registry.lookup('html'), HTMLParserTreeBuilder) + + def test_named_library(self): + if LXML_PRESENT: + self.assertEqual(registry.lookup('lxml', 'xml'), + LXMLTreeBuilderForXML) + self.assertEqual(registry.lookup('lxml', 'html'), + LXMLTreeBuilder) + if HTML5LIB_PRESENT: + self.assertEqual(registry.lookup('html5lib'), + HTML5TreeBuilder) + + self.assertEqual(registry.lookup('html.parser'), + HTMLParserTreeBuilder) + + def test_beautifulsoup_constructor_does_lookup(self): + + with warnings.catch_warnings(record=True) as w: + # This will create a warning about not explicitly + # specifying a parser, but we'll ignore it. + + # You can pass in a string. + BeautifulSoup("", features="html") + # Or a list of strings. + BeautifulSoup("", features=["html", "fast"]) + + # You'll get an exception if BS can't find an appropriate + # builder. + self.assertRaises(ValueError, BeautifulSoup, + "", features="no-such-feature") + +class RegistryTest(unittest.TestCase): + """Test the TreeBuilderRegistry class in general.""" + + def setUp(self): + self.registry = TreeBuilderRegistry() + + def builder_for_features(self, *feature_list): + cls = type('Builder_' + '_'.join(feature_list), + (object,), {'features' : feature_list}) + + self.registry.register(cls) + return cls + + def test_register_with_no_features(self): + builder = self.builder_for_features() + + # Since the builder advertises no features, you can't find it + # by looking up features. + self.assertEqual(self.registry.lookup('foo'), None) + + # But you can find it by doing a lookup with no features, if + # this happens to be the only registered builder. + self.assertEqual(self.registry.lookup(), builder) + + def test_register_with_features_makes_lookup_succeed(self): + builder = self.builder_for_features('foo', 'bar') + self.assertEqual(self.registry.lookup('foo'), builder) + self.assertEqual(self.registry.lookup('bar'), builder) + + def test_lookup_fails_when_no_builder_implements_feature(self): + builder = self.builder_for_features('foo', 'bar') + self.assertEqual(self.registry.lookup('baz'), None) + + def test_lookup_gets_most_recent_registration_when_no_feature_specified(self): + builder1 = self.builder_for_features('foo') + builder2 = self.builder_for_features('bar') + self.assertEqual(self.registry.lookup(), builder2) + + def test_lookup_fails_when_no_tree_builders_registered(self): + self.assertEqual(self.registry.lookup(), None) + + def test_lookup_gets_most_recent_builder_supporting_all_features(self): + has_one = self.builder_for_features('foo') + has_the_other = self.builder_for_features('bar') + has_both_early = self.builder_for_features('foo', 'bar', 'baz') + has_both_late = self.builder_for_features('foo', 'bar', 'quux') + lacks_one = self.builder_for_features('bar') + has_the_other = self.builder_for_features('foo') + + # There are two builders featuring 'foo' and 'bar', but + # the one that also features 'quux' was registered later. + self.assertEqual(self.registry.lookup('foo', 'bar'), + has_both_late) + + # There is only one builder featuring 'foo', 'bar', and 'baz'. + self.assertEqual(self.registry.lookup('foo', 'bar', 'baz'), + has_both_early) + + def test_lookup_fails_when_cannot_reconcile_requested_features(self): + builder1 = self.builder_for_features('foo', 'bar') + builder2 = self.builder_for_features('foo', 'baz') + self.assertEqual(self.registry.lookup('bar', 'baz'), None) diff --git a/venv/lib/python3.11/site-packages/bs4/tests/test_docs.py b/venv/lib/python3.11/site-packages/bs4/tests/test_docs.py new file mode 100644 index 00000000..5b9f6770 --- /dev/null +++ b/venv/lib/python3.11/site-packages/bs4/tests/test_docs.py @@ -0,0 +1,36 @@ +"Test harness for doctests." + +# pylint: disable-msg=E0611,W0142 + +__metaclass__ = type +__all__ = [ + 'additional_tests', + ] + +import atexit +import doctest +import os +#from pkg_resources import ( +# resource_filename, resource_exists, resource_listdir, cleanup_resources) +import unittest + +DOCTEST_FLAGS = ( + doctest.ELLIPSIS | + doctest.NORMALIZE_WHITESPACE | + doctest.REPORT_NDIFF) + + +# def additional_tests(): +# "Run the doc tests (README.txt and docs/*, if any exist)" +# doctest_files = [ +# os.path.abspath(resource_filename('bs4', 'README.txt'))] +# if resource_exists('bs4', 'docs'): +# for name in resource_listdir('bs4', 'docs'): +# if name.endswith('.txt'): +# doctest_files.append( +# os.path.abspath( +# resource_filename('bs4', 'docs/%s' % name))) +# kwargs = dict(module_relative=False, optionflags=DOCTEST_FLAGS) +# atexit.register(cleanup_resources) +# return unittest.TestSuite(( +# doctest.DocFileSuite(*doctest_files, **kwargs))) diff --git a/venv/lib/python3.11/site-packages/bs4/tests/test_html5lib.py b/venv/lib/python3.11/site-packages/bs4/tests/test_html5lib.py new file mode 100644 index 00000000..b77659b1 --- /dev/null +++ b/venv/lib/python3.11/site-packages/bs4/tests/test_html5lib.py @@ -0,0 +1,190 @@ +"""Tests to ensure that the html5lib tree builder generates good trees.""" + +import warnings + +try: + from bs4.builder import HTML5TreeBuilder + HTML5LIB_PRESENT = True +except ImportError as e: + HTML5LIB_PRESENT = False +from bs4.element import SoupStrainer +from bs4.testing import ( + HTML5TreeBuilderSmokeTest, + SoupTest, + skipIf, +) + +@skipIf( + not HTML5LIB_PRESENT, + "html5lib seems not to be present, not testing its tree builder.") +class HTML5LibBuilderSmokeTest(SoupTest, HTML5TreeBuilderSmokeTest): + """See ``HTML5TreeBuilderSmokeTest``.""" + + @property + def default_builder(self): + return HTML5TreeBuilder + + def test_soupstrainer(self): + # The html5lib tree builder does not support SoupStrainers. + strainer = SoupStrainer("b") + markup = "

A bold statement.

" + with warnings.catch_warnings(record=True) as w: + soup = self.soup(markup, parse_only=strainer) + self.assertEqual( + soup.decode(), self.document_for(markup)) + + self.assertTrue( + "the html5lib tree builder doesn't support parse_only" in + str(w[0].message)) + + def test_correctly_nested_tables(self): + """html5lib inserts tags where other parsers don't.""" + markup = ('' + '' + "') + + self.assertSoupEquals( + markup, + '
Here's another table:" + '' + '' + '
foo
Here\'s another table:' + '
foo
' + '
') + + self.assertSoupEquals( + "" + "" + "
Foo
Bar
Baz
") + + def test_xml_declaration_followed_by_doctype(self): + markup = ''' + + + + + +

foo

+ +''' + soup = self.soup(markup) + # Verify that we can reach the

tag; this means the tree is connected. + self.assertEqual(b"

foo

", soup.p.encode()) + + def test_reparented_markup(self): + markup = '

foo

\n

bar

' + soup = self.soup(markup) + self.assertEqual("

foo

\n

bar

", soup.body.decode()) + self.assertEqual(2, len(soup.find_all('p'))) + + + def test_reparented_markup_ends_with_whitespace(self): + markup = '

foo

\n

bar

\n' + soup = self.soup(markup) + self.assertEqual("

foo

\n

bar

\n", soup.body.decode()) + self.assertEqual(2, len(soup.find_all('p'))) + + def test_reparented_markup_containing_identical_whitespace_nodes(self): + """Verify that we keep the two whitespace nodes in this + document distinct when reparenting the adjacent tags. + """ + markup = '
' + soup = self.soup(markup) + space1, space2 = soup.find_all(string=' ') + tbody1, tbody2 = soup.find_all('tbody') + assert space1.next_element is tbody1 + assert tbody2.next_element is space2 + + def test_reparented_markup_containing_children(self): + markup = '' + soup = self.soup(markup) + noscript = soup.noscript + self.assertEqual("target", noscript.next_element) + target = soup.find(string='target') + + # The 'aftermath' string was duplicated; we want the second one. + final_aftermath = soup.find_all(string='aftermath')[-1] + + # The