From 5e1a0d07421446b9bbc02125d5907bcbf2ab3e20 Mon Sep 17 00:00:00 2001 From: Kristofers Solo <67258855+Kristofers-Solo@users.noreply.github.com> Date: Wed, 24 Nov 2021 16:34:37 +0200 Subject: [PATCH] Delete IKEA_scraper/.venv directory --- IKEA_scraper/.venv/bin/Activate.ps1 | 241 - .../bin/__pycache__/bottle.cpython-39.pyc | Bin 145438 -> 0 bytes IKEA_scraper/.venv/bin/activate | 66 - IKEA_scraper/.venv/bin/activate.csh | 25 - IKEA_scraper/.venv/bin/activate.fish | 64 - IKEA_scraper/.venv/bin/bottle.py | 3771 ------ IKEA_scraper/.venv/bin/futurize | 33 - IKEA_scraper/.venv/bin/normalizer | 8 - IKEA_scraper/.venv/bin/pasteurize | 33 - IKEA_scraper/.venv/bin/pip | 8 - IKEA_scraper/.venv/bin/pip3 | 8 - IKEA_scraper/.venv/bin/pip3.9 | 8 - IKEA_scraper/.venv/bin/python | 1 - IKEA_scraper/.venv/bin/python3 | 1 - IKEA_scraper/.venv/bin/python3.9 | 1 - IKEA_scraper/.venv/bin/yapf | 8 - IKEA_scraper/.venv/bin/yapf-diff | 8 - .../site/python3.9/greenlet/greenlet.h | 146 - .../Eel-0.14.0-py3.9.egg-info/PKG-INFO | 383 - .../Eel-0.14.0-py3.9.egg-info/SOURCES.txt | 16 - .../dependency_links.txt | 1 - .../installed-files.txt | 18 - .../Eel-0.14.0-py3.9.egg-info/requires.txt | 8 - .../Eel-0.14.0-py3.9.egg-info/top_level.txt | 1 - .../__pycache__/bottle.cpython-39.pyc | Bin 145425 -> 0 bytes .../__pycache__/pyparsing.cpython-39.pyc | Bin 240403 -> 0 bytes .../__pycache__/whichcraft.cpython-39.pyc | Bin 1949 -> 0 bytes .../site-packages/_distutils_hack/__init__.py | 128 - .../__pycache__/__init__.cpython-39.pyc | Bin 5116 -> 0 bytes .../__pycache__/override.cpython-39.pyc | Bin 247 -> 0 bytes .../site-packages/_distutils_hack/override.py | 1 - .../beautifulsoup4-4.10.0.dist-info/AUTHORS | 49 - .../COPYING.txt | 27 - .../beautifulsoup4-4.10.0.dist-info/INSTALLER | 1 - .../beautifulsoup4-4.10.0.dist-info/LICENSE | 30 - .../beautifulsoup4-4.10.0.dist-info/METADATA | 120 - .../beautifulsoup4-4.10.0.dist-info/RECORD | 28 - .../beautifulsoup4-4.10.0.dist-info/WHEEL | 5 - .../top_level.txt | 1 - .../bottle-0.12.19.dist-info/AUTHORS | 64 - .../bottle-0.12.19.dist-info/INSTALLER | 1 - .../bottle-0.12.19.dist-info/LICENSE | 19 - .../bottle-0.12.19.dist-info/METADATA | 43 - .../bottle-0.12.19.dist-info/RECORD | 11 - .../bottle-0.12.19.dist-info/WHEEL | 5 - .../bottle-0.12.19.dist-info/top_level.txt | 1 - .../lib/python3.9/site-packages/bottle.py | 3771 ------ .../PKG-INFO | 20 - .../SOURCES.txt | 10 - .../dependency_links.txt | 1 - .../installed-files.txt | 11 - .../requires.txt | 2 - .../top_level.txt | 1 - .../bottle_websocket/__init__.py | 5 - .../__pycache__/__init__.cpython-39.pyc | Bin 344 -> 0 bytes .../__pycache__/plugin.cpython-39.pyc | Bin 548 -> 0 bytes .../__pycache__/server.cpython-39.pyc | Bin 972 -> 0 bytes .../site-packages/bottle_websocket/plugin.py | 7 - .../site-packages/bottle_websocket/server.py | 17 - .../bs4-0.0.1-py3.9.egg-info/PKG-INFO | 23 - .../bs4-0.0.1-py3.9.egg-info/SOURCES.txt | 7 - .../dependency_links.txt | 1 - .../installed-files.txt | 5 - .../bs4-0.0.1-py3.9.egg-info/requires.txt | 1 - .../bs4-0.0.1-py3.9.egg-info/top_level.txt | 1 - .../python3.9/site-packages/bs4/__init__.py | 804 -- .../bs4/__pycache__/__init__.cpython-39.pyc | Bin 23198 -> 0 bytes .../bs4/__pycache__/dammit.cpython-39.pyc | Bin 71384 -> 0 bytes .../bs4/__pycache__/diagnose.cpython-39.pyc | Bin 8475 -> 0 bytes .../bs4/__pycache__/element.cpython-39.pyc | Bin 65446 -> 0 bytes .../bs4/__pycache__/formatter.cpython-39.pyc | Bin 6188 -> 0 bytes .../bs4/__pycache__/testing.cpython-39.pyc | Bin 44353 -> 0 bytes .../site-packages/bs4/builder/__init__.py | 520 - .../__pycache__/__init__.cpython-39.pyc | Bin 15420 -> 0 bytes .../__pycache__/_html5lib.cpython-39.pyc | Bin 12510 -> 0 bytes .../__pycache__/_htmlparser.cpython-39.pyc | Bin 13083 -> 0 bytes .../builder/__pycache__/_lxml.cpython-39.pyc | Bin 9515 -> 0 bytes .../site-packages/bs4/builder/_html5lib.py | 467 - .../site-packages/bs4/builder/_htmlparser.py | 492 - .../site-packages/bs4/builder/_lxml.py | 342 - .../lib/python3.9/site-packages/bs4/dammit.py | 3338 ----- .../python3.9/site-packages/bs4/diagnose.py | 242 - .../python3.9/site-packages/bs4/element.py | 2255 ---- .../python3.9/site-packages/bs4/formatter.py | 165 - .../python3.9/site-packages/bs4/testing.py | 1136 -- .../certifi-2021.10.8.dist-info/INSTALLER | 1 - .../certifi-2021.10.8.dist-info/LICENSE | 21 - .../certifi-2021.10.8.dist-info/METADATA | 83 - .../certifi-2021.10.8.dist-info/RECORD | 13 - .../certifi-2021.10.8.dist-info/WHEEL | 6 - .../certifi-2021.10.8.dist-info/top_level.txt | 1 - .../site-packages/certifi/__init__.py | 3 - .../site-packages/certifi/__main__.py | 12 - .../__pycache__/__init__.cpython-39.pyc | Bin 272 -> 0 bytes .../__pycache__/__main__.cpython-39.pyc | Bin 437 -> 0 bytes .../certifi/__pycache__/core.cpython-39.pyc | Bin 1163 -> 0 bytes .../site-packages/certifi/cacert.pem | 4362 ------- .../python3.9/site-packages/certifi/core.py | 60 - .../INSTALLER | 1 - .../LICENSE | 21 - .../METADATA | 267 - .../charset_normalizer-2.0.7.dist-info/RECORD | 33 - .../charset_normalizer-2.0.7.dist-info/WHEEL | 5 - .../entry_points.txt | 3 - .../top_level.txt | 1 - .../charset_normalizer/__init__.py | 47 - .../__pycache__/__init__.cpython-39.pyc | Bin 1579 -> 0 bytes .../__pycache__/api.cpython-39.pyc | Bin 9907 -> 0 bytes .../__pycache__/cd.cpython-39.pyc | Bin 8258 -> 0 bytes .../__pycache__/constant.cpython-39.pyc | Bin 13723 -> 0 bytes .../__pycache__/legacy.cpython-39.pyc | Bin 3049 -> 0 bytes .../__pycache__/md.cpython-39.pyc | Bin 14435 -> 0 bytes .../__pycache__/models.cpython-39.pyc | Bin 13197 -> 0 bytes .../__pycache__/utils.cpython-39.pyc | Bin 7438 -> 0 bytes .../__pycache__/version.cpython-39.pyc | Bin 283 -> 0 bytes .../site-packages/charset_normalizer/api.py | 528 - .../charset_normalizer/assets/__init__.py | 1244 -- .../__pycache__/__init__.cpython-39.pyc | Bin 7368 -> 0 bytes .../site-packages/charset_normalizer/cd.py | 341 - .../charset_normalizer/cli/__init__.py | 0 .../cli/__pycache__/__init__.cpython-39.pyc | Bin 203 -> 0 bytes .../cli/__pycache__/normalizer.cpython-39.pyc | Bin 6151 -> 0 bytes .../charset_normalizer/cli/normalizer.py | 291 - .../charset_normalizer/constant.py | 496 - .../charset_normalizer/legacy.py | 95 - .../site-packages/charset_normalizer/md.py | 540 - .../charset_normalizer/models.py | 393 - .../site-packages/charset_normalizer/py.typed | 0 .../site-packages/charset_normalizer/utils.py | 333 - .../charset_normalizer/version.py | 6 - .../site-packages/distutils-precedence.pth | 1 - .../python3.9/site-packages/eel/__init__.py | 389 - .../python3.9/site-packages/eel/__main__.py | 36 - .../eel/__pycache__/__init__.cpython-39.pyc | Bin 10179 -> 0 bytes .../eel/__pycache__/__main__.cpython-39.pyc | Bin 1226 -> 0 bytes .../eel/__pycache__/browsers.cpython-39.pyc | Bin 1925 -> 0 bytes .../eel/__pycache__/chrome.cpython-39.pyc | Bin 2681 -> 0 bytes .../eel/__pycache__/edge.cpython-39.pyc | Bin 676 -> 0 bytes .../eel/__pycache__/electron.cpython-39.pyc | Bin 862 -> 0 bytes .../python3.9/site-packages/eel/browsers.py | 78 - .../lib/python3.9/site-packages/eel/chrome.py | 84 - .../lib/python3.9/site-packages/eel/edge.py | 17 - .../lib/python3.9/site-packages/eel/eel.js | 173 - .../python3.9/site-packages/eel/electron.py | 24 - .../future-0.18.2-py3.9.egg-info/PKG-INFO | 110 - .../future-0.18.2-py3.9.egg-info/SOURCES.txt | 390 - .../dependency_links.txt | 1 - .../entry_points.txt | 4 - .../installed-files.txt | 413 - .../top_level.txt | 4 - .../site-packages/future/__init__.py | 93 - .../__pycache__/__init__.cpython-39.pyc | Bin 3157 -> 0 bytes .../future/backports/__init__.py | 26 - .../__pycache__/__init__.cpython-39.pyc | Bin 654 -> 0 bytes .../__pycache__/_markupbase.cpython-39.pyc | Bin 9481 -> 0 bytes .../__pycache__/datetime.cpython-39.pyc | Bin 49360 -> 0 bytes .../backports/__pycache__/misc.cpython-39.pyc | Bin 28790 -> 0 bytes .../__pycache__/socket.cpython-39.pyc | Bin 14297 -> 0 bytes .../__pycache__/socketserver.cpython-39.pyc | Bin 22306 -> 0 bytes .../__pycache__/total_ordering.cpython-39.pyc | Bin 2287 -> 0 bytes .../future/backports/_markupbase.py | 422 - .../future/backports/datetime.py | 2152 ---- .../future/backports/email/__init__.py | 78 - .../email/__pycache__/__init__.cpython-39.pyc | Bin 2060 -> 0 bytes .../__pycache__/_encoded_words.cpython-39.pyc | Bin 6181 -> 0 bytes .../_header_value_parser.cpython-39.pyc | Bin 80123 -> 0 bytes .../__pycache__/_parseaddr.cpython-39.pyc | Bin 12598 -> 0 bytes .../__pycache__/_policybase.cpython-39.pyc | Bin 14660 -> 0 bytes .../__pycache__/base64mime.cpython-39.pyc | Bin 3502 -> 0 bytes .../email/__pycache__/charset.cpython-39.pyc | Bin 11903 -> 0 bytes .../email/__pycache__/encoders.cpython-39.pyc | Bin 2193 -> 0 bytes .../email/__pycache__/errors.cpython-39.pyc | Bin 5954 -> 0 bytes .../__pycache__/feedparser.cpython-39.pyc | Bin 10704 -> 0 bytes .../__pycache__/generator.cpython-39.pyc | Bin 11790 -> 0 bytes .../email/__pycache__/header.cpython-39.pyc | Bin 17037 -> 0 bytes .../__pycache__/headerregistry.cpython-39.pyc | Bin 21215 -> 0 bytes .../__pycache__/iterators.cpython-39.pyc | Bin 2196 -> 0 bytes .../email/__pycache__/message.cpython-39.pyc | Bin 28651 -> 0 bytes .../email/__pycache__/parser.cpython-39.pyc | Bin 6103 -> 0 bytes .../email/__pycache__/policy.cpython-39.pyc | Bin 8378 -> 0 bytes .../__pycache__/quoprimime.cpython-39.pyc | Bin 9333 -> 0 bytes .../email/__pycache__/utils.cpython-39.pyc | Bin 10399 -> 0 bytes .../future/backports/email/_encoded_words.py | 232 - .../backports/email/_header_value_parser.py | 2965 ----- .../future/backports/email/_parseaddr.py | 546 - .../future/backports/email/_policybase.py | 365 - .../future/backports/email/base64mime.py | 120 - .../future/backports/email/charset.py | 409 - .../future/backports/email/encoders.py | 90 - .../future/backports/email/errors.py | 111 - .../future/backports/email/feedparser.py | 525 - .../future/backports/email/generator.py | 498 - .../future/backports/email/header.py | 581 - .../future/backports/email/headerregistry.py | 592 - .../future/backports/email/iterators.py | 74 - .../future/backports/email/message.py | 882 -- .../future/backports/email/mime/__init__.py | 0 .../mime/__pycache__/__init__.cpython-39.pyc | Bin 208 -> 0 bytes .../__pycache__/application.cpython-39.pyc | Bin 1651 -> 0 bytes .../mime/__pycache__/audio.cpython-39.pyc | Bin 2816 -> 0 bytes .../mime/__pycache__/base.cpython-39.pyc | Bin 1140 -> 0 bytes .../mime/__pycache__/image.cpython-39.pyc | Bin 2096 -> 0 bytes .../mime/__pycache__/message.cpython-39.pyc | Bin 1482 -> 0 bytes .../mime/__pycache__/multipart.cpython-39.pyc | Bin 1681 -> 0 bytes .../__pycache__/nonmultipart.cpython-39.pyc | Bin 992 -> 0 bytes .../mime/__pycache__/text.cpython-39.pyc | Bin 1508 -> 0 bytes .../backports/email/mime/application.py | 39 - .../future/backports/email/mime/audio.py | 74 - .../future/backports/email/mime/base.py | 25 - .../future/backports/email/mime/image.py | 48 - .../future/backports/email/mime/message.py | 36 - .../future/backports/email/mime/multipart.py | 49 - .../backports/email/mime/nonmultipart.py | 24 - .../future/backports/email/mime/text.py | 44 - .../future/backports/email/parser.py | 135 - .../future/backports/email/policy.py | 193 - .../future/backports/email/quoprimime.py | 326 - .../future/backports/email/utils.py | 400 - .../future/backports/html/__init__.py | 27 - .../html/__pycache__/__init__.cpython-39.pyc | Bin 1060 -> 0 bytes .../html/__pycache__/entities.cpython-39.pyc | Bin 50767 -> 0 bytes .../html/__pycache__/parser.cpython-39.pyc | Bin 13649 -> 0 bytes .../future/backports/html/entities.py | 2514 ---- .../future/backports/html/parser.py | 536 - .../future/backports/http/__init__.py | 0 .../http/__pycache__/__init__.cpython-39.pyc | Bin 202 -> 0 bytes .../http/__pycache__/client.cpython-39.pyc | Bin 30769 -> 0 bytes .../http/__pycache__/cookiejar.cpython-39.pyc | Bin 53872 -> 0 bytes .../http/__pycache__/cookies.cpython-39.pyc | Bin 16347 -> 0 bytes .../http/__pycache__/server.cpython-39.pyc | Bin 34465 -> 0 bytes .../future/backports/http/client.py | 1346 -- .../future/backports/http/cookiejar.py | 2110 ---- .../future/backports/http/cookies.py | 598 - .../future/backports/http/server.py | 1226 -- .../site-packages/future/backports/misc.py | 944 -- .../site-packages/future/backports/socket.py | 454 - .../future/backports/socketserver.py | 747 -- .../future/backports/test/__init__.py | 9 - .../test/__pycache__/__init__.cpython-39.pyc | Bin 474 -> 0 bytes .../test/__pycache__/pystone.cpython-39.pyc | Bin 6778 -> 0 bytes .../__pycache__/ssl_servers.cpython-39.pyc | Bin 7110 -> 0 bytes .../test/__pycache__/support.cpython-39.pyc | Bin 55892 -> 0 bytes .../future/backports/test/badcert.pem | 36 - .../future/backports/test/badkey.pem | 40 - .../future/backports/test/dh512.pem | 9 - .../test/https_svn_python_org_root.pem | 41 - .../future/backports/test/keycert.passwd.pem | 33 - .../future/backports/test/keycert.pem | 31 - .../future/backports/test/keycert2.pem | 31 - .../future/backports/test/nokia.pem | 31 - .../future/backports/test/nullbytecert.pem | 90 - .../future/backports/test/nullcert.pem | 0 .../future/backports/test/pystone.py | 272 - .../future/backports/test/sha256.pem | 128 - .../future/backports/test/ssl_cert.pem | 15 - .../future/backports/test/ssl_key.passwd.pem | 18 - .../future/backports/test/ssl_key.pem | 16 - .../future/backports/test/ssl_servers.py | 207 - .../future/backports/test/support.py | 2048 --- .../future/backports/total_ordering.py | 38 - .../future/backports/urllib/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 204 -> 0 bytes .../urllib/__pycache__/error.cpython-39.pyc | Bin 2700 -> 0 bytes .../urllib/__pycache__/parse.cpython-39.pyc | Bin 28887 -> 0 bytes .../urllib/__pycache__/request.cpython-39.pyc | Bin 69770 -> 0 bytes .../__pycache__/response.cpython-39.pyc | Bin 3943 -> 0 bytes .../__pycache__/robotparser.cpython-39.pyc | Bin 6144 -> 0 bytes .../future/backports/urllib/error.py | 75 - .../future/backports/urllib/parse.py | 991 -- .../future/backports/urllib/request.py | 2647 ---- .../future/backports/urllib/response.py | 103 - .../future/backports/urllib/robotparser.py | 211 - .../future/backports/xmlrpc/__init__.py | 1 - .../__pycache__/__init__.cpython-39.pyc | Bin 204 -> 0 bytes .../xmlrpc/__pycache__/client.cpython-39.pyc | Bin 33767 -> 0 bytes .../xmlrpc/__pycache__/server.cpython-39.pyc | Bin 29902 -> 0 bytes .../future/backports/xmlrpc/client.py | 1496 --- .../future/backports/xmlrpc/server.py | 999 -- .../site-packages/future/builtins/__init__.py | 51 - .../__pycache__/__init__.cpython-39.pyc | Bin 1264 -> 0 bytes .../__pycache__/disabled.cpython-39.pyc | Bin 2360 -> 0 bytes .../__pycache__/iterators.cpython-39.pyc | Bin 1537 -> 0 bytes .../builtins/__pycache__/misc.cpython-39.pyc | Bin 3088 -> 0 bytes .../__pycache__/new_min_max.cpython-39.pyc | Bin 1601 -> 0 bytes .../__pycache__/newnext.cpython-39.pyc | Bin 2055 -> 0 bytes .../__pycache__/newround.cpython-39.pyc | Bin 2838 -> 0 bytes .../__pycache__/newsuper.cpython-39.pyc | Bin 2872 -> 0 bytes .../site-packages/future/builtins/disabled.py | 66 - .../future/builtins/iterators.py | 52 - .../site-packages/future/builtins/misc.py | 135 - .../future/builtins/new_min_max.py | 59 - .../site-packages/future/builtins/newnext.py | 70 - .../site-packages/future/builtins/newround.py | 102 - .../site-packages/future/builtins/newsuper.py | 114 - .../site-packages/future/moves/__init__.py | 8 - .../moves/__pycache__/__init__.cpython-39.pyc | Bin 399 -> 0 bytes .../__pycache__/_dummy_thread.cpython-39.pyc | Bin 376 -> 0 bytes .../__pycache__/_markupbase.cpython-39.pyc | Bin 370 -> 0 bytes .../moves/__pycache__/_thread.cpython-39.pyc | Bin 358 -> 0 bytes .../moves/__pycache__/builtins.cpython-39.pyc | Bin 392 -> 0 bytes .../__pycache__/collections.cpython-39.pyc | Bin 647 -> 0 bytes .../__pycache__/configparser.cpython-39.pyc | Bin 348 -> 0 bytes .../moves/__pycache__/copyreg.cpython-39.pyc | Bin 416 -> 0 bytes .../__pycache__/itertools.cpython-39.pyc | Bin 377 -> 0 bytes .../moves/__pycache__/pickle.cpython-39.pyc | Bin 408 -> 0 bytes .../moves/__pycache__/queue.cpython-39.pyc | Bin 353 -> 0 bytes .../moves/__pycache__/reprlib.cpython-39.pyc | Bin 356 -> 0 bytes .../__pycache__/socketserver.cpython-39.pyc | Bin 374 -> 0 bytes .../__pycache__/subprocess.cpython-39.pyc | Bin 491 -> 0 bytes .../moves/__pycache__/sys.cpython-39.pyc | Bin 346 -> 0 bytes .../moves/__pycache__/winreg.cpython-39.pyc | Bin 357 -> 0 bytes .../future/moves/_dummy_thread.py | 8 - .../site-packages/future/moves/_markupbase.py | 8 - .../site-packages/future/moves/_thread.py | 8 - .../site-packages/future/moves/builtins.py | 10 - .../site-packages/future/moves/collections.py | 18 - .../future/moves/configparser.py | 8 - .../site-packages/future/moves/copyreg.py | 12 - .../future/moves/dbm/__init__.py | 20 - .../dbm/__pycache__/__init__.cpython-39.pyc | Bin 492 -> 0 bytes .../moves/dbm/__pycache__/dumb.cpython-39.pyc | Bin 361 -> 0 bytes .../moves/dbm/__pycache__/gnu.cpython-39.pyc | Bin 356 -> 0 bytes .../moves/dbm/__pycache__/ndbm.cpython-39.pyc | Bin 357 -> 0 bytes .../site-packages/future/moves/dbm/dumb.py | 9 - .../site-packages/future/moves/dbm/gnu.py | 9 - .../site-packages/future/moves/dbm/ndbm.py | 9 - .../future/moves/html/__init__.py | 31 - .../html/__pycache__/__init__.cpython-39.pyc | Bin 874 -> 0 bytes .../html/__pycache__/entities.cpython-39.pyc | Bin 378 -> 0 bytes .../html/__pycache__/parser.cpython-39.pyc | Bin 370 -> 0 bytes .../future/moves/html/entities.py | 8 - .../site-packages/future/moves/html/parser.py | 8 - .../future/moves/http/__init__.py | 4 - .../http/__pycache__/__init__.cpython-39.pyc | Bin 270 -> 0 bytes .../http/__pycache__/client.cpython-39.pyc | Bin 351 -> 0 bytes .../http/__pycache__/cookiejar.cpython-39.pyc | Bin 375 -> 0 bytes .../http/__pycache__/cookies.cpython-39.pyc | Bin 397 -> 0 bytes .../http/__pycache__/server.cpython-39.pyc | Bin 586 -> 0 bytes .../site-packages/future/moves/http/client.py | 8 - .../future/moves/http/cookiejar.py | 8 - .../future/moves/http/cookies.py | 9 - .../site-packages/future/moves/http/server.py | 20 - .../site-packages/future/moves/itertools.py | 8 - .../site-packages/future/moves/pickle.py | 11 - .../site-packages/future/moves/queue.py | 8 - .../site-packages/future/moves/reprlib.py | 8 - .../future/moves/socketserver.py | 8 - .../site-packages/future/moves/subprocess.py | 11 - .../site-packages/future/moves/sys.py | 8 - .../future/moves/test/__init__.py | 5 - .../test/__pycache__/__init__.cpython-39.pyc | Bin 320 -> 0 bytes .../test/__pycache__/support.cpython-39.pyc | Bin 480 -> 0 bytes .../future/moves/test/support.py | 10 - .../future/moves/tkinter/__init__.py | 27 - .../__pycache__/__init__.cpython-39.pyc | Bin 724 -> 0 bytes .../__pycache__/colorchooser.cpython-39.pyc | Bin 500 -> 0 bytes .../__pycache__/commondialog.cpython-39.pyc | Bin 500 -> 0 bytes .../__pycache__/constants.cpython-39.pyc | Bin 488 -> 0 bytes .../tkinter/__pycache__/dialog.cpython-39.pyc | Bin 472 -> 0 bytes .../tkinter/__pycache__/dnd.cpython-39.pyc | Bin 464 -> 0 bytes .../__pycache__/filedialog.cpython-39.pyc | Bin 488 -> 0 bytes .../tkinter/__pycache__/font.cpython-39.pyc | Bin 468 -> 0 bytes .../__pycache__/messagebox.cpython-39.pyc | Bin 492 -> 0 bytes .../__pycache__/scrolledtext.cpython-39.pyc | Bin 496 -> 0 bytes .../__pycache__/simpledialog.cpython-39.pyc | Bin 496 -> 0 bytes .../tkinter/__pycache__/tix.cpython-39.pyc | Bin 460 -> 0 bytes .../tkinter/__pycache__/ttk.cpython-39.pyc | Bin 460 -> 0 bytes .../future/moves/tkinter/colorchooser.py | 12 - .../future/moves/tkinter/commondialog.py | 12 - .../future/moves/tkinter/constants.py | 12 - .../future/moves/tkinter/dialog.py | 12 - .../site-packages/future/moves/tkinter/dnd.py | 12 - .../future/moves/tkinter/filedialog.py | 12 - .../future/moves/tkinter/font.py | 12 - .../future/moves/tkinter/messagebox.py | 12 - .../future/moves/tkinter/scrolledtext.py | 12 - .../future/moves/tkinter/simpledialog.py | 12 - .../site-packages/future/moves/tkinter/tix.py | 12 - .../site-packages/future/moves/tkinter/ttk.py | 12 - .../future/moves/urllib/__init__.py | 5 - .../__pycache__/__init__.cpython-39.pyc | Bin 322 -> 0 bytes .../urllib/__pycache__/error.cpython-39.pyc | Bin 562 -> 0 bytes .../urllib/__pycache__/parse.cpython-39.pyc | Bin 803 -> 0 bytes .../urllib/__pycache__/request.cpython-39.pyc | Bin 1099 -> 0 bytes .../__pycache__/response.cpython-39.pyc | Bin 510 -> 0 bytes .../__pycache__/robotparser.cpython-39.pyc | Bin 385 -> 0 bytes .../future/moves/urllib/error.py | 16 - .../future/moves/urllib/parse.py | 28 - .../future/moves/urllib/request.py | 94 - .../future/moves/urllib/response.py | 12 - .../future/moves/urllib/robotparser.py | 8 - .../site-packages/future/moves/winreg.py | 8 - .../future/moves/xmlrpc/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 200 -> 0 bytes .../xmlrpc/__pycache__/client.cpython-39.pyc | Bin 347 -> 0 bytes .../xmlrpc/__pycache__/server.cpython-39.pyc | Bin 347 -> 0 bytes .../future/moves/xmlrpc/client.py | 7 - .../future/moves/xmlrpc/server.py | 7 - .../future/standard_library/__init__.py | 815 -- .../__pycache__/__init__.cpython-39.pyc | Bin 18428 -> 0 bytes .../site-packages/future/tests/__init__.py | 0 .../tests/__pycache__/__init__.cpython-39.pyc | Bin 193 -> 0 bytes .../tests/__pycache__/base.cpython-39.pyc | Bin 16697 -> 0 bytes .../site-packages/future/tests/base.py | 539 - .../site-packages/future/types/__init__.py | 257 - .../types/__pycache__/__init__.cpython-39.pyc | Bin 6021 -> 0 bytes .../types/__pycache__/newbytes.cpython-39.pyc | Bin 14354 -> 0 bytes .../types/__pycache__/newdict.cpython-39.pyc | Bin 3627 -> 0 bytes .../types/__pycache__/newint.cpython-39.pyc | Bin 12582 -> 0 bytes .../types/__pycache__/newlist.cpython-39.pyc | Bin 3107 -> 0 bytes .../__pycache__/newmemoryview.cpython-39.pyc | Bin 1012 -> 0 bytes .../__pycache__/newobject.cpython-39.pyc | Bin 2679 -> 0 bytes .../types/__pycache__/newopen.cpython-39.pyc | Bin 1616 -> 0 bytes .../types/__pycache__/newrange.cpython-39.pyc | Bin 6144 -> 0 bytes .../types/__pycache__/newstr.cpython-39.pyc | Bin 14573 -> 0 bytes .../site-packages/future/types/newbytes.py | 460 - .../site-packages/future/types/newdict.py | 111 - .../site-packages/future/types/newint.py | 381 - .../site-packages/future/types/newlist.py | 95 - .../future/types/newmemoryview.py | 29 - .../site-packages/future/types/newobject.py | 117 - .../site-packages/future/types/newopen.py | 32 - .../site-packages/future/types/newrange.py | 170 - .../site-packages/future/types/newstr.py | 426 - .../site-packages/future/utils/__init__.py | 767 -- .../utils/__pycache__/__init__.cpython-39.pyc | Bin 20299 -> 0 bytes .../surrogateescape.cpython-39.pyc | Bin 3855 -> 0 bytes .../future/utils/surrogateescape.py | 198 - .../gevent-21.8.0.dist-info/AUTHORS | 62 - .../gevent-21.8.0.dist-info/INSTALLER | 1 - .../gevent-21.8.0.dist-info/LICENSE | 25 - .../gevent-21.8.0.dist-info/METADATA | 340 - .../gevent-21.8.0.dist-info/NOTICE | 94 - .../gevent-21.8.0.dist-info/RECORD | 548 - .../gevent-21.8.0.dist-info/WHEEL | 6 - .../gevent-21.8.0.dist-info/entry_points.txt | 3 - .../gevent-21.8.0.dist-info/top_level.txt | 1 - .../site-packages/gevent/__init__.py | 128 - .../__pycache__/__init__.cpython-39.pyc | Bin 2474 -> 0 bytes .../_abstract_linkable.cpython-39.pyc | Bin 10367 -> 0 bytes .../gevent/__pycache__/_compat.cpython-39.pyc | Bin 5324 -> 0 bytes .../gevent/__pycache__/_config.cpython-39.pyc | Bin 20441 -> 0 bytes .../_fileobjectcommon.cpython-39.pyc | Bin 18962 -> 0 bytes .../_fileobjectposix.cpython-39.pyc | Bin 10468 -> 0 bytes .../_greenlet_primitives.cpython-39.pyc | Bin 2912 -> 0 bytes .../__pycache__/_hub_local.cpython-39.pyc | Bin 2826 -> 0 bytes .../_hub_primitives.cpython-39.pyc | Bin 12063 -> 0 bytes .../gevent/__pycache__/_ident.cpython-39.pyc | Bin 2128 -> 0 bytes .../gevent/__pycache__/_imap.cpython-39.pyc | Bin 6266 -> 0 bytes .../__pycache__/_interfaces.cpython-39.pyc | Bin 11673 -> 0 bytes .../__pycache__/_monitor.cpython-39.pyc | Bin 8087 -> 0 bytes .../__pycache__/_patcher.cpython-39.pyc | Bin 7205 -> 0 bytes .../__pycache__/_semaphore.cpython-39.pyc | Bin 13072 -> 0 bytes .../__pycache__/_socket2.cpython-39.pyc | Bin 8057 -> 0 bytes .../__pycache__/_socket3.cpython-39.pyc | Bin 16279 -> 0 bytes .../__pycache__/_socketcommon.cpython-39.pyc | Bin 17003 -> 0 bytes .../gevent/__pycache__/_ssl2.cpython-39.pyc | Bin 11373 -> 0 bytes .../gevent/__pycache__/_ssl3.cpython-39.pyc | Bin 20386 -> 0 bytes .../__pycache__/_sslgte279.cpython-39.pyc | Bin 18713 -> 0 bytes .../gevent/__pycache__/_tblib.cpython-39.pyc | Bin 10074 -> 0 bytes .../__pycache__/_threading.cpython-39.pyc | Bin 4954 -> 0 bytes .../gevent/__pycache__/_tracer.cpython-39.pyc | Bin 4858 -> 0 bytes .../gevent/__pycache__/_util.cpython-39.pyc | Bin 9177 -> 0 bytes .../__pycache__/_util_py2.cpython-39.pyc | Bin 662 -> 0 bytes .../gevent/__pycache__/_waiter.cpython-39.pyc | Bin 6971 -> 0 bytes .../gevent/__pycache__/ares.cpython-39.pyc | Bin 425 -> 0 bytes .../__pycache__/backdoor.cpython-39.pyc | Bin 7841 -> 0 bytes .../__pycache__/baseserver.cpython-39.pyc | Bin 13092 -> 0 bytes .../__pycache__/builtins.cpython-39.pyc | Bin 2759 -> 0 bytes .../__pycache__/contextvars.cpython-39.pyc | Bin 9773 -> 0 bytes .../gevent/__pycache__/core.cpython-39.pyc | Bin 578 -> 0 bytes .../gevent/__pycache__/event.cpython-39.pyc | Bin 15272 -> 0 bytes .../gevent/__pycache__/events.cpython-39.pyc | Bin 15320 -> 0 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 4692 -> 0 bytes .../__pycache__/fileobject.cpython-39.pyc | Bin 2901 -> 0 bytes .../__pycache__/greenlet.cpython-39.pyc | Bin 33229 -> 0 bytes .../gevent/__pycache__/hub.cpython-39.pyc | Bin 23008 -> 0 bytes .../gevent/__pycache__/local.cpython-39.pyc | Bin 13001 -> 0 bytes .../gevent/__pycache__/lock.cpython-39.pyc | Bin 11112 -> 0 bytes .../gevent/__pycache__/monkey.cpython-39.pyc | Bin 35296 -> 0 bytes .../gevent/__pycache__/os.cpython-39.pyc | Bin 13970 -> 0 bytes .../gevent/__pycache__/pool.cpython-39.pyc | Bin 24127 -> 0 bytes .../gevent/__pycache__/pywsgi.cpython-39.pyc | Bin 39500 -> 0 bytes .../gevent/__pycache__/queue.cpython-39.pyc | Bin 21199 -> 0 bytes .../__pycache__/resolver_ares.cpython-39.pyc | Bin 622 -> 0 bytes .../resolver_thread.cpython-39.pyc | Bin 638 -> 0 bytes .../gevent/__pycache__/select.cpython-39.pyc | Bin 7682 -> 0 bytes .../__pycache__/selectors.cpython-39.pyc | Bin 5636 -> 0 bytes .../gevent/__pycache__/server.cpython-39.pyc | Bin 8627 -> 0 bytes .../gevent/__pycache__/signal.cpython-39.pyc | Bin 4092 -> 0 bytes .../gevent/__pycache__/socket.cpython-39.pyc | Bin 3226 -> 0 bytes .../gevent/__pycache__/ssl.cpython-39.pyc | Bin 934 -> 0 bytes .../__pycache__/subprocess.cpython-39.pyc | Bin 43452 -> 0 bytes .../gevent/__pycache__/thread.cpython-39.pyc | Bin 3195 -> 0 bytes .../__pycache__/threading.cpython-39.pyc | Bin 5678 -> 0 bytes .../__pycache__/threadpool.cpython-39.pyc | Bin 23182 -> 0 bytes .../gevent/__pycache__/time.cpython-39.pyc | Bin 582 -> 0 bytes .../gevent/__pycache__/timeout.cpython-39.pyc | Bin 11247 -> 0 bytes .../gevent/__pycache__/util.cpython-39.pyc | Bin 19534 -> 0 bytes .../__pycache__/win32util.cpython-39.pyc | Bin 2694 -> 0 bytes .../gevent/_abstract_linkable.py | 546 - .../python3.9/site-packages/gevent/_compat.py | 226 - .../python3.9/site-packages/gevent/_config.py | 701 -- .../site-packages/gevent/_ffi/__init__.py | 27 - .../_ffi/__pycache__/__init__.cpython-39.pyc | Bin 818 -> 0 bytes .../_ffi/__pycache__/callback.cpython-39.pyc | Bin 1648 -> 0 bytes .../_ffi/__pycache__/loop.cpython-39.pyc | Bin 19166 -> 0 bytes .../_ffi/__pycache__/watcher.cpython-39.pyc | Bin 19415 -> 0 bytes .../site-packages/gevent/_ffi/callback.py | 58 - .../site-packages/gevent/_ffi/loop.py | 794 -- .../site-packages/gevent/_ffi/watcher.py | 644 - .../site-packages/gevent/_fileobjectcommon.py | 702 -- .../site-packages/gevent/_fileobjectposix.py | 343 - ...ct_linkable.cpython-39-x86_64-linux-gnu.so | Bin 901816 -> 0 bytes ..._primitives.cpython-39-x86_64-linux-gnu.so | Bin 672256 -> 0 bytes ...c_hub_local.cpython-39-x86_64-linux-gnu.so | Bin 383016 -> 0 bytes ..._primitives.cpython-39-x86_64-linux-gnu.so | Bin 1124280 -> 0 bytes ...ent_c_ident.cpython-39-x86_64-linux-gnu.so | Bin 416336 -> 0 bytes ...vent_c_imap.cpython-39-x86_64-linux-gnu.so | Bin 733712 -> 0 bytes ...c_semaphore.cpython-39-x86_64-linux-gnu.so | Bin 980184 -> 0 bytes ...nt_c_tracer.cpython-39-x86_64-linux-gnu.so | Bin 776176 -> 0 bytes ...nt_c_waiter.cpython-39-x86_64-linux-gnu.so | Bin 601592 -> 0 bytes ...vent_cevent.cpython-39-x86_64-linux-gnu.so | Bin 809392 -> 0 bytes ...t_cgreenlet.cpython-39-x86_64-linux-gnu.so | Bin 2050248 -> 0 bytes ...vent_clocal.cpython-39-x86_64-linux-gnu.so | Bin 1039656 -> 0 bytes ...vent_cqueue.cpython-39-x86_64-linux-gnu.so | Bin 1662056 -> 0 bytes .../gevent/_greenlet_primitives.py | 132 - .../site-packages/gevent/_hub_local.py | 101 - .../site-packages/gevent/_hub_primitives.py | 427 - .../python3.9/site-packages/gevent/_ident.py | 82 - .../python3.9/site-packages/gevent/_imap.py | 226 - .../site-packages/gevent/_interfaces.py | 318 - .../site-packages/gevent/_monitor.py | 311 - .../site-packages/gevent/_patcher.py | 255 - .../site-packages/gevent/_semaphore.py | 529 - .../site-packages/gevent/_socket2.py | 336 - .../site-packages/gevent/_socket3.py | 620 - .../site-packages/gevent/_socketcommon.py | 755 -- .../python3.9/site-packages/gevent/_ssl2.py | 439 - .../python3.9/site-packages/gevent/_ssl3.py | 821 -- .../site-packages/gevent/_sslgte279.py | 738 -- .../python3.9/site-packages/gevent/_tblib.py | 476 - .../site-packages/gevent/_threading.py | 180 - .../python3.9/site-packages/gevent/_tracer.py | 182 - .../python3.9/site-packages/gevent/_util.py | 351 - .../site-packages/gevent/_util_py2.py | 23 - .../python3.9/site-packages/gevent/_waiter.py | 207 - .../python3.9/site-packages/gevent/ares.py | 10 - .../site-packages/gevent/backdoor.py | 263 - .../site-packages/gevent/baseserver.py | 440 - .../site-packages/gevent/builtins.py | 135 - .../site-packages/gevent/contextvars.py | 348 - .../python3.9/site-packages/gevent/core.py | 20 - .../python3.9/site-packages/gevent/event.py | 426 - .../python3.9/site-packages/gevent/events.py | 471 - .../site-packages/gevent/exceptions.py | 136 - .../site-packages/gevent/fileobject.py | 85 - .../site-packages/gevent/greenlet.py | 1186 -- .../lib/python3.9/site-packages/gevent/hub.py | 905 -- .../site-packages/gevent/libev/__init__.py | 7 - .../libev/__pycache__/__init__.cpython-39.pyc | Bin 326 -> 0 bytes .../_corecffi_build.cpython-39.pyc | Bin 1979 -> 0 bytes .../libev/__pycache__/corecffi.cpython-39.pyc | Bin 13590 -> 0 bytes .../libev/__pycache__/watcher.cpython-39.pyc | Bin 9079 -> 0 bytes .../gevent/libev/_corecffi.abi3.so | Bin 479784 -> 0 bytes .../gevent/libev/_corecffi_build.py | 125 - .../corecext.cpython-39-x86_64-linux-gnu.so | Bin 2495032 -> 0 bytes .../site-packages/gevent/libev/corecffi.py | 470 - .../site-packages/gevent/libev/watcher.py | 287 - .../site-packages/gevent/libuv/__init__.py | 7 - .../libuv/__pycache__/__init__.cpython-39.pyc | Bin 326 -> 0 bytes .../_corecffi_build.cpython-39.pyc | Bin 5692 -> 0 bytes .../libuv/__pycache__/loop.cpython-39.pyc | Bin 12887 -> 0 bytes .../libuv/__pycache__/watcher.cpython-39.pyc | Bin 19341 -> 0 bytes .../gevent/libuv/_corecffi.abi3.so | Bin 1305880 -> 0 bytes .../gevent/libuv/_corecffi_build.py | 318 - .../site-packages/gevent/libuv/loop.py | 689 - .../site-packages/gevent/libuv/watcher.py | 761 -- .../python3.9/site-packages/gevent/local.py | 624 - .../python3.9/site-packages/gevent/lock.py | 374 - .../python3.9/site-packages/gevent/monkey.py | 1384 --- .../lib/python3.9/site-packages/gevent/os.py | 543 - .../python3.9/site-packages/gevent/pool.py | 677 - .../python3.9/site-packages/gevent/pywsgi.py | 1604 --- .../python3.9/site-packages/gevent/queue.py | 698 -- .../site-packages/gevent/resolver/__init__.py | 277 - .../__pycache__/__init__.cpython-39.pyc | Bin 7126 -> 0 bytes .../__pycache__/_addresses.cpython-39.pyc | Bin 3005 -> 0 bytes .../__pycache__/_hostsfile.cpython-39.pyc | Bin 2890 -> 0 bytes .../resolver/__pycache__/ares.cpython-39.pyc | Bin 9443 -> 0 bytes .../__pycache__/blocking.cpython-39.pyc | Bin 1561 -> 0 bytes .../__pycache__/dnspython.cpython-39.pyc | Bin 11435 -> 0 bytes .../__pycache__/thread.cpython-39.pyc | Bin 2843 -> 0 bytes .../gevent/resolver/_addresses.py | 164 - .../gevent/resolver/_hostsfile.py | 145 - .../site-packages/gevent/resolver/ares.py | 341 - .../site-packages/gevent/resolver/blocking.py | 45 - .../cares.cpython-39-x86_64-linux-gnu.so | Bin 1921408 -> 0 bytes .../gevent/resolver/dnspython.py | 509 - .../site-packages/gevent/resolver/thread.py | 69 - .../site-packages/gevent/resolver_ares.py | 17 - .../site-packages/gevent/resolver_thread.py | 17 - .../python3.9/site-packages/gevent/select.py | 347 - .../site-packages/gevent/selectors.py | 307 - .../python3.9/site-packages/gevent/server.py | 314 - .../python3.9/site-packages/gevent/signal.py | 142 - .../python3.9/site-packages/gevent/socket.py | 134 - .../lib/python3.9/site-packages/gevent/ssl.py | 35 - .../site-packages/gevent/subprocess.py | 1995 --- .../site-packages/gevent/testing/__init__.py | 185 - .../__pycache__/__init__.cpython-39.pyc | Bin 4181 -> 0 bytes .../__pycache__/errorhandler.cpython-39.pyc | Bin 1372 -> 0 bytes .../__pycache__/exception.cpython-39.pyc | Bin 539 -> 0 bytes .../testing/__pycache__/flaky.cpython-39.pyc | Bin 2997 -> 0 bytes .../testing/__pycache__/hub.cpython-39.pyc | Bin 1957 -> 0 bytes .../__pycache__/leakcheck.cpython-39.pyc | Bin 5378 -> 0 bytes .../__pycache__/modules.cpython-39.pyc | Bin 2573 -> 0 bytes .../__pycache__/monkey_test.cpython-39.pyc | Bin 2167 -> 0 bytes .../__pycache__/openfiles.cpython-39.pyc | Bin 4987 -> 0 bytes .../testing/__pycache__/params.cpython-39.pyc | Bin 828 -> 0 bytes .../patched_tests_setup.cpython-39.pyc | Bin 27153 -> 0 bytes .../__pycache__/resources.cpython-39.pyc | Bin 3203 -> 0 bytes .../testing/__pycache__/six.cpython-39.pyc | Bin 1056 -> 0 bytes .../__pycache__/skipping.cpython-39.pyc | Bin 3717 -> 0 bytes .../__pycache__/sockets.cpython-39.pyc | Bin 1343 -> 0 bytes .../__pycache__/support.cpython-39.pyc | Bin 3686 -> 0 bytes .../__pycache__/switching.cpython-39.pyc | Bin 1587 -> 0 bytes .../__pycache__/sysinfo.cpython-39.pyc | Bin 3730 -> 0 bytes .../__pycache__/testcase.cpython-39.pyc | Bin 14112 -> 0 bytes .../__pycache__/testrunner.cpython-39.pyc | Bin 23012 -> 0 bytes .../testing/__pycache__/timing.cpython-39.pyc | Bin 3987 -> 0 bytes .../testing/__pycache__/travis.cpython-39.pyc | Bin 1260 -> 0 bytes .../testing/__pycache__/util.cpython-39.pyc | Bin 16308 -> 0 bytes .../__pycache__/sitecustomize.cpython-39.pyc | Bin 513 -> 0 bytes .../testing/coveragesite/sitecustomize.py | 17 - .../gevent/testing/errorhandler.py | 57 - .../site-packages/gevent/testing/exception.py | 23 - .../site-packages/gevent/testing/flaky.py | 114 - .../site-packages/gevent/testing/hub.py | 71 - .../site-packages/gevent/testing/leakcheck.py | 217 - .../site-packages/gevent/testing/modules.py | 132 - .../gevent/testing/monkey_test.py | 111 - .../site-packages/gevent/testing/openfiles.py | 223 - .../site-packages/gevent/testing/params.py | 68 - .../gevent/testing/patched_tests_setup.py | 1501 --- .../site-packages/gevent/testing/resources.py | 209 - .../site-packages/gevent/testing/six.py | 43 - .../site-packages/gevent/testing/skipping.py | 202 - .../site-packages/gevent/testing/sockets.py | 49 - .../site-packages/gevent/testing/support.py | 147 - .../site-packages/gevent/testing/switching.py | 64 - .../site-packages/gevent/testing/sysinfo.py | 204 - .../site-packages/gevent/testing/testcase.py | 442 - .../gevent/testing/testrunner.py | 948 -- .../site-packages/gevent/testing/timing.py | 138 - .../site-packages/gevent/testing/travis.py | 41 - .../site-packages/gevent/testing/util.py | 641 - .../gevent/tests/2_7_keycert.pem | 81 - .../site-packages/gevent/tests/__init__.py | 0 .../site-packages/gevent/tests/__main__.py | 6 - .../tests/__pycache__/__init__.cpython-39.pyc | Bin 193 -> 0 bytes .../tests/__pycache__/__main__.cpython-39.pyc | Bin 382 -> 0 bytes .../_blocks_at_top_level.cpython-39.pyc | Bin 275 -> 0 bytes .../_import_import_patch.cpython-39.pyc | Bin 237 -> 0 bytes .../__pycache__/_import_patch.cpython-39.pyc | Bin 262 -> 0 bytes .../__pycache__/_import_wait.cpython-39.pyc | Bin 744 -> 0 bytes .../_imports_at_top_level.cpython-39.pyc | Bin 230 -> 0 bytes ...mports_imports_at_top_level.cpython-39.pyc | Bin 405 -> 0 bytes .../getaddrinfo_module.cpython-39.pyc | Bin 317 -> 0 bytes .../__pycache__/known_failures.cpython-39.pyc | Bin 15223 -> 0 bytes .../__pycache__/lock_tests.cpython-39.pyc | Bin 23542 -> 0 bytes .../test__GreenletExit.cpython-39.pyc | Bin 316 -> 0 bytes .../__pycache__/test___config.cpython-39.pyc | Bin 6068 -> 0 bytes .../__pycache__/test___ident.cpython-39.pyc | Bin 2366 -> 0 bytes .../__pycache__/test___monitor.cpython-39.pyc | Bin 12649 -> 0 bytes .../test___monkey_patching.cpython-39.pyc | Bin 3014 -> 0 bytes .../__pycache__/test__all__.cpython-39.pyc | Bin 7406 -> 0 bytes .../__pycache__/test__api.cpython-39.pyc | Bin 4112 -> 0 bytes .../test__api_timeout.cpython-39.pyc | Bin 5742 -> 0 bytes .../test__ares_host_result.cpython-39.pyc | Bin 1302 -> 0 bytes .../test__ares_timeout.cpython-39.pyc | Bin 1476 -> 0 bytes .../__pycache__/test__backdoor.cpython-39.pyc | Bin 6909 -> 0 bytes .../test__close_backend_fd.cpython-39.pyc | Bin 2695 -> 0 bytes .../__pycache__/test__compat.cpython-39.pyc | Bin 2654 -> 0 bytes .../test__contextvars.cpython-39.pyc | Bin 10942 -> 0 bytes .../__pycache__/test__core.cpython-39.pyc | Bin 5915 -> 0 bytes .../test__core_async.cpython-39.pyc | Bin 1087 -> 0 bytes .../test__core_callback.cpython-39.pyc | Bin 1013 -> 0 bytes .../test__core_fork.cpython-39.pyc | Bin 1743 -> 0 bytes .../test__core_loop_run.cpython-39.pyc | Bin 774 -> 0 bytes .../test__core_stat.cpython-39.pyc | Bin 3439 -> 0 bytes .../test__core_timer.cpython-39.pyc | Bin 4103 -> 0 bytes .../test__core_watcher.cpython-39.pyc | Bin 3571 -> 0 bytes .../__pycache__/test__destroy.cpython-39.pyc | Bin 1084 -> 0 bytes .../test__destroy_default_loop.cpython-39.pyc | Bin 1749 -> 0 bytes .../__pycache__/test__doctests.cpython-39.pyc | Bin 3765 -> 0 bytes .../__pycache__/test__environ.cpython-39.pyc | Bin 609 -> 0 bytes .../__pycache__/test__event.cpython-39.pyc | Bin 16086 -> 0 bytes .../__pycache__/test__events.cpython-39.pyc | Bin 1952 -> 0 bytes .../test__example_echoserver.cpython-39.pyc | Bin 1506 -> 0 bytes ...test__example_portforwarder.cpython-39.pyc | Bin 2066 -> 0 bytes .../test__example_udp_client.cpython-39.pyc | Bin 1189 -> 0 bytes .../test__example_udp_server.cpython-39.pyc | Bin 903 -> 0 bytes .../test__example_webproxy.cpython-39.pyc | Bin 1258 -> 0 bytes .../test__example_wsgiserver.cpython-39.pyc | Bin 2667 -> 0 bytes ...est__example_wsgiserver_ssl.cpython-39.pyc | Bin 875 -> 0 bytes .../__pycache__/test__examples.cpython-39.pyc | Bin 3406 -> 0 bytes .../__pycache__/test__exc_info.cpython-39.pyc | Bin 1808 -> 0 bytes .../test__execmodules.cpython-39.pyc | Bin 1596 -> 0 bytes .../test__fileobject.cpython-39.pyc | Bin 17237 -> 0 bytes .../test__getaddrinfo_import.cpython-39.pyc | Bin 243 -> 0 bytes .../__pycache__/test__greenio.cpython-39.pyc | Bin 3381 -> 0 bytes .../__pycache__/test__greenlet.cpython-39.pyc | Bin 36330 -> 0 bytes .../test__greenletset.cpython-39.pyc | Bin 6354 -> 0 bytes .../test__greenness.cpython-39.pyc | Bin 2355 -> 0 bytes .../__pycache__/test__hub.cpython-39.pyc | Bin 12340 -> 0 bytes .../__pycache__/test__hub_join.cpython-39.pyc | Bin 3795 -> 0 bytes .../test__hub_join_timeout.cpython-39.pyc | Bin 3995 -> 0 bytes ...import_blocking_in_greenlet.cpython-39.pyc | Bin 767 -> 0 bytes .../test__import_wait.cpython-39.pyc | Bin 299 -> 0 bytes .../__pycache__/test__issue112.cpython-39.pyc | Bin 768 -> 0 bytes .../test__issue1686.cpython-39.pyc | Bin 1918 -> 0 bytes .../__pycache__/test__issue230.cpython-39.pyc | Bin 912 -> 0 bytes .../__pycache__/test__issue330.cpython-39.pyc | Bin 2522 -> 0 bytes .../__pycache__/test__issue467.cpython-39.pyc | Bin 1198 -> 0 bytes .../__pycache__/test__issue6.cpython-39.pyc | Bin 1161 -> 0 bytes .../__pycache__/test__issue600.cpython-39.pyc | Bin 1432 -> 0 bytes .../__pycache__/test__issue607.cpython-39.pyc | Bin 1920 -> 0 bytes .../__pycache__/test__issue639.cpython-39.pyc | Bin 603 -> 0 bytes .../test__issue_728.cpython-39.pyc | Bin 338 -> 0 bytes .../test__issues461_471.cpython-39.pyc | Bin 2322 -> 0 bytes .../__pycache__/test__iwait.cpython-39.pyc | Bin 1452 -> 0 bytes .../__pycache__/test__joinall.cpython-39.pyc | Bin 752 -> 0 bytes .../__pycache__/test__local.cpython-39.pyc | Bin 14174 -> 0 bytes .../__pycache__/test__lock.cpython-39.pyc | Bin 1025 -> 0 bytes .../test__loop_callback.cpython-39.pyc | Bin 857 -> 0 bytes .../test__makefile_ref.cpython-39.pyc | Bin 14161 -> 0 bytes .../__pycache__/test__memleak.cpython-39.pyc | Bin 1244 -> 0 bytes .../__pycache__/test__monkey.cpython-39.pyc | Bin 5496 -> 0 bytes ...est__monkey_builtins_future.cpython-39.pyc | Bin 569 -> 0 bytes ...test__monkey_futures_thread.cpython-39.pyc | Bin 1344 -> 0 bytes .../test__monkey_hub_in_thread.cpython-39.pyc | Bin 729 -> 0 bytes .../test__monkey_logging.cpython-39.pyc | Bin 1396 -> 0 bytes .../test__monkey_module_run.cpython-39.pyc | Bin 4069 -> 0 bytes ...st__monkey_multiple_imports.cpython-39.pyc | Bin 253 -> 0 bytes .../test__monkey_queue.cpython-39.pyc | Bin 8897 -> 0 bytes .../test__monkey_select.cpython-39.pyc | Bin 1017 -> 0 bytes .../test__monkey_selectors.cpython-39.pyc | Bin 2331 -> 0 bytes .../test__monkey_sigchld.cpython-39.pyc | Bin 1750 -> 0 bytes .../test__monkey_sigchld_2.cpython-39.pyc | Bin 978 -> 0 bytes .../test__monkey_sigchld_3.cpython-39.pyc | Bin 1182 -> 0 bytes .../test__monkey_ssl_warning.cpython-39.pyc | Bin 1255 -> 0 bytes .../test__monkey_ssl_warning2.cpython-39.pyc | Bin 1569 -> 0 bytes .../test__monkey_ssl_warning3.cpython-39.pyc | Bin 1592 -> 0 bytes .../test__nondefaultloop.cpython-39.pyc | Bin 420 -> 0 bytes .../__pycache__/test__order.cpython-39.pyc | Bin 2256 -> 0 bytes .../tests/__pycache__/test__os.cpython-39.pyc | Bin 10375 -> 0 bytes .../__pycache__/test__pool.cpython-39.pyc | Bin 22152 -> 0 bytes .../__pycache__/test__pywsgi.cpython-39.pyc | Bin 64555 -> 0 bytes .../__pycache__/test__queue.cpython-39.pyc | Bin 16598 -> 0 bytes .../test__real_greenlet.cpython-39.pyc | Bin 1179 -> 0 bytes .../__pycache__/test__refcount.cpython-39.pyc | Bin 4402 -> 0 bytes .../test__refcount_core.cpython-39.pyc | Bin 1165 -> 0 bytes .../test__resolver_dnspython.cpython-39.pyc | Bin 1672 -> 0 bytes .../__pycache__/test__select.cpython-39.pyc | Bin 4063 -> 0 bytes .../test__selectors.cpython-39.pyc | Bin 3191 -> 0 bytes .../test__semaphore.cpython-39.pyc | Bin 11940 -> 0 bytes .../__pycache__/test__server.cpython-39.pyc | Bin 19500 -> 0 bytes .../test__server_pywsgi.cpython-39.pyc | Bin 3744 -> 0 bytes .../__pycache__/test__signal.cpython-39.pyc | Bin 2679 -> 0 bytes .../__pycache__/test__sleep0.cpython-39.pyc | Bin 365 -> 0 bytes .../__pycache__/test__socket.cpython-39.pyc | Bin 18795 -> 0 bytes .../test__socket_close.cpython-39.pyc | Bin 2057 -> 0 bytes .../test__socket_dns.cpython-39.pyc | Bin 26524 -> 0 bytes .../test__socket_dns6.cpython-39.pyc | Bin 3061 -> 0 bytes .../test__socket_errors.cpython-39.pyc | Bin 1212 -> 0 bytes .../test__socket_ex.cpython-39.pyc | Bin 1358 -> 0 bytes ...est__socket_send_memoryview.cpython-39.pyc | Bin 1598 -> 0 bytes .../test__socket_ssl.cpython-39.pyc | Bin 1330 -> 0 bytes .../test__socket_timeout.cpython-39.pyc | Bin 1811 -> 0 bytes .../test__socketpair.cpython-39.pyc | Bin 1363 -> 0 bytes .../__pycache__/test__ssl.cpython-39.pyc | Bin 3323 -> 0 bytes .../test__subprocess.cpython-39.pyc | Bin 15795 -> 0 bytes ...est__subprocess_interrupted.cpython-39.pyc | Bin 825 -> 0 bytes .../test__subprocess_poll.cpython-39.pyc | Bin 495 -> 0 bytes .../test__systemerror.cpython-39.pyc | Bin 3674 -> 0 bytes .../__pycache__/test__thread.cpython-39.pyc | Bin 1121 -> 0 bytes .../test__threading.cpython-39.pyc | Bin 2803 -> 0 bytes .../test__threading_2.cpython-39.pyc | Bin 19381 -> 0 bytes ...st__threading_before_monkey.cpython-39.pyc | Bin 859 -> 0 bytes ...g_holding_lock_while_monkey.cpython-39.pyc | Bin 354 -> 0 bytes ..._threading_monkey_in_thread.cpython-39.pyc | Bin 2011 -> 0 bytes ...eading_native_before_monkey.cpython-39.pyc | Bin 2067 -> 0 bytes .../test__threading_no_monkey.cpython-39.pyc | Bin 1060 -> 0 bytes ...st__threading_patched_local.cpython-39.pyc | Bin 778 -> 0 bytes ...test__threading_vs_settrace.cpython-39.pyc | Bin 4155 -> 0 bytes .../test__threadpool.cpython-39.pyc | Bin 26464 -> 0 bytes ...threadpool_executor_patched.cpython-39.pyc | Bin 724 -> 0 bytes .../__pycache__/test__timeout.cpython-39.pyc | Bin 5312 -> 0 bytes .../__pycache__/test__util.cpython-39.pyc | Bin 9582 -> 0 bytes .../gevent/tests/_blocks_at_top_level.py | 3 - .../gevent/tests/_import_import_patch.py | 1 - .../gevent/tests/_import_patch.py | 2 - .../gevent/tests/_import_wait.py | 26 - .../gevent/tests/_imports_at_top_level.py | 2 - .../tests/_imports_imports_at_top_level.py | 13 - .../site-packages/gevent/tests/badcert.pem | 36 - .../site-packages/gevent/tests/badkey.pem | 40 - .../gevent/tests/getaddrinfo_module.py | 4 - .../site-packages/gevent/tests/hosts_file.txt | 10351 ---------------- .../tests/https_svn_python_org_root.pem | 41 - .../site-packages/gevent/tests/keycert.pem | 32 - .../gevent/tests/known_failures.py | 505 - .../site-packages/gevent/tests/lock_tests.py | 764 -- .../gevent/tests/monkey_package/__init__.py | 12 - .../gevent/tests/monkey_package/__main__.py | 9 - .../__pycache__/__init__.cpython-39.pyc | Bin 532 -> 0 bytes .../__pycache__/__main__.cpython-39.pyc | Bin 350 -> 0 bytes .../issue1526_no_monkey.cpython-39.pyc | Bin 669 -> 0 bytes .../issue1526_with_monkey.cpython-39.pyc | Bin 807 -> 0 bytes .../__pycache__/issue302monkey.cpython-39.pyc | Bin 617 -> 0 bytes .../__pycache__/script.cpython-39.pyc | Bin 674 -> 0 bytes .../threadpool_monkey_patches.cpython-39.pyc | Bin 1150 -> 0 bytes .../threadpool_no_monkey.cpython-39.pyc | Bin 1052 -> 0 bytes .../monkey_package/issue1526_no_monkey.py | 21 - .../monkey_package/issue1526_with_monkey.py | 28 - .../tests/monkey_package/issue302monkey.py | 30 - .../gevent/tests/monkey_package/script.py | 20 - .../threadpool_monkey_patches.py | 30 - .../monkey_package/threadpool_no_monkey.py | 30 - .../site-packages/gevent/tests/nullcert.pem | 0 .../site-packages/gevent/tests/server.crt | 29 - .../site-packages/gevent/tests/server.key | 52 - .../site-packages/gevent/tests/sha256.pem | 33 - .../gevent/tests/test__GreenletExit.py | 4 - .../gevent/tests/test___config.py | 161 - .../gevent/tests/test___ident.py | 80 - .../gevent/tests/test___monitor.py | 386 - .../gevent/tests/test___monkey_patching.py | 101 - .../site-packages/gevent/tests/test__all__.py | 301 - .../site-packages/gevent/tests/test__api.py | 132 - .../gevent/tests/test__api_timeout.py | 210 - .../gevent/tests/test__ares_host_result.py | 31 - .../gevent/tests/test__ares_timeout.py | 43 - .../gevent/tests/test__backdoor.py | 171 - .../gevent/tests/test__close_backend_fd.py | 102 - .../gevent/tests/test__compat.py | 56 - .../gevent/tests/test__contextvars.py | 1089 -- .../site-packages/gevent/tests/test__core.py | 160 - .../gevent/tests/test__core_async.py | 31 - .../gevent/tests/test__core_callback.py | 32 - .../gevent/tests/test__core_fork.py | 74 - .../gevent/tests/test__core_loop_run.py | 22 - .../gevent/tests/test__core_stat.py | 118 - .../gevent/tests/test__core_timer.py | 157 - .../gevent/tests/test__core_watcher.py | 124 - .../gevent/tests/test__destroy.py | 51 - .../tests/test__destroy_default_loop.py | 72 - .../gevent/tests/test__doctests.py | 133 - .../gevent/tests/test__environ.py | 18 - .../site-packages/gevent/tests/test__event.py | 446 - .../gevent/tests/test__events.py | 50 - .../gevent/tests/test__example_echoserver.py | 40 - .../tests/test__example_portforwarder.py | 67 - .../gevent/tests/test__example_udp_client.py | 35 - .../gevent/tests/test__example_udp_server.py | 22 - .../gevent/tests/test__example_webproxy.py | 29 - .../gevent/tests/test__example_wsgiserver.py | 93 - .../tests/test__example_wsgiserver_ssl.py | 24 - .../gevent/tests/test__examples.py | 108 - .../gevent/tests/test__exc_info.py | 58 - .../gevent/tests/test__execmodules.py | 45 - .../gevent/tests/test__fileobject.py | 522 - .../gevent/tests/test__getaddrinfo_import.py | 7 - .../gevent/tests/test__greenio.py | 146 - .../gevent/tests/test__greenlet.py | 1025 -- .../gevent/tests/test__greenletset.py | 183 - .../gevent/tests/test__greenness.py | 87 - .../site-packages/gevent/tests/test__hub.py | 404 - .../gevent/tests/test__hub_join.py | 117 - .../gevent/tests/test__hub_join_timeout.py | 99 - .../test__import_blocking_in_greenlet.py | 22 - .../gevent/tests/test__import_wait.py | 7 - .../gevent/tests/test__issue112.py | 19 - .../gevent/tests/test__issue1686.py | 85 - .../gevent/tests/test__issue230.py | 27 - .../gevent/tests/test__issue330.py | 82 - .../gevent/tests/test__issue467.py | 40 - .../gevent/tests/test__issue6.py | 39 - .../gevent/tests/test__issue600.py | 48 - .../gevent/tests/test__issue607.py | 47 - .../gevent/tests/test__issue639.py | 12 - .../gevent/tests/test__issue_728.py | 9 - .../gevent/tests/test__issues461_471.py | 92 - .../site-packages/gevent/tests/test__iwait.py | 42 - .../gevent/tests/test__joinall.py | 20 - .../site-packages/gevent/tests/test__local.py | 425 - .../site-packages/gevent/tests/test__lock.py | 33 - .../gevent/tests/test__loop_callback.py | 18 - .../gevent/tests/test__makefile_ref.py | 546 - .../gevent/tests/test__memleak.py | 56 - .../gevent/tests/test__monkey.py | 168 - .../tests/test__monkey_builtins_future.py | 16 - .../tests/test__monkey_futures_thread.py | 49 - .../tests/test__monkey_hub_in_thread.py | 28 - .../gevent/tests/test__monkey_logging.py | 56 - .../gevent/tests/test__monkey_module_run.py | 129 - .../tests/test__monkey_multiple_imports.py | 6 - .../gevent/tests/test__monkey_queue.py | 331 - .../gevent/tests/test__monkey_select.py | 32 - .../gevent/tests/test__monkey_selectors.py | 82 - .../gevent/tests/test__monkey_sigchld.py | 89 - .../gevent/tests/test__monkey_sigchld_2.py | 56 - .../gevent/tests/test__monkey_sigchld_3.py | 59 - .../gevent/tests/test__monkey_ssl_warning.py | 34 - .../gevent/tests/test__monkey_ssl_warning2.py | 44 - .../gevent/tests/test__monkey_ssl_warning3.py | 47 - .../gevent/tests/test__nondefaultloop.py | 12 - .../site-packages/gevent/tests/test__order.py | 61 - .../site-packages/gevent/tests/test__os.py | 180 - .../site-packages/gevent/tests/test__pool.py | 603 - .../gevent/tests/test__pywsgi.py | 1892 --- .../site-packages/gevent/tests/test__queue.py | 470 - .../gevent/tests/test__real_greenlet.py | 34 - .../gevent/tests/test__refcount.py | 189 - .../gevent/tests/test__refcount_core.py | 25 - .../gevent/tests/test__resolver_dnspython.py | 43 - .../gevent/tests/test__select.py | 115 - .../gevent/tests/test__selectors.py | 109 - .../gevent/tests/test__semaphore.py | 425 - .../gevent/tests/test__server.py | 559 - .../gevent/tests/test__server_pywsgi.py | 106 - .../gevent/tests/test__signal.py | 120 - .../gevent/tests/test__sleep0.py | 10 - .../gevent/tests/test__socket.py | 626 - .../gevent/tests/test__socket_close.py | 58 - .../gevent/tests/test__socket_dns.py | 923 -- .../gevent/tests/test__socket_dns6.py | 114 - .../gevent/tests/test__socket_errors.py | 48 - .../gevent/tests/test__socket_ex.py | 44 - .../tests/test__socket_send_memoryview.py | 40 - .../gevent/tests/test__socket_ssl.py | 39 - .../gevent/tests/test__socket_timeout.py | 51 - .../gevent/tests/test__socketpair.py | 37 - .../site-packages/gevent/tests/test__ssl.py | 128 - .../gevent/tests/test__subprocess.py | 518 - .../tests/test__subprocess_interrupted.py | 42 - .../gevent/tests/test__subprocess_poll.py | 13 - .../gevent/tests/test__systemerror.py | 110 - .../gevent/tests/test__thread.py | 31 - .../gevent/tests/test__threading.py | 93 - .../gevent/tests/test__threading_2.py | 620 - .../tests/test__threading_before_monkey.py | 23 - ...st__threading_holding_lock_while_monkey.py | 8 - .../tests/test__threading_monkey_in_thread.py | 65 - .../test__threading_native_before_monkey.py | 68 - .../gevent/tests/test__threading_no_monkey.py | 30 - .../tests/test__threading_patched_local.py | 24 - .../tests/test__threading_vs_settrace.py | 154 - .../gevent/tests/test__threadpool.py | 825 -- .../test__threadpool_executor_patched.py | 17 - .../gevent/tests/test__timeout.py | 159 - .../site-packages/gevent/tests/test__util.py | 303 - .../gevent/tests/test_server.crt | 29 - .../gevent/tests/test_server.key | 52 - .../tests/tests_that_dont_do_leakchecks.txt | 9 - .../tests/tests_that_dont_monkeypatch.txt | 28 - .../tests/tests_that_dont_use_resolver.txt | 136 - .../site-packages/gevent/tests/wrongcert.pem | 32 - .../python3.9/site-packages/gevent/thread.py | 176 - .../site-packages/gevent/threading.py | 240 - .../site-packages/gevent/threadpool.py | 799 -- .../python3.9/site-packages/gevent/time.py | 27 - .../python3.9/site-packages/gevent/timeout.py | 382 - .../python3.9/site-packages/gevent/util.py | 650 - .../site-packages/gevent/win32util.py | 98 - .../DESCRIPTION.rst | 125 - .../INSTALLER | 1 - .../METADATA | 135 - .../gevent_websocket-0.10.1.dist-info/RECORD | 37 - .../gevent_websocket-0.10.1.dist-info/WHEEL | 5 - .../metadata.json | 1 - .../top_level.txt | 1 - .../site-packages/geventwebsocket/__init__.py | 21 - .../__pycache__/__init__.cpython-39.pyc | Bin 663 -> 0 bytes .../__pycache__/_compat.cpython-39.pyc | Bin 744 -> 0 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 885 -> 0 bytes .../__pycache__/handler.cpython-39.pyc | Bin 7618 -> 0 bytes .../__pycache__/logging.cpython-39.pyc | Bin 1470 -> 0 bytes .../__pycache__/resource.cpython-39.pyc | Bin 3302 -> 0 bytes .../__pycache__/server.cpython-39.pyc | Bin 1467 -> 0 bytes .../__pycache__/utf8validator.cpython-39.pyc | Bin 6132 -> 0 bytes .../__pycache__/utils.cpython-39.pyc | Bin 1241 -> 0 bytes .../__pycache__/websocket.cpython-39.pyc | Bin 12971 -> 0 bytes .../site-packages/geventwebsocket/_compat.py | 23 - .../geventwebsocket/exceptions.py | 19 - .../geventwebsocket/gunicorn/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 205 -> 0 bytes .../__pycache__/workers.cpython-39.pyc | Bin 528 -> 0 bytes .../geventwebsocket/gunicorn/workers.py | 6 - .../site-packages/geventwebsocket/handler.py | 283 - .../site-packages/geventwebsocket/logging.py | 31 - .../geventwebsocket/protocols/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 206 -> 0 bytes .../protocols/__pycache__/base.cpython-39.pyc | Bin 1518 -> 0 bytes .../protocols/__pycache__/wamp.cpython-39.pyc | Bin 7410 -> 0 bytes .../geventwebsocket/protocols/base.py | 35 - .../geventwebsocket/protocols/wamp.py | 235 - .../site-packages/geventwebsocket/resource.py | 100 - .../site-packages/geventwebsocket/server.py | 34 - .../geventwebsocket/utf8validator.py | 224 - .../site-packages/geventwebsocket/utils.py | 45 - .../geventwebsocket/websocket.py | 565 - .../greenlet-1.1.2.dist-info/AUTHORS | 51 - .../greenlet-1.1.2.dist-info/INSTALLER | 1 - .../greenlet-1.1.2.dist-info/LICENSE | 30 - .../greenlet-1.1.2.dist-info/LICENSE.PSF | 47 - .../greenlet-1.1.2.dist-info/METADATA | 104 - .../greenlet-1.1.2.dist-info/RECORD | 71 - .../greenlet-1.1.2.dist-info/WHEEL | 6 - .../greenlet-1.1.2.dist-info/top_level.txt | 1 - .../site-packages/greenlet/__init__.py | 63 - .../__pycache__/__init__.cpython-39.pyc | Bin 852 -> 0 bytes .../_greenlet.cpython-39-x86_64-linux-gnu.so | Bin 130440 -> 0 bytes .../site-packages/greenlet/greenlet.c | 2135 ---- .../site-packages/greenlet/greenlet.h | 146 - .../platform/setup_switch_x64_masm.cmd | 2 - .../greenlet/platform/switch_aarch64_gcc.h | 69 - .../greenlet/platform/switch_alpha_unix.h | 30 - .../greenlet/platform/switch_amd64_unix.h | 84 - .../greenlet/platform/switch_arm32_gcc.h | 79 - .../greenlet/platform/switch_arm32_ios.h | 67 - .../greenlet/platform/switch_csky_gcc.h | 48 - .../greenlet/platform/switch_m68k_gcc.h | 38 - .../greenlet/platform/switch_mips_unix.h | 64 - .../greenlet/platform/switch_ppc64_aix.h | 103 - .../greenlet/platform/switch_ppc64_linux.h | 105 - .../greenlet/platform/switch_ppc_aix.h | 87 - .../greenlet/platform/switch_ppc_linux.h | 84 - .../greenlet/platform/switch_ppc_macosx.h | 82 - .../greenlet/platform/switch_ppc_unix.h | 82 - .../greenlet/platform/switch_riscv_unix.h | 32 - .../greenlet/platform/switch_s390_unix.h | 87 - .../greenlet/platform/switch_sparc_sun_gcc.h | 92 - .../greenlet/platform/switch_x32_unix.h | 63 - .../greenlet/platform/switch_x64_masm.asm | 111 - .../greenlet/platform/switch_x64_masm.obj | Bin 1078 -> 0 bytes .../greenlet/platform/switch_x64_msvc.h | 60 - .../greenlet/platform/switch_x86_msvc.h | 88 - .../greenlet/platform/switch_x86_unix.h | 105 - .../greenlet/slp_platformselect.h | 58 - .../site-packages/greenlet/tests/__init__.py | 0 .../tests/__pycache__/__init__.cpython-39.pyc | Bin 195 -> 0 bytes .../test_contextvars.cpython-39.pyc | Bin 7376 -> 0 bytes .../tests/__pycache__/test_cpp.cpython-39.pyc | Bin 861 -> 0 bytes .../test_extension_interface.cpython-39.pyc | Bin 3564 -> 0 bytes .../tests/__pycache__/test_gc.cpython-39.pyc | Bin 2917 -> 0 bytes .../__pycache__/test_generator.cpython-39.pyc | Bin 2237 -> 0 bytes .../test_generator_nested.cpython-39.pyc | Bin 5344 -> 0 bytes .../__pycache__/test_greenlet.cpython-39.pyc | Bin 27062 -> 0 bytes .../__pycache__/test_leaks.cpython-39.pyc | Bin 5141 -> 0 bytes .../test_stack_saved.cpython-39.pyc | Bin 873 -> 0 bytes .../__pycache__/test_throw.cpython-39.pyc | Bin 2965 -> 0 bytes .../__pycache__/test_tracing.cpython-39.pyc | Bin 8982 -> 0 bytes .../__pycache__/test_version.cpython-39.pyc | Bin 1545 -> 0 bytes .../__pycache__/test_weakref.cpython-39.pyc | Bin 1864 -> 0 bytes .../greenlet/tests/_test_extension.c | 216 - ...t_extension.cpython-39-x86_64-linux-gnu.so | Bin 34632 -> 0 bytes .../greenlet/tests/_test_extension_cpp.cpp | 121 - ...tension_cpp.cpython-39-x86_64-linux-gnu.so | Bin 47368 -> 0 bytes .../greenlet/tests/test_contextvars.py | 266 - .../site-packages/greenlet/tests/test_cpp.py | 18 - .../tests/test_extension_interface.py | 77 - .../site-packages/greenlet/tests/test_gc.py | 77 - .../greenlet/tests/test_generator.py | 59 - .../greenlet/tests/test_generator_nested.py | 165 - .../greenlet/tests/test_greenlet.py | 728 -- .../greenlet/tests/test_leaks.py | 178 - .../greenlet/tests/test_stack_saved.py | 19 - .../greenlet/tests/test_throw.py | 100 - .../greenlet/tests/test_tracing.py | 267 - .../greenlet/tests/test_version.py | 39 - .../greenlet/tests/test_weakref.py | 34 - .../idna-3.3.dist-info/INSTALLER | 1 - .../idna-3.3.dist-info/LICENSE.md | 29 - .../site-packages/idna-3.3.dist-info/METADATA | 236 - .../site-packages/idna-3.3.dist-info/RECORD | 23 - .../site-packages/idna-3.3.dist-info/WHEEL | 5 - .../idna-3.3.dist-info/top_level.txt | 1 - .../python3.9/site-packages/idna/__init__.py | 44 - .../idna/__pycache__/__init__.cpython-39.pyc | Bin 844 -> 0 bytes .../idna/__pycache__/codec.cpython-39.pyc | Bin 3081 -> 0 bytes .../idna/__pycache__/compat.cpython-39.pyc | Bin 763 -> 0 bytes .../idna/__pycache__/core.cpython-39.pyc | Bin 9759 -> 0 bytes .../idna/__pycache__/idnadata.cpython-39.pyc | Bin 23040 -> 0 bytes .../idna/__pycache__/intranges.cpython-39.pyc | Bin 1994 -> 0 bytes .../__pycache__/package_data.cpython-39.pyc | Bin 208 -> 0 bytes .../idna/__pycache__/uts46data.cpython-39.pyc | Bin 151741 -> 0 bytes .../lib/python3.9/site-packages/idna/codec.py | 112 - .../python3.9/site-packages/idna/compat.py | 13 - .../lib/python3.9/site-packages/idna/core.py | 397 - .../python3.9/site-packages/idna/idnadata.py | 2137 ---- .../python3.9/site-packages/idna/intranges.py | 54 - .../site-packages/idna/package_data.py | 2 - .../lib/python3.9/site-packages/idna/py.typed | 0 .../python3.9/site-packages/idna/uts46data.py | 8512 ------------- .../site-packages/libfuturize/__init__.py | 1 - .../__pycache__/__init__.cpython-39.pyc | Bin 192 -> 0 bytes .../__pycache__/fixer_util.cpython-39.pyc | Bin 11979 -> 0 bytes .../__pycache__/main.cpython-39.pyc | Bin 9570 -> 0 bytes .../site-packages/libfuturize/fixer_util.py | 520 - .../libfuturize/fixes/__init__.py | 97 - .../fixes/__pycache__/__init__.cpython-39.pyc | Bin 2000 -> 0 bytes .../__pycache__/fix_UserDict.cpython-39.pyc | Bin 2241 -> 0 bytes .../fix_absolute_import.cpython-39.pyc | Bin 2511 -> 0 bytes ...rts_except_unicode_literals.cpython-39.pyc | Bin 1104 -> 0 bytes .../__pycache__/fix_basestring.cpython-39.pyc | Bin 829 -> 0 bytes .../__pycache__/fix_bytes.cpython-39.pyc | Bin 1069 -> 0 bytes .../fixes/__pycache__/fix_cmp.cpython-39.pyc | Bin 1114 -> 0 bytes .../__pycache__/fix_division.cpython-39.pyc | Bin 450 -> 0 bytes .../fix_division_safe.cpython-39.pyc | Bin 3065 -> 0 bytes .../__pycache__/fix_execfile.cpython-39.pyc | Bin 1347 -> 0 bytes .../fix_future_builtins.cpython-39.pyc | Bin 1761 -> 0 bytes ...fix_future_standard_library.cpython-39.pyc | Bin 1103 -> 0 bytes ...ure_standard_library_urllib.cpython-39.pyc | Bin 1169 -> 0 bytes .../__pycache__/fix_input.cpython-39.pyc | Bin 1107 -> 0 bytes .../__pycache__/fix_metaclass.cpython-39.pyc | Bin 5822 -> 0 bytes .../__pycache__/fix_next_call.cpython-39.pyc | Bin 3062 -> 0 bytes .../__pycache__/fix_object.cpython-39.pyc | Bin 836 -> 0 bytes .../fix_oldstr_wrap.cpython-39.pyc | Bin 1417 -> 0 bytes ...fix_order___future__imports.cpython-39.pyc | Bin 1064 -> 0 bytes .../__pycache__/fix_print.cpython-39.pyc | Bin 2430 -> 0 bytes .../fix_print_with_import.cpython-39.pyc | Bin 983 -> 0 bytes .../__pycache__/fix_raise.cpython-39.pyc | Bin 2523 -> 0 bytes ...remove_old__future__imports.cpython-39.pyc | Bin 1269 -> 0 bytes .../fix_unicode_keep_u.cpython-39.pyc | Bin 1192 -> 0 bytes ...fix_unicode_literals_import.cpython-39.pyc | Bin 835 -> 0 bytes .../fix_xrange_with_import.cpython-39.pyc | Bin 914 -> 0 bytes .../libfuturize/fixes/fix_UserDict.py | 102 - .../libfuturize/fixes/fix_absolute_import.py | 91 - ...future__imports_except_unicode_literals.py | 26 - .../libfuturize/fixes/fix_basestring.py | 17 - .../libfuturize/fixes/fix_bytes.py | 24 - .../libfuturize/fixes/fix_cmp.py | 33 - .../libfuturize/fixes/fix_division.py | 12 - .../libfuturize/fixes/fix_division_safe.py | 104 - .../libfuturize/fixes/fix_execfile.py | 37 - .../libfuturize/fixes/fix_future_builtins.py | 59 - .../fixes/fix_future_standard_library.py | 24 - .../fix_future_standard_library_urllib.py | 28 - .../libfuturize/fixes/fix_input.py | 32 - .../libfuturize/fixes/fix_metaclass.py | 262 - .../libfuturize/fixes/fix_next_call.py | 104 - .../libfuturize/fixes/fix_object.py | 17 - .../libfuturize/fixes/fix_oldstr_wrap.py | 39 - .../fixes/fix_order___future__imports.py | 36 - .../libfuturize/fixes/fix_print.py | 94 - .../fixes/fix_print_with_import.py | 22 - .../libfuturize/fixes/fix_raise.py | 107 - .../fixes/fix_remove_old__future__imports.py | 26 - .../libfuturize/fixes/fix_unicode_keep_u.py | 24 - .../fixes/fix_unicode_literals_import.py | 18 - .../fixes/fix_xrange_with_import.py | 20 - .../site-packages/libfuturize/main.py | 322 - .../site-packages/libpasteurize/__init__.py | 1 - .../__pycache__/__init__.cpython-39.pyc | Bin 194 -> 0 bytes .../__pycache__/main.cpython-39.pyc | Bin 5921 -> 0 bytes .../libpasteurize/fixes/__init__.py | 54 - .../fixes/__pycache__/__init__.cpython-39.pyc | Bin 863 -> 0 bytes .../__pycache__/feature_base.cpython-39.pyc | Bin 2663 -> 0 bytes ...ix_add_all__future__imports.cpython-39.pyc | Bin 1089 -> 0 bytes ...fix_add_all_future_builtins.cpython-39.pyc | Bin 1217 -> 0 bytes ...ure_standard_library_import.cpython-39.pyc | Bin 1018 -> 0 bytes .../fix_annotations.cpython-39.pyc | Bin 1710 -> 0 bytes .../__pycache__/fix_division.cpython-39.pyc | Bin 1376 -> 0 bytes .../__pycache__/fix_features.cpython-39.pyc | Bin 2283 -> 0 bytes .../fix_fullargspec.cpython-39.pyc | Bin 881 -> 0 bytes .../fix_future_builtins.cpython-39.pyc | Bin 1558 -> 0 bytes .../__pycache__/fix_getcwd.cpython-39.pyc | Bin 1143 -> 0 bytes .../__pycache__/fix_imports.cpython-39.pyc | Bin 3628 -> 0 bytes .../__pycache__/fix_imports2.cpython-39.pyc | Bin 6040 -> 0 bytes .../__pycache__/fix_kwargs.cpython-39.pyc | Bin 3788 -> 0 bytes .../__pycache__/fix_memoryview.cpython-39.pyc | Bin 950 -> 0 bytes .../__pycache__/fix_metaclass.cpython-39.pyc | Bin 2042 -> 0 bytes .../__pycache__/fix_newstyle.cpython-39.pyc | Bin 1235 -> 0 bytes .../fixes/__pycache__/fix_next.cpython-39.pyc | Bin 1564 -> 0 bytes .../fix_printfunction.cpython-39.pyc | Bin 823 -> 0 bytes .../__pycache__/fix_raise.cpython-39.pyc | Bin 1365 -> 0 bytes .../__pycache__/fix_raise_.cpython-39.pyc | Bin 1491 -> 0 bytes .../__pycache__/fix_throw.cpython-39.pyc | Bin 1224 -> 0 bytes .../__pycache__/fix_unpacking.cpython-39.pyc | Bin 5684 -> 0 bytes .../libpasteurize/fixes/feature_base.py | 57 - .../fixes/fix_add_all__future__imports.py | 24 - .../fixes/fix_add_all_future_builtins.py | 37 - .../fix_add_future_standard_library_import.py | 23 - .../libpasteurize/fixes/fix_annotations.py | 48 - .../libpasteurize/fixes/fix_division.py | 28 - .../libpasteurize/fixes/fix_features.py | 86 - .../libpasteurize/fixes/fix_fullargspec.py | 16 - .../fixes/fix_future_builtins.py | 46 - .../libpasteurize/fixes/fix_getcwd.py | 26 - .../libpasteurize/fixes/fix_imports.py | 112 - .../libpasteurize/fixes/fix_imports2.py | 174 - .../libpasteurize/fixes/fix_kwargs.py | 147 - .../libpasteurize/fixes/fix_memoryview.py | 21 - .../libpasteurize/fixes/fix_metaclass.py | 78 - .../libpasteurize/fixes/fix_newstyle.py | 33 - .../libpasteurize/fixes/fix_next.py | 43 - .../libpasteurize/fixes/fix_printfunction.py | 17 - .../libpasteurize/fixes/fix_raise.py | 25 - .../libpasteurize/fixes/fix_raise_.py | 35 - .../libpasteurize/fixes/fix_throw.py | 23 - .../libpasteurize/fixes/fix_unpacking.py | 120 - .../site-packages/libpasteurize/main.py | 204 - .../python3.9/site-packages/past/__init__.py | 90 - .../past/__pycache__/__init__.cpython-39.pyc | Bin 3128 -> 0 bytes .../site-packages/past/builtins/__init__.py | 72 - .../__pycache__/__init__.cpython-39.pyc | Bin 1643 -> 0 bytes .../builtins/__pycache__/misc.cpython-39.pyc | Bin 2385 -> 0 bytes .../__pycache__/noniterators.cpython-39.pyc | Bin 3292 -> 0 bytes .../site-packages/past/builtins/misc.py | 94 - .../past/builtins/noniterators.py | 272 - .../past/translation/__init__.py | 485 - .../__pycache__/__init__.cpython-39.pyc | Bin 11174 -> 0 bytes .../site-packages/past/types/__init__.py | 29 - .../types/__pycache__/__init__.cpython-39.pyc | Bin 906 -> 0 bytes .../__pycache__/basestring.cpython-39.pyc | Bin 1335 -> 0 bytes .../types/__pycache__/olddict.cpython-39.pyc | Bin 2392 -> 0 bytes .../types/__pycache__/oldstr.cpython-39.pyc | Bin 2935 -> 0 bytes .../site-packages/past/types/basestring.py | 39 - .../site-packages/past/types/olddict.py | 96 - .../site-packages/past/types/oldstr.py | 135 - .../site-packages/past/utils/__init__.py | 97 - .../utils/__pycache__/__init__.cpython-39.pyc | Bin 3073 -> 0 bytes .../pip-21.3.1.dist-info/INSTALLER | 1 - .../pip-21.3.1.dist-info/LICENSE.txt | 20 - .../pip-21.3.1.dist-info/METADATA | 93 - .../site-packages/pip-21.3.1.dist-info/RECORD | 819 -- .../pip-21.3.1.dist-info/REQUESTED | 0 .../site-packages/pip-21.3.1.dist-info/WHEEL | 5 - .../pip-21.3.1.dist-info/entry_points.txt | 5 - .../pip-21.3.1.dist-info/top_level.txt | 1 - .../python3.9/site-packages/pip/__init__.py | 13 - .../python3.9/site-packages/pip/__main__.py | 31 - .../pip/__pycache__/__init__.cpython-39.pyc | Bin 650 -> 0 bytes .../pip/__pycache__/__main__.cpython-39.pyc | Bin 606 -> 0 bytes .../site-packages/pip/_internal/__init__.py | 19 - .../__pycache__/__init__.cpython-39.pyc | Bin 771 -> 0 bytes .../__pycache__/build_env.cpython-39.pyc | Bin 9487 -> 0 bytes .../__pycache__/cache.cpython-39.pyc | Bin 8328 -> 0 bytes .../__pycache__/configuration.cpython-39.pyc | Bin 11144 -> 0 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 16390 -> 0 bytes .../_internal/__pycache__/main.cpython-39.pyc | Bin 626 -> 0 bytes .../__pycache__/pyproject.cpython-39.pyc | Bin 3710 -> 0 bytes .../self_outdated_check.cpython-39.pyc | Bin 4523 -> 0 bytes .../__pycache__/wheel_builder.cpython-39.pyc | Bin 9137 -> 0 bytes .../site-packages/pip/_internal/build_env.py | 293 - .../site-packages/pip/_internal/cache.py | 264 - .../pip/_internal/cli/__init__.py | 4 - .../cli/__pycache__/__init__.cpython-39.pyc | Bin 291 -> 0 bytes .../__pycache__/autocompletion.cpython-39.pyc | Bin 5148 -> 0 bytes .../__pycache__/base_command.cpython-39.pyc | Bin 6154 -> 0 bytes .../cli/__pycache__/cmdoptions.cpython-39.pyc | Bin 22584 -> 0 bytes .../command_context.cpython-39.pyc | Bin 1306 -> 0 bytes .../cli/__pycache__/main.cpython-39.pyc | Bin 1381 -> 0 bytes .../__pycache__/main_parser.cpython-39.pyc | Bin 2178 -> 0 bytes .../cli/__pycache__/parser.cpython-39.pyc | Bin 9962 -> 0 bytes .../__pycache__/progress_bars.cpython-39.pyc | Bin 7638 -> 0 bytes .../__pycache__/req_command.cpython-39.pyc | Bin 12373 -> 0 bytes .../cli/__pycache__/spinners.cpython-39.pyc | Bin 4961 -> 0 bytes .../__pycache__/status_codes.cpython-39.pyc | Bin 370 -> 0 bytes .../pip/_internal/cli/autocompletion.py | 163 - .../pip/_internal/cli/base_command.py | 214 - .../pip/_internal/cli/cmdoptions.py | 1010 -- .../pip/_internal/cli/command_context.py | 27 - .../site-packages/pip/_internal/cli/main.py | 70 - .../pip/_internal/cli/main_parser.py | 87 - .../site-packages/pip/_internal/cli/parser.py | 292 - .../pip/_internal/cli/progress_bars.py | 250 - .../pip/_internal/cli/req_command.py | 469 - .../pip/_internal/cli/spinners.py | 157 - .../pip/_internal/cli/status_codes.py | 6 - .../pip/_internal/commands/__init__.py | 127 - .../__pycache__/__init__.cpython-39.pyc | Bin 3068 -> 0 bytes .../commands/__pycache__/cache.cpython-39.pyc | Bin 6185 -> 0 bytes .../commands/__pycache__/check.cpython-39.pyc | Bin 1574 -> 0 bytes .../__pycache__/completion.cpython-39.pyc | Bin 3147 -> 0 bytes .../__pycache__/configuration.cpython-39.pyc | Bin 8328 -> 0 bytes .../commands/__pycache__/debug.cpython-39.pyc | Bin 6663 -> 0 bytes .../__pycache__/download.cpython-39.pyc | Bin 3969 -> 0 bytes .../__pycache__/freeze.cpython-39.pyc | Bin 2644 -> 0 bytes .../commands/__pycache__/hash.cpython-39.pyc | Bin 2140 -> 0 bytes .../commands/__pycache__/help.cpython-39.pyc | Bin 1312 -> 0 bytes .../commands/__pycache__/index.cpython-39.pyc | Bin 4528 -> 0 bytes .../__pycache__/install.cpython-39.pyc | Bin 17702 -> 0 bytes .../commands/__pycache__/list.cpython-39.pyc | Bin 10166 -> 0 bytes .../__pycache__/search.cpython-39.pyc | Bin 5330 -> 0 bytes .../commands/__pycache__/show.cpython-39.pyc | Bin 8475 -> 0 bytes .../__pycache__/uninstall.cpython-39.pyc | Bin 3108 -> 0 bytes .../commands/__pycache__/wheel.cpython-39.pyc | Bin 4836 -> 0 bytes .../pip/_internal/commands/cache.py | 223 - .../pip/_internal/commands/check.py | 53 - .../pip/_internal/commands/completion.py | 96 - .../pip/_internal/commands/configuration.py | 266 - .../pip/_internal/commands/debug.py | 202 - .../pip/_internal/commands/download.py | 139 - .../pip/_internal/commands/freeze.py | 97 - .../pip/_internal/commands/hash.py | 59 - .../pip/_internal/commands/help.py | 41 - .../pip/_internal/commands/index.py | 138 - .../pip/_internal/commands/install.py | 770 -- .../pip/_internal/commands/list.py | 361 - .../pip/_internal/commands/search.py | 174 - .../pip/_internal/commands/show.py | 235 - .../pip/_internal/commands/uninstall.py | 105 - .../pip/_internal/commands/wheel.py | 177 - .../pip/_internal/configuration.py | 367 - .../pip/_internal/distributions/__init__.py | 21 - .../__pycache__/__init__.cpython-39.pyc | Bin 814 -> 0 bytes .../__pycache__/base.cpython-39.pyc | Bin 1889 -> 0 bytes .../__pycache__/installed.cpython-39.pyc | Bin 1336 -> 0 bytes .../__pycache__/sdist.cpython-39.pyc | Bin 4662 -> 0 bytes .../__pycache__/wheel.cpython-39.pyc | Bin 1614 -> 0 bytes .../pip/_internal/distributions/base.py | 36 - .../pip/_internal/distributions/installed.py | 22 - .../pip/_internal/distributions/sdist.py | 129 - .../pip/_internal/distributions/wheel.py | 31 - .../site-packages/pip/_internal/exceptions.py | 402 - .../pip/_internal/index/__init__.py | 2 - .../index/__pycache__/__init__.cpython-39.pyc | Bin 245 -> 0 bytes .../__pycache__/collector.cpython-39.pyc | Bin 15899 -> 0 bytes .../__pycache__/package_finder.cpython-39.pyc | Bin 28114 -> 0 bytes .../index/__pycache__/sources.cpython-39.pyc | Bin 7210 -> 0 bytes .../pip/_internal/index/collector.py | 536 - .../pip/_internal/index/package_finder.py | 993 -- .../pip/_internal/index/sources.py | 224 - .../pip/_internal/locations/__init__.py | 446 - .../__pycache__/__init__.cpython-39.pyc | Bin 10625 -> 0 bytes .../__pycache__/_distutils.cpython-39.pyc | Bin 4684 -> 0 bytes .../__pycache__/_sysconfig.cpython-39.pyc | Bin 6275 -> 0 bytes .../locations/__pycache__/base.cpython-39.pyc | Bin 1553 -> 0 bytes .../pip/_internal/locations/_distutils.py | 169 - .../pip/_internal/locations/_sysconfig.py | 219 - .../pip/_internal/locations/base.py | 52 - .../site-packages/pip/_internal/main.py | 12 - .../pip/_internal/metadata/__init__.py | 51 - .../__pycache__/__init__.cpython-39.pyc | Bin 1940 -> 0 bytes .../metadata/__pycache__/base.cpython-39.pyc | Bin 13019 -> 0 bytes .../__pycache__/pkg_resources.cpython-39.pyc | Bin 6127 -> 0 bytes .../pip/_internal/metadata/base.py | 330 - .../pip/_internal/metadata/pkg_resources.py | 146 - .../pip/_internal/models/__init__.py | 2 - .../__pycache__/__init__.cpython-39.pyc | Bin 279 -> 0 bytes .../__pycache__/candidate.cpython-39.pyc | Bin 1462 -> 0 bytes .../__pycache__/direct_url.cpython-39.pyc | Bin 7252 -> 0 bytes .../__pycache__/format_control.cpython-39.pyc | Bin 2733 -> 0 bytes .../models/__pycache__/index.cpython-39.pyc | Bin 1249 -> 0 bytes .../models/__pycache__/link.cpython-39.pyc | Bin 10273 -> 0 bytes .../models/__pycache__/scheme.cpython-39.pyc | Bin 1025 -> 0 bytes .../__pycache__/search_scope.cpython-39.pyc | Bin 3494 -> 0 bytes .../selection_prefs.cpython-39.pyc | Bin 1681 -> 0 bytes .../__pycache__/target_python.cpython-39.pyc | Bin 3427 -> 0 bytes .../models/__pycache__/wheel.cpython-39.pyc | Bin 4351 -> 0 bytes .../pip/_internal/models/candidate.py | 34 - .../pip/_internal/models/direct_url.py | 220 - .../pip/_internal/models/format_control.py | 80 - .../pip/_internal/models/index.py | 28 - .../pip/_internal/models/link.py | 288 - .../pip/_internal/models/scheme.py | 31 - .../pip/_internal/models/search_scope.py | 129 - .../pip/_internal/models/selection_prefs.py | 51 - .../pip/_internal/models/target_python.py | 110 - .../pip/_internal/models/wheel.py | 89 - .../pip/_internal/network/__init__.py | 2 - .../__pycache__/__init__.cpython-39.pyc | Bin 267 -> 0 bytes .../network/__pycache__/auth.cpython-39.pyc | Bin 7499 -> 0 bytes .../network/__pycache__/cache.cpython-39.pyc | Bin 2909 -> 0 bytes .../__pycache__/download.cpython-39.pyc | Bin 5498 -> 0 bytes .../__pycache__/lazy_wheel.cpython-39.pyc | Bin 8399 -> 0 bytes .../__pycache__/session.cpython-39.pyc | Bin 10769 -> 0 bytes .../network/__pycache__/utils.cpython-39.pyc | Bin 1445 -> 0 bytes .../network/__pycache__/xmlrpc.cpython-39.pyc | Bin 2056 -> 0 bytes .../pip/_internal/network/auth.py | 323 - .../pip/_internal/network/cache.py | 69 - .../pip/_internal/network/download.py | 184 - .../pip/_internal/network/lazy_wheel.py | 210 - .../pip/_internal/network/session.py | 454 - .../pip/_internal/network/utils.py | 96 - .../pip/_internal/network/xmlrpc.py | 60 - .../pip/_internal/operations/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 215 -> 0 bytes .../__pycache__/check.cpython-39.pyc | Bin 4019 -> 0 bytes .../__pycache__/freeze.cpython-39.pyc | Bin 6151 -> 0 bytes .../__pycache__/prepare.cpython-39.pyc | Bin 14671 -> 0 bytes .../_internal/operations/build/__init__.py | 0 .../build/__pycache__/__init__.cpython-39.pyc | Bin 221 -> 0 bytes .../build/__pycache__/metadata.cpython-39.pyc | Bin 1205 -> 0 bytes .../metadata_editable.cpython-39.pyc | Bin 1243 -> 0 bytes .../metadata_legacy.cpython-39.pyc | Bin 2173 -> 0 bytes .../build/__pycache__/wheel.cpython-39.pyc | Bin 1216 -> 0 bytes .../__pycache__/wheel_editable.cpython-39.pyc | Bin 1432 -> 0 bytes .../__pycache__/wheel_legacy.cpython-39.pyc | Bin 2732 -> 0 bytes .../_internal/operations/build/metadata.py | 30 - .../operations/build/metadata_editable.py | 34 - .../operations/build/metadata_legacy.py | 67 - .../pip/_internal/operations/build/wheel.py | 37 - .../operations/build/wheel_editable.py | 46 - .../operations/build/wheel_legacy.py | 105 - .../pip/_internal/operations/check.py | 149 - .../pip/_internal/operations/freeze.py | 254 - .../_internal/operations/install/__init__.py | 2 - .../__pycache__/__init__.cpython-39.pyc | Bin 279 -> 0 bytes .../editable_legacy.cpython-39.pyc | Bin 1457 -> 0 bytes .../install/__pycache__/legacy.cpython-39.pyc | Bin 3517 -> 0 bytes .../install/__pycache__/wheel.cpython-39.pyc | Bin 20981 -> 0 bytes .../operations/install/editable_legacy.py | 46 - .../_internal/operations/install/legacy.py | 125 - .../pip/_internal/operations/install/wheel.py | 738 -- .../pip/_internal/operations/prepare.py | 632 - .../site-packages/pip/_internal/pyproject.py | 183 - .../pip/_internal/req/__init__.py | 94 - .../req/__pycache__/__init__.cpython-39.pyc | Bin 2574 -> 0 bytes .../__pycache__/constructors.cpython-39.pyc | Bin 11332 -> 0 bytes .../req/__pycache__/req_file.cpython-39.pyc | Bin 13392 -> 0 bytes .../__pycache__/req_install.cpython-39.pyc | Bin 22771 -> 0 bytes .../req/__pycache__/req_set.cpython-39.pyc | Bin 5912 -> 0 bytes .../__pycache__/req_tracker.cpython-39.pyc | Bin 4272 -> 0 bytes .../__pycache__/req_uninstall.cpython-39.pyc | Bin 18804 -> 0 bytes .../pip/_internal/req/constructors.py | 466 - .../pip/_internal/req/req_file.py | 536 - .../pip/_internal/req/req_install.py | 891 -- .../pip/_internal/req/req_set.py | 189 - .../pip/_internal/req/req_tracker.py | 124 - .../pip/_internal/req/req_uninstall.py | 633 - .../pip/_internal/resolution/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 215 -> 0 bytes .../__pycache__/base.cpython-39.pyc | Bin 1072 -> 0 bytes .../pip/_internal/resolution/base.py | 20 - .../_internal/resolution/legacy/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 212 -> 0 bytes .../__pycache__/resolver.cpython-39.pyc | Bin 12221 -> 0 bytes .../_internal/resolution/legacy/resolver.py | 467 - .../resolution/resolvelib/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 226 -> 0 bytes .../__pycache__/base.cpython-39.pyc | Bin 6623 -> 0 bytes .../__pycache__/candidates.cpython-39.pyc | Bin 18523 -> 0 bytes .../__pycache__/factory.cpython-39.pyc | Bin 18385 -> 0 bytes .../found_candidates.cpython-39.pyc | Bin 4857 -> 0 bytes .../__pycache__/provider.cpython-39.pyc | Bin 7128 -> 0 bytes .../__pycache__/reporter.cpython-39.pyc | Bin 3287 -> 0 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 7596 -> 0 bytes .../__pycache__/resolver.cpython-39.pyc | Bin 7312 -> 0 bytes .../_internal/resolution/resolvelib/base.py | 141 - .../resolution/resolvelib/candidates.py | 540 - .../resolution/resolvelib/factory.py | 701 -- .../resolution/resolvelib/found_candidates.py | 155 - .../resolution/resolvelib/provider.py | 215 - .../resolution/resolvelib/reporter.py | 68 - .../resolution/resolvelib/requirements.py | 166 - .../resolution/resolvelib/resolver.py | 251 - .../pip/_internal/self_outdated_check.py | 182 - .../pip/_internal/utils/__init__.py | 0 .../utils/__pycache__/__init__.cpython-39.pyc | Bin 210 -> 0 bytes .../utils/__pycache__/_log.cpython-39.pyc | Bin 1535 -> 0 bytes .../utils/__pycache__/appdirs.cpython-39.pyc | Bin 1641 -> 0 bytes .../utils/__pycache__/compat.cpython-39.pyc | Bin 1529 -> 0 bytes .../compatibility_tags.cpython-39.pyc | Bin 4081 -> 0 bytes .../utils/__pycache__/datetime.cpython-39.pyc | Bin 519 -> 0 bytes .../__pycache__/deprecation.cpython-39.pyc | Bin 3267 -> 0 bytes .../direct_url_helpers.cpython-39.pyc | Bin 2100 -> 0 bytes .../__pycache__/distutils_args.cpython-39.pyc | Bin 1115 -> 0 bytes .../utils/__pycache__/egg_link.cpython-39.pyc | Bin 2156 -> 0 bytes .../utils/__pycache__/encoding.cpython-39.pyc | Bin 1319 -> 0 bytes .../__pycache__/entrypoints.cpython-39.pyc | Bin 1310 -> 0 bytes .../__pycache__/filesystem.cpython-39.pyc | Bin 5156 -> 0 bytes .../__pycache__/filetypes.cpython-39.pyc | Bin 960 -> 0 bytes .../utils/__pycache__/glibc.cpython-39.pyc | Bin 1691 -> 0 bytes .../utils/__pycache__/hashes.cpython-39.pyc | Bin 5205 -> 0 bytes .../inject_securetransport.cpython-39.pyc | Bin 999 -> 0 bytes .../utils/__pycache__/logging.cpython-39.pyc | Bin 9305 -> 0 bytes .../utils/__pycache__/misc.cpython-39.pyc | Bin 20952 -> 0 bytes .../utils/__pycache__/models.cpython-39.pyc | Bin 2078 -> 0 bytes .../__pycache__/packaging.cpython-39.pyc | Bin 2654 -> 0 bytes .../utils/__pycache__/parallel.cpython-39.pyc | Bin 3225 -> 0 bytes .../__pycache__/pkg_resources.cpython-39.pyc | Bin 1887 -> 0 bytes .../setuptools_build.cpython-39.pyc | Bin 3449 -> 0 bytes .../__pycache__/subprocess.cpython-39.pyc | Bin 6262 -> 0 bytes .../utils/__pycache__/temp_dir.cpython-39.pyc | Bin 7277 -> 0 bytes .../__pycache__/unpacking.cpython-39.pyc | Bin 6741 -> 0 bytes .../utils/__pycache__/urls.cpython-39.pyc | Bin 1614 -> 0 bytes .../__pycache__/virtualenv.cpython-39.pyc | Bin 3304 -> 0 bytes .../utils/__pycache__/wheel.cpython-39.pyc | Bin 6293 -> 0 bytes .../site-packages/pip/_internal/utils/_log.py | 38 - .../pip/_internal/utils/appdirs.py | 52 - .../pip/_internal/utils/compat.py | 63 - .../pip/_internal/utils/compatibility_tags.py | 165 - .../pip/_internal/utils/datetime.py | 11 - .../pip/_internal/utils/deprecation.py | 120 - .../pip/_internal/utils/direct_url_helpers.py | 87 - .../pip/_internal/utils/distutils_args.py | 42 - .../pip/_internal/utils/egg_link.py | 75 - .../pip/_internal/utils/encoding.py | 36 - .../pip/_internal/utils/entrypoints.py | 27 - .../pip/_internal/utils/filesystem.py | 182 - .../pip/_internal/utils/filetypes.py | 27 - .../pip/_internal/utils/glibc.py | 88 - .../pip/_internal/utils/hashes.py | 144 - .../_internal/utils/inject_securetransport.py | 35 - .../pip/_internal/utils/logging.py | 358 - .../site-packages/pip/_internal/utils/misc.py | 689 - .../pip/_internal/utils/models.py | 39 - .../pip/_internal/utils/packaging.py | 84 - .../pip/_internal/utils/parallel.py | 103 - .../pip/_internal/utils/pkg_resources.py | 33 - .../pip/_internal/utils/setuptools_build.py | 167 - .../pip/_internal/utils/subprocess.py | 289 - .../pip/_internal/utils/temp_dir.py | 246 - .../pip/_internal/utils/unpacking.py | 258 - .../site-packages/pip/_internal/utils/urls.py | 62 - .../pip/_internal/utils/virtualenv.py | 104 - .../pip/_internal/utils/wheel.py | 182 - .../pip/_internal/vcs/__init__.py | 15 - .../vcs/__pycache__/__init__.cpython-39.pyc | Bin 533 -> 0 bytes .../vcs/__pycache__/bazaar.cpython-39.pyc | Bin 3243 -> 0 bytes .../vcs/__pycache__/git.cpython-39.pyc | Bin 12367 -> 0 bytes .../vcs/__pycache__/mercurial.cpython-39.pyc | Bin 4872 -> 0 bytes .../vcs/__pycache__/subversion.cpython-39.pyc | Bin 8405 -> 0 bytes .../__pycache__/versioncontrol.cpython-39.pyc | Bin 20905 -> 0 bytes .../site-packages/pip/_internal/vcs/bazaar.py | 93 - .../site-packages/pip/_internal/vcs/git.py | 513 - .../pip/_internal/vcs/mercurial.py | 153 - .../pip/_internal/vcs/subversion.py | 318 - .../pip/_internal/vcs/versioncontrol.py | 693 -- .../pip/_internal/wheel_builder.py | 375 - .../site-packages/pip/_vendor/__init__.py | 111 - .../__pycache__/__init__.cpython-39.pyc | Bin 2923 -> 0 bytes .../_vendor/__pycache__/distro.cpython-39.pyc | Bin 38385 -> 0 bytes .../__pycache__/pyparsing.cpython-39.pyc | Bin 240454 -> 0 bytes .../_vendor/__pycache__/six.cpython-39.pyc | Bin 27527 -> 0 bytes .../pip/_vendor/cachecontrol/__init__.py | 11 - .../__pycache__/__init__.cpython-39.pyc | Bin 568 -> 0 bytes .../__pycache__/_cmd.cpython-39.pyc | Bin 1579 -> 0 bytes .../__pycache__/adapter.cpython-39.pyc | Bin 3094 -> 0 bytes .../__pycache__/cache.cpython-39.pyc | Bin 1841 -> 0 bytes .../__pycache__/compat.cpython-39.pyc | Bin 765 -> 0 bytes .../__pycache__/controller.cpython-39.pyc | Bin 7782 -> 0 bytes .../__pycache__/filewrapper.cpython-39.pyc | Bin 2190 -> 0 bytes .../__pycache__/heuristics.cpython-39.pyc | Bin 4712 -> 0 bytes .../__pycache__/serialize.cpython-39.pyc | Bin 4241 -> 0 bytes .../__pycache__/wrapper.cpython-39.pyc | Bin 692 -> 0 bytes .../pip/_vendor/cachecontrol/_cmd.py | 57 - .../pip/_vendor/cachecontrol/adapter.py | 133 - .../pip/_vendor/cachecontrol/cache.py | 39 - .../_vendor/cachecontrol/caches/__init__.py | 2 - .../__pycache__/__init__.cpython-39.pyc | Bin 312 -> 0 bytes .../__pycache__/file_cache.cpython-39.pyc | Bin 3328 -> 0 bytes .../__pycache__/redis_cache.cpython-39.pyc | Bin 1584 -> 0 bytes .../_vendor/cachecontrol/caches/file_cache.py | 146 - .../cachecontrol/caches/redis_cache.py | 33 - .../pip/_vendor/cachecontrol/compat.py | 29 - .../pip/_vendor/cachecontrol/controller.py | 376 - .../pip/_vendor/cachecontrol/filewrapper.py | 80 - .../pip/_vendor/cachecontrol/heuristics.py | 135 - .../pip/_vendor/cachecontrol/serialize.py | 188 - .../pip/_vendor/cachecontrol/wrapper.py | 29 - .../pip/_vendor/certifi/__init__.py | 3 - .../pip/_vendor/certifi/__main__.py | 12 - .../__pycache__/__init__.cpython-39.pyc | Bin 294 -> 0 bytes .../__pycache__/__main__.cpython-39.pyc | Bin 461 -> 0 bytes .../certifi/__pycache__/core.cpython-39.pyc | Bin 1562 -> 0 bytes .../pip/_vendor/certifi/cacert.pem | 4257 ------- .../site-packages/pip/_vendor/certifi/core.py | 76 - .../pip/_vendor/chardet/__init__.py | 83 - .../__pycache__/__init__.cpython-39.pyc | Bin 1918 -> 0 bytes .../__pycache__/big5freq.cpython-39.pyc | Bin 27197 -> 0 bytes .../__pycache__/big5prober.cpython-39.pyc | Bin 1152 -> 0 bytes .../chardistribution.cpython-39.pyc | Bin 6238 -> 0 bytes .../charsetgroupprober.cpython-39.pyc | Bin 2279 -> 0 bytes .../__pycache__/charsetprober.cpython-39.pyc | Bin 3501 -> 0 bytes .../codingstatemachine.cpython-39.pyc | Bin 2928 -> 0 bytes .../chardet/__pycache__/compat.cpython-39.pyc | Bin 407 -> 0 bytes .../__pycache__/cp949prober.cpython-39.pyc | Bin 1159 -> 0 bytes .../chardet/__pycache__/enums.cpython-39.pyc | Bin 2666 -> 0 bytes .../__pycache__/escprober.cpython-39.pyc | Bin 2651 -> 0 bytes .../chardet/__pycache__/escsm.cpython-39.pyc | Bin 7100 -> 0 bytes .../__pycache__/eucjpprober.cpython-39.pyc | Bin 2465 -> 0 bytes .../__pycache__/euckrfreq.cpython-39.pyc | Bin 12081 -> 0 bytes .../__pycache__/euckrprober.cpython-39.pyc | Bin 1160 -> 0 bytes .../__pycache__/euctwfreq.cpython-39.pyc | Bin 27201 -> 0 bytes .../__pycache__/euctwprober.cpython-39.pyc | Bin 1160 -> 0 bytes .../__pycache__/gb2312freq.cpython-39.pyc | Bin 19125 -> 0 bytes .../__pycache__/gb2312prober.cpython-39.pyc | Bin 1168 -> 0 bytes .../__pycache__/hebrewprober.cpython-39.pyc | Bin 3037 -> 0 bytes .../__pycache__/jisfreq.cpython-39.pyc | Bin 22153 -> 0 bytes .../chardet/__pycache__/jpcntx.cpython-39.pyc | Bin 37626 -> 0 bytes .../langbulgarianmodel.cpython-39.pyc | Bin 21828 -> 0 bytes .../__pycache__/langgreekmodel.cpython-39.pyc | Bin 20504 -> 0 bytes .../langhebrewmodel.cpython-39.pyc | Bin 20572 -> 0 bytes .../langhungarianmodel.cpython-39.pyc | Bin 21763 -> 0 bytes .../langrussianmodel.cpython-39.pyc | Bin 26376 -> 0 bytes .../__pycache__/langthaimodel.cpython-39.pyc | Bin 20748 -> 0 bytes .../langturkishmodel.cpython-39.pyc | Bin 20588 -> 0 bytes .../__pycache__/latin1prober.cpython-39.pyc | Bin 2973 -> 0 bytes .../mbcharsetprober.cpython-39.pyc | Bin 2280 -> 0 bytes .../mbcsgroupprober.cpython-39.pyc | Bin 1149 -> 0 bytes .../chardet/__pycache__/mbcssm.cpython-39.pyc | Bin 15736 -> 0 bytes .../sbcharsetprober.cpython-39.pyc | Bin 3133 -> 0 bytes .../sbcsgroupprober.cpython-39.pyc | Bin 1718 -> 0 bytes .../__pycache__/sjisprober.cpython-39.pyc | Bin 2501 -> 0 bytes .../universaldetector.cpython-39.pyc | Bin 5849 -> 0 bytes .../__pycache__/utf8prober.cpython-39.pyc | Bin 2010 -> 0 bytes .../__pycache__/version.cpython-39.pyc | Bin 457 -> 0 bytes .../pip/_vendor/chardet/big5freq.py | 386 - .../pip/_vendor/chardet/big5prober.py | 47 - .../pip/_vendor/chardet/chardistribution.py | 233 - .../pip/_vendor/chardet/charsetgroupprober.py | 107 - .../pip/_vendor/chardet/charsetprober.py | 145 - .../pip/_vendor/chardet/cli/__init__.py | 1 - .../cli/__pycache__/__init__.cpython-39.pyc | Bin 204 -> 0 bytes .../cli/__pycache__/chardetect.cpython-39.pyc | Bin 2698 -> 0 bytes .../pip/_vendor/chardet/cli/chardetect.py | 84 - .../pip/_vendor/chardet/codingstatemachine.py | 88 - .../pip/_vendor/chardet/compat.py | 36 - .../pip/_vendor/chardet/cp949prober.py | 49 - .../pip/_vendor/chardet/enums.py | 76 - .../pip/_vendor/chardet/escprober.py | 101 - .../pip/_vendor/chardet/escsm.py | 246 - .../pip/_vendor/chardet/eucjpprober.py | 92 - .../pip/_vendor/chardet/euckrfreq.py | 195 - .../pip/_vendor/chardet/euckrprober.py | 47 - .../pip/_vendor/chardet/euctwfreq.py | 387 - .../pip/_vendor/chardet/euctwprober.py | 46 - .../pip/_vendor/chardet/gb2312freq.py | 283 - .../pip/_vendor/chardet/gb2312prober.py | 46 - .../pip/_vendor/chardet/hebrewprober.py | 292 - .../pip/_vendor/chardet/jisfreq.py | 325 - .../pip/_vendor/chardet/jpcntx.py | 233 - .../pip/_vendor/chardet/langbulgarianmodel.py | 4650 ------- .../pip/_vendor/chardet/langgreekmodel.py | 4398 ------- .../pip/_vendor/chardet/langhebrewmodel.py | 4383 ------- .../pip/_vendor/chardet/langhungarianmodel.py | 4650 ------- .../pip/_vendor/chardet/langrussianmodel.py | 5718 --------- .../pip/_vendor/chardet/langthaimodel.py | 4383 ------- .../pip/_vendor/chardet/langturkishmodel.py | 4383 ------- .../pip/_vendor/chardet/latin1prober.py | 145 - .../pip/_vendor/chardet/mbcharsetprober.py | 91 - .../pip/_vendor/chardet/mbcsgroupprober.py | 54 - .../pip/_vendor/chardet/mbcssm.py | 572 - .../pip/_vendor/chardet/metadata/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 209 -> 0 bytes .../__pycache__/languages.cpython-39.pyc | Bin 7940 -> 0 bytes .../pip/_vendor/chardet/metadata/languages.py | 310 - .../pip/_vendor/chardet/sbcharsetprober.py | 145 - .../pip/_vendor/chardet/sbcsgroupprober.py | 83 - .../pip/_vendor/chardet/sjisprober.py | 92 - .../pip/_vendor/chardet/universaldetector.py | 286 - .../pip/_vendor/chardet/utf8prober.py | 82 - .../pip/_vendor/chardet/version.py | 9 - .../pip/_vendor/colorama/__init__.py | 6 - .../__pycache__/__init__.cpython-39.pyc | Bin 462 -> 0 bytes .../colorama/__pycache__/ansi.cpython-39.pyc | Bin 3247 -> 0 bytes .../__pycache__/ansitowin32.cpython-39.pyc | Bin 7713 -> 0 bytes .../__pycache__/initialise.cpython-39.pyc | Bin 1729 -> 0 bytes .../colorama/__pycache__/win32.cpython-39.pyc | Bin 3961 -> 0 bytes .../__pycache__/winterm.cpython-39.pyc | Bin 4683 -> 0 bytes .../pip/_vendor/colorama/ansi.py | 102 - .../pip/_vendor/colorama/ansitowin32.py | 258 - .../pip/_vendor/colorama/initialise.py | 80 - .../pip/_vendor/colorama/win32.py | 152 - .../pip/_vendor/colorama/winterm.py | 169 - .../pip/_vendor/distlib/__init__.py | 23 - .../__pycache__/__init__.cpython-39.pyc | Bin 1075 -> 0 bytes .../distlib/__pycache__/compat.cpython-39.pyc | Bin 31840 -> 0 bytes .../__pycache__/database.cpython-39.pyc | Bin 42489 -> 0 bytes .../distlib/__pycache__/index.cpython-39.pyc | Bin 17298 -> 0 bytes .../__pycache__/locators.cpython-39.pyc | Bin 38262 -> 0 bytes .../__pycache__/manifest.cpython-39.pyc | Bin 10201 -> 0 bytes .../__pycache__/markers.cpython-39.pyc | Bin 4983 -> 0 bytes .../__pycache__/metadata.cpython-39.pyc | Bin 26596 -> 0 bytes .../__pycache__/resources.cpython-39.pyc | Bin 11029 -> 0 bytes .../__pycache__/scripts.cpython-39.pyc | Bin 11261 -> 0 bytes .../distlib/__pycache__/util.cpython-39.pyc | Bin 52624 -> 0 bytes .../__pycache__/version.cpython-39.pyc | Bin 20364 -> 0 bytes .../distlib/__pycache__/wheel.cpython-39.pyc | Bin 27177 -> 0 bytes .../pip/_vendor/distlib/_backport/__init__.py | 6 - .../__pycache__/__init__.cpython-39.pyc | Bin 492 -> 0 bytes .../_backport/__pycache__/misc.cpython-39.pyc | Bin 1112 -> 0 bytes .../__pycache__/shutil.cpython-39.pyc | Bin 21686 -> 0 bytes .../__pycache__/sysconfig.cpython-39.pyc | Bin 15976 -> 0 bytes .../__pycache__/tarfile.cpython-39.pyc | Bin 62740 -> 0 bytes .../pip/_vendor/distlib/_backport/misc.py | 41 - .../pip/_vendor/distlib/_backport/shutil.py | 764 -- .../_vendor/distlib/_backport/sysconfig.cfg | 84 - .../_vendor/distlib/_backport/sysconfig.py | 786 -- .../pip/_vendor/distlib/_backport/tarfile.py | 2607 ---- .../pip/_vendor/distlib/compat.py | 1122 -- .../pip/_vendor/distlib/database.py | 1339 -- .../pip/_vendor/distlib/index.py | 509 - .../pip/_vendor/distlib/locators.py | 1300 -- .../pip/_vendor/distlib/manifest.py | 393 - .../pip/_vendor/distlib/markers.py | 147 - .../pip/_vendor/distlib/metadata.py | 1058 -- .../pip/_vendor/distlib/resources.py | 358 - .../pip/_vendor/distlib/scripts.py | 429 - .../site-packages/pip/_vendor/distlib/t32.exe | Bin 96768 -> 0 bytes .../pip/_vendor/distlib/t64-arm.exe | Bin 180736 -> 0 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 105984 -> 0 bytes .../site-packages/pip/_vendor/distlib/util.py | 1969 --- .../pip/_vendor/distlib/version.py | 739 -- .../site-packages/pip/_vendor/distlib/w32.exe | Bin 90112 -> 0 bytes .../pip/_vendor/distlib/w64-arm.exe | Bin 166400 -> 0 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 99840 -> 0 bytes .../pip/_vendor/distlib/wheel.py | 1053 -- .../site-packages/pip/_vendor/distro.py | 1386 --- .../pip/_vendor/html5lib/__init__.py | 35 - .../__pycache__/__init__.cpython-39.pyc | Bin 1321 -> 0 bytes .../__pycache__/_ihatexml.cpython-39.pyc | Bin 13790 -> 0 bytes .../__pycache__/_inputstream.cpython-39.pyc | Bin 21649 -> 0 bytes .../__pycache__/_tokenizer.cpython-39.pyc | Bin 39744 -> 0 bytes .../__pycache__/_utils.cpython-39.pyc | Bin 4821 -> 0 bytes .../__pycache__/constants.cpython-39.pyc | Bin 66359 -> 0 bytes .../__pycache__/html5parser.cpython-39.pyc | Bin 91030 -> 0 bytes .../__pycache__/serializer.cpython-39.pyc | Bin 10832 -> 0 bytes .../pip/_vendor/html5lib/_ihatexml.py | 289 - .../pip/_vendor/html5lib/_inputstream.py | 918 -- .../pip/_vendor/html5lib/_tokenizer.py | 1735 --- .../pip/_vendor/html5lib/_trie/__init__.py | 5 - .../_trie/__pycache__/__init__.cpython-39.pyc | Bin 371 -> 0 bytes .../_trie/__pycache__/_base.cpython-39.pyc | Bin 1615 -> 0 bytes .../_trie/__pycache__/py.cpython-39.pyc | Bin 2276 -> 0 bytes .../pip/_vendor/html5lib/_trie/_base.py | 40 - .../pip/_vendor/html5lib/_trie/py.py | 67 - .../pip/_vendor/html5lib/_utils.py | 159 - .../pip/_vendor/html5lib/constants.py | 2946 ----- .../pip/_vendor/html5lib/filters/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 209 -> 0 bytes .../alphabeticalattributes.cpython-39.pyc | Bin 1331 -> 0 bytes .../filters/__pycache__/base.cpython-39.pyc | Bin 879 -> 0 bytes .../inject_meta_charset.cpython-39.pyc | Bin 1885 -> 0 bytes .../filters/__pycache__/lint.cpython-39.pyc | Bin 2627 -> 0 bytes .../__pycache__/optionaltags.cpython-39.pyc | Bin 2772 -> 0 bytes .../__pycache__/sanitizer.cpython-39.pyc | Bin 16895 -> 0 bytes .../__pycache__/whitespace.cpython-39.pyc | Bin 1377 -> 0 bytes .../filters/alphabeticalattributes.py | 29 - .../pip/_vendor/html5lib/filters/base.py | 12 - .../html5lib/filters/inject_meta_charset.py | 73 - .../pip/_vendor/html5lib/filters/lint.py | 93 - .../_vendor/html5lib/filters/optionaltags.py | 207 - .../pip/_vendor/html5lib/filters/sanitizer.py | 916 -- .../_vendor/html5lib/filters/whitespace.py | 38 - .../pip/_vendor/html5lib/html5parser.py | 2795 ----- .../pip/_vendor/html5lib/serializer.py | 409 - .../_vendor/html5lib/treeadapters/__init__.py | 30 - .../__pycache__/__init__.cpython-39.pyc | Bin 946 -> 0 bytes .../__pycache__/genshi.cpython-39.pyc | Bin 1554 -> 0 bytes .../__pycache__/sax.cpython-39.pyc | Bin 1473 -> 0 bytes .../_vendor/html5lib/treeadapters/genshi.py | 54 - .../pip/_vendor/html5lib/treeadapters/sax.py | 50 - .../_vendor/html5lib/treebuilders/__init__.py | 88 - .../__pycache__/__init__.cpython-39.pyc | Bin 3351 -> 0 bytes .../__pycache__/base.cpython-39.pyc | Bin 11335 -> 0 bytes .../__pycache__/dom.cpython-39.pyc | Bin 9462 -> 0 bytes .../__pycache__/etree.cpython-39.pyc | Bin 11840 -> 0 bytes .../__pycache__/etree_lxml.cpython-39.pyc | Bin 13013 -> 0 bytes .../pip/_vendor/html5lib/treebuilders/base.py | 417 - .../pip/_vendor/html5lib/treebuilders/dom.py | 239 - .../_vendor/html5lib/treebuilders/etree.py | 343 - .../html5lib/treebuilders/etree_lxml.py | 392 - .../_vendor/html5lib/treewalkers/__init__.py | 154 - .../__pycache__/__init__.cpython-39.pyc | Bin 4017 -> 0 bytes .../__pycache__/base.cpython-39.pyc | Bin 7006 -> 0 bytes .../__pycache__/dom.cpython-39.pyc | Bin 1741 -> 0 bytes .../__pycache__/etree.cpython-39.pyc | Bin 3503 -> 0 bytes .../__pycache__/etree_lxml.cpython-39.pyc | Bin 6640 -> 0 bytes .../__pycache__/genshi.cpython-39.pyc | Bin 1897 -> 0 bytes .../pip/_vendor/html5lib/treewalkers/base.py | 252 - .../pip/_vendor/html5lib/treewalkers/dom.py | 43 - .../pip/_vendor/html5lib/treewalkers/etree.py | 131 - .../html5lib/treewalkers/etree_lxml.py | 215 - .../_vendor/html5lib/treewalkers/genshi.py | 69 - .../pip/_vendor/idna/__init__.py | 44 - .../idna/__pycache__/__init__.cpython-39.pyc | Bin 866 -> 0 bytes .../idna/__pycache__/codec.cpython-39.pyc | Bin 2887 -> 0 bytes .../idna/__pycache__/compat.cpython-39.pyc | Bin 693 -> 0 bytes .../idna/__pycache__/core.cpython-39.pyc | Bin 9171 -> 0 bytes .../idna/__pycache__/idnadata.cpython-39.pyc | Bin 22157 -> 0 bytes .../idna/__pycache__/intranges.cpython-39.pyc | Bin 1879 -> 0 bytes .../__pycache__/package_data.cpython-39.pyc | Bin 230 -> 0 bytes .../idna/__pycache__/uts46data.cpython-39.pyc | Bin 146180 -> 0 bytes .../site-packages/pip/_vendor/idna/codec.py | 117 - .../site-packages/pip/_vendor/idna/compat.py | 16 - .../site-packages/pip/_vendor/idna/core.py | 409 - .../pip/_vendor/idna/idnadata.py | 2050 --- .../pip/_vendor/idna/intranges.py | 58 - .../pip/_vendor/idna/package_data.py | 2 - .../pip/_vendor/idna/uts46data.py | 8438 ------------- .../pip/_vendor/msgpack/__init__.py | 54 - .../__pycache__/__init__.cpython-39.pyc | Bin 1433 -> 0 bytes .../__pycache__/_version.cpython-39.pyc | Bin 237 -> 0 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 1871 -> 0 bytes .../msgpack/__pycache__/ext.cpython-39.pyc | Bin 6299 -> 0 bytes .../__pycache__/fallback.cpython-39.pyc | Bin 26745 -> 0 bytes .../pip/_vendor/msgpack/_version.py | 1 - .../pip/_vendor/msgpack/exceptions.py | 48 - .../site-packages/pip/_vendor/msgpack/ext.py | 193 - .../pip/_vendor/msgpack/fallback.py | 1087 -- .../pip/_vendor/packaging/__about__.py | 26 - .../pip/_vendor/packaging/__init__.py | 25 - .../__pycache__/__about__.cpython-39.pyc | Bin 609 -> 0 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 465 -> 0 bytes .../__pycache__/_manylinux.cpython-39.pyc | Bin 7313 -> 0 bytes .../__pycache__/_musllinux.cpython-39.pyc | Bin 4628 -> 0 bytes .../__pycache__/_structures.cpython-39.pyc | Bin 3101 -> 0 bytes .../__pycache__/markers.cpython-39.pyc | Bin 9472 -> 0 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 3993 -> 0 bytes .../__pycache__/specifiers.cpython-39.pyc | Bin 22229 -> 0 bytes .../packaging/__pycache__/tags.cpython-39.pyc | Bin 12308 -> 0 bytes .../__pycache__/utils.cpython-39.pyc | Bin 3630 -> 0 bytes .../__pycache__/version.cpython-39.pyc | Bin 13171 -> 0 bytes .../pip/_vendor/packaging/_manylinux.py | 301 - .../pip/_vendor/packaging/_musllinux.py | 136 - .../pip/_vendor/packaging/_structures.py | 67 - .../pip/_vendor/packaging/markers.py | 304 - .../pip/_vendor/packaging/requirements.py | 146 - .../pip/_vendor/packaging/specifiers.py | 828 -- .../pip/_vendor/packaging/tags.py | 484 - .../pip/_vendor/packaging/utils.py | 136 - .../pip/_vendor/packaging/version.py | 504 - .../pip/_vendor/pep517/__init__.py | 6 - .../__pycache__/__init__.cpython-39.pyc | Bin 332 -> 0 bytes .../pep517/__pycache__/build.cpython-39.pyc | Bin 3570 -> 0 bytes .../pep517/__pycache__/check.cpython-39.pyc | Bin 5117 -> 0 bytes .../__pycache__/colorlog.cpython-39.pyc | Bin 2947 -> 0 bytes .../pep517/__pycache__/compat.cpython-39.pyc | Bin 1548 -> 0 bytes .../__pycache__/dirtools.cpython-39.pyc | Bin 1356 -> 0 bytes .../__pycache__/envbuild.cpython-39.pyc | Bin 4516 -> 0 bytes .../pep517/__pycache__/meta.cpython-39.pyc | Bin 2929 -> 0 bytes .../__pycache__/wrappers.cpython-39.pyc | Bin 12512 -> 0 bytes .../site-packages/pip/_vendor/pep517/build.py | 127 - .../site-packages/pip/_vendor/pep517/check.py | 207 - .../pip/_vendor/pep517/colorlog.py | 115 - .../pip/_vendor/pep517/compat.py | 51 - .../pip/_vendor/pep517/dirtools.py | 44 - .../pip/_vendor/pep517/envbuild.py | 171 - .../pip/_vendor/pep517/in_process/__init__.py | 17 - .../__pycache__/__init__.cpython-39.pyc | Bin 934 -> 0 bytes .../__pycache__/_in_process.cpython-39.pyc | Bin 10278 -> 0 bytes .../_vendor/pep517/in_process/_in_process.py | 363 - .../site-packages/pip/_vendor/pep517/meta.py | 92 - .../pip/_vendor/pep517/wrappers.py | 375 - .../pip/_vendor/pkg_resources/__init__.py | 3296 ----- .../__pycache__/__init__.cpython-39.pyc | Bin 100355 -> 0 bytes .../__pycache__/py31compat.cpython-39.pyc | Bin 669 -> 0 bytes .../pip/_vendor/pkg_resources/py31compat.py | 23 - .../pip/_vendor/platformdirs/__init__.py | 329 - .../pip/_vendor/platformdirs/__main__.py | 44 - .../__pycache__/__init__.cpython-39.pyc | Bin 11093 -> 0 bytes .../__pycache__/__main__.cpython-39.pyc | Bin 1179 -> 0 bytes .../__pycache__/android.cpython-39.pyc | Bin 4334 -> 0 bytes .../__pycache__/api.cpython-39.pyc | Bin 5321 -> 0 bytes .../__pycache__/macos.cpython-39.pyc | Bin 3269 -> 0 bytes .../__pycache__/unix.cpython-39.pyc | Bin 7058 -> 0 bytes .../__pycache__/version.cpython-39.pyc | Bin 314 -> 0 bytes .../__pycache__/windows.cpython-39.pyc | Bin 6417 -> 0 bytes .../pip/_vendor/platformdirs/android.py | 117 - .../pip/_vendor/platformdirs/api.py | 155 - .../pip/_vendor/platformdirs/macos.py | 62 - .../pip/_vendor/platformdirs/unix.py | 180 - .../pip/_vendor/platformdirs/version.py | 4 - .../pip/_vendor/platformdirs/windows.py | 180 - .../pip/_vendor/progress/__init__.py | 189 - .../__pycache__/__init__.cpython-39.pyc | Bin 5701 -> 0 bytes .../progress/__pycache__/bar.cpython-39.pyc | Bin 2746 -> 0 bytes .../__pycache__/colors.cpython-39.pyc | Bin 1519 -> 0 bytes .../__pycache__/counter.cpython-39.pyc | Bin 1634 -> 0 bytes .../__pycache__/spinner.cpython-39.pyc | Bin 1470 -> 0 bytes .../site-packages/pip/_vendor/progress/bar.py | 93 - .../pip/_vendor/progress/colors.py | 79 - .../pip/_vendor/progress/counter.py | 47 - .../pip/_vendor/progress/spinner.py | 45 - .../site-packages/pip/_vendor/pyparsing.py | 7107 ----------- .../pip/_vendor/requests/__init__.py | 154 - .../__pycache__/__init__.cpython-39.pyc | Bin 4021 -> 0 bytes .../__pycache__/__version__.cpython-39.pyc | Bin 574 -> 0 bytes .../_internal_utils.cpython-39.pyc | Bin 1321 -> 0 bytes .../__pycache__/adapters.cpython-39.pyc | Bin 16992 -> 0 bytes .../requests/__pycache__/api.cpython-39.pyc | Bin 6738 -> 0 bytes .../requests/__pycache__/auth.cpython-39.pyc | Bin 8350 -> 0 bytes .../requests/__pycache__/certs.cpython-39.pyc | Bin 652 -> 0 bytes .../__pycache__/compat.cpython-39.pyc | Bin 1631 -> 0 bytes .../__pycache__/cookies.cpython-39.pyc | Bin 18841 -> 0 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 5424 -> 0 bytes .../requests/__pycache__/help.cpython-39.pyc | Bin 2902 -> 0 bytes .../requests/__pycache__/hooks.cpython-39.pyc | Bin 1009 -> 0 bytes .../__pycache__/models.cpython-39.pyc | Bin 24489 -> 0 bytes .../__pycache__/packages.cpython-39.pyc | Bin 521 -> 0 bytes .../__pycache__/sessions.cpython-39.pyc | Bin 19876 -> 0 bytes .../__pycache__/status_codes.cpython-39.pyc | Bin 4258 -> 0 bytes .../__pycache__/structures.cpython-39.pyc | Bin 4479 -> 0 bytes .../requests/__pycache__/utils.cpython-39.pyc | Bin 23343 -> 0 bytes .../pip/_vendor/requests/__version__.py | 14 - .../pip/_vendor/requests/_internal_utils.py | 42 - .../pip/_vendor/requests/adapters.py | 533 - .../site-packages/pip/_vendor/requests/api.py | 159 - .../pip/_vendor/requests/auth.py | 305 - .../pip/_vendor/requests/certs.py | 18 - .../pip/_vendor/requests/compat.py | 76 - .../pip/_vendor/requests/cookies.py | 549 - .../pip/_vendor/requests/exceptions.py | 127 - .../pip/_vendor/requests/help.py | 132 - .../pip/_vendor/requests/hooks.py | 34 - .../pip/_vendor/requests/models.py | 966 -- .../pip/_vendor/requests/packages.py | 16 - .../pip/_vendor/requests/sessions.py | 781 -- .../pip/_vendor/requests/status_codes.py | 123 - .../pip/_vendor/requests/structures.py | 105 - .../pip/_vendor/requests/utils.py | 1013 -- .../pip/_vendor/resolvelib/__init__.py | 26 - .../__pycache__/__init__.cpython-39.pyc | Bin 625 -> 0 bytes .../__pycache__/providers.cpython-39.pyc | Bin 6724 -> 0 bytes .../__pycache__/reporters.cpython-39.pyc | Bin 2321 -> 0 bytes .../__pycache__/resolvers.cpython-39.pyc | Bin 15278 -> 0 bytes .../__pycache__/structs.cpython-39.pyc | Bin 7298 -> 0 bytes .../pip/_vendor/resolvelib/compat/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 220 -> 0 bytes .../collections_abc.cpython-39.pyc | Bin 394 -> 0 bytes .../resolvelib/compat/collections_abc.py | 6 - .../pip/_vendor/resolvelib/providers.py | 133 - .../pip/_vendor/resolvelib/reporters.py | 37 - .../pip/_vendor/resolvelib/resolvers.py | 483 - .../pip/_vendor/resolvelib/structs.py | 165 - .../site-packages/pip/_vendor/six.py | 998 -- .../pip/_vendor/tenacity/__init__.py | 517 - .../__pycache__/__init__.cpython-39.pyc | Bin 16298 -> 0 bytes .../__pycache__/_asyncio.cpython-39.pyc | Bin 2613 -> 0 bytes .../__pycache__/_utils.cpython-39.pyc | Bin 1250 -> 0 bytes .../tenacity/__pycache__/after.cpython-39.pyc | Bin 1229 -> 0 bytes .../__pycache__/before.cpython-39.pyc | Bin 1117 -> 0 bytes .../__pycache__/before_sleep.cpython-39.pyc | Bin 1409 -> 0 bytes .../tenacity/__pycache__/nap.cpython-39.pyc | Bin 1211 -> 0 bytes .../tenacity/__pycache__/retry.cpython-39.pyc | Bin 8799 -> 0 bytes .../tenacity/__pycache__/stop.cpython-39.pyc | Bin 4263 -> 0 bytes .../__pycache__/tornadoweb.cpython-39.pyc | Bin 1753 -> 0 bytes .../tenacity/__pycache__/wait.cpython-39.pyc | Bin 7975 -> 0 bytes .../pip/_vendor/tenacity/_asyncio.py | 92 - .../pip/_vendor/tenacity/_utils.py | 68 - .../pip/_vendor/tenacity/after.py | 46 - .../pip/_vendor/tenacity/before.py | 41 - .../pip/_vendor/tenacity/before_sleep.py | 58 - .../site-packages/pip/_vendor/tenacity/nap.py | 43 - .../pip/_vendor/tenacity/retry.py | 213 - .../pip/_vendor/tenacity/stop.py | 96 - .../pip/_vendor/tenacity/tornadoweb.py | 59 - .../pip/_vendor/tenacity/wait.py | 191 - .../pip/_vendor/tomli/__init__.py | 6 - .../tomli/__pycache__/__init__.cpython-39.pyc | Bin 398 -> 0 bytes .../tomli/__pycache__/_parser.cpython-39.pyc | Bin 16373 -> 0 bytes .../tomli/__pycache__/_re.cpython-39.pyc | Bin 2448 -> 0 bytes .../pip/_vendor/tomli/_parser.py | 703 -- .../site-packages/pip/_vendor/tomli/_re.py | 83 - .../pip/_vendor/urllib3/__init__.py | 85 - .../__pycache__/__init__.cpython-39.pyc | Bin 2208 -> 0 bytes .../__pycache__/_collections.cpython-39.pyc | Bin 10803 -> 0 bytes .../__pycache__/_version.cpython-39.pyc | Bin 232 -> 0 bytes .../__pycache__/connection.cpython-39.pyc | Bin 13701 -> 0 bytes .../__pycache__/connectionpool.cpython-39.pyc | Bin 24728 -> 0 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 11665 -> 0 bytes .../urllib3/__pycache__/fields.cpython-39.pyc | Bin 8180 -> 0 bytes .../__pycache__/filepost.cpython-39.pyc | Bin 2781 -> 0 bytes .../__pycache__/poolmanager.cpython-39.pyc | Bin 15183 -> 0 bytes .../__pycache__/request.cpython-39.pyc | Bin 5644 -> 0 bytes .../__pycache__/response.cpython-39.pyc | Bin 20855 -> 0 bytes .../pip/_vendor/urllib3/_collections.py | 337 - .../pip/_vendor/urllib3/_version.py | 2 - .../pip/_vendor/urllib3/connection.py | 569 - .../pip/_vendor/urllib3/connectionpool.py | 1078 -- .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 218 -> 0 bytes .../_appengine_environ.cpython-39.pyc | Bin 1438 -> 0 bytes .../__pycache__/appengine.cpython-39.pyc | Bin 8281 -> 0 bytes .../__pycache__/ntlmpool.cpython-39.pyc | Bin 3635 -> 0 bytes .../__pycache__/pyopenssl.cpython-39.pyc | Bin 15612 -> 0 bytes .../securetransport.cpython-39.pyc | Bin 21934 -> 0 bytes .../contrib/__pycache__/socks.cpython-39.pyc | Bin 5654 -> 0 bytes .../urllib3/contrib/_appengine_environ.py | 36 - .../contrib/_securetransport/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 225 -> 0 bytes .../__pycache__/bindings.cpython-39.pyc | Bin 10732 -> 0 bytes .../__pycache__/low_level.cpython-39.pyc | Bin 9185 -> 0 bytes .../contrib/_securetransport/bindings.py | 519 - .../contrib/_securetransport/low_level.py | 397 - .../pip/_vendor/urllib3/contrib/appengine.py | 314 - .../pip/_vendor/urllib3/contrib/ntlmpool.py | 130 - .../pip/_vendor/urllib3/contrib/pyopenssl.py | 511 - .../urllib3/contrib/securetransport.py | 922 -- .../pip/_vendor/urllib3/contrib/socks.py | 216 - .../pip/_vendor/urllib3/exceptions.py | 323 - .../pip/_vendor/urllib3/fields.py | 274 - .../pip/_vendor/urllib3/filepost.py | 98 - .../pip/_vendor/urllib3/packages/__init__.py | 5 - .../__pycache__/__init__.cpython-39.pyc | Bin 332 -> 0 bytes .../packages/__pycache__/six.cpython-39.pyc | Bin 27602 -> 0 bytes .../urllib3/packages/backports/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 219 -> 0 bytes .../__pycache__/makefile.cpython-39.pyc | Bin 1317 -> 0 bytes .../urllib3/packages/backports/makefile.py | 51 - .../pip/_vendor/urllib3/packages/six.py | 1077 -- .../packages/ssl_match_hostname/__init__.py | 24 - .../__pycache__/__init__.cpython-39.pyc | Bin 591 -> 0 bytes .../_implementation.cpython-39.pyc | Bin 3312 -> 0 bytes .../ssl_match_hostname/_implementation.py | 160 - .../pip/_vendor/urllib3/poolmanager.py | 536 - .../pip/_vendor/urllib3/request.py | 170 - .../pip/_vendor/urllib3/response.py | 821 -- .../pip/_vendor/urllib3/util/__init__.py | 49 - .../util/__pycache__/__init__.cpython-39.pyc | Bin 1128 -> 0 bytes .../__pycache__/connection.cpython-39.pyc | Bin 3480 -> 0 bytes .../util/__pycache__/proxy.cpython-39.pyc | Bin 1364 -> 0 bytes .../util/__pycache__/queue.cpython-39.pyc | Bin 1083 -> 0 bytes .../util/__pycache__/request.cpython-39.pyc | Bin 3471 -> 0 bytes .../util/__pycache__/response.cpython-39.pyc | Bin 2368 -> 0 bytes .../util/__pycache__/retry.cpython-39.pyc | Bin 15855 -> 0 bytes .../util/__pycache__/ssl_.cpython-39.pyc | Bin 11352 -> 0 bytes .../__pycache__/ssltransport.cpython-39.pyc | Bin 7533 -> 0 bytes .../util/__pycache__/timeout.cpython-39.pyc | Bin 8966 -> 0 bytes .../util/__pycache__/url.cpython-39.pyc | Bin 10691 -> 0 bytes .../util/__pycache__/wait.cpython-39.pyc | Bin 3151 -> 0 bytes .../pip/_vendor/urllib3/util/connection.py | 150 - .../pip/_vendor/urllib3/util/proxy.py | 57 - .../pip/_vendor/urllib3/util/queue.py | 22 - .../pip/_vendor/urllib3/util/request.py | 143 - .../pip/_vendor/urllib3/util/response.py | 107 - .../pip/_vendor/urllib3/util/retry.py | 602 - .../pip/_vendor/urllib3/util/ssl_.py | 495 - .../pip/_vendor/urllib3/util/ssltransport.py | 221 - .../pip/_vendor/urllib3/util/timeout.py | 268 - .../pip/_vendor/urllib3/util/url.py | 432 - .../pip/_vendor/urllib3/util/wait.py | 153 - .../site-packages/pip/_vendor/vendor.txt | 22 - .../pip/_vendor/webencodings/__init__.py | 342 - .../__pycache__/__init__.cpython-39.pyc | Bin 9742 -> 0 bytes .../__pycache__/labels.cpython-39.pyc | Bin 3856 -> 0 bytes .../__pycache__/mklabels.cpython-39.pyc | Bin 1916 -> 0 bytes .../__pycache__/tests.cpython-39.pyc | Bin 5080 -> 0 bytes .../__pycache__/x_user_defined.cpython-39.pyc | Bin 2676 -> 0 bytes .../pip/_vendor/webencodings/labels.py | 231 - .../pip/_vendor/webencodings/mklabels.py | 59 - .../pip/_vendor/webencodings/tests.py | 153 - .../_vendor/webencodings/x_user_defined.py | 325 - .../lib/python3.9/site-packages/pip/py.typed | 4 - .../site-packages/pkg_resources/__init__.py | 3288 ----- .../__pycache__/__init__.cpython-39.pyc | Bin 100448 -> 0 bytes .../pkg_resources/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 202 -> 0 bytes .../__pycache__/appdirs.cpython-39.pyc | Bin 20519 -> 0 bytes .../__pycache__/pyparsing.cpython-39.pyc | Bin 201356 -> 0 bytes .../pkg_resources/_vendor/appdirs.py | 608 - .../_vendor/packaging/__about__.py | 27 - .../_vendor/packaging/__init__.py | 26 - .../__pycache__/__about__.cpython-39.pyc | Bin 718 -> 0 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 564 -> 0 bytes .../__pycache__/_compat.cpython-39.pyc | Bin 1162 -> 0 bytes .../__pycache__/_structures.cpython-39.pyc | Bin 2916 -> 0 bytes .../__pycache__/_typing.cpython-39.pyc | Bin 1507 -> 0 bytes .../__pycache__/markers.cpython-39.pyc | Bin 9323 -> 0 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 4100 -> 0 bytes .../__pycache__/specifiers.cpython-39.pyc | Bin 20599 -> 0 bytes .../packaging/__pycache__/tags.cpython-39.pyc | Bin 17278 -> 0 bytes .../__pycache__/utils.cpython-39.pyc | Bin 1669 -> 0 bytes .../__pycache__/version.cpython-39.pyc | Bin 13336 -> 0 bytes .../_vendor/packaging/_compat.py | 38 - .../_vendor/packaging/_structures.py | 86 - .../_vendor/packaging/_typing.py | 48 - .../_vendor/packaging/markers.py | 328 - .../_vendor/packaging/requirements.py | 145 - .../_vendor/packaging/specifiers.py | 863 -- .../pkg_resources/_vendor/packaging/tags.py | 751 -- .../pkg_resources/_vendor/packaging/utils.py | 65 - .../_vendor/packaging/version.py | 535 - .../pkg_resources/_vendor/pyparsing.py | 5742 --------- .../pkg_resources/extern/__init__.py | 73 - .../__pycache__/__init__.cpython-39.pyc | Bin 2891 -> 0 bytes .../__pycache__/setup.cpython-39.pyc | Bin 330 -> 0 bytes .../data/my-test-package-source/setup.py | 6 - .../pyparsing-2.4.7.dist-info/INSTALLER | 1 - .../pyparsing-2.4.7.dist-info/LICENSE | 18 - .../pyparsing-2.4.7.dist-info/METADATA | 104 - .../pyparsing-2.4.7.dist-info/RECORD | 8 - .../pyparsing-2.4.7.dist-info/WHEEL | 6 - .../pyparsing-2.4.7.dist-info/top_level.txt | 1 - .../lib/python3.9/site-packages/pyparsing.py | 7107 ----------- .../requests-2.26.0.dist-info/INSTALLER | 1 - .../requests-2.26.0.dist-info/LICENSE | 175 - .../requests-2.26.0.dist-info/METADATA | 120 - .../requests-2.26.0.dist-info/RECORD | 43 - .../requests-2.26.0.dist-info/REQUESTED | 0 .../requests-2.26.0.dist-info/WHEEL | 6 - .../requests-2.26.0.dist-info/top_level.txt | 1 - .../site-packages/requests/__init__.py | 152 - .../__pycache__/__init__.cpython-39.pyc | Bin 3874 -> 0 bytes .../__pycache__/__version__.cpython-39.pyc | Bin 552 -> 0 bytes .../_internal_utils.cpython-39.pyc | Bin 1299 -> 0 bytes .../__pycache__/adapters.cpython-39.pyc | Bin 16898 -> 0 bytes .../requests/__pycache__/api.cpython-39.pyc | Bin 6716 -> 0 bytes .../requests/__pycache__/auth.cpython-39.pyc | Bin 8328 -> 0 bytes .../requests/__pycache__/certs.cpython-39.pyc | Bin 618 -> 0 bytes .../__pycache__/compat.cpython-39.pyc | Bin 1708 -> 0 bytes .../__pycache__/cookies.cpython-39.pyc | Bin 18819 -> 0 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 5390 -> 0 bytes .../requests/__pycache__/help.cpython-39.pyc | Bin 2870 -> 0 bytes .../requests/__pycache__/hooks.cpython-39.pyc | Bin 987 -> 0 bytes .../__pycache__/models.cpython-39.pyc | Bin 24395 -> 0 bytes .../__pycache__/packages.cpython-39.pyc | Bin 694 -> 0 bytes .../__pycache__/sessions.cpython-39.pyc | Bin 19854 -> 0 bytes .../__pycache__/status_codes.cpython-39.pyc | Bin 4236 -> 0 bytes .../__pycache__/structures.cpython-39.pyc | Bin 4457 -> 0 bytes .../requests/__pycache__/utils.cpython-39.pyc | Bin 23309 -> 0 bytes .../site-packages/requests/__version__.py | 14 - .../site-packages/requests/_internal_utils.py | 42 - .../site-packages/requests/adapters.py | 533 - .../python3.9/site-packages/requests/api.py | 159 - .../python3.9/site-packages/requests/auth.py | 305 - .../python3.9/site-packages/requests/certs.py | 18 - .../site-packages/requests/compat.py | 75 - .../site-packages/requests/cookies.py | 549 - .../site-packages/requests/exceptions.py | 127 - .../python3.9/site-packages/requests/help.py | 135 - .../python3.9/site-packages/requests/hooks.py | 34 - .../site-packages/requests/models.py | 966 -- .../site-packages/requests/packages.py | 26 - .../site-packages/requests/sessions.py | 781 -- .../site-packages/requests/status_codes.py | 123 - .../site-packages/requests/structures.py | 105 - .../python3.9/site-packages/requests/utils.py | 1013 -- .../setuptools-57.4.0.dist-info/INSTALLER | 1 - .../setuptools-57.4.0.dist-info/LICENSE | 19 - .../setuptools-57.4.0.dist-info/METADATA | 119 - .../setuptools-57.4.0.dist-info/RECORD | 298 - .../setuptools-57.4.0.dist-info/REQUESTED | 0 .../setuptools-57.4.0.dist-info/WHEEL | 5 - .../entry_points.txt | 60 - .../setuptools-57.4.0.dist-info/top_level.txt | 3 - .../site-packages/setuptools/__init__.py | 241 - .../__pycache__/__init__.cpython-39.pyc | Bin 8635 -> 0 bytes .../_deprecation_warning.cpython-39.pyc | Bin 561 -> 0 bytes .../__pycache__/_imp.cpython-39.pyc | Bin 2096 -> 0 bytes .../__pycache__/archive_util.cpython-39.pyc | Bin 5825 -> 0 bytes .../__pycache__/build_meta.cpython-39.pyc | Bin 9084 -> 0 bytes .../__pycache__/config.cpython-39.pyc | Bin 19974 -> 0 bytes .../__pycache__/dep_util.cpython-39.pyc | Bin 868 -> 0 bytes .../__pycache__/depends.cpython-39.pyc | Bin 5260 -> 0 bytes .../__pycache__/dist.cpython-39.pyc | Bin 36255 -> 0 bytes .../__pycache__/errors.cpython-39.pyc | Bin 861 -> 0 bytes .../__pycache__/extension.cpython-39.pyc | Bin 1955 -> 0 bytes .../__pycache__/glob.cpython-39.pyc | Bin 3705 -> 0 bytes .../__pycache__/installer.cpython-39.pyc | Bin 2782 -> 0 bytes .../__pycache__/launch.cpython-39.pyc | Bin 912 -> 0 bytes .../__pycache__/lib2to3_ex.cpython-39.pyc | Bin 2714 -> 0 bytes .../__pycache__/monkey.cpython-39.pyc | Bin 4624 -> 0 bytes .../__pycache__/msvc.cpython-39.pyc | Bin 42850 -> 0 bytes .../__pycache__/namespaces.cpython-39.pyc | Bin 3611 -> 0 bytes .../__pycache__/package_index.cpython-39.pyc | Bin 32647 -> 0 bytes .../__pycache__/py34compat.cpython-39.pyc | Bin 491 -> 0 bytes .../__pycache__/sandbox.cpython-39.pyc | Bin 15785 -> 0 bytes .../__pycache__/unicode_utils.cpython-39.pyc | Bin 1125 -> 0 bytes .../__pycache__/version.cpython-39.pyc | Bin 335 -> 0 bytes .../__pycache__/wheel.cpython-39.pyc | Bin 7291 -> 0 bytes .../windows_support.cpython-39.pyc | Bin 1034 -> 0 bytes .../setuptools/_deprecation_warning.py | 7 - .../setuptools/_distutils/__init__.py | 15 - .../__pycache__/__init__.cpython-39.pyc | Bin 469 -> 0 bytes .../__pycache__/_msvccompiler.cpython-39.pyc | Bin 13824 -> 0 bytes .../__pycache__/archive_util.cpython-39.pyc | Bin 6656 -> 0 bytes .../__pycache__/bcppcompiler.cpython-39.pyc | Bin 6567 -> 0 bytes .../__pycache__/ccompiler.cpython-39.pyc | Bin 33435 -> 0 bytes .../_distutils/__pycache__/cmd.cpython-39.pyc | Bin 13995 -> 0 bytes .../__pycache__/config.cpython-39.pyc | Bin 3598 -> 0 bytes .../__pycache__/core.cpython-39.pyc | Bin 6723 -> 0 bytes .../cygwinccompiler.cpython-39.pyc | Bin 8803 -> 0 bytes .../__pycache__/debug.cpython-39.pyc | Bin 265 -> 0 bytes .../__pycache__/dep_util.cpython-39.pyc | Bin 2785 -> 0 bytes .../__pycache__/dir_util.cpython-39.pyc | Bin 5886 -> 0 bytes .../__pycache__/dist.cpython-39.pyc | Bin 34456 -> 0 bytes .../__pycache__/errors.cpython-39.pyc | Bin 5321 -> 0 bytes .../__pycache__/extension.cpython-39.pyc | Bin 6986 -> 0 bytes .../__pycache__/fancy_getopt.cpython-39.pyc | Bin 10694 -> 0 bytes .../__pycache__/file_util.cpython-39.pyc | Bin 6052 -> 0 bytes .../__pycache__/filelist.cpython-39.pyc | Bin 10844 -> 0 bytes .../_distutils/__pycache__/log.cpython-39.pyc | Bin 2384 -> 0 bytes .../__pycache__/msvc9compiler.cpython-39.pyc | Bin 17581 -> 0 bytes .../__pycache__/msvccompiler.cpython-39.pyc | Bin 14776 -> 0 bytes .../__pycache__/py35compat.cpython-39.pyc | Bin 641 -> 0 bytes .../__pycache__/py38compat.cpython-39.pyc | Bin 436 -> 0 bytes .../__pycache__/spawn.cpython-39.pyc | Bin 2912 -> 0 bytes .../__pycache__/sysconfig.cpython-39.pyc | Bin 12418 -> 0 bytes .../__pycache__/text_file.cpython-39.pyc | Bin 8510 -> 0 bytes .../__pycache__/unixccompiler.cpython-39.pyc | Bin 6849 -> 0 bytes .../__pycache__/util.cpython-39.pyc | Bin 17348 -> 0 bytes .../__pycache__/version.cpython-39.pyc | Bin 7410 -> 0 bytes .../versionpredicate.cpython-39.pyc | Bin 5194 -> 0 bytes .../setuptools/_distutils/_msvccompiler.py | 561 - .../setuptools/_distutils/archive_util.py | 256 - .../setuptools/_distutils/bcppcompiler.py | 393 - .../setuptools/_distutils/ccompiler.py | 1123 -- .../setuptools/_distutils/cmd.py | 403 - .../setuptools/_distutils/command/__init__.py | 31 - .../__pycache__/__init__.cpython-39.pyc | Bin 544 -> 0 bytes .../command/__pycache__/bdist.cpython-39.pyc | Bin 3674 -> 0 bytes .../__pycache__/bdist_dumb.cpython-39.pyc | Bin 3657 -> 0 bytes .../__pycache__/bdist_msi.cpython-39.pyc | Bin 19839 -> 0 bytes .../__pycache__/bdist_rpm.cpython-39.pyc | Bin 12294 -> 0 bytes .../__pycache__/bdist_wininst.cpython-39.pyc | Bin 8614 -> 0 bytes .../command/__pycache__/build.cpython-39.pyc | Bin 3946 -> 0 bytes .../__pycache__/build_clib.cpython-39.pyc | Bin 4869 -> 0 bytes .../__pycache__/build_ext.cpython-39.pyc | Bin 16328 -> 0 bytes .../__pycache__/build_py.cpython-39.pyc | Bin 10502 -> 0 bytes .../__pycache__/build_scripts.cpython-39.pyc | Bin 4399 -> 0 bytes .../command/__pycache__/check.cpython-39.pyc | Bin 4978 -> 0 bytes .../command/__pycache__/clean.cpython-39.pyc | Bin 2151 -> 0 bytes .../command/__pycache__/config.cpython-39.pyc | Bin 10281 -> 0 bytes .../__pycache__/install.cpython-39.pyc | Bin 13862 -> 0 bytes .../__pycache__/install_data.cpython-39.pyc | Bin 2354 -> 0 bytes .../install_egg_info.cpython-39.pyc | Bin 3089 -> 0 bytes .../install_headers.cpython-39.pyc | Bin 1779 -> 0 bytes .../__pycache__/install_lib.cpython-39.pyc | Bin 5151 -> 0 bytes .../install_scripts.cpython-39.pyc | Bin 2202 -> 0 bytes .../__pycache__/py37compat.cpython-39.pyc | Bin 1045 -> 0 bytes .../__pycache__/register.cpython-39.pyc | Bin 8522 -> 0 bytes .../command/__pycache__/sdist.cpython-39.pyc | Bin 14549 -> 0 bytes .../command/__pycache__/upload.cpython-39.pyc | Bin 5272 -> 0 bytes .../setuptools/_distutils/command/bdist.py | 143 - .../_distutils/command/bdist_dumb.py | 123 - .../_distutils/command/bdist_msi.py | 749 -- .../_distutils/command/bdist_rpm.py | 579 - .../_distutils/command/bdist_wininst.py | 377 - .../setuptools/_distutils/command/build.py | 157 - .../_distutils/command/build_clib.py | 209 - .../_distutils/command/build_ext.py | 757 -- .../setuptools/_distutils/command/build_py.py | 416 - .../_distutils/command/build_scripts.py | 160 - .../setuptools/_distutils/command/check.py | 148 - .../setuptools/_distutils/command/clean.py | 76 - .../setuptools/_distutils/command/config.py | 344 - .../setuptools/_distutils/command/install.py | 677 - .../_distutils/command/install_data.py | 79 - .../_distutils/command/install_egg_info.py | 77 - .../_distutils/command/install_headers.py | 47 - .../_distutils/command/install_lib.py | 217 - .../_distutils/command/install_scripts.py | 60 - .../_distutils/command/py37compat.py | 30 - .../setuptools/_distutils/command/register.py | 304 - .../setuptools/_distutils/command/sdist.py | 494 - .../setuptools/_distutils/command/upload.py | 214 - .../setuptools/_distutils/config.py | 130 - .../setuptools/_distutils/core.py | 234 - .../setuptools/_distutils/cygwinccompiler.py | 414 - .../setuptools/_distutils/debug.py | 5 - .../setuptools/_distutils/dep_util.py | 92 - .../setuptools/_distutils/dir_util.py | 210 - .../setuptools/_distutils/dist.py | 1257 -- .../setuptools/_distutils/errors.py | 97 - .../setuptools/_distutils/extension.py | 240 - .../setuptools/_distutils/fancy_getopt.py | 457 - .../setuptools/_distutils/file_util.py | 238 - .../setuptools/_distutils/filelist.py | 355 - .../setuptools/_distutils/log.py | 77 - .../setuptools/_distutils/msvc9compiler.py | 788 -- .../setuptools/_distutils/msvccompiler.py | 643 - .../setuptools/_distutils/py35compat.py | 19 - .../setuptools/_distutils/py38compat.py | 7 - .../setuptools/_distutils/spawn.py | 106 - .../setuptools/_distutils/sysconfig.py | 573 - .../setuptools/_distutils/text_file.py | 286 - .../setuptools/_distutils/unixccompiler.py | 332 - .../setuptools/_distutils/util.py | 616 - .../setuptools/_distutils/version.py | 347 - .../setuptools/_distutils/versionpredicate.py | 166 - .../site-packages/setuptools/_imp.py | 82 - .../setuptools/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 199 -> 0 bytes .../__pycache__/ordered_set.cpython-39.pyc | Bin 16393 -> 0 bytes .../__pycache__/pyparsing.cpython-39.pyc | Bin 201353 -> 0 bytes .../_vendor/more_itertools/__init__.py | 4 - .../__pycache__/__init__.cpython-39.pyc | Bin 280 -> 0 bytes .../__pycache__/more.cpython-39.pyc | Bin 110025 -> 0 bytes .../__pycache__/recipes.cpython-39.pyc | Bin 17945 -> 0 bytes .../setuptools/_vendor/more_itertools/more.py | 3825 ------ .../_vendor/more_itertools/recipes.py | 620 - .../setuptools/_vendor/ordered_set.py | 488 - .../setuptools/_vendor/packaging/__about__.py | 27 - .../setuptools/_vendor/packaging/__init__.py | 26 - .../__pycache__/__about__.cpython-39.pyc | Bin 715 -> 0 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 561 -> 0 bytes .../__pycache__/_compat.cpython-39.pyc | Bin 1159 -> 0 bytes .../__pycache__/_structures.cpython-39.pyc | Bin 2913 -> 0 bytes .../__pycache__/_typing.cpython-39.pyc | Bin 1504 -> 0 bytes .../__pycache__/markers.cpython-39.pyc | Bin 9317 -> 0 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 4094 -> 0 bytes .../__pycache__/specifiers.cpython-39.pyc | Bin 20596 -> 0 bytes .../packaging/__pycache__/tags.cpython-39.pyc | Bin 17275 -> 0 bytes .../__pycache__/utils.cpython-39.pyc | Bin 1666 -> 0 bytes .../__pycache__/version.cpython-39.pyc | Bin 13333 -> 0 bytes .../setuptools/_vendor/packaging/_compat.py | 38 - .../_vendor/packaging/_structures.py | 86 - .../setuptools/_vendor/packaging/_typing.py | 48 - .../setuptools/_vendor/packaging/markers.py | 328 - .../_vendor/packaging/requirements.py | 145 - .../_vendor/packaging/specifiers.py | 863 -- .../setuptools/_vendor/packaging/tags.py | 751 -- .../setuptools/_vendor/packaging/utils.py | 65 - .../setuptools/_vendor/packaging/version.py | 535 - .../setuptools/_vendor/pyparsing.py | 5742 --------- .../site-packages/setuptools/archive_util.py | 205 - .../site-packages/setuptools/build_meta.py | 281 - .../site-packages/setuptools/cli-32.exe | Bin 65536 -> 0 bytes .../site-packages/setuptools/cli-64.exe | Bin 74752 -> 0 bytes .../site-packages/setuptools/cli.exe | Bin 65536 -> 0 bytes .../setuptools/command/__init__.py | 8 - .../__pycache__/__init__.cpython-39.pyc | Bin 388 -> 0 bytes .../command/__pycache__/alias.cpython-39.pyc | Bin 2382 -> 0 bytes .../__pycache__/bdist_egg.cpython-39.pyc | Bin 13048 -> 0 bytes .../__pycache__/bdist_rpm.cpython-39.pyc | Bin 1368 -> 0 bytes .../__pycache__/build_clib.cpython-39.pyc | Bin 2479 -> 0 bytes .../__pycache__/build_ext.cpython-39.pyc | Bin 9838 -> 0 bytes .../__pycache__/build_py.cpython-39.pyc | Bin 8393 -> 0 bytes .../__pycache__/develop.cpython-39.pyc | Bin 6467 -> 0 bytes .../__pycache__/dist_info.cpython-39.pyc | Bin 1406 -> 0 bytes .../__pycache__/easy_install.cpython-39.pyc | Bin 63563 -> 0 bytes .../__pycache__/egg_info.cpython-39.pyc | Bin 21989 -> 0 bytes .../__pycache__/install.cpython-39.pyc | Bin 4047 -> 0 bytes .../install_egg_info.cpython-39.pyc | Bin 2438 -> 0 bytes .../__pycache__/install_lib.cpython-39.pyc | Bin 4145 -> 0 bytes .../install_scripts.cpython-39.pyc | Bin 2433 -> 0 bytes .../__pycache__/py36compat.cpython-39.pyc | Bin 4600 -> 0 bytes .../__pycache__/register.cpython-39.pyc | Bin 856 -> 0 bytes .../command/__pycache__/rotate.cpython-39.pyc | Bin 2515 -> 0 bytes .../__pycache__/saveopts.cpython-39.pyc | Bin 934 -> 0 bytes .../command/__pycache__/sdist.cpython-39.pyc | Bin 6475 -> 0 bytes .../command/__pycache__/setopt.cpython-39.pyc | Bin 4546 -> 0 bytes .../command/__pycache__/test.cpython-39.pyc | Bin 8532 -> 0 bytes .../command/__pycache__/upload.cpython-39.pyc | Bin 829 -> 0 bytes .../__pycache__/upload_docs.cpython-39.pyc | Bin 6172 -> 0 bytes .../site-packages/setuptools/command/alias.py | 78 - .../setuptools/command/bdist_egg.py | 456 - .../setuptools/command/bdist_rpm.py | 31 - .../setuptools/command/build_clib.py | 101 - .../setuptools/command/build_ext.py | 328 - .../setuptools/command/build_py.py | 252 - .../setuptools/command/develop.py | 216 - .../setuptools/command/dist_info.py | 36 - .../setuptools/command/easy_install.py | 2290 ---- .../setuptools/command/egg_info.py | 734 -- .../setuptools/command/install.py | 125 - .../setuptools/command/install_egg_info.py | 62 - .../setuptools/command/install_lib.py | 122 - .../setuptools/command/install_scripts.py | 69 - .../setuptools/command/launcher manifest.xml | 15 - .../setuptools/command/py36compat.py | 134 - .../setuptools/command/register.py | 18 - .../setuptools/command/rotate.py | 64 - .../setuptools/command/saveopts.py | 22 - .../site-packages/setuptools/command/sdist.py | 189 - .../setuptools/command/setopt.py | 148 - .../site-packages/setuptools/command/test.py | 274 - .../setuptools/command/upload.py | 17 - .../setuptools/command/upload_docs.py | 202 - .../site-packages/setuptools/config.py | 715 -- .../site-packages/setuptools/dep_util.py | 25 - .../site-packages/setuptools/depends.py | 175 - .../site-packages/setuptools/dist.py | 1121 -- .../site-packages/setuptools/errors.py | 16 - .../site-packages/setuptools/extension.py | 55 - .../setuptools/extern/__init__.py | 73 - .../__pycache__/__init__.cpython-39.pyc | Bin 2930 -> 0 bytes .../site-packages/setuptools/glob.py | 167 - .../site-packages/setuptools/gui-32.exe | Bin 65536 -> 0 bytes .../site-packages/setuptools/gui-64.exe | Bin 75264 -> 0 bytes .../site-packages/setuptools/gui.exe | Bin 65536 -> 0 bytes .../site-packages/setuptools/installer.py | 97 - .../site-packages/setuptools/launch.py | 36 - .../site-packages/setuptools/lib2to3_ex.py | 68 - .../site-packages/setuptools/monkey.py | 177 - .../site-packages/setuptools/msvc.py | 1805 --- .../site-packages/setuptools/namespaces.py | 107 - .../site-packages/setuptools/package_index.py | 1119 -- .../site-packages/setuptools/py34compat.py | 13 - .../site-packages/setuptools/sandbox.py | 496 - .../setuptools/script (dev).tmpl | 6 - .../site-packages/setuptools/script.tmpl | 3 - .../site-packages/setuptools/unicode_utils.py | 42 - .../site-packages/setuptools/version.py | 6 - .../site-packages/setuptools/wheel.py | 213 - .../setuptools/windows_support.py | 29 - .../soupsieve-2.2.1.dist-info/INSTALLER | 1 - .../soupsieve-2.2.1.dist-info/LICENSE.md | 21 - .../soupsieve-2.2.1.dist-info/METADATA | 124 - .../soupsieve-2.2.1.dist-info/RECORD | 18 - .../soupsieve-2.2.1.dist-info/WHEEL | 5 - .../soupsieve-2.2.1.dist-info/top_level.txt | 1 - .../site-packages/soupsieve/__init__.py | 111 - .../site-packages/soupsieve/__meta__.py | 192 - .../__pycache__/__init__.cpython-39.pyc | Bin 3683 -> 0 bytes .../__pycache__/__meta__.cpython-39.pyc | Bin 5792 -> 0 bytes .../__pycache__/css_match.cpython-39.pyc | Bin 34043 -> 0 bytes .../__pycache__/css_parser.cpython-39.pyc | Bin 26881 -> 0 bytes .../__pycache__/css_types.cpython-39.pyc | Bin 10637 -> 0 bytes .../soupsieve/__pycache__/util.cpython-39.pyc | Bin 2880 -> 0 bytes .../site-packages/soupsieve/css_match.py | 1534 --- .../site-packages/soupsieve/css_parser.py | 1209 -- .../site-packages/soupsieve/css_types.py | 344 - .../python3.9/site-packages/soupsieve/util.py | 110 - .../urllib3-1.26.7.dist-info/INSTALLER | 1 - .../urllib3-1.26.7.dist-info/LICENSE.txt | 21 - .../urllib3-1.26.7.dist-info/METADATA | 1396 --- .../urllib3-1.26.7.dist-info/RECORD | 84 - .../urllib3-1.26.7.dist-info/WHEEL | 6 - .../urllib3-1.26.7.dist-info/top_level.txt | 1 - .../site-packages/urllib3/__init__.py | 85 - .../__pycache__/__init__.cpython-39.pyc | Bin 2186 -> 0 bytes .../__pycache__/_collections.cpython-39.pyc | Bin 10781 -> 0 bytes .../__pycache__/_version.cpython-39.pyc | Bin 210 -> 0 bytes .../__pycache__/connection.cpython-39.pyc | Bin 13679 -> 0 bytes .../__pycache__/connectionpool.cpython-39.pyc | Bin 24706 -> 0 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 11643 -> 0 bytes .../urllib3/__pycache__/fields.cpython-39.pyc | Bin 8158 -> 0 bytes .../__pycache__/filepost.cpython-39.pyc | Bin 2759 -> 0 bytes .../__pycache__/poolmanager.cpython-39.pyc | Bin 15161 -> 0 bytes .../__pycache__/request.cpython-39.pyc | Bin 5622 -> 0 bytes .../__pycache__/response.cpython-39.pyc | Bin 20833 -> 0 bytes .../site-packages/urllib3/_collections.py | 337 - .../site-packages/urllib3/_version.py | 2 - .../site-packages/urllib3/connection.py | 569 - .../site-packages/urllib3/connectionpool.py | 1078 -- .../site-packages/urllib3/contrib/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 196 -> 0 bytes .../_appengine_environ.cpython-39.pyc | Bin 1416 -> 0 bytes .../__pycache__/appengine.cpython-39.pyc | Bin 8245 -> 0 bytes .../__pycache__/ntlmpool.cpython-39.pyc | Bin 3623 -> 0 bytes .../__pycache__/pyopenssl.cpython-39.pyc | Bin 15567 -> 0 bytes .../securetransport.cpython-39.pyc | Bin 21883 -> 0 bytes .../contrib/__pycache__/socks.cpython-39.pyc | Bin 5632 -> 0 bytes .../urllib3/contrib/_appengine_environ.py | 36 - .../contrib/_securetransport/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 213 -> 0 bytes .../__pycache__/bindings.cpython-39.pyc | Bin 10708 -> 0 bytes .../__pycache__/low_level.cpython-39.pyc | Bin 9173 -> 0 bytes .../contrib/_securetransport/bindings.py | 519 - .../contrib/_securetransport/low_level.py | 397 - .../urllib3/contrib/appengine.py | 314 - .../site-packages/urllib3/contrib/ntlmpool.py | 130 - .../urllib3/contrib/pyopenssl.py | 511 - .../urllib3/contrib/securetransport.py | 922 -- .../site-packages/urllib3/contrib/socks.py | 216 - .../site-packages/urllib3/exceptions.py | 323 - .../python3.9/site-packages/urllib3/fields.py | 274 - .../site-packages/urllib3/filepost.py | 98 - .../urllib3/packages/__init__.py | 5 - .../__pycache__/__init__.cpython-39.pyc | Bin 310 -> 0 bytes .../packages/__pycache__/six.cpython-39.pyc | Bin 27580 -> 0 bytes .../urllib3/packages/backports/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 207 -> 0 bytes .../__pycache__/makefile.cpython-39.pyc | Bin 1305 -> 0 bytes .../urllib3/packages/backports/makefile.py | 51 - .../site-packages/urllib3/packages/six.py | 1077 -- .../packages/ssl_match_hostname/__init__.py | 24 - .../__pycache__/__init__.cpython-39.pyc | Bin 569 -> 0 bytes .../_implementation.cpython-39.pyc | Bin 3300 -> 0 bytes .../ssl_match_hostname/_implementation.py | 160 - .../site-packages/urllib3/poolmanager.py | 536 - .../site-packages/urllib3/request.py | 170 - .../site-packages/urllib3/response.py | 821 -- .../site-packages/urllib3/util/__init__.py | 49 - .../util/__pycache__/__init__.cpython-39.pyc | Bin 1106 -> 0 bytes .../__pycache__/connection.cpython-39.pyc | Bin 3446 -> 0 bytes .../util/__pycache__/proxy.cpython-39.pyc | Bin 1342 -> 0 bytes .../util/__pycache__/queue.cpython-39.pyc | Bin 1061 -> 0 bytes .../util/__pycache__/request.cpython-39.pyc | Bin 3449 -> 0 bytes .../util/__pycache__/response.cpython-39.pyc | Bin 2346 -> 0 bytes .../util/__pycache__/retry.cpython-39.pyc | Bin 15833 -> 0 bytes .../util/__pycache__/ssl_.cpython-39.pyc | Bin 11318 -> 0 bytes .../__pycache__/ssltransport.cpython-39.pyc | Bin 7487 -> 0 bytes .../util/__pycache__/timeout.cpython-39.pyc | Bin 8944 -> 0 bytes .../util/__pycache__/url.cpython-39.pyc | Bin 10645 -> 0 bytes .../util/__pycache__/wait.cpython-39.pyc | Bin 3129 -> 0 bytes .../site-packages/urllib3/util/connection.py | 150 - .../site-packages/urllib3/util/proxy.py | 57 - .../site-packages/urllib3/util/queue.py | 22 - .../site-packages/urllib3/util/request.py | 143 - .../site-packages/urllib3/util/response.py | 107 - .../site-packages/urllib3/util/retry.py | 602 - .../site-packages/urllib3/util/ssl_.py | 495 - .../urllib3/util/ssltransport.py | 221 - .../site-packages/urllib3/util/timeout.py | 268 - .../site-packages/urllib3/util/url.py | 432 - .../site-packages/urllib3/util/wait.py | 153 - .../whichcraft-0.6.1.dist-info/AUTHORS.rst | 16 - .../whichcraft-0.6.1.dist-info/INSTALLER | 1 - .../whichcraft-0.6.1.dist-info/LICENSE | 12 - .../whichcraft-0.6.1.dist-info/METADATA | 172 - .../whichcraft-0.6.1.dist-info/RECORD | 9 - .../whichcraft-0.6.1.dist-info/WHEEL | 6 - .../whichcraft-0.6.1.dist-info/top_level.txt | 1 - .../lib/python3.9/site-packages/whichcraft.py | 77 - .../yapf-0.31.0.dist-info/AUTHORS | 9 - .../yapf-0.31.0.dist-info/INSTALLER | 1 - .../yapf-0.31.0.dist-info/LICENSE | 202 - .../yapf-0.31.0.dist-info/METADATA | 1026 -- .../yapf-0.31.0.dist-info/RECORD | 109 - .../yapf-0.31.0.dist-info/REQUESTED | 0 .../site-packages/yapf-0.31.0.dist-info/WHEEL | 6 - .../yapf-0.31.0.dist-info/entry_points.txt | 4 - .../yapf-0.31.0.dist-info/top_level.txt | 2 - .../python3.9/site-packages/yapf/__init__.py | 369 - .../python3.9/site-packages/yapf/__main__.py | 18 - .../yapf/__pycache__/__init__.cpython-39.pyc | Bin 9693 -> 0 bytes .../yapf/__pycache__/__main__.cpython-39.pyc | Bin 265 -> 0 bytes .../site-packages/yapf/yapflib/__init__.py | 13 - .../__pycache__/__init__.cpython-39.pyc | Bin 203 -> 0 bytes .../blank_line_calculator.cpython-39.pyc | Bin 5542 -> 0 bytes .../comment_splicer.cpython-39.pyc | Bin 6874 -> 0 bytes .../continuation_splicer.cpython-39.pyc | Bin 1437 -> 0 bytes .../yapflib/__pycache__/errors.cpython-39.pyc | Bin 589 -> 0 bytes .../__pycache__/file_resources.cpython-39.pyc | Bin 7315 -> 0 bytes .../format_decision_state.cpython-39.pyc | Bin 26609 -> 0 bytes .../__pycache__/format_token.cpython-39.pyc | Bin 12893 -> 0 bytes .../identify_container.cpython-39.pyc | Bin 1840 -> 0 bytes .../__pycache__/line_joiner.cpython-39.pyc | Bin 2714 -> 0 bytes .../__pycache__/object_state.cpython-39.pyc | Bin 9343 -> 0 bytes .../__pycache__/py3compat.cpython-39.pyc | Bin 3067 -> 0 bytes .../pytree_unwrapper.cpython-39.pyc | Bin 12915 -> 0 bytes .../__pycache__/pytree_utils.cpython-39.pyc | Bin 9361 -> 0 bytes .../__pycache__/pytree_visitor.cpython-39.pyc | Bin 4762 -> 0 bytes .../__pycache__/reformatter.cpython-39.pyc | Bin 18401 -> 0 bytes .../__pycache__/split_penalty.cpython-39.pyc | Bin 16490 -> 0 bytes .../yapflib/__pycache__/style.cpython-39.pyc | Bin 23363 -> 0 bytes .../subtype_assigner.cpython-39.pyc | Bin 14179 -> 0 bytes .../__pycache__/unwrapped_line.cpython-39.pyc | Bin 13935 -> 0 bytes .../__pycache__/verifier.cpython-39.pyc | Bin 2408 -> 0 bytes .../__pycache__/yapf_api.cpython-39.pyc | Bin 9345 -> 0 bytes .../yapf/yapflib/blank_line_calculator.py | 177 - .../yapf/yapflib/comment_splicer.py | 365 - .../yapf/yapflib/continuation_splicer.py | 52 - .../site-packages/yapf/yapflib/errors.py | 23 - .../yapf/yapflib/file_resources.py | 265 - .../yapf/yapflib/format_decision_state.py | 1254 -- .../yapf/yapflib/format_token.py | 385 - .../yapf/yapflib/identify_container.py | 67 - .../site-packages/yapf/yapflib/line_joiner.py | 109 - .../yapf/yapflib/object_state.py | 235 - .../site-packages/yapf/yapflib/py3compat.py | 131 - .../yapf/yapflib/pytree_unwrapper.py | 424 - .../yapf/yapflib/pytree_utils.py | 346 - .../yapf/yapflib/pytree_visitor.py | 135 - .../site-packages/yapf/yapflib/reformatter.py | 800 -- .../yapf/yapflib/split_penalty.py | 629 - .../site-packages/yapf/yapflib/style.py | 849 -- .../yapf/yapflib/subtype_assigner.py | 509 - .../yapf/yapflib/unwrapped_line.py | 673 - .../site-packages/yapf/yapflib/verifier.py | 93 - .../site-packages/yapf/yapflib/yapf_api.py | 319 - .../site-packages/yapftests/__init__.py | 13 - .../__pycache__/__init__.cpython-39.pyc | Bin 200 -> 0 bytes .../blank_line_calculator_test.cpython-39.pyc | Bin 8671 -> 0 bytes .../comment_splicer_test.cpython-39.pyc | Bin 8800 -> 0 bytes .../file_resources_test.cpython-39.pyc | Bin 16564 -> 0 bytes .../format_decision_state_test.cpython-39.pyc | Bin 3126 -> 0 bytes .../format_token_test.cpython-39.pyc | Bin 2515 -> 0 bytes .../line_joiner_test.cpython-39.pyc | Bin 3012 -> 0 bytes .../__pycache__/main_test.cpython-39.pyc | Bin 5548 -> 0 bytes .../pytree_unwrapper_test.cpython-39.pyc | Bin 9304 -> 0 bytes .../pytree_utils_test.cpython-39.pyc | Bin 7313 -> 0 bytes .../pytree_visitor_test.cpython-39.pyc | Bin 3657 -> 0 bytes .../reformatter_basic_test.cpython-39.pyc | Bin 93805 -> 0 bytes .../reformatter_buganizer_test.cpython-39.pyc | Bin 80291 -> 0 bytes .../reformatter_facebook_test.cpython-39.pyc | Bin 14233 -> 0 bytes .../reformatter_pep8_test.cpython-39.pyc | Bin 29269 -> 0 bytes .../reformatter_python3_test.cpython-39.pyc | Bin 13009 -> 0 bytes ...formatter_style_config_test.cpython-39.pyc | Bin 6120 -> 0 bytes .../reformatter_verify_test.cpython-39.pyc | Bin 3208 -> 0 bytes .../split_penalty_test.cpython-39.pyc | Bin 4809 -> 0 bytes .../__pycache__/style_test.cpython-39.pyc | Bin 12797 -> 0 bytes .../subtype_assigner_test.cpython-39.pyc | Bin 6369 -> 0 bytes .../unwrapped_line_test.cpython-39.pyc | Bin 3956 -> 0 bytes .../__pycache__/utils.cpython-39.pyc | Bin 1445 -> 0 bytes .../__pycache__/yapf_test.cpython-39.pyc | Bin 56085 -> 0 bytes .../yapf_test_helper.cpython-39.pyc | Bin 2403 -> 0 bytes .../yapftests/blank_line_calculator_test.py | 422 - .../yapftests/comment_splicer_test.py | 334 - .../yapftests/file_resources_test.py | 493 - .../yapftests/format_decision_state_test.py | 145 - .../yapftests/format_token_test.py | 88 - .../yapftests/line_joiner_test.py | 82 - .../site-packages/yapftests/main_test.py | 144 - .../yapftests/pytree_unwrapper_test.py | 356 - .../yapftests/pytree_utils_test.py | 205 - .../yapftests/pytree_visitor_test.py | 120 - .../yapftests/reformatter_basic_test.py | 3140 ----- .../yapftests/reformatter_buganizer_test.py | 2350 ---- .../yapftests/reformatter_facebook_test.py | 432 - .../yapftests/reformatter_pep8_test.py | 919 -- .../yapftests/reformatter_python3_test.py | 471 - .../reformatter_style_config_test.py | 198 - .../yapftests/reformatter_verify_test.py | 108 - .../yapftests/split_penalty_test.py | 266 - .../site-packages/yapftests/style_test.py | 336 - .../yapftests/subtype_assigner_test.py | 304 - .../yapftests/unwrapped_line_test.py | 96 - .../site-packages/yapftests/utils.py | 89 - .../site-packages/yapftests/yapf_test.py | 2038 --- .../yapftests/yapf_test_helper.py | 89 - .../zope.event-4.5.0-py3.6-nspkg.pth | 1 - .../zope.event-4.5.0.dist-info/INSTALLER | 1 - .../zope.event-4.5.0.dist-info/LICENSE.txt | 44 - .../zope.event-4.5.0.dist-info/METADATA | 175 - .../zope.event-4.5.0.dist-info/RECORD | 14 - .../zope.event-4.5.0.dist-info/WHEEL | 6 - .../namespace_packages.txt | 1 - .../zope.event-4.5.0.dist-info/top_level.txt | 1 - .../zope.interface-5.4.0-py3.9-nspkg.pth | 1 - .../zope.interface-5.4.0.dist-info/INSTALLER | 1 - .../LICENSE.txt | 44 - .../zope.interface-5.4.0.dist-info/METADATA | 1068 -- .../zope.interface-5.4.0.dist-info/RECORD | 108 - .../zope.interface-5.4.0.dist-info/WHEEL | 5 - .../namespace_packages.txt | 1 - .../top_level.txt | 1 - .../site-packages/zope/event/__init__.py | 32 - .../event/__pycache__/__init__.cpython-39.pyc | Bin 440 -> 0 bytes .../__pycache__/classhandler.cpython-39.pyc | Bin 2066 -> 0 bytes .../event/__pycache__/tests.cpython-39.pyc | Bin 2007 -> 0 bytes .../site-packages/zope/event/classhandler.py | 73 - .../site-packages/zope/event/tests.py | 60 - .../site-packages/zope/interface/__init__.py | 96 - .../__pycache__/__init__.cpython-39.pyc | Bin 2690 -> 0 bytes .../__pycache__/_compat.cpython-39.pyc | Bin 4598 -> 0 bytes .../__pycache__/_flatten.cpython-39.pyc | Bin 605 -> 0 bytes .../__pycache__/adapter.cpython-39.pyc | Bin 26378 -> 0 bytes .../__pycache__/advice.cpython-39.pyc | Bin 4985 -> 0 bytes .../__pycache__/declarations.cpython-39.pyc | Bin 31009 -> 0 bytes .../__pycache__/document.cpython-39.pyc | Bin 3202 -> 0 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 7279 -> 0 bytes .../__pycache__/interface.cpython-39.pyc | Bin 25260 -> 0 bytes .../__pycache__/interfaces.cpython-39.pyc | Bin 60695 -> 0 bytes .../__pycache__/registry.cpython-39.pyc | Bin 22629 -> 0 bytes .../interface/__pycache__/ro.cpython-39.pyc | Bin 19724 -> 0 bytes .../__pycache__/verify.cpython-39.pyc | Bin 4501 -> 0 bytes .../site-packages/zope/interface/_compat.py | 170 - .../site-packages/zope/interface/_flatten.py | 35 - .../_zope_interface_coptimizations.c | 2122 ---- ...timizations.cpython-39-x86_64-linux-gnu.so | Bin 179072 -> 0 bytes .../site-packages/zope/interface/adapter.py | 1018 -- .../site-packages/zope/interface/advice.py | 213 - .../zope/interface/common/__init__.py | 272 - .../__pycache__/__init__.cpython-39.pyc | Bin 8738 -> 0 bytes .../__pycache__/builtins.cpython-39.pyc | Bin 3561 -> 0 bytes .../__pycache__/collections.cpython-39.pyc | Bin 8131 -> 0 bytes .../__pycache__/idatetime.cpython-39.pyc | Bin 24696 -> 0 bytes .../__pycache__/interfaces.cpython-39.pyc | Bin 7750 -> 0 bytes .../common/__pycache__/io.cpython-39.pyc | Bin 1396 -> 0 bytes .../common/__pycache__/mapping.cpython-39.pyc | Bin 7390 -> 0 bytes .../common/__pycache__/numbers.cpython-39.pyc | Bin 2197 -> 0 bytes .../__pycache__/sequence.cpython-39.pyc | Bin 9010 -> 0 bytes .../zope/interface/common/builtins.py | 125 - .../zope/interface/common/collections.py | 284 - .../zope/interface/common/idatetime.py | 606 - .../zope/interface/common/interfaces.py | 212 - .../site-packages/zope/interface/common/io.py | 53 - .../zope/interface/common/mapping.py | 184 - .../zope/interface/common/numbers.py | 84 - .../zope/interface/common/sequence.py | 215 - .../zope/interface/common/tests/__init__.py | 133 - .../tests/__pycache__/__init__.cpython-39.pyc | Bin 3696 -> 0 bytes .../__pycache__/basemapping.cpython-39.pyc | Bin 3682 -> 0 bytes .../__pycache__/test_builtins.cpython-39.pyc | Bin 1276 -> 0 bytes .../test_collections.cpython-39.pyc | Bin 5334 -> 0 bytes .../__pycache__/test_idatetime.cpython-39.pyc | Bin 1324 -> 0 bytes .../test_import_interfaces.cpython-39.pyc | Bin 680 -> 0 bytes .../tests/__pycache__/test_io.cpython-39.pyc | Bin 1890 -> 0 bytes .../__pycache__/test_numbers.cpython-39.pyc | Bin 1238 -> 0 bytes .../interface/common/tests/basemapping.py | 107 - .../interface/common/tests/test_builtins.py | 45 - .../common/tests/test_collections.py | 160 - .../interface/common/tests/test_idatetime.py | 37 - .../common/tests/test_import_interfaces.py | 20 - .../zope/interface/common/tests/test_io.py | 52 - .../interface/common/tests/test_numbers.py | 41 - .../zope/interface/declarations.py | 1313 -- .../site-packages/zope/interface/document.py | 124 - .../zope/interface/exceptions.py | 275 - .../site-packages/zope/interface/interface.py | 1153 -- .../zope/interface/interfaces.py | 1588 --- .../site-packages/zope/interface/registry.py | 726 -- .../site-packages/zope/interface/ro.py | 666 - .../zope/interface/tests/__init__.py | 115 - .../tests/__pycache__/__init__.cpython-39.pyc | Bin 3754 -> 0 bytes .../advisory_testing.cpython-39.pyc | Bin 1173 -> 0 bytes .../tests/__pycache__/dummy.cpython-39.pyc | Bin 480 -> 0 bytes .../tests/__pycache__/idummy.cpython-39.pyc | Bin 678 -> 0 bytes .../tests/__pycache__/m1.cpython-39.pyc | Bin 601 -> 0 bytes .../tests/__pycache__/odd.cpython-39.pyc | Bin 3511 -> 0 bytes .../__pycache__/test_adapter.cpython-39.pyc | Bin 75810 -> 0 bytes .../__pycache__/test_advice.cpython-39.pyc | Bin 15871 -> 0 bytes .../test_declarations.cpython-39.pyc | Bin 114009 -> 0 bytes .../__pycache__/test_document.cpython-39.pyc | Bin 21248 -> 0 bytes .../__pycache__/test_element.cpython-39.pyc | Bin 871 -> 0 bytes .../test_exceptions.cpython-39.pyc | Bin 7157 -> 0 bytes .../__pycache__/test_interface.cpython-39.pyc | Bin 114983 -> 0 bytes .../test_interfaces.cpython-39.pyc | Bin 6621 -> 0 bytes .../test_odd_declarations.cpython-39.pyc | Bin 10343 -> 0 bytes .../__pycache__/test_registry.cpython-39.pyc | Bin 125207 -> 0 bytes .../tests/__pycache__/test_ro.cpython-39.pyc | Bin 18912 -> 0 bytes .../__pycache__/test_sorting.cpython-39.pyc | Bin 2498 -> 0 bytes .../__pycache__/test_verify.cpython-39.pyc | Bin 34011 -> 0 bytes .../zope/interface/tests/advisory_testing.py | 42 - .../zope/interface/tests/dummy.py | 23 - .../zope/interface/tests/idummy.py | 23 - .../site-packages/zope/interface/tests/m1.py | 21 - .../site-packages/zope/interface/tests/odd.py | 128 - .../zope/interface/tests/test_adapter.py | 2109 ---- .../zope/interface/tests/test_advice.py | 355 - .../zope/interface/tests/test_declarations.py | 2678 ---- .../zope/interface/tests/test_document.py | 505 - .../zope/interface/tests/test_element.py | 31 - .../zope/interface/tests/test_exceptions.py | 184 - .../zope/interface/tests/test_interface.py | 2660 ---- .../zope/interface/tests/test_interfaces.py | 128 - .../interface/tests/test_odd_declarations.py | 268 - .../zope/interface/tests/test_registry.py | 3057 ----- .../zope/interface/tests/test_ro.py | 426 - .../zope/interface/tests/test_sorting.py | 64 - .../zope/interface/tests/test_verify.py | 656 - .../site-packages/zope/interface/verify.py | 218 - IKEA_scraper/.venv/lib64 | 1 - IKEA_scraper/.venv/pyvenv.cfg | 3 - 2734 files changed, 455145 deletions(-) delete mode 100644 IKEA_scraper/.venv/bin/Activate.ps1 delete mode 100644 IKEA_scraper/.venv/bin/__pycache__/bottle.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/bin/activate delete mode 100644 IKEA_scraper/.venv/bin/activate.csh delete mode 100644 IKEA_scraper/.venv/bin/activate.fish delete mode 100644 IKEA_scraper/.venv/bin/bottle.py delete mode 100644 IKEA_scraper/.venv/bin/futurize delete mode 100644 IKEA_scraper/.venv/bin/normalizer delete mode 100644 IKEA_scraper/.venv/bin/pasteurize delete mode 100644 IKEA_scraper/.venv/bin/pip delete mode 100644 IKEA_scraper/.venv/bin/pip3 delete mode 100644 IKEA_scraper/.venv/bin/pip3.9 delete mode 120000 IKEA_scraper/.venv/bin/python delete mode 120000 IKEA_scraper/.venv/bin/python3 delete mode 120000 IKEA_scraper/.venv/bin/python3.9 delete mode 100755 IKEA_scraper/.venv/bin/yapf delete mode 100755 IKEA_scraper/.venv/bin/yapf-diff delete mode 100644 IKEA_scraper/.venv/include/site/python3.9/greenlet/greenlet.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/PKG-INFO delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/SOURCES.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/dependency_links.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/installed-files.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/requires.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/__pycache__/bottle.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/__pycache__/pyparsing.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/__pycache__/whichcraft.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/_distutils_hack/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/_distutils_hack/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/_distutils_hack/__pycache__/override.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/_distutils_hack/override.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/AUTHORS delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/COPYING.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/LICENSE delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/AUTHORS delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/LICENSE delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/PKG-INFO delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/SOURCES.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/dependency_links.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/installed-files.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/requires.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/__pycache__/plugin.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/__pycache__/server.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/plugin.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/server.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/PKG-INFO delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/SOURCES.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/dependency_links.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/installed-files.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/requires.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/dammit.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/diagnose.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/element.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/formatter.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/testing.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/builder/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/builder/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/builder/__pycache__/_html5lib.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/builder/__pycache__/_htmlparser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/builder/__pycache__/_lxml.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/builder/_html5lib.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/builder/_htmlparser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/builder/_lxml.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/dammit.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/diagnose.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/element.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/formatter.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/testing.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/LICENSE delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__main__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__pycache__/__main__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__pycache__/core.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/cacert.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/core.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/LICENSE delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/entry_points.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/api.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/cd.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/constant.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/legacy.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/md.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/models.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/version.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/api.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/assets/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/assets/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cd.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__pycache__/normalizer.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cli/normalizer.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/constant.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/legacy.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/md.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/models.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/py.typed delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/version.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/distutils-precedence.pth delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__main__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/__main__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/browsers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/chrome.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/edge.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/electron.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/eel/browsers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/eel/chrome.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/eel/edge.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/eel/eel.js delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/eel/electron.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/PKG-INFO delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/SOURCES.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/dependency_links.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/entry_points.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/installed-files.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/_markupbase.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/datetime.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/misc.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/socket.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/socketserver.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/total_ordering.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/_markupbase.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/datetime.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/_encoded_words.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/_header_value_parser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/_parseaddr.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/_policybase.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/base64mime.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/charset.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/encoders.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/errors.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/feedparser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/generator.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/header.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/headerregistry.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/iterators.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/message.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/parser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/policy.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/quoprimime.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_encoded_words.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_header_value_parser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_parseaddr.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_policybase.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/base64mime.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/charset.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/encoders.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/errors.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/feedparser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/generator.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/header.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/headerregistry.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/iterators.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/message.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/application.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/audio.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/base.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/image.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/message.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/multipart.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/nonmultipart.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/text.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/application.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/audio.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/base.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/image.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/message.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/multipart.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/nonmultipart.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/text.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/parser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/policy.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/quoprimime.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/__pycache__/entities.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/__pycache__/parser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/entities.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/parser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__pycache__/client.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__pycache__/cookiejar.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__pycache__/cookies.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__pycache__/server.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/client.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/cookiejar.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/cookies.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/server.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/misc.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/socket.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/socketserver.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/__pycache__/pystone.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/__pycache__/ssl_servers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/__pycache__/support.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/badcert.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/badkey.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/dh512.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/https_svn_python_org_root.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/keycert.passwd.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/keycert.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/keycert2.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/nokia.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/nullbytecert.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/nullcert.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/pystone.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/sha256.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_cert.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_key.passwd.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_key.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_servers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/support.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/total_ordering.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__pycache__/error.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__pycache__/parse.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__pycache__/request.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__pycache__/response.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__pycache__/robotparser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/error.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/parse.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/request.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/response.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/robotparser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/__pycache__/client.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/__pycache__/server.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/client.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/server.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__pycache__/disabled.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__pycache__/iterators.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__pycache__/misc.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__pycache__/new_min_max.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__pycache__/newnext.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__pycache__/newround.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__pycache__/newsuper.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/disabled.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/iterators.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/misc.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/new_min_max.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/newnext.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/newround.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/newsuper.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/_dummy_thread.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/_markupbase.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/_thread.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/builtins.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/collections.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/configparser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/copyreg.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/itertools.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/pickle.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/queue.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/reprlib.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/socketserver.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/subprocess.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/sys.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/winreg.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/_dummy_thread.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/_markupbase.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/_thread.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/builtins.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/collections.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/configparser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/copyreg.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__pycache__/dumb.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__pycache__/gnu.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__pycache__/ndbm.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/dumb.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/gnu.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/ndbm.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/__pycache__/entities.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/__pycache__/parser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/entities.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/parser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/__pycache__/client.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/__pycache__/cookiejar.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/__pycache__/cookies.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/__pycache__/server.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/client.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/cookiejar.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/cookies.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/server.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/itertools.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/pickle.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/queue.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/reprlib.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/socketserver.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/subprocess.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/sys.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/test/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/test/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/test/__pycache__/support.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/test/support.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/__pycache__/colorchooser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/__pycache__/commondialog.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/__pycache__/constants.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/__pycache__/dialog.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/__pycache__/dnd.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/__pycache__/filedialog.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/__pycache__/font.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/__pycache__/messagebox.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/__pycache__/scrolledtext.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/__pycache__/simpledialog.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/__pycache__/tix.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/__pycache__/ttk.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/colorchooser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/commondialog.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/constants.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/dialog.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/dnd.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/filedialog.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/font.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/messagebox.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/scrolledtext.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/simpledialog.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/tix.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/tkinter/ttk.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/urllib/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/urllib/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/urllib/__pycache__/error.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/urllib/__pycache__/parse.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/urllib/__pycache__/request.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/urllib/__pycache__/response.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/urllib/__pycache__/robotparser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/urllib/error.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/urllib/parse.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/urllib/request.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/urllib/response.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/urllib/robotparser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/winreg.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/xmlrpc/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/xmlrpc/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/xmlrpc/__pycache__/client.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/xmlrpc/__pycache__/server.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/xmlrpc/client.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/xmlrpc/server.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/standard_library/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/standard_library/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/tests/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/tests/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/tests/__pycache__/base.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/tests/base.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/__pycache__/newbytes.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/__pycache__/newdict.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/__pycache__/newint.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/__pycache__/newlist.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/__pycache__/newmemoryview.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/__pycache__/newobject.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/__pycache__/newopen.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/__pycache__/newrange.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/__pycache__/newstr.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/newbytes.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/newdict.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/newint.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/newlist.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/newmemoryview.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/newobject.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/newopen.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/newrange.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/types/newstr.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/utils/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/utils/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/utils/__pycache__/surrogateescape.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/future/utils/surrogateescape.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent-21.8.0.dist-info/AUTHORS delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent-21.8.0.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent-21.8.0.dist-info/LICENSE delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent-21.8.0.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent-21.8.0.dist-info/NOTICE delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent-21.8.0.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent-21.8.0.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent-21.8.0.dist-info/entry_points.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent-21.8.0.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_abstract_linkable.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_config.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_fileobjectcommon.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_fileobjectposix.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_greenlet_primitives.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_hub_local.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_hub_primitives.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_ident.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_imap.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_interfaces.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_monitor.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_patcher.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_semaphore.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_socket2.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_socket3.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_socketcommon.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_ssl2.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_ssl3.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_sslgte279.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_tblib.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_threading.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_tracer.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_util_py2.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/_waiter.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/ares.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/backdoor.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/baseserver.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/builtins.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/contextvars.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/core.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/event.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/events.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/exceptions.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/fileobject.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/greenlet.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/hub.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/local.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/lock.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/monkey.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/os.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/pool.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/pywsgi.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/queue.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/resolver_ares.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/resolver_thread.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/select.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/selectors.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/server.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/signal.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/socket.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/ssl.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/subprocess.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/thread.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/threading.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/threadpool.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/time.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/timeout.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/__pycache__/win32util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_abstract_linkable.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_config.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_ffi/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_ffi/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_ffi/__pycache__/callback.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_ffi/__pycache__/loop.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_ffi/__pycache__/watcher.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_ffi/callback.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_ffi/loop.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_ffi/watcher.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_fileobjectcommon.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_fileobjectposix.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_gevent_c_abstract_linkable.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_gevent_c_greenlet_primitives.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_gevent_c_hub_local.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_gevent_c_hub_primitives.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_gevent_c_ident.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_gevent_c_imap.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_gevent_c_semaphore.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_gevent_c_tracer.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_gevent_c_waiter.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_gevent_cevent.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_gevent_cgreenlet.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_gevent_clocal.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_gevent_cqueue.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_greenlet_primitives.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_hub_local.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_hub_primitives.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_ident.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_imap.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_interfaces.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_monitor.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_patcher.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_semaphore.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_socket2.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_socket3.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_socketcommon.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_ssl2.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_ssl3.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_sslgte279.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_tblib.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_threading.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_tracer.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_util_py2.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/_waiter.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/ares.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/backdoor.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/baseserver.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/builtins.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/contextvars.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/core.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/event.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/events.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/exceptions.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/fileobject.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/greenlet.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/hub.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libev/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libev/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libev/__pycache__/_corecffi_build.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libev/__pycache__/corecffi.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libev/__pycache__/watcher.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libev/_corecffi.abi3.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libev/_corecffi_build.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libev/corecext.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libev/corecffi.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libev/watcher.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libuv/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libuv/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libuv/__pycache__/_corecffi_build.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libuv/__pycache__/loop.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libuv/__pycache__/watcher.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libuv/_corecffi.abi3.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libuv/_corecffi_build.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libuv/loop.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/libuv/watcher.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/local.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/lock.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/monkey.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/os.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/pool.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/pywsgi.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/queue.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/__pycache__/_addresses.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/__pycache__/_hostsfile.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/__pycache__/ares.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/__pycache__/blocking.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/__pycache__/dnspython.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/__pycache__/thread.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/_addresses.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/_hostsfile.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/ares.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/blocking.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/cares.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/dnspython.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver/thread.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver_ares.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/resolver_thread.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/select.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/selectors.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/server.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/signal.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/socket.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/ssl.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/subprocess.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/errorhandler.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/exception.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/flaky.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/hub.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/leakcheck.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/modules.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/monkey_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/openfiles.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/params.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/patched_tests_setup.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/resources.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/six.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/skipping.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/sockets.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/support.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/switching.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/sysinfo.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/testcase.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/testrunner.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/timing.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/travis.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/__pycache__/util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/coveragesite/__pycache__/sitecustomize.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/coveragesite/sitecustomize.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/errorhandler.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/exception.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/flaky.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/hub.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/leakcheck.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/modules.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/monkey_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/openfiles.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/params.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/patched_tests_setup.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/resources.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/six.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/skipping.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/sockets.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/support.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/switching.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/sysinfo.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/testcase.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/testrunner.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/timing.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/travis.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/testing/util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/2_7_keycert.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__main__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/__main__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/_blocks_at_top_level.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/_import_import_patch.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/_import_patch.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/_import_wait.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/_imports_at_top_level.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/_imports_imports_at_top_level.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/getaddrinfo_module.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/known_failures.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/lock_tests.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__GreenletExit.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test___config.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test___ident.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test___monitor.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test___monkey_patching.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__all__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__api.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__api_timeout.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__ares_host_result.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__ares_timeout.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__backdoor.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__close_backend_fd.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__contextvars.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__core.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__core_async.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__core_callback.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__core_fork.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__core_loop_run.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__core_stat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__core_timer.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__core_watcher.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__destroy.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__destroy_default_loop.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__doctests.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__environ.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__event.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__events.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__example_echoserver.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__example_portforwarder.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__example_udp_client.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__example_udp_server.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__example_webproxy.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__example_wsgiserver.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__example_wsgiserver_ssl.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__examples.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__exc_info.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__execmodules.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__fileobject.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__getaddrinfo_import.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__greenio.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__greenlet.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__greenletset.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__greenness.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__hub.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__hub_join.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__hub_join_timeout.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__import_blocking_in_greenlet.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__import_wait.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__issue112.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__issue1686.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__issue230.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__issue330.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__issue467.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__issue6.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__issue600.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__issue607.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__issue639.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__issue_728.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__issues461_471.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__iwait.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__joinall.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__local.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__lock.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__loop_callback.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__makefile_ref.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__memleak.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_builtins_future.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_futures_thread.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_hub_in_thread.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_logging.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_module_run.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_multiple_imports.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_queue.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_select.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_selectors.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_sigchld.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_sigchld_2.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_sigchld_3.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning2.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning3.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__nondefaultloop.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__order.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__os.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__pool.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__pywsgi.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__queue.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__real_greenlet.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__refcount.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__refcount_core.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__resolver_dnspython.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__select.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__selectors.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__semaphore.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__server.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__server_pywsgi.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__signal.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__sleep0.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__socket.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__socket_close.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__socket_dns.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__socket_dns6.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__socket_errors.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__socket_ex.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__socket_send_memoryview.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__socket_ssl.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__socket_timeout.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__socketpair.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__ssl.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__subprocess.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__subprocess_interrupted.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__subprocess_poll.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__systemerror.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__thread.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__threading.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__threading_2.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__threading_before_monkey.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__threading_holding_lock_while_monkey.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__threading_monkey_in_thread.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__threading_native_before_monkey.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__threading_no_monkey.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__threading_patched_local.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__threading_vs_settrace.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__threadpool.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__threadpool_executor_patched.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__timeout.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/__pycache__/test__util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/_blocks_at_top_level.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/_import_import_patch.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/_import_patch.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/_import_wait.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/_imports_at_top_level.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/_imports_imports_at_top_level.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/badcert.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/badkey.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/getaddrinfo_module.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/hosts_file.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/https_svn_python_org_root.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/keycert.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/known_failures.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/lock_tests.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/__main__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/__pycache__/__main__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/__pycache__/issue1526_no_monkey.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/__pycache__/issue1526_with_monkey.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/__pycache__/issue302monkey.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/__pycache__/script.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/__pycache__/threadpool_monkey_patches.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/__pycache__/threadpool_no_monkey.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/issue1526_no_monkey.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/issue1526_with_monkey.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/issue302monkey.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/script.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/threadpool_monkey_patches.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/monkey_package/threadpool_no_monkey.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/nullcert.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/server.crt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/server.key delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/sha256.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__GreenletExit.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test___config.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test___ident.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test___monitor.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test___monkey_patching.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__all__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__api.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__api_timeout.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__ares_host_result.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__ares_timeout.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__backdoor.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__close_backend_fd.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__contextvars.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__core.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__core_async.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__core_callback.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__core_fork.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__core_loop_run.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__core_stat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__core_timer.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__core_watcher.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__destroy.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__destroy_default_loop.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__doctests.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__environ.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__event.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__events.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__example_echoserver.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__example_portforwarder.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__example_udp_client.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__example_udp_server.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__example_webproxy.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__example_wsgiserver.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__example_wsgiserver_ssl.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__examples.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__exc_info.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__execmodules.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__fileobject.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__getaddrinfo_import.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__greenio.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__greenlet.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__greenletset.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__greenness.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__hub.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__hub_join.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__hub_join_timeout.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__import_blocking_in_greenlet.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__import_wait.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__issue112.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__issue1686.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__issue230.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__issue330.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__issue467.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__issue6.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__issue600.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__issue607.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__issue639.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__issue_728.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__issues461_471.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__iwait.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__joinall.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__local.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__lock.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__loop_callback.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__makefile_ref.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__memleak.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_builtins_future.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_futures_thread.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_hub_in_thread.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_logging.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_module_run.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_multiple_imports.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_queue.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_select.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_selectors.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_sigchld.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_sigchld_2.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_sigchld_3.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_ssl_warning.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_ssl_warning2.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__monkey_ssl_warning3.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__nondefaultloop.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__order.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__os.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__pool.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__pywsgi.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__queue.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__real_greenlet.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__refcount.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__refcount_core.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__resolver_dnspython.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__select.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__selectors.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__semaphore.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__server.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__server_pywsgi.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__signal.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__sleep0.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__socket.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__socket_close.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__socket_dns.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__socket_dns6.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__socket_errors.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__socket_ex.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__socket_send_memoryview.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__socket_ssl.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__socket_timeout.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__socketpair.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__ssl.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__subprocess.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__subprocess_interrupted.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__subprocess_poll.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__systemerror.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__thread.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__threading.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__threading_2.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__threading_before_monkey.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__threading_holding_lock_while_monkey.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__threading_monkey_in_thread.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__threading_native_before_monkey.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__threading_no_monkey.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__threading_patched_local.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__threading_vs_settrace.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__threadpool.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__threadpool_executor_patched.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__timeout.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test__util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test_server.crt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/test_server.key delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/tests_that_dont_do_leakchecks.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/tests_that_dont_monkeypatch.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/tests_that_dont_use_resolver.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/tests/wrongcert.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/thread.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/threading.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/threadpool.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/time.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/timeout.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent/win32util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent_websocket-0.10.1.dist-info/DESCRIPTION.rst delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent_websocket-0.10.1.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent_websocket-0.10.1.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent_websocket-0.10.1.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent_websocket-0.10.1.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent_websocket-0.10.1.dist-info/metadata.json delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/gevent_websocket-0.10.1.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/__pycache__/_compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/__pycache__/exceptions.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/__pycache__/handler.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/__pycache__/logging.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/__pycache__/resource.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/__pycache__/server.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/__pycache__/utf8validator.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/__pycache__/utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/__pycache__/websocket.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/_compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/exceptions.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/gunicorn/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/gunicorn/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/gunicorn/__pycache__/workers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/gunicorn/workers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/handler.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/logging.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/protocols/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/protocols/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/protocols/__pycache__/base.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/protocols/__pycache__/wamp.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/protocols/base.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/protocols/wamp.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/resource.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/server.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/utf8validator.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/geventwebsocket/websocket.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet-1.1.2.dist-info/AUTHORS delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet-1.1.2.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet-1.1.2.dist-info/LICENSE delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet-1.1.2.dist-info/LICENSE.PSF delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet-1.1.2.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet-1.1.2.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet-1.1.2.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet-1.1.2.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/_greenlet.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/greenlet.c delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/greenlet.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/setup_switch_x64_masm.cmd delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_aarch64_gcc.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_alpha_unix.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_amd64_unix.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_arm32_gcc.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_arm32_ios.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_csky_gcc.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_m68k_gcc.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_mips_unix.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_ppc64_aix.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_ppc64_linux.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_ppc_aix.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_ppc_linux.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_ppc_macosx.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_ppc_unix.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_riscv_unix.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_s390_unix.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_sparc_sun_gcc.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_x32_unix.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_x64_masm.asm delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_x64_masm.obj delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_x64_msvc.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_x86_msvc.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/platform/switch_x86_unix.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/slp_platformselect.h delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__pycache__/test_contextvars.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__pycache__/test_cpp.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__pycache__/test_extension_interface.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__pycache__/test_gc.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__pycache__/test_generator.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__pycache__/test_generator_nested.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__pycache__/test_greenlet.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__pycache__/test_leaks.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__pycache__/test_stack_saved.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__pycache__/test_throw.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__pycache__/test_tracing.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__pycache__/test_version.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/__pycache__/test_weakref.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/_test_extension.c delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/_test_extension.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/_test_extension_cpp.cpp delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/_test_extension_cpp.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/test_contextvars.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/test_cpp.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/test_extension_interface.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/test_gc.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/test_generator.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/test_generator_nested.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/test_greenlet.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/test_leaks.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/test_stack_saved.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/test_throw.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/test_tracing.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/test_version.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/greenlet/tests/test_weakref.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna-3.3.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna-3.3.dist-info/LICENSE.md delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna-3.3.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna-3.3.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna-3.3.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna-3.3.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/__pycache__/codec.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/__pycache__/compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/__pycache__/core.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/__pycache__/idnadata.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/__pycache__/intranges.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/__pycache__/package_data.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/__pycache__/uts46data.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/codec.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/core.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/idnadata.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/intranges.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/package_data.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/py.typed delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/idna/uts46data.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/__pycache__/fixer_util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/__pycache__/main.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixer_util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_UserDict.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_absolute_import.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_add__future__imports_except_unicode_literals.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_basestring.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_bytes.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_cmp.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_division.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_division_safe.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_execfile.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_future_builtins.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_future_standard_library.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_future_standard_library_urllib.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_input.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_metaclass.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_next_call.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_object.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_oldstr_wrap.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_order___future__imports.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_print.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_print_with_import.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_raise.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_remove_old__future__imports.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_unicode_keep_u.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_unicode_literals_import.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/__pycache__/fix_xrange_with_import.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_UserDict.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_absolute_import.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_basestring.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_bytes.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_cmp.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_division.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_division_safe.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_execfile.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_future_builtins.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_future_standard_library.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_future_standard_library_urllib.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_input.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_metaclass.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_next_call.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_object.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_oldstr_wrap.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_order___future__imports.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_print.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_print_with_import.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_raise.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_remove_old__future__imports.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_unicode_keep_u.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_unicode_literals_import.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/fixes/fix_xrange_with_import.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libfuturize/main.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/__pycache__/main.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/feature_base.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_add_all__future__imports.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_add_all_future_builtins.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_add_future_standard_library_import.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_annotations.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_division.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_features.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_fullargspec.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_future_builtins.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_getcwd.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_imports.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_imports2.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_kwargs.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_memoryview.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_metaclass.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_newstyle.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_next.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_printfunction.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_raise.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_raise_.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_throw.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/__pycache__/fix_unpacking.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/feature_base.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_add_all__future__imports.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_add_all_future_builtins.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_add_future_standard_library_import.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_annotations.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_division.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_features.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_fullargspec.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_future_builtins.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_getcwd.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_imports.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_imports2.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_kwargs.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_memoryview.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_metaclass.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_newstyle.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_next.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_printfunction.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_raise.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_raise_.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_throw.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/fixes/fix_unpacking.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/libpasteurize/main.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/builtins/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/builtins/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/builtins/__pycache__/misc.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/builtins/__pycache__/noniterators.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/builtins/misc.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/builtins/noniterators.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/translation/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/translation/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/types/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/types/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/types/__pycache__/basestring.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/types/__pycache__/olddict.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/types/__pycache__/oldstr.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/types/basestring.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/types/olddict.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/types/oldstr.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/utils/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/past/utils/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip-21.3.1.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip-21.3.1.dist-info/LICENSE.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip-21.3.1.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip-21.3.1.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip-21.3.1.dist-info/REQUESTED delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip-21.3.1.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip-21.3.1.dist-info/entry_points.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip-21.3.1.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/__main__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/__pycache__/__main__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/__pycache__/build_env.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/__pycache__/cache.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/__pycache__/configuration.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/__pycache__/exceptions.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/__pycache__/main.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/__pycache__/pyproject.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/build_env.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cache.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/main.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/parser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/autocompletion.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/base_command.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/cmdoptions.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/command_context.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/main.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/main_parser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/parser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/progress_bars.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/req_command.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/spinners.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/cli/status_codes.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/cache.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/check.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/completion.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/debug.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/download.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/hash.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/help.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/index.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/install.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/list.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/search.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/show.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/cache.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/check.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/completion.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/configuration.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/debug.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/download.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/freeze.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/hash.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/help.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/index.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/install.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/list.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/search.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/show.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/uninstall.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/commands/wheel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/configuration.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/distributions/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/base.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/distributions/base.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/distributions/installed.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/distributions/sdist.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/distributions/wheel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/exceptions.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/index/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/index/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/index/__pycache__/collector.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/index/__pycache__/sources.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/index/collector.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/index/package_finder.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/index/sources.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/locations/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/locations/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/locations/__pycache__/_distutils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/locations/__pycache__/_sysconfig.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/locations/__pycache__/base.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/locations/_distutils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/locations/_sysconfig.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/locations/base.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/main.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/metadata/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/metadata/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/metadata/__pycache__/base.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/metadata/__pycache__/pkg_resources.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/metadata/base.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/metadata/pkg_resources.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/candidate.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/format_control.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/index.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/link.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/scheme.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/target_python.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/wheel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/candidate.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/direct_url.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/format_control.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/index.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/link.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/scheme.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/search_scope.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/selection_prefs.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/target_python.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/models/wheel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/auth.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/cache.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/download.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/session.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/auth.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/cache.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/download.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/lazy_wheel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/session.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/network/xmlrpc.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/check.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/build/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/metadata_editable.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel_editable.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/build/metadata.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/build/metadata_editable.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/build/metadata_legacy.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/build/wheel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/build/wheel_editable.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/build/wheel_legacy.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/check.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/freeze.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/install/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/legacy.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/install/editable_legacy.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/install/legacy.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/install/wheel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/operations/prepare.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/pyproject.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/req/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/req/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/req/__pycache__/constructors.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_file.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_install.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_set.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_tracker.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/req/constructors.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/req/req_file.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/req/req_install.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/req/req_set.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/req/req_tracker.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/req/req_uninstall.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/__pycache__/base.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/base.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/legacy/resolver.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/base.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/candidates.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/factory.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/provider.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/reporter.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/requirements.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/resolver.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/self_outdated_check.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/_log.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/egg_link.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/logging.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/misc.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/models.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/parallel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/pkg_resources.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/urls.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/_log.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/appdirs.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/compatibility_tags.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/datetime.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/deprecation.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/direct_url_helpers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/distutils_args.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/egg_link.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/encoding.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/entrypoints.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/filesystem.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/filetypes.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/glibc.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/hashes.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/inject_securetransport.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/logging.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/misc.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/models.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/packaging.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/parallel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/pkg_resources.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/setuptools_build.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/subprocess.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/temp_dir.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/unpacking.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/urls.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/virtualenv.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/utils/wheel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/vcs/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/git.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/vcs/bazaar.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/vcs/git.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/vcs/mercurial.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/vcs/subversion.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_internal/wheel_builder.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/__pycache__/distro.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/__pycache__/pyparsing.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/__pycache__/six.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/cache.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/_cmd.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/adapter.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/cache.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/controller.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/filewrapper.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/heuristics.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/serialize.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/wrapper.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/certifi/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/certifi/__main__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/certifi/cacert.pem delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/certifi/core.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/big5prober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/enums.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langrussianmodel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/sjisprober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/utf8prober.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/big5freq.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/big5prober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/chardistribution.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/charsetgroupprober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/charsetprober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/cli/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/cli/chardetect.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/codingstatemachine.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/cp949prober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/enums.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/escprober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/escsm.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/eucjpprober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/euckrfreq.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/euckrprober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/euctwfreq.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/euctwprober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312freq.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312prober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/hebrewprober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/jisfreq.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/jpcntx.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/langbulgarianmodel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/langgreekmodel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/langhebrewmodel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/langhungarianmodel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/langrussianmodel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/langthaimodel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/langturkishmodel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/latin1prober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/mbcharsetprober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/mbcsgroupprober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/mbcssm.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/metadata/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/metadata/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/metadata/__pycache__/languages.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/metadata/languages.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/sbcharsetprober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/sbcsgroupprober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/sjisprober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/universaldetector.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/utf8prober.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/chardet/version.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/colorama/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/ansi.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/win32.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/colorama/ansi.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/colorama/ansitowin32.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/colorama/initialise.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/colorama/win32.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/colorama/winterm.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/misc.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/shutil.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/sysconfig.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/tarfile.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/misc.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/shutil.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/sysconfig.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/tarfile.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/database.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/index.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/locators.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/manifest.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/markers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/metadata.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/resources.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/scripts.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/t32.exe delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/t64-arm.exe delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/t64.exe delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/version.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/w32.exe delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/w64-arm.exe delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/w64.exe delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distlib/wheel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/distro.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/_ihatexml.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/_inputstream.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/_tokenizer.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/_utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/constants.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/html5parser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/serializer.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_ihatexml.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_inputstream.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_tokenizer.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/__pycache__/_base.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/__pycache__/py.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/_base.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/py.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/constants.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/alphabeticalattributes.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/base.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/inject_meta_charset.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/lint.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/optionaltags.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/sanitizer.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/whitespace.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/base.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/lint.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/optionaltags.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/sanitizer.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/whitespace.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/html5parser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/serializer.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/genshi.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/sax.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/sax.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/base.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/dom.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/etree_lxml.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/base.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/dom.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/etree.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/base.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/dom.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/etree.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/etree_lxml.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/genshi.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/base.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/dom.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/etree.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/core.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/intranges.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/package_data.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/codec.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/core.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/idnadata.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/intranges.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/package_data.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/idna/uts46data.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/msgpack/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/msgpack/__pycache__/_version.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/msgpack/_version.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/msgpack/exceptions.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/msgpack/ext.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/msgpack/fallback.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/__about__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/__about__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/_manylinux.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/_musllinux.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/tags.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/_manylinux.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/_musllinux.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/_structures.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/markers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/requirements.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/specifiers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/tags.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/packaging/version.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/build.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/check.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/colorlog.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/dirtools.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/envbuild.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/meta.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/wrappers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/build.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/check.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/colorlog.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/dirtools.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/envbuild.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/in_process/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/in_process/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/in_process/__pycache__/_in_process.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/in_process/_in_process.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/meta.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pep517/wrappers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pkg_resources/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pkg_resources/__pycache__/py31compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pkg_resources/py31compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/__main__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/__pycache__/__main__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/__pycache__/android.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/__pycache__/api.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/__pycache__/macos.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/__pycache__/unix.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/__pycache__/version.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/__pycache__/windows.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/android.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/api.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/macos.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/unix.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/version.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/platformdirs/windows.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/progress/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/progress/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/progress/__pycache__/bar.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/progress/__pycache__/colors.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/progress/__pycache__/counter.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/progress/__pycache__/spinner.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/progress/bar.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/progress/colors.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/progress/counter.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/progress/spinner.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/pyparsing.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/_internal_utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/adapters.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/api.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/cookies.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/help.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/models.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/__version__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/_internal_utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/adapters.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/api.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/auth.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/certs.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/cookies.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/exceptions.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/help.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/hooks.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/models.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/packages.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/sessions.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/status_codes.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/structures.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/requests/utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/__pycache__/providers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/__pycache__/reporters.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/__pycache__/resolvers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/__pycache__/structs.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/compat/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/compat/__pycache__/collections_abc.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/providers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/reporters.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/resolvers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/structs.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/six.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/__pycache__/_asyncio.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/__pycache__/_utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/__pycache__/after.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/__pycache__/before.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/__pycache__/before_sleep.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/__pycache__/nap.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/__pycache__/retry.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/__pycache__/stop.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/__pycache__/tornadoweb.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/__pycache__/wait.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/_asyncio.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/_utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/after.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/before.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/before_sleep.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/nap.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/retry.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/stop.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/tornadoweb.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tenacity/wait.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tomli/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tomli/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tomli/__pycache__/_parser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tomli/__pycache__/_re.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tomli/_parser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/tomli/_re.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/_collections.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/_version.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/exceptions.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/fields.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/filepost.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/response.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/_collections.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/_version.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/connection.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/connectionpool.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/appengine.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/securetransport.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/socks.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/exceptions.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/fields.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/filepost.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/__pycache__/six.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/six.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/poolmanager.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/request.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/response.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/connection.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/proxy.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/queue.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/request.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/response.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/retry.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/ssltransport.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/url.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/wait.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/connection.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/proxy.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/queue.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/request.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/response.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/retry.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/ssl_.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/ssltransport.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/timeout.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/url.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/wait.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/vendor.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/webencodings/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/webencodings/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/webencodings/__pycache__/labels.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/webencodings/__pycache__/mklabels.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/webencodings/__pycache__/tests.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/webencodings/labels.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/webencodings/mklabels.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/webencodings/tests.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/_vendor/webencodings/x_user_defined.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pip/py.typed delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/appdirs.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/pyparsing.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/appdirs.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__about__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/_typing.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/markers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/tags.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_structures.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_typing.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/markers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/requirements.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/specifiers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/tags.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/version.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/_vendor/pyparsing.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/extern/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/tests/data/my-test-package-source/__pycache__/setup.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pkg_resources/tests/data/my-test-package-source/setup.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pyparsing-2.4.7.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pyparsing-2.4.7.dist-info/LICENSE delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pyparsing-2.4.7.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pyparsing-2.4.7.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pyparsing-2.4.7.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pyparsing-2.4.7.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/pyparsing.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests-2.26.0.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests-2.26.0.dist-info/LICENSE delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests-2.26.0.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests-2.26.0.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests-2.26.0.dist-info/REQUESTED delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests-2.26.0.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests-2.26.0.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/__version__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/_internal_utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/adapters.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/api.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/auth.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/certs.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/cookies.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/exceptions.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/help.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/hooks.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/models.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/packages.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/sessions.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/status_codes.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/structures.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__pycache__/utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/__version__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/_internal_utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/adapters.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/api.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/auth.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/certs.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/cookies.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/exceptions.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/help.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/hooks.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/models.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/packages.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/sessions.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/status_codes.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/structures.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/requests/utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools-57.4.0.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools-57.4.0.dist-info/LICENSE delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools-57.4.0.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools-57.4.0.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools-57.4.0.dist-info/REQUESTED delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools-57.4.0.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools-57.4.0.dist-info/entry_points.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools-57.4.0.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/_deprecation_warning.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/_imp.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/archive_util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/build_meta.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/config.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/dep_util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/depends.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/dist.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/errors.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/extension.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/glob.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/installer.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/launch.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/lib2to3_ex.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/monkey.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/msvc.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/namespaces.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/package_index.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/py34compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/sandbox.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/unicode_utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/version.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/wheel.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/__pycache__/windows_support.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_deprecation_warning.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/_msvccompiler.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/archive_util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/bcppcompiler.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/ccompiler.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/cmd.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/config.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/core.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/cygwinccompiler.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/debug.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/dep_util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/dir_util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/dist.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/errors.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/extension.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/fancy_getopt.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/file_util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/filelist.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/log.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/msvc9compiler.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/msvccompiler.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/py35compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/py38compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/spawn.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/sysconfig.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/text_file.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/unixccompiler.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/version.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/versionpredicate.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/_msvccompiler.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/archive_util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/bcppcompiler.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/ccompiler.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/cmd.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/bdist.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/bdist_dumb.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/bdist_msi.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/bdist_rpm.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/bdist_wininst.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/build.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/build_clib.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/build_ext.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/build_py.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/build_scripts.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/check.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/clean.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/config.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/install.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/install_data.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/install_egg_info.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/install_headers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/install_lib.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/install_scripts.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/py37compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/register.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/sdist.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/upload.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/bdist.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/bdist_dumb.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/bdist_msi.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/bdist_rpm.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/bdist_wininst.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/build.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/build_clib.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/build_ext.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/build_py.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/build_scripts.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/check.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/clean.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/config.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/install.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/install_data.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/install_egg_info.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/install_headers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/install_lib.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/install_scripts.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/py37compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/register.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/sdist.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/command/upload.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/config.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/core.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/cygwinccompiler.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/debug.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/dep_util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/dir_util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/dist.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/errors.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/extension.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/fancy_getopt.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/file_util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/filelist.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/log.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/msvc9compiler.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/msvccompiler.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/py35compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/py38compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/spawn.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/sysconfig.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/text_file.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/unixccompiler.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/version.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_distutils/versionpredicate.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_imp.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/ordered_set.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/pyparsing.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/more_itertools/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/more_itertools/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/more_itertools/__pycache__/more.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/more_itertools/__pycache__/recipes.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/more_itertools/more.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/more_itertools/recipes.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/ordered_set.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__about__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/__about__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/_compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/_structures.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/_typing.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/markers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/requirements.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/tags.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/version.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/_compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/_structures.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/_typing.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/markers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/requirements.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/specifiers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/tags.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/version.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/_vendor/pyparsing.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/archive_util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/build_meta.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/cli-32.exe delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/cli-64.exe delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/cli.exe delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/alias.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/build_clib.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/build_ext.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/build_py.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/develop.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/dist_info.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/easy_install.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/egg_info.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/install.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/install_lib.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/install_scripts.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/py36compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/register.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/rotate.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/saveopts.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/sdist.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/setopt.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/upload.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/__pycache__/upload_docs.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/alias.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/bdist_egg.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/bdist_rpm.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/build_clib.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/build_ext.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/build_py.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/develop.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/dist_info.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/easy_install.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/egg_info.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/install.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/install_egg_info.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/install_lib.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/install_scripts.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/launcher manifest.xml delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/py36compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/register.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/rotate.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/saveopts.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/sdist.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/setopt.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/upload.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/command/upload_docs.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/config.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/dep_util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/depends.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/dist.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/errors.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/extension.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/extern/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/extern/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/glob.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/gui-32.exe delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/gui-64.exe delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/gui.exe delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/installer.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/launch.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/lib2to3_ex.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/monkey.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/msvc.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/namespaces.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/package_index.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/py34compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/sandbox.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/script (dev).tmpl delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/script.tmpl delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/unicode_utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/version.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/wheel.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/setuptools/windows_support.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve-2.2.1.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve-2.2.1.dist-info/LICENSE.md delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve-2.2.1.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve-2.2.1.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve-2.2.1.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve-2.2.1.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve/__meta__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve/__pycache__/__meta__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve/__pycache__/css_match.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve/__pycache__/css_parser.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve/__pycache__/css_types.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve/__pycache__/util.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve/css_match.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve/css_parser.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve/css_types.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/soupsieve/util.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3-1.26.7.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3-1.26.7.dist-info/LICENSE.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3-1.26.7.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3-1.26.7.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3-1.26.7.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3-1.26.7.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/__pycache__/_collections.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/__pycache__/_version.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/__pycache__/connection.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/__pycache__/connectionpool.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/__pycache__/exceptions.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/__pycache__/fields.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/__pycache__/filepost.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/__pycache__/poolmanager.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/__pycache__/request.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/__pycache__/response.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/_collections.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/_version.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/connection.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/connectionpool.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/__pycache__/_appengine_environ.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/__pycache__/appengine.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/__pycache__/ntlmpool.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/__pycache__/pyopenssl.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/__pycache__/securetransport.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/__pycache__/socks.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/_appengine_environ.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/_securetransport/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/_securetransport/bindings.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/_securetransport/low_level.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/appengine.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/ntlmpool.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/pyopenssl.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/securetransport.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/contrib/socks.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/exceptions.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/fields.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/filepost.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/packages/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/packages/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/packages/__pycache__/six.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/packages/backports/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/packages/backports/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/packages/backports/__pycache__/makefile.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/packages/backports/makefile.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/packages/six.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/packages/ssl_match_hostname/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/packages/ssl_match_hostname/_implementation.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/poolmanager.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/request.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/response.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/__pycache__/connection.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/__pycache__/proxy.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/__pycache__/queue.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/__pycache__/request.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/__pycache__/response.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/__pycache__/retry.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/__pycache__/ssl_.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/__pycache__/ssltransport.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/__pycache__/timeout.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/__pycache__/url.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/__pycache__/wait.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/connection.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/proxy.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/queue.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/request.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/response.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/retry.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/ssl_.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/ssltransport.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/timeout.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/url.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/urllib3/util/wait.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/whichcraft-0.6.1.dist-info/AUTHORS.rst delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/whichcraft-0.6.1.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/whichcraft-0.6.1.dist-info/LICENSE delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/whichcraft-0.6.1.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/whichcraft-0.6.1.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/whichcraft-0.6.1.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/whichcraft-0.6.1.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/whichcraft.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf-0.31.0.dist-info/AUTHORS delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf-0.31.0.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf-0.31.0.dist-info/LICENSE delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf-0.31.0.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf-0.31.0.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf-0.31.0.dist-info/REQUESTED delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf-0.31.0.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf-0.31.0.dist-info/entry_points.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf-0.31.0.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/__main__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/__pycache__/__main__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/blank_line_calculator.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/comment_splicer.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/continuation_splicer.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/errors.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/file_resources.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/format_decision_state.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/format_token.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/identify_container.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/line_joiner.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/object_state.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/py3compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/pytree_unwrapper.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/pytree_utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/pytree_visitor.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/reformatter.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/split_penalty.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/style.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/subtype_assigner.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/unwrapped_line.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/verifier.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/__pycache__/yapf_api.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/blank_line_calculator.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/comment_splicer.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/continuation_splicer.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/errors.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/file_resources.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/format_decision_state.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/format_token.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/identify_container.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/line_joiner.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/object_state.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/py3compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/pytree_unwrapper.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/pytree_utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/pytree_visitor.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/reformatter.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/split_penalty.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/style.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/subtype_assigner.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/unwrapped_line.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/verifier.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapf/yapflib/yapf_api.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/blank_line_calculator_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/comment_splicer_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/file_resources_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/format_decision_state_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/format_token_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/line_joiner_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/main_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/pytree_unwrapper_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/pytree_utils_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/pytree_visitor_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/reformatter_basic_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/reformatter_buganizer_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/reformatter_facebook_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/reformatter_pep8_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/reformatter_python3_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/reformatter_style_config_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/reformatter_verify_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/split_penalty_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/style_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/subtype_assigner_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/unwrapped_line_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/utils.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/yapf_test.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/__pycache__/yapf_test_helper.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/blank_line_calculator_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/comment_splicer_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/file_resources_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/format_decision_state_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/format_token_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/line_joiner_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/main_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/pytree_unwrapper_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/pytree_utils_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/pytree_visitor_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/reformatter_basic_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/reformatter_buganizer_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/reformatter_facebook_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/reformatter_pep8_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/reformatter_python3_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/reformatter_style_config_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/reformatter_verify_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/split_penalty_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/style_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/subtype_assigner_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/unwrapped_line_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/utils.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/yapf_test.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/yapftests/yapf_test_helper.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.event-4.5.0-py3.6-nspkg.pth delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.event-4.5.0.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.event-4.5.0.dist-info/LICENSE.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.event-4.5.0.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.event-4.5.0.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.event-4.5.0.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.event-4.5.0.dist-info/namespace_packages.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.event-4.5.0.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.interface-5.4.0-py3.9-nspkg.pth delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.interface-5.4.0.dist-info/INSTALLER delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.interface-5.4.0.dist-info/LICENSE.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.interface-5.4.0.dist-info/METADATA delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.interface-5.4.0.dist-info/RECORD delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.interface-5.4.0.dist-info/WHEEL delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.interface-5.4.0.dist-info/namespace_packages.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope.interface-5.4.0.dist-info/top_level.txt delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/event/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/event/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/event/__pycache__/classhandler.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/event/__pycache__/tests.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/event/classhandler.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/event/tests.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/__pycache__/_compat.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/__pycache__/_flatten.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/__pycache__/adapter.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/__pycache__/advice.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/__pycache__/declarations.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/__pycache__/document.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/__pycache__/exceptions.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/__pycache__/interface.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/__pycache__/interfaces.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/__pycache__/registry.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/__pycache__/ro.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/__pycache__/verify.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/_compat.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/_flatten.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/_zope_interface_coptimizations.c delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/_zope_interface_coptimizations.cpython-39-x86_64-linux-gnu.so delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/adapter.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/advice.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/__pycache__/builtins.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/__pycache__/collections.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/__pycache__/idatetime.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/__pycache__/interfaces.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/__pycache__/io.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/__pycache__/mapping.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/__pycache__/numbers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/__pycache__/sequence.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/builtins.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/collections.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/idatetime.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/interfaces.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/io.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/mapping.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/numbers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/sequence.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/__pycache__/basemapping.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/__pycache__/test_builtins.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/__pycache__/test_collections.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/__pycache__/test_idatetime.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/__pycache__/test_import_interfaces.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/__pycache__/test_io.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/__pycache__/test_numbers.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/basemapping.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/test_builtins.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/test_collections.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/test_idatetime.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/test_import_interfaces.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/test_io.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/common/tests/test_numbers.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/declarations.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/document.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/exceptions.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/interface.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/interfaces.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/registry.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/ro.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__init__.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/__init__.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/advisory_testing.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/dummy.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/idummy.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/m1.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/odd.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/test_adapter.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/test_advice.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/test_declarations.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/test_document.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/test_element.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/test_exceptions.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/test_interface.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/test_interfaces.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/test_odd_declarations.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/test_registry.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/test_ro.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/test_sorting.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/__pycache__/test_verify.cpython-39.pyc delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/advisory_testing.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/dummy.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/idummy.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/m1.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/odd.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/test_adapter.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/test_advice.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/test_declarations.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/test_document.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/test_element.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/test_exceptions.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/test_interface.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/test_interfaces.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/test_odd_declarations.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/test_registry.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/test_ro.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/test_sorting.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/tests/test_verify.py delete mode 100644 IKEA_scraper/.venv/lib/python3.9/site-packages/zope/interface/verify.py delete mode 120000 IKEA_scraper/.venv/lib64 delete mode 100644 IKEA_scraper/.venv/pyvenv.cfg diff --git a/IKEA_scraper/.venv/bin/Activate.ps1 b/IKEA_scraper/.venv/bin/Activate.ps1 deleted file mode 100644 index a3bc6fb1..00000000 --- a/IKEA_scraper/.venv/bin/Activate.ps1 +++ /dev/null @@ -1,241 +0,0 @@ -<# -.Synopsis -Activate a Python virtual environment for the current PowerShell session. - -.Description -Pushes the python executable for a virtual environment to the front of the -$Env:PATH environment variable and sets the prompt to signify that you are -in a Python virtual environment. Makes use of the command line switches as -well as the `pyvenv.cfg` file values present in the virtual environment. - -.Parameter VenvDir -Path to the directory that contains the virtual environment to activate. The -default value for this is the parent of the directory that the Activate.ps1 -script is located within. - -.Parameter Prompt -The prompt prefix to display when this virtual environment is activated. By -default, this prompt is the name of the virtual environment folder (VenvDir) -surrounded by parentheses and followed by a single space (ie. '(.venv) '). - -.Example -Activate.ps1 -Activates the Python virtual environment that contains the Activate.ps1 script. - -.Example -Activate.ps1 -Verbose -Activates the Python virtual environment that contains the Activate.ps1 script, -and shows extra information about the activation as it executes. - -.Example -Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv -Activates the Python virtual environment located in the specified location. - -.Example -Activate.ps1 -Prompt "MyPython" -Activates the Python virtual environment that contains the Activate.ps1 script, -and prefixes the current prompt with the specified string (surrounded in -parentheses) while the virtual environment is active. - -.Notes -On Windows, it may be required to enable this Activate.ps1 script by setting the -execution policy for the user. You can do this by issuing the following PowerShell -command: - -PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser - -For more information on Execution Policies: -https://go.microsoft.com/fwlink/?LinkID=135170 - -#> -Param( - [Parameter(Mandatory = $false)] - [String] - $VenvDir, - [Parameter(Mandatory = $false)] - [String] - $Prompt -) - -<# Function declarations --------------------------------------------------- #> - -<# -.Synopsis -Remove all shell session elements added by the Activate script, including the -addition of the virtual environment's Python executable from the beginning of -the PATH variable. - -.Parameter NonDestructive -If present, do not remove this function from the global namespace for the -session. - -#> -function global:deactivate ([switch]$NonDestructive) { - # Revert to original values - - # The prior prompt: - if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { - Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt - Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT - } - - # The prior PYTHONHOME: - if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { - Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME - Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME - } - - # The prior PATH: - if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { - Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH - Remove-Item -Path Env:_OLD_VIRTUAL_PATH - } - - # Just remove the VIRTUAL_ENV altogether: - if (Test-Path -Path Env:VIRTUAL_ENV) { - Remove-Item -Path env:VIRTUAL_ENV - } - - # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: - if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { - Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force - } - - # Leave deactivate function in the global namespace if requested: - if (-not $NonDestructive) { - Remove-Item -Path function:deactivate - } -} - -<# -.Description -Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the -given folder, and returns them in a map. - -For each line in the pyvenv.cfg file, if that line can be parsed into exactly -two strings separated by `=` (with any amount of whitespace surrounding the =) -then it is considered a `key = value` line. The left hand string is the key, -the right hand is the value. - -If the value starts with a `'` or a `"` then the first and last character is -stripped from the value before being captured. - -.Parameter ConfigDir -Path to the directory that contains the `pyvenv.cfg` file. -#> -function Get-PyVenvConfig( - [String] - $ConfigDir -) { - Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" - - # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). - $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue - - # An empty map will be returned if no config file is found. - $pyvenvConfig = @{ } - - if ($pyvenvConfigPath) { - - Write-Verbose "File exists, parse `key = value` lines" - $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath - - $pyvenvConfigContent | ForEach-Object { - $keyval = $PSItem -split "\s*=\s*", 2 - if ($keyval[0] -and $keyval[1]) { - $val = $keyval[1] - - # Remove extraneous quotations around a string value. - if ("'""".Contains($val.Substring(0, 1))) { - $val = $val.Substring(1, $val.Length - 2) - } - - $pyvenvConfig[$keyval[0]] = $val - Write-Verbose "Adding Key: '$($keyval[0])'='$val'" - } - } - } - return $pyvenvConfig -} - - -<# Begin Activate script --------------------------------------------------- #> - -# Determine the containing directory of this script -$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition -$VenvExecDir = Get-Item -Path $VenvExecPath - -Write-Verbose "Activation script is located in path: '$VenvExecPath'" -Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" -Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" - -# Set values required in priority: CmdLine, ConfigFile, Default -# First, get the location of the virtual environment, it might not be -# VenvExecDir if specified on the command line. -if ($VenvDir) { - Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" -} -else { - Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." - $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") - Write-Verbose "VenvDir=$VenvDir" -} - -# Next, read the `pyvenv.cfg` file to determine any required value such -# as `prompt`. -$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir - -# Next, set the prompt from the command line, or the config file, or -# just use the name of the virtual environment folder. -if ($Prompt) { - Write-Verbose "Prompt specified as argument, using '$Prompt'" -} -else { - Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" - if ($pyvenvCfg -and $pyvenvCfg['prompt']) { - Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" - $Prompt = $pyvenvCfg['prompt']; - } - else { - Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virutal environment)" - Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" - $Prompt = Split-Path -Path $venvDir -Leaf - } -} - -Write-Verbose "Prompt = '$Prompt'" -Write-Verbose "VenvDir='$VenvDir'" - -# Deactivate any currently active virtual environment, but leave the -# deactivate function in place. -deactivate -nondestructive - -# Now set the environment variable VIRTUAL_ENV, used by many tools to determine -# that there is an activated venv. -$env:VIRTUAL_ENV = $VenvDir - -if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { - - Write-Verbose "Setting prompt to '$Prompt'" - - # Set the prompt to include the env name - # Make sure _OLD_VIRTUAL_PROMPT is global - function global:_OLD_VIRTUAL_PROMPT { "" } - Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT - New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt - - function global:prompt { - Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " - _OLD_VIRTUAL_PROMPT - } -} - -# Clear PYTHONHOME -if (Test-Path -Path Env:PYTHONHOME) { - Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME - Remove-Item -Path Env:PYTHONHOME -} - -# Add the venv to the PATH -Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH -$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/IKEA_scraper/.venv/bin/__pycache__/bottle.cpython-39.pyc b/IKEA_scraper/.venv/bin/__pycache__/bottle.cpython-39.pyc deleted file mode 100644 index 9490621afe4f1c30482f29ce9a813672d5655b7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145438 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 ?*#7*rzF}<{F#DWZFS|Tvv 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

2u0V#9nk%b!S=53xRd_*TP1Ppa;N>!yl8jFK` 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 /dev/null - fi - - if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then - PS1="${_OLD_VIRTUAL_PS1:-}" - export PS1 - unset _OLD_VIRTUAL_PS1 - fi - - unset VIRTUAL_ENV - if [ ! "${1:-}" = "nondestructive" ] ; then - # Self destruct! - unset -f deactivate - fi -} - -# unset irrelevant variables -deactivate nondestructive - -VIRTUAL_ENV="/media/HardDrive/Pyhton/School/IKEA_scraper/.venv" -export VIRTUAL_ENV - -_OLD_VIRTUAL_PATH="$PATH" -PATH="$VIRTUAL_ENV/bin:$PATH" -export PATH - -# unset PYTHONHOME if set -# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) -# could use `if (set -u; : $PYTHONHOME) ;` in bash -if [ -n "${PYTHONHOME:-}" ] ; then - _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" - unset PYTHONHOME -fi - -if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then - _OLD_VIRTUAL_PS1="${PS1:-}" - PS1="(.venv) ${PS1:-}" - export PS1 -fi - -# This should detect bash and zsh, which have a hash command that must -# be called to get it to forget past commands. Without forgetting -# past commands the $PATH changes we made may not be respected -if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then - hash -r 2> /dev/null -fi diff --git a/IKEA_scraper/.venv/bin/activate.csh b/IKEA_scraper/.venv/bin/activate.csh deleted file mode 100644 index 53d06142..00000000 --- a/IKEA_scraper/.venv/bin/activate.csh +++ /dev/null @@ -1,25 +0,0 @@ -# This file must be used with "source bin/activate.csh" *from csh*. -# You cannot run it directly. -# Created by Davide Di Blasi . -# Ported to Python 3.3 venv by Andrew Svetlov - -alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' - -# Unset irrelevant variables. -deactivate nondestructive - -setenv VIRTUAL_ENV "/media/HardDrive/Pyhton/School/IKEA_scraper/.venv" - -set _OLD_VIRTUAL_PATH="$PATH" -setenv PATH "$VIRTUAL_ENV/bin:$PATH" - - -set _OLD_VIRTUAL_PROMPT="$prompt" - -if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then - set prompt = "(.venv) $prompt" -endif - -alias pydoc python -m pydoc - -rehash diff --git a/IKEA_scraper/.venv/bin/activate.fish b/IKEA_scraper/.venv/bin/activate.fish deleted file mode 100644 index e4aeb982..00000000 --- a/IKEA_scraper/.venv/bin/activate.fish +++ /dev/null @@ -1,64 +0,0 @@ -# This file must be used with "source /bin/activate.fish" *from fish* -# (https://fishshell.com/); you cannot run it directly. - -function deactivate -d "Exit virtual environment and return to normal shell environment" - # reset old environment variables - if test -n "$_OLD_VIRTUAL_PATH" - set -gx PATH $_OLD_VIRTUAL_PATH - set -e _OLD_VIRTUAL_PATH - end - if test -n "$_OLD_VIRTUAL_PYTHONHOME" - set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME - set -e _OLD_VIRTUAL_PYTHONHOME - end - - if test -n "$_OLD_FISH_PROMPT_OVERRIDE" - functions -e fish_prompt - set -e _OLD_FISH_PROMPT_OVERRIDE - functions -c _old_fish_prompt fish_prompt - functions -e _old_fish_prompt - end - - set -e VIRTUAL_ENV - if test "$argv[1]" != "nondestructive" - # Self-destruct! - functions -e deactivate - end -end - -# Unset irrelevant variables. -deactivate nondestructive - -set -gx VIRTUAL_ENV "/media/HardDrive/Pyhton/School/IKEA_scraper/.venv" - -set -gx _OLD_VIRTUAL_PATH $PATH -set -gx PATH "$VIRTUAL_ENV/bin" $PATH - -# Unset PYTHONHOME if set. -if set -q PYTHONHOME - set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME - set -e PYTHONHOME -end - -if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" - # fish uses a function instead of an env var to generate the prompt. - - # Save the current fish_prompt function as the function _old_fish_prompt. - functions -c fish_prompt _old_fish_prompt - - # With the original prompt function renamed, we can override with our own. - function fish_prompt - # Save the return status of the last command. - set -l old_status $status - - # Output the venv prompt; color taken from the blue of the Python logo. - printf "%s%s%s" (set_color 4B8BBE) "(.venv) " (set_color normal) - - # Restore the return status of the previous command. - echo "exit $old_status" | . - # Output the original/"old" prompt. - _old_fish_prompt - end - - set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" -end diff --git a/IKEA_scraper/.venv/bin/bottle.py b/IKEA_scraper/.venv/bin/bottle.py deleted file mode 100644 index 331e7ae0..00000000 --- a/IKEA_scraper/.venv/bin/bottle.py +++ /dev/null @@ -1,3771 +0,0 @@ -#!/media/HardDrive/Pyhton/School/IKEA_scraper/.venv/bin/python3 -# -*- coding: utf-8 -*- -""" -Bottle is a fast and simple micro-framework for small web applications. It -offers request dispatching (Routes) with url parameter support, templates, -a built-in HTTP Server and adapters for many third party WSGI/HTTP-server and -template engines - all in a single file and with no dependencies other than the -Python Standard Library. - -Homepage and documentation: http://bottlepy.org/ - -Copyright (c) 2016, Marcel Hellkamp. -License: MIT (see LICENSE for details) -""" - -from __future__ import with_statement - -__author__ = 'Marcel Hellkamp' -__version__ = '0.12.19' -__license__ = 'MIT' - -# The gevent server adapter needs to patch some modules before they are imported -# This is why we parse the commandline parameters here but handle them later -if __name__ == '__main__': - from optparse import OptionParser - _cmd_parser = OptionParser(usage="usage: %prog [options] package.module:app") - _opt = _cmd_parser.add_option - _opt("--version", action="store_true", help="show version number.") - _opt("-b", "--bind", metavar="ADDRESS", help="bind socket to ADDRESS.") - _opt("-s", "--server", default='wsgiref', help="use SERVER as backend.") - _opt("-p", "--plugin", action="append", help="install additional plugin/s.") - _opt("--debug", action="store_true", help="start server in debug mode.") - _opt("--reload", action="store_true", help="auto-reload on file changes.") - _cmd_options, _cmd_args = _cmd_parser.parse_args() - if _cmd_options.server and _cmd_options.server.startswith('gevent'): - import gevent.monkey; gevent.monkey.patch_all() - -import base64, cgi, email.utils, functools, hmac, itertools, mimetypes,\ - os, re, subprocess, sys, tempfile, threading, time, warnings, hashlib - -from datetime import date as datedate, datetime, timedelta -from tempfile import TemporaryFile -from traceback import format_exc, print_exc -from inspect import getargspec -from unicodedata import normalize - - -try: from simplejson import dumps as json_dumps, loads as json_lds -except ImportError: # pragma: no cover - try: from json import dumps as json_dumps, loads as json_lds - except ImportError: - try: from django.utils.simplejson import dumps as json_dumps, loads as json_lds - except ImportError: - def json_dumps(data): - raise ImportError("JSON support requires Python 2.6 or simplejson.") - json_lds = json_dumps - - - -# We now try to fix 2.5/2.6/3.1/3.2 incompatibilities. -# It ain't pretty but it works... Sorry for the mess. - -py = sys.version_info -py3k = py >= (3, 0, 0) -py25 = py < (2, 6, 0) -py31 = (3, 1, 0) <= py < (3, 2, 0) - -# Workaround for the missing "as" keyword in py3k. -def _e(): return sys.exc_info()[1] - -# Workaround for the "print is a keyword/function" Python 2/3 dilemma -# and a fallback for mod_wsgi (resticts stdout/err attribute access) -try: - _stdout, _stderr = sys.stdout.write, sys.stderr.write -except IOError: - _stdout = lambda x: sys.stdout.write(x) - _stderr = lambda x: sys.stderr.write(x) - -# Lots of stdlib and builtin differences. -if py3k: - import http.client as httplib - import _thread as thread - from urllib.parse import urljoin, SplitResult as UrlSplitResult - from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote - urlunquote = functools.partial(urlunquote, encoding='latin1') - from http.cookies import SimpleCookie - if py >= (3, 3, 0): - from collections.abc import MutableMapping as DictMixin - from types import ModuleType as new_module - else: - from collections import MutableMapping as DictMixin - from imp import new_module - import pickle - from io import BytesIO - from configparser import ConfigParser - basestring = str - unicode = str - json_loads = lambda s: json_lds(touni(s)) - callable = lambda x: hasattr(x, '__call__') - imap = map - def _raise(*a): raise a[0](a[1]).with_traceback(a[2]) -else: # 2.x - import httplib - import thread - from urlparse import urljoin, SplitResult as UrlSplitResult - from urllib import urlencode, quote as urlquote, unquote as urlunquote - from Cookie import SimpleCookie - from itertools import imap - import cPickle as pickle - from imp import new_module - from StringIO import StringIO as BytesIO - from ConfigParser import SafeConfigParser as ConfigParser - if py25: - msg = "Python 2.5 support may be dropped in future versions of Bottle." - warnings.warn(msg, DeprecationWarning) - from UserDict import DictMixin - def next(it): return it.next() - bytes = str - else: # 2.6, 2.7 - from collections import MutableMapping as DictMixin - unicode = unicode - json_loads = json_lds - eval(compile('def _raise(*a): raise a[0], a[1], a[2]', '', 'exec')) - -# Some helpers for string/byte handling -def tob(s, enc='utf8'): - return s.encode(enc) if isinstance(s, unicode) else bytes(s) -def touni(s, enc='utf8', err='strict'): - return s.decode(enc, err) if isinstance(s, bytes) else unicode(s) -tonat = touni if py3k else tob - -# 3.2 fixes cgi.FieldStorage to accept bytes (which makes a lot of sense). -# 3.1 needs a workaround. -if py31: - from io import TextIOWrapper - class NCTextIOWrapper(TextIOWrapper): - def close(self): pass # Keep wrapped buffer open. - - -# A bug in functools causes it to break if the wrapper is an instance method -def update_wrapper(wrapper, wrapped, *a, **ka): - try: functools.update_wrapper(wrapper, wrapped, *a, **ka) - except AttributeError: pass - - - -# These helpers are used at module level and need to be defined first. -# And yes, I know PEP-8, but sometimes a lower-case classname makes more sense. - -def depr(message, hard=False): - warnings.warn(message, DeprecationWarning, stacklevel=3) - -def makelist(data): # This is just to handy - if isinstance(data, (tuple, list, set, dict)): return list(data) - elif data: return [data] - else: return [] - - -class DictProperty(object): - ''' Property that maps to a key in a local dict-like attribute. ''' - def __init__(self, attr, key=None, read_only=False): - self.attr, self.key, self.read_only = attr, key, read_only - - def __call__(self, func): - functools.update_wrapper(self, func, updated=[]) - self.getter, self.key = func, self.key or func.__name__ - return self - - def __get__(self, obj, cls): - if obj is None: return self - key, storage = self.key, getattr(obj, self.attr) - if key not in storage: storage[key] = self.getter(obj) - return storage[key] - - def __set__(self, obj, value): - if self.read_only: raise AttributeError("Read-Only property.") - getattr(obj, self.attr)[self.key] = value - - def __delete__(self, obj): - if self.read_only: raise AttributeError("Read-Only property.") - del getattr(obj, self.attr)[self.key] - - -class cached_property(object): - ''' A property that is only computed once per instance and then replaces - itself with an ordinary attribute. Deleting the attribute resets the - property. ''' - - def __init__(self, func): - self.__doc__ = getattr(func, '__doc__') - self.func = func - - def __get__(self, obj, cls): - if obj is None: return self - value = obj.__dict__[self.func.__name__] = self.func(obj) - return value - - -class lazy_attribute(object): - ''' A property that caches itself to the class object. ''' - def __init__(self, func): - functools.update_wrapper(self, func, updated=[]) - self.getter = func - - def __get__(self, obj, cls): - value = self.getter(cls) - setattr(cls, self.__name__, value) - return value - - - - - - -############################################################################### -# Exceptions and Events ######################################################## -############################################################################### - - -class BottleException(Exception): - """ A base class for exceptions used by bottle. """ - pass - - - - - - -############################################################################### -# Routing ###################################################################### -############################################################################### - - -class RouteError(BottleException): - """ This is a base class for all routing related exceptions """ - - -class RouteReset(BottleException): - """ If raised by a plugin or request handler, the route is reset and all - plugins are re-applied. """ - -class RouterUnknownModeError(RouteError): pass - - -class RouteSyntaxError(RouteError): - """ The route parser found something not supported by this router. """ - - -class RouteBuildError(RouteError): - """ The route could not be built. """ - - -def _re_flatten(p): - ''' Turn all capturing groups in a regular expression pattern into - non-capturing groups. ''' - if '(' not in p: return p - return re.sub(r'(\\*)(\(\?P<[^>]+>|\((?!\?))', - lambda m: m.group(0) if len(m.group(1)) % 2 else m.group(1) + '(?:', p) - - -class Router(object): - ''' A Router is an ordered collection of route->target pairs. It is used to - efficiently match WSGI requests against a number of routes and return - the first target that satisfies the request. The target may be anything, - usually a string, ID or callable object. A route consists of a path-rule - and a HTTP method. - - The path-rule is either a static path (e.g. `/contact`) or a dynamic - path that contains wildcards (e.g. `/wiki/`). The wildcard syntax - and details on the matching order are described in docs:`routing`. - ''' - - default_pattern = '[^/]+' - default_filter = 're' - - #: The current CPython regexp implementation does not allow more - #: than 99 matching groups per regular expression. - _MAX_GROUPS_PER_PATTERN = 99 - - def __init__(self, strict=False): - self.rules = [] # All rules in order - self._groups = {} # index of regexes to find them in dyna_routes - self.builder = {} # Data structure for the url builder - self.static = {} # Search structure for static routes - self.dyna_routes = {} - self.dyna_regexes = {} # Search structure for dynamic routes - #: If true, static routes are no longer checked first. - self.strict_order = strict - self.filters = { - 're': lambda conf: - (_re_flatten(conf or self.default_pattern), None, None), - 'int': lambda conf: (r'-?\d+', int, lambda x: str(int(x))), - 'float': lambda conf: (r'-?[\d.]+', float, lambda x: str(float(x))), - 'path': lambda conf: (r'.+?', None, None)} - - def add_filter(self, name, func): - ''' Add a filter. The provided function is called with the configuration - string as parameter and must return a (regexp, to_python, to_url) tuple. - The first element is a string, the last two are callables or None. ''' - self.filters[name] = func - - rule_syntax = re.compile('(\\\\*)'\ - '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)'\ - '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)'\ - '(?::((?:\\\\.|[^\\\\>]+)+)?)?)?>))') - - def _itertokens(self, rule): - offset, prefix = 0, '' - for match in self.rule_syntax.finditer(rule): - prefix += rule[offset:match.start()] - g = match.groups() - if len(g[0])%2: # Escaped wildcard - prefix += match.group(0)[len(g[0]):] - offset = match.end() - continue - if prefix: - yield prefix, None, None - name, filtr, conf = g[4:7] if g[2] is None else g[1:4] - yield name, filtr or 'default', conf or None - offset, prefix = match.end(), '' - if offset <= len(rule) or prefix: - yield prefix+rule[offset:], None, None - - def add(self, rule, method, target, name=None): - ''' Add a new rule or replace the target for an existing rule. ''' - anons = 0 # Number of anonymous wildcards found - keys = [] # Names of keys - pattern = '' # Regular expression pattern with named groups - filters = [] # Lists of wildcard input filters - builder = [] # Data structure for the URL builder - is_static = True - - for key, mode, conf in self._itertokens(rule): - if mode: - is_static = False - if mode == 'default': mode = self.default_filter - mask, in_filter, out_filter = self.filters[mode](conf) - if not key: - pattern += '(?:%s)' % mask - key = 'anon%d' % anons - anons += 1 - else: - pattern += '(?P<%s>%s)' % (key, mask) - keys.append(key) - if in_filter: filters.append((key, in_filter)) - builder.append((key, out_filter or str)) - elif key: - pattern += re.escape(key) - builder.append((None, key)) - - self.builder[rule] = builder - if name: self.builder[name] = builder - - if is_static and not self.strict_order: - self.static.setdefault(method, {}) - self.static[method][self.build(rule)] = (target, None) - return - - try: - re_pattern = re.compile('^(%s)$' % pattern) - re_match = re_pattern.match - except re.error: - raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, _e())) - - if filters: - def getargs(path): - url_args = re_match(path).groupdict() - for name, wildcard_filter in filters: - try: - url_args[name] = wildcard_filter(url_args[name]) - except ValueError: - raise HTTPError(400, 'Path has wrong format.') - return url_args - elif re_pattern.groupindex: - def getargs(path): - return re_match(path).groupdict() - else: - getargs = None - - flatpat = _re_flatten(pattern) - whole_rule = (rule, flatpat, target, getargs) - - if (flatpat, method) in self._groups: - if DEBUG: - msg = 'Route <%s %s> overwrites a previously defined route' - warnings.warn(msg % (method, rule), RuntimeWarning) - self.dyna_routes[method][self._groups[flatpat, method]] = whole_rule - else: - self.dyna_routes.setdefault(method, []).append(whole_rule) - self._groups[flatpat, method] = len(self.dyna_routes[method]) - 1 - - self._compile(method) - - def _compile(self, method): - all_rules = self.dyna_routes[method] - comborules = self.dyna_regexes[method] = [] - maxgroups = self._MAX_GROUPS_PER_PATTERN - for x in range(0, len(all_rules), maxgroups): - some = all_rules[x:x+maxgroups] - combined = (flatpat for (_, flatpat, _, _) in some) - combined = '|'.join('(^%s$)' % flatpat for flatpat in combined) - combined = re.compile(combined).match - rules = [(target, getargs) for (_, _, target, getargs) in some] - comborules.append((combined, rules)) - - def build(self, _name, *anons, **query): - ''' Build an URL by filling the wildcards in a rule. ''' - builder = self.builder.get(_name) - if not builder: raise RouteBuildError("No route with that name.", _name) - try: - for i, value in enumerate(anons): query['anon%d'%i] = value - url = ''.join([f(query.pop(n)) if n else f for (n,f) in builder]) - return url if not query else url+'?'+urlencode(query) - except KeyError: - raise RouteBuildError('Missing URL argument: %r' % _e().args[0]) - - def match(self, environ): - ''' Return a (target, url_agrs) tuple or raise HTTPError(400/404/405). ''' - verb = environ['REQUEST_METHOD'].upper() - path = environ['PATH_INFO'] or '/' - target = None - if verb == 'HEAD': - methods = ['PROXY', verb, 'GET', 'ANY'] - else: - methods = ['PROXY', verb, 'ANY'] - - for method in methods: - if method in self.static and path in self.static[method]: - target, getargs = self.static[method][path] - return target, getargs(path) if getargs else {} - elif method in self.dyna_regexes: - for combined, rules in self.dyna_regexes[method]: - match = combined(path) - if match: - target, getargs = rules[match.lastindex - 1] - return target, getargs(path) if getargs else {} - - # No matching route found. Collect alternative methods for 405 response - allowed = set([]) - nocheck = set(methods) - for method in set(self.static) - nocheck: - if path in self.static[method]: - allowed.add(method) - for method in set(self.dyna_regexes) - allowed - nocheck: - for combined, rules in self.dyna_regexes[method]: - match = combined(path) - if match: - allowed.add(method) - if allowed: - allow_header = ",".join(sorted(allowed)) - raise HTTPError(405, "Method not allowed.", Allow=allow_header) - - # No matching route and no alternative method found. We give up - raise HTTPError(404, "Not found: " + repr(path)) - - - - - - -class Route(object): - ''' This class wraps a route callback along with route specific metadata and - configuration and applies Plugins on demand. It is also responsible for - turing an URL path rule into a regular expression usable by the Router. - ''' - - def __init__(self, app, rule, method, callback, name=None, - plugins=None, skiplist=None, **config): - #: The application this route is installed to. - self.app = app - #: The path-rule string (e.g. ``/wiki/:page``). - self.rule = rule - #: The HTTP method as a string (e.g. ``GET``). - self.method = method - #: The original callback with no plugins applied. Useful for introspection. - self.callback = callback - #: The name of the route (if specified) or ``None``. - self.name = name or None - #: A list of route-specific plugins (see :meth:`Bottle.route`). - self.plugins = plugins or [] - #: A list of plugins to not apply to this route (see :meth:`Bottle.route`). - self.skiplist = skiplist or [] - #: Additional keyword arguments passed to the :meth:`Bottle.route` - #: decorator are stored in this dictionary. Used for route-specific - #: plugin configuration and meta-data. - self.config = ConfigDict().load_dict(config, make_namespaces=True) - - def __call__(self, *a, **ka): - depr("Some APIs changed to return Route() instances instead of"\ - " callables. Make sure to use the Route.call method and not to"\ - " call Route instances directly.") #0.12 - return self.call(*a, **ka) - - @cached_property - def call(self): - ''' The route callback with all plugins applied. This property is - created on demand and then cached to speed up subsequent requests.''' - return self._make_callback() - - def reset(self): - ''' Forget any cached values. The next time :attr:`call` is accessed, - all plugins are re-applied. ''' - self.__dict__.pop('call', None) - - def prepare(self): - ''' Do all on-demand work immediately (useful for debugging).''' - self.call - - @property - def _context(self): - depr('Switch to Plugin API v2 and access the Route object directly.') #0.12 - return dict(rule=self.rule, method=self.method, callback=self.callback, - name=self.name, app=self.app, config=self.config, - apply=self.plugins, skip=self.skiplist) - - def all_plugins(self): - ''' Yield all Plugins affecting this route. ''' - unique = set() - for p in reversed(self.app.plugins + self.plugins): - if True in self.skiplist: break - name = getattr(p, 'name', False) - if name and (name in self.skiplist or name in unique): continue - if p in self.skiplist or type(p) in self.skiplist: continue - if name: unique.add(name) - yield p - - def _make_callback(self): - callback = self.callback - for plugin in self.all_plugins(): - try: - if hasattr(plugin, 'apply'): - api = getattr(plugin, 'api', 1) - context = self if api > 1 else self._context - callback = plugin.apply(callback, context) - else: - callback = plugin(callback) - except RouteReset: # Try again with changed configuration. - return self._make_callback() - if not callback is self.callback: - update_wrapper(callback, self.callback) - return callback - - def get_undecorated_callback(self): - ''' Return the callback. If the callback is a decorated function, try to - recover the original function. ''' - func = self.callback - func = getattr(func, '__func__' if py3k else 'im_func', func) - closure_attr = '__closure__' if py3k else 'func_closure' - while hasattr(func, closure_attr) and getattr(func, closure_attr): - func = getattr(func, closure_attr)[0].cell_contents - return func - - def get_callback_args(self): - ''' Return a list of argument names the callback (most likely) accepts - as keyword arguments. If the callback is a decorated function, try - to recover the original function before inspection. ''' - return getargspec(self.get_undecorated_callback())[0] - - def get_config(self, key, default=None): - ''' Lookup a config field and return its value, first checking the - route.config, then route.app.config.''' - for conf in (self.config, self.app.conifg): - if key in conf: return conf[key] - return default - - def __repr__(self): - cb = self.get_undecorated_callback() - return '<%s %r %r>' % (self.method, self.rule, cb) - - - - - - -############################################################################### -# Application Object ########################################################### -############################################################################### - - -class Bottle(object): - """ Each Bottle object represents a single, distinct web application and - consists of routes, callbacks, plugins, resources and configuration. - Instances are callable WSGI applications. - - :param catchall: If true (default), handle all exceptions. Turn off to - let debugging middleware handle exceptions. - """ - - def __init__(self, catchall=True, autojson=True): - - #: A :class:`ConfigDict` for app specific configuration. - self.config = ConfigDict() - self.config._on_change = functools.partial(self.trigger_hook, 'config') - self.config.meta_set('autojson', 'validate', bool) - self.config.meta_set('catchall', 'validate', bool) - self.config['catchall'] = catchall - self.config['autojson'] = autojson - - #: A :class:`ResourceManager` for application files - self.resources = ResourceManager() - - self.routes = [] # List of installed :class:`Route` instances. - self.router = Router() # Maps requests to :class:`Route` instances. - self.error_handler = {} - - # Core plugins - self.plugins = [] # List of installed plugins. - if self.config['autojson']: - self.install(JSONPlugin()) - self.install(TemplatePlugin()) - - #: If true, most exceptions are caught and returned as :exc:`HTTPError` - catchall = DictProperty('config', 'catchall') - - __hook_names = 'before_request', 'after_request', 'app_reset', 'config' - __hook_reversed = 'after_request' - - @cached_property - def _hooks(self): - return dict((name, []) for name in self.__hook_names) - - def add_hook(self, name, func): - ''' Attach a callback to a hook. Three hooks are currently implemented: - - before_request - Executed once before each request. The request context is - available, but no routing has happened yet. - after_request - Executed once after each request regardless of its outcome. - app_reset - Called whenever :meth:`Bottle.reset` is called. - ''' - if name in self.__hook_reversed: - self._hooks[name].insert(0, func) - else: - self._hooks[name].append(func) - - def remove_hook(self, name, func): - ''' Remove a callback from a hook. ''' - if name in self._hooks and func in self._hooks[name]: - self._hooks[name].remove(func) - return True - - def trigger_hook(self, __name, *args, **kwargs): - ''' Trigger a hook and return a list of results. ''' - return [hook(*args, **kwargs) for hook in self._hooks[__name][:]] - - def hook(self, name): - """ Return a decorator that attaches a callback to a hook. See - :meth:`add_hook` for details.""" - def decorator(func): - self.add_hook(name, func) - return func - return decorator - - def mount(self, prefix, app, **options): - ''' Mount an application (:class:`Bottle` or plain WSGI) to a specific - URL prefix. Example:: - - root_app.mount('/admin/', admin_app) - - :param prefix: path prefix or `mount-point`. If it ends in a slash, - that slash is mandatory. - :param app: an instance of :class:`Bottle` or a WSGI application. - - All other parameters are passed to the underlying :meth:`route` call. - ''' - if isinstance(app, basestring): - depr('Parameter order of Bottle.mount() changed.', True) # 0.10 - - segments = [p for p in prefix.split('/') if p] - if not segments: raise ValueError('Empty path prefix.') - path_depth = len(segments) - - def mountpoint_wrapper(): - try: - request.path_shift(path_depth) - rs = HTTPResponse([]) - def start_response(status, headerlist, exc_info=None): - if exc_info: - try: - _raise(*exc_info) - finally: - exc_info = None - rs.status = status - for name, value in headerlist: rs.add_header(name, value) - return rs.body.append - body = app(request.environ, start_response) - if body and rs.body: body = itertools.chain(rs.body, body) - rs.body = body or rs.body - return rs - finally: - request.path_shift(-path_depth) - - options.setdefault('skip', True) - options.setdefault('method', 'PROXY') - options.setdefault('mountpoint', {'prefix': prefix, 'target': app}) - options['callback'] = mountpoint_wrapper - - self.route('/%s/<:re:.*>' % '/'.join(segments), **options) - if not prefix.endswith('/'): - self.route('/' + '/'.join(segments), **options) - - def merge(self, routes): - ''' Merge the routes of another :class:`Bottle` application or a list of - :class:`Route` objects into this application. The routes keep their - 'owner', meaning that the :data:`Route.app` attribute is not - changed. ''' - if isinstance(routes, Bottle): - routes = routes.routes - for route in routes: - self.add_route(route) - - def install(self, plugin): - ''' Add a plugin to the list of plugins and prepare it for being - applied to all routes of this application. A plugin may be a simple - decorator or an object that implements the :class:`Plugin` API. - ''' - if hasattr(plugin, 'setup'): plugin.setup(self) - if not callable(plugin) and not hasattr(plugin, 'apply'): - raise TypeError("Plugins must be callable or implement .apply()") - self.plugins.append(plugin) - self.reset() - return plugin - - def uninstall(self, plugin): - ''' Uninstall plugins. Pass an instance to remove a specific plugin, a type - object to remove all plugins that match that type, a string to remove - all plugins with a matching ``name`` attribute or ``True`` to remove all - plugins. Return the list of removed plugins. ''' - removed, remove = [], plugin - for i, plugin in list(enumerate(self.plugins))[::-1]: - if remove is True or remove is plugin or remove is type(plugin) \ - or getattr(plugin, 'name', True) == remove: - removed.append(plugin) - del self.plugins[i] - if hasattr(plugin, 'close'): plugin.close() - if removed: self.reset() - return removed - - def reset(self, route=None): - ''' Reset all routes (force plugins to be re-applied) and clear all - caches. If an ID or route object is given, only that specific route - is affected. ''' - if route is None: routes = self.routes - elif isinstance(route, Route): routes = [route] - else: routes = [self.routes[route]] - for route in routes: route.reset() - if DEBUG: - for route in routes: route.prepare() - self.trigger_hook('app_reset') - - def close(self): - ''' Close the application and all installed plugins. ''' - for plugin in self.plugins: - if hasattr(plugin, 'close'): plugin.close() - self.stopped = True - - def run(self, **kwargs): - ''' Calls :func:`run` with the same parameters. ''' - run(self, **kwargs) - - def match(self, environ): - """ Search for a matching route and return a (:class:`Route` , urlargs) - tuple. The second value is a dictionary with parameters extracted - from the URL. Raise :exc:`HTTPError` (404/405) on a non-match.""" - return self.router.match(environ) - - def get_url(self, routename, **kargs): - """ Return a string that matches a named route """ - scriptname = request.environ.get('SCRIPT_NAME', '').strip('/') + '/' - location = self.router.build(routename, **kargs).lstrip('/') - return urljoin(urljoin('/', scriptname), location) - - def add_route(self, route): - ''' Add a route object, but do not change the :data:`Route.app` - attribute.''' - self.routes.append(route) - self.router.add(route.rule, route.method, route, name=route.name) - if DEBUG: route.prepare() - - def route(self, path=None, method='GET', callback=None, name=None, - apply=None, skip=None, **config): - """ A decorator to bind a function to a request URL. Example:: - - @app.route('/hello/:name') - def hello(name): - return 'Hello %s' % name - - The ``:name`` part is a wildcard. See :class:`Router` for syntax - details. - - :param path: Request path or a list of paths to listen to. If no - path is specified, it is automatically generated from the - signature of the function. - :param method: HTTP method (`GET`, `POST`, `PUT`, ...) or a list of - methods to listen to. (default: `GET`) - :param callback: An optional shortcut to avoid the decorator - syntax. ``route(..., callback=func)`` equals ``route(...)(func)`` - :param name: The name for this route. (default: None) - :param apply: A decorator or plugin or a list of plugins. These are - applied to the route callback in addition to installed plugins. - :param skip: A list of plugins, plugin classes or names. Matching - plugins are not installed to this route. ``True`` skips all. - - Any additional keyword arguments are stored as route-specific - configuration and passed to plugins (see :meth:`Plugin.apply`). - """ - if callable(path): path, callback = None, path - plugins = makelist(apply) - skiplist = makelist(skip) - def decorator(callback): - # TODO: Documentation and tests - if isinstance(callback, basestring): callback = load(callback) - for rule in makelist(path) or yieldroutes(callback): - for verb in makelist(method): - verb = verb.upper() - route = Route(self, rule, verb, callback, name=name, - plugins=plugins, skiplist=skiplist, **config) - self.add_route(route) - return callback - return decorator(callback) if callback else decorator - - def get(self, path=None, method='GET', **options): - """ Equals :meth:`route`. """ - return self.route(path, method, **options) - - def post(self, path=None, method='POST', **options): - """ Equals :meth:`route` with a ``POST`` method parameter. """ - return self.route(path, method, **options) - - def put(self, path=None, method='PUT', **options): - """ Equals :meth:`route` with a ``PUT`` method parameter. """ - return self.route(path, method, **options) - - def delete(self, path=None, method='DELETE', **options): - """ Equals :meth:`route` with a ``DELETE`` method parameter. """ - return self.route(path, method, **options) - - def error(self, code=500): - """ Decorator: Register an output handler for a HTTP error code""" - def wrapper(handler): - self.error_handler[int(code)] = handler - return handler - return wrapper - - def default_error_handler(self, res): - return tob(template(ERROR_PAGE_TEMPLATE, e=res)) - - def _handle(self, environ): - path = environ['bottle.raw_path'] = environ['PATH_INFO'] - if py3k: - try: - environ['PATH_INFO'] = path.encode('latin1').decode('utf8') - except UnicodeError: - return HTTPError(400, 'Invalid path string. Expected UTF-8') - - try: - environ['bottle.app'] = self - request.bind(environ) - response.bind() - try: - self.trigger_hook('before_request') - route, args = self.router.match(environ) - environ['route.handle'] = route - environ['bottle.route'] = route - environ['route.url_args'] = args - return route.call(**args) - finally: - self.trigger_hook('after_request') - - except HTTPResponse: - return _e() - except RouteReset: - route.reset() - return self._handle(environ) - except (KeyboardInterrupt, SystemExit, MemoryError): - raise - except Exception: - if not self.catchall: raise - stacktrace = format_exc() - environ['wsgi.errors'].write(stacktrace) - return HTTPError(500, "Internal Server Error", _e(), stacktrace) - - def _cast(self, out, peek=None): - """ Try to convert the parameter into something WSGI compatible and set - correct HTTP headers when possible. - Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like, - iterable of strings and iterable of unicodes - """ - - # Empty output is done here - if not out: - if 'Content-Length' not in response: - response['Content-Length'] = 0 - return [] - # Join lists of byte or unicode strings. Mixed lists are NOT supported - if isinstance(out, (tuple, list))\ - and isinstance(out[0], (bytes, unicode)): - out = out[0][0:0].join(out) # b'abc'[0:0] -> b'' - # Encode unicode strings - if isinstance(out, unicode): - out = out.encode(response.charset) - # Byte Strings are just returned - if isinstance(out, bytes): - if 'Content-Length' not in response: - response['Content-Length'] = len(out) - return [out] - # HTTPError or HTTPException (recursive, because they may wrap anything) - # TODO: Handle these explicitly in handle() or make them iterable. - if isinstance(out, HTTPError): - out.apply(response) - out = self.error_handler.get(out.status_code, self.default_error_handler)(out) - return self._cast(out) - if isinstance(out, HTTPResponse): - out.apply(response) - return self._cast(out.body) - - # File-like objects. - if hasattr(out, 'read'): - if 'wsgi.file_wrapper' in request.environ: - return request.environ['wsgi.file_wrapper'](out) - elif hasattr(out, 'close') or not hasattr(out, '__iter__'): - return WSGIFileWrapper(out) - - # Handle Iterables. We peek into them to detect their inner type. - try: - iout = iter(out) - first = next(iout) - while not first: - first = next(iout) - except StopIteration: - return self._cast('') - except HTTPResponse: - first = _e() - except (KeyboardInterrupt, SystemExit, MemoryError): - raise - except Exception: - if not self.catchall: raise - first = HTTPError(500, 'Unhandled exception', _e(), format_exc()) - - # These are the inner types allowed in iterator or generator objects. - if isinstance(first, HTTPResponse): - return self._cast(first) - elif isinstance(first, bytes): - new_iter = itertools.chain([first], iout) - elif isinstance(first, unicode): - encoder = lambda x: x.encode(response.charset) - new_iter = imap(encoder, itertools.chain([first], iout)) - else: - msg = 'Unsupported response type: %s' % type(first) - return self._cast(HTTPError(500, msg)) - if hasattr(out, 'close'): - new_iter = _closeiter(new_iter, out.close) - return new_iter - - def wsgi(self, environ, start_response): - """ The bottle WSGI-interface. """ - try: - out = self._cast(self._handle(environ)) - # rfc2616 section 4.3 - if response._status_code in (100, 101, 204, 304)\ - or environ['REQUEST_METHOD'] == 'HEAD': - if hasattr(out, 'close'): out.close() - out = [] - start_response(response._status_line, response.headerlist) - return out - except (KeyboardInterrupt, SystemExit, MemoryError): - raise - except Exception: - if not self.catchall: raise - err = '

Critical error while processing request: %s

' \ - % html_escape(environ.get('PATH_INFO', '/')) - if DEBUG: - err += '

Error:

\n
\n%s\n
\n' \ - '

Traceback:

\n
\n%s\n
\n' \ - % (html_escape(repr(_e())), html_escape(format_exc())) - environ['wsgi.errors'].write(err) - headers = [('Content-Type', 'text/html; charset=UTF-8')] - start_response('500 INTERNAL SERVER ERROR', headers, sys.exc_info()) - return [tob(err)] - - def __call__(self, environ, start_response): - ''' Each instance of :class:'Bottle' is a WSGI application. ''' - return self.wsgi(environ, start_response) - - - - - - -############################################################################### -# HTTP and WSGI Tools ########################################################## -############################################################################### - -class BaseRequest(object): - """ A wrapper for WSGI environment dictionaries that adds a lot of - convenient access methods and properties. Most of them are read-only. - - Adding new attributes to a request actually adds them to the environ - dictionary (as 'bottle.request.ext.'). This is the recommended - way to store and access request-specific data. - """ - - __slots__ = ('environ') - - #: Maximum size of memory buffer for :attr:`body` in bytes. - MEMFILE_MAX = 102400 - - def __init__(self, environ=None): - """ Wrap a WSGI environ dictionary. """ - #: The wrapped WSGI environ dictionary. This is the only real attribute. - #: All other attributes actually are read-only properties. - self.environ = {} if environ is None else environ - self.environ['bottle.request'] = self - - @DictProperty('environ', 'bottle.app', read_only=True) - def app(self): - ''' Bottle application handling this request. ''' - raise RuntimeError('This request is not connected to an application.') - - @DictProperty('environ', 'bottle.route', read_only=True) - def route(self): - """ The bottle :class:`Route` object that matches this request. """ - raise RuntimeError('This request is not connected to a route.') - - @DictProperty('environ', 'route.url_args', read_only=True) - def url_args(self): - """ The arguments extracted from the URL. """ - raise RuntimeError('This request is not connected to a route.') - - @property - def path(self): - ''' The value of ``PATH_INFO`` with exactly one prefixed slash (to fix - broken clients and avoid the "empty path" edge case). ''' - return '/' + self.environ.get('PATH_INFO','').lstrip('/') - - @property - def method(self): - ''' The ``REQUEST_METHOD`` value as an uppercase string. ''' - return self.environ.get('REQUEST_METHOD', 'GET').upper() - - @DictProperty('environ', 'bottle.request.headers', read_only=True) - def headers(self): - ''' A :class:`WSGIHeaderDict` that provides case-insensitive access to - HTTP request headers. ''' - return WSGIHeaderDict(self.environ) - - def get_header(self, name, default=None): - ''' Return the value of a request header, or a given default value. ''' - return self.headers.get(name, default) - - @DictProperty('environ', 'bottle.request.cookies', read_only=True) - def cookies(self): - """ Cookies parsed into a :class:`FormsDict`. Signed cookies are NOT - decoded. Use :meth:`get_cookie` if you expect signed cookies. """ - cookies = SimpleCookie(self.environ.get('HTTP_COOKIE','')).values() - return FormsDict((c.key, c.value) for c in cookies) - - def get_cookie(self, key, default=None, secret=None): - """ Return the content of a cookie. To read a `Signed Cookie`, the - `secret` must match the one used to create the cookie (see - :meth:`BaseResponse.set_cookie`). If anything goes wrong (missing - cookie or wrong signature), return a default value. """ - value = self.cookies.get(key) - if secret and value: - dec = cookie_decode(value, secret) # (key, value) tuple or None - return dec[1] if dec and dec[0] == key else default - return value or default - - @DictProperty('environ', 'bottle.request.query', read_only=True) - def query(self): - ''' The :attr:`query_string` parsed into a :class:`FormsDict`. These - values are sometimes called "URL arguments" or "GET parameters", but - not to be confused with "URL wildcards" as they are provided by the - :class:`Router`. ''' - get = self.environ['bottle.get'] = FormsDict() - pairs = _parse_qsl(self.environ.get('QUERY_STRING', '')) - for key, value in pairs: - get[key] = value - return get - - @DictProperty('environ', 'bottle.request.forms', read_only=True) - def forms(self): - """ Form values parsed from an `url-encoded` or `multipart/form-data` - encoded POST or PUT request body. The result is returned as a - :class:`FormsDict`. All keys and values are strings. File uploads - are stored separately in :attr:`files`. """ - forms = FormsDict() - for name, item in self.POST.allitems(): - if not isinstance(item, FileUpload): - forms[name] = item - return forms - - @DictProperty('environ', 'bottle.request.params', read_only=True) - def params(self): - """ A :class:`FormsDict` with the combined values of :attr:`query` and - :attr:`forms`. File uploads are stored in :attr:`files`. """ - params = FormsDict() - for key, value in self.query.allitems(): - params[key] = value - for key, value in self.forms.allitems(): - params[key] = value - return params - - @DictProperty('environ', 'bottle.request.files', read_only=True) - def files(self): - """ File uploads parsed from `multipart/form-data` encoded POST or PUT - request body. The values are instances of :class:`FileUpload`. - - """ - files = FormsDict() - for name, item in self.POST.allitems(): - if isinstance(item, FileUpload): - files[name] = item - return files - - @DictProperty('environ', 'bottle.request.json', read_only=True) - def json(self): - ''' If the ``Content-Type`` header is ``application/json``, this - property holds the parsed content of the request body. Only requests - smaller than :attr:`MEMFILE_MAX` are processed to avoid memory - exhaustion. ''' - ctype = self.environ.get('CONTENT_TYPE', '').lower().split(';')[0] - if ctype == 'application/json': - b = self._get_body_string() - if not b: - return None - return json_loads(b) - return None - - def _iter_body(self, read, bufsize): - maxread = max(0, self.content_length) - while maxread: - part = read(min(maxread, bufsize)) - if not part: break - yield part - maxread -= len(part) - - def _iter_chunked(self, read, bufsize): - err = HTTPError(400, 'Error while parsing chunked transfer body.') - rn, sem, bs = tob('\r\n'), tob(';'), tob('') - while True: - header = read(1) - while header[-2:] != rn: - c = read(1) - header += c - if not c: raise err - if len(header) > bufsize: raise err - size, _, _ = header.partition(sem) - try: - maxread = int(tonat(size.strip()), 16) - except ValueError: - raise err - if maxread == 0: break - buff = bs - while maxread > 0: - if not buff: - buff = read(min(maxread, bufsize)) - part, buff = buff[:maxread], buff[maxread:] - if not part: raise err - yield part - maxread -= len(part) - if read(2) != rn: - raise err - - @DictProperty('environ', 'bottle.request.body', read_only=True) - def _body(self): - body_iter = self._iter_chunked if self.chunked else self._iter_body - read_func = self.environ['wsgi.input'].read - body, body_size, is_temp_file = BytesIO(), 0, False - for part in body_iter(read_func, self.MEMFILE_MAX): - body.write(part) - body_size += len(part) - if not is_temp_file and body_size > self.MEMFILE_MAX: - body, tmp = TemporaryFile(mode='w+b'), body - body.write(tmp.getvalue()) - del tmp - is_temp_file = True - self.environ['wsgi.input'] = body - body.seek(0) - return body - - def _get_body_string(self): - ''' read body until content-length or MEMFILE_MAX into a string. Raise - HTTPError(413) on requests that are to large. ''' - clen = self.content_length - if clen > self.MEMFILE_MAX: - raise HTTPError(413, 'Request to large') - if clen < 0: clen = self.MEMFILE_MAX + 1 - data = self.body.read(clen) - if len(data) > self.MEMFILE_MAX: # Fail fast - raise HTTPError(413, 'Request to large') - return data - - @property - def body(self): - """ The HTTP request body as a seek-able file-like object. Depending on - :attr:`MEMFILE_MAX`, this is either a temporary file or a - :class:`io.BytesIO` instance. Accessing this property for the first - time reads and replaces the ``wsgi.input`` environ variable. - Subsequent accesses just do a `seek(0)` on the file object. """ - self._body.seek(0) - return self._body - - @property - def chunked(self): - ''' True if Chunked transfer encoding was. ''' - return 'chunked' in self.environ.get('HTTP_TRANSFER_ENCODING', '').lower() - - #: An alias for :attr:`query`. - GET = query - - @DictProperty('environ', 'bottle.request.post', read_only=True) - def POST(self): - """ The values of :attr:`forms` and :attr:`files` combined into a single - :class:`FormsDict`. Values are either strings (form values) or - instances of :class:`cgi.FieldStorage` (file uploads). - """ - post = FormsDict() - # We default to application/x-www-form-urlencoded for everything that - # is not multipart and take the fast path (also: 3.1 workaround) - if not self.content_type.startswith('multipart/'): - pairs = _parse_qsl(tonat(self._get_body_string(), 'latin1')) - for key, value in pairs: - post[key] = value - return post - - safe_env = {'QUERY_STRING':''} # Build a safe environment for cgi - for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'): - if key in self.environ: safe_env[key] = self.environ[key] - args = dict(fp=self.body, environ=safe_env, keep_blank_values=True) - if py31: - args['fp'] = NCTextIOWrapper(args['fp'], encoding='utf8', - newline='\n') - elif py3k: - args['encoding'] = 'utf8' - data = cgi.FieldStorage(**args) - self['_cgi.FieldStorage'] = data #http://bugs.python.org/issue18394#msg207958 - data = data.list or [] - for item in data: - if item.filename: - post[item.name] = FileUpload(item.file, item.name, - item.filename, item.headers) - else: - post[item.name] = item.value - return post - - @property - def url(self): - """ The full request URI including hostname and scheme. If your app - lives behind a reverse proxy or load balancer and you get confusing - results, make sure that the ``X-Forwarded-Host`` header is set - correctly. """ - return self.urlparts.geturl() - - @DictProperty('environ', 'bottle.request.urlparts', read_only=True) - def urlparts(self): - ''' The :attr:`url` string as an :class:`urlparse.SplitResult` tuple. - The tuple contains (scheme, host, path, query_string and fragment), - but the fragment is always empty because it is not visible to the - server. ''' - env = self.environ - http = env.get('HTTP_X_FORWARDED_PROTO') or env.get('wsgi.url_scheme', 'http') - host = env.get('HTTP_X_FORWARDED_HOST') or env.get('HTTP_HOST') - if not host: - # HTTP 1.1 requires a Host-header. This is for HTTP/1.0 clients. - host = env.get('SERVER_NAME', '127.0.0.1') - port = env.get('SERVER_PORT') - if port and port != ('80' if http == 'http' else '443'): - host += ':' + port - path = urlquote(self.fullpath) - return UrlSplitResult(http, host, path, env.get('QUERY_STRING'), '') - - @property - def fullpath(self): - """ Request path including :attr:`script_name` (if present). """ - return urljoin(self.script_name, self.path.lstrip('/')) - - @property - def query_string(self): - """ The raw :attr:`query` part of the URL (everything in between ``?`` - and ``#``) as a string. """ - return self.environ.get('QUERY_STRING', '') - - @property - def script_name(self): - ''' The initial portion of the URL's `path` that was removed by a higher - level (server or routing middleware) before the application was - called. This script path is returned with leading and tailing - slashes. ''' - script_name = self.environ.get('SCRIPT_NAME', '').strip('/') - return '/' + script_name + '/' if script_name else '/' - - def path_shift(self, shift=1): - ''' Shift path segments from :attr:`path` to :attr:`script_name` and - vice versa. - - :param shift: The number of path segments to shift. May be negative - to change the shift direction. (default: 1) - ''' - script = self.environ.get('SCRIPT_NAME','/') - self['SCRIPT_NAME'], self['PATH_INFO'] = path_shift(script, self.path, shift) - - @property - def content_length(self): - ''' The request body length as an integer. The client is responsible to - set this header. Otherwise, the real length of the body is unknown - and -1 is returned. In this case, :attr:`body` will be empty. ''' - return int(self.environ.get('CONTENT_LENGTH') or -1) - - @property - def content_type(self): - ''' The Content-Type header as a lowercase-string (default: empty). ''' - return self.environ.get('CONTENT_TYPE', '').lower() - - @property - def is_xhr(self): - ''' True if the request was triggered by a XMLHttpRequest. This only - works with JavaScript libraries that support the `X-Requested-With` - header (most of the popular libraries do). ''' - requested_with = self.environ.get('HTTP_X_REQUESTED_WITH','') - return requested_with.lower() == 'xmlhttprequest' - - @property - def is_ajax(self): - ''' Alias for :attr:`is_xhr`. "Ajax" is not the right term. ''' - return self.is_xhr - - @property - def auth(self): - """ HTTP authentication data as a (user, password) tuple. This - implementation currently supports basic (not digest) authentication - only. If the authentication happened at a higher level (e.g. in the - front web-server or a middleware), the password field is None, but - the user field is looked up from the ``REMOTE_USER`` environ - variable. On any errors, None is returned. """ - basic = parse_auth(self.environ.get('HTTP_AUTHORIZATION','')) - if basic: return basic - ruser = self.environ.get('REMOTE_USER') - if ruser: return (ruser, None) - return None - - @property - def remote_route(self): - """ A list of all IPs that were involved in this request, starting with - the client IP and followed by zero or more proxies. This does only - work if all proxies support the ```X-Forwarded-For`` header. Note - that this information can be forged by malicious clients. """ - proxy = self.environ.get('HTTP_X_FORWARDED_FOR') - if proxy: return [ip.strip() for ip in proxy.split(',')] - remote = self.environ.get('REMOTE_ADDR') - return [remote] if remote else [] - - @property - def remote_addr(self): - """ The client IP as a string. Note that this information can be forged - by malicious clients. """ - route = self.remote_route - return route[0] if route else None - - def copy(self): - """ Return a new :class:`Request` with a shallow :attr:`environ` copy. """ - return Request(self.environ.copy()) - - def get(self, value, default=None): return self.environ.get(value, default) - def __getitem__(self, key): return self.environ[key] - def __delitem__(self, key): self[key] = ""; del(self.environ[key]) - def __iter__(self): return iter(self.environ) - def __len__(self): return len(self.environ) - def keys(self): return self.environ.keys() - def __setitem__(self, key, value): - """ Change an environ value and clear all caches that depend on it. """ - - if self.environ.get('bottle.request.readonly'): - raise KeyError('The environ dictionary is read-only.') - - self.environ[key] = value - todelete = () - - if key == 'wsgi.input': - todelete = ('body', 'forms', 'files', 'params', 'post', 'json') - elif key == 'QUERY_STRING': - todelete = ('query', 'params') - elif key.startswith('HTTP_'): - todelete = ('headers', 'cookies') - - for key in todelete: - self.environ.pop('bottle.request.'+key, None) - - def __repr__(self): - return '<%s: %s %s>' % (self.__class__.__name__, self.method, self.url) - - def __getattr__(self, name): - ''' Search in self.environ for additional user defined attributes. ''' - try: - var = self.environ['bottle.request.ext.%s'%name] - return var.__get__(self) if hasattr(var, '__get__') else var - except KeyError: - raise AttributeError('Attribute %r not defined.' % name) - - def __setattr__(self, name, value): - if name == 'environ': return object.__setattr__(self, name, value) - self.environ['bottle.request.ext.%s'%name] = value - - -def _hkey(key): - if '\n' in key or '\r' in key or '\0' in key: - raise ValueError("Header names must not contain control characters: %r" % key) - return key.title().replace('_', '-') - - -def _hval(value): - value = tonat(value) - if '\n' in value or '\r' in value or '\0' in value: - raise ValueError("Header value must not contain control characters: %r" % value) - return value - - - -class HeaderProperty(object): - def __init__(self, name, reader=None, writer=None, default=''): - self.name, self.default = name, default - self.reader, self.writer = reader, writer - self.__doc__ = 'Current value of the %r header.' % name.title() - - def __get__(self, obj, cls): - if obj is None: return self - value = obj.get_header(self.name, self.default) - return self.reader(value) if self.reader else value - - def __set__(self, obj, value): - obj[self.name] = self.writer(value) if self.writer else value - - def __delete__(self, obj): - del obj[self.name] - - -class BaseResponse(object): - """ Storage class for a response body as well as headers and cookies. - - This class does support dict-like case-insensitive item-access to - headers, but is NOT a dict. Most notably, iterating over a response - yields parts of the body and not the headers. - - :param body: The response body as one of the supported types. - :param status: Either an HTTP status code (e.g. 200) or a status line - including the reason phrase (e.g. '200 OK'). - :param headers: A dictionary or a list of name-value pairs. - - Additional keyword arguments are added to the list of headers. - Underscores in the header name are replaced with dashes. - """ - - default_status = 200 - default_content_type = 'text/html; charset=UTF-8' - - # Header blacklist for specific response codes - # (rfc2616 section 10.2.3 and 10.3.5) - bad_headers = { - 204: set(('Content-Type',)), - 304: set(('Allow', 'Content-Encoding', 'Content-Language', - 'Content-Length', 'Content-Range', 'Content-Type', - 'Content-Md5', 'Last-Modified'))} - - def __init__(self, body='', status=None, headers=None, **more_headers): - self._cookies = None - self._headers = {} - self.body = body - self.status = status or self.default_status - if headers: - if isinstance(headers, dict): - headers = headers.items() - for name, value in headers: - self.add_header(name, value) - if more_headers: - for name, value in more_headers.items(): - self.add_header(name, value) - - def copy(self, cls=None): - ''' Returns a copy of self. ''' - cls = cls or BaseResponse - assert issubclass(cls, BaseResponse) - copy = cls() - copy.status = self.status - copy._headers = dict((k, v[:]) for (k, v) in self._headers.items()) - if self._cookies: - copy._cookies = SimpleCookie() - copy._cookies.load(self._cookies.output(header='')) - return copy - - def __iter__(self): - return iter(self.body) - - def close(self): - if hasattr(self.body, 'close'): - self.body.close() - - @property - def status_line(self): - ''' The HTTP status line as a string (e.g. ``404 Not Found``).''' - return self._status_line - - @property - def status_code(self): - ''' The HTTP status code as an integer (e.g. 404).''' - return self._status_code - - def _set_status(self, status): - if isinstance(status, int): - code, status = status, _HTTP_STATUS_LINES.get(status) - elif ' ' in status: - status = status.strip() - code = int(status.split()[0]) - else: - raise ValueError('String status line without a reason phrase.') - if not 100 <= code <= 999: raise ValueError('Status code out of range.') - self._status_code = code - self._status_line = str(status or ('%d Unknown' % code)) - - def _get_status(self): - return self._status_line - - status = property(_get_status, _set_status, None, - ''' A writeable property to change the HTTP response status. It accepts - either a numeric code (100-999) or a string with a custom reason - phrase (e.g. "404 Brain not found"). Both :data:`status_line` and - :data:`status_code` are updated accordingly. The return value is - always a status string. ''') - del _get_status, _set_status - - @property - def headers(self): - ''' An instance of :class:`HeaderDict`, a case-insensitive dict-like - view on the response headers. ''' - hdict = HeaderDict() - hdict.dict = self._headers - return hdict - - def __contains__(self, name): return _hkey(name) in self._headers - def __delitem__(self, name): del self._headers[_hkey(name)] - def __getitem__(self, name): return self._headers[_hkey(name)][-1] - def __setitem__(self, name, value): self._headers[_hkey(name)] = [_hval(value)] - - def get_header(self, name, default=None): - ''' Return the value of a previously defined header. If there is no - header with that name, return a default value. ''' - return self._headers.get(_hkey(name), [default])[-1] - - def set_header(self, name, value): - ''' Create a new response header, replacing any previously defined - headers with the same name. ''' - self._headers[_hkey(name)] = [_hval(value)] - - def add_header(self, name, value): - ''' Add an additional response header, not removing duplicates. ''' - self._headers.setdefault(_hkey(name), []).append(_hval(value)) - - def iter_headers(self): - ''' Yield (header, value) tuples, skipping headers that are not - allowed with the current response status code. ''' - return self.headerlist - - @property - def headerlist(self): - """ WSGI conform list of (header, value) tuples. """ - out = [] - headers = list(self._headers.items()) - if 'Content-Type' not in self._headers: - headers.append(('Content-Type', [self.default_content_type])) - if self._status_code in self.bad_headers: - bad_headers = self.bad_headers[self._status_code] - headers = [h for h in headers if h[0] not in bad_headers] - out += [(name, val) for (name, vals) in headers for val in vals] - if self._cookies: - for c in self._cookies.values(): - out.append(('Set-Cookie', _hval(c.OutputString()))) - if py3k: - out = [(k, v.encode('utf8').decode('latin1')) for (k, v) in out] - return out - - content_type = HeaderProperty('Content-Type') - content_length = HeaderProperty('Content-Length', reader=int) - expires = HeaderProperty('Expires', - reader=lambda x: datetime.utcfromtimestamp(parse_date(x)), - writer=lambda x: http_date(x)) - - @property - def charset(self, default='UTF-8'): - """ Return the charset specified in the content-type header (default: utf8). """ - if 'charset=' in self.content_type: - return self.content_type.split('charset=')[-1].split(';')[0].strip() - return default - - def set_cookie(self, name, value, secret=None, **options): - ''' Create a new cookie or replace an old one. If the `secret` parameter is - set, create a `Signed Cookie` (described below). - - :param name: the name of the cookie. - :param value: the value of the cookie. - :param secret: a signature key required for signed cookies. - - Additionally, this method accepts all RFC 2109 attributes that are - supported by :class:`cookie.Morsel`, including: - - :param max_age: maximum age in seconds. (default: None) - :param expires: a datetime object or UNIX timestamp. (default: None) - :param domain: the domain that is allowed to read the cookie. - (default: current domain) - :param path: limits the cookie to a given path (default: current path) - :param secure: limit the cookie to HTTPS connections (default: off). - :param httponly: prevents client-side javascript to read this cookie - (default: off, requires Python 2.6 or newer). - - If neither `expires` nor `max_age` is set (default), the cookie will - expire at the end of the browser session (as soon as the browser - window is closed). - - Signed cookies may store any pickle-able object and are - cryptographically signed to prevent manipulation. Keep in mind that - cookies are limited to 4kb in most browsers. - - Warning: Signed cookies are not encrypted (the client can still see - the content) and not copy-protected (the client can restore an old - cookie). The main intention is to make pickling and unpickling - save, not to store secret information at client side. - ''' - if not self._cookies: - self._cookies = SimpleCookie() - - if secret: - value = touni(cookie_encode((name, value), secret)) - elif not isinstance(value, basestring): - raise TypeError('Secret key missing for non-string Cookie.') - - if len(value) > 4096: raise ValueError('Cookie value to long.') - self._cookies[name] = value - - for key, value in options.items(): - if key == 'max_age': - if isinstance(value, timedelta): - value = value.seconds + value.days * 24 * 3600 - if key == 'expires': - if isinstance(value, (datedate, datetime)): - value = value.timetuple() - elif isinstance(value, (int, float)): - value = time.gmtime(value) - value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value) - self._cookies[name][key.replace('_', '-')] = value - - def delete_cookie(self, key, **kwargs): - ''' Delete a cookie. Be sure to use the same `domain` and `path` - settings as used to create the cookie. ''' - kwargs['max_age'] = -1 - kwargs['expires'] = 0 - self.set_cookie(key, '', **kwargs) - - def __repr__(self): - out = '' - for name, value in self.headerlist: - out += '%s: %s\n' % (name.title(), value.strip()) - return out - - -def local_property(name=None): - if name: depr('local_property() is deprecated and will be removed.') #0.12 - ls = threading.local() - def fget(self): - try: return ls.var - except AttributeError: - raise RuntimeError("Request context not initialized.") - def fset(self, value): ls.var = value - def fdel(self): del ls.var - return property(fget, fset, fdel, 'Thread-local property') - - -class LocalRequest(BaseRequest): - ''' A thread-local subclass of :class:`BaseRequest` with a different - set of attributes for each thread. There is usually only one global - instance of this class (:data:`request`). If accessed during a - request/response cycle, this instance always refers to the *current* - request (even on a multithreaded server). ''' - bind = BaseRequest.__init__ - environ = local_property() - - -class LocalResponse(BaseResponse): - ''' A thread-local subclass of :class:`BaseResponse` with a different - set of attributes for each thread. There is usually only one global - instance of this class (:data:`response`). Its attributes are used - to build the HTTP response at the end of the request/response cycle. - ''' - bind = BaseResponse.__init__ - _status_line = local_property() - _status_code = local_property() - _cookies = local_property() - _headers = local_property() - body = local_property() - - -Request = BaseRequest -Response = BaseResponse - - -class HTTPResponse(Response, BottleException): - def __init__(self, body='', status=None, headers=None, **more_headers): - super(HTTPResponse, self).__init__(body, status, headers, **more_headers) - - def apply(self, response): - response._status_code = self._status_code - response._status_line = self._status_line - response._headers = self._headers - response._cookies = self._cookies - response.body = self.body - - -class HTTPError(HTTPResponse): - default_status = 500 - def __init__(self, status=None, body=None, exception=None, traceback=None, - **options): - self.exception = exception - self.traceback = traceback - super(HTTPError, self).__init__(body, status, **options) - - - - - -############################################################################### -# Plugins ###################################################################### -############################################################################### - -class PluginError(BottleException): pass - - -class JSONPlugin(object): - name = 'json' - api = 2 - - def __init__(self, json_dumps=json_dumps): - self.json_dumps = json_dumps - - def apply(self, callback, route): - dumps = self.json_dumps - if not dumps: return callback - def wrapper(*a, **ka): - try: - rv = callback(*a, **ka) - except HTTPError: - rv = _e() - - if isinstance(rv, dict): - #Attempt to serialize, raises exception on failure - json_response = dumps(rv) - #Set content type only if serialization succesful - response.content_type = 'application/json' - return json_response - elif isinstance(rv, HTTPResponse) and isinstance(rv.body, dict): - rv.body = dumps(rv.body) - rv.content_type = 'application/json' - return rv - - return wrapper - - -class TemplatePlugin(object): - ''' This plugin applies the :func:`view` decorator to all routes with a - `template` config parameter. If the parameter is a tuple, the second - element must be a dict with additional options (e.g. `template_engine`) - or default variables for the template. ''' - name = 'template' - api = 2 - - def apply(self, callback, route): - conf = route.config.get('template') - if isinstance(conf, (tuple, list)) and len(conf) == 2: - return view(conf[0], **conf[1])(callback) - elif isinstance(conf, str): - return view(conf)(callback) - else: - return callback - - -#: Not a plugin, but part of the plugin API. TODO: Find a better place. -class _ImportRedirect(object): - def __init__(self, name, impmask): - ''' Create a virtual package that redirects imports (see PEP 302). ''' - self.name = name - self.impmask = impmask - self.module = sys.modules.setdefault(name, new_module(name)) - self.module.__dict__.update({'__file__': __file__, '__path__': [], - '__all__': [], '__loader__': self}) - sys.meta_path.append(self) - - def find_module(self, fullname, path=None): - if '.' not in fullname: return - packname = fullname.rsplit('.', 1)[0] - if packname != self.name: return - return self - - def load_module(self, fullname): - if fullname in sys.modules: return sys.modules[fullname] - modname = fullname.rsplit('.', 1)[1] - realname = self.impmask % modname - __import__(realname) - module = sys.modules[fullname] = sys.modules[realname] - setattr(self.module, modname, module) - module.__loader__ = self - return module - - - - - - -############################################################################### -# Common Utilities ############################################################# -############################################################################### - - -class MultiDict(DictMixin): - """ This dict stores multiple values per key, but behaves exactly like a - normal dict in that it returns only the newest value for any given key. - There are special methods available to access the full list of values. - """ - - def __init__(self, *a, **k): - self.dict = dict((k, [v]) for (k, v) in dict(*a, **k).items()) - - def __len__(self): return len(self.dict) - def __iter__(self): return iter(self.dict) - def __contains__(self, key): return key in self.dict - def __delitem__(self, key): del self.dict[key] - def __getitem__(self, key): return self.dict[key][-1] - def __setitem__(self, key, value): self.append(key, value) - def keys(self): return self.dict.keys() - - if py3k: - def values(self): return (v[-1] for v in self.dict.values()) - def items(self): return ((k, v[-1]) for k, v in self.dict.items()) - def allitems(self): - return ((k, v) for k, vl in self.dict.items() for v in vl) - iterkeys = keys - itervalues = values - iteritems = items - iterallitems = allitems - - else: - def values(self): return [v[-1] for v in self.dict.values()] - def items(self): return [(k, v[-1]) for k, v in self.dict.items()] - def iterkeys(self): return self.dict.iterkeys() - def itervalues(self): return (v[-1] for v in self.dict.itervalues()) - def iteritems(self): - return ((k, v[-1]) for k, v in self.dict.iteritems()) - def iterallitems(self): - return ((k, v) for k, vl in self.dict.iteritems() for v in vl) - def allitems(self): - return [(k, v) for k, vl in self.dict.iteritems() for v in vl] - - def get(self, key, default=None, index=-1, type=None): - ''' Return the most recent value for a key. - - :param default: The default value to be returned if the key is not - present or the type conversion fails. - :param index: An index for the list of available values. - :param type: If defined, this callable is used to cast the value - into a specific type. Exception are suppressed and result in - the default value to be returned. - ''' - try: - val = self.dict[key][index] - return type(val) if type else val - except Exception: - pass - return default - - def append(self, key, value): - ''' Add a new value to the list of values for this key. ''' - self.dict.setdefault(key, []).append(value) - - def replace(self, key, value): - ''' Replace the list of values with a single value. ''' - self.dict[key] = [value] - - def getall(self, key): - ''' Return a (possibly empty) list of values for a key. ''' - return self.dict.get(key) or [] - - #: Aliases for WTForms to mimic other multi-dict APIs (Django) - getone = get - getlist = getall - - -class FormsDict(MultiDict): - ''' This :class:`MultiDict` subclass is used to store request form data. - Additionally to the normal dict-like item access methods (which return - unmodified data as native strings), this container also supports - attribute-like access to its values. Attributes are automatically de- - or recoded to match :attr:`input_encoding` (default: 'utf8'). Missing - attributes default to an empty string. ''' - - #: Encoding used for attribute values. - input_encoding = 'utf8' - #: If true (default), unicode strings are first encoded with `latin1` - #: and then decoded to match :attr:`input_encoding`. - recode_unicode = True - - def _fix(self, s, encoding=None): - if isinstance(s, unicode) and self.recode_unicode: # Python 3 WSGI - return s.encode('latin1').decode(encoding or self.input_encoding) - elif isinstance(s, bytes): # Python 2 WSGI - return s.decode(encoding or self.input_encoding) - else: - return s - - def decode(self, encoding=None): - ''' Returns a copy with all keys and values de- or recoded to match - :attr:`input_encoding`. Some libraries (e.g. WTForms) want a - unicode dictionary. ''' - copy = FormsDict() - enc = copy.input_encoding = encoding or self.input_encoding - copy.recode_unicode = False - for key, value in self.allitems(): - copy.append(self._fix(key, enc), self._fix(value, enc)) - return copy - - def getunicode(self, name, default=None, encoding=None): - ''' Return the value as a unicode string, or the default. ''' - try: - return self._fix(self[name], encoding) - except (UnicodeError, KeyError): - return default - - def __getattr__(self, name, default=unicode()): - # Without this guard, pickle generates a cryptic TypeError: - if name.startswith('__') and name.endswith('__'): - return super(FormsDict, self).__getattr__(name) - return self.getunicode(name, default=default) - -class HeaderDict(MultiDict): - """ A case-insensitive version of :class:`MultiDict` that defaults to - replace the old value instead of appending it. """ - - def __init__(self, *a, **ka): - self.dict = {} - if a or ka: self.update(*a, **ka) - - def __contains__(self, key): return _hkey(key) in self.dict - def __delitem__(self, key): del self.dict[_hkey(key)] - def __getitem__(self, key): return self.dict[_hkey(key)][-1] - def __setitem__(self, key, value): self.dict[_hkey(key)] = [_hval(value)] - def append(self, key, value): self.dict.setdefault(_hkey(key), []).append(_hval(value)) - def replace(self, key, value): self.dict[_hkey(key)] = [_hval(value)] - def getall(self, key): return self.dict.get(_hkey(key)) or [] - def get(self, key, default=None, index=-1): - return MultiDict.get(self, _hkey(key), default, index) - def filter(self, names): - for name in (_hkey(n) for n in names): - if name in self.dict: - del self.dict[name] - - -class WSGIHeaderDict(DictMixin): - ''' This dict-like class wraps a WSGI environ dict and provides convenient - access to HTTP_* fields. Keys and values are native strings - (2.x bytes or 3.x unicode) and keys are case-insensitive. If the WSGI - environment contains non-native string values, these are de- or encoded - using a lossless 'latin1' character set. - - The API will remain stable even on changes to the relevant PEPs. - Currently PEP 333, 444 and 3333 are supported. (PEP 444 is the only one - that uses non-native strings.) - ''' - #: List of keys that do not have a ``HTTP_`` prefix. - cgikeys = ('CONTENT_TYPE', 'CONTENT_LENGTH') - - def __init__(self, environ): - self.environ = environ - - def _ekey(self, key): - ''' Translate header field name to CGI/WSGI environ key. ''' - key = key.replace('-','_').upper() - if key in self.cgikeys: - return key - return 'HTTP_' + key - - def raw(self, key, default=None): - ''' Return the header value as is (may be bytes or unicode). ''' - return self.environ.get(self._ekey(key), default) - - def __getitem__(self, key): - return tonat(self.environ[self._ekey(key)], 'latin1') - - def __setitem__(self, key, value): - raise TypeError("%s is read-only." % self.__class__) - - def __delitem__(self, key): - raise TypeError("%s is read-only." % self.__class__) - - def __iter__(self): - for key in self.environ: - if key[:5] == 'HTTP_': - yield key[5:].replace('_', '-').title() - elif key in self.cgikeys: - yield key.replace('_', '-').title() - - def keys(self): return [x for x in self] - def __len__(self): return len(self.keys()) - def __contains__(self, key): return self._ekey(key) in self.environ - - - -class ConfigDict(dict): - ''' A dict-like configuration storage with additional support for - namespaces, validators, meta-data, on_change listeners and more. - - This storage is optimized for fast read access. Retrieving a key - or using non-altering dict methods (e.g. `dict.get()`) has no overhead - compared to a native dict. - ''' - __slots__ = ('_meta', '_on_change') - - class Namespace(DictMixin): - - def __init__(self, config, namespace): - self._config = config - self._prefix = namespace - - def __getitem__(self, key): - depr('Accessing namespaces as dicts is discouraged. ' - 'Only use flat item access: ' - 'cfg["names"]["pace"]["key"] -> cfg["name.space.key"]') #0.12 - return self._config[self._prefix + '.' + key] - - def __setitem__(self, key, value): - self._config[self._prefix + '.' + key] = value - - def __delitem__(self, key): - del self._config[self._prefix + '.' + key] - - def __iter__(self): - ns_prefix = self._prefix + '.' - for key in self._config: - ns, dot, name = key.rpartition('.') - if ns == self._prefix and name: - yield name - - def keys(self): return [x for x in self] - def __len__(self): return len(self.keys()) - def __contains__(self, key): return self._prefix + '.' + key in self._config - def __repr__(self): return '' % self._prefix - def __str__(self): return '' % self._prefix - - # Deprecated ConfigDict features - def __getattr__(self, key): - depr('Attribute access is deprecated.') #0.12 - if key not in self and key[0].isupper(): - self[key] = ConfigDict.Namespace(self._config, self._prefix + '.' + key) - if key not in self and key.startswith('__'): - raise AttributeError(key) - return self.get(key) - - def __setattr__(self, key, value): - if key in ('_config', '_prefix'): - self.__dict__[key] = value - return - depr('Attribute assignment is deprecated.') #0.12 - if hasattr(DictMixin, key): - raise AttributeError('Read-only attribute.') - if key in self and self[key] and isinstance(self[key], self.__class__): - raise AttributeError('Non-empty namespace attribute.') - self[key] = value - - def __delattr__(self, key): - if key in self: - val = self.pop(key) - if isinstance(val, self.__class__): - prefix = key + '.' - for key in self: - if key.startswith(prefix): - del self[prefix+key] - - def __call__(self, *a, **ka): - depr('Calling ConfDict is deprecated. Use the update() method.') #0.12 - self.update(*a, **ka) - return self - - def __init__(self, *a, **ka): - self._meta = {} - self._on_change = lambda name, value: None - if a or ka: - depr('Constructor does no longer accept parameters.') #0.12 - self.update(*a, **ka) - - def load_config(self, filename): - ''' Load values from an *.ini style config file. - - If the config file contains sections, their names are used as - namespaces for the values within. The two special sections - ``DEFAULT`` and ``bottle`` refer to the root namespace (no prefix). - ''' - conf = ConfigParser() - conf.read(filename) - for section in conf.sections(): - for key, value in conf.items(section): - if section not in ('DEFAULT', 'bottle'): - key = section + '.' + key - self[key] = value - return self - - def load_dict(self, source, namespace='', make_namespaces=False): - ''' Import values from a dictionary structure. Nesting can be used to - represent namespaces. - - >>> ConfigDict().load_dict({'name': {'space': {'key': 'value'}}}) - {'name.space.key': 'value'} - ''' - stack = [(namespace, source)] - while stack: - prefix, source = stack.pop() - if not isinstance(source, dict): - raise TypeError('Source is not a dict (r)' % type(key)) - for key, value in source.items(): - if not isinstance(key, basestring): - raise TypeError('Key is not a string (%r)' % type(key)) - full_key = prefix + '.' + key if prefix else key - if isinstance(value, dict): - stack.append((full_key, value)) - if make_namespaces: - self[full_key] = self.Namespace(self, full_key) - else: - self[full_key] = value - return self - - def update(self, *a, **ka): - ''' If the first parameter is a string, all keys are prefixed with this - namespace. Apart from that it works just as the usual dict.update(). - Example: ``update('some.namespace', key='value')`` ''' - prefix = '' - if a and isinstance(a[0], basestring): - prefix = a[0].strip('.') + '.' - a = a[1:] - for key, value in dict(*a, **ka).items(): - self[prefix+key] = value - - def setdefault(self, key, value): - if key not in self: - self[key] = value - return self[key] - - def __setitem__(self, key, value): - if not isinstance(key, basestring): - raise TypeError('Key has type %r (not a string)' % type(key)) - - value = self.meta_get(key, 'filter', lambda x: x)(value) - if key in self and self[key] is value: - return - self._on_change(key, value) - dict.__setitem__(self, key, value) - - def __delitem__(self, key): - dict.__delitem__(self, key) - - def clear(self): - for key in self: - del self[key] - - def meta_get(self, key, metafield, default=None): - ''' Return the value of a meta field for a key. ''' - return self._meta.get(key, {}).get(metafield, default) - - def meta_set(self, key, metafield, value): - ''' Set the meta field for a key to a new value. This triggers the - on-change handler for existing keys. ''' - self._meta.setdefault(key, {})[metafield] = value - if key in self: - self[key] = self[key] - - def meta_list(self, key): - ''' Return an iterable of meta field names defined for a key. ''' - return self._meta.get(key, {}).keys() - - # Deprecated ConfigDict features - def __getattr__(self, key): - depr('Attribute access is deprecated.') #0.12 - if key not in self and key[0].isupper(): - self[key] = self.Namespace(self, key) - if key not in self and key.startswith('__'): - raise AttributeError(key) - return self.get(key) - - def __setattr__(self, key, value): - if key in self.__slots__: - return dict.__setattr__(self, key, value) - depr('Attribute assignment is deprecated.') #0.12 - if hasattr(dict, key): - raise AttributeError('Read-only attribute.') - if key in self and self[key] and isinstance(self[key], self.Namespace): - raise AttributeError('Non-empty namespace attribute.') - self[key] = value - - def __delattr__(self, key): - if key in self: - val = self.pop(key) - if isinstance(val, self.Namespace): - prefix = key + '.' - for key in self: - if key.startswith(prefix): - del self[prefix+key] - - def __call__(self, *a, **ka): - depr('Calling ConfDict is deprecated. Use the update() method.') #0.12 - self.update(*a, **ka) - return self - - - -class AppStack(list): - """ A stack-like list. Calling it returns the head of the stack. """ - - def __call__(self): - """ Return the current default application. """ - return self[-1] - - def push(self, value=None): - """ Add a new :class:`Bottle` instance to the stack """ - if not isinstance(value, Bottle): - value = Bottle() - self.append(value) - return value - - -class WSGIFileWrapper(object): - - def __init__(self, fp, buffer_size=1024*64): - self.fp, self.buffer_size = fp, buffer_size - for attr in ('fileno', 'close', 'read', 'readlines', 'tell', 'seek'): - if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr)) - - def __iter__(self): - buff, read = self.buffer_size, self.read - while True: - part = read(buff) - if not part: return - yield part - - -class _closeiter(object): - ''' This only exists to be able to attach a .close method to iterators that - do not support attribute assignment (most of itertools). ''' - - def __init__(self, iterator, close=None): - self.iterator = iterator - self.close_callbacks = makelist(close) - - def __iter__(self): - return iter(self.iterator) - - def close(self): - for func in self.close_callbacks: - func() - - -class ResourceManager(object): - ''' This class manages a list of search paths and helps to find and open - application-bound resources (files). - - :param base: default value for :meth:`add_path` calls. - :param opener: callable used to open resources. - :param cachemode: controls which lookups are cached. One of 'all', - 'found' or 'none'. - ''' - - def __init__(self, base='./', opener=open, cachemode='all'): - self.opener = open - self.base = base - self.cachemode = cachemode - - #: A list of search paths. See :meth:`add_path` for details. - self.path = [] - #: A cache for resolved paths. ``res.cache.clear()`` clears the cache. - self.cache = {} - - def add_path(self, path, base=None, index=None, create=False): - ''' Add a new path to the list of search paths. Return False if the - path does not exist. - - :param path: The new search path. Relative paths are turned into - an absolute and normalized form. If the path looks like a file - (not ending in `/`), the filename is stripped off. - :param base: Path used to absolutize relative search paths. - Defaults to :attr:`base` which defaults to ``os.getcwd()``. - :param index: Position within the list of search paths. Defaults - to last index (appends to the list). - - The `base` parameter makes it easy to reference files installed - along with a python module or package:: - - res.add_path('./resources/', __file__) - ''' - base = os.path.abspath(os.path.dirname(base or self.base)) - path = os.path.abspath(os.path.join(base, os.path.dirname(path))) - path += os.sep - if path in self.path: - self.path.remove(path) - if create and not os.path.isdir(path): - os.makedirs(path) - if index is None: - self.path.append(path) - else: - self.path.insert(index, path) - self.cache.clear() - return os.path.exists(path) - - def __iter__(self): - ''' Iterate over all existing files in all registered paths. ''' - search = self.path[:] - while search: - path = search.pop() - if not os.path.isdir(path): continue - for name in os.listdir(path): - full = os.path.join(path, name) - if os.path.isdir(full): search.append(full) - else: yield full - - def lookup(self, name): - ''' Search for a resource and return an absolute file path, or `None`. - - The :attr:`path` list is searched in order. The first match is - returend. Symlinks are followed. The result is cached to speed up - future lookups. ''' - if name not in self.cache or DEBUG: - for path in self.path: - fpath = os.path.join(path, name) - if os.path.isfile(fpath): - if self.cachemode in ('all', 'found'): - self.cache[name] = fpath - return fpath - if self.cachemode == 'all': - self.cache[name] = None - return self.cache[name] - - def open(self, name, mode='r', *args, **kwargs): - ''' Find a resource and return a file object, or raise IOError. ''' - fname = self.lookup(name) - if not fname: raise IOError("Resource %r not found." % name) - return self.opener(fname, mode=mode, *args, **kwargs) - - -class FileUpload(object): - - def __init__(self, fileobj, name, filename, headers=None): - ''' Wrapper for file uploads. ''' - #: Open file(-like) object (BytesIO buffer or temporary file) - self.file = fileobj - #: Name of the upload form field - self.name = name - #: Raw filename as sent by the client (may contain unsafe characters) - self.raw_filename = filename - #: A :class:`HeaderDict` with additional headers (e.g. content-type) - self.headers = HeaderDict(headers) if headers else HeaderDict() - - content_type = HeaderProperty('Content-Type') - content_length = HeaderProperty('Content-Length', reader=int, default=-1) - - def get_header(self, name, default=None): - """ Return the value of a header within the mulripart part. """ - return self.headers.get(name, default) - - @cached_property - def filename(self): - ''' Name of the file on the client file system, but normalized to ensure - file system compatibility. An empty filename is returned as 'empty'. - - Only ASCII letters, digits, dashes, underscores and dots are - allowed in the final filename. Accents are removed, if possible. - Whitespace is replaced by a single dash. Leading or tailing dots - or dashes are removed. The filename is limited to 255 characters. - ''' - fname = self.raw_filename - if not isinstance(fname, unicode): - fname = fname.decode('utf8', 'ignore') - fname = normalize('NFKD', fname).encode('ASCII', 'ignore').decode('ASCII') - fname = os.path.basename(fname.replace('\\', os.path.sep)) - fname = re.sub(r'[^a-zA-Z0-9-_.\s]', '', fname).strip() - fname = re.sub(r'[-\s]+', '-', fname).strip('.-') - return fname[:255] or 'empty' - - def _copy_file(self, fp, chunk_size=2**16): - read, write, offset = self.file.read, fp.write, self.file.tell() - while 1: - buf = read(chunk_size) - if not buf: break - write(buf) - self.file.seek(offset) - - def save(self, destination, overwrite=False, chunk_size=2**16): - ''' Save file to disk or copy its content to an open file(-like) object. - If *destination* is a directory, :attr:`filename` is added to the - path. Existing files are not overwritten by default (IOError). - - :param destination: File path, directory or file(-like) object. - :param overwrite: If True, replace existing files. (default: False) - :param chunk_size: Bytes to read at a time. (default: 64kb) - ''' - if isinstance(destination, basestring): # Except file-likes here - if os.path.isdir(destination): - destination = os.path.join(destination, self.filename) - if not overwrite and os.path.exists(destination): - raise IOError('File exists.') - with open(destination, 'wb') as fp: - self._copy_file(fp, chunk_size) - else: - self._copy_file(destination, chunk_size) - - - - - - -############################################################################### -# Application Helper ########################################################### -############################################################################### - - -def abort(code=500, text='Unknown Error.'): - """ Aborts execution and causes a HTTP error. """ - raise HTTPError(code, text) - - -def redirect(url, code=None): - """ Aborts execution and causes a 303 or 302 redirect, depending on - the HTTP protocol version. """ - if not code: - code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302 - res = response.copy(cls=HTTPResponse) - res.status = code - res.body = "" - res.set_header('Location', urljoin(request.url, url)) - raise res - - -def _file_iter_range(fp, offset, bytes, maxread=1024*1024): - ''' Yield chunks from a range in a file. No chunk is bigger than maxread.''' - fp.seek(offset) - while bytes > 0: - part = fp.read(min(bytes, maxread)) - if not part: break - bytes -= len(part) - yield part - - -def static_file(filename, root, mimetype='auto', download=False, charset='UTF-8'): - """ Open a file in a safe way and return :exc:`HTTPResponse` with status - code 200, 305, 403 or 404. The ``Content-Type``, ``Content-Encoding``, - ``Content-Length`` and ``Last-Modified`` headers are set if possible. - Special support for ``If-Modified-Since``, ``Range`` and ``HEAD`` - requests. - - :param filename: Name or path of the file to send. - :param root: Root path for file lookups. Should be an absolute directory - path. - :param mimetype: Defines the content-type header (default: guess from - file extension) - :param download: If True, ask the browser to open a `Save as...` dialog - instead of opening the file with the associated program. You can - specify a custom filename as a string. If not specified, the - original filename is used (default: False). - :param charset: The charset to use for files with a ``text/*`` - mime-type. (default: UTF-8) - """ - - root = os.path.abspath(root) + os.sep - filename = os.path.abspath(os.path.join(root, filename.strip('/\\'))) - headers = dict() - - if not filename.startswith(root): - return HTTPError(403, "Access denied.") - if not os.path.exists(filename) or not os.path.isfile(filename): - return HTTPError(404, "File does not exist.") - if not os.access(filename, os.R_OK): - return HTTPError(403, "You do not have permission to access this file.") - - if mimetype == 'auto': - mimetype, encoding = mimetypes.guess_type(filename) - if encoding: headers['Content-Encoding'] = encoding - - if mimetype: - if mimetype[:5] == 'text/' and charset and 'charset' not in mimetype: - mimetype += '; charset=%s' % charset - headers['Content-Type'] = mimetype - - if download: - download = os.path.basename(filename if download == True else download) - headers['Content-Disposition'] = 'attachment; filename="%s"' % download - - stats = os.stat(filename) - headers['Content-Length'] = clen = stats.st_size - lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime)) - headers['Last-Modified'] = lm - - ims = request.environ.get('HTTP_IF_MODIFIED_SINCE') - if ims: - ims = parse_date(ims.split(";")[0].strip()) - if ims is not None and ims >= int(stats.st_mtime): - headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) - return HTTPResponse(status=304, **headers) - - body = '' if request.method == 'HEAD' else open(filename, 'rb') - - headers["Accept-Ranges"] = "bytes" - ranges = request.environ.get('HTTP_RANGE') - if 'HTTP_RANGE' in request.environ: - ranges = list(parse_range_header(request.environ['HTTP_RANGE'], clen)) - if not ranges: - return HTTPError(416, "Requested Range Not Satisfiable") - offset, end = ranges[0] - headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end-1, clen) - headers["Content-Length"] = str(end-offset) - if body: body = _file_iter_range(body, offset, end-offset) - return HTTPResponse(body, status=206, **headers) - return HTTPResponse(body, **headers) - - - - - - -############################################################################### -# HTTP Utilities and MISC (TODO) ############################################### -############################################################################### - - -def debug(mode=True): - """ Change the debug level. - There is only one debug level supported at the moment.""" - global DEBUG - if mode: warnings.simplefilter('default') - DEBUG = bool(mode) - -def http_date(value): - if isinstance(value, (datedate, datetime)): - value = value.utctimetuple() - elif isinstance(value, (int, float)): - value = time.gmtime(value) - if not isinstance(value, basestring): - value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value) - return value - -def parse_date(ims): - """ Parse rfc1123, rfc850 and asctime timestamps and return UTC epoch. """ - try: - ts = email.utils.parsedate_tz(ims) - return time.mktime(ts[:8] + (0,)) - (ts[9] or 0) - time.timezone - except (TypeError, ValueError, IndexError, OverflowError): - return None - -def parse_auth(header): - """ Parse rfc2617 HTTP authentication header string (basic) and return (user,pass) tuple or None""" - try: - method, data = header.split(None, 1) - if method.lower() == 'basic': - user, pwd = touni(base64.b64decode(tob(data))).split(':',1) - return user, pwd - except (KeyError, ValueError): - return None - -def parse_range_header(header, maxlen=0): - ''' Yield (start, end) ranges parsed from a HTTP Range header. Skip - unsatisfiable ranges. The end index is non-inclusive.''' - if not header or header[:6] != 'bytes=': return - ranges = [r.split('-', 1) for r in header[6:].split(',') if '-' in r] - for start, end in ranges: - try: - if not start: # bytes=-100 -> last 100 bytes - start, end = max(0, maxlen-int(end)), maxlen - elif not end: # bytes=100- -> all but the first 99 bytes - start, end = int(start), maxlen - else: # bytes=100-200 -> bytes 100-200 (inclusive) - start, end = int(start), min(int(end)+1, maxlen) - if 0 <= start < end <= maxlen: - yield start, end - except ValueError: - pass - -def _parse_qsl(qs): - r = [] - for pair in qs.split('&'): - if not pair: continue - nv = pair.split('=', 1) - if len(nv) != 2: nv.append('') - key = urlunquote(nv[0].replace('+', ' ')) - value = urlunquote(nv[1].replace('+', ' ')) - r.append((key, value)) - return r - -def _lscmp(a, b): - ''' Compares two strings in a cryptographically safe way: - Runtime is not affected by length of common prefix. ''' - return not sum(0 if x==y else 1 for x, y in zip(a, b)) and len(a) == len(b) - - -def cookie_encode(data, key): - ''' Encode and sign a pickle-able object. Return a (byte) string ''' - msg = base64.b64encode(pickle.dumps(data, -1)) - sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest()) - return tob('!') + sig + tob('?') + msg - - -def cookie_decode(data, key): - ''' Verify and decode an encoded string. Return an object or None.''' - data = tob(data) - if cookie_is_encoded(data): - sig, msg = data.split(tob('?'), 1) - if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest())): - return pickle.loads(base64.b64decode(msg)) - return None - - -def cookie_is_encoded(data): - ''' Return True if the argument looks like a encoded cookie.''' - return bool(data.startswith(tob('!')) and tob('?') in data) - - -def html_escape(string): - ''' Escape HTML special characters ``&<>`` and quotes ``'"``. ''' - return string.replace('&','&').replace('<','<').replace('>','>')\ - .replace('"','"').replace("'",''') - - -def html_quote(string): - ''' Escape and quote a string to be used as an HTTP attribute.''' - return '"%s"' % html_escape(string).replace('\n',' ')\ - .replace('\r',' ').replace('\t',' ') - - -def yieldroutes(func): - """ Return a generator for routes that match the signature (name, args) - of the func parameter. This may yield more than one route if the function - takes optional keyword arguments. The output is best described by example:: - - a() -> '/a' - b(x, y) -> '/b//' - c(x, y=5) -> '/c/' and '/c//' - d(x=5, y=6) -> '/d' and '/d/' and '/d//' - """ - path = '/' + func.__name__.replace('__','/').lstrip('/') - spec = getargspec(func) - argc = len(spec[0]) - len(spec[3] or []) - path += ('/<%s>' * argc) % tuple(spec[0][:argc]) - yield path - for arg in spec[0][argc:]: - path += '/<%s>' % arg - yield path - - -def path_shift(script_name, path_info, shift=1): - ''' Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa. - - :return: The modified paths. - :param script_name: The SCRIPT_NAME path. - :param script_name: The PATH_INFO path. - :param shift: The number of path fragments to shift. May be negative to - change the shift direction. (default: 1) - ''' - if shift == 0: return script_name, path_info - pathlist = path_info.strip('/').split('/') - scriptlist = script_name.strip('/').split('/') - if pathlist and pathlist[0] == '': pathlist = [] - if scriptlist and scriptlist[0] == '': scriptlist = [] - if shift > 0 and shift <= len(pathlist): - moved = pathlist[:shift] - scriptlist = scriptlist + moved - pathlist = pathlist[shift:] - elif shift < 0 and shift >= -len(scriptlist): - moved = scriptlist[shift:] - pathlist = moved + pathlist - scriptlist = scriptlist[:shift] - else: - empty = 'SCRIPT_NAME' if shift < 0 else 'PATH_INFO' - raise AssertionError("Cannot shift. Nothing left from %s" % empty) - new_script_name = '/' + '/'.join(scriptlist) - new_path_info = '/' + '/'.join(pathlist) - if path_info.endswith('/') and pathlist: new_path_info += '/' - return new_script_name, new_path_info - - -def auth_basic(check, realm="private", text="Access denied"): - ''' Callback decorator to require HTTP auth (basic). - TODO: Add route(check_auth=...) parameter. ''' - def decorator(func): - def wrapper(*a, **ka): - user, password = request.auth or (None, None) - if user is None or not check(user, password): - err = HTTPError(401, text) - err.add_header('WWW-Authenticate', 'Basic realm="%s"' % realm) - return err - return func(*a, **ka) - return wrapper - return decorator - - -# Shortcuts for common Bottle methods. -# They all refer to the current default application. - -def make_default_app_wrapper(name): - ''' Return a callable that relays calls to the current default app. ''' - @functools.wraps(getattr(Bottle, name)) - def wrapper(*a, **ka): - return getattr(app(), name)(*a, **ka) - return wrapper - -route = make_default_app_wrapper('route') -get = make_default_app_wrapper('get') -post = make_default_app_wrapper('post') -put = make_default_app_wrapper('put') -delete = make_default_app_wrapper('delete') -error = make_default_app_wrapper('error') -mount = make_default_app_wrapper('mount') -hook = make_default_app_wrapper('hook') -install = make_default_app_wrapper('install') -uninstall = make_default_app_wrapper('uninstall') -url = make_default_app_wrapper('get_url') - - - - - - - -############################################################################### -# Server Adapter ############################################################### -############################################################################### - - -class ServerAdapter(object): - quiet = False - def __init__(self, host='127.0.0.1', port=8080, **options): - self.options = options - self.host = host - self.port = int(port) - - def run(self, handler): # pragma: no cover - pass - - def __repr__(self): - args = ', '.join(['%s=%s'%(k,repr(v)) for k, v in self.options.items()]) - return "%s(%s)" % (self.__class__.__name__, args) - - -class CGIServer(ServerAdapter): - quiet = True - def run(self, handler): # pragma: no cover - from wsgiref.handlers import CGIHandler - def fixed_environ(environ, start_response): - environ.setdefault('PATH_INFO', '') - return handler(environ, start_response) - CGIHandler().run(fixed_environ) - - -class FlupFCGIServer(ServerAdapter): - def run(self, handler): # pragma: no cover - import flup.server.fcgi - self.options.setdefault('bindAddress', (self.host, self.port)) - flup.server.fcgi.WSGIServer(handler, **self.options).run() - - -class WSGIRefServer(ServerAdapter): - def run(self, app): # pragma: no cover - from wsgiref.simple_server import WSGIRequestHandler, WSGIServer - from wsgiref.simple_server import make_server - import socket - - class FixedHandler(WSGIRequestHandler): - def address_string(self): # Prevent reverse DNS lookups please. - return self.client_address[0] - def log_request(*args, **kw): - if not self.quiet: - return WSGIRequestHandler.log_request(*args, **kw) - - handler_cls = self.options.get('handler_class', FixedHandler) - server_cls = self.options.get('server_class', WSGIServer) - - if ':' in self.host: # Fix wsgiref for IPv6 addresses. - if getattr(server_cls, 'address_family') == socket.AF_INET: - class server_cls(server_cls): - address_family = socket.AF_INET6 - - srv = make_server(self.host, self.port, app, server_cls, handler_cls) - srv.serve_forever() - - -class CherryPyServer(ServerAdapter): - def run(self, handler): # pragma: no cover - from cherrypy import wsgiserver - self.options['bind_addr'] = (self.host, self.port) - self.options['wsgi_app'] = handler - - certfile = self.options.get('certfile') - if certfile: - del self.options['certfile'] - keyfile = self.options.get('keyfile') - if keyfile: - del self.options['keyfile'] - - server = wsgiserver.CherryPyWSGIServer(**self.options) - if certfile: - server.ssl_certificate = certfile - if keyfile: - server.ssl_private_key = keyfile - - try: - server.start() - finally: - server.stop() - - -class WaitressServer(ServerAdapter): - def run(self, handler): - from waitress import serve - serve(handler, host=self.host, port=self.port) - - -class PasteServer(ServerAdapter): - def run(self, handler): # pragma: no cover - from paste import httpserver - from paste.translogger import TransLogger - handler = TransLogger(handler, setup_console_handler=(not self.quiet)) - httpserver.serve(handler, host=self.host, port=str(self.port), - **self.options) - - -class MeinheldServer(ServerAdapter): - def run(self, handler): - from meinheld import server - server.listen((self.host, self.port)) - server.run(handler) - - -class FapwsServer(ServerAdapter): - """ Extremely fast webserver using libev. See http://www.fapws.org/ """ - def run(self, handler): # pragma: no cover - import fapws._evwsgi as evwsgi - from fapws import base, config - port = self.port - if float(config.SERVER_IDENT[-2:]) > 0.4: - # fapws3 silently changed its API in 0.5 - port = str(port) - evwsgi.start(self.host, port) - # fapws3 never releases the GIL. Complain upstream. I tried. No luck. - if 'BOTTLE_CHILD' in os.environ and not self.quiet: - _stderr("WARNING: Auto-reloading does not work with Fapws3.\n") - _stderr(" (Fapws3 breaks python thread support)\n") - evwsgi.set_base_module(base) - def app(environ, start_response): - environ['wsgi.multiprocess'] = False - return handler(environ, start_response) - evwsgi.wsgi_cb(('', app)) - evwsgi.run() - - -class TornadoServer(ServerAdapter): - """ The super hyped asynchronous server by facebook. Untested. """ - def run(self, handler): # pragma: no cover - import tornado.wsgi, tornado.httpserver, tornado.ioloop - container = tornado.wsgi.WSGIContainer(handler) - server = tornado.httpserver.HTTPServer(container) - server.listen(port=self.port,address=self.host) - tornado.ioloop.IOLoop.instance().start() - - -class AppEngineServer(ServerAdapter): - """ Adapter for Google App Engine. """ - quiet = True - def run(self, handler): - from google.appengine.ext.webapp import util - # A main() function in the handler script enables 'App Caching'. - # Lets makes sure it is there. This _really_ improves performance. - module = sys.modules.get('__main__') - if module and not hasattr(module, 'main'): - module.main = lambda: util.run_wsgi_app(handler) - util.run_wsgi_app(handler) - - -class TwistedServer(ServerAdapter): - """ Untested. """ - def run(self, handler): - from twisted.web import server, wsgi - from twisted.python.threadpool import ThreadPool - from twisted.internet import reactor - thread_pool = ThreadPool() - thread_pool.start() - reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop) - factory = server.Site(wsgi.WSGIResource(reactor, thread_pool, handler)) - reactor.listenTCP(self.port, factory, interface=self.host) - reactor.run() - - -class DieselServer(ServerAdapter): - """ Untested. """ - def run(self, handler): - from diesel.protocols.wsgi import WSGIApplication - app = WSGIApplication(handler, port=self.port) - app.run() - - -class GeventServer(ServerAdapter): - """ Untested. Options: - - * `fast` (default: False) uses libevent's http server, but has some - issues: No streaming, no pipelining, no SSL. - * See gevent.wsgi.WSGIServer() documentation for more options. - """ - def run(self, handler): - from gevent import pywsgi, local - if not isinstance(threading.local(), local.local): - msg = "Bottle requires gevent.monkey.patch_all() (before import)" - raise RuntimeError(msg) - if self.options.pop('fast', None): - depr('The "fast" option has been deprecated and removed by Gevent.') - if self.quiet: - self.options['log'] = None - address = (self.host, self.port) - server = pywsgi.WSGIServer(address, handler, **self.options) - if 'BOTTLE_CHILD' in os.environ: - import signal - signal.signal(signal.SIGINT, lambda s, f: server.stop()) - server.serve_forever() - - -class GeventSocketIOServer(ServerAdapter): - def run(self,handler): - from socketio import server - address = (self.host, self.port) - server.SocketIOServer(address, handler, **self.options).serve_forever() - - -class GunicornServer(ServerAdapter): - """ Untested. See http://gunicorn.org/configure.html for options. """ - def run(self, handler): - from gunicorn.app.base import Application - - config = {'bind': "%s:%d" % (self.host, int(self.port))} - config.update(self.options) - - class GunicornApplication(Application): - def init(self, parser, opts, args): - return config - - def load(self): - return handler - - GunicornApplication().run() - - -class EventletServer(ServerAdapter): - """ Untested """ - def run(self, handler): - from eventlet import wsgi, listen - try: - wsgi.server(listen((self.host, self.port)), handler, - log_output=(not self.quiet)) - except TypeError: - # Fallback, if we have old version of eventlet - wsgi.server(listen((self.host, self.port)), handler) - - -class RocketServer(ServerAdapter): - """ Untested. """ - def run(self, handler): - from rocket import Rocket - server = Rocket((self.host, self.port), 'wsgi', { 'wsgi_app' : handler }) - server.start() - - -class BjoernServer(ServerAdapter): - """ Fast server written in C: https://github.com/jonashaag/bjoern """ - def run(self, handler): - from bjoern import run - run(handler, self.host, self.port) - - -class AutoServer(ServerAdapter): - """ Untested. """ - adapters = [WaitressServer, PasteServer, TwistedServer, CherryPyServer, WSGIRefServer] - def run(self, handler): - for sa in self.adapters: - try: - return sa(self.host, self.port, **self.options).run(handler) - except ImportError: - pass - -server_names = { - 'cgi': CGIServer, - 'flup': FlupFCGIServer, - 'wsgiref': WSGIRefServer, - 'waitress': WaitressServer, - 'cherrypy': CherryPyServer, - 'paste': PasteServer, - 'fapws3': FapwsServer, - 'tornado': TornadoServer, - 'gae': AppEngineServer, - 'twisted': TwistedServer, - 'diesel': DieselServer, - 'meinheld': MeinheldServer, - 'gunicorn': GunicornServer, - 'eventlet': EventletServer, - 'gevent': GeventServer, - 'geventSocketIO':GeventSocketIOServer, - 'rocket': RocketServer, - 'bjoern' : BjoernServer, - 'auto': AutoServer, -} - - - - - - -############################################################################### -# Application Control ########################################################## -############################################################################### - - -def load(target, **namespace): - """ Import a module or fetch an object from a module. - - * ``package.module`` returns `module` as a module object. - * ``pack.mod:name`` returns the module variable `name` from `pack.mod`. - * ``pack.mod:func()`` calls `pack.mod.func()` and returns the result. - - The last form accepts not only function calls, but any type of - expression. Keyword arguments passed to this function are available as - local variables. Example: ``import_string('re:compile(x)', x='[a-z]')`` - """ - module, target = target.split(":", 1) if ':' in target else (target, None) - if module not in sys.modules: __import__(module) - if not target: return sys.modules[module] - if target.isalnum(): return getattr(sys.modules[module], target) - package_name = module.split('.')[0] - namespace[package_name] = sys.modules[package_name] - return eval('%s.%s' % (module, target), namespace) - - -def load_app(target): - """ Load a bottle application from a module and make sure that the import - does not affect the current default application, but returns a separate - application object. See :func:`load` for the target parameter. """ - global NORUN; NORUN, nr_old = True, NORUN - try: - tmp = default_app.push() # Create a new "default application" - rv = load(target) # Import the target module - return rv if callable(rv) else tmp - finally: - default_app.remove(tmp) # Remove the temporary added default application - NORUN = nr_old - -_debug = debug -def run(app=None, server='wsgiref', host='127.0.0.1', port=8080, - interval=1, reloader=False, quiet=False, plugins=None, - debug=None, **kargs): - """ Start a server instance. This method blocks until the server terminates. - - :param app: WSGI application or target string supported by - :func:`load_app`. (default: :func:`default_app`) - :param server: Server adapter to use. See :data:`server_names` keys - for valid names or pass a :class:`ServerAdapter` subclass. - (default: `wsgiref`) - :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on - all interfaces including the external one. (default: 127.0.0.1) - :param port: Server port to bind to. Values below 1024 require root - privileges. (default: 8080) - :param reloader: Start auto-reloading server? (default: False) - :param interval: Auto-reloader interval in seconds (default: 1) - :param quiet: Suppress output to stdout and stderr? (default: False) - :param options: Options passed to the server adapter. - """ - if NORUN: return - if reloader and not os.environ.get('BOTTLE_CHILD'): - try: - lockfile = None - fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock') - os.close(fd) # We only need this file to exist. We never write to it - while os.path.exists(lockfile): - args = [sys.executable] + sys.argv - environ = os.environ.copy() - environ['BOTTLE_CHILD'] = 'true' - environ['BOTTLE_LOCKFILE'] = lockfile - p = subprocess.Popen(args, env=environ) - while p.poll() is None: # Busy wait... - os.utime(lockfile, None) # I am alive! - time.sleep(interval) - if p.poll() != 3: - if os.path.exists(lockfile): os.unlink(lockfile) - sys.exit(p.poll()) - except KeyboardInterrupt: - pass - finally: - if os.path.exists(lockfile): - os.unlink(lockfile) - return - - try: - if debug is not None: _debug(debug) - app = app or default_app() - if isinstance(app, basestring): - app = load_app(app) - if not callable(app): - raise ValueError("Application is not callable: %r" % app) - - for plugin in plugins or []: - app.install(plugin) - - if server in server_names: - server = server_names.get(server) - if isinstance(server, basestring): - server = load(server) - if isinstance(server, type): - server = server(host=host, port=port, **kargs) - if not isinstance(server, ServerAdapter): - raise ValueError("Unknown or unsupported server: %r" % server) - - server.quiet = server.quiet or quiet - if not server.quiet: - _stderr("Bottle v%s server starting up (using %s)...\n" % (__version__, repr(server))) - _stderr("Listening on http://%s:%d/\n" % (server.host, server.port)) - _stderr("Hit Ctrl-C to quit.\n\n") - - if reloader: - lockfile = os.environ.get('BOTTLE_LOCKFILE') - bgcheck = FileCheckerThread(lockfile, interval) - with bgcheck: - server.run(app) - if bgcheck.status == 'reload': - sys.exit(3) - else: - server.run(app) - except KeyboardInterrupt: - pass - except (SystemExit, MemoryError): - raise - except: - if not reloader: raise - if not getattr(server, 'quiet', quiet): - print_exc() - time.sleep(interval) - sys.exit(3) - - - -class FileCheckerThread(threading.Thread): - ''' Interrupt main-thread as soon as a changed module file is detected, - the lockfile gets deleted or gets to old. ''' - - def __init__(self, lockfile, interval): - threading.Thread.__init__(self) - self.lockfile, self.interval = lockfile, interval - #: Is one of 'reload', 'error' or 'exit' - self.status = None - - def run(self): - exists = os.path.exists - mtime = lambda path: os.stat(path).st_mtime - files = dict() - - for module in list(sys.modules.values()): - path = getattr(module, '__file__', '') or '' - if path[-4:] in ('.pyo', '.pyc'): path = path[:-1] - if path and exists(path): files[path] = mtime(path) - - while not self.status: - if not exists(self.lockfile)\ - or mtime(self.lockfile) < time.time() - self.interval - 5: - self.status = 'error' - thread.interrupt_main() - for path, lmtime in list(files.items()): - if not exists(path) or mtime(path) > lmtime: - self.status = 'reload' - thread.interrupt_main() - break - time.sleep(self.interval) - - def __enter__(self): - self.start() - - def __exit__(self, exc_type, exc_val, exc_tb): - if not self.status: self.status = 'exit' # silent exit - self.join() - return exc_type is not None and issubclass(exc_type, KeyboardInterrupt) - - - - - -############################################################################### -# Template Adapters ############################################################ -############################################################################### - - -class TemplateError(HTTPError): - def __init__(self, message): - HTTPError.__init__(self, 500, message) - - -class BaseTemplate(object): - """ Base class and minimal API for template adapters """ - extensions = ['tpl','html','thtml','stpl'] - settings = {} #used in prepare() - defaults = {} #used in render() - - def __init__(self, source=None, name=None, lookup=[], encoding='utf8', **settings): - """ Create a new template. - If the source parameter (str or buffer) is missing, the name argument - is used to guess a template filename. Subclasses can assume that - self.source and/or self.filename are set. Both are strings. - The lookup, encoding and settings parameters are stored as instance - variables. - The lookup parameter stores a list containing directory paths. - The encoding parameter should be used to decode byte strings or files. - The settings parameter contains a dict for engine-specific settings. - """ - self.name = name - self.source = source.read() if hasattr(source, 'read') else source - self.filename = source.filename if hasattr(source, 'filename') else None - self.lookup = [os.path.abspath(x) for x in lookup] - self.encoding = encoding - self.settings = self.settings.copy() # Copy from class variable - self.settings.update(settings) # Apply - if not self.source and self.name: - self.filename = self.search(self.name, self.lookup) - if not self.filename: - raise TemplateError('Template %s not found.' % repr(name)) - if not self.source and not self.filename: - raise TemplateError('No template specified.') - self.prepare(**self.settings) - - @classmethod - def search(cls, name, lookup=[]): - """ Search name in all directories specified in lookup. - First without, then with common extensions. Return first hit. """ - if not lookup: - depr('The template lookup path list should not be empty.') #0.12 - lookup = ['.'] - - if os.path.isabs(name) and os.path.isfile(name): - depr('Absolute template path names are deprecated.') #0.12 - return os.path.abspath(name) - - for spath in lookup: - spath = os.path.abspath(spath) + os.sep - fname = os.path.abspath(os.path.join(spath, name)) - if not fname.startswith(spath): continue - if os.path.isfile(fname): return fname - for ext in cls.extensions: - if os.path.isfile('%s.%s' % (fname, ext)): - return '%s.%s' % (fname, ext) - - @classmethod - def global_config(cls, key, *args): - ''' This reads or sets the global settings stored in class.settings. ''' - if args: - cls.settings = cls.settings.copy() # Make settings local to class - cls.settings[key] = args[0] - else: - return cls.settings[key] - - def prepare(self, **options): - """ Run preparations (parsing, caching, ...). - It should be possible to call this again to refresh a template or to - update settings. - """ - raise NotImplementedError - - def render(self, *args, **kwargs): - """ Render the template with the specified local variables and return - a single byte or unicode string. If it is a byte string, the encoding - must match self.encoding. This method must be thread-safe! - Local variables may be provided in dictionaries (args) - or directly, as keywords (kwargs). - """ - raise NotImplementedError - - -class MakoTemplate(BaseTemplate): - def prepare(self, **options): - from mako.template import Template - from mako.lookup import TemplateLookup - options.update({'input_encoding':self.encoding}) - options.setdefault('format_exceptions', bool(DEBUG)) - lookup = TemplateLookup(directories=self.lookup, **options) - if self.source: - self.tpl = Template(self.source, lookup=lookup, **options) - else: - self.tpl = Template(uri=self.name, filename=self.filename, lookup=lookup, **options) - - def render(self, *args, **kwargs): - for dictarg in args: kwargs.update(dictarg) - _defaults = self.defaults.copy() - _defaults.update(kwargs) - return self.tpl.render(**_defaults) - - -class CheetahTemplate(BaseTemplate): - def prepare(self, **options): - from Cheetah.Template import Template - self.context = threading.local() - self.context.vars = {} - options['searchList'] = [self.context.vars] - if self.source: - self.tpl = Template(source=self.source, **options) - else: - self.tpl = Template(file=self.filename, **options) - - def render(self, *args, **kwargs): - for dictarg in args: kwargs.update(dictarg) - self.context.vars.update(self.defaults) - self.context.vars.update(kwargs) - out = str(self.tpl) - self.context.vars.clear() - return out - - -class Jinja2Template(BaseTemplate): - def prepare(self, filters=None, tests=None, globals={}, **kwargs): - from jinja2 import Environment, FunctionLoader - if 'prefix' in kwargs: # TODO: to be removed after a while - raise RuntimeError('The keyword argument `prefix` has been removed. ' - 'Use the full jinja2 environment name line_statement_prefix instead.') - self.env = Environment(loader=FunctionLoader(self.loader), **kwargs) - if filters: self.env.filters.update(filters) - if tests: self.env.tests.update(tests) - if globals: self.env.globals.update(globals) - if self.source: - self.tpl = self.env.from_string(self.source) - else: - self.tpl = self.env.get_template(self.filename) - - def render(self, *args, **kwargs): - for dictarg in args: kwargs.update(dictarg) - _defaults = self.defaults.copy() - _defaults.update(kwargs) - return self.tpl.render(**_defaults) - - def loader(self, name): - fname = self.search(name, self.lookup) - if not fname: return - with open(fname, "rb") as f: - return f.read().decode(self.encoding) - - -class SimpleTemplate(BaseTemplate): - - def prepare(self, escape_func=html_escape, noescape=False, syntax=None, **ka): - self.cache = {} - enc = self.encoding - self._str = lambda x: touni(x, enc) - self._escape = lambda x: escape_func(touni(x, enc)) - self.syntax = syntax - if noescape: - self._str, self._escape = self._escape, self._str - - @cached_property - def co(self): - return compile(self.code, self.filename or '', 'exec') - - @cached_property - def code(self): - source = self.source - if not source: - with open(self.filename, 'rb') as f: - source = f.read() - try: - source, encoding = touni(source), 'utf8' - except UnicodeError: - depr('Template encodings other than utf8 are no longer supported.') #0.11 - source, encoding = touni(source, 'latin1'), 'latin1' - parser = StplParser(source, encoding=encoding, syntax=self.syntax) - code = parser.translate() - self.encoding = parser.encoding - return code - - def _rebase(self, _env, _name=None, **kwargs): - if _name is None: - depr('Rebase function called without arguments.' - ' You were probably looking for {{base}}?', True) #0.12 - _env['_rebase'] = (_name, kwargs) - - def _include(self, _env, _name=None, **kwargs): - if _name is None: - depr('Rebase function called without arguments.' - ' You were probably looking for {{base}}?', True) #0.12 - env = _env.copy() - env.update(kwargs) - if _name not in self.cache: - self.cache[_name] = self.__class__(name=_name, lookup=self.lookup) - return self.cache[_name].execute(env['_stdout'], env) - - def execute(self, _stdout, kwargs): - env = self.defaults.copy() - env.update(kwargs) - env.update({'_stdout': _stdout, '_printlist': _stdout.extend, - 'include': functools.partial(self._include, env), - 'rebase': functools.partial(self._rebase, env), '_rebase': None, - '_str': self._str, '_escape': self._escape, 'get': env.get, - 'setdefault': env.setdefault, 'defined': env.__contains__ }) - eval(self.co, env) - if env.get('_rebase'): - subtpl, rargs = env.pop('_rebase') - rargs['base'] = ''.join(_stdout) #copy stdout - del _stdout[:] # clear stdout - return self._include(env, subtpl, **rargs) - return env - - def render(self, *args, **kwargs): - """ Render the template using keyword arguments as local variables. """ - env = {}; stdout = [] - for dictarg in args: env.update(dictarg) - env.update(kwargs) - self.execute(stdout, env) - return ''.join(stdout) - - -class StplSyntaxError(TemplateError): pass - - -class StplParser(object): - ''' Parser for stpl templates. ''' - _re_cache = {} #: Cache for compiled re patterns - # This huge pile of voodoo magic splits python code into 8 different tokens. - # 1: All kinds of python strings (trust me, it works) - _re_tok = '([urbURB]?(?:\'\'(?!\')|""(?!")|\'{6}|"{6}' \ - '|\'(?:[^\\\\\']|\\\\.)+?\'|"(?:[^\\\\"]|\\\\.)+?"' \ - '|\'{3}(?:[^\\\\]|\\\\.|\\n)+?\'{3}' \ - '|"{3}(?:[^\\\\]|\\\\.|\\n)+?"{3}))' - _re_inl = _re_tok.replace('|\\n','') # We re-use this string pattern later - # 2: Comments (until end of line, but not the newline itself) - _re_tok += '|(#.*)' - # 3,4: Open and close grouping tokens - _re_tok += '|([\\[\\{\\(])' - _re_tok += '|([\\]\\}\\)])' - # 5,6: Keywords that start or continue a python block (only start of line) - _re_tok += '|^([ \\t]*(?:if|for|while|with|try|def|class)\\b)' \ - '|^([ \\t]*(?:elif|else|except|finally)\\b)' - # 7: Our special 'end' keyword (but only if it stands alone) - _re_tok += '|((?:^|;)[ \\t]*end[ \\t]*(?=(?:%(block_close)s[ \\t]*)?\\r?$|;|#))' - # 8: A customizable end-of-code-block template token (only end of line) - _re_tok += '|(%(block_close)s[ \\t]*(?=\\r?$))' - # 9: And finally, a single newline. The 10th token is 'everything else' - _re_tok += '|(\\r?\\n)' - - # Match the start tokens of code areas in a template - _re_split = '(?m)^[ \t]*(\\\\?)((%(line_start)s)|(%(block_start)s))(%%?)' - # Match inline statements (may contain python strings) - _re_inl = '(?m)%%(inline_start)s((?:%s|[^\'"\n]*?)+)%%(inline_end)s' % _re_inl - _re_tok = '(?m)' + _re_tok - - default_syntax = '<% %> % {{ }}' - - def __init__(self, source, syntax=None, encoding='utf8'): - self.source, self.encoding = touni(source, encoding), encoding - self.set_syntax(syntax or self.default_syntax) - self.code_buffer, self.text_buffer = [], [] - self.lineno, self.offset = 1, 0 - self.indent, self.indent_mod = 0, 0 - self.paren_depth = 0 - - def get_syntax(self): - ''' Tokens as a space separated string (default: <% %> % {{ }}) ''' - return self._syntax - - def set_syntax(self, syntax): - self._syntax = syntax - self._tokens = syntax.split() - if not syntax in self._re_cache: - names = 'block_start block_close line_start inline_start inline_end' - etokens = map(re.escape, self._tokens) - pattern_vars = dict(zip(names.split(), etokens)) - patterns = (self._re_split, self._re_tok, self._re_inl) - patterns = [re.compile(p%pattern_vars) for p in patterns] - self._re_cache[syntax] = patterns - self.re_split, self.re_tok, self.re_inl = self._re_cache[syntax] - - syntax = property(get_syntax, set_syntax) - - def translate(self): - if self.offset: raise RuntimeError('Parser is a one time instance.') - while True: - m = self.re_split.search(self.source[self.offset:]) - if m: - text = self.source[self.offset:self.offset+m.start()] - self.text_buffer.append(text) - self.offset += m.end() - if m.group(1): # New escape syntax - line, sep, _ = self.source[self.offset:].partition('\n') - self.text_buffer.append(m.group(2)+m.group(5)+line+sep) - self.offset += len(line+sep)+1 - continue - elif m.group(5): # Old escape syntax - depr('Escape code lines with a backslash.') #0.12 - line, sep, _ = self.source[self.offset:].partition('\n') - self.text_buffer.append(m.group(2)+line+sep) - self.offset += len(line+sep)+1 - continue - self.flush_text() - self.read_code(multiline=bool(m.group(4))) - else: break - self.text_buffer.append(self.source[self.offset:]) - self.flush_text() - return ''.join(self.code_buffer) - - def read_code(self, multiline): - code_line, comment = '', '' - while True: - m = self.re_tok.search(self.source[self.offset:]) - if not m: - code_line += self.source[self.offset:] - self.offset = len(self.source) - self.write_code(code_line.strip(), comment) - return - code_line += self.source[self.offset:self.offset+m.start()] - self.offset += m.end() - _str, _com, _po, _pc, _blk1, _blk2, _end, _cend, _nl = m.groups() - if (code_line or self.paren_depth > 0) and (_blk1 or _blk2): # a if b else c - code_line += _blk1 or _blk2 - continue - if _str: # Python string - code_line += _str - elif _com: # Python comment (up to EOL) - comment = _com - if multiline and _com.strip().endswith(self._tokens[1]): - multiline = False # Allow end-of-block in comments - elif _po: # open parenthesis - self.paren_depth += 1 - code_line += _po - elif _pc: # close parenthesis - if self.paren_depth > 0: - # we could check for matching parentheses here, but it's - # easier to leave that to python - just check counts - self.paren_depth -= 1 - code_line += _pc - elif _blk1: # Start-block keyword (if/for/while/def/try/...) - code_line, self.indent_mod = _blk1, -1 - self.indent += 1 - elif _blk2: # Continue-block keyword (else/elif/except/...) - code_line, self.indent_mod = _blk2, -1 - elif _end: # The non-standard 'end'-keyword (ends a block) - self.indent -= 1 - elif _cend: # The end-code-block template token (usually '%>') - if multiline: multiline = False - else: code_line += _cend - else: # \n - self.write_code(code_line.strip(), comment) - self.lineno += 1 - code_line, comment, self.indent_mod = '', '', 0 - if not multiline: - break - - def flush_text(self): - text = ''.join(self.text_buffer) - del self.text_buffer[:] - if not text: return - parts, pos, nl = [], 0, '\\\n'+' '*self.indent - for m in self.re_inl.finditer(text): - prefix, pos = text[pos:m.start()], m.end() - if prefix: - parts.append(nl.join(map(repr, prefix.splitlines(True)))) - if prefix.endswith('\n'): parts[-1] += nl - parts.append(self.process_inline(m.group(1).strip())) - if pos < len(text): - prefix = text[pos:] - lines = prefix.splitlines(True) - if lines[-1].endswith('\\\\\n'): lines[-1] = lines[-1][:-3] - elif lines[-1].endswith('\\\\\r\n'): lines[-1] = lines[-1][:-4] - parts.append(nl.join(map(repr, lines))) - code = '_printlist((%s,))' % ', '.join(parts) - self.lineno += code.count('\n')+1 - self.write_code(code) - - def process_inline(self, chunk): - if chunk[0] == '!': return '_str(%s)' % chunk[1:] - return '_escape(%s)' % chunk - - def write_code(self, line, comment=''): - line, comment = self.fix_backward_compatibility(line, comment) - code = ' ' * (self.indent+self.indent_mod) - code += line.lstrip() + comment + '\n' - self.code_buffer.append(code) - - def fix_backward_compatibility(self, line, comment): - parts = line.strip().split(None, 2) - if parts and parts[0] in ('include', 'rebase'): - depr('The include and rebase keywords are functions now.') #0.12 - if len(parts) == 1: return "_printlist([base])", comment - elif len(parts) == 2: return "_=%s(%r)" % tuple(parts), comment - else: return "_=%s(%r, %s)" % tuple(parts), comment - if self.lineno <= 2 and not line.strip() and 'coding' in comment: - m = re.match(r"#.*coding[:=]\s*([-\w.]+)", comment) - if m: - depr('PEP263 encoding strings in templates are deprecated.') #0.12 - enc = m.group(1) - self.source = self.source.encode(self.encoding).decode(enc) - self.encoding = enc - return line, comment.replace('coding','coding*') - return line, comment - - -def template(*args, **kwargs): - ''' - Get a rendered template as a string iterator. - You can use a name, a filename or a template string as first parameter. - Template rendering arguments can be passed as dictionaries - or directly (as keyword arguments). - ''' - tpl = args[0] if args else None - adapter = kwargs.pop('template_adapter', SimpleTemplate) - lookup = kwargs.pop('template_lookup', TEMPLATE_PATH) - tplid = (id(lookup), tpl) - if tplid not in TEMPLATES or DEBUG: - settings = kwargs.pop('template_settings', {}) - if isinstance(tpl, adapter): - TEMPLATES[tplid] = tpl - if settings: TEMPLATES[tplid].prepare(**settings) - elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl: - TEMPLATES[tplid] = adapter(source=tpl, lookup=lookup, **settings) - else: - TEMPLATES[tplid] = adapter(name=tpl, lookup=lookup, **settings) - if not TEMPLATES[tplid]: - abort(500, 'Template (%s) not found' % tpl) - for dictarg in args[1:]: kwargs.update(dictarg) - return TEMPLATES[tplid].render(kwargs) - -mako_template = functools.partial(template, template_adapter=MakoTemplate) -cheetah_template = functools.partial(template, template_adapter=CheetahTemplate) -jinja2_template = functools.partial(template, template_adapter=Jinja2Template) - - -def view(tpl_name, **defaults): - ''' Decorator: renders a template for a handler. - The handler can control its behavior like that: - - - return a dict of template vars to fill out the template - - return something other than a dict and the view decorator will not - process the template, but return the handler result as is. - This includes returning a HTTPResponse(dict) to get, - for instance, JSON with autojson or other castfilters. - ''' - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - result = func(*args, **kwargs) - if isinstance(result, (dict, DictMixin)): - tplvars = defaults.copy() - tplvars.update(result) - return template(tpl_name, **tplvars) - elif result is None: - return template(tpl_name, defaults) - return result - return wrapper - return decorator - -mako_view = functools.partial(view, template_adapter=MakoTemplate) -cheetah_view = functools.partial(view, template_adapter=CheetahTemplate) -jinja2_view = functools.partial(view, template_adapter=Jinja2Template) - - - - - - -############################################################################### -# Constants and Globals ######################################################## -############################################################################### - - -TEMPLATE_PATH = ['./', './views/'] -TEMPLATES = {} -DEBUG = False -NORUN = False # If set, run() does nothing. Used by load_app() - -#: A dict to map HTTP status codes (e.g. 404) to phrases (e.g. 'Not Found') -HTTP_CODES = httplib.responses -HTTP_CODES[418] = "I'm a teapot" # RFC 2324 -HTTP_CODES[422] = "Unprocessable Entity" # RFC 4918 -HTTP_CODES[428] = "Precondition Required" -HTTP_CODES[429] = "Too Many Requests" -HTTP_CODES[431] = "Request Header Fields Too Large" -HTTP_CODES[511] = "Network Authentication Required" -_HTTP_STATUS_LINES = dict((k, '%d %s'%(k,v)) for (k,v) in HTTP_CODES.items()) - -#: The default template used for error pages. Override with @error() -ERROR_PAGE_TEMPLATE = """ -%%try: - %%from %s import DEBUG, HTTP_CODES, request, touni - - - - Error: {{e.status}} - - - -

Error: {{e.status}}

-

Sorry, the requested URL {{repr(request.url)}} - caused an error:

-
{{e.body}}
- %%if DEBUG and e.exception: -

Exception:

-
{{repr(e.exception)}}
- %%end - %%if DEBUG and e.traceback: -

Traceback:

-
{{e.traceback}}
- %%end - - -%%except ImportError: - ImportError: Could not generate the error page. Please add bottle to - the import path. -%%end -""" % __name__ - -#: A thread-safe instance of :class:`LocalRequest`. If accessed from within a -#: request callback, this instance always refers to the *current* request -#: (even on a multithreaded server). -request = LocalRequest() - -#: A thread-safe instance of :class:`LocalResponse`. It is used to change the -#: HTTP response for the *current* request. -response = LocalResponse() - -#: A thread-safe namespace. Not used by Bottle. -local = threading.local() - -# Initialize app stack (create first empty Bottle app) -# BC: 0.6.4 and needed for run() -app = default_app = AppStack() -app.push() - -#: A virtual package that redirects import statements. -#: Example: ``import bottle.ext.sqlite`` actually imports `bottle_sqlite`. -ext = _ImportRedirect('bottle.ext' if __name__ == '__main__' else __name__+".ext", 'bottle_%s').module - -if __name__ == '__main__': - opt, args, parser = _cmd_options, _cmd_args, _cmd_parser - if opt.version: - _stdout('Bottle %s\n'%__version__) - sys.exit(0) - if not args: - parser.print_help() - _stderr('\nError: No application specified.\n') - sys.exit(1) - - sys.path.insert(0, '.') - sys.modules.setdefault('bottle', sys.modules['__main__']) - - host, port = (opt.bind or 'localhost'), 8080 - if ':' in host and host.rfind(']') < host.rfind(':'): - host, port = host.rsplit(':', 1) - host = host.strip('[]') - - run(args[0], host=host, port=int(port), server=opt.server, - reloader=opt.reload, plugins=opt.plugin, debug=opt.debug) - - - - -# THE END diff --git a/IKEA_scraper/.venv/bin/futurize b/IKEA_scraper/.venv/bin/futurize deleted file mode 100644 index bbc864ca..00000000 --- a/IKEA_scraper/.venv/bin/futurize +++ /dev/null @@ -1,33 +0,0 @@ -#!/media/HardDrive/Pyhton/School/IKEA_scraper/.venv/bin/python3 -# EASY-INSTALL-ENTRY-SCRIPT: 'future==0.18.2','console_scripts','futurize' -import re -import sys - -# for compatibility with easy_install; see #2198 -__requires__ = 'future==0.18.2' - -try: - from importlib.metadata import distribution -except ImportError: - try: - from importlib_metadata import distribution - except ImportError: - from pkg_resources import load_entry_point - - -def importlib_load_entry_point(spec, group, name): - dist_name, _, _ = spec.partition('==') - matches = ( - entry_point - for entry_point in distribution(dist_name).entry_points - if entry_point.group == group and entry_point.name == name - ) - return next(matches).load() - - -globals().setdefault('load_entry_point', importlib_load_entry_point) - - -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) - sys.exit(load_entry_point('future==0.18.2', 'console_scripts', 'futurize')()) diff --git a/IKEA_scraper/.venv/bin/normalizer b/IKEA_scraper/.venv/bin/normalizer deleted file mode 100644 index c58b0f94..00000000 --- a/IKEA_scraper/.venv/bin/normalizer +++ /dev/null @@ -1,8 +0,0 @@ -#!/media/HardDrive/Pyhton/School/IKEA_scraper/.venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from charset_normalizer.cli.normalizer import cli_detect -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(cli_detect()) diff --git a/IKEA_scraper/.venv/bin/pasteurize b/IKEA_scraper/.venv/bin/pasteurize deleted file mode 100644 index da7b65d0..00000000 --- a/IKEA_scraper/.venv/bin/pasteurize +++ /dev/null @@ -1,33 +0,0 @@ -#!/media/HardDrive/Pyhton/School/IKEA_scraper/.venv/bin/python3 -# EASY-INSTALL-ENTRY-SCRIPT: 'future==0.18.2','console_scripts','pasteurize' -import re -import sys - -# for compatibility with easy_install; see #2198 -__requires__ = 'future==0.18.2' - -try: - from importlib.metadata import distribution -except ImportError: - try: - from importlib_metadata import distribution - except ImportError: - from pkg_resources import load_entry_point - - -def importlib_load_entry_point(spec, group, name): - dist_name, _, _ = spec.partition('==') - matches = ( - entry_point - for entry_point in distribution(dist_name).entry_points - if entry_point.group == group and entry_point.name == name - ) - return next(matches).load() - - -globals().setdefault('load_entry_point', importlib_load_entry_point) - - -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) - sys.exit(load_entry_point('future==0.18.2', 'console_scripts', 'pasteurize')()) diff --git a/IKEA_scraper/.venv/bin/pip b/IKEA_scraper/.venv/bin/pip deleted file mode 100644 index fff7184c..00000000 --- a/IKEA_scraper/.venv/bin/pip +++ /dev/null @@ -1,8 +0,0 @@ -#!/media/HardDrive/Pyhton/School/IKEA_scraper/.venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/IKEA_scraper/.venv/bin/pip3 b/IKEA_scraper/.venv/bin/pip3 deleted file mode 100644 index fff7184c..00000000 --- a/IKEA_scraper/.venv/bin/pip3 +++ /dev/null @@ -1,8 +0,0 @@ -#!/media/HardDrive/Pyhton/School/IKEA_scraper/.venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/IKEA_scraper/.venv/bin/pip3.9 b/IKEA_scraper/.venv/bin/pip3.9 deleted file mode 100644 index fff7184c..00000000 --- a/IKEA_scraper/.venv/bin/pip3.9 +++ /dev/null @@ -1,8 +0,0 @@ -#!/media/HardDrive/Pyhton/School/IKEA_scraper/.venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/IKEA_scraper/.venv/bin/python b/IKEA_scraper/.venv/bin/python deleted file mode 120000 index b8a0adbb..00000000 --- a/IKEA_scraper/.venv/bin/python +++ /dev/null @@ -1 +0,0 @@ -python3 \ No newline at end of file diff --git a/IKEA_scraper/.venv/bin/python3 b/IKEA_scraper/.venv/bin/python3 deleted file mode 120000 index ae65fdaa..00000000 --- a/IKEA_scraper/.venv/bin/python3 +++ /dev/null @@ -1 +0,0 @@ -/usr/bin/python3 \ No newline at end of file diff --git a/IKEA_scraper/.venv/bin/python3.9 b/IKEA_scraper/.venv/bin/python3.9 deleted file mode 120000 index b8a0adbb..00000000 --- a/IKEA_scraper/.venv/bin/python3.9 +++ /dev/null @@ -1 +0,0 @@ -python3 \ No newline at end of file diff --git a/IKEA_scraper/.venv/bin/yapf b/IKEA_scraper/.venv/bin/yapf deleted file mode 100755 index 547508c1..00000000 --- a/IKEA_scraper/.venv/bin/yapf +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/kristofers/Documents/python/School/IKEA_scraper/.venv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from yapf import run_main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(run_main()) diff --git a/IKEA_scraper/.venv/bin/yapf-diff b/IKEA_scraper/.venv/bin/yapf-diff deleted file mode 100755 index f6aa1c67..00000000 --- a/IKEA_scraper/.venv/bin/yapf-diff +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/kristofers/Documents/python/School/IKEA_scraper/.venv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from yapf.third_party.yapf_diff.yapf_diff import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/IKEA_scraper/.venv/include/site/python3.9/greenlet/greenlet.h b/IKEA_scraper/.venv/include/site/python3.9/greenlet/greenlet.h deleted file mode 100644 index 830bef8d..00000000 --- a/IKEA_scraper/.venv/include/site/python3.9/greenlet/greenlet.h +++ /dev/null @@ -1,146 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ - -/* Greenlet object interface */ - -#ifndef Py_GREENLETOBJECT_H -#define Py_GREENLETOBJECT_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* This is deprecated and undocumented. It does not change. */ -#define GREENLET_VERSION "1.0.0" - -typedef struct _greenlet { - PyObject_HEAD - char* stack_start; - char* stack_stop; - char* stack_copy; - intptr_t stack_saved; - struct _greenlet* stack_prev; - struct _greenlet* parent; - PyObject* run_info; - struct _frame* top_frame; - int recursion_depth; - PyObject* weakreflist; -#if PY_VERSION_HEX >= 0x030700A3 - _PyErr_StackItem* exc_info; - _PyErr_StackItem exc_state; -#else - PyObject* exc_type; - PyObject* exc_value; - PyObject* exc_traceback; -#endif - PyObject* dict; -#if PY_VERSION_HEX >= 0x030700A3 - PyObject* context; -#endif -#if PY_VERSION_HEX >= 0x30A00B1 - CFrame* cframe; -#endif -} PyGreenlet; - -#define PyGreenlet_Check(op) PyObject_TypeCheck(op, &PyGreenlet_Type) -#define PyGreenlet_MAIN(op) (((PyGreenlet*)(op))->stack_stop == (char*)-1) -#define PyGreenlet_STARTED(op) (((PyGreenlet*)(op))->stack_stop != NULL) -#define PyGreenlet_ACTIVE(op) (((PyGreenlet*)(op))->stack_start != NULL) -#define PyGreenlet_GET_PARENT(op) (((PyGreenlet*)(op))->parent) - -/* C API functions */ - -/* Total number of symbols that are exported */ -#define PyGreenlet_API_pointers 8 - -#define PyGreenlet_Type_NUM 0 -#define PyExc_GreenletError_NUM 1 -#define PyExc_GreenletExit_NUM 2 - -#define PyGreenlet_New_NUM 3 -#define PyGreenlet_GetCurrent_NUM 4 -#define PyGreenlet_Throw_NUM 5 -#define PyGreenlet_Switch_NUM 6 -#define PyGreenlet_SetParent_NUM 7 - -#ifndef GREENLET_MODULE -/* This section is used by modules that uses the greenlet C API */ -static void** _PyGreenlet_API = NULL; - -# define PyGreenlet_Type \ - (*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM]) - -# define PyExc_GreenletError \ - ((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM]) - -# define PyExc_GreenletExit \ - ((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM]) - -/* - * PyGreenlet_New(PyObject *args) - * - * greenlet.greenlet(run, parent=None) - */ -# define PyGreenlet_New \ - (*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \ - _PyGreenlet_API[PyGreenlet_New_NUM]) - -/* - * PyGreenlet_GetCurrent(void) - * - * greenlet.getcurrent() - */ -# define PyGreenlet_GetCurrent \ - (*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) - -/* - * PyGreenlet_Throw( - * PyGreenlet *greenlet, - * PyObject *typ, - * PyObject *val, - * PyObject *tb) - * - * g.throw(...) - */ -# define PyGreenlet_Throw \ - (*(PyObject * (*)(PyGreenlet * self, \ - PyObject * typ, \ - PyObject * val, \ - PyObject * tb)) \ - _PyGreenlet_API[PyGreenlet_Throw_NUM]) - -/* - * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) - * - * g.switch(*args, **kwargs) - */ -# define PyGreenlet_Switch \ - (*(PyObject * \ - (*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \ - _PyGreenlet_API[PyGreenlet_Switch_NUM]) - -/* - * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) - * - * g.parent = new_parent - */ -# define PyGreenlet_SetParent \ - (*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \ - _PyGreenlet_API[PyGreenlet_SetParent_NUM]) - -/* Macro that imports greenlet and initializes C API */ -/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we - keep the older definition to be sure older code that might have a copy of - the header still works. */ -# define PyGreenlet_Import() \ - { \ - _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \ - } - -#endif /* GREENLET_MODULE */ - -#ifdef __cplusplus -} -#endif -#endif /* !Py_GREENLETOBJECT_H */ diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/PKG-INFO b/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/PKG-INFO deleted file mode 100644 index 614d4d3c..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/PKG-INFO +++ /dev/null @@ -1,383 +0,0 @@ -Metadata-Version: 2.1 -Name: Eel -Version: 0.14.0 -Summary: For little HTML GUI applications, with easy Python/JS interop -Home-page: https://github.com/samuelhwilliams/Eel -Author: Chris Knott -Author-email: chrisknott@hotmail.co.uk -License: UNKNOWN -Keywords: gui,html,javascript,electron -Platform: UNKNOWN -Classifier: Development Status :: 3 - Alpha -Classifier: Natural Language :: English -Classifier: Operating System :: MacOS -Classifier: Operating System :: POSIX -Classifier: Operating System :: Microsoft :: Windows :: Windows 10 -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 :: Implementation :: CPython -Classifier: License :: OSI Approved :: MIT License -Requires-Python: >=3.6 -Description-Content-Type: text/markdown -Provides-Extra: jinja2 - -# Eel - -[![PyPI version](https://img.shields.io/pypi/v/Eel?style=for-the-badge)](https://pypi.org/project/Eel/) -[![PyPi Downloads](https://img.shields.io/pypi/dm/Eel?style=for-the-badge)](https://pypistats.org/packages/eel) -![Python](https://img.shields.io/pypi/pyversions/Eel?style=for-the-badge) -[![License](https://img.shields.io/pypi/l/Eel.svg?style=for-the-badge)](https://pypi.org/project/Eel/) - - -[![Total alerts](https://img.shields.io/lgtm/alerts/g/samuelhwilliams/Eel.svg?logo=lgtm&style=for-the-badge)](https://lgtm.com/projects/g/samuelhwilliams/Eel/alerts/) -[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/samuelhwilliams/Eel.svg?logo=lgtm&style=for-the-badge)](https://lgtm.com/projects/g/samuelhwilliams/Eel/context:javascript) -[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/samuelhwilliams/Eel.svg?logo=lgtm&style=for-the-badge)](https://lgtm.com/projects/g/samuelhwilliams/Eel/context:python) - - -Eel is a little Python library for making simple Electron-like offline HTML/JS GUI apps, with full access to Python capabilities and libraries. - -> **Eel hosts a local webserver, then lets you annotate functions in Python so that they can be called from Javascript, and vice versa.** - -Eel is designed to take the hassle out of writing short and simple GUI applications. If you are familiar with Python and web development, probably just jump to [this example](https://github.com/ChrisKnott/Eel/tree/master/examples/04%20-%20file_access) which picks random file names out of the given folder (something that is impossible from a browser). - -

- - - -- [Eel](#eel) - - [Intro](#intro) - - [Install](#install) - - [Usage](#usage) - - [Directory Structure](#directory-structure) - - [Starting the app](#starting-the-app) - - [App options](#app-options) - - [Chrome/Chromium flags](#chromechromium-flags) - - [Exposing functions](#exposing-functions) - - [Eello, World!](#eello-world) - - [Return values](#return-values) - - [Callbacks](#callbacks) - - [Synchronous returns](#synchronous-returns) - - [Asynchronous Python](#asynchronous-python) - - [Building distributable binary with PyInstaller](#building-distributable-binary-with-pyinstaller) - - [Microsoft Edge](#microsoft-edge) - - - -## Intro - -There are several options for making GUI apps in Python, but if you want to use HTML/JS (in order to use jQueryUI or Bootstrap, for example) then you generally have to write a lot of boilerplate code to communicate from the Client (Javascript) side to the Server (Python) side. - -The closest Python equivalent to Electron (to my knowledge) is [cefpython](https://github.com/cztomczak/cefpython). It is a bit heavy weight for what I wanted. - -Eel is not as fully-fledged as Electron or cefpython - it is probably not suitable for making full blown applications like Atom - but it is very suitable for making the GUI equivalent of little utility scripts that you use internally in your team. - -For some reason many of the best-in-class number crunching and maths libraries are in Python (Tensorflow, Numpy, Scipy etc) but many of the best visualization libraries are in Javascript (D3, THREE.js etc). Hopefully Eel makes it easy to combine these into simple utility apps for assisting your development. - -Join Eel's users and maintainers on [Discord](https://discord.com/invite/3nqXPFX), if you like. - -## Install - -Install from pypi with `pip`: - -```shell -pip install eel -``` - -To include support for HTML templating, currently using [Jinja2](https://pypi.org/project/Jinja2/#description): - -```shell -pip install eel[jinja2] -``` - -## Usage - -### Directory Structure - -An Eel application will be split into a frontend consisting of various web-technology files (.html, .js, .css) and a backend consisting of various Python scripts. - -All the frontend files should be put in a single directory (they can be further divided into folders inside this if necessary). - -``` -my_python_script.py <-- Python scripts -other_python_module.py -static_web_folder/ <-- Web folder - main_page.html - css/ - style.css - img/ - logo.png -``` - -### Starting the app - -Suppose you put all the frontend files in a directory called `web`, including your start page `main.html`, then the app is started like this; - -```python -import eel -eel.init('web') -eel.start('main.html') -``` - -This will start a webserver on the default settings (http://localhost:8000) and open a browser to http://localhost:8000/main.html. - -If Chrome or Chromium is installed then by default it will open in that in App Mode (with the `--app` cmdline flag), regardless of what the OS's default browser is set to (it is possible to override this behaviour). - -### App options - -Additional options can be passed to `eel.start()` as keyword arguments. - -Some of the options include the mode the app is in (e.g. 'chrome'), the port the app runs on, the host name of the app, and adding additional command line flags. - -As of Eel v0.12.0, the following options are available to `start()`: - - **mode**, a string specifying what browser to use (e.g. `'chrome'`, `'electron'`, `'edge'`, `'custom'`). Can also be `None` or `False` to not open a window. *Default: `'chrome'`* - - **host**, a string specifying what hostname to use for the Bottle server. *Default: `'localhost'`)* - - **port**, an int specifying what port to use for the Bottle server. Use `0` for port to be picked automatically. *Default: `8000`*. - - **block**, a bool saying whether or not the call to `start()` should block the calling thread. *Default: `True`* - - **jinja_templates**, a string specifying a folder to use for Jinja2 templates, e.g. `my_templates`. *Default: `None`* - - **cmdline_args**, a list of strings to pass to the command to start the browser. For example, we might add extra flags for Chrome; ```eel.start('main.html', mode='chrome-app', port=8080, cmdline_args=['--start-fullscreen', '--browser-startup-dialog'])```. *Default: `[]`* - - **size**, a tuple of ints specifying the (width, height) of the main window in pixels *Default: `None`* - - **position**, a tuple of ints specifying the (left, top) of the main window in pixels *Default: `None`* - - **geometry**, a dictionary specifying the size and position for all windows. The keys should be the relative path of the page, and the values should be a dictionary of the form `{'size': (200, 100), 'position': (300, 50)}`. *Default: {}* - - **close_callback**, a lambda or function that is called when a websocket to a window closes (i.e. when the user closes the window). It should take two arguments; a string which is the relative path of the page that just closed, and a list of other websockets that are still open. *Default: `None`* - - **app**, an instance of Bottle which will be used rather than creating a fresh one. This can be used to install middleware on the - instance before starting eel, e.g. for session management, authentication, etc. - - - -### Exposing functions - -In addition to the files in the frontend folder, a Javascript library will be served at `/eel.js`. You should include this in any pages: - -```html - -``` - -Including this library creates an `eel` object which can be used to communicate with the Python side. - -Any functions in the Python code which are decorated with `@eel.expose` like this... - -```python -@eel.expose -def my_python_function(a, b): - print(a, b, a + b) -``` - -...will appear as methods on the `eel` object on the Javascript side, like this... - -```javascript -console.log("Calling Python..."); -eel.my_python_function(1, 2); // This calls the Python function that was decorated -``` - -Similarly, any Javascript functions which are exposed like this... - -```javascript -eel.expose(my_javascript_function); -function my_javascript_function(a, b, c, d) { - if (a < b) { - console.log(c * d); - } -} -``` - -can be called from the Python side like this... - -```python -print('Calling Javascript...') -eel.my_javascript_function(1, 2, 3, 4) # This calls the Javascript function -``` - -The exposed name can also be overridden by passing in a second argument. If your app minifies JavaScript during builds, this may be necessary to ensure that functions can be resolved on the Python side: - -```javascript -eel.expose(someFunction, "my_javascript_function"); -``` - -When passing complex objects as arguments, bear in mind that internally they are converted to JSON and sent down a websocket (a process that potentially loses information). - -### Eello, World! - -> See full example in: [examples/01 - hello_world](https://github.com/ChrisKnott/Eel/tree/master/examples/01%20-%20hello_world) - -Putting this together into a **Hello, World!** example, we have a short HTML page, `web/hello.html`: - -```html - - - - Hello, World! - - - - - - - - Hello, World! - - -``` - -and a short Python script `hello.py`: - -```python -import eel - -# Set web files folder and optionally specify which file types to check for eel.expose() -# *Default allowed_extensions are: ['.js', '.html', '.txt', '.htm', '.xhtml'] -eel.init('web', allowed_extensions=['.js', '.html']) - -@eel.expose # Expose this function to Javascript -def say_hello_py(x): - print('Hello from %s' % x) - -say_hello_py('Python World!') -eel.say_hello_js('Python World!') # Call a Javascript function - -eel.start('hello.html') # Start (this blocks and enters loop) -``` - -If we run the Python script (`python hello.py`), then a browser window will open displaying `hello.html`, and we will see... - -``` -Hello from Python World! -Hello from Javascript World! -``` - -...in the terminal, and... - -``` -Hello from Javascript World! -Hello from Python World! -``` - -...in the browser console (press F12 to open). - -You will notice that in the Python code, the Javascript function is called before the browser window is even started - any early calls like this are queued up and then sent once the websocket has been established. - -### Return values - -While we want to think of our code as comprising a single application, the Python interpreter and the browser window run in separate processes. This can make communicating back and forth between them a bit of a mess, especially if we always had to explicitly _send_ values from one side to the other. - -Eel supports two ways of retrieving _return values_ from the other side of the app, which helps keep the code concise. - -To prevent hanging forever on the Python side, a timeout has been put in place for trying to retrieve values from -the JavaScript side, which defaults to 10000 milliseconds (10 seconds). This can be changed with the `_js_result_timeout` parameter to `eel.init`. There is no corresponding timeout on the JavaScript side. - -#### Callbacks - -When you call an exposed function, you can immediately pass a callback function afterwards. This callback will automatically be called asynchrounously with the return value when the function has finished executing on the other side. - -For example, if we have the following function defined and exposed in Javascript: - -```javascript -eel.expose(js_random); -function js_random() { - return Math.random(); -} -``` - -Then in Python we can retrieve random values from the Javascript side like so: - -```python -def print_num(n): - print('Got this from Javascript:', n) - -# Call Javascript function, and pass explicit callback function -eel.js_random()(print_num) - -# Do the same with an inline lambda as callback -eel.js_random()(lambda n: print('Got this from Javascript:', n)) -``` - -(It works exactly the same the other way around). - -#### Synchronous returns - -In most situations, the calls to the other side are to quickly retrieve some piece of data, such as the state of a widget or contents of an input field. In these cases it is more convenient to just synchronously wait a few milliseconds then continue with your code, rather than breaking the whole thing up into callbacks. - -To synchronously retrieve the return value, simply pass nothing to the second set of brackets. So in Python we would write: - -```python -n = eel.js_random()() # This immediately returns the value -print('Got this from Javascript:', n) -``` - -You can only perform synchronous returns after the browser window has started (after calling `eel.start()`), otherwise obviously the call with hang. - -In Javascript, the language doesn't allow us to block while we wait for a callback, except by using `await` from inside an `async` function. So the equivalent code from the Javascript side would be: - -```javascript -async function run() { - // Inside a function marked 'async' we can use the 'await' keyword. - - let n = await eel.py_random()(); // Must prefix call with 'await', otherwise it's the same syntax - console.log("Got this from Python: " + n); -} - -run(); -``` - -## Asynchronous Python - -Eel is built on Bottle and Gevent, which provide an asynchronous event loop similar to Javascript. A lot of Python's standard library implicitly assumes there is a single execution thread - to deal with this, Gevent can "[monkey patch](https://en.wikipedia.org/wiki/Monkey_patch)" many of the standard modules such as `time`. ~~This monkey patching is done automatically when you call `import eel`~~. If you need monkey patching you should `import gevent.monkey` and call `gevent.monkey.patch_all()` _before_ you `import eel`. Monkey patching can interfere with things like debuggers so should be avoided unless necessary. - -For most cases you should be fine by avoiding using `time.sleep()` and instead using the versions provided by `gevent`. For convenience, the two most commonly needed gevent methods, `sleep()` and `spawn()` are provided directly from Eel (to save importing `time` and/or `gevent` as well). - -In this example... - -```python -import eel -eel.init('web') - -def my_other_thread(): - while True: - print("I'm a thread") - eel.sleep(1.0) # Use eel.sleep(), not time.sleep() - -eel.spawn(my_other_thread) - -eel.start('main.html', block=False) # Don't block on this call - -while True: - print("I'm a main loop") - eel.sleep(1.0) # Use eel.sleep(), not time.sleep() -``` - -...we would then have three "threads" (greenlets) running; - -1. Eel's internal thread for serving the web folder -2. The `my_other_thread` method, repeatedly printing **"I'm a thread"** -3. The main Python thread, which would be stuck in the final `while` loop, repeatedly printing **"I'm a main loop"** - -## Building distributable binary with PyInstaller - -If you want to package your app into a program that can be run on a computer without a Python interpreter installed, you should use **PyInstaller**. - -1. Configure a virtualenv with desired Python version and minimum necessary Python packages -2. Install PyInstaller `pip install PyInstaller` -3. In your app's folder, run `python -m eel [your_main_script] [your_web_folder]` (for example, you might run `python -m eel hello.py web`) -4. This will create a new folder `dist/` -5. Valid PyInstaller flags can be passed through, such as excluding modules with the flag: `--exclude module_name`. For example, you might run `python -m eel file_access.py web --exclude win32com --exclude numpy --exclude cryptography` -6. When happy that your app is working correctly, add `--onefile --noconsole` flags to build a single executable file - -Consult the [documentation for PyInstaller](http://PyInstaller.readthedocs.io/en/stable/) for more options. - -## Microsoft Edge - -For Windows 10 users, Microsoft Edge (`eel.start(.., mode='edge')`) is installed by default and a useful fallback if a preferred browser is not installed. See the examples: - -- A Hello World example using Microsoft Edge: [examples/01 - hello_world-Edge/](https://github.com/ChrisKnott/Eel/tree/master/examples/01%20-%20hello_world-Edge) -- Example implementing browser-fallbacks: [examples/07 - CreateReactApp/eel_CRA.py](https://github.com/ChrisKnott/Eel/tree/master/examples/07%20-%20CreateReactApp/eel_CRA.py) - - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/SOURCES.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/SOURCES.txt deleted file mode 100644 index dd390d1c..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/SOURCES.txt +++ /dev/null @@ -1,16 +0,0 @@ -MANIFEST.in -README.md -setup.cfg -setup.py -Eel.egg-info/PKG-INFO -Eel.egg-info/SOURCES.txt -Eel.egg-info/dependency_links.txt -Eel.egg-info/requires.txt -Eel.egg-info/top_level.txt -eel/__init__.py -eel/__main__.py -eel/browsers.py -eel/chrome.py -eel/edge.py -eel/eel.js -eel/electron.py \ No newline at end of file diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/dependency_links.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/installed-files.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/installed-files.txt deleted file mode 100644 index 7a85ed3b..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/installed-files.txt +++ /dev/null @@ -1,18 +0,0 @@ -../eel/__init__.py -../eel/__main__.py -../eel/__pycache__/__init__.cpython-39.pyc -../eel/__pycache__/__main__.cpython-39.pyc -../eel/__pycache__/browsers.cpython-39.pyc -../eel/__pycache__/chrome.cpython-39.pyc -../eel/__pycache__/edge.cpython-39.pyc -../eel/__pycache__/electron.cpython-39.pyc -../eel/browsers.py -../eel/chrome.py -../eel/edge.py -../eel/eel.js -../eel/electron.py -PKG-INFO -SOURCES.txt -dependency_links.txt -requires.txt -top_level.txt diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/requires.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/requires.txt deleted file mode 100644 index 20dd3e6c..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/requires.txt +++ /dev/null @@ -1,8 +0,0 @@ -bottle -bottle-websocket -future -pyparsing -whichcraft - -[jinja2] -jinja2>=2.10 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/top_level.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/top_level.txt deleted file mode 100644 index debd42be..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/Eel-0.14.0-py3.9.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -eel diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/__pycache__/bottle.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/__pycache__/bottle.cpython-39.pyc deleted file mode 100644 index 4110c3b3ea0c50eb3938c42bfdd7061bc8b6113b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145425 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 OB=3Nh*T=R0`u_!6^N(Qw diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/__pycache__/pyparsing.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/__pycache__/pyparsing.cpython-39.pyc deleted file mode 100644 index 3d3c6cd9bd72f4d2e3900df27e245d1f01aa83f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 240403 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/_distutils_hack/__pycache__/override.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/_distutils_hack/__pycache__/override.cpython-39.pyc deleted file mode 100644 index 08ac01ad7efbaf0ebe841efb269e1599916028c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 247 zcmYe~<>g`kf^+AK6Lo;}V-N=!FabFZKwK;UBvKht7@8RuFfL?ZWJqBQX3%7c;*U?s zEG{W6$;>H^&qz$p_S0m##T6f)nOl%wR1zN_#hsELpI??*RFs*Lx{{#?WE`0Im8qYb znv$8Q?~z!P;!>1ZmZ~36nNgCTryrc0k)NNV@9FL87+;)Rlvt2jq_0<&npdWulbNJn zP+5|ZpJ%LRsb8E~lB!z(v@$V0wOAj`8Tv3g^$IF)aoFVMr3.0.0 -Description-Content-Type: text/markdown -Requires-Dist: soupsieve (>1.2) -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 - -Beautiful Soup's support for Python 2 was discontinued on December 31, -2020: one year after the sunset date for Python 2 itself. From this -point onward, new Beautiful Soup development will exclusively target -Python 3. The final release of Beautiful Soup 4 to support Python 2 -was 4.9.3. - -# 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 -``` - -``` -$ python3 -m unittest discover -s bs4 -``` - - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/RECORD b/IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/RECORD deleted file mode 100644 index 0c7602d7..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/RECORD +++ /dev/null @@ -1,28 +0,0 @@ -beautifulsoup4-4.10.0.dist-info/AUTHORS,sha256=uSIdbrBb1sobdXl7VrlUvuvim2dN9kF3MH4Edn0WKGE,2176 -beautifulsoup4-4.10.0.dist-info/COPYING.txt,sha256=pH6lEjYJhGT-C09Vl0NZC1MwVtngD0nsv4Apn6tH4jE,1315 -beautifulsoup4-4.10.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -beautifulsoup4-4.10.0.dist-info/LICENSE,sha256=ynIn3bnu1syAnhV_Z7Ag543eBjJAAB0RhW-FxJy25CM,1447 -beautifulsoup4-4.10.0.dist-info/METADATA,sha256=xXGta_JNOdH5pvsMsrB1-MPPjMDhfi5q22-8r-iTMkg,3542 -beautifulsoup4-4.10.0.dist-info/RECORD,, -beautifulsoup4-4.10.0.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92 -beautifulsoup4-4.10.0.dist-info/top_level.txt,sha256=H8VT-IuPWLzQqwG9_eChjXDJ1z0H9RRebdSR90Bjnkw,4 -bs4/__init__.py,sha256=kZ9EDFbdsNtqNkUL95_7epbnZCO7HKgW-s2ukdAXXY0,32673 -bs4/__pycache__/__init__.cpython-39.pyc,, -bs4/__pycache__/dammit.cpython-39.pyc,, -bs4/__pycache__/diagnose.cpython-39.pyc,, -bs4/__pycache__/element.cpython-39.pyc,, -bs4/__pycache__/formatter.cpython-39.pyc,, -bs4/__pycache__/testing.cpython-39.pyc,, -bs4/builder/__init__.py,sha256=FP2SbcvOUZWk8a84wc9-K90F2YA2--nQpxm-GK0G8Gc,19870 -bs4/builder/__pycache__/__init__.cpython-39.pyc,, -bs4/builder/__pycache__/_html5lib.cpython-39.pyc,, -bs4/builder/__pycache__/_htmlparser.cpython-39.pyc,, -bs4/builder/__pycache__/_lxml.cpython-39.pyc,, -bs4/builder/_html5lib.py,sha256=hDxlzVrAku_eU7zEt4gZ-sAXzG58GvkLfMz6P4zUqoA,18748 -bs4/builder/_htmlparser.py,sha256=KjnSpA8C8-_yX4nFd_UwCB4SGO9pzqR05WjjbuZjzH4,18933 -bs4/builder/_lxml.py,sha256=h20vsAgSkeiIPiCKwJ-ggajeaxra7bicCtQSOoinUTc,12699 -bs4/dammit.py,sha256=lMWxYrl9VeLqEgXvk4MJK8isKPgNF_yQXk8B9R4UxXE,98709 -bs4/diagnose.py,sha256=WOzytCTkvqh_fGhqYlMyaYVjtH50w4jbdf1Fd0iundE,7755 -bs4/element.py,sha256=noCV6m4euxWVo3I1Bjh9SYDgG_VNu7oGoWLB0XlCldc,85238 -bs4/formatter.py,sha256=wAuETtbENr2HP-ZiR6WRu4_bLFY8ALRV_BPqWhhr7HA,6385 -bs4/testing.py,sha256=6leLHpE7mHFV7W8SqR3vRvsgEVR3eqxprJVIf8W_bgY,47412 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/WHEEL b/IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/WHEEL deleted file mode 100644 index b552003f..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.34.2) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/top_level.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/top_level.txt deleted file mode 100644 index 13154420..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/beautifulsoup4-4.10.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -bs4 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/AUTHORS b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/AUTHORS deleted file mode 100644 index 1b7869d1..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/AUTHORS +++ /dev/null @@ -1,64 +0,0 @@ -Bottle is written and maintained by Marcel Hellkamp . - -Thanks to all the people who found bugs, sent patches, spread the word, helped each other on the mailing-list and made this project possible. I hope the following (alphabetically sorted) list is complete. If you miss your name on that list (or want your name removed) please :doc:`tell me ` or add it yourself. - -* acasajus -* Adam R. Smith -* Alexey Borzenkov -* Alexis Daboville -* Anton I. Sipos -* Anton Kolechkin -* apexi200sx -* apheage -* BillMa -* Brad Greenlee -* Brandon Gilmore -* Branko Vukelic -* Brian Sierakowski -* Brian Wickman -* Carl Scharenberg -* Damien Degois -* David Buxton -* Duane Johnson -* fcamel -* Frank Murphy -* Frederic Junod -* goldfaber3012 -* Greg Milby -* gstein -* Ian Davis -* Itamar Nabriski -* Iuri de Silvio -* Jaimie Murdock -* Jeff Nichols -* Jeremy Kelley -* joegester -* Johannes Krampf -* Jonas Haag -* Joshua Roesslein -* Karl -* Kevin Zuber -* Kraken -* Kyle Fritz -* m35 -* Marcos Neves -* masklinn -* Michael Labbe -* Michael Soulier -* `reddit `_ -* Nicolas Vanhoren -* Robert Rollins -* rogererens -* rwxrwx -* Santiago Gala -* Sean M. Collins -* Sebastian Wollrath -* Seth -* Sigurd Høgsbro -* Stuart Rackham -* Sun Ning -* Tomás A. Schertel -* Tristan Zajonc -* voltron -* Wieland Hoffmann -* zombat diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/INSTALLER b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/LICENSE b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/LICENSE deleted file mode 100644 index cdd0c706..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2012, Marcel Hellkamp. - -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/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/METADATA b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/METADATA deleted file mode 100644 index 5ce4b240..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/METADATA +++ /dev/null @@ -1,43 +0,0 @@ -Metadata-Version: 2.1 -Name: bottle -Version: 0.12.19 -Summary: Fast and simple WSGI-framework for small web-applications. -Home-page: http://bottlepy.org/ -Author: Marcel Hellkamp -Author-email: marc@gsites.de -License: MIT -Platform: any -Classifier: Development Status :: 4 - Beta -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries -Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers -Classifier: Topic :: Internet :: WWW/HTTP :: WSGI -Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application -Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware -Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Server -Classifier: Topic :: Software Development :: Libraries :: Application Frameworks -Classifier: Programming Language :: Python :: 2.5 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.2 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 - - -Bottle is a fast and simple micro-framework for small web applications. It -offers request dispatching (Routes) with url parameter support, templates, -a built-in HTTP Server and adapters for many third party WSGI/HTTP-server and -template engines - all in a single file and with no dependencies other than the -Python Standard Library. - -Homepage and documentation: http://bottlepy.org/ - -Copyright (c) 2016, Marcel Hellkamp. -License: MIT (see LICENSE for details) - - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/RECORD b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/RECORD deleted file mode 100644 index 92721286..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/RECORD +++ /dev/null @@ -1,11 +0,0 @@ -../../../bin/__pycache__/bottle.cpython-39.pyc,, -../../../bin/bottle.py,sha256=FeDaVhjUfbcKX1ewPnTkMF9P32HxbHeJOTjA5_8RKmk,150552 -__pycache__/bottle.cpython-39.pyc,, -bottle-0.12.19.dist-info/AUTHORS,sha256=A0Y_uWygTzQczXdwcMI8h6XqqWns2pGsJnZOGwu_IPo,1308 -bottle-0.12.19.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -bottle-0.12.19.dist-info/LICENSE,sha256=0OchHxw8GhxW850YvLB_J_SAyKlVJhd1bdo6M1kzuKY,1061 -bottle-0.12.19.dist-info/METADATA,sha256=Rd2Q9BoIBEeq2dr-HFELfJbfF9hBsXVUL2bjouuCcUA,1794 -bottle-0.12.19.dist-info/RECORD,, -bottle-0.12.19.dist-info/WHEEL,sha256=EVRjI69F5qVjm_YgqcTXPnTAv3BfSUr0WVAHuSP3Xoo,92 -bottle-0.12.19.dist-info/top_level.txt,sha256=cK8mpC1WUvVJAVL1XsjCoCGkD-0Yc-pcrqfH0fRXkhg,7 -bottle.py,sha256=BIMSOqP40amv6LWoAPNRU6sY8SFRCuhRqayJ1grqQjs,150565 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/WHEEL b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/WHEEL deleted file mode 100644 index 83ff02e9..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.35.1) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/top_level.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/top_level.txt deleted file mode 100644 index 310dc0bd..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle-0.12.19.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -bottle diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle.py deleted file mode 100644 index 9806efd0..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle.py +++ /dev/null @@ -1,3771 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Bottle is a fast and simple micro-framework for small web applications. It -offers request dispatching (Routes) with url parameter support, templates, -a built-in HTTP Server and adapters for many third party WSGI/HTTP-server and -template engines - all in a single file and with no dependencies other than the -Python Standard Library. - -Homepage and documentation: http://bottlepy.org/ - -Copyright (c) 2016, Marcel Hellkamp. -License: MIT (see LICENSE for details) -""" - -from __future__ import with_statement - -__author__ = 'Marcel Hellkamp' -__version__ = '0.12.19' -__license__ = 'MIT' - -# The gevent server adapter needs to patch some modules before they are imported -# This is why we parse the commandline parameters here but handle them later -if __name__ == '__main__': - from optparse import OptionParser - _cmd_parser = OptionParser(usage="usage: %prog [options] package.module:app") - _opt = _cmd_parser.add_option - _opt("--version", action="store_true", help="show version number.") - _opt("-b", "--bind", metavar="ADDRESS", help="bind socket to ADDRESS.") - _opt("-s", "--server", default='wsgiref', help="use SERVER as backend.") - _opt("-p", "--plugin", action="append", help="install additional plugin/s.") - _opt("--debug", action="store_true", help="start server in debug mode.") - _opt("--reload", action="store_true", help="auto-reload on file changes.") - _cmd_options, _cmd_args = _cmd_parser.parse_args() - if _cmd_options.server and _cmd_options.server.startswith('gevent'): - import gevent.monkey; gevent.monkey.patch_all() - -import base64, cgi, email.utils, functools, hmac, itertools, mimetypes,\ - os, re, subprocess, sys, tempfile, threading, time, warnings, hashlib - -from datetime import date as datedate, datetime, timedelta -from tempfile import TemporaryFile -from traceback import format_exc, print_exc -from inspect import getargspec -from unicodedata import normalize - - -try: from simplejson import dumps as json_dumps, loads as json_lds -except ImportError: # pragma: no cover - try: from json import dumps as json_dumps, loads as json_lds - except ImportError: - try: from django.utils.simplejson import dumps as json_dumps, loads as json_lds - except ImportError: - def json_dumps(data): - raise ImportError("JSON support requires Python 2.6 or simplejson.") - json_lds = json_dumps - - - -# We now try to fix 2.5/2.6/3.1/3.2 incompatibilities. -# It ain't pretty but it works... Sorry for the mess. - -py = sys.version_info -py3k = py >= (3, 0, 0) -py25 = py < (2, 6, 0) -py31 = (3, 1, 0) <= py < (3, 2, 0) - -# Workaround for the missing "as" keyword in py3k. -def _e(): return sys.exc_info()[1] - -# Workaround for the "print is a keyword/function" Python 2/3 dilemma -# and a fallback for mod_wsgi (resticts stdout/err attribute access) -try: - _stdout, _stderr = sys.stdout.write, sys.stderr.write -except IOError: - _stdout = lambda x: sys.stdout.write(x) - _stderr = lambda x: sys.stderr.write(x) - -# Lots of stdlib and builtin differences. -if py3k: - import http.client as httplib - import _thread as thread - from urllib.parse import urljoin, SplitResult as UrlSplitResult - from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote - urlunquote = functools.partial(urlunquote, encoding='latin1') - from http.cookies import SimpleCookie - if py >= (3, 3, 0): - from collections.abc import MutableMapping as DictMixin - from types import ModuleType as new_module - else: - from collections import MutableMapping as DictMixin - from imp import new_module - import pickle - from io import BytesIO - from configparser import ConfigParser - basestring = str - unicode = str - json_loads = lambda s: json_lds(touni(s)) - callable = lambda x: hasattr(x, '__call__') - imap = map - def _raise(*a): raise a[0](a[1]).with_traceback(a[2]) -else: # 2.x - import httplib - import thread - from urlparse import urljoin, SplitResult as UrlSplitResult - from urllib import urlencode, quote as urlquote, unquote as urlunquote - from Cookie import SimpleCookie - from itertools import imap - import cPickle as pickle - from imp import new_module - from StringIO import StringIO as BytesIO - from ConfigParser import SafeConfigParser as ConfigParser - if py25: - msg = "Python 2.5 support may be dropped in future versions of Bottle." - warnings.warn(msg, DeprecationWarning) - from UserDict import DictMixin - def next(it): return it.next() - bytes = str - else: # 2.6, 2.7 - from collections import MutableMapping as DictMixin - unicode = unicode - json_loads = json_lds - eval(compile('def _raise(*a): raise a[0], a[1], a[2]', '', 'exec')) - -# Some helpers for string/byte handling -def tob(s, enc='utf8'): - return s.encode(enc) if isinstance(s, unicode) else bytes(s) -def touni(s, enc='utf8', err='strict'): - return s.decode(enc, err) if isinstance(s, bytes) else unicode(s) -tonat = touni if py3k else tob - -# 3.2 fixes cgi.FieldStorage to accept bytes (which makes a lot of sense). -# 3.1 needs a workaround. -if py31: - from io import TextIOWrapper - class NCTextIOWrapper(TextIOWrapper): - def close(self): pass # Keep wrapped buffer open. - - -# A bug in functools causes it to break if the wrapper is an instance method -def update_wrapper(wrapper, wrapped, *a, **ka): - try: functools.update_wrapper(wrapper, wrapped, *a, **ka) - except AttributeError: pass - - - -# These helpers are used at module level and need to be defined first. -# And yes, I know PEP-8, but sometimes a lower-case classname makes more sense. - -def depr(message, hard=False): - warnings.warn(message, DeprecationWarning, stacklevel=3) - -def makelist(data): # This is just to handy - if isinstance(data, (tuple, list, set, dict)): return list(data) - elif data: return [data] - else: return [] - - -class DictProperty(object): - ''' Property that maps to a key in a local dict-like attribute. ''' - def __init__(self, attr, key=None, read_only=False): - self.attr, self.key, self.read_only = attr, key, read_only - - def __call__(self, func): - functools.update_wrapper(self, func, updated=[]) - self.getter, self.key = func, self.key or func.__name__ - return self - - def __get__(self, obj, cls): - if obj is None: return self - key, storage = self.key, getattr(obj, self.attr) - if key not in storage: storage[key] = self.getter(obj) - return storage[key] - - def __set__(self, obj, value): - if self.read_only: raise AttributeError("Read-Only property.") - getattr(obj, self.attr)[self.key] = value - - def __delete__(self, obj): - if self.read_only: raise AttributeError("Read-Only property.") - del getattr(obj, self.attr)[self.key] - - -class cached_property(object): - ''' A property that is only computed once per instance and then replaces - itself with an ordinary attribute. Deleting the attribute resets the - property. ''' - - def __init__(self, func): - self.__doc__ = getattr(func, '__doc__') - self.func = func - - def __get__(self, obj, cls): - if obj is None: return self - value = obj.__dict__[self.func.__name__] = self.func(obj) - return value - - -class lazy_attribute(object): - ''' A property that caches itself to the class object. ''' - def __init__(self, func): - functools.update_wrapper(self, func, updated=[]) - self.getter = func - - def __get__(self, obj, cls): - value = self.getter(cls) - setattr(cls, self.__name__, value) - return value - - - - - - -############################################################################### -# Exceptions and Events ######################################################## -############################################################################### - - -class BottleException(Exception): - """ A base class for exceptions used by bottle. """ - pass - - - - - - -############################################################################### -# Routing ###################################################################### -############################################################################### - - -class RouteError(BottleException): - """ This is a base class for all routing related exceptions """ - - -class RouteReset(BottleException): - """ If raised by a plugin or request handler, the route is reset and all - plugins are re-applied. """ - -class RouterUnknownModeError(RouteError): pass - - -class RouteSyntaxError(RouteError): - """ The route parser found something not supported by this router. """ - - -class RouteBuildError(RouteError): - """ The route could not be built. """ - - -def _re_flatten(p): - ''' Turn all capturing groups in a regular expression pattern into - non-capturing groups. ''' - if '(' not in p: return p - return re.sub(r'(\\*)(\(\?P<[^>]+>|\((?!\?))', - lambda m: m.group(0) if len(m.group(1)) % 2 else m.group(1) + '(?:', p) - - -class Router(object): - ''' A Router is an ordered collection of route->target pairs. It is used to - efficiently match WSGI requests against a number of routes and return - the first target that satisfies the request. The target may be anything, - usually a string, ID or callable object. A route consists of a path-rule - and a HTTP method. - - The path-rule is either a static path (e.g. `/contact`) or a dynamic - path that contains wildcards (e.g. `/wiki/`). The wildcard syntax - and details on the matching order are described in docs:`routing`. - ''' - - default_pattern = '[^/]+' - default_filter = 're' - - #: The current CPython regexp implementation does not allow more - #: than 99 matching groups per regular expression. - _MAX_GROUPS_PER_PATTERN = 99 - - def __init__(self, strict=False): - self.rules = [] # All rules in order - self._groups = {} # index of regexes to find them in dyna_routes - self.builder = {} # Data structure for the url builder - self.static = {} # Search structure for static routes - self.dyna_routes = {} - self.dyna_regexes = {} # Search structure for dynamic routes - #: If true, static routes are no longer checked first. - self.strict_order = strict - self.filters = { - 're': lambda conf: - (_re_flatten(conf or self.default_pattern), None, None), - 'int': lambda conf: (r'-?\d+', int, lambda x: str(int(x))), - 'float': lambda conf: (r'-?[\d.]+', float, lambda x: str(float(x))), - 'path': lambda conf: (r'.+?', None, None)} - - def add_filter(self, name, func): - ''' Add a filter. The provided function is called with the configuration - string as parameter and must return a (regexp, to_python, to_url) tuple. - The first element is a string, the last two are callables or None. ''' - self.filters[name] = func - - rule_syntax = re.compile('(\\\\*)'\ - '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)'\ - '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)'\ - '(?::((?:\\\\.|[^\\\\>]+)+)?)?)?>))') - - def _itertokens(self, rule): - offset, prefix = 0, '' - for match in self.rule_syntax.finditer(rule): - prefix += rule[offset:match.start()] - g = match.groups() - if len(g[0])%2: # Escaped wildcard - prefix += match.group(0)[len(g[0]):] - offset = match.end() - continue - if prefix: - yield prefix, None, None - name, filtr, conf = g[4:7] if g[2] is None else g[1:4] - yield name, filtr or 'default', conf or None - offset, prefix = match.end(), '' - if offset <= len(rule) or prefix: - yield prefix+rule[offset:], None, None - - def add(self, rule, method, target, name=None): - ''' Add a new rule or replace the target for an existing rule. ''' - anons = 0 # Number of anonymous wildcards found - keys = [] # Names of keys - pattern = '' # Regular expression pattern with named groups - filters = [] # Lists of wildcard input filters - builder = [] # Data structure for the URL builder - is_static = True - - for key, mode, conf in self._itertokens(rule): - if mode: - is_static = False - if mode == 'default': mode = self.default_filter - mask, in_filter, out_filter = self.filters[mode](conf) - if not key: - pattern += '(?:%s)' % mask - key = 'anon%d' % anons - anons += 1 - else: - pattern += '(?P<%s>%s)' % (key, mask) - keys.append(key) - if in_filter: filters.append((key, in_filter)) - builder.append((key, out_filter or str)) - elif key: - pattern += re.escape(key) - builder.append((None, key)) - - self.builder[rule] = builder - if name: self.builder[name] = builder - - if is_static and not self.strict_order: - self.static.setdefault(method, {}) - self.static[method][self.build(rule)] = (target, None) - return - - try: - re_pattern = re.compile('^(%s)$' % pattern) - re_match = re_pattern.match - except re.error: - raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, _e())) - - if filters: - def getargs(path): - url_args = re_match(path).groupdict() - for name, wildcard_filter in filters: - try: - url_args[name] = wildcard_filter(url_args[name]) - except ValueError: - raise HTTPError(400, 'Path has wrong format.') - return url_args - elif re_pattern.groupindex: - def getargs(path): - return re_match(path).groupdict() - else: - getargs = None - - flatpat = _re_flatten(pattern) - whole_rule = (rule, flatpat, target, getargs) - - if (flatpat, method) in self._groups: - if DEBUG: - msg = 'Route <%s %s> overwrites a previously defined route' - warnings.warn(msg % (method, rule), RuntimeWarning) - self.dyna_routes[method][self._groups[flatpat, method]] = whole_rule - else: - self.dyna_routes.setdefault(method, []).append(whole_rule) - self._groups[flatpat, method] = len(self.dyna_routes[method]) - 1 - - self._compile(method) - - def _compile(self, method): - all_rules = self.dyna_routes[method] - comborules = self.dyna_regexes[method] = [] - maxgroups = self._MAX_GROUPS_PER_PATTERN - for x in range(0, len(all_rules), maxgroups): - some = all_rules[x:x+maxgroups] - combined = (flatpat for (_, flatpat, _, _) in some) - combined = '|'.join('(^%s$)' % flatpat for flatpat in combined) - combined = re.compile(combined).match - rules = [(target, getargs) for (_, _, target, getargs) in some] - comborules.append((combined, rules)) - - def build(self, _name, *anons, **query): - ''' Build an URL by filling the wildcards in a rule. ''' - builder = self.builder.get(_name) - if not builder: raise RouteBuildError("No route with that name.", _name) - try: - for i, value in enumerate(anons): query['anon%d'%i] = value - url = ''.join([f(query.pop(n)) if n else f for (n,f) in builder]) - return url if not query else url+'?'+urlencode(query) - except KeyError: - raise RouteBuildError('Missing URL argument: %r' % _e().args[0]) - - def match(self, environ): - ''' Return a (target, url_agrs) tuple or raise HTTPError(400/404/405). ''' - verb = environ['REQUEST_METHOD'].upper() - path = environ['PATH_INFO'] or '/' - target = None - if verb == 'HEAD': - methods = ['PROXY', verb, 'GET', 'ANY'] - else: - methods = ['PROXY', verb, 'ANY'] - - for method in methods: - if method in self.static and path in self.static[method]: - target, getargs = self.static[method][path] - return target, getargs(path) if getargs else {} - elif method in self.dyna_regexes: - for combined, rules in self.dyna_regexes[method]: - match = combined(path) - if match: - target, getargs = rules[match.lastindex - 1] - return target, getargs(path) if getargs else {} - - # No matching route found. Collect alternative methods for 405 response - allowed = set([]) - nocheck = set(methods) - for method in set(self.static) - nocheck: - if path in self.static[method]: - allowed.add(method) - for method in set(self.dyna_regexes) - allowed - nocheck: - for combined, rules in self.dyna_regexes[method]: - match = combined(path) - if match: - allowed.add(method) - if allowed: - allow_header = ",".join(sorted(allowed)) - raise HTTPError(405, "Method not allowed.", Allow=allow_header) - - # No matching route and no alternative method found. We give up - raise HTTPError(404, "Not found: " + repr(path)) - - - - - - -class Route(object): - ''' This class wraps a route callback along with route specific metadata and - configuration and applies Plugins on demand. It is also responsible for - turing an URL path rule into a regular expression usable by the Router. - ''' - - def __init__(self, app, rule, method, callback, name=None, - plugins=None, skiplist=None, **config): - #: The application this route is installed to. - self.app = app - #: The path-rule string (e.g. ``/wiki/:page``). - self.rule = rule - #: The HTTP method as a string (e.g. ``GET``). - self.method = method - #: The original callback with no plugins applied. Useful for introspection. - self.callback = callback - #: The name of the route (if specified) or ``None``. - self.name = name or None - #: A list of route-specific plugins (see :meth:`Bottle.route`). - self.plugins = plugins or [] - #: A list of plugins to not apply to this route (see :meth:`Bottle.route`). - self.skiplist = skiplist or [] - #: Additional keyword arguments passed to the :meth:`Bottle.route` - #: decorator are stored in this dictionary. Used for route-specific - #: plugin configuration and meta-data. - self.config = ConfigDict().load_dict(config, make_namespaces=True) - - def __call__(self, *a, **ka): - depr("Some APIs changed to return Route() instances instead of"\ - " callables. Make sure to use the Route.call method and not to"\ - " call Route instances directly.") #0.12 - return self.call(*a, **ka) - - @cached_property - def call(self): - ''' The route callback with all plugins applied. This property is - created on demand and then cached to speed up subsequent requests.''' - return self._make_callback() - - def reset(self): - ''' Forget any cached values. The next time :attr:`call` is accessed, - all plugins are re-applied. ''' - self.__dict__.pop('call', None) - - def prepare(self): - ''' Do all on-demand work immediately (useful for debugging).''' - self.call - - @property - def _context(self): - depr('Switch to Plugin API v2 and access the Route object directly.') #0.12 - return dict(rule=self.rule, method=self.method, callback=self.callback, - name=self.name, app=self.app, config=self.config, - apply=self.plugins, skip=self.skiplist) - - def all_plugins(self): - ''' Yield all Plugins affecting this route. ''' - unique = set() - for p in reversed(self.app.plugins + self.plugins): - if True in self.skiplist: break - name = getattr(p, 'name', False) - if name and (name in self.skiplist or name in unique): continue - if p in self.skiplist or type(p) in self.skiplist: continue - if name: unique.add(name) - yield p - - def _make_callback(self): - callback = self.callback - for plugin in self.all_plugins(): - try: - if hasattr(plugin, 'apply'): - api = getattr(plugin, 'api', 1) - context = self if api > 1 else self._context - callback = plugin.apply(callback, context) - else: - callback = plugin(callback) - except RouteReset: # Try again with changed configuration. - return self._make_callback() - if not callback is self.callback: - update_wrapper(callback, self.callback) - return callback - - def get_undecorated_callback(self): - ''' Return the callback. If the callback is a decorated function, try to - recover the original function. ''' - func = self.callback - func = getattr(func, '__func__' if py3k else 'im_func', func) - closure_attr = '__closure__' if py3k else 'func_closure' - while hasattr(func, closure_attr) and getattr(func, closure_attr): - func = getattr(func, closure_attr)[0].cell_contents - return func - - def get_callback_args(self): - ''' Return a list of argument names the callback (most likely) accepts - as keyword arguments. If the callback is a decorated function, try - to recover the original function before inspection. ''' - return getargspec(self.get_undecorated_callback())[0] - - def get_config(self, key, default=None): - ''' Lookup a config field and return its value, first checking the - route.config, then route.app.config.''' - for conf in (self.config, self.app.conifg): - if key in conf: return conf[key] - return default - - def __repr__(self): - cb = self.get_undecorated_callback() - return '<%s %r %r>' % (self.method, self.rule, cb) - - - - - - -############################################################################### -# Application Object ########################################################### -############################################################################### - - -class Bottle(object): - """ Each Bottle object represents a single, distinct web application and - consists of routes, callbacks, plugins, resources and configuration. - Instances are callable WSGI applications. - - :param catchall: If true (default), handle all exceptions. Turn off to - let debugging middleware handle exceptions. - """ - - def __init__(self, catchall=True, autojson=True): - - #: A :class:`ConfigDict` for app specific configuration. - self.config = ConfigDict() - self.config._on_change = functools.partial(self.trigger_hook, 'config') - self.config.meta_set('autojson', 'validate', bool) - self.config.meta_set('catchall', 'validate', bool) - self.config['catchall'] = catchall - self.config['autojson'] = autojson - - #: A :class:`ResourceManager` for application files - self.resources = ResourceManager() - - self.routes = [] # List of installed :class:`Route` instances. - self.router = Router() # Maps requests to :class:`Route` instances. - self.error_handler = {} - - # Core plugins - self.plugins = [] # List of installed plugins. - if self.config['autojson']: - self.install(JSONPlugin()) - self.install(TemplatePlugin()) - - #: If true, most exceptions are caught and returned as :exc:`HTTPError` - catchall = DictProperty('config', 'catchall') - - __hook_names = 'before_request', 'after_request', 'app_reset', 'config' - __hook_reversed = 'after_request' - - @cached_property - def _hooks(self): - return dict((name, []) for name in self.__hook_names) - - def add_hook(self, name, func): - ''' Attach a callback to a hook. Three hooks are currently implemented: - - before_request - Executed once before each request. The request context is - available, but no routing has happened yet. - after_request - Executed once after each request regardless of its outcome. - app_reset - Called whenever :meth:`Bottle.reset` is called. - ''' - if name in self.__hook_reversed: - self._hooks[name].insert(0, func) - else: - self._hooks[name].append(func) - - def remove_hook(self, name, func): - ''' Remove a callback from a hook. ''' - if name in self._hooks and func in self._hooks[name]: - self._hooks[name].remove(func) - return True - - def trigger_hook(self, __name, *args, **kwargs): - ''' Trigger a hook and return a list of results. ''' - return [hook(*args, **kwargs) for hook in self._hooks[__name][:]] - - def hook(self, name): - """ Return a decorator that attaches a callback to a hook. See - :meth:`add_hook` for details.""" - def decorator(func): - self.add_hook(name, func) - return func - return decorator - - def mount(self, prefix, app, **options): - ''' Mount an application (:class:`Bottle` or plain WSGI) to a specific - URL prefix. Example:: - - root_app.mount('/admin/', admin_app) - - :param prefix: path prefix or `mount-point`. If it ends in a slash, - that slash is mandatory. - :param app: an instance of :class:`Bottle` or a WSGI application. - - All other parameters are passed to the underlying :meth:`route` call. - ''' - if isinstance(app, basestring): - depr('Parameter order of Bottle.mount() changed.', True) # 0.10 - - segments = [p for p in prefix.split('/') if p] - if not segments: raise ValueError('Empty path prefix.') - path_depth = len(segments) - - def mountpoint_wrapper(): - try: - request.path_shift(path_depth) - rs = HTTPResponse([]) - def start_response(status, headerlist, exc_info=None): - if exc_info: - try: - _raise(*exc_info) - finally: - exc_info = None - rs.status = status - for name, value in headerlist: rs.add_header(name, value) - return rs.body.append - body = app(request.environ, start_response) - if body and rs.body: body = itertools.chain(rs.body, body) - rs.body = body or rs.body - return rs - finally: - request.path_shift(-path_depth) - - options.setdefault('skip', True) - options.setdefault('method', 'PROXY') - options.setdefault('mountpoint', {'prefix': prefix, 'target': app}) - options['callback'] = mountpoint_wrapper - - self.route('/%s/<:re:.*>' % '/'.join(segments), **options) - if not prefix.endswith('/'): - self.route('/' + '/'.join(segments), **options) - - def merge(self, routes): - ''' Merge the routes of another :class:`Bottle` application or a list of - :class:`Route` objects into this application. The routes keep their - 'owner', meaning that the :data:`Route.app` attribute is not - changed. ''' - if isinstance(routes, Bottle): - routes = routes.routes - for route in routes: - self.add_route(route) - - def install(self, plugin): - ''' Add a plugin to the list of plugins and prepare it for being - applied to all routes of this application. A plugin may be a simple - decorator or an object that implements the :class:`Plugin` API. - ''' - if hasattr(plugin, 'setup'): plugin.setup(self) - if not callable(plugin) and not hasattr(plugin, 'apply'): - raise TypeError("Plugins must be callable or implement .apply()") - self.plugins.append(plugin) - self.reset() - return plugin - - def uninstall(self, plugin): - ''' Uninstall plugins. Pass an instance to remove a specific plugin, a type - object to remove all plugins that match that type, a string to remove - all plugins with a matching ``name`` attribute or ``True`` to remove all - plugins. Return the list of removed plugins. ''' - removed, remove = [], plugin - for i, plugin in list(enumerate(self.plugins))[::-1]: - if remove is True or remove is plugin or remove is type(plugin) \ - or getattr(plugin, 'name', True) == remove: - removed.append(plugin) - del self.plugins[i] - if hasattr(plugin, 'close'): plugin.close() - if removed: self.reset() - return removed - - def reset(self, route=None): - ''' Reset all routes (force plugins to be re-applied) and clear all - caches. If an ID or route object is given, only that specific route - is affected. ''' - if route is None: routes = self.routes - elif isinstance(route, Route): routes = [route] - else: routes = [self.routes[route]] - for route in routes: route.reset() - if DEBUG: - for route in routes: route.prepare() - self.trigger_hook('app_reset') - - def close(self): - ''' Close the application and all installed plugins. ''' - for plugin in self.plugins: - if hasattr(plugin, 'close'): plugin.close() - self.stopped = True - - def run(self, **kwargs): - ''' Calls :func:`run` with the same parameters. ''' - run(self, **kwargs) - - def match(self, environ): - """ Search for a matching route and return a (:class:`Route` , urlargs) - tuple. The second value is a dictionary with parameters extracted - from the URL. Raise :exc:`HTTPError` (404/405) on a non-match.""" - return self.router.match(environ) - - def get_url(self, routename, **kargs): - """ Return a string that matches a named route """ - scriptname = request.environ.get('SCRIPT_NAME', '').strip('/') + '/' - location = self.router.build(routename, **kargs).lstrip('/') - return urljoin(urljoin('/', scriptname), location) - - def add_route(self, route): - ''' Add a route object, but do not change the :data:`Route.app` - attribute.''' - self.routes.append(route) - self.router.add(route.rule, route.method, route, name=route.name) - if DEBUG: route.prepare() - - def route(self, path=None, method='GET', callback=None, name=None, - apply=None, skip=None, **config): - """ A decorator to bind a function to a request URL. Example:: - - @app.route('/hello/:name') - def hello(name): - return 'Hello %s' % name - - The ``:name`` part is a wildcard. See :class:`Router` for syntax - details. - - :param path: Request path or a list of paths to listen to. If no - path is specified, it is automatically generated from the - signature of the function. - :param method: HTTP method (`GET`, `POST`, `PUT`, ...) or a list of - methods to listen to. (default: `GET`) - :param callback: An optional shortcut to avoid the decorator - syntax. ``route(..., callback=func)`` equals ``route(...)(func)`` - :param name: The name for this route. (default: None) - :param apply: A decorator or plugin or a list of plugins. These are - applied to the route callback in addition to installed plugins. - :param skip: A list of plugins, plugin classes or names. Matching - plugins are not installed to this route. ``True`` skips all. - - Any additional keyword arguments are stored as route-specific - configuration and passed to plugins (see :meth:`Plugin.apply`). - """ - if callable(path): path, callback = None, path - plugins = makelist(apply) - skiplist = makelist(skip) - def decorator(callback): - # TODO: Documentation and tests - if isinstance(callback, basestring): callback = load(callback) - for rule in makelist(path) or yieldroutes(callback): - for verb in makelist(method): - verb = verb.upper() - route = Route(self, rule, verb, callback, name=name, - plugins=plugins, skiplist=skiplist, **config) - self.add_route(route) - return callback - return decorator(callback) if callback else decorator - - def get(self, path=None, method='GET', **options): - """ Equals :meth:`route`. """ - return self.route(path, method, **options) - - def post(self, path=None, method='POST', **options): - """ Equals :meth:`route` with a ``POST`` method parameter. """ - return self.route(path, method, **options) - - def put(self, path=None, method='PUT', **options): - """ Equals :meth:`route` with a ``PUT`` method parameter. """ - return self.route(path, method, **options) - - def delete(self, path=None, method='DELETE', **options): - """ Equals :meth:`route` with a ``DELETE`` method parameter. """ - return self.route(path, method, **options) - - def error(self, code=500): - """ Decorator: Register an output handler for a HTTP error code""" - def wrapper(handler): - self.error_handler[int(code)] = handler - return handler - return wrapper - - def default_error_handler(self, res): - return tob(template(ERROR_PAGE_TEMPLATE, e=res)) - - def _handle(self, environ): - path = environ['bottle.raw_path'] = environ['PATH_INFO'] - if py3k: - try: - environ['PATH_INFO'] = path.encode('latin1').decode('utf8') - except UnicodeError: - return HTTPError(400, 'Invalid path string. Expected UTF-8') - - try: - environ['bottle.app'] = self - request.bind(environ) - response.bind() - try: - self.trigger_hook('before_request') - route, args = self.router.match(environ) - environ['route.handle'] = route - environ['bottle.route'] = route - environ['route.url_args'] = args - return route.call(**args) - finally: - self.trigger_hook('after_request') - - except HTTPResponse: - return _e() - except RouteReset: - route.reset() - return self._handle(environ) - except (KeyboardInterrupt, SystemExit, MemoryError): - raise - except Exception: - if not self.catchall: raise - stacktrace = format_exc() - environ['wsgi.errors'].write(stacktrace) - return HTTPError(500, "Internal Server Error", _e(), stacktrace) - - def _cast(self, out, peek=None): - """ Try to convert the parameter into something WSGI compatible and set - correct HTTP headers when possible. - Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like, - iterable of strings and iterable of unicodes - """ - - # Empty output is done here - if not out: - if 'Content-Length' not in response: - response['Content-Length'] = 0 - return [] - # Join lists of byte or unicode strings. Mixed lists are NOT supported - if isinstance(out, (tuple, list))\ - and isinstance(out[0], (bytes, unicode)): - out = out[0][0:0].join(out) # b'abc'[0:0] -> b'' - # Encode unicode strings - if isinstance(out, unicode): - out = out.encode(response.charset) - # Byte Strings are just returned - if isinstance(out, bytes): - if 'Content-Length' not in response: - response['Content-Length'] = len(out) - return [out] - # HTTPError or HTTPException (recursive, because they may wrap anything) - # TODO: Handle these explicitly in handle() or make them iterable. - if isinstance(out, HTTPError): - out.apply(response) - out = self.error_handler.get(out.status_code, self.default_error_handler)(out) - return self._cast(out) - if isinstance(out, HTTPResponse): - out.apply(response) - return self._cast(out.body) - - # File-like objects. - if hasattr(out, 'read'): - if 'wsgi.file_wrapper' in request.environ: - return request.environ['wsgi.file_wrapper'](out) - elif hasattr(out, 'close') or not hasattr(out, '__iter__'): - return WSGIFileWrapper(out) - - # Handle Iterables. We peek into them to detect their inner type. - try: - iout = iter(out) - first = next(iout) - while not first: - first = next(iout) - except StopIteration: - return self._cast('') - except HTTPResponse: - first = _e() - except (KeyboardInterrupt, SystemExit, MemoryError): - raise - except Exception: - if not self.catchall: raise - first = HTTPError(500, 'Unhandled exception', _e(), format_exc()) - - # These are the inner types allowed in iterator or generator objects. - if isinstance(first, HTTPResponse): - return self._cast(first) - elif isinstance(first, bytes): - new_iter = itertools.chain([first], iout) - elif isinstance(first, unicode): - encoder = lambda x: x.encode(response.charset) - new_iter = imap(encoder, itertools.chain([first], iout)) - else: - msg = 'Unsupported response type: %s' % type(first) - return self._cast(HTTPError(500, msg)) - if hasattr(out, 'close'): - new_iter = _closeiter(new_iter, out.close) - return new_iter - - def wsgi(self, environ, start_response): - """ The bottle WSGI-interface. """ - try: - out = self._cast(self._handle(environ)) - # rfc2616 section 4.3 - if response._status_code in (100, 101, 204, 304)\ - or environ['REQUEST_METHOD'] == 'HEAD': - if hasattr(out, 'close'): out.close() - out = [] - start_response(response._status_line, response.headerlist) - return out - except (KeyboardInterrupt, SystemExit, MemoryError): - raise - except Exception: - if not self.catchall: raise - err = '

Critical error while processing request: %s

' \ - % html_escape(environ.get('PATH_INFO', '/')) - if DEBUG: - err += '

Error:

\n
\n%s\n
\n' \ - '

Traceback:

\n
\n%s\n
\n' \ - % (html_escape(repr(_e())), html_escape(format_exc())) - environ['wsgi.errors'].write(err) - headers = [('Content-Type', 'text/html; charset=UTF-8')] - start_response('500 INTERNAL SERVER ERROR', headers, sys.exc_info()) - return [tob(err)] - - def __call__(self, environ, start_response): - ''' Each instance of :class:'Bottle' is a WSGI application. ''' - return self.wsgi(environ, start_response) - - - - - - -############################################################################### -# HTTP and WSGI Tools ########################################################## -############################################################################### - -class BaseRequest(object): - """ A wrapper for WSGI environment dictionaries that adds a lot of - convenient access methods and properties. Most of them are read-only. - - Adding new attributes to a request actually adds them to the environ - dictionary (as 'bottle.request.ext.'). This is the recommended - way to store and access request-specific data. - """ - - __slots__ = ('environ') - - #: Maximum size of memory buffer for :attr:`body` in bytes. - MEMFILE_MAX = 102400 - - def __init__(self, environ=None): - """ Wrap a WSGI environ dictionary. """ - #: The wrapped WSGI environ dictionary. This is the only real attribute. - #: All other attributes actually are read-only properties. - self.environ = {} if environ is None else environ - self.environ['bottle.request'] = self - - @DictProperty('environ', 'bottle.app', read_only=True) - def app(self): - ''' Bottle application handling this request. ''' - raise RuntimeError('This request is not connected to an application.') - - @DictProperty('environ', 'bottle.route', read_only=True) - def route(self): - """ The bottle :class:`Route` object that matches this request. """ - raise RuntimeError('This request is not connected to a route.') - - @DictProperty('environ', 'route.url_args', read_only=True) - def url_args(self): - """ The arguments extracted from the URL. """ - raise RuntimeError('This request is not connected to a route.') - - @property - def path(self): - ''' The value of ``PATH_INFO`` with exactly one prefixed slash (to fix - broken clients and avoid the "empty path" edge case). ''' - return '/' + self.environ.get('PATH_INFO','').lstrip('/') - - @property - def method(self): - ''' The ``REQUEST_METHOD`` value as an uppercase string. ''' - return self.environ.get('REQUEST_METHOD', 'GET').upper() - - @DictProperty('environ', 'bottle.request.headers', read_only=True) - def headers(self): - ''' A :class:`WSGIHeaderDict` that provides case-insensitive access to - HTTP request headers. ''' - return WSGIHeaderDict(self.environ) - - def get_header(self, name, default=None): - ''' Return the value of a request header, or a given default value. ''' - return self.headers.get(name, default) - - @DictProperty('environ', 'bottle.request.cookies', read_only=True) - def cookies(self): - """ Cookies parsed into a :class:`FormsDict`. Signed cookies are NOT - decoded. Use :meth:`get_cookie` if you expect signed cookies. """ - cookies = SimpleCookie(self.environ.get('HTTP_COOKIE','')).values() - return FormsDict((c.key, c.value) for c in cookies) - - def get_cookie(self, key, default=None, secret=None): - """ Return the content of a cookie. To read a `Signed Cookie`, the - `secret` must match the one used to create the cookie (see - :meth:`BaseResponse.set_cookie`). If anything goes wrong (missing - cookie or wrong signature), return a default value. """ - value = self.cookies.get(key) - if secret and value: - dec = cookie_decode(value, secret) # (key, value) tuple or None - return dec[1] if dec and dec[0] == key else default - return value or default - - @DictProperty('environ', 'bottle.request.query', read_only=True) - def query(self): - ''' The :attr:`query_string` parsed into a :class:`FormsDict`. These - values are sometimes called "URL arguments" or "GET parameters", but - not to be confused with "URL wildcards" as they are provided by the - :class:`Router`. ''' - get = self.environ['bottle.get'] = FormsDict() - pairs = _parse_qsl(self.environ.get('QUERY_STRING', '')) - for key, value in pairs: - get[key] = value - return get - - @DictProperty('environ', 'bottle.request.forms', read_only=True) - def forms(self): - """ Form values parsed from an `url-encoded` or `multipart/form-data` - encoded POST or PUT request body. The result is returned as a - :class:`FormsDict`. All keys and values are strings. File uploads - are stored separately in :attr:`files`. """ - forms = FormsDict() - for name, item in self.POST.allitems(): - if not isinstance(item, FileUpload): - forms[name] = item - return forms - - @DictProperty('environ', 'bottle.request.params', read_only=True) - def params(self): - """ A :class:`FormsDict` with the combined values of :attr:`query` and - :attr:`forms`. File uploads are stored in :attr:`files`. """ - params = FormsDict() - for key, value in self.query.allitems(): - params[key] = value - for key, value in self.forms.allitems(): - params[key] = value - return params - - @DictProperty('environ', 'bottle.request.files', read_only=True) - def files(self): - """ File uploads parsed from `multipart/form-data` encoded POST or PUT - request body. The values are instances of :class:`FileUpload`. - - """ - files = FormsDict() - for name, item in self.POST.allitems(): - if isinstance(item, FileUpload): - files[name] = item - return files - - @DictProperty('environ', 'bottle.request.json', read_only=True) - def json(self): - ''' If the ``Content-Type`` header is ``application/json``, this - property holds the parsed content of the request body. Only requests - smaller than :attr:`MEMFILE_MAX` are processed to avoid memory - exhaustion. ''' - ctype = self.environ.get('CONTENT_TYPE', '').lower().split(';')[0] - if ctype == 'application/json': - b = self._get_body_string() - if not b: - return None - return json_loads(b) - return None - - def _iter_body(self, read, bufsize): - maxread = max(0, self.content_length) - while maxread: - part = read(min(maxread, bufsize)) - if not part: break - yield part - maxread -= len(part) - - def _iter_chunked(self, read, bufsize): - err = HTTPError(400, 'Error while parsing chunked transfer body.') - rn, sem, bs = tob('\r\n'), tob(';'), tob('') - while True: - header = read(1) - while header[-2:] != rn: - c = read(1) - header += c - if not c: raise err - if len(header) > bufsize: raise err - size, _, _ = header.partition(sem) - try: - maxread = int(tonat(size.strip()), 16) - except ValueError: - raise err - if maxread == 0: break - buff = bs - while maxread > 0: - if not buff: - buff = read(min(maxread, bufsize)) - part, buff = buff[:maxread], buff[maxread:] - if not part: raise err - yield part - maxread -= len(part) - if read(2) != rn: - raise err - - @DictProperty('environ', 'bottle.request.body', read_only=True) - def _body(self): - body_iter = self._iter_chunked if self.chunked else self._iter_body - read_func = self.environ['wsgi.input'].read - body, body_size, is_temp_file = BytesIO(), 0, False - for part in body_iter(read_func, self.MEMFILE_MAX): - body.write(part) - body_size += len(part) - if not is_temp_file and body_size > self.MEMFILE_MAX: - body, tmp = TemporaryFile(mode='w+b'), body - body.write(tmp.getvalue()) - del tmp - is_temp_file = True - self.environ['wsgi.input'] = body - body.seek(0) - return body - - def _get_body_string(self): - ''' read body until content-length or MEMFILE_MAX into a string. Raise - HTTPError(413) on requests that are to large. ''' - clen = self.content_length - if clen > self.MEMFILE_MAX: - raise HTTPError(413, 'Request to large') - if clen < 0: clen = self.MEMFILE_MAX + 1 - data = self.body.read(clen) - if len(data) > self.MEMFILE_MAX: # Fail fast - raise HTTPError(413, 'Request to large') - return data - - @property - def body(self): - """ The HTTP request body as a seek-able file-like object. Depending on - :attr:`MEMFILE_MAX`, this is either a temporary file or a - :class:`io.BytesIO` instance. Accessing this property for the first - time reads and replaces the ``wsgi.input`` environ variable. - Subsequent accesses just do a `seek(0)` on the file object. """ - self._body.seek(0) - return self._body - - @property - def chunked(self): - ''' True if Chunked transfer encoding was. ''' - return 'chunked' in self.environ.get('HTTP_TRANSFER_ENCODING', '').lower() - - #: An alias for :attr:`query`. - GET = query - - @DictProperty('environ', 'bottle.request.post', read_only=True) - def POST(self): - """ The values of :attr:`forms` and :attr:`files` combined into a single - :class:`FormsDict`. Values are either strings (form values) or - instances of :class:`cgi.FieldStorage` (file uploads). - """ - post = FormsDict() - # We default to application/x-www-form-urlencoded for everything that - # is not multipart and take the fast path (also: 3.1 workaround) - if not self.content_type.startswith('multipart/'): - pairs = _parse_qsl(tonat(self._get_body_string(), 'latin1')) - for key, value in pairs: - post[key] = value - return post - - safe_env = {'QUERY_STRING':''} # Build a safe environment for cgi - for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'): - if key in self.environ: safe_env[key] = self.environ[key] - args = dict(fp=self.body, environ=safe_env, keep_blank_values=True) - if py31: - args['fp'] = NCTextIOWrapper(args['fp'], encoding='utf8', - newline='\n') - elif py3k: - args['encoding'] = 'utf8' - data = cgi.FieldStorage(**args) - self['_cgi.FieldStorage'] = data #http://bugs.python.org/issue18394#msg207958 - data = data.list or [] - for item in data: - if item.filename: - post[item.name] = FileUpload(item.file, item.name, - item.filename, item.headers) - else: - post[item.name] = item.value - return post - - @property - def url(self): - """ The full request URI including hostname and scheme. If your app - lives behind a reverse proxy or load balancer and you get confusing - results, make sure that the ``X-Forwarded-Host`` header is set - correctly. """ - return self.urlparts.geturl() - - @DictProperty('environ', 'bottle.request.urlparts', read_only=True) - def urlparts(self): - ''' The :attr:`url` string as an :class:`urlparse.SplitResult` tuple. - The tuple contains (scheme, host, path, query_string and fragment), - but the fragment is always empty because it is not visible to the - server. ''' - env = self.environ - http = env.get('HTTP_X_FORWARDED_PROTO') or env.get('wsgi.url_scheme', 'http') - host = env.get('HTTP_X_FORWARDED_HOST') or env.get('HTTP_HOST') - if not host: - # HTTP 1.1 requires a Host-header. This is for HTTP/1.0 clients. - host = env.get('SERVER_NAME', '127.0.0.1') - port = env.get('SERVER_PORT') - if port and port != ('80' if http == 'http' else '443'): - host += ':' + port - path = urlquote(self.fullpath) - return UrlSplitResult(http, host, path, env.get('QUERY_STRING'), '') - - @property - def fullpath(self): - """ Request path including :attr:`script_name` (if present). """ - return urljoin(self.script_name, self.path.lstrip('/')) - - @property - def query_string(self): - """ The raw :attr:`query` part of the URL (everything in between ``?`` - and ``#``) as a string. """ - return self.environ.get('QUERY_STRING', '') - - @property - def script_name(self): - ''' The initial portion of the URL's `path` that was removed by a higher - level (server or routing middleware) before the application was - called. This script path is returned with leading and tailing - slashes. ''' - script_name = self.environ.get('SCRIPT_NAME', '').strip('/') - return '/' + script_name + '/' if script_name else '/' - - def path_shift(self, shift=1): - ''' Shift path segments from :attr:`path` to :attr:`script_name` and - vice versa. - - :param shift: The number of path segments to shift. May be negative - to change the shift direction. (default: 1) - ''' - script = self.environ.get('SCRIPT_NAME','/') - self['SCRIPT_NAME'], self['PATH_INFO'] = path_shift(script, self.path, shift) - - @property - def content_length(self): - ''' The request body length as an integer. The client is responsible to - set this header. Otherwise, the real length of the body is unknown - and -1 is returned. In this case, :attr:`body` will be empty. ''' - return int(self.environ.get('CONTENT_LENGTH') or -1) - - @property - def content_type(self): - ''' The Content-Type header as a lowercase-string (default: empty). ''' - return self.environ.get('CONTENT_TYPE', '').lower() - - @property - def is_xhr(self): - ''' True if the request was triggered by a XMLHttpRequest. This only - works with JavaScript libraries that support the `X-Requested-With` - header (most of the popular libraries do). ''' - requested_with = self.environ.get('HTTP_X_REQUESTED_WITH','') - return requested_with.lower() == 'xmlhttprequest' - - @property - def is_ajax(self): - ''' Alias for :attr:`is_xhr`. "Ajax" is not the right term. ''' - return self.is_xhr - - @property - def auth(self): - """ HTTP authentication data as a (user, password) tuple. This - implementation currently supports basic (not digest) authentication - only. If the authentication happened at a higher level (e.g. in the - front web-server or a middleware), the password field is None, but - the user field is looked up from the ``REMOTE_USER`` environ - variable. On any errors, None is returned. """ - basic = parse_auth(self.environ.get('HTTP_AUTHORIZATION','')) - if basic: return basic - ruser = self.environ.get('REMOTE_USER') - if ruser: return (ruser, None) - return None - - @property - def remote_route(self): - """ A list of all IPs that were involved in this request, starting with - the client IP and followed by zero or more proxies. This does only - work if all proxies support the ```X-Forwarded-For`` header. Note - that this information can be forged by malicious clients. """ - proxy = self.environ.get('HTTP_X_FORWARDED_FOR') - if proxy: return [ip.strip() for ip in proxy.split(',')] - remote = self.environ.get('REMOTE_ADDR') - return [remote] if remote else [] - - @property - def remote_addr(self): - """ The client IP as a string. Note that this information can be forged - by malicious clients. """ - route = self.remote_route - return route[0] if route else None - - def copy(self): - """ Return a new :class:`Request` with a shallow :attr:`environ` copy. """ - return Request(self.environ.copy()) - - def get(self, value, default=None): return self.environ.get(value, default) - def __getitem__(self, key): return self.environ[key] - def __delitem__(self, key): self[key] = ""; del(self.environ[key]) - def __iter__(self): return iter(self.environ) - def __len__(self): return len(self.environ) - def keys(self): return self.environ.keys() - def __setitem__(self, key, value): - """ Change an environ value and clear all caches that depend on it. """ - - if self.environ.get('bottle.request.readonly'): - raise KeyError('The environ dictionary is read-only.') - - self.environ[key] = value - todelete = () - - if key == 'wsgi.input': - todelete = ('body', 'forms', 'files', 'params', 'post', 'json') - elif key == 'QUERY_STRING': - todelete = ('query', 'params') - elif key.startswith('HTTP_'): - todelete = ('headers', 'cookies') - - for key in todelete: - self.environ.pop('bottle.request.'+key, None) - - def __repr__(self): - return '<%s: %s %s>' % (self.__class__.__name__, self.method, self.url) - - def __getattr__(self, name): - ''' Search in self.environ for additional user defined attributes. ''' - try: - var = self.environ['bottle.request.ext.%s'%name] - return var.__get__(self) if hasattr(var, '__get__') else var - except KeyError: - raise AttributeError('Attribute %r not defined.' % name) - - def __setattr__(self, name, value): - if name == 'environ': return object.__setattr__(self, name, value) - self.environ['bottle.request.ext.%s'%name] = value - - -def _hkey(key): - if '\n' in key or '\r' in key or '\0' in key: - raise ValueError("Header names must not contain control characters: %r" % key) - return key.title().replace('_', '-') - - -def _hval(value): - value = tonat(value) - if '\n' in value or '\r' in value or '\0' in value: - raise ValueError("Header value must not contain control characters: %r" % value) - return value - - - -class HeaderProperty(object): - def __init__(self, name, reader=None, writer=None, default=''): - self.name, self.default = name, default - self.reader, self.writer = reader, writer - self.__doc__ = 'Current value of the %r header.' % name.title() - - def __get__(self, obj, cls): - if obj is None: return self - value = obj.get_header(self.name, self.default) - return self.reader(value) if self.reader else value - - def __set__(self, obj, value): - obj[self.name] = self.writer(value) if self.writer else value - - def __delete__(self, obj): - del obj[self.name] - - -class BaseResponse(object): - """ Storage class for a response body as well as headers and cookies. - - This class does support dict-like case-insensitive item-access to - headers, but is NOT a dict. Most notably, iterating over a response - yields parts of the body and not the headers. - - :param body: The response body as one of the supported types. - :param status: Either an HTTP status code (e.g. 200) or a status line - including the reason phrase (e.g. '200 OK'). - :param headers: A dictionary or a list of name-value pairs. - - Additional keyword arguments are added to the list of headers. - Underscores in the header name are replaced with dashes. - """ - - default_status = 200 - default_content_type = 'text/html; charset=UTF-8' - - # Header blacklist for specific response codes - # (rfc2616 section 10.2.3 and 10.3.5) - bad_headers = { - 204: set(('Content-Type',)), - 304: set(('Allow', 'Content-Encoding', 'Content-Language', - 'Content-Length', 'Content-Range', 'Content-Type', - 'Content-Md5', 'Last-Modified'))} - - def __init__(self, body='', status=None, headers=None, **more_headers): - self._cookies = None - self._headers = {} - self.body = body - self.status = status or self.default_status - if headers: - if isinstance(headers, dict): - headers = headers.items() - for name, value in headers: - self.add_header(name, value) - if more_headers: - for name, value in more_headers.items(): - self.add_header(name, value) - - def copy(self, cls=None): - ''' Returns a copy of self. ''' - cls = cls or BaseResponse - assert issubclass(cls, BaseResponse) - copy = cls() - copy.status = self.status - copy._headers = dict((k, v[:]) for (k, v) in self._headers.items()) - if self._cookies: - copy._cookies = SimpleCookie() - copy._cookies.load(self._cookies.output(header='')) - return copy - - def __iter__(self): - return iter(self.body) - - def close(self): - if hasattr(self.body, 'close'): - self.body.close() - - @property - def status_line(self): - ''' The HTTP status line as a string (e.g. ``404 Not Found``).''' - return self._status_line - - @property - def status_code(self): - ''' The HTTP status code as an integer (e.g. 404).''' - return self._status_code - - def _set_status(self, status): - if isinstance(status, int): - code, status = status, _HTTP_STATUS_LINES.get(status) - elif ' ' in status: - status = status.strip() - code = int(status.split()[0]) - else: - raise ValueError('String status line without a reason phrase.') - if not 100 <= code <= 999: raise ValueError('Status code out of range.') - self._status_code = code - self._status_line = str(status or ('%d Unknown' % code)) - - def _get_status(self): - return self._status_line - - status = property(_get_status, _set_status, None, - ''' A writeable property to change the HTTP response status. It accepts - either a numeric code (100-999) or a string with a custom reason - phrase (e.g. "404 Brain not found"). Both :data:`status_line` and - :data:`status_code` are updated accordingly. The return value is - always a status string. ''') - del _get_status, _set_status - - @property - def headers(self): - ''' An instance of :class:`HeaderDict`, a case-insensitive dict-like - view on the response headers. ''' - hdict = HeaderDict() - hdict.dict = self._headers - return hdict - - def __contains__(self, name): return _hkey(name) in self._headers - def __delitem__(self, name): del self._headers[_hkey(name)] - def __getitem__(self, name): return self._headers[_hkey(name)][-1] - def __setitem__(self, name, value): self._headers[_hkey(name)] = [_hval(value)] - - def get_header(self, name, default=None): - ''' Return the value of a previously defined header. If there is no - header with that name, return a default value. ''' - return self._headers.get(_hkey(name), [default])[-1] - - def set_header(self, name, value): - ''' Create a new response header, replacing any previously defined - headers with the same name. ''' - self._headers[_hkey(name)] = [_hval(value)] - - def add_header(self, name, value): - ''' Add an additional response header, not removing duplicates. ''' - self._headers.setdefault(_hkey(name), []).append(_hval(value)) - - def iter_headers(self): - ''' Yield (header, value) tuples, skipping headers that are not - allowed with the current response status code. ''' - return self.headerlist - - @property - def headerlist(self): - """ WSGI conform list of (header, value) tuples. """ - out = [] - headers = list(self._headers.items()) - if 'Content-Type' not in self._headers: - headers.append(('Content-Type', [self.default_content_type])) - if self._status_code in self.bad_headers: - bad_headers = self.bad_headers[self._status_code] - headers = [h for h in headers if h[0] not in bad_headers] - out += [(name, val) for (name, vals) in headers for val in vals] - if self._cookies: - for c in self._cookies.values(): - out.append(('Set-Cookie', _hval(c.OutputString()))) - if py3k: - out = [(k, v.encode('utf8').decode('latin1')) for (k, v) in out] - return out - - content_type = HeaderProperty('Content-Type') - content_length = HeaderProperty('Content-Length', reader=int) - expires = HeaderProperty('Expires', - reader=lambda x: datetime.utcfromtimestamp(parse_date(x)), - writer=lambda x: http_date(x)) - - @property - def charset(self, default='UTF-8'): - """ Return the charset specified in the content-type header (default: utf8). """ - if 'charset=' in self.content_type: - return self.content_type.split('charset=')[-1].split(';')[0].strip() - return default - - def set_cookie(self, name, value, secret=None, **options): - ''' Create a new cookie or replace an old one. If the `secret` parameter is - set, create a `Signed Cookie` (described below). - - :param name: the name of the cookie. - :param value: the value of the cookie. - :param secret: a signature key required for signed cookies. - - Additionally, this method accepts all RFC 2109 attributes that are - supported by :class:`cookie.Morsel`, including: - - :param max_age: maximum age in seconds. (default: None) - :param expires: a datetime object or UNIX timestamp. (default: None) - :param domain: the domain that is allowed to read the cookie. - (default: current domain) - :param path: limits the cookie to a given path (default: current path) - :param secure: limit the cookie to HTTPS connections (default: off). - :param httponly: prevents client-side javascript to read this cookie - (default: off, requires Python 2.6 or newer). - - If neither `expires` nor `max_age` is set (default), the cookie will - expire at the end of the browser session (as soon as the browser - window is closed). - - Signed cookies may store any pickle-able object and are - cryptographically signed to prevent manipulation. Keep in mind that - cookies are limited to 4kb in most browsers. - - Warning: Signed cookies are not encrypted (the client can still see - the content) and not copy-protected (the client can restore an old - cookie). The main intention is to make pickling and unpickling - save, not to store secret information at client side. - ''' - if not self._cookies: - self._cookies = SimpleCookie() - - if secret: - value = touni(cookie_encode((name, value), secret)) - elif not isinstance(value, basestring): - raise TypeError('Secret key missing for non-string Cookie.') - - if len(value) > 4096: raise ValueError('Cookie value to long.') - self._cookies[name] = value - - for key, value in options.items(): - if key == 'max_age': - if isinstance(value, timedelta): - value = value.seconds + value.days * 24 * 3600 - if key == 'expires': - if isinstance(value, (datedate, datetime)): - value = value.timetuple() - elif isinstance(value, (int, float)): - value = time.gmtime(value) - value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value) - self._cookies[name][key.replace('_', '-')] = value - - def delete_cookie(self, key, **kwargs): - ''' Delete a cookie. Be sure to use the same `domain` and `path` - settings as used to create the cookie. ''' - kwargs['max_age'] = -1 - kwargs['expires'] = 0 - self.set_cookie(key, '', **kwargs) - - def __repr__(self): - out = '' - for name, value in self.headerlist: - out += '%s: %s\n' % (name.title(), value.strip()) - return out - - -def local_property(name=None): - if name: depr('local_property() is deprecated and will be removed.') #0.12 - ls = threading.local() - def fget(self): - try: return ls.var - except AttributeError: - raise RuntimeError("Request context not initialized.") - def fset(self, value): ls.var = value - def fdel(self): del ls.var - return property(fget, fset, fdel, 'Thread-local property') - - -class LocalRequest(BaseRequest): - ''' A thread-local subclass of :class:`BaseRequest` with a different - set of attributes for each thread. There is usually only one global - instance of this class (:data:`request`). If accessed during a - request/response cycle, this instance always refers to the *current* - request (even on a multithreaded server). ''' - bind = BaseRequest.__init__ - environ = local_property() - - -class LocalResponse(BaseResponse): - ''' A thread-local subclass of :class:`BaseResponse` with a different - set of attributes for each thread. There is usually only one global - instance of this class (:data:`response`). Its attributes are used - to build the HTTP response at the end of the request/response cycle. - ''' - bind = BaseResponse.__init__ - _status_line = local_property() - _status_code = local_property() - _cookies = local_property() - _headers = local_property() - body = local_property() - - -Request = BaseRequest -Response = BaseResponse - - -class HTTPResponse(Response, BottleException): - def __init__(self, body='', status=None, headers=None, **more_headers): - super(HTTPResponse, self).__init__(body, status, headers, **more_headers) - - def apply(self, response): - response._status_code = self._status_code - response._status_line = self._status_line - response._headers = self._headers - response._cookies = self._cookies - response.body = self.body - - -class HTTPError(HTTPResponse): - default_status = 500 - def __init__(self, status=None, body=None, exception=None, traceback=None, - **options): - self.exception = exception - self.traceback = traceback - super(HTTPError, self).__init__(body, status, **options) - - - - - -############################################################################### -# Plugins ###################################################################### -############################################################################### - -class PluginError(BottleException): pass - - -class JSONPlugin(object): - name = 'json' - api = 2 - - def __init__(self, json_dumps=json_dumps): - self.json_dumps = json_dumps - - def apply(self, callback, route): - dumps = self.json_dumps - if not dumps: return callback - def wrapper(*a, **ka): - try: - rv = callback(*a, **ka) - except HTTPError: - rv = _e() - - if isinstance(rv, dict): - #Attempt to serialize, raises exception on failure - json_response = dumps(rv) - #Set content type only if serialization succesful - response.content_type = 'application/json' - return json_response - elif isinstance(rv, HTTPResponse) and isinstance(rv.body, dict): - rv.body = dumps(rv.body) - rv.content_type = 'application/json' - return rv - - return wrapper - - -class TemplatePlugin(object): - ''' This plugin applies the :func:`view` decorator to all routes with a - `template` config parameter. If the parameter is a tuple, the second - element must be a dict with additional options (e.g. `template_engine`) - or default variables for the template. ''' - name = 'template' - api = 2 - - def apply(self, callback, route): - conf = route.config.get('template') - if isinstance(conf, (tuple, list)) and len(conf) == 2: - return view(conf[0], **conf[1])(callback) - elif isinstance(conf, str): - return view(conf)(callback) - else: - return callback - - -#: Not a plugin, but part of the plugin API. TODO: Find a better place. -class _ImportRedirect(object): - def __init__(self, name, impmask): - ''' Create a virtual package that redirects imports (see PEP 302). ''' - self.name = name - self.impmask = impmask - self.module = sys.modules.setdefault(name, new_module(name)) - self.module.__dict__.update({'__file__': __file__, '__path__': [], - '__all__': [], '__loader__': self}) - sys.meta_path.append(self) - - def find_module(self, fullname, path=None): - if '.' not in fullname: return - packname = fullname.rsplit('.', 1)[0] - if packname != self.name: return - return self - - def load_module(self, fullname): - if fullname in sys.modules: return sys.modules[fullname] - modname = fullname.rsplit('.', 1)[1] - realname = self.impmask % modname - __import__(realname) - module = sys.modules[fullname] = sys.modules[realname] - setattr(self.module, modname, module) - module.__loader__ = self - return module - - - - - - -############################################################################### -# Common Utilities ############################################################# -############################################################################### - - -class MultiDict(DictMixin): - """ This dict stores multiple values per key, but behaves exactly like a - normal dict in that it returns only the newest value for any given key. - There are special methods available to access the full list of values. - """ - - def __init__(self, *a, **k): - self.dict = dict((k, [v]) for (k, v) in dict(*a, **k).items()) - - def __len__(self): return len(self.dict) - def __iter__(self): return iter(self.dict) - def __contains__(self, key): return key in self.dict - def __delitem__(self, key): del self.dict[key] - def __getitem__(self, key): return self.dict[key][-1] - def __setitem__(self, key, value): self.append(key, value) - def keys(self): return self.dict.keys() - - if py3k: - def values(self): return (v[-1] for v in self.dict.values()) - def items(self): return ((k, v[-1]) for k, v in self.dict.items()) - def allitems(self): - return ((k, v) for k, vl in self.dict.items() for v in vl) - iterkeys = keys - itervalues = values - iteritems = items - iterallitems = allitems - - else: - def values(self): return [v[-1] for v in self.dict.values()] - def items(self): return [(k, v[-1]) for k, v in self.dict.items()] - def iterkeys(self): return self.dict.iterkeys() - def itervalues(self): return (v[-1] for v in self.dict.itervalues()) - def iteritems(self): - return ((k, v[-1]) for k, v in self.dict.iteritems()) - def iterallitems(self): - return ((k, v) for k, vl in self.dict.iteritems() for v in vl) - def allitems(self): - return [(k, v) for k, vl in self.dict.iteritems() for v in vl] - - def get(self, key, default=None, index=-1, type=None): - ''' Return the most recent value for a key. - - :param default: The default value to be returned if the key is not - present or the type conversion fails. - :param index: An index for the list of available values. - :param type: If defined, this callable is used to cast the value - into a specific type. Exception are suppressed and result in - the default value to be returned. - ''' - try: - val = self.dict[key][index] - return type(val) if type else val - except Exception: - pass - return default - - def append(self, key, value): - ''' Add a new value to the list of values for this key. ''' - self.dict.setdefault(key, []).append(value) - - def replace(self, key, value): - ''' Replace the list of values with a single value. ''' - self.dict[key] = [value] - - def getall(self, key): - ''' Return a (possibly empty) list of values for a key. ''' - return self.dict.get(key) or [] - - #: Aliases for WTForms to mimic other multi-dict APIs (Django) - getone = get - getlist = getall - - -class FormsDict(MultiDict): - ''' This :class:`MultiDict` subclass is used to store request form data. - Additionally to the normal dict-like item access methods (which return - unmodified data as native strings), this container also supports - attribute-like access to its values. Attributes are automatically de- - or recoded to match :attr:`input_encoding` (default: 'utf8'). Missing - attributes default to an empty string. ''' - - #: Encoding used for attribute values. - input_encoding = 'utf8' - #: If true (default), unicode strings are first encoded with `latin1` - #: and then decoded to match :attr:`input_encoding`. - recode_unicode = True - - def _fix(self, s, encoding=None): - if isinstance(s, unicode) and self.recode_unicode: # Python 3 WSGI - return s.encode('latin1').decode(encoding or self.input_encoding) - elif isinstance(s, bytes): # Python 2 WSGI - return s.decode(encoding or self.input_encoding) - else: - return s - - def decode(self, encoding=None): - ''' Returns a copy with all keys and values de- or recoded to match - :attr:`input_encoding`. Some libraries (e.g. WTForms) want a - unicode dictionary. ''' - copy = FormsDict() - enc = copy.input_encoding = encoding or self.input_encoding - copy.recode_unicode = False - for key, value in self.allitems(): - copy.append(self._fix(key, enc), self._fix(value, enc)) - return copy - - def getunicode(self, name, default=None, encoding=None): - ''' Return the value as a unicode string, or the default. ''' - try: - return self._fix(self[name], encoding) - except (UnicodeError, KeyError): - return default - - def __getattr__(self, name, default=unicode()): - # Without this guard, pickle generates a cryptic TypeError: - if name.startswith('__') and name.endswith('__'): - return super(FormsDict, self).__getattr__(name) - return self.getunicode(name, default=default) - -class HeaderDict(MultiDict): - """ A case-insensitive version of :class:`MultiDict` that defaults to - replace the old value instead of appending it. """ - - def __init__(self, *a, **ka): - self.dict = {} - if a or ka: self.update(*a, **ka) - - def __contains__(self, key): return _hkey(key) in self.dict - def __delitem__(self, key): del self.dict[_hkey(key)] - def __getitem__(self, key): return self.dict[_hkey(key)][-1] - def __setitem__(self, key, value): self.dict[_hkey(key)] = [_hval(value)] - def append(self, key, value): self.dict.setdefault(_hkey(key), []).append(_hval(value)) - def replace(self, key, value): self.dict[_hkey(key)] = [_hval(value)] - def getall(self, key): return self.dict.get(_hkey(key)) or [] - def get(self, key, default=None, index=-1): - return MultiDict.get(self, _hkey(key), default, index) - def filter(self, names): - for name in (_hkey(n) for n in names): - if name in self.dict: - del self.dict[name] - - -class WSGIHeaderDict(DictMixin): - ''' This dict-like class wraps a WSGI environ dict and provides convenient - access to HTTP_* fields. Keys and values are native strings - (2.x bytes or 3.x unicode) and keys are case-insensitive. If the WSGI - environment contains non-native string values, these are de- or encoded - using a lossless 'latin1' character set. - - The API will remain stable even on changes to the relevant PEPs. - Currently PEP 333, 444 and 3333 are supported. (PEP 444 is the only one - that uses non-native strings.) - ''' - #: List of keys that do not have a ``HTTP_`` prefix. - cgikeys = ('CONTENT_TYPE', 'CONTENT_LENGTH') - - def __init__(self, environ): - self.environ = environ - - def _ekey(self, key): - ''' Translate header field name to CGI/WSGI environ key. ''' - key = key.replace('-','_').upper() - if key in self.cgikeys: - return key - return 'HTTP_' + key - - def raw(self, key, default=None): - ''' Return the header value as is (may be bytes or unicode). ''' - return self.environ.get(self._ekey(key), default) - - def __getitem__(self, key): - return tonat(self.environ[self._ekey(key)], 'latin1') - - def __setitem__(self, key, value): - raise TypeError("%s is read-only." % self.__class__) - - def __delitem__(self, key): - raise TypeError("%s is read-only." % self.__class__) - - def __iter__(self): - for key in self.environ: - if key[:5] == 'HTTP_': - yield key[5:].replace('_', '-').title() - elif key in self.cgikeys: - yield key.replace('_', '-').title() - - def keys(self): return [x for x in self] - def __len__(self): return len(self.keys()) - def __contains__(self, key): return self._ekey(key) in self.environ - - - -class ConfigDict(dict): - ''' A dict-like configuration storage with additional support for - namespaces, validators, meta-data, on_change listeners and more. - - This storage is optimized for fast read access. Retrieving a key - or using non-altering dict methods (e.g. `dict.get()`) has no overhead - compared to a native dict. - ''' - __slots__ = ('_meta', '_on_change') - - class Namespace(DictMixin): - - def __init__(self, config, namespace): - self._config = config - self._prefix = namespace - - def __getitem__(self, key): - depr('Accessing namespaces as dicts is discouraged. ' - 'Only use flat item access: ' - 'cfg["names"]["pace"]["key"] -> cfg["name.space.key"]') #0.12 - return self._config[self._prefix + '.' + key] - - def __setitem__(self, key, value): - self._config[self._prefix + '.' + key] = value - - def __delitem__(self, key): - del self._config[self._prefix + '.' + key] - - def __iter__(self): - ns_prefix = self._prefix + '.' - for key in self._config: - ns, dot, name = key.rpartition('.') - if ns == self._prefix and name: - yield name - - def keys(self): return [x for x in self] - def __len__(self): return len(self.keys()) - def __contains__(self, key): return self._prefix + '.' + key in self._config - def __repr__(self): return '' % self._prefix - def __str__(self): return '' % self._prefix - - # Deprecated ConfigDict features - def __getattr__(self, key): - depr('Attribute access is deprecated.') #0.12 - if key not in self and key[0].isupper(): - self[key] = ConfigDict.Namespace(self._config, self._prefix + '.' + key) - if key not in self and key.startswith('__'): - raise AttributeError(key) - return self.get(key) - - def __setattr__(self, key, value): - if key in ('_config', '_prefix'): - self.__dict__[key] = value - return - depr('Attribute assignment is deprecated.') #0.12 - if hasattr(DictMixin, key): - raise AttributeError('Read-only attribute.') - if key in self and self[key] and isinstance(self[key], self.__class__): - raise AttributeError('Non-empty namespace attribute.') - self[key] = value - - def __delattr__(self, key): - if key in self: - val = self.pop(key) - if isinstance(val, self.__class__): - prefix = key + '.' - for key in self: - if key.startswith(prefix): - del self[prefix+key] - - def __call__(self, *a, **ka): - depr('Calling ConfDict is deprecated. Use the update() method.') #0.12 - self.update(*a, **ka) - return self - - def __init__(self, *a, **ka): - self._meta = {} - self._on_change = lambda name, value: None - if a or ka: - depr('Constructor does no longer accept parameters.') #0.12 - self.update(*a, **ka) - - def load_config(self, filename): - ''' Load values from an *.ini style config file. - - If the config file contains sections, their names are used as - namespaces for the values within. The two special sections - ``DEFAULT`` and ``bottle`` refer to the root namespace (no prefix). - ''' - conf = ConfigParser() - conf.read(filename) - for section in conf.sections(): - for key, value in conf.items(section): - if section not in ('DEFAULT', 'bottle'): - key = section + '.' + key - self[key] = value - return self - - def load_dict(self, source, namespace='', make_namespaces=False): - ''' Import values from a dictionary structure. Nesting can be used to - represent namespaces. - - >>> ConfigDict().load_dict({'name': {'space': {'key': 'value'}}}) - {'name.space.key': 'value'} - ''' - stack = [(namespace, source)] - while stack: - prefix, source = stack.pop() - if not isinstance(source, dict): - raise TypeError('Source is not a dict (r)' % type(key)) - for key, value in source.items(): - if not isinstance(key, basestring): - raise TypeError('Key is not a string (%r)' % type(key)) - full_key = prefix + '.' + key if prefix else key - if isinstance(value, dict): - stack.append((full_key, value)) - if make_namespaces: - self[full_key] = self.Namespace(self, full_key) - else: - self[full_key] = value - return self - - def update(self, *a, **ka): - ''' If the first parameter is a string, all keys are prefixed with this - namespace. Apart from that it works just as the usual dict.update(). - Example: ``update('some.namespace', key='value')`` ''' - prefix = '' - if a and isinstance(a[0], basestring): - prefix = a[0].strip('.') + '.' - a = a[1:] - for key, value in dict(*a, **ka).items(): - self[prefix+key] = value - - def setdefault(self, key, value): - if key not in self: - self[key] = value - return self[key] - - def __setitem__(self, key, value): - if not isinstance(key, basestring): - raise TypeError('Key has type %r (not a string)' % type(key)) - - value = self.meta_get(key, 'filter', lambda x: x)(value) - if key in self and self[key] is value: - return - self._on_change(key, value) - dict.__setitem__(self, key, value) - - def __delitem__(self, key): - dict.__delitem__(self, key) - - def clear(self): - for key in self: - del self[key] - - def meta_get(self, key, metafield, default=None): - ''' Return the value of a meta field for a key. ''' - return self._meta.get(key, {}).get(metafield, default) - - def meta_set(self, key, metafield, value): - ''' Set the meta field for a key to a new value. This triggers the - on-change handler for existing keys. ''' - self._meta.setdefault(key, {})[metafield] = value - if key in self: - self[key] = self[key] - - def meta_list(self, key): - ''' Return an iterable of meta field names defined for a key. ''' - return self._meta.get(key, {}).keys() - - # Deprecated ConfigDict features - def __getattr__(self, key): - depr('Attribute access is deprecated.') #0.12 - if key not in self and key[0].isupper(): - self[key] = self.Namespace(self, key) - if key not in self and key.startswith('__'): - raise AttributeError(key) - return self.get(key) - - def __setattr__(self, key, value): - if key in self.__slots__: - return dict.__setattr__(self, key, value) - depr('Attribute assignment is deprecated.') #0.12 - if hasattr(dict, key): - raise AttributeError('Read-only attribute.') - if key in self and self[key] and isinstance(self[key], self.Namespace): - raise AttributeError('Non-empty namespace attribute.') - self[key] = value - - def __delattr__(self, key): - if key in self: - val = self.pop(key) - if isinstance(val, self.Namespace): - prefix = key + '.' - for key in self: - if key.startswith(prefix): - del self[prefix+key] - - def __call__(self, *a, **ka): - depr('Calling ConfDict is deprecated. Use the update() method.') #0.12 - self.update(*a, **ka) - return self - - - -class AppStack(list): - """ A stack-like list. Calling it returns the head of the stack. """ - - def __call__(self): - """ Return the current default application. """ - return self[-1] - - def push(self, value=None): - """ Add a new :class:`Bottle` instance to the stack """ - if not isinstance(value, Bottle): - value = Bottle() - self.append(value) - return value - - -class WSGIFileWrapper(object): - - def __init__(self, fp, buffer_size=1024*64): - self.fp, self.buffer_size = fp, buffer_size - for attr in ('fileno', 'close', 'read', 'readlines', 'tell', 'seek'): - if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr)) - - def __iter__(self): - buff, read = self.buffer_size, self.read - while True: - part = read(buff) - if not part: return - yield part - - -class _closeiter(object): - ''' This only exists to be able to attach a .close method to iterators that - do not support attribute assignment (most of itertools). ''' - - def __init__(self, iterator, close=None): - self.iterator = iterator - self.close_callbacks = makelist(close) - - def __iter__(self): - return iter(self.iterator) - - def close(self): - for func in self.close_callbacks: - func() - - -class ResourceManager(object): - ''' This class manages a list of search paths and helps to find and open - application-bound resources (files). - - :param base: default value for :meth:`add_path` calls. - :param opener: callable used to open resources. - :param cachemode: controls which lookups are cached. One of 'all', - 'found' or 'none'. - ''' - - def __init__(self, base='./', opener=open, cachemode='all'): - self.opener = open - self.base = base - self.cachemode = cachemode - - #: A list of search paths. See :meth:`add_path` for details. - self.path = [] - #: A cache for resolved paths. ``res.cache.clear()`` clears the cache. - self.cache = {} - - def add_path(self, path, base=None, index=None, create=False): - ''' Add a new path to the list of search paths. Return False if the - path does not exist. - - :param path: The new search path. Relative paths are turned into - an absolute and normalized form. If the path looks like a file - (not ending in `/`), the filename is stripped off. - :param base: Path used to absolutize relative search paths. - Defaults to :attr:`base` which defaults to ``os.getcwd()``. - :param index: Position within the list of search paths. Defaults - to last index (appends to the list). - - The `base` parameter makes it easy to reference files installed - along with a python module or package:: - - res.add_path('./resources/', __file__) - ''' - base = os.path.abspath(os.path.dirname(base or self.base)) - path = os.path.abspath(os.path.join(base, os.path.dirname(path))) - path += os.sep - if path in self.path: - self.path.remove(path) - if create and not os.path.isdir(path): - os.makedirs(path) - if index is None: - self.path.append(path) - else: - self.path.insert(index, path) - self.cache.clear() - return os.path.exists(path) - - def __iter__(self): - ''' Iterate over all existing files in all registered paths. ''' - search = self.path[:] - while search: - path = search.pop() - if not os.path.isdir(path): continue - for name in os.listdir(path): - full = os.path.join(path, name) - if os.path.isdir(full): search.append(full) - else: yield full - - def lookup(self, name): - ''' Search for a resource and return an absolute file path, or `None`. - - The :attr:`path` list is searched in order. The first match is - returend. Symlinks are followed. The result is cached to speed up - future lookups. ''' - if name not in self.cache or DEBUG: - for path in self.path: - fpath = os.path.join(path, name) - if os.path.isfile(fpath): - if self.cachemode in ('all', 'found'): - self.cache[name] = fpath - return fpath - if self.cachemode == 'all': - self.cache[name] = None - return self.cache[name] - - def open(self, name, mode='r', *args, **kwargs): - ''' Find a resource and return a file object, or raise IOError. ''' - fname = self.lookup(name) - if not fname: raise IOError("Resource %r not found." % name) - return self.opener(fname, mode=mode, *args, **kwargs) - - -class FileUpload(object): - - def __init__(self, fileobj, name, filename, headers=None): - ''' Wrapper for file uploads. ''' - #: Open file(-like) object (BytesIO buffer or temporary file) - self.file = fileobj - #: Name of the upload form field - self.name = name - #: Raw filename as sent by the client (may contain unsafe characters) - self.raw_filename = filename - #: A :class:`HeaderDict` with additional headers (e.g. content-type) - self.headers = HeaderDict(headers) if headers else HeaderDict() - - content_type = HeaderProperty('Content-Type') - content_length = HeaderProperty('Content-Length', reader=int, default=-1) - - def get_header(self, name, default=None): - """ Return the value of a header within the mulripart part. """ - return self.headers.get(name, default) - - @cached_property - def filename(self): - ''' Name of the file on the client file system, but normalized to ensure - file system compatibility. An empty filename is returned as 'empty'. - - Only ASCII letters, digits, dashes, underscores and dots are - allowed in the final filename. Accents are removed, if possible. - Whitespace is replaced by a single dash. Leading or tailing dots - or dashes are removed. The filename is limited to 255 characters. - ''' - fname = self.raw_filename - if not isinstance(fname, unicode): - fname = fname.decode('utf8', 'ignore') - fname = normalize('NFKD', fname).encode('ASCII', 'ignore').decode('ASCII') - fname = os.path.basename(fname.replace('\\', os.path.sep)) - fname = re.sub(r'[^a-zA-Z0-9-_.\s]', '', fname).strip() - fname = re.sub(r'[-\s]+', '-', fname).strip('.-') - return fname[:255] or 'empty' - - def _copy_file(self, fp, chunk_size=2**16): - read, write, offset = self.file.read, fp.write, self.file.tell() - while 1: - buf = read(chunk_size) - if not buf: break - write(buf) - self.file.seek(offset) - - def save(self, destination, overwrite=False, chunk_size=2**16): - ''' Save file to disk or copy its content to an open file(-like) object. - If *destination* is a directory, :attr:`filename` is added to the - path. Existing files are not overwritten by default (IOError). - - :param destination: File path, directory or file(-like) object. - :param overwrite: If True, replace existing files. (default: False) - :param chunk_size: Bytes to read at a time. (default: 64kb) - ''' - if isinstance(destination, basestring): # Except file-likes here - if os.path.isdir(destination): - destination = os.path.join(destination, self.filename) - if not overwrite and os.path.exists(destination): - raise IOError('File exists.') - with open(destination, 'wb') as fp: - self._copy_file(fp, chunk_size) - else: - self._copy_file(destination, chunk_size) - - - - - - -############################################################################### -# Application Helper ########################################################### -############################################################################### - - -def abort(code=500, text='Unknown Error.'): - """ Aborts execution and causes a HTTP error. """ - raise HTTPError(code, text) - - -def redirect(url, code=None): - """ Aborts execution and causes a 303 or 302 redirect, depending on - the HTTP protocol version. """ - if not code: - code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302 - res = response.copy(cls=HTTPResponse) - res.status = code - res.body = "" - res.set_header('Location', urljoin(request.url, url)) - raise res - - -def _file_iter_range(fp, offset, bytes, maxread=1024*1024): - ''' Yield chunks from a range in a file. No chunk is bigger than maxread.''' - fp.seek(offset) - while bytes > 0: - part = fp.read(min(bytes, maxread)) - if not part: break - bytes -= len(part) - yield part - - -def static_file(filename, root, mimetype='auto', download=False, charset='UTF-8'): - """ Open a file in a safe way and return :exc:`HTTPResponse` with status - code 200, 305, 403 or 404. The ``Content-Type``, ``Content-Encoding``, - ``Content-Length`` and ``Last-Modified`` headers are set if possible. - Special support for ``If-Modified-Since``, ``Range`` and ``HEAD`` - requests. - - :param filename: Name or path of the file to send. - :param root: Root path for file lookups. Should be an absolute directory - path. - :param mimetype: Defines the content-type header (default: guess from - file extension) - :param download: If True, ask the browser to open a `Save as...` dialog - instead of opening the file with the associated program. You can - specify a custom filename as a string. If not specified, the - original filename is used (default: False). - :param charset: The charset to use for files with a ``text/*`` - mime-type. (default: UTF-8) - """ - - root = os.path.abspath(root) + os.sep - filename = os.path.abspath(os.path.join(root, filename.strip('/\\'))) - headers = dict() - - if not filename.startswith(root): - return HTTPError(403, "Access denied.") - if not os.path.exists(filename) or not os.path.isfile(filename): - return HTTPError(404, "File does not exist.") - if not os.access(filename, os.R_OK): - return HTTPError(403, "You do not have permission to access this file.") - - if mimetype == 'auto': - mimetype, encoding = mimetypes.guess_type(filename) - if encoding: headers['Content-Encoding'] = encoding - - if mimetype: - if mimetype[:5] == 'text/' and charset and 'charset' not in mimetype: - mimetype += '; charset=%s' % charset - headers['Content-Type'] = mimetype - - if download: - download = os.path.basename(filename if download == True else download) - headers['Content-Disposition'] = 'attachment; filename="%s"' % download - - stats = os.stat(filename) - headers['Content-Length'] = clen = stats.st_size - lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime)) - headers['Last-Modified'] = lm - - ims = request.environ.get('HTTP_IF_MODIFIED_SINCE') - if ims: - ims = parse_date(ims.split(";")[0].strip()) - if ims is not None and ims >= int(stats.st_mtime): - headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) - return HTTPResponse(status=304, **headers) - - body = '' if request.method == 'HEAD' else open(filename, 'rb') - - headers["Accept-Ranges"] = "bytes" - ranges = request.environ.get('HTTP_RANGE') - if 'HTTP_RANGE' in request.environ: - ranges = list(parse_range_header(request.environ['HTTP_RANGE'], clen)) - if not ranges: - return HTTPError(416, "Requested Range Not Satisfiable") - offset, end = ranges[0] - headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end-1, clen) - headers["Content-Length"] = str(end-offset) - if body: body = _file_iter_range(body, offset, end-offset) - return HTTPResponse(body, status=206, **headers) - return HTTPResponse(body, **headers) - - - - - - -############################################################################### -# HTTP Utilities and MISC (TODO) ############################################### -############################################################################### - - -def debug(mode=True): - """ Change the debug level. - There is only one debug level supported at the moment.""" - global DEBUG - if mode: warnings.simplefilter('default') - DEBUG = bool(mode) - -def http_date(value): - if isinstance(value, (datedate, datetime)): - value = value.utctimetuple() - elif isinstance(value, (int, float)): - value = time.gmtime(value) - if not isinstance(value, basestring): - value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value) - return value - -def parse_date(ims): - """ Parse rfc1123, rfc850 and asctime timestamps and return UTC epoch. """ - try: - ts = email.utils.parsedate_tz(ims) - return time.mktime(ts[:8] + (0,)) - (ts[9] or 0) - time.timezone - except (TypeError, ValueError, IndexError, OverflowError): - return None - -def parse_auth(header): - """ Parse rfc2617 HTTP authentication header string (basic) and return (user,pass) tuple or None""" - try: - method, data = header.split(None, 1) - if method.lower() == 'basic': - user, pwd = touni(base64.b64decode(tob(data))).split(':',1) - return user, pwd - except (KeyError, ValueError): - return None - -def parse_range_header(header, maxlen=0): - ''' Yield (start, end) ranges parsed from a HTTP Range header. Skip - unsatisfiable ranges. The end index is non-inclusive.''' - if not header or header[:6] != 'bytes=': return - ranges = [r.split('-', 1) for r in header[6:].split(',') if '-' in r] - for start, end in ranges: - try: - if not start: # bytes=-100 -> last 100 bytes - start, end = max(0, maxlen-int(end)), maxlen - elif not end: # bytes=100- -> all but the first 99 bytes - start, end = int(start), maxlen - else: # bytes=100-200 -> bytes 100-200 (inclusive) - start, end = int(start), min(int(end)+1, maxlen) - if 0 <= start < end <= maxlen: - yield start, end - except ValueError: - pass - -def _parse_qsl(qs): - r = [] - for pair in qs.split('&'): - if not pair: continue - nv = pair.split('=', 1) - if len(nv) != 2: nv.append('') - key = urlunquote(nv[0].replace('+', ' ')) - value = urlunquote(nv[1].replace('+', ' ')) - r.append((key, value)) - return r - -def _lscmp(a, b): - ''' Compares two strings in a cryptographically safe way: - Runtime is not affected by length of common prefix. ''' - return not sum(0 if x==y else 1 for x, y in zip(a, b)) and len(a) == len(b) - - -def cookie_encode(data, key): - ''' Encode and sign a pickle-able object. Return a (byte) string ''' - msg = base64.b64encode(pickle.dumps(data, -1)) - sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest()) - return tob('!') + sig + tob('?') + msg - - -def cookie_decode(data, key): - ''' Verify and decode an encoded string. Return an object or None.''' - data = tob(data) - if cookie_is_encoded(data): - sig, msg = data.split(tob('?'), 1) - if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest())): - return pickle.loads(base64.b64decode(msg)) - return None - - -def cookie_is_encoded(data): - ''' Return True if the argument looks like a encoded cookie.''' - return bool(data.startswith(tob('!')) and tob('?') in data) - - -def html_escape(string): - ''' Escape HTML special characters ``&<>`` and quotes ``'"``. ''' - return string.replace('&','&').replace('<','<').replace('>','>')\ - .replace('"','"').replace("'",''') - - -def html_quote(string): - ''' Escape and quote a string to be used as an HTTP attribute.''' - return '"%s"' % html_escape(string).replace('\n',' ')\ - .replace('\r',' ').replace('\t',' ') - - -def yieldroutes(func): - """ Return a generator for routes that match the signature (name, args) - of the func parameter. This may yield more than one route if the function - takes optional keyword arguments. The output is best described by example:: - - a() -> '/a' - b(x, y) -> '/b//' - c(x, y=5) -> '/c/' and '/c//' - d(x=5, y=6) -> '/d' and '/d/' and '/d//' - """ - path = '/' + func.__name__.replace('__','/').lstrip('/') - spec = getargspec(func) - argc = len(spec[0]) - len(spec[3] or []) - path += ('/<%s>' * argc) % tuple(spec[0][:argc]) - yield path - for arg in spec[0][argc:]: - path += '/<%s>' % arg - yield path - - -def path_shift(script_name, path_info, shift=1): - ''' Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa. - - :return: The modified paths. - :param script_name: The SCRIPT_NAME path. - :param script_name: The PATH_INFO path. - :param shift: The number of path fragments to shift. May be negative to - change the shift direction. (default: 1) - ''' - if shift == 0: return script_name, path_info - pathlist = path_info.strip('/').split('/') - scriptlist = script_name.strip('/').split('/') - if pathlist and pathlist[0] == '': pathlist = [] - if scriptlist and scriptlist[0] == '': scriptlist = [] - if shift > 0 and shift <= len(pathlist): - moved = pathlist[:shift] - scriptlist = scriptlist + moved - pathlist = pathlist[shift:] - elif shift < 0 and shift >= -len(scriptlist): - moved = scriptlist[shift:] - pathlist = moved + pathlist - scriptlist = scriptlist[:shift] - else: - empty = 'SCRIPT_NAME' if shift < 0 else 'PATH_INFO' - raise AssertionError("Cannot shift. Nothing left from %s" % empty) - new_script_name = '/' + '/'.join(scriptlist) - new_path_info = '/' + '/'.join(pathlist) - if path_info.endswith('/') and pathlist: new_path_info += '/' - return new_script_name, new_path_info - - -def auth_basic(check, realm="private", text="Access denied"): - ''' Callback decorator to require HTTP auth (basic). - TODO: Add route(check_auth=...) parameter. ''' - def decorator(func): - def wrapper(*a, **ka): - user, password = request.auth or (None, None) - if user is None or not check(user, password): - err = HTTPError(401, text) - err.add_header('WWW-Authenticate', 'Basic realm="%s"' % realm) - return err - return func(*a, **ka) - return wrapper - return decorator - - -# Shortcuts for common Bottle methods. -# They all refer to the current default application. - -def make_default_app_wrapper(name): - ''' Return a callable that relays calls to the current default app. ''' - @functools.wraps(getattr(Bottle, name)) - def wrapper(*a, **ka): - return getattr(app(), name)(*a, **ka) - return wrapper - -route = make_default_app_wrapper('route') -get = make_default_app_wrapper('get') -post = make_default_app_wrapper('post') -put = make_default_app_wrapper('put') -delete = make_default_app_wrapper('delete') -error = make_default_app_wrapper('error') -mount = make_default_app_wrapper('mount') -hook = make_default_app_wrapper('hook') -install = make_default_app_wrapper('install') -uninstall = make_default_app_wrapper('uninstall') -url = make_default_app_wrapper('get_url') - - - - - - - -############################################################################### -# Server Adapter ############################################################### -############################################################################### - - -class ServerAdapter(object): - quiet = False - def __init__(self, host='127.0.0.1', port=8080, **options): - self.options = options - self.host = host - self.port = int(port) - - def run(self, handler): # pragma: no cover - pass - - def __repr__(self): - args = ', '.join(['%s=%s'%(k,repr(v)) for k, v in self.options.items()]) - return "%s(%s)" % (self.__class__.__name__, args) - - -class CGIServer(ServerAdapter): - quiet = True - def run(self, handler): # pragma: no cover - from wsgiref.handlers import CGIHandler - def fixed_environ(environ, start_response): - environ.setdefault('PATH_INFO', '') - return handler(environ, start_response) - CGIHandler().run(fixed_environ) - - -class FlupFCGIServer(ServerAdapter): - def run(self, handler): # pragma: no cover - import flup.server.fcgi - self.options.setdefault('bindAddress', (self.host, self.port)) - flup.server.fcgi.WSGIServer(handler, **self.options).run() - - -class WSGIRefServer(ServerAdapter): - def run(self, app): # pragma: no cover - from wsgiref.simple_server import WSGIRequestHandler, WSGIServer - from wsgiref.simple_server import make_server - import socket - - class FixedHandler(WSGIRequestHandler): - def address_string(self): # Prevent reverse DNS lookups please. - return self.client_address[0] - def log_request(*args, **kw): - if not self.quiet: - return WSGIRequestHandler.log_request(*args, **kw) - - handler_cls = self.options.get('handler_class', FixedHandler) - server_cls = self.options.get('server_class', WSGIServer) - - if ':' in self.host: # Fix wsgiref for IPv6 addresses. - if getattr(server_cls, 'address_family') == socket.AF_INET: - class server_cls(server_cls): - address_family = socket.AF_INET6 - - srv = make_server(self.host, self.port, app, server_cls, handler_cls) - srv.serve_forever() - - -class CherryPyServer(ServerAdapter): - def run(self, handler): # pragma: no cover - from cherrypy import wsgiserver - self.options['bind_addr'] = (self.host, self.port) - self.options['wsgi_app'] = handler - - certfile = self.options.get('certfile') - if certfile: - del self.options['certfile'] - keyfile = self.options.get('keyfile') - if keyfile: - del self.options['keyfile'] - - server = wsgiserver.CherryPyWSGIServer(**self.options) - if certfile: - server.ssl_certificate = certfile - if keyfile: - server.ssl_private_key = keyfile - - try: - server.start() - finally: - server.stop() - - -class WaitressServer(ServerAdapter): - def run(self, handler): - from waitress import serve - serve(handler, host=self.host, port=self.port) - - -class PasteServer(ServerAdapter): - def run(self, handler): # pragma: no cover - from paste import httpserver - from paste.translogger import TransLogger - handler = TransLogger(handler, setup_console_handler=(not self.quiet)) - httpserver.serve(handler, host=self.host, port=str(self.port), - **self.options) - - -class MeinheldServer(ServerAdapter): - def run(self, handler): - from meinheld import server - server.listen((self.host, self.port)) - server.run(handler) - - -class FapwsServer(ServerAdapter): - """ Extremely fast webserver using libev. See http://www.fapws.org/ """ - def run(self, handler): # pragma: no cover - import fapws._evwsgi as evwsgi - from fapws import base, config - port = self.port - if float(config.SERVER_IDENT[-2:]) > 0.4: - # fapws3 silently changed its API in 0.5 - port = str(port) - evwsgi.start(self.host, port) - # fapws3 never releases the GIL. Complain upstream. I tried. No luck. - if 'BOTTLE_CHILD' in os.environ and not self.quiet: - _stderr("WARNING: Auto-reloading does not work with Fapws3.\n") - _stderr(" (Fapws3 breaks python thread support)\n") - evwsgi.set_base_module(base) - def app(environ, start_response): - environ['wsgi.multiprocess'] = False - return handler(environ, start_response) - evwsgi.wsgi_cb(('', app)) - evwsgi.run() - - -class TornadoServer(ServerAdapter): - """ The super hyped asynchronous server by facebook. Untested. """ - def run(self, handler): # pragma: no cover - import tornado.wsgi, tornado.httpserver, tornado.ioloop - container = tornado.wsgi.WSGIContainer(handler) - server = tornado.httpserver.HTTPServer(container) - server.listen(port=self.port,address=self.host) - tornado.ioloop.IOLoop.instance().start() - - -class AppEngineServer(ServerAdapter): - """ Adapter for Google App Engine. """ - quiet = True - def run(self, handler): - from google.appengine.ext.webapp import util - # A main() function in the handler script enables 'App Caching'. - # Lets makes sure it is there. This _really_ improves performance. - module = sys.modules.get('__main__') - if module and not hasattr(module, 'main'): - module.main = lambda: util.run_wsgi_app(handler) - util.run_wsgi_app(handler) - - -class TwistedServer(ServerAdapter): - """ Untested. """ - def run(self, handler): - from twisted.web import server, wsgi - from twisted.python.threadpool import ThreadPool - from twisted.internet import reactor - thread_pool = ThreadPool() - thread_pool.start() - reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop) - factory = server.Site(wsgi.WSGIResource(reactor, thread_pool, handler)) - reactor.listenTCP(self.port, factory, interface=self.host) - reactor.run() - - -class DieselServer(ServerAdapter): - """ Untested. """ - def run(self, handler): - from diesel.protocols.wsgi import WSGIApplication - app = WSGIApplication(handler, port=self.port) - app.run() - - -class GeventServer(ServerAdapter): - """ Untested. Options: - - * `fast` (default: False) uses libevent's http server, but has some - issues: No streaming, no pipelining, no SSL. - * See gevent.wsgi.WSGIServer() documentation for more options. - """ - def run(self, handler): - from gevent import pywsgi, local - if not isinstance(threading.local(), local.local): - msg = "Bottle requires gevent.monkey.patch_all() (before import)" - raise RuntimeError(msg) - if self.options.pop('fast', None): - depr('The "fast" option has been deprecated and removed by Gevent.') - if self.quiet: - self.options['log'] = None - address = (self.host, self.port) - server = pywsgi.WSGIServer(address, handler, **self.options) - if 'BOTTLE_CHILD' in os.environ: - import signal - signal.signal(signal.SIGINT, lambda s, f: server.stop()) - server.serve_forever() - - -class GeventSocketIOServer(ServerAdapter): - def run(self,handler): - from socketio import server - address = (self.host, self.port) - server.SocketIOServer(address, handler, **self.options).serve_forever() - - -class GunicornServer(ServerAdapter): - """ Untested. See http://gunicorn.org/configure.html for options. """ - def run(self, handler): - from gunicorn.app.base import Application - - config = {'bind': "%s:%d" % (self.host, int(self.port))} - config.update(self.options) - - class GunicornApplication(Application): - def init(self, parser, opts, args): - return config - - def load(self): - return handler - - GunicornApplication().run() - - -class EventletServer(ServerAdapter): - """ Untested """ - def run(self, handler): - from eventlet import wsgi, listen - try: - wsgi.server(listen((self.host, self.port)), handler, - log_output=(not self.quiet)) - except TypeError: - # Fallback, if we have old version of eventlet - wsgi.server(listen((self.host, self.port)), handler) - - -class RocketServer(ServerAdapter): - """ Untested. """ - def run(self, handler): - from rocket import Rocket - server = Rocket((self.host, self.port), 'wsgi', { 'wsgi_app' : handler }) - server.start() - - -class BjoernServer(ServerAdapter): - """ Fast server written in C: https://github.com/jonashaag/bjoern """ - def run(self, handler): - from bjoern import run - run(handler, self.host, self.port) - - -class AutoServer(ServerAdapter): - """ Untested. """ - adapters = [WaitressServer, PasteServer, TwistedServer, CherryPyServer, WSGIRefServer] - def run(self, handler): - for sa in self.adapters: - try: - return sa(self.host, self.port, **self.options).run(handler) - except ImportError: - pass - -server_names = { - 'cgi': CGIServer, - 'flup': FlupFCGIServer, - 'wsgiref': WSGIRefServer, - 'waitress': WaitressServer, - 'cherrypy': CherryPyServer, - 'paste': PasteServer, - 'fapws3': FapwsServer, - 'tornado': TornadoServer, - 'gae': AppEngineServer, - 'twisted': TwistedServer, - 'diesel': DieselServer, - 'meinheld': MeinheldServer, - 'gunicorn': GunicornServer, - 'eventlet': EventletServer, - 'gevent': GeventServer, - 'geventSocketIO':GeventSocketIOServer, - 'rocket': RocketServer, - 'bjoern' : BjoernServer, - 'auto': AutoServer, -} - - - - - - -############################################################################### -# Application Control ########################################################## -############################################################################### - - -def load(target, **namespace): - """ Import a module or fetch an object from a module. - - * ``package.module`` returns `module` as a module object. - * ``pack.mod:name`` returns the module variable `name` from `pack.mod`. - * ``pack.mod:func()`` calls `pack.mod.func()` and returns the result. - - The last form accepts not only function calls, but any type of - expression. Keyword arguments passed to this function are available as - local variables. Example: ``import_string('re:compile(x)', x='[a-z]')`` - """ - module, target = target.split(":", 1) if ':' in target else (target, None) - if module not in sys.modules: __import__(module) - if not target: return sys.modules[module] - if target.isalnum(): return getattr(sys.modules[module], target) - package_name = module.split('.')[0] - namespace[package_name] = sys.modules[package_name] - return eval('%s.%s' % (module, target), namespace) - - -def load_app(target): - """ Load a bottle application from a module and make sure that the import - does not affect the current default application, but returns a separate - application object. See :func:`load` for the target parameter. """ - global NORUN; NORUN, nr_old = True, NORUN - try: - tmp = default_app.push() # Create a new "default application" - rv = load(target) # Import the target module - return rv if callable(rv) else tmp - finally: - default_app.remove(tmp) # Remove the temporary added default application - NORUN = nr_old - -_debug = debug -def run(app=None, server='wsgiref', host='127.0.0.1', port=8080, - interval=1, reloader=False, quiet=False, plugins=None, - debug=None, **kargs): - """ Start a server instance. This method blocks until the server terminates. - - :param app: WSGI application or target string supported by - :func:`load_app`. (default: :func:`default_app`) - :param server: Server adapter to use. See :data:`server_names` keys - for valid names or pass a :class:`ServerAdapter` subclass. - (default: `wsgiref`) - :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on - all interfaces including the external one. (default: 127.0.0.1) - :param port: Server port to bind to. Values below 1024 require root - privileges. (default: 8080) - :param reloader: Start auto-reloading server? (default: False) - :param interval: Auto-reloader interval in seconds (default: 1) - :param quiet: Suppress output to stdout and stderr? (default: False) - :param options: Options passed to the server adapter. - """ - if NORUN: return - if reloader and not os.environ.get('BOTTLE_CHILD'): - try: - lockfile = None - fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock') - os.close(fd) # We only need this file to exist. We never write to it - while os.path.exists(lockfile): - args = [sys.executable] + sys.argv - environ = os.environ.copy() - environ['BOTTLE_CHILD'] = 'true' - environ['BOTTLE_LOCKFILE'] = lockfile - p = subprocess.Popen(args, env=environ) - while p.poll() is None: # Busy wait... - os.utime(lockfile, None) # I am alive! - time.sleep(interval) - if p.poll() != 3: - if os.path.exists(lockfile): os.unlink(lockfile) - sys.exit(p.poll()) - except KeyboardInterrupt: - pass - finally: - if os.path.exists(lockfile): - os.unlink(lockfile) - return - - try: - if debug is not None: _debug(debug) - app = app or default_app() - if isinstance(app, basestring): - app = load_app(app) - if not callable(app): - raise ValueError("Application is not callable: %r" % app) - - for plugin in plugins or []: - app.install(plugin) - - if server in server_names: - server = server_names.get(server) - if isinstance(server, basestring): - server = load(server) - if isinstance(server, type): - server = server(host=host, port=port, **kargs) - if not isinstance(server, ServerAdapter): - raise ValueError("Unknown or unsupported server: %r" % server) - - server.quiet = server.quiet or quiet - if not server.quiet: - _stderr("Bottle v%s server starting up (using %s)...\n" % (__version__, repr(server))) - _stderr("Listening on http://%s:%d/\n" % (server.host, server.port)) - _stderr("Hit Ctrl-C to quit.\n\n") - - if reloader: - lockfile = os.environ.get('BOTTLE_LOCKFILE') - bgcheck = FileCheckerThread(lockfile, interval) - with bgcheck: - server.run(app) - if bgcheck.status == 'reload': - sys.exit(3) - else: - server.run(app) - except KeyboardInterrupt: - pass - except (SystemExit, MemoryError): - raise - except: - if not reloader: raise - if not getattr(server, 'quiet', quiet): - print_exc() - time.sleep(interval) - sys.exit(3) - - - -class FileCheckerThread(threading.Thread): - ''' Interrupt main-thread as soon as a changed module file is detected, - the lockfile gets deleted or gets to old. ''' - - def __init__(self, lockfile, interval): - threading.Thread.__init__(self) - self.lockfile, self.interval = lockfile, interval - #: Is one of 'reload', 'error' or 'exit' - self.status = None - - def run(self): - exists = os.path.exists - mtime = lambda path: os.stat(path).st_mtime - files = dict() - - for module in list(sys.modules.values()): - path = getattr(module, '__file__', '') or '' - if path[-4:] in ('.pyo', '.pyc'): path = path[:-1] - if path and exists(path): files[path] = mtime(path) - - while not self.status: - if not exists(self.lockfile)\ - or mtime(self.lockfile) < time.time() - self.interval - 5: - self.status = 'error' - thread.interrupt_main() - for path, lmtime in list(files.items()): - if not exists(path) or mtime(path) > lmtime: - self.status = 'reload' - thread.interrupt_main() - break - time.sleep(self.interval) - - def __enter__(self): - self.start() - - def __exit__(self, exc_type, exc_val, exc_tb): - if not self.status: self.status = 'exit' # silent exit - self.join() - return exc_type is not None and issubclass(exc_type, KeyboardInterrupt) - - - - - -############################################################################### -# Template Adapters ############################################################ -############################################################################### - - -class TemplateError(HTTPError): - def __init__(self, message): - HTTPError.__init__(self, 500, message) - - -class BaseTemplate(object): - """ Base class and minimal API for template adapters """ - extensions = ['tpl','html','thtml','stpl'] - settings = {} #used in prepare() - defaults = {} #used in render() - - def __init__(self, source=None, name=None, lookup=[], encoding='utf8', **settings): - """ Create a new template. - If the source parameter (str or buffer) is missing, the name argument - is used to guess a template filename. Subclasses can assume that - self.source and/or self.filename are set. Both are strings. - The lookup, encoding and settings parameters are stored as instance - variables. - The lookup parameter stores a list containing directory paths. - The encoding parameter should be used to decode byte strings or files. - The settings parameter contains a dict for engine-specific settings. - """ - self.name = name - self.source = source.read() if hasattr(source, 'read') else source - self.filename = source.filename if hasattr(source, 'filename') else None - self.lookup = [os.path.abspath(x) for x in lookup] - self.encoding = encoding - self.settings = self.settings.copy() # Copy from class variable - self.settings.update(settings) # Apply - if not self.source and self.name: - self.filename = self.search(self.name, self.lookup) - if not self.filename: - raise TemplateError('Template %s not found.' % repr(name)) - if not self.source and not self.filename: - raise TemplateError('No template specified.') - self.prepare(**self.settings) - - @classmethod - def search(cls, name, lookup=[]): - """ Search name in all directories specified in lookup. - First without, then with common extensions. Return first hit. """ - if not lookup: - depr('The template lookup path list should not be empty.') #0.12 - lookup = ['.'] - - if os.path.isabs(name) and os.path.isfile(name): - depr('Absolute template path names are deprecated.') #0.12 - return os.path.abspath(name) - - for spath in lookup: - spath = os.path.abspath(spath) + os.sep - fname = os.path.abspath(os.path.join(spath, name)) - if not fname.startswith(spath): continue - if os.path.isfile(fname): return fname - for ext in cls.extensions: - if os.path.isfile('%s.%s' % (fname, ext)): - return '%s.%s' % (fname, ext) - - @classmethod - def global_config(cls, key, *args): - ''' This reads or sets the global settings stored in class.settings. ''' - if args: - cls.settings = cls.settings.copy() # Make settings local to class - cls.settings[key] = args[0] - else: - return cls.settings[key] - - def prepare(self, **options): - """ Run preparations (parsing, caching, ...). - It should be possible to call this again to refresh a template or to - update settings. - """ - raise NotImplementedError - - def render(self, *args, **kwargs): - """ Render the template with the specified local variables and return - a single byte or unicode string. If it is a byte string, the encoding - must match self.encoding. This method must be thread-safe! - Local variables may be provided in dictionaries (args) - or directly, as keywords (kwargs). - """ - raise NotImplementedError - - -class MakoTemplate(BaseTemplate): - def prepare(self, **options): - from mako.template import Template - from mako.lookup import TemplateLookup - options.update({'input_encoding':self.encoding}) - options.setdefault('format_exceptions', bool(DEBUG)) - lookup = TemplateLookup(directories=self.lookup, **options) - if self.source: - self.tpl = Template(self.source, lookup=lookup, **options) - else: - self.tpl = Template(uri=self.name, filename=self.filename, lookup=lookup, **options) - - def render(self, *args, **kwargs): - for dictarg in args: kwargs.update(dictarg) - _defaults = self.defaults.copy() - _defaults.update(kwargs) - return self.tpl.render(**_defaults) - - -class CheetahTemplate(BaseTemplate): - def prepare(self, **options): - from Cheetah.Template import Template - self.context = threading.local() - self.context.vars = {} - options['searchList'] = [self.context.vars] - if self.source: - self.tpl = Template(source=self.source, **options) - else: - self.tpl = Template(file=self.filename, **options) - - def render(self, *args, **kwargs): - for dictarg in args: kwargs.update(dictarg) - self.context.vars.update(self.defaults) - self.context.vars.update(kwargs) - out = str(self.tpl) - self.context.vars.clear() - return out - - -class Jinja2Template(BaseTemplate): - def prepare(self, filters=None, tests=None, globals={}, **kwargs): - from jinja2 import Environment, FunctionLoader - if 'prefix' in kwargs: # TODO: to be removed after a while - raise RuntimeError('The keyword argument `prefix` has been removed. ' - 'Use the full jinja2 environment name line_statement_prefix instead.') - self.env = Environment(loader=FunctionLoader(self.loader), **kwargs) - if filters: self.env.filters.update(filters) - if tests: self.env.tests.update(tests) - if globals: self.env.globals.update(globals) - if self.source: - self.tpl = self.env.from_string(self.source) - else: - self.tpl = self.env.get_template(self.filename) - - def render(self, *args, **kwargs): - for dictarg in args: kwargs.update(dictarg) - _defaults = self.defaults.copy() - _defaults.update(kwargs) - return self.tpl.render(**_defaults) - - def loader(self, name): - fname = self.search(name, self.lookup) - if not fname: return - with open(fname, "rb") as f: - return f.read().decode(self.encoding) - - -class SimpleTemplate(BaseTemplate): - - def prepare(self, escape_func=html_escape, noescape=False, syntax=None, **ka): - self.cache = {} - enc = self.encoding - self._str = lambda x: touni(x, enc) - self._escape = lambda x: escape_func(touni(x, enc)) - self.syntax = syntax - if noescape: - self._str, self._escape = self._escape, self._str - - @cached_property - def co(self): - return compile(self.code, self.filename or '', 'exec') - - @cached_property - def code(self): - source = self.source - if not source: - with open(self.filename, 'rb') as f: - source = f.read() - try: - source, encoding = touni(source), 'utf8' - except UnicodeError: - depr('Template encodings other than utf8 are no longer supported.') #0.11 - source, encoding = touni(source, 'latin1'), 'latin1' - parser = StplParser(source, encoding=encoding, syntax=self.syntax) - code = parser.translate() - self.encoding = parser.encoding - return code - - def _rebase(self, _env, _name=None, **kwargs): - if _name is None: - depr('Rebase function called without arguments.' - ' You were probably looking for {{base}}?', True) #0.12 - _env['_rebase'] = (_name, kwargs) - - def _include(self, _env, _name=None, **kwargs): - if _name is None: - depr('Rebase function called without arguments.' - ' You were probably looking for {{base}}?', True) #0.12 - env = _env.copy() - env.update(kwargs) - if _name not in self.cache: - self.cache[_name] = self.__class__(name=_name, lookup=self.lookup) - return self.cache[_name].execute(env['_stdout'], env) - - def execute(self, _stdout, kwargs): - env = self.defaults.copy() - env.update(kwargs) - env.update({'_stdout': _stdout, '_printlist': _stdout.extend, - 'include': functools.partial(self._include, env), - 'rebase': functools.partial(self._rebase, env), '_rebase': None, - '_str': self._str, '_escape': self._escape, 'get': env.get, - 'setdefault': env.setdefault, 'defined': env.__contains__ }) - eval(self.co, env) - if env.get('_rebase'): - subtpl, rargs = env.pop('_rebase') - rargs['base'] = ''.join(_stdout) #copy stdout - del _stdout[:] # clear stdout - return self._include(env, subtpl, **rargs) - return env - - def render(self, *args, **kwargs): - """ Render the template using keyword arguments as local variables. """ - env = {}; stdout = [] - for dictarg in args: env.update(dictarg) - env.update(kwargs) - self.execute(stdout, env) - return ''.join(stdout) - - -class StplSyntaxError(TemplateError): pass - - -class StplParser(object): - ''' Parser for stpl templates. ''' - _re_cache = {} #: Cache for compiled re patterns - # This huge pile of voodoo magic splits python code into 8 different tokens. - # 1: All kinds of python strings (trust me, it works) - _re_tok = '([urbURB]?(?:\'\'(?!\')|""(?!")|\'{6}|"{6}' \ - '|\'(?:[^\\\\\']|\\\\.)+?\'|"(?:[^\\\\"]|\\\\.)+?"' \ - '|\'{3}(?:[^\\\\]|\\\\.|\\n)+?\'{3}' \ - '|"{3}(?:[^\\\\]|\\\\.|\\n)+?"{3}))' - _re_inl = _re_tok.replace('|\\n','') # We re-use this string pattern later - # 2: Comments (until end of line, but not the newline itself) - _re_tok += '|(#.*)' - # 3,4: Open and close grouping tokens - _re_tok += '|([\\[\\{\\(])' - _re_tok += '|([\\]\\}\\)])' - # 5,6: Keywords that start or continue a python block (only start of line) - _re_tok += '|^([ \\t]*(?:if|for|while|with|try|def|class)\\b)' \ - '|^([ \\t]*(?:elif|else|except|finally)\\b)' - # 7: Our special 'end' keyword (but only if it stands alone) - _re_tok += '|((?:^|;)[ \\t]*end[ \\t]*(?=(?:%(block_close)s[ \\t]*)?\\r?$|;|#))' - # 8: A customizable end-of-code-block template token (only end of line) - _re_tok += '|(%(block_close)s[ \\t]*(?=\\r?$))' - # 9: And finally, a single newline. The 10th token is 'everything else' - _re_tok += '|(\\r?\\n)' - - # Match the start tokens of code areas in a template - _re_split = '(?m)^[ \t]*(\\\\?)((%(line_start)s)|(%(block_start)s))(%%?)' - # Match inline statements (may contain python strings) - _re_inl = '(?m)%%(inline_start)s((?:%s|[^\'"\n]*?)+)%%(inline_end)s' % _re_inl - _re_tok = '(?m)' + _re_tok - - default_syntax = '<% %> % {{ }}' - - def __init__(self, source, syntax=None, encoding='utf8'): - self.source, self.encoding = touni(source, encoding), encoding - self.set_syntax(syntax or self.default_syntax) - self.code_buffer, self.text_buffer = [], [] - self.lineno, self.offset = 1, 0 - self.indent, self.indent_mod = 0, 0 - self.paren_depth = 0 - - def get_syntax(self): - ''' Tokens as a space separated string (default: <% %> % {{ }}) ''' - return self._syntax - - def set_syntax(self, syntax): - self._syntax = syntax - self._tokens = syntax.split() - if not syntax in self._re_cache: - names = 'block_start block_close line_start inline_start inline_end' - etokens = map(re.escape, self._tokens) - pattern_vars = dict(zip(names.split(), etokens)) - patterns = (self._re_split, self._re_tok, self._re_inl) - patterns = [re.compile(p%pattern_vars) for p in patterns] - self._re_cache[syntax] = patterns - self.re_split, self.re_tok, self.re_inl = self._re_cache[syntax] - - syntax = property(get_syntax, set_syntax) - - def translate(self): - if self.offset: raise RuntimeError('Parser is a one time instance.') - while True: - m = self.re_split.search(self.source[self.offset:]) - if m: - text = self.source[self.offset:self.offset+m.start()] - self.text_buffer.append(text) - self.offset += m.end() - if m.group(1): # New escape syntax - line, sep, _ = self.source[self.offset:].partition('\n') - self.text_buffer.append(m.group(2)+m.group(5)+line+sep) - self.offset += len(line+sep)+1 - continue - elif m.group(5): # Old escape syntax - depr('Escape code lines with a backslash.') #0.12 - line, sep, _ = self.source[self.offset:].partition('\n') - self.text_buffer.append(m.group(2)+line+sep) - self.offset += len(line+sep)+1 - continue - self.flush_text() - self.read_code(multiline=bool(m.group(4))) - else: break - self.text_buffer.append(self.source[self.offset:]) - self.flush_text() - return ''.join(self.code_buffer) - - def read_code(self, multiline): - code_line, comment = '', '' - while True: - m = self.re_tok.search(self.source[self.offset:]) - if not m: - code_line += self.source[self.offset:] - self.offset = len(self.source) - self.write_code(code_line.strip(), comment) - return - code_line += self.source[self.offset:self.offset+m.start()] - self.offset += m.end() - _str, _com, _po, _pc, _blk1, _blk2, _end, _cend, _nl = m.groups() - if (code_line or self.paren_depth > 0) and (_blk1 or _blk2): # a if b else c - code_line += _blk1 or _blk2 - continue - if _str: # Python string - code_line += _str - elif _com: # Python comment (up to EOL) - comment = _com - if multiline and _com.strip().endswith(self._tokens[1]): - multiline = False # Allow end-of-block in comments - elif _po: # open parenthesis - self.paren_depth += 1 - code_line += _po - elif _pc: # close parenthesis - if self.paren_depth > 0: - # we could check for matching parentheses here, but it's - # easier to leave that to python - just check counts - self.paren_depth -= 1 - code_line += _pc - elif _blk1: # Start-block keyword (if/for/while/def/try/...) - code_line, self.indent_mod = _blk1, -1 - self.indent += 1 - elif _blk2: # Continue-block keyword (else/elif/except/...) - code_line, self.indent_mod = _blk2, -1 - elif _end: # The non-standard 'end'-keyword (ends a block) - self.indent -= 1 - elif _cend: # The end-code-block template token (usually '%>') - if multiline: multiline = False - else: code_line += _cend - else: # \n - self.write_code(code_line.strip(), comment) - self.lineno += 1 - code_line, comment, self.indent_mod = '', '', 0 - if not multiline: - break - - def flush_text(self): - text = ''.join(self.text_buffer) - del self.text_buffer[:] - if not text: return - parts, pos, nl = [], 0, '\\\n'+' '*self.indent - for m in self.re_inl.finditer(text): - prefix, pos = text[pos:m.start()], m.end() - if prefix: - parts.append(nl.join(map(repr, prefix.splitlines(True)))) - if prefix.endswith('\n'): parts[-1] += nl - parts.append(self.process_inline(m.group(1).strip())) - if pos < len(text): - prefix = text[pos:] - lines = prefix.splitlines(True) - if lines[-1].endswith('\\\\\n'): lines[-1] = lines[-1][:-3] - elif lines[-1].endswith('\\\\\r\n'): lines[-1] = lines[-1][:-4] - parts.append(nl.join(map(repr, lines))) - code = '_printlist((%s,))' % ', '.join(parts) - self.lineno += code.count('\n')+1 - self.write_code(code) - - def process_inline(self, chunk): - if chunk[0] == '!': return '_str(%s)' % chunk[1:] - return '_escape(%s)' % chunk - - def write_code(self, line, comment=''): - line, comment = self.fix_backward_compatibility(line, comment) - code = ' ' * (self.indent+self.indent_mod) - code += line.lstrip() + comment + '\n' - self.code_buffer.append(code) - - def fix_backward_compatibility(self, line, comment): - parts = line.strip().split(None, 2) - if parts and parts[0] in ('include', 'rebase'): - depr('The include and rebase keywords are functions now.') #0.12 - if len(parts) == 1: return "_printlist([base])", comment - elif len(parts) == 2: return "_=%s(%r)" % tuple(parts), comment - else: return "_=%s(%r, %s)" % tuple(parts), comment - if self.lineno <= 2 and not line.strip() and 'coding' in comment: - m = re.match(r"#.*coding[:=]\s*([-\w.]+)", comment) - if m: - depr('PEP263 encoding strings in templates are deprecated.') #0.12 - enc = m.group(1) - self.source = self.source.encode(self.encoding).decode(enc) - self.encoding = enc - return line, comment.replace('coding','coding*') - return line, comment - - -def template(*args, **kwargs): - ''' - Get a rendered template as a string iterator. - You can use a name, a filename or a template string as first parameter. - Template rendering arguments can be passed as dictionaries - or directly (as keyword arguments). - ''' - tpl = args[0] if args else None - adapter = kwargs.pop('template_adapter', SimpleTemplate) - lookup = kwargs.pop('template_lookup', TEMPLATE_PATH) - tplid = (id(lookup), tpl) - if tplid not in TEMPLATES or DEBUG: - settings = kwargs.pop('template_settings', {}) - if isinstance(tpl, adapter): - TEMPLATES[tplid] = tpl - if settings: TEMPLATES[tplid].prepare(**settings) - elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl: - TEMPLATES[tplid] = adapter(source=tpl, lookup=lookup, **settings) - else: - TEMPLATES[tplid] = adapter(name=tpl, lookup=lookup, **settings) - if not TEMPLATES[tplid]: - abort(500, 'Template (%s) not found' % tpl) - for dictarg in args[1:]: kwargs.update(dictarg) - return TEMPLATES[tplid].render(kwargs) - -mako_template = functools.partial(template, template_adapter=MakoTemplate) -cheetah_template = functools.partial(template, template_adapter=CheetahTemplate) -jinja2_template = functools.partial(template, template_adapter=Jinja2Template) - - -def view(tpl_name, **defaults): - ''' Decorator: renders a template for a handler. - The handler can control its behavior like that: - - - return a dict of template vars to fill out the template - - return something other than a dict and the view decorator will not - process the template, but return the handler result as is. - This includes returning a HTTPResponse(dict) to get, - for instance, JSON with autojson or other castfilters. - ''' - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - result = func(*args, **kwargs) - if isinstance(result, (dict, DictMixin)): - tplvars = defaults.copy() - tplvars.update(result) - return template(tpl_name, **tplvars) - elif result is None: - return template(tpl_name, defaults) - return result - return wrapper - return decorator - -mako_view = functools.partial(view, template_adapter=MakoTemplate) -cheetah_view = functools.partial(view, template_adapter=CheetahTemplate) -jinja2_view = functools.partial(view, template_adapter=Jinja2Template) - - - - - - -############################################################################### -# Constants and Globals ######################################################## -############################################################################### - - -TEMPLATE_PATH = ['./', './views/'] -TEMPLATES = {} -DEBUG = False -NORUN = False # If set, run() does nothing. Used by load_app() - -#: A dict to map HTTP status codes (e.g. 404) to phrases (e.g. 'Not Found') -HTTP_CODES = httplib.responses -HTTP_CODES[418] = "I'm a teapot" # RFC 2324 -HTTP_CODES[422] = "Unprocessable Entity" # RFC 4918 -HTTP_CODES[428] = "Precondition Required" -HTTP_CODES[429] = "Too Many Requests" -HTTP_CODES[431] = "Request Header Fields Too Large" -HTTP_CODES[511] = "Network Authentication Required" -_HTTP_STATUS_LINES = dict((k, '%d %s'%(k,v)) for (k,v) in HTTP_CODES.items()) - -#: The default template used for error pages. Override with @error() -ERROR_PAGE_TEMPLATE = """ -%%try: - %%from %s import DEBUG, HTTP_CODES, request, touni - - - - Error: {{e.status}} - - - -

Error: {{e.status}}

-

Sorry, the requested URL {{repr(request.url)}} - caused an error:

-
{{e.body}}
- %%if DEBUG and e.exception: -

Exception:

-
{{repr(e.exception)}}
- %%end - %%if DEBUG and e.traceback: -

Traceback:

-
{{e.traceback}}
- %%end - - -%%except ImportError: - ImportError: Could not generate the error page. Please add bottle to - the import path. -%%end -""" % __name__ - -#: A thread-safe instance of :class:`LocalRequest`. If accessed from within a -#: request callback, this instance always refers to the *current* request -#: (even on a multithreaded server). -request = LocalRequest() - -#: A thread-safe instance of :class:`LocalResponse`. It is used to change the -#: HTTP response for the *current* request. -response = LocalResponse() - -#: A thread-safe namespace. Not used by Bottle. -local = threading.local() - -# Initialize app stack (create first empty Bottle app) -# BC: 0.6.4 and needed for run() -app = default_app = AppStack() -app.push() - -#: A virtual package that redirects import statements. -#: Example: ``import bottle.ext.sqlite`` actually imports `bottle_sqlite`. -ext = _ImportRedirect('bottle.ext' if __name__ == '__main__' else __name__+".ext", 'bottle_%s').module - -if __name__ == '__main__': - opt, args, parser = _cmd_options, _cmd_args, _cmd_parser - if opt.version: - _stdout('Bottle %s\n'%__version__) - sys.exit(0) - if not args: - parser.print_help() - _stderr('\nError: No application specified.\n') - sys.exit(1) - - sys.path.insert(0, '.') - sys.modules.setdefault('bottle', sys.modules['__main__']) - - host, port = (opt.bind or 'localhost'), 8080 - if ':' in host and host.rfind(']') < host.rfind(':'): - host, port = host.rsplit(':', 1) - host = host.strip('[]') - - run(args[0], host=host, port=int(port), server=opt.server, - reloader=opt.reload, plugins=opt.plugin, debug=opt.debug) - - - - -# THE END diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/PKG-INFO b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/PKG-INFO deleted file mode 100644 index 02edb3af..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/PKG-INFO +++ /dev/null @@ -1,20 +0,0 @@ -Metadata-Version: 2.1 -Name: bottle-websocket -Version: 0.2.9 -Summary: WebSockets for bottle -Home-page: https://github.com/zeekay/bottle-websocket -Author: Zach Kelling -Author-email: zk@monoid.io -License: MIT -Keywords: bottle websockets -Platform: UNKNOWN -Classifier: Environment :: Web Environment -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content -Classifier: Topic :: Software Development :: Libraries :: Python Modules - -Easy websockets for bottle. - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/SOURCES.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/SOURCES.txt deleted file mode 100644 index 7a4d4212..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/SOURCES.txt +++ /dev/null @@ -1,10 +0,0 @@ -setup.cfg -setup.py -bottle_websocket/__init__.py -bottle_websocket/plugin.py -bottle_websocket/server.py -bottle_websocket.egg-info/PKG-INFO -bottle_websocket.egg-info/SOURCES.txt -bottle_websocket.egg-info/dependency_links.txt -bottle_websocket.egg-info/requires.txt -bottle_websocket.egg-info/top_level.txt \ No newline at end of file diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/dependency_links.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/installed-files.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/installed-files.txt deleted file mode 100644 index 0111411b..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/installed-files.txt +++ /dev/null @@ -1,11 +0,0 @@ -../bottle_websocket/__init__.py -../bottle_websocket/__pycache__/__init__.cpython-39.pyc -../bottle_websocket/__pycache__/plugin.cpython-39.pyc -../bottle_websocket/__pycache__/server.cpython-39.pyc -../bottle_websocket/plugin.py -../bottle_websocket/server.py -PKG-INFO -SOURCES.txt -dependency_links.txt -requires.txt -top_level.txt diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/requires.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/requires.txt deleted file mode 100644 index fbc97e26..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/requires.txt +++ /dev/null @@ -1,2 +0,0 @@ -bottle -gevent-websocket diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/top_level.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/top_level.txt deleted file mode 100644 index 82fc1a88..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket-0.2.9-py3.9.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -bottle_websocket diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/__init__.py deleted file mode 100644 index 0dcfbd4a..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .plugin import websocket -from .server import GeventWebSocketServer - -__all__ = ['websocket', 'GeventWebSocketServer'] -__version__ = '0.2.9' diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 8559865017c92b5adb6a58348ec791dac4b6f516..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 344 zcmYjM!Ait15N+DscGcaZ|6mU`qNfEB1w};=p@`7SkTyd%ZrYS2TUmd=t0(`#U+UG9 zzu-la6~uvgGcWUICc|d4?ZDZ~+xPveiyxl+x6qMO=Vs3V4>*xQLkdEC9?GzZipb>= zAJt(o;^Sh><2*={PvYb$8MdCVG^$#nb!7Jvdu;6kmigu>N8O{=eua)4JdY2u!)$(= z#zox8RV~_0ZHUdU&})VSfJq53>;SlKBUB6U9_YkR@$mIX8{~q~E2jCm7Cq9t^}?!_ z=G8(eNw04%&cIZfb*O3Pe$-PbO4_aMLbXTPoEl+qzhl*s)o5s`td$6Vhfn~~3JZ{R W>tS--D87<-;_o%FgDIJY6Y>LZ0A0lZ diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/__pycache__/plugin.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/__pycache__/plugin.cpython-39.pyc deleted file mode 100644 index 31dfa48c08738f6b0be873ee5558da49247cda76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 548 zcmYk3K}#bs6vyAobZyltdlWCi-sZ3qVNVMp3JNO=MJ#*irA#MrI@@$ICexOdE_*4g zC%=K9;-z;#O|G8&3VYg@v6OxA-b-HI`|(c_@9b;=+2QZ|A1{<&{pNpTv^gQSdlUo+ zR4~L2A_yje1$3CGwITfQMyC*>s~dSUlD16H7gpvO!OHDNTKs`)6e!rD@eAij^Fb?F z^;XFb)W{FW?I#MiP{L1qU;-a8#W_6T6=X2SXT0-LfDo)T+nv}nizc#Xbuy4;7#I(7 zb(xPdqjMLeRAjCeo7B3E!Nlh!x<(Q!)r*tC68`r2P>L+(-(n-qOg5JMa@sF-&f7^} zYsD|VpPzPZV&Xy?9*v32Ro3IhwCwBrOZ1i7tdyS%g2kz{yr;`j$?jSbFVrZ_@~D`) z`h*}v@%OY|C*W*<-54DyO|tB9v}!Y()Dbe%pk%gv@C8%%!T+G!T+jNawjK-E3`~=9 hD-7JmyO!!ns^UAKqEw-xG>(Q^jFdd~|5wRsvVZHHf5iX* diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/__pycache__/server.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/__pycache__/server.cpython-39.pyc deleted file mode 100644 index 89b3219538964d025015d0ea5efd53e49f7f2268..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 972 zcmZuw&2G~`5Z+xowqs&c2`bvB?hzM2M%gXU?6Q^rC-QA>06~v*v z@(4)nkyqL)CtiUQvv#N=m9b_#~4a1)JhS@D@}bg5ii` ziKx31iAzEWcD^UQ(x-mo`?6C8G-&*e49kc{80{e*@bDV(P>?H+b~zrRe)JnEqN6^x z$xx_;P&+)E8KK}oFk4>eaRJ`ei5Ly*{7jhrtm3lCCb<%s5lq(Ou~50IvMDe{i}*1( z0#VEnB^+~dji|>x=@T`VLfo24m0^uh5i$dZ%QCjnDCS z@2iVDde;P;T;UaA;fhSdo5*yh@h!%vi5{5lP45=9@lA4z+YIF(of5YsjGCDPwHBx2OmG|FrBMxCR93r%`Z|}jMCZCOzP_8;8m&% zBc9K){45&_osMc_q+oX|NZW3M+0uGyUUi|R?twx0DG6|lAL1=?`Ru=x4&ePGAUtBM z%1XhQjTtLzK8O0u_82>#XR^(xZK&AHvtisBQURC7w3!4PTp>qfZOUfy4$tO9KS diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/plugin.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/plugin.py deleted file mode 100644 index 5c541833..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/plugin.py +++ /dev/null @@ -1,7 +0,0 @@ -from bottle import request - -def websocket(callback): - def wrapper(*args, **kwargs): - callback(request.environ.get('wsgi.websocket'), *args, **kwargs) - - return wrapper diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/server.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/server.py deleted file mode 100644 index 6dd135aa..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bottle_websocket/server.py +++ /dev/null @@ -1,17 +0,0 @@ -import logging -from bottle import ServerAdapter -from gevent import pywsgi -from geventwebsocket.handler import WebSocketHandler -from geventwebsocket.logging import create_logger - - -class GeventWebSocketServer(ServerAdapter): - def run(self, handler): - server = pywsgi.WSGIServer((self.host, self.port), handler, handler_class=WebSocketHandler) - - if not self.quiet: - server.logger = create_logger('geventwebsocket.logging') - server.logger.setLevel(logging.INFO) - server.logger.addHandler(logging.StreamHandler()) - - server.serve_forever() diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/PKG-INFO b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/PKG-INFO deleted file mode 100644 index 805d1d3b..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/PKG-INFO +++ /dev/null @@ -1,23 +0,0 @@ -Metadata-Version: 2.1 -Name: bs4 -Version: 0.0.1 -Summary: Screen-scraping library -Home-page: https://pypi.python.org/pypi/beautifulsoup4 -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 -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 - -Use `beautifulsoup4 `_ instead. - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/SOURCES.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/SOURCES.txt deleted file mode 100644 index b46d1c5e..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/SOURCES.txt +++ /dev/null @@ -1,7 +0,0 @@ -setup.cfg -setup.py -bs4.egg-info/PKG-INFO -bs4.egg-info/SOURCES.txt -bs4.egg-info/dependency_links.txt -bs4.egg-info/requires.txt -bs4.egg-info/top_level.txt \ No newline at end of file diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/dependency_links.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/installed-files.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/installed-files.txt deleted file mode 100644 index 62cfadb1..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/installed-files.txt +++ /dev/null @@ -1,5 +0,0 @@ -PKG-INFO -SOURCES.txt -dependency_links.txt -requires.txt -top_level.txt diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/requires.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/requires.txt deleted file mode 100644 index c1f5f713..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/requires.txt +++ /dev/null @@ -1 +0,0 @@ -beautifulsoup4 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/top_level.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/top_level.txt deleted file mode 100644 index 8b137891..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4-0.0.1-py3.9.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__init__.py deleted file mode 100644 index 2a436d34..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__init__.py +++ /dev/null @@ -1,804 +0,0 @@ -"""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 3.5 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.10.0" -__copyright__ = "Copyright (c) 2004-2021 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 - -# The very first thing we do is give a useful error if someone is -# running this code under Python 2. -if sys.version_info.major < 3: - raise ImportError('You are trying to use a Python 3-specific version of Beautiful Soup under Python 2. This will not work. The final version of Beautiful Soup to support Python 2 was 4.9.3.') - -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, - ) - -# 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 - is_directory = False - try: - is_file = os.path.exists(possible_filename) - if is_file: - is_directory = os.path.isdir(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_directory: - warnings.warn( - '"%s" looks like a directory name, not markup. You may' - ' want to open a file found in this directory and pass' - ' the filehandle into Beautiful Soup.' % ( - self._decode_markup(markup) - ), - MarkupResemblesLocatorWarning - ) - elif 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 and container is NavigableString: - 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. - """ - 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 - - containerClass = self.string_container(containerClass) - 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/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 0d221e12641e51ada47b80c2179abc4608311c36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23198 zcmc(HTaX;rd0uzVb!RVF+!l)q$>#F1I}k7nf*?gf-~|L0BqVZ)V3#5_mNJ-~?%nOh zTo$K$0PNJPr7~!XPDBQ>Wk+@#XOSr7Fjpmx6Q$y+I4Q@Ks<>Qv$wT@nRZdmRs^lqE zsR~OX@_qm5?zt==kxyP=PIsR^eJ=n1pa0%n9Um_k_$&Muzk2UmhVkdT=>JRO;uZY- zubGD77-hpT%cf(MEhkk@Iq7oR$*h^>jFWY8^=uTbGwO`hCmVaqdmH=8`_R{@GhW}{I8Z(y=M(jV zjj8ff<52lfW4b)uI9xv5I8r{+I9fj1I95K^c%=MD~SXFGn`5HsXM9i)41O2?8Egwxt_)KekX(LjQa%c&$)BuB7VnF zf5172`h)Hq>c4>NDd!Nb54k6BeG=Ex&S6|1c1yUPcTYJ-u3L9abv0hSJBRf?bH^;7 zcAs@m@0_`3+%d7H{E|^TTK%eJ7_Yk3cHphH>-JKsy=gDhz1yC$t4+sVZZ*A{ebSy; zUU%)KnsVLdNgS%1uA24jOUiSb&P=ILSPz2Dv-9(}Zrv)?RJ+mGD%DzzdB3$9+^Q;f zJ{f@h&ld{GwH~nTyS`nuH|y=SwdzXUwcma74ZEf6*OzhJtSaACcF@vicC|3K+46mF zrM_i*&70M_=M-(HRcklgW`G(rtl9x)YAbhBxjt&Ef!Au5?7>kBo2qrwb1-tl4c1!@ zW`G4>*}^&3>^WYm;oHG_HLx4i>#pquwp;bLIFV-crngoN+!F=gt*YAk33;^9a=caC z2c%@qG6}$Da9I0ROI`QvTVAjp4Q4-EdQLiMZ}_d^LT!~&^i8w+iF&oV_)@Z>p1$Yraf2Jb?O!0U0bcT z>p`id){33%nbI?-N~b!SJ<^q8a8cwAxs&TKqVLiq1d$I=}$x10J@#<{F^@LIZJV9NW*H^f%p_w+dtfRz66? zZmkYM$8I+rz%hE_bje;`2jp*g^}5|`X;iV!wO74nwf-~u!Qg&-bF-y_q%&a0x6hQm zRC>1bF*ux2w8F`iwpYi173HpZKIR=BxFY0#)!lGwK;@fNb-lfbwT{0HB5gVD#cHGB z1;ssK=EB8lP$di++z8>Jiwl>|zx~E?<*m1uue`lnSy;UA*2T+1j}LW zVyhNxZMxx}MWJbIzNJ6`TH)xG>YBSyccrs~S5&L!V$(I(E`#b+yT%*9V@L+#t+?VR)NCSXc_S>aM@;qHc_M!$?)H>8dcbTwMz% zmfgl?9lJ_Tphl$j{}Dv5;O9@Ga1Dq81ERo$D6qiDQsLBVZOqVh&IcM~?^IP2^X+^{ z^neTT0^7!KV?*3pcbh#-3)5Q%GJvEtc7prZ^$FuC*!UyqoIGiRX{>-nZP_bst;)J) z#Fx0)d^@5j4B<6}6VzG}yjrRx{VaZz4)c{tv)XVgm9S8$fNiwvJRhr6ZnUfQ=uWOu z!G@|-J~Gr}Sg;hId~?3xI$m}Db!@DQ%Dd^#U)fp@TFv>T+Ip*1pTGRoh4U3(^XK`} zO}BY-9?W8XQ<&Pbr7z9F^M$d)SjE$~~#POZDr5B9s``PU<~s3S3yd+=5^>mEn( z3VtzEc)bfMIaKn`&x1|}sS}C{)Ri?0s!FDub+YB0^MW(t6y7tTs6gp>&w!#b?o8lV za7VS00AW3bb5Rg*J`UC30PasX2c0P#_c(bc??Hq+`PGbb$eBi8lg^i%!_E<$?RCyN zN1bCh+vhyuJc{=FoyQy-#{^$W>jh2U;bIzeYBi+8J`P811579y=`y7V z_<4Qn1V#fXf?9h=m#qlvn`E#prmgK#4z+V-pydDq<=l#TiBA+yXfhc> zxMuVb9GxHK)fa z`ZGl`1@NY}uce{qH)qkOXefZy31AN9kcOvfQyU>81BnTv1YLt4+PdQtWPeatst|~p ziv;#fPf^U;&>=k4YElh6K^ur$POH(qw$b{D-W7MfdeiOoQ{Mt+mr1M~S*$71n@Eh9 zPfsu7tmeEu6k^0wBpNZl(oJvP?TSiq$J$PJGn>DQEBGF8mFZ^G9|l&}3{rP2oN<18rQdzH zm)rc~AbrQ^8kI~pwO#01+oNck3bJVXKTtE~?AuN?|7tho>}wnFFuaPpmh|*#*IF~T zai#D0Q$en4-7%g0;)yucH8%3wRxq-GK0h?J$GYe#>S2`Ljh(SBpfqLB4-@rnspq?6 ztA=O1Z~V~w(A-XS3wJI5Qg^hQ+89T#@%h9}L(a{+mb!#{doZu>I#b)@&9rl9dmJ;K z=u8BYJA0k!d*=4UJ4W+B+6eaDHMjniY4G3Ijiz?Hswsu~FyjbB_M9b=ZhnT?*3O zz1>O9w>$0}`yjo&KQJ~-z~XRs|CA9Np>p;?$~2mj?_&+>l3?R8VxyY^4vum)IN~SA zV-ap1>84TpC~A-HJk~YXKCru{W8VQ()OWk)&f}Py+5Er2fe9Que%-vAQokfvpOH2{ za%Q^bP0ZT=lb9CR!g)e!^!FFR6B|!%JcYJ@fu5gso&*#QG}B#_&Qse5@tf+* zu+7eF_W>fm42c%|BYNi15rvdZg-PG1O)7U;_8lACs0C(W#&@Ucp7WDCy0W3`6 z*>4COp6(hu$Gaw}BKX2R3s5>HJ>QnQv%`0BeS)n`W6ju}#;gt#J{u>yW;fH#I&&YS zE*jrl{{|sf!fzhGQ}{iD=T6@P6?CR?_AJkw;`ZTpaJ-wok;TkUVdiJyH%#E~AnhP$a7ztcT@H|?Krjyqp?&*)BH1YK_*3C>WR2Bm{u4hf|@`#2kx8%Hb8 zJNuw8m#TY54C6vS%Mqbwl>cm72q#N6?$ebMzjB@!yGi`j9+@};BXc?t$65=)d57=tXB{F=`}V#f}b z=<&H3I=5!D;Fc*!#Yw5OXDDz>y6KF_D5;_UYo;h7ON%2tOm7wxq8v{{Dq1Y31_8=0 z(UF^|oWq3Vb)gB~xn^v^d0u+g-uo-wz z0`0k$>aA1Xfcb((01z}~$U3(w@|4!KkQZb!oWd}80F0Jam1LRMVw$*CvgvATHKFt* z76jCBk?>jrnT1Y}XyNEb+c(lpt?dU;*ZbzadyPT^6%N2<_WO=X_FEhl4FJwaRftu# z-a4TV*Vbd*tN(HB$^!vW%EK9~68eQQ0f@jS-s?qdI8KEXTj)^LcQ$+gG$^g!Mt=19pa})$1cdafWf6ThIX40A_6nk&NR-L&=UOuzLmW?OMI< zxXB`XuA&adCg-ZT7iSSKr#JPuOJDrf&|QUJ$t+5{7Qnp;qXNn=o!q@n9x_eQi}{SW z#0OW8)o2G`KNR$^yU-IEBo*0Zt}G1;5mi%df%j}~5^_oZq`KbRqKCBeY8fUC_xq3w z7QTm;t=06nIi@|UO>&_X;rALgeOsKqN4<;b(gdz#_hSYPV@~#JqzG z;N(y>G`HSLFnkpFfO~|leQg4G)RvQ05k+5zy1xcUFJ?g+m*o427YyTNNocS{6x25@qX6I-Q5f@uDL?~J`0gm1@ytO8RG!VJ*R=0a+ z=Y^w)>w=7vF{P~sERBW@7^rZL0SF~aB&7|)3*I*Ptv9QTSz%V%%IRacy-D;NJKDTt z>BU03x0`Y<= zg8BBqRNjLT={j406bn4p@4Wx&(i!Y}OpS3EAOM<0Ak;q8G8sHx_e0@Sf@ekdj%Tp4 zI$Ud{P8nFkEeg8WjBLUEhNw}gJg(J@H?dqe)jPlb(1{JS`#jTH#vTqB(2PVqgMVBx zMHZu_+_EH=rpp*jRk#mzL@*(l6Xxh+@Zhhs;mq=vTb;l5&^ZpY`#f`8;sEvy8R2t* zZx6jV7qk-xyvFbR;Ng=SZ25Vn_gO&Lb9$0&hn-8nbk_6fSL%HKp)=GCKF`FK5b~xk zhyn>D507Q!nV6+!JS(`BFgW?i;7w}Dx&fTXrJB{}mPp||gWrDq{8K2pFr(p?I3dOp zOwg{mYE6mh_&)BzWVLvF(dZ(U<`^(Uti{e71lr6~{>*1z58R>jK7;GQs|iI{m<_rp z%^IOmGiB-BayH$50|t)%ZPvVBQwluX`!PrHr5dCcNowYS7s$<*nh?q z9zbE3c{6QVX7;{yKb^{(SzL9V9oX!pB-V9~L87|H^ zG4khyNGA)&d6j54o%+{-#3-x4v14ALx4mc`7!uAxopu0mrCrH_;(?AtomLX3NIFjx z)mVfqDk8bFftYvQq4I;+1p6Fd6yjzLfX0||gwB+E15r+hg0nVIV_$4(^Ct5Bizb0s zKopWu&{0+tn99qam?y#%NUfYEL8>f?;B1Uh4PT{zn3 z)C;o$Q5BAA*ofEjW701I+XXn6YuD@WE7v>!_N%)jdnCLD@H!yu;_yQZCr0P7ue4eY z>D!RdA++)#LE3MC^mnNJsMUenTG@ixM2~OMkBy6K5sSW73eyUP)W>ie82<-93a zxiH-hR$sW(`A~M%K={(}K-mbBf{SC{XgR5&qm3(?)6C`&oe&`ephrBStMHRY!DdK> zUN2-9oSBG3w$$Ejtv4jl6K$x}xwU&fG2(X5BnlHGf$$b*Bc`5A=<^H=bno3|BP`bl zuK15s!9=hn{A8c6flqyNY}*XL!|s^hvu>o9bZ{J_E*p7U=kFI-(bzr0Z4^->6FS%HKCQnvhXqykU< zD%@9Kr(a_LZ9%D)3P)BeYjq@4)P1mu3Ry(6MV`VLBO@Y(hr&#~bqhHnd3a-d^0V;h z;#-v~=dUhdo>Ah-!o|uv=dUgzyG2qK!d$cq%OkY$nlg=15idS+A6#FZz_QAFnE2o! zf2@NLAg~|iAiWs;Q!jAUnUyVQOQF?*ykho1cqB^T@hfeedn2rm+tm-Vf<70=+aNMO z%y>TbVmPu8BQBh%IB+V#7}gXdSq0&NN^RY(U61N2fLL4AA%Nh#g9;i|hgtndxVIJ2 zLn0Lrz?Wnm*QwC4)vJFDYAbCcADKk(F4ZLZiNRvEHOxSPBycMXkt&8X!YoWy069z( z7{#MFLf=v@Z{=h8q29%%fKQ#|*^4X|SR7~Z1r+7UWSTJ=dJ~WPDUV0@wE3)_g=28nedm#02|>t|a|i*-z7XJdLX{%!>ak_GV6+ zlj$iOah=Nl)XG?&Wb<;xS*EO?q_bQFSBGam-G{S0o}?Z&hGz@r5sW**zV4^9S+g_s zYMf^}z-)K%dupy12zBx^@Oe1P;hZ-excA{Iw;Y7jK1glzJk>QT_%Z*h^DdJJAr=P) zu_JFdtj)-fqi#=)D0W~`pjKm7x)xsA!3UGKx`r)O`XmFXee#8(rHP+(Ureh@IH(E> z*%GU)wbXSS!gP)4j7ip3LQkx&RJtG~UoxKwo%`8*{sEBDO?N>ih3*F+vjG{n4agvH zW2hNIXCvj$IFOBwc@Cv$@-jnA`66e##DY^Uk8FBSwCLTBCvG~!vqnxDcrc_%03GjF zIdh4AS_R-2^FA^FY>3ZZ<6ZU$rMS%9#tEf&U?QwIji8cX^q#eCNmvO%M8W6pEi`?4 z7_MpVca2uBuw+KlJ-F-PKUfu40aAP;he&K;lz7tTe_DR);#+59*NiWBVX8ziXlgPD zn2Se>KAh91o;fAzI-}i^lnNI^;-7$_%|zRtZ$c@oC(K2BwwO{}r1}L`W;M%4a$>4h z_cbfc>AGP^s~R4-n*cb6Eo#AlIW`2~0U0s?VuCk|UkX3pK@q^@6A&M85Seu>L~f_0 z&H~uyoD}NPs6!V0k`d_iEo9Qam+z(+uexKZYh4(HH*pNoI~gaBEtq{DCL`i;xqFDm z@wwti=i4I5Pl|fUy`__>xvP-e3B|kT5rePUgXPqaRuaV08GH$7qf9>J?De7UAyKa$ z{6)LAi?bDfbTU%i$?CerUE7MVqpb0#Q9!+%+(s0C2PAsW{2CPa z8F=3-=?Ak;ub1iR*Cw1+jLkQjFRBU2EpcaY5+Bj?uKCK5u_OaBCjRj;D1^V|?Z zA0YoriFKMh-}!gDHFSch$LzFU6I+DtFeJ^qzPx+|X=}*Ohl`hX>9E>PXTxAS?Tn6$ zL&;FWZXV>7$nKz(N&bo9>7t}lf@VHLAN?#fS*A8IiuvBQ&{}B<8>y$Y3i*pp1U|I| zKov(tj;0Z1625p*s4iaXmshXRZi0*TRLIkPB=7!k%ENk_GrjVx+&*Hh{R)aF@x z!*+xrcfNHwN-#`zOOkI4ds4YKy;j?21`*(|xgYYEU{31HL0dvd$*$3fGIV7>B#sct zK!grsuk&JYL`$tTtOXfOQ~_O{=DjuNp%cbC?_8w{4ARA}iPVHh7hs$e zHXYssX|Ju*_f1*Rls78G?t-7Cxs^auW2gsR@r*1MC)6QA<^~H<5x#@7FuU$Kj@wiM z`LPJf6=uSzU*`RMP+eQ12Skx6g_E?dE2yhP331^gpbuDWw4!cKQZuX%_##rOYC>{dAdypMFhdoy z1s42Pjd5$_>@#IhkTpO=5$bkJ3{ve`dViAq1G25C;~usm`66Z2{z6SbRz7XL6L~|fDHuP zB!aA8joAeS*pNuZr$w(J!81EzRfwabD?ueFn@+TW!R90csDShO7Q7v?gNymRy4OU| zJY-auhkDS&-evGgvH*pOpyOTZ&F>m>v!nS8%p;nc{eb!G8aRUJS$ns~lF^w#A<)b4Te5R-VC$8lmIHbycCk3@goD3=i>|qu z4G>_!#s?d~#KXJ?%&9#%^sWAH z@RF+!_YQMCUl9^e#tD^jfypb{(-4j%oN;*Jlf#|-FG64+IzVheTku)f&9r7yoks^D zG5lzaV2J<7$$2dxVSf-sh_oPCE(IzoB^hXJCX0?P^)pBhjL%{uTy> zVntwtCVXvifM5MKYKGxZAX@c;enJRv`bN57cBTi{+-o)o+Td%rd?>@|t{GT@Qq6D2E$q>f+9!j8k9M)%N% zC6>S9Vk08rk5K6<_7J5y_y9Mih;)gY^kCLGGcahZLSbx<9KXp2o{E@sMRz?{EG>Ry zhWp_iy&S)9;5uR*eH1$enEo&f?(uVG)YT##V_Inn_rY_cy>&!?7^pToGgLY z8AK@^^95`q_%*;T)a%e9aCDGxd)>kn&J&e9qgLf^Cc3+dyP4#!p>E^a;5{miIL}f8 z>3nHeE$_Y5Q=z)sg3Q$~@>JzD1W*^>K0rQ)sC+S(`YzTIrtpC9AoT%(_aO`6SJd)i zdPsrI4#8EukNbNFPSM5SXc;pP;9r41IE=>3&VfO)(2Do~560s?9?TA9xn|phy9Emu zJ}kMixJrTRCg%}Iz_X#G0c5aVuHcyEwICq%MU4P`Kev$wiZ;+TT!|f?!MGU`j*-GWfEb!Ki6q#;1ps8I-*HSD zIU8`Uh+wq*MY?Oc7E+d{kibt=q~M6L;j~G?jC|pS-A$=QC*8FG2Q zeCORa<@NZ%H{1rop@JuJQu^VbpCZ@=dj{6)8s8d8xO1xpbEnS=RZju{%a=m4pt?AV z%%)NG*Lm?(7Th)A*!iUkmoFpKbN&JXQjFXviWWgLf>`ctykV?g!xu4!D2crESAkkt z(bifqtJQ#sMD$8sL_CK!(%6Fd(YLPM3_OFI zA(c(RY`Sk*1$f5c-AC;(K}5E7P2?&2!XJ{ojU0Z$1b=`Nyg^|?BborBs53zu!md4r zcQ4XSGAeUB?U-p}JA+?#I~Ql~t9Q9Wfjn~~vyH6(jqG-|i4?-9vq0ru@7S?&==MneF=L&AP&#+{bvM>ZsQH35v@7@h=VaP0n;e&f6$!%fL;6x7XOmP@1sCm z%DX*GF#i-c-Xy`0z704&7<|dX@if+NRU-hjkNi9ZFYjZ69DNr75AYC@OTt6`5Eno& zP{bS_IpYf`&?|v*aFB=qOeY00dY9T}1w^%x;)_61_Xfy-B=&}r>Sk~cuisQsI8RmZ z)-uXWki`o?Ik=2(<2g#s)K1=kT@OM`b<-7mj0Gk70KsL^qoKZkUZFxhwB48{mgnAqdp(X?^S1y8f@e(j6 zSp6}p1TTW21UWq-Do<9h;R8fqeNdSLXCjOVQHCwI9%fcO( zXfn*gJp*UxFsA=CM)rtlIy?->1cvv3Rp51!7)L-Zn?fKAAvmxMPy&N>48&QxJ^KSh zxjeum=vyJv5GFxZDY^}{o(XPExcAcm<%jU^0C;q)X}o6Ur0>9$|IM!eGvxZf5xD*< zkRQ~3tZo)`0#?z@ZDi3?6D%PIcO}nK6zOzXZo}Ee*82R$i7s+2ika$vhgEQ;wW)JU zXeEn_i7yGzeOreFv1+Cweog+FWzB zdL_;XTHK0?yy&p~9triEs6}YLZ>8k}eJr-VMg1CBin71J&O5I9XLu+yhc_KkNzhhw z@$Yf#9YZT)58}}HM2}|OArzB zQskpJ58ncdAj^0k$_|9rQ2usNIa`XuIi~Q~xAmaM#NV{rdAZy9H8fn$`}J8ohXek> zKAN`wa(sp2t?c79!oz@u$lakoA>hl)2l9f0<}yS1V+hM3xf~oJSCXxw<;An7Oac*KFalDvr#^J4L6i&vHjZB^*+4|=_x*wS7Q zTE`elKA`}qIzR(jnOEP#PC_7rD)f-%uKqFVpnS{sv*>Q@^XpOX(;*WY%j%!A5cy6U zY=E!)25*tMsK3eL*I3ZGsr~_rK?^S2Gh|=>KH109XlU%qTUvP^6Xl)O9Ax!Rvq|Rk004)BC)4&(PJmD^uQ7%fUec*AO76^j87oo9VzCm{H5*bMFhJA zi+_a0L~d1WV+-_$AU!@n75|NJCNzv3jG`f@)egezl;kmPo1JN2d_BOd8Xh5;2ZT8yf9Bg&9Co!b8en?BChi-rXO#5>+p4xrtTM*;TuzpUC&Zjem?wNMWl zXKqWlD(HJ<5<|)t_{iR3)}_C8#}sQ??z@yK`78;ron%C6S&}5#>KDJ5Gf#?0zB9zt z#eaQs<-1~15C-g{(Idac?YkCX)9f@v#YirZau+Wn{(1Uy6Hi z=9$td)W7^vK}?%sM!gAIQ2S88bZz51i}DF2bsH6t(NZ3RewhHZym!}Hkygt*cYXq< zJRUEZzK}rFGrDdG^@=3f!(}~E(Xo+AMYBwsGvI*#lLRgb)+k&YWUijDW})J$iE^}XXL+y3u|#1Ke>Ed@dFx)L#RhsJjy~8&DVMMk666JLc-_) z&u*d!MZ?#Tttruwd9j{Vt0iviSQf-e*BUqWKw(bwxW){Sk|Q&*DF@ z_+u7S2?Y}2cmyNFH0v>(gf;vpHnCZd6TF6>e-#BJ$K&ZVAO^J*DGH|XY2jPOw~Uee zcs^&Dg)#h&;(QP6!uy3og~>uLkKD@qM1G<$Q#h8NE6m`T`-P_qg?w6)IzO$U*MUC@ z8UFb;ivCZI$+tjYn7@}Vr{z;z%+yMU`+6)2pNw;36_V+#jR zr2)r%EZp{RY&yjd)CZS3C%GDqP%Ox{dC5lO0D?|#p{9V$B7_9{BwGyMA7o%X~`kDxfE z0tce-#4wgjvWAO+Kd^S=75pCdVZ_m;ptX5Lzqa04NHWrOex7`?Z0PM4u(j_0)SA3IrA@>}2{sV^N&V;l4$sWPZ#MZy-Y$Z$CXJoQt1x#Fuo}TM92P zj8-bqXGL*A_Q9lJrjzP>_)3(E@F6bnrAKJetgW^15jlB<+?Vtp^$m<0S_+>u<;e)Y z`R4M)X(8TlEbiZHuC~HVqq>1tDn=1}#8*Zo?x>Y$Y8sN^kB<+^*FqJ&&dBxBU(`|s z-XCQ##$ues1Pg9(Ma@g?WkH#w_Om#^;vkDD7NW0H%MC~0MnLo({jE)T6+=?`gfo_{ zieJviixzsXt?*IOhPuLnAeKD>u@^wwRU4=XGom<@^CI!^b=|&wl5EH@e@=fx^Cj*C y|5X&}NlGa4-fU`2WR|4Ar*UpgLTX*yKbe`#O_C$)e+z6qIXao1MCv_V`~M%hV-{8b diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/dammit.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/dammit.cpython-39.pyc deleted file mode 100644 index 89724168fa59891f3a7934080cce09d40e93fc41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71384 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/diagnose.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/diagnose.cpython-39.pyc deleted file mode 100644 index bc5d137681f7b6f82f416ccee1608971d966aea5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8475 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 UW>H6uyHG6Xg{gv7m?)_K3p~wW&;S4c diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/element.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/element.cpython-39.pyc deleted file mode 100644 index 83c587624852f9895fe06edc46e05fa13c2ec65b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65446 zcmeIb3z!_&bskt<{hXe`U@#aw2trf=B7hkXXz(S80znYK03acfLl`p@#TKbXW2y$| zK~MLfst3Sy8kQnT7Gz6~>^#=-I?BM7vu1X`tfM&bdN;8ZB^!I~tgZOqyP#)BElORt_xS>;U1YHydPS^e}S%T1Rv zAIda}>hFWrgBD7p>$!4H{^rYh`CBL#+9Pr{R31XD4@<2-6xO-}Zx4BewW6E3mEu>| zsW)36UK%NnENv)nSQ;&lrYtvC-st5Q(dK(BH-9-(-sI)h#@)iLRC%+U4dHCUExN-W zx5`_*trsnK() zdb2x$>j}BuiR&%yR$OoO-ht=$;d-099oO5v?cRN_X3FoxoqOCJxU<81CrUWDzSrG} z>z$s1>s`2hhkGBc@007@xPGVW;M(za)rDFeq6uHy&u>2%k>^y?{W9y zdaqpX#q~aS64#URy?wZza`)qUzg$n^`T_R+>RH|E;uGZ_-3w3X%eQNHK=LF4_<)G$W z^_=DE{N?IJ/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 Y9m|g8f-B8Rl@gtJPYv9LG-MA_=BIP zTyitRmAtA96i|Q^1=8P;6utP+Q~pEGJ@eXAj=cs*-RB*0NlJFmOInl*&JKt3@qRz= zOR>D%vT(KjuEjrJw5)&7L-nfT;b*w@-_ZD$&q8a=dW>1`S-$PpKDGRsWGA(rjc3QN zm0e&ci2YxyAQQ>%B*zvM6Z z%b&8|l7HS`!N{_I!G8|_=lzTR^Y~x!7eUtLbn#BC#v)0i5*E(ACAFu5zSLZBohXsW z;fyCSpK8euM>68^G?`43B#4HXF%b&miIQmIYz1C)S}l%iCmu_hk!T(dcrprfxuZw# z1}Yw7Tyj5vEFzC&!TT~flrl0qyuW+z?pv#z{%)>v5&8V1dv`nhL#$CCP)Y8^(I6O3 zmFS0(_dzLk1F0$}q2Mi>JrP<%32 z);c0^%sml7hB_k!gvXK0q$wfziITn?1QE`Jt{{1^sd<$tC=jy3Rw9$=SnOw-i{v3M zq{HL>p7fHnOm7%ABIkbW8HwmM!vlYfUmYdm@G8Hay@xDd$y`@VnS)V2l#x_{cV?3g zzXoE(G)&s3w!8Ts_cQu0seuGDagdP}*P#qU;$D+w#gil$2Sy-VHD0dLQFh>1 zsQu}rbO7?1Qg~0RGK)v2xHMY)h{RqlEO@y3UlNpY?WauSelWRTXhOwwf%zI*HToy}j~-Q8(BsiS2$NSmREU~@y6 z)}ibv{`L-<`}5?V=l$^*TCfYMSVgyBc4@sNF*+Vgq9uG(4zN64Wo|FzNBa8xQ-Tx z2ehh*8<6k2@aaTNy(FH8c~aaOkVcI8TtI7?59v`n4Sn*Wk~;E-V8Cbb)I@@uLuj+= zVl*l9B!fHUYU35pT%N_dQ|?zGEDM%yHJGgNO+J|BxChYiL>LlyC^-p*hrlzDULe9M zS>6a-dI$#;9+DCHcGpm3whyH45Tjf^6k~uAz(xa_s~bS49bb&cLDI<_znX}23hJaw zZeUoijKL}LFi;BoAP#(2X2|GzVXUPKSQ-Qm*Z4a#p4-bRr)fN&b}D*j!5B+4SOAbA zgs;P^x*Pl&=|D}vMw?e~%)H*H@^sBeAe)KU83TZlY}Ts5&N%?c@r2;YAmvkhJkzzw ziX;z_o*2+2@Box1#%^-ttr7y-%htZsPd5;fv?HRyHdYL(X$;v!J4S z3hiu=fviV_Kz3aqI*ugp6thmAXJ8xEM5x(nH`ICjr_UQ_nVs^*^vZV{vU(ni%ns?x zhL!IU4fbEAsdR|COw9}PmFfr7qad9%7tupFb%7#@>%M^}O{=UI8wjNhgw)29M(tEc z&8K<6YWicM#K@qDmh*vA0~M|LpG5|dNRe$r@dLpWU8X_RTd{bsR*)p zC4S~n7T_@BkpOU3O&0`2$bBh`HT+RzE#7|(;ZP~JvLz0JA(b9GCZ=q)7mV7|g*?!y zA7WzK3UmO(5|Jm<22ctcrVgEGY^q%|Mw8l+Hq*}Ddts`^Z%rSHd7oNwC&90R?t5y zxs}4IT*HpU9BtfZ;pj>!tTVHok{G2cBsJYZ0|$a@P2uB1WNiui%o@Pa@J*tb*!1p; z+LzV|yM_BCbEL0eE88+w@D(@!fbA+As*Gu)_)s3;6(u8ws3b}wE3B=GqLq~t`E;xd zWQLQGK;0Zr0x`?02P$7mpzX^MY!y}w%+P={*dSceRNDN$N&onp25<6*8(EoZU|Ahh zH$rlVloKHUCHw>hRzC=-2sGJ991@%0N1M6}=JSpe_{1@ohWHv#6eZ-u9}M_|ESTmH zPzkCJD7^2gsaz%as6+>ZbwZj#G)ApiJ%^+@tGmy@&Af=hHwt{qFzH2pV6jUn)KdCe z9{KqEBK1>Ze*?ERS*!J%S3kBNu}4t)9}siZE0pFVv46#9k$r3r>|a^T`UKx>5KZkA z(8CM@Ajh{mucvH3We1O0X1WFx3?3}5Q|0_{qBed<2hoX+-FR|z^%=&hJL_Q#ed~=* zu?l5EotV+6T#RL;4y7(>9a;TY=dP00V1${gP_NK}#HjPG)EP^rA+ zFvq6bX00a;r`d#-nDuRoH!n38&1?@7PhGT}F8wN+v)|%M_wUs|gNgJSgq~UTRw(W0 z<;~)gNHJ(iY~r%>OiQD9ra77fLFU*xu~8^md$l1uW`AbeR)C7>FYHgSF13hBq zkpI1CUnl>DF$5TsaZ=FO+C@@P=AV>$rgf_4ZuS=Pk2mebUaKqx6``Ur|IC1B z!`@1nMVMkxt<&iF$|%Y~hoti8?CfUPyP)<=BXI~ diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/testing.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/__pycache__/testing.cpython-39.pyc deleted file mode 100644 index 17c5796ef9e52321840d2657104389b8bf5f176b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44353 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 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. NOTE: This argument is not used by the - calling code and can probably be removed. - :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/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/builder/_lxml.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/builder/_lxml.py deleted file mode 100644 index 11c9a696..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/builder/_lxml.py +++ /dev/null @@ -1,342 +0,0 @@ -# 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) - - # This was provided by the end-user; treat it as a known - # definite encoding per the algorithm laid out in the HTML5 - # spec. (See the EncodingDetector class for details.) - known_definite_encodings = [user_specified_encoding] - - # This was found in the document; treat it as a slightly lower-priority - # user encoding. - user_encodings = [document_declared_encoding] - detector = EncodingDetector( - markup, known_definite_encodings=known_definite_encodings, - user_encodings=user_encodings, is_html=is_html, - exclude_encodings=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/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/dammit.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/dammit.py deleted file mode 100644 index e017408b..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/dammit.py +++ /dev/null @@ -1,3338 +0,0 @@ -# -*- 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" - -from html.entities import codepoint2name -from collections import defaultdict -import codecs -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) -} - -try: - from html.entities import html5 -except ImportError: - # This is a copy of html.entities.html5 from Python 3.9. There's - # no equivalent table in Python 2, so we'll just provide a copy - # here. - html5 = { - 'Aacute': '\xc1', - 'aacute': '\xe1', - 'Aacute;': '\xc1', - 'aacute;': '\xe1', - 'Abreve;': '\u0102', - 'abreve;': '\u0103', - 'ac;': '\u223e', - 'acd;': '\u223f', - 'acE;': '\u223e\u0333', - 'Acirc': '\xc2', - 'acirc': '\xe2', - 'Acirc;': '\xc2', - 'acirc;': '\xe2', - 'acute': '\xb4', - 'acute;': '\xb4', - 'Acy;': '\u0410', - 'acy;': '\u0430', - 'AElig': '\xc6', - 'aelig': '\xe6', - 'AElig;': '\xc6', - 'aelig;': '\xe6', - 'af;': '\u2061', - 'Afr;': '\U0001d504', - 'afr;': '\U0001d51e', - 'Agrave': '\xc0', - 'agrave': '\xe0', - 'Agrave;': '\xc0', - 'agrave;': '\xe0', - 'alefsym;': '\u2135', - 'aleph;': '\u2135', - 'Alpha;': '\u0391', - 'alpha;': '\u03b1', - 'Amacr;': '\u0100', - 'amacr;': '\u0101', - 'amalg;': '\u2a3f', - 'AMP': '&', - 'amp': '&', - 'AMP;': '&', - 'amp;': '&', - 'And;': '\u2a53', - 'and;': '\u2227', - 'andand;': '\u2a55', - 'andd;': '\u2a5c', - 'andslope;': '\u2a58', - 'andv;': '\u2a5a', - 'ang;': '\u2220', - 'ange;': '\u29a4', - 'angle;': '\u2220', - 'angmsd;': '\u2221', - 'angmsdaa;': '\u29a8', - 'angmsdab;': '\u29a9', - 'angmsdac;': '\u29aa', - 'angmsdad;': '\u29ab', - 'angmsdae;': '\u29ac', - 'angmsdaf;': '\u29ad', - 'angmsdag;': '\u29ae', - 'angmsdah;': '\u29af', - 'angrt;': '\u221f', - 'angrtvb;': '\u22be', - 'angrtvbd;': '\u299d', - 'angsph;': '\u2222', - 'angst;': '\xc5', - 'angzarr;': '\u237c', - 'Aogon;': '\u0104', - 'aogon;': '\u0105', - 'Aopf;': '\U0001d538', - 'aopf;': '\U0001d552', - 'ap;': '\u2248', - 'apacir;': '\u2a6f', - 'apE;': '\u2a70', - 'ape;': '\u224a', - 'apid;': '\u224b', - 'apos;': "'", - 'ApplyFunction;': '\u2061', - 'approx;': '\u2248', - 'approxeq;': '\u224a', - 'Aring': '\xc5', - 'aring': '\xe5', - 'Aring;': '\xc5', - 'aring;': '\xe5', - 'Ascr;': '\U0001d49c', - 'ascr;': '\U0001d4b6', - 'Assign;': '\u2254', - 'ast;': '*', - 'asymp;': '\u2248', - 'asympeq;': '\u224d', - 'Atilde': '\xc3', - 'atilde': '\xe3', - 'Atilde;': '\xc3', - 'atilde;': '\xe3', - 'Auml': '\xc4', - 'auml': '\xe4', - 'Auml;': '\xc4', - 'auml;': '\xe4', - 'awconint;': '\u2233', - 'awint;': '\u2a11', - 'backcong;': '\u224c', - 'backepsilon;': '\u03f6', - 'backprime;': '\u2035', - 'backsim;': '\u223d', - 'backsimeq;': '\u22cd', - 'Backslash;': '\u2216', - 'Barv;': '\u2ae7', - 'barvee;': '\u22bd', - 'Barwed;': '\u2306', - 'barwed;': '\u2305', - 'barwedge;': '\u2305', - 'bbrk;': '\u23b5', - 'bbrktbrk;': '\u23b6', - 'bcong;': '\u224c', - 'Bcy;': '\u0411', - 'bcy;': '\u0431', - 'bdquo;': '\u201e', - 'becaus;': '\u2235', - 'Because;': '\u2235', - 'because;': '\u2235', - 'bemptyv;': '\u29b0', - 'bepsi;': '\u03f6', - 'bernou;': '\u212c', - 'Bernoullis;': '\u212c', - 'Beta;': '\u0392', - 'beta;': '\u03b2', - 'beth;': '\u2136', - 'between;': '\u226c', - 'Bfr;': '\U0001d505', - 'bfr;': '\U0001d51f', - 'bigcap;': '\u22c2', - 'bigcirc;': '\u25ef', - 'bigcup;': '\u22c3', - 'bigodot;': '\u2a00', - 'bigoplus;': '\u2a01', - 'bigotimes;': '\u2a02', - 'bigsqcup;': '\u2a06', - 'bigstar;': '\u2605', - 'bigtriangledown;': '\u25bd', - 'bigtriangleup;': '\u25b3', - 'biguplus;': '\u2a04', - 'bigvee;': '\u22c1', - 'bigwedge;': '\u22c0', - 'bkarow;': '\u290d', - 'blacklozenge;': '\u29eb', - 'blacksquare;': '\u25aa', - 'blacktriangle;': '\u25b4', - 'blacktriangledown;': '\u25be', - 'blacktriangleleft;': '\u25c2', - 'blacktriangleright;': '\u25b8', - 'blank;': '\u2423', - 'blk12;': '\u2592', - 'blk14;': '\u2591', - 'blk34;': '\u2593', - 'block;': '\u2588', - 'bne;': '=\u20e5', - 'bnequiv;': '\u2261\u20e5', - 'bNot;': '\u2aed', - 'bnot;': '\u2310', - 'Bopf;': '\U0001d539', - 'bopf;': '\U0001d553', - 'bot;': '\u22a5', - 'bottom;': '\u22a5', - 'bowtie;': '\u22c8', - 'boxbox;': '\u29c9', - 'boxDL;': '\u2557', - 'boxDl;': '\u2556', - 'boxdL;': '\u2555', - 'boxdl;': '\u2510', - 'boxDR;': '\u2554', - 'boxDr;': '\u2553', - 'boxdR;': '\u2552', - 'boxdr;': '\u250c', - 'boxH;': '\u2550', - 'boxh;': '\u2500', - 'boxHD;': '\u2566', - 'boxHd;': '\u2564', - 'boxhD;': '\u2565', - 'boxhd;': '\u252c', - 'boxHU;': '\u2569', - 'boxHu;': '\u2567', - 'boxhU;': '\u2568', - 'boxhu;': '\u2534', - 'boxminus;': '\u229f', - 'boxplus;': '\u229e', - 'boxtimes;': '\u22a0', - 'boxUL;': '\u255d', - 'boxUl;': '\u255c', - 'boxuL;': '\u255b', - 'boxul;': '\u2518', - 'boxUR;': '\u255a', - 'boxUr;': '\u2559', - 'boxuR;': '\u2558', - 'boxur;': '\u2514', - 'boxV;': '\u2551', - 'boxv;': '\u2502', - 'boxVH;': '\u256c', - 'boxVh;': '\u256b', - 'boxvH;': '\u256a', - 'boxvh;': '\u253c', - 'boxVL;': '\u2563', - 'boxVl;': '\u2562', - 'boxvL;': '\u2561', - 'boxvl;': '\u2524', - 'boxVR;': '\u2560', - 'boxVr;': '\u255f', - 'boxvR;': '\u255e', - 'boxvr;': '\u251c', - 'bprime;': '\u2035', - 'Breve;': '\u02d8', - 'breve;': '\u02d8', - 'brvbar': '\xa6', - 'brvbar;': '\xa6', - 'Bscr;': '\u212c', - 'bscr;': '\U0001d4b7', - 'bsemi;': '\u204f', - 'bsim;': '\u223d', - 'bsime;': '\u22cd', - 'bsol;': '\\', - 'bsolb;': '\u29c5', - 'bsolhsub;': '\u27c8', - 'bull;': '\u2022', - 'bullet;': '\u2022', - 'bump;': '\u224e', - 'bumpE;': '\u2aae', - 'bumpe;': '\u224f', - 'Bumpeq;': '\u224e', - 'bumpeq;': '\u224f', - 'Cacute;': '\u0106', - 'cacute;': '\u0107', - 'Cap;': '\u22d2', - 'cap;': '\u2229', - 'capand;': '\u2a44', - 'capbrcup;': '\u2a49', - 'capcap;': '\u2a4b', - 'capcup;': '\u2a47', - 'capdot;': '\u2a40', - 'CapitalDifferentialD;': '\u2145', - 'caps;': '\u2229\ufe00', - 'caret;': '\u2041', - 'caron;': '\u02c7', - 'Cayleys;': '\u212d', - 'ccaps;': '\u2a4d', - 'Ccaron;': '\u010c', - 'ccaron;': '\u010d', - 'Ccedil': '\xc7', - 'ccedil': '\xe7', - 'Ccedil;': '\xc7', - 'ccedil;': '\xe7', - 'Ccirc;': '\u0108', - 'ccirc;': '\u0109', - 'Cconint;': '\u2230', - 'ccups;': '\u2a4c', - 'ccupssm;': '\u2a50', - 'Cdot;': '\u010a', - 'cdot;': '\u010b', - 'cedil': '\xb8', - 'cedil;': '\xb8', - 'Cedilla;': '\xb8', - 'cemptyv;': '\u29b2', - 'cent': '\xa2', - 'cent;': '\xa2', - 'CenterDot;': '\xb7', - 'centerdot;': '\xb7', - 'Cfr;': '\u212d', - 'cfr;': '\U0001d520', - 'CHcy;': '\u0427', - 'chcy;': '\u0447', - 'check;': '\u2713', - 'checkmark;': '\u2713', - 'Chi;': '\u03a7', - 'chi;': '\u03c7', - 'cir;': '\u25cb', - 'circ;': '\u02c6', - 'circeq;': '\u2257', - 'circlearrowleft;': '\u21ba', - 'circlearrowright;': '\u21bb', - 'circledast;': '\u229b', - 'circledcirc;': '\u229a', - 'circleddash;': '\u229d', - 'CircleDot;': '\u2299', - 'circledR;': '\xae', - 'circledS;': '\u24c8', - 'CircleMinus;': '\u2296', - 'CirclePlus;': '\u2295', - 'CircleTimes;': '\u2297', - 'cirE;': '\u29c3', - 'cire;': '\u2257', - 'cirfnint;': '\u2a10', - 'cirmid;': '\u2aef', - 'cirscir;': '\u29c2', - 'ClockwiseContourIntegral;': '\u2232', - 'CloseCurlyDoubleQuote;': '\u201d', - 'CloseCurlyQuote;': '\u2019', - 'clubs;': '\u2663', - 'clubsuit;': '\u2663', - 'Colon;': '\u2237', - 'colon;': ':', - 'Colone;': '\u2a74', - 'colone;': '\u2254', - 'coloneq;': '\u2254', - 'comma;': ',', - 'commat;': '@', - 'comp;': '\u2201', - 'compfn;': '\u2218', - 'complement;': '\u2201', - 'complexes;': '\u2102', - 'cong;': '\u2245', - 'congdot;': '\u2a6d', - 'Congruent;': '\u2261', - 'Conint;': '\u222f', - 'conint;': '\u222e', - 'ContourIntegral;': '\u222e', - 'Copf;': '\u2102', - 'copf;': '\U0001d554', - 'coprod;': '\u2210', - 'Coproduct;': '\u2210', - 'COPY': '\xa9', - 'copy': '\xa9', - 'COPY;': '\xa9', - 'copy;': '\xa9', - 'copysr;': '\u2117', - 'CounterClockwiseContourIntegral;': '\u2233', - 'crarr;': '\u21b5', - 'Cross;': '\u2a2f', - 'cross;': '\u2717', - 'Cscr;': '\U0001d49e', - 'cscr;': '\U0001d4b8', - 'csub;': '\u2acf', - 'csube;': '\u2ad1', - 'csup;': '\u2ad0', - 'csupe;': '\u2ad2', - 'ctdot;': '\u22ef', - 'cudarrl;': '\u2938', - 'cudarrr;': '\u2935', - 'cuepr;': '\u22de', - 'cuesc;': '\u22df', - 'cularr;': '\u21b6', - 'cularrp;': '\u293d', - 'Cup;': '\u22d3', - 'cup;': '\u222a', - 'cupbrcap;': '\u2a48', - 'CupCap;': '\u224d', - 'cupcap;': '\u2a46', - 'cupcup;': '\u2a4a', - 'cupdot;': '\u228d', - 'cupor;': '\u2a45', - 'cups;': '\u222a\ufe00', - 'curarr;': '\u21b7', - 'curarrm;': '\u293c', - 'curlyeqprec;': '\u22de', - 'curlyeqsucc;': '\u22df', - 'curlyvee;': '\u22ce', - 'curlywedge;': '\u22cf', - 'curren': '\xa4', - 'curren;': '\xa4', - 'curvearrowleft;': '\u21b6', - 'curvearrowright;': '\u21b7', - 'cuvee;': '\u22ce', - 'cuwed;': '\u22cf', - 'cwconint;': '\u2232', - 'cwint;': '\u2231', - 'cylcty;': '\u232d', - 'Dagger;': '\u2021', - 'dagger;': '\u2020', - 'daleth;': '\u2138', - 'Darr;': '\u21a1', - 'dArr;': '\u21d3', - 'darr;': '\u2193', - 'dash;': '\u2010', - 'Dashv;': '\u2ae4', - 'dashv;': '\u22a3', - 'dbkarow;': '\u290f', - 'dblac;': '\u02dd', - 'Dcaron;': '\u010e', - 'dcaron;': '\u010f', - 'Dcy;': '\u0414', - 'dcy;': '\u0434', - 'DD;': '\u2145', - 'dd;': '\u2146', - 'ddagger;': '\u2021', - 'ddarr;': '\u21ca', - 'DDotrahd;': '\u2911', - 'ddotseq;': '\u2a77', - 'deg': '\xb0', - 'deg;': '\xb0', - 'Del;': '\u2207', - 'Delta;': '\u0394', - 'delta;': '\u03b4', - 'demptyv;': '\u29b1', - 'dfisht;': '\u297f', - 'Dfr;': '\U0001d507', - 'dfr;': '\U0001d521', - 'dHar;': '\u2965', - 'dharl;': '\u21c3', - 'dharr;': '\u21c2', - 'DiacriticalAcute;': '\xb4', - 'DiacriticalDot;': '\u02d9', - 'DiacriticalDoubleAcute;': '\u02dd', - 'DiacriticalGrave;': '`', - 'DiacriticalTilde;': '\u02dc', - 'diam;': '\u22c4', - 'Diamond;': '\u22c4', - 'diamond;': '\u22c4', - 'diamondsuit;': '\u2666', - 'diams;': '\u2666', - 'die;': '\xa8', - 'DifferentialD;': '\u2146', - 'digamma;': '\u03dd', - 'disin;': '\u22f2', - 'div;': '\xf7', - 'divide': '\xf7', - 'divide;': '\xf7', - 'divideontimes;': '\u22c7', - 'divonx;': '\u22c7', - 'DJcy;': '\u0402', - 'djcy;': '\u0452', - 'dlcorn;': '\u231e', - 'dlcrop;': '\u230d', - 'dollar;': '$', - 'Dopf;': '\U0001d53b', - 'dopf;': '\U0001d555', - 'Dot;': '\xa8', - 'dot;': '\u02d9', - 'DotDot;': '\u20dc', - 'doteq;': '\u2250', - 'doteqdot;': '\u2251', - 'DotEqual;': '\u2250', - 'dotminus;': '\u2238', - 'dotplus;': '\u2214', - 'dotsquare;': '\u22a1', - 'doublebarwedge;': '\u2306', - 'DoubleContourIntegral;': '\u222f', - 'DoubleDot;': '\xa8', - 'DoubleDownArrow;': '\u21d3', - 'DoubleLeftArrow;': '\u21d0', - 'DoubleLeftRightArrow;': '\u21d4', - 'DoubleLeftTee;': '\u2ae4', - 'DoubleLongLeftArrow;': '\u27f8', - 'DoubleLongLeftRightArrow;': '\u27fa', - 'DoubleLongRightArrow;': '\u27f9', - 'DoubleRightArrow;': '\u21d2', - 'DoubleRightTee;': '\u22a8', - 'DoubleUpArrow;': '\u21d1', - 'DoubleUpDownArrow;': '\u21d5', - 'DoubleVerticalBar;': '\u2225', - 'DownArrow;': '\u2193', - 'Downarrow;': '\u21d3', - 'downarrow;': '\u2193', - 'DownArrowBar;': '\u2913', - 'DownArrowUpArrow;': '\u21f5', - 'DownBreve;': '\u0311', - 'downdownarrows;': '\u21ca', - 'downharpoonleft;': '\u21c3', - 'downharpoonright;': '\u21c2', - 'DownLeftRightVector;': '\u2950', - 'DownLeftTeeVector;': '\u295e', - 'DownLeftVector;': '\u21bd', - 'DownLeftVectorBar;': '\u2956', - 'DownRightTeeVector;': '\u295f', - 'DownRightVector;': '\u21c1', - 'DownRightVectorBar;': '\u2957', - 'DownTee;': '\u22a4', - 'DownTeeArrow;': '\u21a7', - 'drbkarow;': '\u2910', - 'drcorn;': '\u231f', - 'drcrop;': '\u230c', - 'Dscr;': '\U0001d49f', - 'dscr;': '\U0001d4b9', - 'DScy;': '\u0405', - 'dscy;': '\u0455', - 'dsol;': '\u29f6', - 'Dstrok;': '\u0110', - 'dstrok;': '\u0111', - 'dtdot;': '\u22f1', - 'dtri;': '\u25bf', - 'dtrif;': '\u25be', - 'duarr;': '\u21f5', - 'duhar;': '\u296f', - 'dwangle;': '\u29a6', - 'DZcy;': '\u040f', - 'dzcy;': '\u045f', - 'dzigrarr;': '\u27ff', - 'Eacute': '\xc9', - 'eacute': '\xe9', - 'Eacute;': '\xc9', - 'eacute;': '\xe9', - 'easter;': '\u2a6e', - 'Ecaron;': '\u011a', - 'ecaron;': '\u011b', - 'ecir;': '\u2256', - 'Ecirc': '\xca', - 'ecirc': '\xea', - 'Ecirc;': '\xca', - 'ecirc;': '\xea', - 'ecolon;': '\u2255', - 'Ecy;': '\u042d', - 'ecy;': '\u044d', - 'eDDot;': '\u2a77', - 'Edot;': '\u0116', - 'eDot;': '\u2251', - 'edot;': '\u0117', - 'ee;': '\u2147', - 'efDot;': '\u2252', - 'Efr;': '\U0001d508', - 'efr;': '\U0001d522', - 'eg;': '\u2a9a', - 'Egrave': '\xc8', - 'egrave': '\xe8', - 'Egrave;': '\xc8', - 'egrave;': '\xe8', - 'egs;': '\u2a96', - 'egsdot;': '\u2a98', - 'el;': '\u2a99', - 'Element;': '\u2208', - 'elinters;': '\u23e7', - 'ell;': '\u2113', - 'els;': '\u2a95', - 'elsdot;': '\u2a97', - 'Emacr;': '\u0112', - 'emacr;': '\u0113', - 'empty;': '\u2205', - 'emptyset;': '\u2205', - 'EmptySmallSquare;': '\u25fb', - 'emptyv;': '\u2205', - 'EmptyVerySmallSquare;': '\u25ab', - 'emsp13;': '\u2004', - 'emsp14;': '\u2005', - 'emsp;': '\u2003', - 'ENG;': '\u014a', - 'eng;': '\u014b', - 'ensp;': '\u2002', - 'Eogon;': '\u0118', - 'eogon;': '\u0119', - 'Eopf;': '\U0001d53c', - 'eopf;': '\U0001d556', - 'epar;': '\u22d5', - 'eparsl;': '\u29e3', - 'eplus;': '\u2a71', - 'epsi;': '\u03b5', - 'Epsilon;': '\u0395', - 'epsilon;': '\u03b5', - 'epsiv;': '\u03f5', - 'eqcirc;': '\u2256', - 'eqcolon;': '\u2255', - 'eqsim;': '\u2242', - 'eqslantgtr;': '\u2a96', - 'eqslantless;': '\u2a95', - 'Equal;': '\u2a75', - 'equals;': '=', - 'EqualTilde;': '\u2242', - 'equest;': '\u225f', - 'Equilibrium;': '\u21cc', - 'equiv;': '\u2261', - 'equivDD;': '\u2a78', - 'eqvparsl;': '\u29e5', - 'erarr;': '\u2971', - 'erDot;': '\u2253', - 'Escr;': '\u2130', - 'escr;': '\u212f', - 'esdot;': '\u2250', - 'Esim;': '\u2a73', - 'esim;': '\u2242', - 'Eta;': '\u0397', - 'eta;': '\u03b7', - 'ETH': '\xd0', - 'eth': '\xf0', - 'ETH;': '\xd0', - 'eth;': '\xf0', - 'Euml': '\xcb', - 'euml': '\xeb', - 'Euml;': '\xcb', - 'euml;': '\xeb', - 'euro;': '\u20ac', - 'excl;': '!', - 'exist;': '\u2203', - 'Exists;': '\u2203', - 'expectation;': '\u2130', - 'ExponentialE;': '\u2147', - 'exponentiale;': '\u2147', - 'fallingdotseq;': '\u2252', - 'Fcy;': '\u0424', - 'fcy;': '\u0444', - 'female;': '\u2640', - 'ffilig;': '\ufb03', - 'fflig;': '\ufb00', - 'ffllig;': '\ufb04', - 'Ffr;': '\U0001d509', - 'ffr;': '\U0001d523', - 'filig;': '\ufb01', - 'FilledSmallSquare;': '\u25fc', - 'FilledVerySmallSquare;': '\u25aa', - 'fjlig;': 'fj', - 'flat;': '\u266d', - 'fllig;': '\ufb02', - 'fltns;': '\u25b1', - 'fnof;': '\u0192', - 'Fopf;': '\U0001d53d', - 'fopf;': '\U0001d557', - 'ForAll;': '\u2200', - 'forall;': '\u2200', - 'fork;': '\u22d4', - 'forkv;': '\u2ad9', - 'Fouriertrf;': '\u2131', - 'fpartint;': '\u2a0d', - 'frac12': '\xbd', - 'frac12;': '\xbd', - 'frac13;': '\u2153', - 'frac14': '\xbc', - 'frac14;': '\xbc', - 'frac15;': '\u2155', - 'frac16;': '\u2159', - 'frac18;': '\u215b', - 'frac23;': '\u2154', - 'frac25;': '\u2156', - 'frac34': '\xbe', - 'frac34;': '\xbe', - 'frac35;': '\u2157', - 'frac38;': '\u215c', - 'frac45;': '\u2158', - 'frac56;': '\u215a', - 'frac58;': '\u215d', - 'frac78;': '\u215e', - 'frasl;': '\u2044', - 'frown;': '\u2322', - 'Fscr;': '\u2131', - 'fscr;': '\U0001d4bb', - 'gacute;': '\u01f5', - 'Gamma;': '\u0393', - 'gamma;': '\u03b3', - 'Gammad;': '\u03dc', - 'gammad;': '\u03dd', - 'gap;': '\u2a86', - 'Gbreve;': '\u011e', - 'gbreve;': '\u011f', - 'Gcedil;': '\u0122', - 'Gcirc;': '\u011c', - 'gcirc;': '\u011d', - 'Gcy;': '\u0413', - 'gcy;': '\u0433', - 'Gdot;': '\u0120', - 'gdot;': '\u0121', - 'gE;': '\u2267', - 'ge;': '\u2265', - 'gEl;': '\u2a8c', - 'gel;': '\u22db', - 'geq;': '\u2265', - 'geqq;': '\u2267', - 'geqslant;': '\u2a7e', - 'ges;': '\u2a7e', - 'gescc;': '\u2aa9', - 'gesdot;': '\u2a80', - 'gesdoto;': '\u2a82', - 'gesdotol;': '\u2a84', - 'gesl;': '\u22db\ufe00', - 'gesles;': '\u2a94', - 'Gfr;': '\U0001d50a', - 'gfr;': '\U0001d524', - 'Gg;': '\u22d9', - 'gg;': '\u226b', - 'ggg;': '\u22d9', - 'gimel;': '\u2137', - 'GJcy;': '\u0403', - 'gjcy;': '\u0453', - 'gl;': '\u2277', - 'gla;': '\u2aa5', - 'glE;': '\u2a92', - 'glj;': '\u2aa4', - 'gnap;': '\u2a8a', - 'gnapprox;': '\u2a8a', - 'gnE;': '\u2269', - 'gne;': '\u2a88', - 'gneq;': '\u2a88', - 'gneqq;': '\u2269', - 'gnsim;': '\u22e7', - 'Gopf;': '\U0001d53e', - 'gopf;': '\U0001d558', - 'grave;': '`', - 'GreaterEqual;': '\u2265', - 'GreaterEqualLess;': '\u22db', - 'GreaterFullEqual;': '\u2267', - 'GreaterGreater;': '\u2aa2', - 'GreaterLess;': '\u2277', - 'GreaterSlantEqual;': '\u2a7e', - 'GreaterTilde;': '\u2273', - 'Gscr;': '\U0001d4a2', - 'gscr;': '\u210a', - 'gsim;': '\u2273', - 'gsime;': '\u2a8e', - 'gsiml;': '\u2a90', - 'GT': '>', - 'gt': '>', - 'GT;': '>', - 'Gt;': '\u226b', - 'gt;': '>', - 'gtcc;': '\u2aa7', - 'gtcir;': '\u2a7a', - 'gtdot;': '\u22d7', - 'gtlPar;': '\u2995', - 'gtquest;': '\u2a7c', - 'gtrapprox;': '\u2a86', - 'gtrarr;': '\u2978', - 'gtrdot;': '\u22d7', - 'gtreqless;': '\u22db', - 'gtreqqless;': '\u2a8c', - 'gtrless;': '\u2277', - 'gtrsim;': '\u2273', - 'gvertneqq;': '\u2269\ufe00', - 'gvnE;': '\u2269\ufe00', - 'Hacek;': '\u02c7', - 'hairsp;': '\u200a', - 'half;': '\xbd', - 'hamilt;': '\u210b', - 'HARDcy;': '\u042a', - 'hardcy;': '\u044a', - 'hArr;': '\u21d4', - 'harr;': '\u2194', - 'harrcir;': '\u2948', - 'harrw;': '\u21ad', - 'Hat;': '^', - 'hbar;': '\u210f', - 'Hcirc;': '\u0124', - 'hcirc;': '\u0125', - 'hearts;': '\u2665', - 'heartsuit;': '\u2665', - 'hellip;': '\u2026', - 'hercon;': '\u22b9', - 'Hfr;': '\u210c', - 'hfr;': '\U0001d525', - 'HilbertSpace;': '\u210b', - 'hksearow;': '\u2925', - 'hkswarow;': '\u2926', - 'hoarr;': '\u21ff', - 'homtht;': '\u223b', - 'hookleftarrow;': '\u21a9', - 'hookrightarrow;': '\u21aa', - 'Hopf;': '\u210d', - 'hopf;': '\U0001d559', - 'horbar;': '\u2015', - 'HorizontalLine;': '\u2500', - 'Hscr;': '\u210b', - 'hscr;': '\U0001d4bd', - 'hslash;': '\u210f', - 'Hstrok;': '\u0126', - 'hstrok;': '\u0127', - 'HumpDownHump;': '\u224e', - 'HumpEqual;': '\u224f', - 'hybull;': '\u2043', - 'hyphen;': '\u2010', - 'Iacute': '\xcd', - 'iacute': '\xed', - 'Iacute;': '\xcd', - 'iacute;': '\xed', - 'ic;': '\u2063', - 'Icirc': '\xce', - 'icirc': '\xee', - 'Icirc;': '\xce', - 'icirc;': '\xee', - 'Icy;': '\u0418', - 'icy;': '\u0438', - 'Idot;': '\u0130', - 'IEcy;': '\u0415', - 'iecy;': '\u0435', - 'iexcl': '\xa1', - 'iexcl;': '\xa1', - 'iff;': '\u21d4', - 'Ifr;': '\u2111', - 'ifr;': '\U0001d526', - 'Igrave': '\xcc', - 'igrave': '\xec', - 'Igrave;': '\xcc', - 'igrave;': '\xec', - 'ii;': '\u2148', - 'iiiint;': '\u2a0c', - 'iiint;': '\u222d', - 'iinfin;': '\u29dc', - 'iiota;': '\u2129', - 'IJlig;': '\u0132', - 'ijlig;': '\u0133', - 'Im;': '\u2111', - 'Imacr;': '\u012a', - 'imacr;': '\u012b', - 'image;': '\u2111', - 'ImaginaryI;': '\u2148', - 'imagline;': '\u2110', - 'imagpart;': '\u2111', - 'imath;': '\u0131', - 'imof;': '\u22b7', - 'imped;': '\u01b5', - 'Implies;': '\u21d2', - 'in;': '\u2208', - 'incare;': '\u2105', - 'infin;': '\u221e', - 'infintie;': '\u29dd', - 'inodot;': '\u0131', - 'Int;': '\u222c', - 'int;': '\u222b', - 'intcal;': '\u22ba', - 'integers;': '\u2124', - 'Integral;': '\u222b', - 'intercal;': '\u22ba', - 'Intersection;': '\u22c2', - 'intlarhk;': '\u2a17', - 'intprod;': '\u2a3c', - 'InvisibleComma;': '\u2063', - 'InvisibleTimes;': '\u2062', - 'IOcy;': '\u0401', - 'iocy;': '\u0451', - 'Iogon;': '\u012e', - 'iogon;': '\u012f', - 'Iopf;': '\U0001d540', - 'iopf;': '\U0001d55a', - 'Iota;': '\u0399', - 'iota;': '\u03b9', - 'iprod;': '\u2a3c', - 'iquest': '\xbf', - 'iquest;': '\xbf', - 'Iscr;': '\u2110', - 'iscr;': '\U0001d4be', - 'isin;': '\u2208', - 'isindot;': '\u22f5', - 'isinE;': '\u22f9', - 'isins;': '\u22f4', - 'isinsv;': '\u22f3', - 'isinv;': '\u2208', - 'it;': '\u2062', - 'Itilde;': '\u0128', - 'itilde;': '\u0129', - 'Iukcy;': '\u0406', - 'iukcy;': '\u0456', - 'Iuml': '\xcf', - 'iuml': '\xef', - 'Iuml;': '\xcf', - 'iuml;': '\xef', - 'Jcirc;': '\u0134', - 'jcirc;': '\u0135', - 'Jcy;': '\u0419', - 'jcy;': '\u0439', - 'Jfr;': '\U0001d50d', - 'jfr;': '\U0001d527', - 'jmath;': '\u0237', - 'Jopf;': '\U0001d541', - 'jopf;': '\U0001d55b', - 'Jscr;': '\U0001d4a5', - 'jscr;': '\U0001d4bf', - 'Jsercy;': '\u0408', - 'jsercy;': '\u0458', - 'Jukcy;': '\u0404', - 'jukcy;': '\u0454', - 'Kappa;': '\u039a', - 'kappa;': '\u03ba', - 'kappav;': '\u03f0', - 'Kcedil;': '\u0136', - 'kcedil;': '\u0137', - 'Kcy;': '\u041a', - 'kcy;': '\u043a', - 'Kfr;': '\U0001d50e', - 'kfr;': '\U0001d528', - 'kgreen;': '\u0138', - 'KHcy;': '\u0425', - 'khcy;': '\u0445', - 'KJcy;': '\u040c', - 'kjcy;': '\u045c', - 'Kopf;': '\U0001d542', - 'kopf;': '\U0001d55c', - 'Kscr;': '\U0001d4a6', - 'kscr;': '\U0001d4c0', - 'lAarr;': '\u21da', - 'Lacute;': '\u0139', - 'lacute;': '\u013a', - 'laemptyv;': '\u29b4', - 'lagran;': '\u2112', - 'Lambda;': '\u039b', - 'lambda;': '\u03bb', - 'Lang;': '\u27ea', - 'lang;': '\u27e8', - 'langd;': '\u2991', - 'langle;': '\u27e8', - 'lap;': '\u2a85', - 'Laplacetrf;': '\u2112', - 'laquo': '\xab', - 'laquo;': '\xab', - 'Larr;': '\u219e', - 'lArr;': '\u21d0', - 'larr;': '\u2190', - 'larrb;': '\u21e4', - 'larrbfs;': '\u291f', - 'larrfs;': '\u291d', - 'larrhk;': '\u21a9', - 'larrlp;': '\u21ab', - 'larrpl;': '\u2939', - 'larrsim;': '\u2973', - 'larrtl;': '\u21a2', - 'lat;': '\u2aab', - 'lAtail;': '\u291b', - 'latail;': '\u2919', - 'late;': '\u2aad', - 'lates;': '\u2aad\ufe00', - 'lBarr;': '\u290e', - 'lbarr;': '\u290c', - 'lbbrk;': '\u2772', - 'lbrace;': '{', - 'lbrack;': '[', - 'lbrke;': '\u298b', - 'lbrksld;': '\u298f', - 'lbrkslu;': '\u298d', - 'Lcaron;': '\u013d', - 'lcaron;': '\u013e', - 'Lcedil;': '\u013b', - 'lcedil;': '\u013c', - 'lceil;': '\u2308', - 'lcub;': '{', - 'Lcy;': '\u041b', - 'lcy;': '\u043b', - 'ldca;': '\u2936', - 'ldquo;': '\u201c', - 'ldquor;': '\u201e', - 'ldrdhar;': '\u2967', - 'ldrushar;': '\u294b', - 'ldsh;': '\u21b2', - 'lE;': '\u2266', - 'le;': '\u2264', - 'LeftAngleBracket;': '\u27e8', - 'LeftArrow;': '\u2190', - 'Leftarrow;': '\u21d0', - 'leftarrow;': '\u2190', - 'LeftArrowBar;': '\u21e4', - 'LeftArrowRightArrow;': '\u21c6', - 'leftarrowtail;': '\u21a2', - 'LeftCeiling;': '\u2308', - 'LeftDoubleBracket;': '\u27e6', - 'LeftDownTeeVector;': '\u2961', - 'LeftDownVector;': '\u21c3', - 'LeftDownVectorBar;': '\u2959', - 'LeftFloor;': '\u230a', - 'leftharpoondown;': '\u21bd', - 'leftharpoonup;': '\u21bc', - 'leftleftarrows;': '\u21c7', - 'LeftRightArrow;': '\u2194', - 'Leftrightarrow;': '\u21d4', - 'leftrightarrow;': '\u2194', - 'leftrightarrows;': '\u21c6', - 'leftrightharpoons;': '\u21cb', - 'leftrightsquigarrow;': '\u21ad', - 'LeftRightVector;': '\u294e', - 'LeftTee;': '\u22a3', - 'LeftTeeArrow;': '\u21a4', - 'LeftTeeVector;': '\u295a', - 'leftthreetimes;': '\u22cb', - 'LeftTriangle;': '\u22b2', - 'LeftTriangleBar;': '\u29cf', - 'LeftTriangleEqual;': '\u22b4', - 'LeftUpDownVector;': '\u2951', - 'LeftUpTeeVector;': '\u2960', - 'LeftUpVector;': '\u21bf', - 'LeftUpVectorBar;': '\u2958', - 'LeftVector;': '\u21bc', - 'LeftVectorBar;': '\u2952', - 'lEg;': '\u2a8b', - 'leg;': '\u22da', - 'leq;': '\u2264', - 'leqq;': '\u2266', - 'leqslant;': '\u2a7d', - 'les;': '\u2a7d', - 'lescc;': '\u2aa8', - 'lesdot;': '\u2a7f', - 'lesdoto;': '\u2a81', - 'lesdotor;': '\u2a83', - 'lesg;': '\u22da\ufe00', - 'lesges;': '\u2a93', - 'lessapprox;': '\u2a85', - 'lessdot;': '\u22d6', - 'lesseqgtr;': '\u22da', - 'lesseqqgtr;': '\u2a8b', - 'LessEqualGreater;': '\u22da', - 'LessFullEqual;': '\u2266', - 'LessGreater;': '\u2276', - 'lessgtr;': '\u2276', - 'LessLess;': '\u2aa1', - 'lesssim;': '\u2272', - 'LessSlantEqual;': '\u2a7d', - 'LessTilde;': '\u2272', - 'lfisht;': '\u297c', - 'lfloor;': '\u230a', - 'Lfr;': '\U0001d50f', - 'lfr;': '\U0001d529', - 'lg;': '\u2276', - 'lgE;': '\u2a91', - 'lHar;': '\u2962', - 'lhard;': '\u21bd', - 'lharu;': '\u21bc', - 'lharul;': '\u296a', - 'lhblk;': '\u2584', - 'LJcy;': '\u0409', - 'ljcy;': '\u0459', - 'Ll;': '\u22d8', - 'll;': '\u226a', - 'llarr;': '\u21c7', - 'llcorner;': '\u231e', - 'Lleftarrow;': '\u21da', - 'llhard;': '\u296b', - 'lltri;': '\u25fa', - 'Lmidot;': '\u013f', - 'lmidot;': '\u0140', - 'lmoust;': '\u23b0', - 'lmoustache;': '\u23b0', - 'lnap;': '\u2a89', - 'lnapprox;': '\u2a89', - 'lnE;': '\u2268', - 'lne;': '\u2a87', - 'lneq;': '\u2a87', - 'lneqq;': '\u2268', - 'lnsim;': '\u22e6', - 'loang;': '\u27ec', - 'loarr;': '\u21fd', - 'lobrk;': '\u27e6', - 'LongLeftArrow;': '\u27f5', - 'Longleftarrow;': '\u27f8', - 'longleftarrow;': '\u27f5', - 'LongLeftRightArrow;': '\u27f7', - 'Longleftrightarrow;': '\u27fa', - 'longleftrightarrow;': '\u27f7', - 'longmapsto;': '\u27fc', - 'LongRightArrow;': '\u27f6', - 'Longrightarrow;': '\u27f9', - 'longrightarrow;': '\u27f6', - 'looparrowleft;': '\u21ab', - 'looparrowright;': '\u21ac', - 'lopar;': '\u2985', - 'Lopf;': '\U0001d543', - 'lopf;': '\U0001d55d', - 'loplus;': '\u2a2d', - 'lotimes;': '\u2a34', - 'lowast;': '\u2217', - 'lowbar;': '_', - 'LowerLeftArrow;': '\u2199', - 'LowerRightArrow;': '\u2198', - 'loz;': '\u25ca', - 'lozenge;': '\u25ca', - 'lozf;': '\u29eb', - 'lpar;': '(', - 'lparlt;': '\u2993', - 'lrarr;': '\u21c6', - 'lrcorner;': '\u231f', - 'lrhar;': '\u21cb', - 'lrhard;': '\u296d', - 'lrm;': '\u200e', - 'lrtri;': '\u22bf', - 'lsaquo;': '\u2039', - 'Lscr;': '\u2112', - 'lscr;': '\U0001d4c1', - 'Lsh;': '\u21b0', - 'lsh;': '\u21b0', - 'lsim;': '\u2272', - 'lsime;': '\u2a8d', - 'lsimg;': '\u2a8f', - 'lsqb;': '[', - 'lsquo;': '\u2018', - 'lsquor;': '\u201a', - 'Lstrok;': '\u0141', - 'lstrok;': '\u0142', - 'LT': '<', - 'lt': '<', - 'LT;': '<', - 'Lt;': '\u226a', - 'lt;': '<', - 'ltcc;': '\u2aa6', - 'ltcir;': '\u2a79', - 'ltdot;': '\u22d6', - 'lthree;': '\u22cb', - 'ltimes;': '\u22c9', - 'ltlarr;': '\u2976', - 'ltquest;': '\u2a7b', - 'ltri;': '\u25c3', - 'ltrie;': '\u22b4', - 'ltrif;': '\u25c2', - 'ltrPar;': '\u2996', - 'lurdshar;': '\u294a', - 'luruhar;': '\u2966', - 'lvertneqq;': '\u2268\ufe00', - 'lvnE;': '\u2268\ufe00', - 'macr': '\xaf', - 'macr;': '\xaf', - 'male;': '\u2642', - 'malt;': '\u2720', - 'maltese;': '\u2720', - 'Map;': '\u2905', - 'map;': '\u21a6', - 'mapsto;': '\u21a6', - 'mapstodown;': '\u21a7', - 'mapstoleft;': '\u21a4', - 'mapstoup;': '\u21a5', - 'marker;': '\u25ae', - 'mcomma;': '\u2a29', - 'Mcy;': '\u041c', - 'mcy;': '\u043c', - 'mdash;': '\u2014', - 'mDDot;': '\u223a', - 'measuredangle;': '\u2221', - 'MediumSpace;': '\u205f', - 'Mellintrf;': '\u2133', - 'Mfr;': '\U0001d510', - 'mfr;': '\U0001d52a', - 'mho;': '\u2127', - 'micro': '\xb5', - 'micro;': '\xb5', - 'mid;': '\u2223', - 'midast;': '*', - 'midcir;': '\u2af0', - 'middot': '\xb7', - 'middot;': '\xb7', - 'minus;': '\u2212', - 'minusb;': '\u229f', - 'minusd;': '\u2238', - 'minusdu;': '\u2a2a', - 'MinusPlus;': '\u2213', - 'mlcp;': '\u2adb', - 'mldr;': '\u2026', - 'mnplus;': '\u2213', - 'models;': '\u22a7', - 'Mopf;': '\U0001d544', - 'mopf;': '\U0001d55e', - 'mp;': '\u2213', - 'Mscr;': '\u2133', - 'mscr;': '\U0001d4c2', - 'mstpos;': '\u223e', - 'Mu;': '\u039c', - 'mu;': '\u03bc', - 'multimap;': '\u22b8', - 'mumap;': '\u22b8', - 'nabla;': '\u2207', - 'Nacute;': '\u0143', - 'nacute;': '\u0144', - 'nang;': '\u2220\u20d2', - 'nap;': '\u2249', - 'napE;': '\u2a70\u0338', - 'napid;': '\u224b\u0338', - 'napos;': '\u0149', - 'napprox;': '\u2249', - 'natur;': '\u266e', - 'natural;': '\u266e', - 'naturals;': '\u2115', - 'nbsp': '\xa0', - 'nbsp;': '\xa0', - 'nbump;': '\u224e\u0338', - 'nbumpe;': '\u224f\u0338', - 'ncap;': '\u2a43', - 'Ncaron;': '\u0147', - 'ncaron;': '\u0148', - 'Ncedil;': '\u0145', - 'ncedil;': '\u0146', - 'ncong;': '\u2247', - 'ncongdot;': '\u2a6d\u0338', - 'ncup;': '\u2a42', - 'Ncy;': '\u041d', - 'ncy;': '\u043d', - 'ndash;': '\u2013', - 'ne;': '\u2260', - 'nearhk;': '\u2924', - 'neArr;': '\u21d7', - 'nearr;': '\u2197', - 'nearrow;': '\u2197', - 'nedot;': '\u2250\u0338', - 'NegativeMediumSpace;': '\u200b', - 'NegativeThickSpace;': '\u200b', - 'NegativeThinSpace;': '\u200b', - 'NegativeVeryThinSpace;': '\u200b', - 'nequiv;': '\u2262', - 'nesear;': '\u2928', - 'nesim;': '\u2242\u0338', - 'NestedGreaterGreater;': '\u226b', - 'NestedLessLess;': '\u226a', - 'NewLine;': '\n', - 'nexist;': '\u2204', - 'nexists;': '\u2204', - 'Nfr;': '\U0001d511', - 'nfr;': '\U0001d52b', - 'ngE;': '\u2267\u0338', - 'nge;': '\u2271', - 'ngeq;': '\u2271', - 'ngeqq;': '\u2267\u0338', - 'ngeqslant;': '\u2a7e\u0338', - 'nges;': '\u2a7e\u0338', - 'nGg;': '\u22d9\u0338', - 'ngsim;': '\u2275', - 'nGt;': '\u226b\u20d2', - 'ngt;': '\u226f', - 'ngtr;': '\u226f', - 'nGtv;': '\u226b\u0338', - 'nhArr;': '\u21ce', - 'nharr;': '\u21ae', - 'nhpar;': '\u2af2', - 'ni;': '\u220b', - 'nis;': '\u22fc', - 'nisd;': '\u22fa', - 'niv;': '\u220b', - 'NJcy;': '\u040a', - 'njcy;': '\u045a', - 'nlArr;': '\u21cd', - 'nlarr;': '\u219a', - 'nldr;': '\u2025', - 'nlE;': '\u2266\u0338', - 'nle;': '\u2270', - 'nLeftarrow;': '\u21cd', - 'nleftarrow;': '\u219a', - 'nLeftrightarrow;': '\u21ce', - 'nleftrightarrow;': '\u21ae', - 'nleq;': '\u2270', - 'nleqq;': '\u2266\u0338', - 'nleqslant;': '\u2a7d\u0338', - 'nles;': '\u2a7d\u0338', - 'nless;': '\u226e', - 'nLl;': '\u22d8\u0338', - 'nlsim;': '\u2274', - 'nLt;': '\u226a\u20d2', - 'nlt;': '\u226e', - 'nltri;': '\u22ea', - 'nltrie;': '\u22ec', - 'nLtv;': '\u226a\u0338', - 'nmid;': '\u2224', - 'NoBreak;': '\u2060', - 'NonBreakingSpace;': '\xa0', - 'Nopf;': '\u2115', - 'nopf;': '\U0001d55f', - 'not': '\xac', - 'Not;': '\u2aec', - 'not;': '\xac', - 'NotCongruent;': '\u2262', - 'NotCupCap;': '\u226d', - 'NotDoubleVerticalBar;': '\u2226', - 'NotElement;': '\u2209', - 'NotEqual;': '\u2260', - 'NotEqualTilde;': '\u2242\u0338', - 'NotExists;': '\u2204', - 'NotGreater;': '\u226f', - 'NotGreaterEqual;': '\u2271', - 'NotGreaterFullEqual;': '\u2267\u0338', - 'NotGreaterGreater;': '\u226b\u0338', - 'NotGreaterLess;': '\u2279', - 'NotGreaterSlantEqual;': '\u2a7e\u0338', - 'NotGreaterTilde;': '\u2275', - 'NotHumpDownHump;': '\u224e\u0338', - 'NotHumpEqual;': '\u224f\u0338', - 'notin;': '\u2209', - 'notindot;': '\u22f5\u0338', - 'notinE;': '\u22f9\u0338', - 'notinva;': '\u2209', - 'notinvb;': '\u22f7', - 'notinvc;': '\u22f6', - 'NotLeftTriangle;': '\u22ea', - 'NotLeftTriangleBar;': '\u29cf\u0338', - 'NotLeftTriangleEqual;': '\u22ec', - 'NotLess;': '\u226e', - 'NotLessEqual;': '\u2270', - 'NotLessGreater;': '\u2278', - 'NotLessLess;': '\u226a\u0338', - 'NotLessSlantEqual;': '\u2a7d\u0338', - 'NotLessTilde;': '\u2274', - 'NotNestedGreaterGreater;': '\u2aa2\u0338', - 'NotNestedLessLess;': '\u2aa1\u0338', - 'notni;': '\u220c', - 'notniva;': '\u220c', - 'notnivb;': '\u22fe', - 'notnivc;': '\u22fd', - 'NotPrecedes;': '\u2280', - 'NotPrecedesEqual;': '\u2aaf\u0338', - 'NotPrecedesSlantEqual;': '\u22e0', - 'NotReverseElement;': '\u220c', - 'NotRightTriangle;': '\u22eb', - 'NotRightTriangleBar;': '\u29d0\u0338', - 'NotRightTriangleEqual;': '\u22ed', - 'NotSquareSubset;': '\u228f\u0338', - 'NotSquareSubsetEqual;': '\u22e2', - 'NotSquareSuperset;': '\u2290\u0338', - 'NotSquareSupersetEqual;': '\u22e3', - 'NotSubset;': '\u2282\u20d2', - 'NotSubsetEqual;': '\u2288', - 'NotSucceeds;': '\u2281', - 'NotSucceedsEqual;': '\u2ab0\u0338', - 'NotSucceedsSlantEqual;': '\u22e1', - 'NotSucceedsTilde;': '\u227f\u0338', - 'NotSuperset;': '\u2283\u20d2', - 'NotSupersetEqual;': '\u2289', - 'NotTilde;': '\u2241', - 'NotTildeEqual;': '\u2244', - 'NotTildeFullEqual;': '\u2247', - 'NotTildeTilde;': '\u2249', - 'NotVerticalBar;': '\u2224', - 'npar;': '\u2226', - 'nparallel;': '\u2226', - 'nparsl;': '\u2afd\u20e5', - 'npart;': '\u2202\u0338', - 'npolint;': '\u2a14', - 'npr;': '\u2280', - 'nprcue;': '\u22e0', - 'npre;': '\u2aaf\u0338', - 'nprec;': '\u2280', - 'npreceq;': '\u2aaf\u0338', - 'nrArr;': '\u21cf', - 'nrarr;': '\u219b', - 'nrarrc;': '\u2933\u0338', - 'nrarrw;': '\u219d\u0338', - 'nRightarrow;': '\u21cf', - 'nrightarrow;': '\u219b', - 'nrtri;': '\u22eb', - 'nrtrie;': '\u22ed', - 'nsc;': '\u2281', - 'nsccue;': '\u22e1', - 'nsce;': '\u2ab0\u0338', - 'Nscr;': '\U0001d4a9', - 'nscr;': '\U0001d4c3', - 'nshortmid;': '\u2224', - 'nshortparallel;': '\u2226', - 'nsim;': '\u2241', - 'nsime;': '\u2244', - 'nsimeq;': '\u2244', - 'nsmid;': '\u2224', - 'nspar;': '\u2226', - 'nsqsube;': '\u22e2', - 'nsqsupe;': '\u22e3', - 'nsub;': '\u2284', - 'nsubE;': '\u2ac5\u0338', - 'nsube;': '\u2288', - 'nsubset;': '\u2282\u20d2', - 'nsubseteq;': '\u2288', - 'nsubseteqq;': '\u2ac5\u0338', - 'nsucc;': '\u2281', - 'nsucceq;': '\u2ab0\u0338', - 'nsup;': '\u2285', - 'nsupE;': '\u2ac6\u0338', - 'nsupe;': '\u2289', - 'nsupset;': '\u2283\u20d2', - 'nsupseteq;': '\u2289', - 'nsupseteqq;': '\u2ac6\u0338', - 'ntgl;': '\u2279', - 'Ntilde': '\xd1', - 'ntilde': '\xf1', - 'Ntilde;': '\xd1', - 'ntilde;': '\xf1', - 'ntlg;': '\u2278', - 'ntriangleleft;': '\u22ea', - 'ntrianglelefteq;': '\u22ec', - 'ntriangleright;': '\u22eb', - 'ntrianglerighteq;': '\u22ed', - 'Nu;': '\u039d', - 'nu;': '\u03bd', - 'num;': '#', - 'numero;': '\u2116', - 'numsp;': '\u2007', - 'nvap;': '\u224d\u20d2', - 'nVDash;': '\u22af', - 'nVdash;': '\u22ae', - 'nvDash;': '\u22ad', - 'nvdash;': '\u22ac', - 'nvge;': '\u2265\u20d2', - 'nvgt;': '>\u20d2', - 'nvHarr;': '\u2904', - 'nvinfin;': '\u29de', - 'nvlArr;': '\u2902', - 'nvle;': '\u2264\u20d2', - 'nvlt;': '<\u20d2', - 'nvltrie;': '\u22b4\u20d2', - 'nvrArr;': '\u2903', - 'nvrtrie;': '\u22b5\u20d2', - 'nvsim;': '\u223c\u20d2', - 'nwarhk;': '\u2923', - 'nwArr;': '\u21d6', - 'nwarr;': '\u2196', - 'nwarrow;': '\u2196', - 'nwnear;': '\u2927', - 'Oacute': '\xd3', - 'oacute': '\xf3', - 'Oacute;': '\xd3', - 'oacute;': '\xf3', - 'oast;': '\u229b', - 'ocir;': '\u229a', - 'Ocirc': '\xd4', - 'ocirc': '\xf4', - 'Ocirc;': '\xd4', - 'ocirc;': '\xf4', - 'Ocy;': '\u041e', - 'ocy;': '\u043e', - 'odash;': '\u229d', - 'Odblac;': '\u0150', - 'odblac;': '\u0151', - 'odiv;': '\u2a38', - 'odot;': '\u2299', - 'odsold;': '\u29bc', - 'OElig;': '\u0152', - 'oelig;': '\u0153', - 'ofcir;': '\u29bf', - 'Ofr;': '\U0001d512', - 'ofr;': '\U0001d52c', - 'ogon;': '\u02db', - 'Ograve': '\xd2', - 'ograve': '\xf2', - 'Ograve;': '\xd2', - 'ograve;': '\xf2', - 'ogt;': '\u29c1', - 'ohbar;': '\u29b5', - 'ohm;': '\u03a9', - 'oint;': '\u222e', - 'olarr;': '\u21ba', - 'olcir;': '\u29be', - 'olcross;': '\u29bb', - 'oline;': '\u203e', - 'olt;': '\u29c0', - 'Omacr;': '\u014c', - 'omacr;': '\u014d', - 'Omega;': '\u03a9', - 'omega;': '\u03c9', - 'Omicron;': '\u039f', - 'omicron;': '\u03bf', - 'omid;': '\u29b6', - 'ominus;': '\u2296', - 'Oopf;': '\U0001d546', - 'oopf;': '\U0001d560', - 'opar;': '\u29b7', - 'OpenCurlyDoubleQuote;': '\u201c', - 'OpenCurlyQuote;': '\u2018', - 'operp;': '\u29b9', - 'oplus;': '\u2295', - 'Or;': '\u2a54', - 'or;': '\u2228', - 'orarr;': '\u21bb', - 'ord;': '\u2a5d', - 'order;': '\u2134', - 'orderof;': '\u2134', - 'ordf': '\xaa', - 'ordf;': '\xaa', - 'ordm': '\xba', - 'ordm;': '\xba', - 'origof;': '\u22b6', - 'oror;': '\u2a56', - 'orslope;': '\u2a57', - 'orv;': '\u2a5b', - 'oS;': '\u24c8', - 'Oscr;': '\U0001d4aa', - 'oscr;': '\u2134', - 'Oslash': '\xd8', - 'oslash': '\xf8', - 'Oslash;': '\xd8', - 'oslash;': '\xf8', - 'osol;': '\u2298', - 'Otilde': '\xd5', - 'otilde': '\xf5', - 'Otilde;': '\xd5', - 'otilde;': '\xf5', - 'Otimes;': '\u2a37', - 'otimes;': '\u2297', - 'otimesas;': '\u2a36', - 'Ouml': '\xd6', - 'ouml': '\xf6', - 'Ouml;': '\xd6', - 'ouml;': '\xf6', - 'ovbar;': '\u233d', - 'OverBar;': '\u203e', - 'OverBrace;': '\u23de', - 'OverBracket;': '\u23b4', - 'OverParenthesis;': '\u23dc', - 'par;': '\u2225', - 'para': '\xb6', - 'para;': '\xb6', - 'parallel;': '\u2225', - 'parsim;': '\u2af3', - 'parsl;': '\u2afd', - 'part;': '\u2202', - 'PartialD;': '\u2202', - 'Pcy;': '\u041f', - 'pcy;': '\u043f', - 'percnt;': '%', - 'period;': '.', - 'permil;': '\u2030', - 'perp;': '\u22a5', - 'pertenk;': '\u2031', - 'Pfr;': '\U0001d513', - 'pfr;': '\U0001d52d', - 'Phi;': '\u03a6', - 'phi;': '\u03c6', - 'phiv;': '\u03d5', - 'phmmat;': '\u2133', - 'phone;': '\u260e', - 'Pi;': '\u03a0', - 'pi;': '\u03c0', - 'pitchfork;': '\u22d4', - 'piv;': '\u03d6', - 'planck;': '\u210f', - 'planckh;': '\u210e', - 'plankv;': '\u210f', - 'plus;': '+', - 'plusacir;': '\u2a23', - 'plusb;': '\u229e', - 'pluscir;': '\u2a22', - 'plusdo;': '\u2214', - 'plusdu;': '\u2a25', - 'pluse;': '\u2a72', - 'PlusMinus;': '\xb1', - 'plusmn': '\xb1', - 'plusmn;': '\xb1', - 'plussim;': '\u2a26', - 'plustwo;': '\u2a27', - 'pm;': '\xb1', - 'Poincareplane;': '\u210c', - 'pointint;': '\u2a15', - 'Popf;': '\u2119', - 'popf;': '\U0001d561', - 'pound': '\xa3', - 'pound;': '\xa3', - 'Pr;': '\u2abb', - 'pr;': '\u227a', - 'prap;': '\u2ab7', - 'prcue;': '\u227c', - 'prE;': '\u2ab3', - 'pre;': '\u2aaf', - 'prec;': '\u227a', - 'precapprox;': '\u2ab7', - 'preccurlyeq;': '\u227c', - 'Precedes;': '\u227a', - 'PrecedesEqual;': '\u2aaf', - 'PrecedesSlantEqual;': '\u227c', - 'PrecedesTilde;': '\u227e', - 'preceq;': '\u2aaf', - 'precnapprox;': '\u2ab9', - 'precneqq;': '\u2ab5', - 'precnsim;': '\u22e8', - 'precsim;': '\u227e', - 'Prime;': '\u2033', - 'prime;': '\u2032', - 'primes;': '\u2119', - 'prnap;': '\u2ab9', - 'prnE;': '\u2ab5', - 'prnsim;': '\u22e8', - 'prod;': '\u220f', - 'Product;': '\u220f', - 'profalar;': '\u232e', - 'profline;': '\u2312', - 'profsurf;': '\u2313', - 'prop;': '\u221d', - 'Proportion;': '\u2237', - 'Proportional;': '\u221d', - 'propto;': '\u221d', - 'prsim;': '\u227e', - 'prurel;': '\u22b0', - 'Pscr;': '\U0001d4ab', - 'pscr;': '\U0001d4c5', - 'Psi;': '\u03a8', - 'psi;': '\u03c8', - 'puncsp;': '\u2008', - 'Qfr;': '\U0001d514', - 'qfr;': '\U0001d52e', - 'qint;': '\u2a0c', - 'Qopf;': '\u211a', - 'qopf;': '\U0001d562', - 'qprime;': '\u2057', - 'Qscr;': '\U0001d4ac', - 'qscr;': '\U0001d4c6', - 'quaternions;': '\u210d', - 'quatint;': '\u2a16', - 'quest;': '?', - 'questeq;': '\u225f', - 'QUOT': '"', - 'quot': '"', - 'QUOT;': '"', - 'quot;': '"', - 'rAarr;': '\u21db', - 'race;': '\u223d\u0331', - 'Racute;': '\u0154', - 'racute;': '\u0155', - 'radic;': '\u221a', - 'raemptyv;': '\u29b3', - 'Rang;': '\u27eb', - 'rang;': '\u27e9', - 'rangd;': '\u2992', - 'range;': '\u29a5', - 'rangle;': '\u27e9', - 'raquo': '\xbb', - 'raquo;': '\xbb', - 'Rarr;': '\u21a0', - 'rArr;': '\u21d2', - 'rarr;': '\u2192', - 'rarrap;': '\u2975', - 'rarrb;': '\u21e5', - 'rarrbfs;': '\u2920', - 'rarrc;': '\u2933', - 'rarrfs;': '\u291e', - 'rarrhk;': '\u21aa', - 'rarrlp;': '\u21ac', - 'rarrpl;': '\u2945', - 'rarrsim;': '\u2974', - 'Rarrtl;': '\u2916', - 'rarrtl;': '\u21a3', - 'rarrw;': '\u219d', - 'rAtail;': '\u291c', - 'ratail;': '\u291a', - 'ratio;': '\u2236', - 'rationals;': '\u211a', - 'RBarr;': '\u2910', - 'rBarr;': '\u290f', - 'rbarr;': '\u290d', - 'rbbrk;': '\u2773', - 'rbrace;': '}', - 'rbrack;': ']', - 'rbrke;': '\u298c', - 'rbrksld;': '\u298e', - 'rbrkslu;': '\u2990', - 'Rcaron;': '\u0158', - 'rcaron;': '\u0159', - 'Rcedil;': '\u0156', - 'rcedil;': '\u0157', - 'rceil;': '\u2309', - 'rcub;': '}', - 'Rcy;': '\u0420', - 'rcy;': '\u0440', - 'rdca;': '\u2937', - 'rdldhar;': '\u2969', - 'rdquo;': '\u201d', - 'rdquor;': '\u201d', - 'rdsh;': '\u21b3', - 'Re;': '\u211c', - 'real;': '\u211c', - 'realine;': '\u211b', - 'realpart;': '\u211c', - 'reals;': '\u211d', - 'rect;': '\u25ad', - 'REG': '\xae', - 'reg': '\xae', - 'REG;': '\xae', - 'reg;': '\xae', - 'ReverseElement;': '\u220b', - 'ReverseEquilibrium;': '\u21cb', - 'ReverseUpEquilibrium;': '\u296f', - 'rfisht;': '\u297d', - 'rfloor;': '\u230b', - 'Rfr;': '\u211c', - 'rfr;': '\U0001d52f', - 'rHar;': '\u2964', - 'rhard;': '\u21c1', - 'rharu;': '\u21c0', - 'rharul;': '\u296c', - 'Rho;': '\u03a1', - 'rho;': '\u03c1', - 'rhov;': '\u03f1', - 'RightAngleBracket;': '\u27e9', - 'RightArrow;': '\u2192', - 'Rightarrow;': '\u21d2', - 'rightarrow;': '\u2192', - 'RightArrowBar;': '\u21e5', - 'RightArrowLeftArrow;': '\u21c4', - 'rightarrowtail;': '\u21a3', - 'RightCeiling;': '\u2309', - 'RightDoubleBracket;': '\u27e7', - 'RightDownTeeVector;': '\u295d', - 'RightDownVector;': '\u21c2', - 'RightDownVectorBar;': '\u2955', - 'RightFloor;': '\u230b', - 'rightharpoondown;': '\u21c1', - 'rightharpoonup;': '\u21c0', - 'rightleftarrows;': '\u21c4', - 'rightleftharpoons;': '\u21cc', - 'rightrightarrows;': '\u21c9', - 'rightsquigarrow;': '\u219d', - 'RightTee;': '\u22a2', - 'RightTeeArrow;': '\u21a6', - 'RightTeeVector;': '\u295b', - 'rightthreetimes;': '\u22cc', - 'RightTriangle;': '\u22b3', - 'RightTriangleBar;': '\u29d0', - 'RightTriangleEqual;': '\u22b5', - 'RightUpDownVector;': '\u294f', - 'RightUpTeeVector;': '\u295c', - 'RightUpVector;': '\u21be', - 'RightUpVectorBar;': '\u2954', - 'RightVector;': '\u21c0', - 'RightVectorBar;': '\u2953', - 'ring;': '\u02da', - 'risingdotseq;': '\u2253', - 'rlarr;': '\u21c4', - 'rlhar;': '\u21cc', - 'rlm;': '\u200f', - 'rmoust;': '\u23b1', - 'rmoustache;': '\u23b1', - 'rnmid;': '\u2aee', - 'roang;': '\u27ed', - 'roarr;': '\u21fe', - 'robrk;': '\u27e7', - 'ropar;': '\u2986', - 'Ropf;': '\u211d', - 'ropf;': '\U0001d563', - 'roplus;': '\u2a2e', - 'rotimes;': '\u2a35', - 'RoundImplies;': '\u2970', - 'rpar;': ')', - 'rpargt;': '\u2994', - 'rppolint;': '\u2a12', - 'rrarr;': '\u21c9', - 'Rrightarrow;': '\u21db', - 'rsaquo;': '\u203a', - 'Rscr;': '\u211b', - 'rscr;': '\U0001d4c7', - 'Rsh;': '\u21b1', - 'rsh;': '\u21b1', - 'rsqb;': ']', - 'rsquo;': '\u2019', - 'rsquor;': '\u2019', - 'rthree;': '\u22cc', - 'rtimes;': '\u22ca', - 'rtri;': '\u25b9', - 'rtrie;': '\u22b5', - 'rtrif;': '\u25b8', - 'rtriltri;': '\u29ce', - 'RuleDelayed;': '\u29f4', - 'ruluhar;': '\u2968', - 'rx;': '\u211e', - 'Sacute;': '\u015a', - 'sacute;': '\u015b', - 'sbquo;': '\u201a', - 'Sc;': '\u2abc', - 'sc;': '\u227b', - 'scap;': '\u2ab8', - 'Scaron;': '\u0160', - 'scaron;': '\u0161', - 'sccue;': '\u227d', - 'scE;': '\u2ab4', - 'sce;': '\u2ab0', - 'Scedil;': '\u015e', - 'scedil;': '\u015f', - 'Scirc;': '\u015c', - 'scirc;': '\u015d', - 'scnap;': '\u2aba', - 'scnE;': '\u2ab6', - 'scnsim;': '\u22e9', - 'scpolint;': '\u2a13', - 'scsim;': '\u227f', - 'Scy;': '\u0421', - 'scy;': '\u0441', - 'sdot;': '\u22c5', - 'sdotb;': '\u22a1', - 'sdote;': '\u2a66', - 'searhk;': '\u2925', - 'seArr;': '\u21d8', - 'searr;': '\u2198', - 'searrow;': '\u2198', - 'sect': '\xa7', - 'sect;': '\xa7', - 'semi;': ';', - 'seswar;': '\u2929', - 'setminus;': '\u2216', - 'setmn;': '\u2216', - 'sext;': '\u2736', - 'Sfr;': '\U0001d516', - 'sfr;': '\U0001d530', - 'sfrown;': '\u2322', - 'sharp;': '\u266f', - 'SHCHcy;': '\u0429', - 'shchcy;': '\u0449', - 'SHcy;': '\u0428', - 'shcy;': '\u0448', - 'ShortDownArrow;': '\u2193', - 'ShortLeftArrow;': '\u2190', - 'shortmid;': '\u2223', - 'shortparallel;': '\u2225', - 'ShortRightArrow;': '\u2192', - 'ShortUpArrow;': '\u2191', - 'shy': '\xad', - 'shy;': '\xad', - 'Sigma;': '\u03a3', - 'sigma;': '\u03c3', - 'sigmaf;': '\u03c2', - 'sigmav;': '\u03c2', - 'sim;': '\u223c', - 'simdot;': '\u2a6a', - 'sime;': '\u2243', - 'simeq;': '\u2243', - 'simg;': '\u2a9e', - 'simgE;': '\u2aa0', - 'siml;': '\u2a9d', - 'simlE;': '\u2a9f', - 'simne;': '\u2246', - 'simplus;': '\u2a24', - 'simrarr;': '\u2972', - 'slarr;': '\u2190', - 'SmallCircle;': '\u2218', - 'smallsetminus;': '\u2216', - 'smashp;': '\u2a33', - 'smeparsl;': '\u29e4', - 'smid;': '\u2223', - 'smile;': '\u2323', - 'smt;': '\u2aaa', - 'smte;': '\u2aac', - 'smtes;': '\u2aac\ufe00', - 'SOFTcy;': '\u042c', - 'softcy;': '\u044c', - 'sol;': '/', - 'solb;': '\u29c4', - 'solbar;': '\u233f', - 'Sopf;': '\U0001d54a', - 'sopf;': '\U0001d564', - 'spades;': '\u2660', - 'spadesuit;': '\u2660', - 'spar;': '\u2225', - 'sqcap;': '\u2293', - 'sqcaps;': '\u2293\ufe00', - 'sqcup;': '\u2294', - 'sqcups;': '\u2294\ufe00', - 'Sqrt;': '\u221a', - 'sqsub;': '\u228f', - 'sqsube;': '\u2291', - 'sqsubset;': '\u228f', - 'sqsubseteq;': '\u2291', - 'sqsup;': '\u2290', - 'sqsupe;': '\u2292', - 'sqsupset;': '\u2290', - 'sqsupseteq;': '\u2292', - 'squ;': '\u25a1', - 'Square;': '\u25a1', - 'square;': '\u25a1', - 'SquareIntersection;': '\u2293', - 'SquareSubset;': '\u228f', - 'SquareSubsetEqual;': '\u2291', - 'SquareSuperset;': '\u2290', - 'SquareSupersetEqual;': '\u2292', - 'SquareUnion;': '\u2294', - 'squarf;': '\u25aa', - 'squf;': '\u25aa', - 'srarr;': '\u2192', - 'Sscr;': '\U0001d4ae', - 'sscr;': '\U0001d4c8', - 'ssetmn;': '\u2216', - 'ssmile;': '\u2323', - 'sstarf;': '\u22c6', - 'Star;': '\u22c6', - 'star;': '\u2606', - 'starf;': '\u2605', - 'straightepsilon;': '\u03f5', - 'straightphi;': '\u03d5', - 'strns;': '\xaf', - 'Sub;': '\u22d0', - 'sub;': '\u2282', - 'subdot;': '\u2abd', - 'subE;': '\u2ac5', - 'sube;': '\u2286', - 'subedot;': '\u2ac3', - 'submult;': '\u2ac1', - 'subnE;': '\u2acb', - 'subne;': '\u228a', - 'subplus;': '\u2abf', - 'subrarr;': '\u2979', - 'Subset;': '\u22d0', - 'subset;': '\u2282', - 'subseteq;': '\u2286', - 'subseteqq;': '\u2ac5', - 'SubsetEqual;': '\u2286', - 'subsetneq;': '\u228a', - 'subsetneqq;': '\u2acb', - 'subsim;': '\u2ac7', - 'subsub;': '\u2ad5', - 'subsup;': '\u2ad3', - 'succ;': '\u227b', - 'succapprox;': '\u2ab8', - 'succcurlyeq;': '\u227d', - 'Succeeds;': '\u227b', - 'SucceedsEqual;': '\u2ab0', - 'SucceedsSlantEqual;': '\u227d', - 'SucceedsTilde;': '\u227f', - 'succeq;': '\u2ab0', - 'succnapprox;': '\u2aba', - 'succneqq;': '\u2ab6', - 'succnsim;': '\u22e9', - 'succsim;': '\u227f', - 'SuchThat;': '\u220b', - 'Sum;': '\u2211', - 'sum;': '\u2211', - 'sung;': '\u266a', - 'sup1': '\xb9', - 'sup1;': '\xb9', - 'sup2': '\xb2', - 'sup2;': '\xb2', - 'sup3': '\xb3', - 'sup3;': '\xb3', - 'Sup;': '\u22d1', - 'sup;': '\u2283', - 'supdot;': '\u2abe', - 'supdsub;': '\u2ad8', - 'supE;': '\u2ac6', - 'supe;': '\u2287', - 'supedot;': '\u2ac4', - 'Superset;': '\u2283', - 'SupersetEqual;': '\u2287', - 'suphsol;': '\u27c9', - 'suphsub;': '\u2ad7', - 'suplarr;': '\u297b', - 'supmult;': '\u2ac2', - 'supnE;': '\u2acc', - 'supne;': '\u228b', - 'supplus;': '\u2ac0', - 'Supset;': '\u22d1', - 'supset;': '\u2283', - 'supseteq;': '\u2287', - 'supseteqq;': '\u2ac6', - 'supsetneq;': '\u228b', - 'supsetneqq;': '\u2acc', - 'supsim;': '\u2ac8', - 'supsub;': '\u2ad4', - 'supsup;': '\u2ad6', - 'swarhk;': '\u2926', - 'swArr;': '\u21d9', - 'swarr;': '\u2199', - 'swarrow;': '\u2199', - 'swnwar;': '\u292a', - 'szlig': '\xdf', - 'szlig;': '\xdf', - 'Tab;': '\t', - 'target;': '\u2316', - 'Tau;': '\u03a4', - 'tau;': '\u03c4', - 'tbrk;': '\u23b4', - 'Tcaron;': '\u0164', - 'tcaron;': '\u0165', - 'Tcedil;': '\u0162', - 'tcedil;': '\u0163', - 'Tcy;': '\u0422', - 'tcy;': '\u0442', - 'tdot;': '\u20db', - 'telrec;': '\u2315', - 'Tfr;': '\U0001d517', - 'tfr;': '\U0001d531', - 'there4;': '\u2234', - 'Therefore;': '\u2234', - 'therefore;': '\u2234', - 'Theta;': '\u0398', - 'theta;': '\u03b8', - 'thetasym;': '\u03d1', - 'thetav;': '\u03d1', - 'thickapprox;': '\u2248', - 'thicksim;': '\u223c', - 'ThickSpace;': '\u205f\u200a', - 'thinsp;': '\u2009', - 'ThinSpace;': '\u2009', - 'thkap;': '\u2248', - 'thksim;': '\u223c', - 'THORN': '\xde', - 'thorn': '\xfe', - 'THORN;': '\xde', - 'thorn;': '\xfe', - 'Tilde;': '\u223c', - 'tilde;': '\u02dc', - 'TildeEqual;': '\u2243', - 'TildeFullEqual;': '\u2245', - 'TildeTilde;': '\u2248', - 'times': '\xd7', - 'times;': '\xd7', - 'timesb;': '\u22a0', - 'timesbar;': '\u2a31', - 'timesd;': '\u2a30', - 'tint;': '\u222d', - 'toea;': '\u2928', - 'top;': '\u22a4', - 'topbot;': '\u2336', - 'topcir;': '\u2af1', - 'Topf;': '\U0001d54b', - 'topf;': '\U0001d565', - 'topfork;': '\u2ada', - 'tosa;': '\u2929', - 'tprime;': '\u2034', - 'TRADE;': '\u2122', - 'trade;': '\u2122', - 'triangle;': '\u25b5', - 'triangledown;': '\u25bf', - 'triangleleft;': '\u25c3', - 'trianglelefteq;': '\u22b4', - 'triangleq;': '\u225c', - 'triangleright;': '\u25b9', - 'trianglerighteq;': '\u22b5', - 'tridot;': '\u25ec', - 'trie;': '\u225c', - 'triminus;': '\u2a3a', - 'TripleDot;': '\u20db', - 'triplus;': '\u2a39', - 'trisb;': '\u29cd', - 'tritime;': '\u2a3b', - 'trpezium;': '\u23e2', - 'Tscr;': '\U0001d4af', - 'tscr;': '\U0001d4c9', - 'TScy;': '\u0426', - 'tscy;': '\u0446', - 'TSHcy;': '\u040b', - 'tshcy;': '\u045b', - 'Tstrok;': '\u0166', - 'tstrok;': '\u0167', - 'twixt;': '\u226c', - 'twoheadleftarrow;': '\u219e', - 'twoheadrightarrow;': '\u21a0', - 'Uacute': '\xda', - 'uacute': '\xfa', - 'Uacute;': '\xda', - 'uacute;': '\xfa', - 'Uarr;': '\u219f', - 'uArr;': '\u21d1', - 'uarr;': '\u2191', - 'Uarrocir;': '\u2949', - 'Ubrcy;': '\u040e', - 'ubrcy;': '\u045e', - 'Ubreve;': '\u016c', - 'ubreve;': '\u016d', - 'Ucirc': '\xdb', - 'ucirc': '\xfb', - 'Ucirc;': '\xdb', - 'ucirc;': '\xfb', - 'Ucy;': '\u0423', - 'ucy;': '\u0443', - 'udarr;': '\u21c5', - 'Udblac;': '\u0170', - 'udblac;': '\u0171', - 'udhar;': '\u296e', - 'ufisht;': '\u297e', - 'Ufr;': '\U0001d518', - 'ufr;': '\U0001d532', - 'Ugrave': '\xd9', - 'ugrave': '\xf9', - 'Ugrave;': '\xd9', - 'ugrave;': '\xf9', - 'uHar;': '\u2963', - 'uharl;': '\u21bf', - 'uharr;': '\u21be', - 'uhblk;': '\u2580', - 'ulcorn;': '\u231c', - 'ulcorner;': '\u231c', - 'ulcrop;': '\u230f', - 'ultri;': '\u25f8', - 'Umacr;': '\u016a', - 'umacr;': '\u016b', - 'uml': '\xa8', - 'uml;': '\xa8', - 'UnderBar;': '_', - 'UnderBrace;': '\u23df', - 'UnderBracket;': '\u23b5', - 'UnderParenthesis;': '\u23dd', - 'Union;': '\u22c3', - 'UnionPlus;': '\u228e', - 'Uogon;': '\u0172', - 'uogon;': '\u0173', - 'Uopf;': '\U0001d54c', - 'uopf;': '\U0001d566', - 'UpArrow;': '\u2191', - 'Uparrow;': '\u21d1', - 'uparrow;': '\u2191', - 'UpArrowBar;': '\u2912', - 'UpArrowDownArrow;': '\u21c5', - 'UpDownArrow;': '\u2195', - 'Updownarrow;': '\u21d5', - 'updownarrow;': '\u2195', - 'UpEquilibrium;': '\u296e', - 'upharpoonleft;': '\u21bf', - 'upharpoonright;': '\u21be', - 'uplus;': '\u228e', - 'UpperLeftArrow;': '\u2196', - 'UpperRightArrow;': '\u2197', - 'Upsi;': '\u03d2', - 'upsi;': '\u03c5', - 'upsih;': '\u03d2', - 'Upsilon;': '\u03a5', - 'upsilon;': '\u03c5', - 'UpTee;': '\u22a5', - 'UpTeeArrow;': '\u21a5', - 'upuparrows;': '\u21c8', - 'urcorn;': '\u231d', - 'urcorner;': '\u231d', - 'urcrop;': '\u230e', - 'Uring;': '\u016e', - 'uring;': '\u016f', - 'urtri;': '\u25f9', - 'Uscr;': '\U0001d4b0', - 'uscr;': '\U0001d4ca', - 'utdot;': '\u22f0', - 'Utilde;': '\u0168', - 'utilde;': '\u0169', - 'utri;': '\u25b5', - 'utrif;': '\u25b4', - 'uuarr;': '\u21c8', - 'Uuml': '\xdc', - 'uuml': '\xfc', - 'Uuml;': '\xdc', - 'uuml;': '\xfc', - 'uwangle;': '\u29a7', - 'vangrt;': '\u299c', - 'varepsilon;': '\u03f5', - 'varkappa;': '\u03f0', - 'varnothing;': '\u2205', - 'varphi;': '\u03d5', - 'varpi;': '\u03d6', - 'varpropto;': '\u221d', - 'vArr;': '\u21d5', - 'varr;': '\u2195', - 'varrho;': '\u03f1', - 'varsigma;': '\u03c2', - 'varsubsetneq;': '\u228a\ufe00', - 'varsubsetneqq;': '\u2acb\ufe00', - 'varsupsetneq;': '\u228b\ufe00', - 'varsupsetneqq;': '\u2acc\ufe00', - 'vartheta;': '\u03d1', - 'vartriangleleft;': '\u22b2', - 'vartriangleright;': '\u22b3', - 'Vbar;': '\u2aeb', - 'vBar;': '\u2ae8', - 'vBarv;': '\u2ae9', - 'Vcy;': '\u0412', - 'vcy;': '\u0432', - 'VDash;': '\u22ab', - 'Vdash;': '\u22a9', - 'vDash;': '\u22a8', - 'vdash;': '\u22a2', - 'Vdashl;': '\u2ae6', - 'Vee;': '\u22c1', - 'vee;': '\u2228', - 'veebar;': '\u22bb', - 'veeeq;': '\u225a', - 'vellip;': '\u22ee', - 'Verbar;': '\u2016', - 'verbar;': '|', - 'Vert;': '\u2016', - 'vert;': '|', - 'VerticalBar;': '\u2223', - 'VerticalLine;': '|', - 'VerticalSeparator;': '\u2758', - 'VerticalTilde;': '\u2240', - 'VeryThinSpace;': '\u200a', - 'Vfr;': '\U0001d519', - 'vfr;': '\U0001d533', - 'vltri;': '\u22b2', - 'vnsub;': '\u2282\u20d2', - 'vnsup;': '\u2283\u20d2', - 'Vopf;': '\U0001d54d', - 'vopf;': '\U0001d567', - 'vprop;': '\u221d', - 'vrtri;': '\u22b3', - 'Vscr;': '\U0001d4b1', - 'vscr;': '\U0001d4cb', - 'vsubnE;': '\u2acb\ufe00', - 'vsubne;': '\u228a\ufe00', - 'vsupnE;': '\u2acc\ufe00', - 'vsupne;': '\u228b\ufe00', - 'Vvdash;': '\u22aa', - 'vzigzag;': '\u299a', - 'Wcirc;': '\u0174', - 'wcirc;': '\u0175', - 'wedbar;': '\u2a5f', - 'Wedge;': '\u22c0', - 'wedge;': '\u2227', - 'wedgeq;': '\u2259', - 'weierp;': '\u2118', - 'Wfr;': '\U0001d51a', - 'wfr;': '\U0001d534', - 'Wopf;': '\U0001d54e', - 'wopf;': '\U0001d568', - 'wp;': '\u2118', - 'wr;': '\u2240', - 'wreath;': '\u2240', - 'Wscr;': '\U0001d4b2', - 'wscr;': '\U0001d4cc', - 'xcap;': '\u22c2', - 'xcirc;': '\u25ef', - 'xcup;': '\u22c3', - 'xdtri;': '\u25bd', - 'Xfr;': '\U0001d51b', - 'xfr;': '\U0001d535', - 'xhArr;': '\u27fa', - 'xharr;': '\u27f7', - 'Xi;': '\u039e', - 'xi;': '\u03be', - 'xlArr;': '\u27f8', - 'xlarr;': '\u27f5', - 'xmap;': '\u27fc', - 'xnis;': '\u22fb', - 'xodot;': '\u2a00', - 'Xopf;': '\U0001d54f', - 'xopf;': '\U0001d569', - 'xoplus;': '\u2a01', - 'xotime;': '\u2a02', - 'xrArr;': '\u27f9', - 'xrarr;': '\u27f6', - 'Xscr;': '\U0001d4b3', - 'xscr;': '\U0001d4cd', - 'xsqcup;': '\u2a06', - 'xuplus;': '\u2a04', - 'xutri;': '\u25b3', - 'xvee;': '\u22c1', - 'xwedge;': '\u22c0', - 'Yacute': '\xdd', - 'yacute': '\xfd', - 'Yacute;': '\xdd', - 'yacute;': '\xfd', - 'YAcy;': '\u042f', - 'yacy;': '\u044f', - 'Ycirc;': '\u0176', - 'ycirc;': '\u0177', - 'Ycy;': '\u042b', - 'ycy;': '\u044b', - 'yen': '\xa5', - 'yen;': '\xa5', - 'Yfr;': '\U0001d51c', - 'yfr;': '\U0001d536', - 'YIcy;': '\u0407', - 'yicy;': '\u0457', - 'Yopf;': '\U0001d550', - 'yopf;': '\U0001d56a', - 'Yscr;': '\U0001d4b4', - 'yscr;': '\U0001d4ce', - 'YUcy;': '\u042e', - 'yucy;': '\u044e', - 'yuml': '\xff', - 'Yuml;': '\u0178', - 'yuml;': '\xff', - 'Zacute;': '\u0179', - 'zacute;': '\u017a', - 'Zcaron;': '\u017d', - 'zcaron;': '\u017e', - 'Zcy;': '\u0417', - 'zcy;': '\u0437', - 'Zdot;': '\u017b', - 'zdot;': '\u017c', - 'zeetrf;': '\u2128', - 'ZeroWidthSpace;': '\u200b', - 'Zeta;': '\u0396', - 'zeta;': '\u03b6', - 'Zfr;': '\u2128', - 'zfr;': '\U0001d537', - 'ZHcy;': '\u0416', - 'zhcy;': '\u0436', - 'zigrarr;': '\u21dd', - 'Zopf;': '\u2124', - 'zopf;': '\U0001d56b', - 'Zscr;': '\U0001d4b5', - 'zscr;': '\U0001d4cf', - 'zwj;': '\u200d', - 'zwnj;': '\u200c', -} - - -class EntitySubstitution(object): - """The ability to substitute XML or HTML entities for certain characters.""" - - def _populate_class_variables(): - """Initialize variables used by this class to manage the plethora of - HTML5 named entities. - - This function returns a 3-tuple containing two dictionaries - and a regular expression: - - unicode_to_name - A mapping of Unicode strings like "⦨" to - entity names like "angmsdaa". When a single Unicode string has - multiple entity names, we try to choose the most commonly-used - name. - - name_to_unicode: A mapping of entity names like "angmsdaa" to - Unicode strings like "⦨". - - named_entity_re: A regular expression matching (almost) any - Unicode string that corresponds to an HTML5 named entity. - """ - unicode_to_name = {} - name_to_unicode = {} - - short_entities = set() - long_entities_by_first_character = defaultdict(set) - - for name_with_semicolon, character in sorted(html5.items()): - # "It is intentional, for legacy compatibility, that many - # code points have multiple character reference names. For - # example, some appear both with and without the trailing - # semicolon, or with different capitalizations." - # - https://html.spec.whatwg.org/multipage/named-characters.html#named-character-references - # - # The parsers are in charge of handling (or not) character - # references with no trailing semicolon, so we remove the - # semicolon whenever it appears. - if name_with_semicolon.endswith(';'): - name = name_with_semicolon[:-1] - else: - name = name_with_semicolon - - # When parsing HTML, we want to recognize any known named - # entity and convert it to a sequence of Unicode - # characters. - if name not in name_to_unicode: - name_to_unicode[name] = character - - # When _generating_ HTML, we want to recognize special - # character sequences that _could_ be converted to named - # entities. - unicode_to_name[character] = name - - # We also need to build a regular expression that lets us - # _find_ those characters in output strings so we can - # replace them. - # - # This is tricky, for two reasons. - - if (len(character) == 1 and ord(character) < 128 - and character not in '<>&'): - # First, it would be annoying to turn single ASCII - # characters like | into named entities like - # |. The exceptions are <>&, which we _must_ - # turn into named entities to produce valid HTML. - continue - - if len(character) > 1 and all(ord(x) < 128 for x in character): - # We also do not want to turn _combinations_ of ASCII - # characters like 'fj' into named entities like 'fj', - # though that's more debateable. - continue - - # Second, some named entities have a Unicode value that's - # a subset of the Unicode value for some _other_ named - # entity. As an example, \u2267' is ≧, - # but '\u2267\u0338' is ≧̸. Our regular - # expression needs to match the first two characters of - # "\u2267\u0338foo", but only the first character of - # "\u2267foo". - # - # In this step, we build two sets of characters that - # _eventually_ need to go into the regular expression. But - # we won't know exactly what the regular expression needs - # to look like until we've gone through the entire list of - # named entities. - if len(character) == 1: - short_entities.add(character) - else: - long_entities_by_first_character[character[0]].add(character) - - # Now that we've been through the entire list of entities, we - # can create a regular expression that matches any of them. - particles = set() - for short in short_entities: - long_versions = long_entities_by_first_character[short] - if not long_versions: - particles.add(short) - else: - ignore = "".join([x[1] for x in long_versions]) - # This finds, e.g. \u2267 but only if it is _not_ - # followed by \u0338. - particles.add("%s(?![%s])" % (short, ignore)) - - for long_entities in list(long_entities_by_first_character.values()): - for long_entity in long_entities: - particles.add(long_entity) - - re_definition = "(%s)" % "|".join(particles) - - # If an entity shows up in both html5 and codepoint2name, it's - # likely that HTML5 gives it several different names, such as - # 'rsquo' and 'rsquor'. When converting Unicode characters to - # named entities, the codepoint2name name should take - # precedence where possible, since that's the more easily - # recognizable one. - for codepoint, name in list(codepoint2name.items()): - character = chr(codepoint) - unicode_to_name[character] = name - - return unicode_to_name, name_to_unicode, 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 string.""" - 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 string.""" - 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 known_definite_encodings argument to the constructor). - - 2. An encoding determined by sniffing the document's byte-order mark. - - 3. Encodings you specifically tell EncodingDetector to try if - byte-order mark sniffing fails (the user_encodings argument to the - constructor). - - 4. 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.) - - 5. An encoding detected through textual analysis by chardet, - cchardet, or a similar external library. - - 4. UTF-8. - - 5. Windows-1252. - - """ - def __init__(self, markup, known_definite_encodings=None, - is_html=False, exclude_encodings=None, - user_encodings=None, override_encodings=None): - """Constructor. - - :param markup: Some markup in an unknown encoding. - - :param known_definite_encodings: When determining the encoding - of `markup`, these encodings will be tried first, in - order. In HTML terms, this corresponds to the "known - definite encoding" step defined here: - https://html.spec.whatwg.org/multipage/parsing.html#parsing-with-a-known-character-encoding - - :param user_encodings: These encodings will be tried after the - `known_definite_encodings` have been tried and failed, and - after an attempt to sniff the encoding by looking at a - byte order mark has failed. In HTML terms, this - corresponds to the step "user has explicitly instructed - the user agent to override the document's character - encoding", defined here: - https://html.spec.whatwg.org/multipage/parsing.html#determining-the-character-encoding - - :param override_encodings: A deprecated alias for - known_definite_encodings. Any encodings here will be tried - immediately after the encodings in - known_definite_encodings. - - :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.known_definite_encodings = list(known_definite_encodings or []) - if override_encodings: - self.known_definite_encodings += override_encodings - self.user_encodings = user_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() - - # First, try the known definite encodings - for e in self.known_definite_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 - - # Sniffing the byte-order mark did nothing; try the user - # encodings. - for e in self.user_encodings: - if self._usable(e, tried): - yield e - - # 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, known_definite_encodings=[], - smart_quotes_to=None, is_html=False, exclude_encodings=[], - user_encodings=None, override_encodings=None - ): - """Constructor. - - :param markup: A bytestring representing markup in an unknown encoding. - - :param known_definite_encodings: When determining the encoding - of `markup`, these encodings will be tried first, in - order. In HTML terms, this corresponds to the "known - definite encoding" step defined here: - https://html.spec.whatwg.org/multipage/parsing.html#parsing-with-a-known-character-encoding - - :param user_encodings: These encodings will be tried after the - `known_definite_encodings` have been tried and failed, and - after an attempt to sniff the encoding by looking at a - byte order mark has failed. In HTML terms, this - corresponds to the step "user has explicitly instructed - the user agent to override the document's character - encoding", defined here: - https://html.spec.whatwg.org/multipage/parsing.html#determining-the-character-encoding - - :param override_encodings: A deprecated alias for - known_definite_encodings. Any encodings here will be tried - immediately after the encodings in - known_definite_encodings. - - :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, known_definite_encodings, is_html, exclude_encodings, - user_encodings, override_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/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/diagnose.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/diagnose.py deleted file mode 100644 index 500e92df..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/diagnose.py +++ /dev/null @@ -1,242 +0,0 @@ -"""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/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/element.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/element.py deleted file mode 100644 index 82a986e4..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/element.py +++ /dev/null @@ -1,2255 +0,0 @@ -# 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 not name: - obj = str.__new__(cls, prefix) - elif not prefix: - # 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 - - default = object() - def _all_strings(self, strip=False, types=default): - """Yield all strings of certain classes, possibly stripping them. - - This is implemented differently in Tag and NavigableString. - """ - raise NotImplementedError() - - @property - def stripped_strings(self): - """Yield all strings in this PageElement, 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=default): - """Get all child strings of this PageElement, 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. - - :param types: A tuple of NavigableString subclasses. Any - strings of a subclass not found in this list will be - ignored. Although there are exceptions, the default - behavior in most cases is to consider only NavigableString - and CData objects. That means no comments, processing - instructions, 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 replace_with(self, *args): - """Replace this PageElement with one or more PageElements, keeping the - rest of the tree the same. - - :param args: One or more PageElements. - :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 len(args) == 1 and args[0] is self: - return - if any(x is self.parent for x in args): - 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) - for idx, replace_with in enumerate(args, start=my_index): - old_parent.insert(idx, 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.") - - def _all_strings(self, strip=False, types=PageElement.default): - """Yield all strings of certain classes, possibly stripping them. - - This makes it easy for NavigableString to implement methods - like get_text() as conveniences, creating a consistent - text-extraction API across all PageElements. - - :param strip: If True, all strings will be stripped before being - yielded. - - :param types: A tuple of NavigableString subclasses. If this - NavigableString isn't one of those subclasses, the - sequence will be empty. By default, the subclasses - considered are NavigableString and CData objects. That - means no comments, processing instructions, etc. - - :yield: A sequence that either contains this string, or is empty. - - """ - if types is self.default: - # This is kept in Tag because it's full of subclasses of - # this class, which aren't defined until later in the file. - types = Tag.DEFAULT_INTERESTING_STRING_TYPES - - # Do nothing if the caller is looking for specific types of - # string, and we're of a different type. - my_type = type(self) - if types is not None: - if isinstance(types, type): - # Looking for a single type. - if my_type is not types: - return - elif my_type not in types: - # Looking for one of a list of types. - return - - value = self - if strip: - value = value.strip() - if len(value) > 0: - yield value - strings = property(_all_strings) - -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, - interesting_string_types=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. - :param interesting_string_types: This is a NavigableString - subclass or a tuple of them. When iterating over this - Tag's strings in methods like Tag.strings or Tag.get_text, - these are the types of strings that are interesting enough - to be considered. The default is to consider - NavigableString and CData the only interesting string - subtypes. - """ - 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 - self.interesting_string_types = interesting_string_types - 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 - - if self.name in builder.string_containers: - # This sort of tag uses a special string container - # subclass for most of its strings. When we ask the - self.interesting_string_types = builder.string_containers[self.name] - else: - self.interesting_string_types = self.DEFAULT_INTERESTING_STRING_TYPES - - 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)) - - DEFAULT_INTERESTING_STRING_TYPES = (NavigableString, CData) - def _all_strings(self, strip=False, types=PageElement.default): - """Yield all strings of certain classes, possibly stripping them. - - :param strip: If True, all strings will be stripped before being - yielded. - - :param types: A tuple of NavigableString subclasses. Any strings of - a subclass not found in this list will be ignored. By - default, the subclasses considered are the ones found in - self.interesting_string_types. If that's not specified, - only NavigableString and CData objects will be - considered. That means no comments, processing - instructions, etc. - - :yield: A sequence of strings. - - """ - if types is self.default: - types = self.interesting_string_types - - for descendant in self.descendants: - if (types is None and not isinstance(descendant, NavigableString)): - continue - descendant_type = type(descendant) - if isinstance(types, type): - if descendant_type is not types: - # We're not interested in strings of this type. - continue - elif types is not None and descendant_type not in types: - # We're not interested in strings of this type. - continue - if strip: - descendant = descendant.strip() - if len(descendant) == 0: - continue - yield descendant - strings = property(_all_strings) - - 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/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/formatter.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/formatter.py
deleted file mode 100644
index 3bd9f859..00000000
--- a/IKEA_scraper/.venv/lib/python3.9/site-packages/bs4/formatter.py
+++ /dev/null
@@ -1,165 +0,0 @@
-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, as
-                 well as some optimizations in the way tags are rendered.
-     * '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,
-            empty_attributes_are_booleans=False,
-    ):
-        """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 TreeBuilderSmokeTest(object): - # Tests that are common to HTML and XML tree builders. - - def test_fuzzed_input(self): - # This test centralizes in one place the various fuzz tests - # for Beautiful Soup created by the oss-fuzz project. - - # These strings superficially resemble markup, but they - # generally can't be parsed into anything. The best we can - # hope for is that parsing these strings won't crash the - # parser. - # - # n.b. This markup is commented out because these fuzz tests - # _do_ crash the parser. However the crashes are due to bugs - # in html.parser, not Beautiful Soup -- otherwise I'd fix the - # bugs! - - bad_markup = [ - # https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=28873 - # https://github.com/guidovranken/python-library-fuzzers/blob/master/corp-html/519e5b4269a01185a0d5e76295251921da2f0700 - # https://bugs.python.org/issue37747 - # - #b'\nSome CSS" - ) - 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(TreeBuilderSmokeTest): - - 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/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/INSTALLER b/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/LICENSE b/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/LICENSE deleted file mode 100644 index c2fda9a2..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -This package contains a modified version of ca-bundle.crt: - -ca-bundle.crt -- Bundle of CA Root Certificates - -Certificate data from Mozilla as of: Thu Nov 3 19:04:19 2011# -This is a bundle of X.509 certificates of public Certificate Authorities -(CA). These were automatically extracted from Mozilla's root certificates -file (certdata.txt). This file can be found in the mozilla source tree: -http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1# -It contains the certificates in PEM format and therefore -can be directly used with curl / libcurl / php_curl, or with -an Apache+mod_ssl webserver for SSL client authentication. -Just configure this file as the SSLCACertificateFile.# - -***** BEGIN LICENSE BLOCK ***** -This Source Code Form is subject to the terms of the Mozilla Public License, -v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain -one at http://mozilla.org/MPL/2.0/. - -***** END LICENSE BLOCK ***** -@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $ diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/METADATA b/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/METADATA deleted file mode 100644 index 7a6860db..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/METADATA +++ /dev/null @@ -1,83 +0,0 @@ -Metadata-Version: 2.1 -Name: certifi -Version: 2021.10.8 -Summary: Python package for providing Mozilla's CA Bundle. -Home-page: https://certifiio.readthedocs.io/en/latest/ -Author: Kenneth Reitz -Author-email: me@kennethreitz.com -License: MPL-2.0 -Project-URL: Documentation, https://certifiio.readthedocs.io/en/latest/ -Project-URL: Source, https://github.com/certifi/python-certifi -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) -Classifier: Natural Language :: English -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -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 - -Certifi: Python SSL Certificates -================================ - -`Certifi`_ provides Mozilla's carefully curated collection of Root Certificates for -validating the trustworthiness of SSL certificates while verifying the identity -of TLS hosts. It has been extracted from the `Requests`_ project. - -Installation ------------- - -``certifi`` is available on PyPI. Simply install it with ``pip``:: - - $ pip install certifi - -Usage ------ - -To reference the installed certificate authority (CA) bundle, you can use the -built-in function:: - - >>> import certifi - - >>> certifi.where() - '/usr/local/lib/python3.7/site-packages/certifi/cacert.pem' - -Or from the command line:: - - $ python -m certifi - /usr/local/lib/python3.7/site-packages/certifi/cacert.pem - -Enjoy! - -1024-bit Root Certificates -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Browsers and certificate authorities have concluded that 1024-bit keys are -unacceptably weak for certificates, particularly root certificates. For this -reason, Mozilla has removed any weak (i.e. 1024-bit key) certificate from its -bundle, replacing it with an equivalent strong (i.e. 2048-bit or greater key) -certificate from the same CA. Because Mozilla removed these certificates from -its bundle, ``certifi`` removed them as well. - -In previous versions, ``certifi`` provided the ``certifi.old_where()`` function -to intentionally re-add the 1024-bit roots back into your bundle. This was not -recommended in production and therefore was removed at the end of 2018. - -.. _`Certifi`: https://certifiio.readthedocs.io/en/latest/ -.. _`Requests`: https://requests.readthedocs.io/en/master/ - -Addition/Removal of Certificates --------------------------------- - -Certifi does not support any addition/removal or other modification of the -CA trust store content. This project is intended to provide a reliable and -highly portable root of trust to python deployments. Look to upstream projects -for methods to use alternate trust. - - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/RECORD b/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/RECORD deleted file mode 100644 index 824d4f82..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/RECORD +++ /dev/null @@ -1,13 +0,0 @@ -certifi-2021.10.8.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -certifi-2021.10.8.dist-info/LICENSE,sha256=vp2C82ES-Hp_HXTs1Ih-FGe7roh4qEAEoAEXseR1o-I,1049 -certifi-2021.10.8.dist-info/METADATA,sha256=iB_zbT1uX_8_NC7iGv0YEB-9b3idhQwHrFTSq8R1kD8,2994 -certifi-2021.10.8.dist-info/RECORD,, -certifi-2021.10.8.dist-info/WHEEL,sha256=ADKeyaGyKF5DwBNE0sRE5pvW-bSkFMJfBuhzZ3rceP4,110 -certifi-2021.10.8.dist-info/top_level.txt,sha256=KMu4vUCfsjLrkPbSNdgdekS-pVJzBAJFO__nI8NF6-U,8 -certifi/__init__.py,sha256=xWdRgntT3j1V95zkRipGOg_A1UfEju2FcpujhysZLRI,62 -certifi/__main__.py,sha256=xBBoj905TUWBLRGANOcf7oi6e-3dMP4cEoG9OyMs11g,243 -certifi/__pycache__/__init__.cpython-39.pyc,, -certifi/__pycache__/__main__.cpython-39.pyc,, -certifi/__pycache__/core.cpython-39.pyc,, -certifi/cacert.pem,sha256=-og4Keu4zSpgL5shwfhd4kz0eUnVILzrGCi0zRy2kGw,265969 -certifi/core.py,sha256=V0uyxKOYdz6ulDSusclrLmjbPgOXsD0BnEf0SQ7OnoE,2303 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/WHEEL b/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/WHEEL deleted file mode 100644 index 6d38aa06..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.35.1) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/top_level.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/top_level.txt deleted file mode 100644 index 963eac53..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi-2021.10.8.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -certifi diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__init__.py deleted file mode 100644 index 8db1a0e5..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .core import contents, where - -__version__ = "2021.10.08" diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__main__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__main__.py deleted file mode 100644 index 8945b5da..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__main__.py +++ /dev/null @@ -1,12 +0,0 @@ -import argparse - -from certifi import contents, where - -parser = argparse.ArgumentParser() -parser.add_argument("-c", "--contents", action="store_true") -args = parser.parse_args() - -if args.contents: - print(contents()) -else: - print(where()) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 0ae67e0ba4d30a6a8b7dd34217eee2b7d626682e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 272 zcmYk0!Ait16h+gt%1{|_=`VC)UuWDX$e^I042UqhZbD3+HrOWRC052i@^4&s>&jnn zWwP_aJD1Ca$Cb-v$)d_HFE`P@Q}{0)!&N+g5}}#SdNyQjHt?2bO!GF^`Hoe^PZsNn z&kN;jKpXsg@ZKTeN4aj+t9sSc&G|!B%!P7{)#Q-UK`yrFxYw=s_U` c^K3u}V@(jE9{2O&a&Y>s$4%Pq)23to2Tz+vk^lez diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__pycache__/__main__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__pycache__/__main__.cpython-39.pyc deleted file mode 100644 index e6ea699f8d0c8dbad3b2e1b8bd84bfc493bad978..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 437 zcmYjLJx{|h5Vf5q4N+V1H#)Q)n2->n5=eYVs8Sb{B{K2RxFk+=c8l7zGykEC{2OMk zOw4RdI0Ye2x_9@U&+kt3`w8)pyczN7>w8Q7N9f6!Pjlx%5=l#vww0B%VkxVFG@t~? zuJc0D5W+Omp%o)~y=g9*-; zmA9R?6X>G-B)XoEGV>qzZ!X!ZEjO;S$FM=A?JL1SYt_P+;1x(E_@zL3j%o#bx-M+3 z`7AH$y5v_k7n987C>lUMUV&cmQr+`rZHrnTjgPre7DkQ8AH^ID|GkxGStXRtvT?I^ b@kv$7WeKO$FEs7em>sg19@3aaw8y>ylMZ~& diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__pycache__/core.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/certifi/__pycache__/core.cpython-39.pyc deleted file mode 100644 index cf290061a892356b4670399c197afc7fb075ee2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1163 zcmZuv&1)1f6i+gl-RbOB`~p7^8N|yT+)>1XC}ODvMJS>R>VkwgNxRY6$?}p`TUsym zD*g-Y(f`JCuAclCyy(kpt5tj%vZ78l12_sD&m?ln3^ z#zaxYYC5LdS20RzHciHSnvPRS6q6mCUBy-Uk&LtFq`At8Uf3E@5{K%muF5{rajF*P zCti|1=@Mg^nXIuG)-`{qXtRPtp-NOogY*4TEEWxbsC87yKPQt2vVs>sHXaSY zP8jmWaqJ}&Il4jI@OrBWN?t}~9P&ot3k{iWDyvMhK1c$$Gu_Pp=dnWr9=c*KfsnT3 zL4+H)l3xBH3i`MS5o(FkMVy45rtMA@7e_`hh}pJ+ix_zFhVEle59sbZ<~vx=K~yGN zg%nR@OskAo2MY$q5Wl zX{a!jDE=wI-_uoyKwAh9^KcT~U|!R)uUfIh@=3.5.0 -Description-Content-Type: text/markdown -License-File: LICENSE -Provides-Extra: unicode_backport -Requires-Dist: unicodedata2 ; extra == 'unicode_backport' - - -

Charset Detection, for Everyone 👋

- -

- The Real First Universal Charset Detector
- - - - - - - - Download Count Total - -

- -> A library that helps you read text from an unknown charset encoding.
Motivated by `chardet`, -> I'm trying to resolve the issue by taking a new approach. -> All IANA character set names for which the Python core library provides codecs are supported. - -

- >>>>> 👉 Try Me Online Now, Then Adopt Me 👈 <<<<< -

- -This project offers you an alternative to **Universal Charset Encoding Detector**, also known as **Chardet**. - -| Feature | [Chardet](https://github.com/chardet/chardet) | Charset Normalizer | [cChardet](https://github.com/PyYoshi/cChardet) | -| ------------- | :-------------: | :------------------: | :------------------: | -| `Fast` | âŒ
| ✅
| ✅
| -| `Universal**` | ⌠| ✅ | ⌠| -| `Reliable` **without** distinguishable standards | ⌠| ✅ | ✅ | -| `Reliable` **with** distinguishable standards | ✅ | ✅ | ✅ | -| `Free & Open` | ✅ | ✅ | ✅ | -| `License` | LGPL-2.1 | MIT | MPL-1.1 -| `Native Python` | ✅ | ✅ | ⌠| -| `Detect spoken language` | ⌠| ✅ | N/A | -| `Supported Encoding` | 30 | :tada: [93](https://charset-normalizer.readthedocs.io/en/latest/support.html) | 40 - -

-Reading Normalized TextCat Reading Text - -*\*\* : They are clearly using specific code for a specific encoding even if covering most of used one*
- -## ⭠Your support - -*Fork, test-it, star-it, submit your ideas! We do listen.* - -## ⚡ Performance - -This package offer better performance than its counterpart Chardet. Here are some numbers. - -| Package | Accuracy | Mean per file (ms) | File per sec (est) | -| ------------- | :-------------: | :------------------: | :------------------: | -| [chardet](https://github.com/chardet/chardet) | 92 % | 220 ms | 5 file/sec | -| charset-normalizer | **98 %** | **40 ms** | 25 file/sec | - -| Package | 99th percentile | 95th percentile | 50th percentile | -| ------------- | :-------------: | :------------------: | :------------------: | -| [chardet](https://github.com/chardet/chardet) | 1115 ms | 300 ms | 27 ms | -| charset-normalizer | 460 ms | 240 ms | 18 ms | - -Chardet's performance on larger file (1MB+) are very poor. Expect huge difference on large payload. - -> Stats are generated using 400+ files using default parameters. More details on used files, see GHA workflows. -> And yes, these results might change at any time. The dataset can be updated to include more files. -> The actual delays heavily depends on your CPU capabilities. The factors should remain the same. - -[cchardet](https://github.com/PyYoshi/cChardet) is a non-native (cpp binding) faster alternative. If speed is the most important factor, -you should try it. - -## ✨ Installation - -Using PyPi for latest stable -```sh -pip install charset-normalizer -U -``` - -If you want a more up-to-date `unicodedata` than the one available in your Python setup. -```sh -pip install charset-normalizer[unicode_backport] -U -``` - -## 🚀 Basic Usage - -### CLI -This package comes with a CLI. - -``` -usage: normalizer [-h] [-v] [-a] [-n] [-m] [-r] [-f] [-t THRESHOLD] - file [file ...] - -The Real First Universal Charset Detector. Discover originating encoding used -on text file. Normalize text to unicode. - -positional arguments: - files File(s) to be analysed - -optional arguments: - -h, --help show this help message and exit - -v, --verbose Display complementary information about file if any. - Stdout will contain logs about the detection process. - -a, --with-alternative - Output complementary possibilities if any. Top-level - JSON WILL be a list. - -n, --normalize Permit to normalize input file. If not set, program - does not write anything. - -m, --minimal Only output the charset detected to STDOUT. Disabling - JSON output. - -r, --replace Replace file when trying to normalize it instead of - creating a new one. - -f, --force Replace file without asking if you are sure, use this - flag with caution. - -t THRESHOLD, --threshold THRESHOLD - Define a custom maximum amount of chaos allowed in - decoded content. 0. <= chaos <= 1. - --version Show version information and exit. -``` - -```bash -normalizer ./data/sample.1.fr.srt -``` - -:tada: Since version 1.4.0 the CLI produce easily usable stdout result in JSON format. - -```json -{ - "path": "/home/default/projects/charset_normalizer/data/sample.1.fr.srt", - "encoding": "cp1252", - "encoding_aliases": [ - "1252", - "windows_1252" - ], - "alternative_encodings": [ - "cp1254", - "cp1256", - "cp1258", - "iso8859_14", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_9", - "latin_1", - "mbcs" - ], - "language": "French", - "alphabets": [ - "Basic Latin", - "Latin-1 Supplement" - ], - "has_sig_or_bom": false, - "chaos": 0.149, - "coherence": 97.152, - "unicode_path": null, - "is_preferred": true -} -``` - -### Python -*Just print out normalized text* -```python -from charset_normalizer import from_path - -results = from_path('./my_subtitle.srt') - -print(str(results.best())) -``` - -*Normalize any text file* -```python -from charset_normalizer import normalize -try: - normalize('./my_subtitle.srt') # should write to disk my_subtitle-***.srt -except IOError as e: - print('Sadly, we are unable to perform charset normalization.', str(e)) -``` - -*Upgrade your code without effort* -```python -from charset_normalizer import detect -``` - -The above code will behave the same as **chardet**. We ensure that we offer the best (reasonable) BC result possible. - -See the docs for advanced usage : [readthedocs.io](https://charset-normalizer.readthedocs.io/en/latest/) - -## 😇 Why - -When I started using Chardet, I noticed that it was not suited to my expectations, and I wanted to propose a -reliable alternative using a completely different method. Also! I never back down on a good challenge! - -I **don't care** about the **originating charset** encoding, because **two different tables** can -produce **two identical rendered string.** -What I want is to get readable text, the best I can. - -In a way, **I'm brute forcing text decoding.** How cool is that ? 😎 - -Don't confuse package **ftfy** with charset-normalizer or chardet. ftfy goal is to repair unicode string whereas charset-normalizer to convert raw file in unknown encoding to unicode. - -## 🰠How - - - Discard all charset encoding table that could not fit the binary content. - - Measure chaos, or the mess once opened (by chunks) with a corresponding charset encoding. - - Extract matches with the lowest mess detected. - - Additionally, we measure coherence / probe for a language. - -**Wait a minute**, what is chaos/mess and coherence according to **YOU ?** - -*Chaos :* I opened hundred of text files, **written by humans**, with the wrong encoding table. **I observed**, then -**I established** some ground rules about **what is obvious** when **it seems like** a mess. - I know that my interpretation of what is chaotic is very subjective, feel free to contribute in order to - improve or rewrite it. - -*Coherence :* For each language there is on earth, we have computed ranked letter appearance occurrences (the best we can). So I thought -that intel is worth something here. So I use those records against decoded text to check if I can detect intelligent design. - -## ⚡ Known limitations - - - Language detection is unreliable when text contains two or more languages sharing identical letters. (eg. HTML (english tags) + Turkish content (Sharing Latin characters)) - - Every charset detector heavily depends on sufficient content. In common cases, do not bother run detection on very tiny content. - -## 👤 Contributing - -Contributions, issues and feature requests are very much welcome.
-Feel free to check [issues page](https://github.com/ousret/charset_normalizer/issues) if you want to contribute. - -## 📠License - -Copyright © 2019 [Ahmed TAHRI @Ousret](https://github.com/Ousret).
-This project is [MIT](https://github.com/Ousret/charset_normalizer/blob/master/LICENSE) licensed. - -Characters frequencies used in this project © 2012 [Denny VrandeÄić](http://simia.net/letters/) - - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/RECORD b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/RECORD deleted file mode 100644 index f09241ca..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/RECORD +++ /dev/null @@ -1,33 +0,0 @@ -../../../bin/normalizer,sha256=q3bDC-grXpmalhoyzVJipOt4wbNQjBeaswBLfh0rFC8,289 -charset_normalizer-2.0.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -charset_normalizer-2.0.7.dist-info/LICENSE,sha256=6zGgxaT7Cbik4yBV0lweX5w1iidS_vPNcgIT0cz-4kE,1070 -charset_normalizer-2.0.7.dist-info/METADATA,sha256=IBf9rYJsfiUkX_1wIFO3ABSfIPbgA-k1gqrq_VfypyY,11350 -charset_normalizer-2.0.7.dist-info/RECORD,, -charset_normalizer-2.0.7.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 -charset_normalizer-2.0.7.dist-info/entry_points.txt,sha256=5AJq_EPtGGUwJPgQLnBZfbVr-FYCIwT0xP7dIEZO3NI,77 -charset_normalizer-2.0.7.dist-info/top_level.txt,sha256=7ASyzePr8_xuZWJsnqJjIBtyV8vhEo0wBCv1MPRRi3Q,19 -charset_normalizer/__init__.py,sha256=BVLv4gxL3YZ0xFHfrJacXqdV5YUm98fACgSaEtynXZc,1491 -charset_normalizer/__pycache__/__init__.cpython-39.pyc,, -charset_normalizer/__pycache__/api.cpython-39.pyc,, -charset_normalizer/__pycache__/cd.cpython-39.pyc,, -charset_normalizer/__pycache__/constant.cpython-39.pyc,, -charset_normalizer/__pycache__/legacy.cpython-39.pyc,, -charset_normalizer/__pycache__/md.cpython-39.pyc,, -charset_normalizer/__pycache__/models.cpython-39.pyc,, -charset_normalizer/__pycache__/utils.cpython-39.pyc,, -charset_normalizer/__pycache__/version.cpython-39.pyc,, -charset_normalizer/api.py,sha256=aTYqVTGki22DYJDVqiUyEheBjwo2BKBQNJwa_7SU3KY,17092 -charset_normalizer/assets/__init__.py,sha256=FPnfk8limZRb8ZIUQcTvPEcbuM1eqOdWGw0vbWGycDs,25485 -charset_normalizer/assets/__pycache__/__init__.cpython-39.pyc,, -charset_normalizer/cd.py,sha256=xdMt8glWp1uX84nBJMynjBqr4g951q0jTQ1BX82TJNE,11003 -charset_normalizer/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -charset_normalizer/cli/__pycache__/__init__.cpython-39.pyc,, -charset_normalizer/cli/__pycache__/normalizer.cpython-39.pyc,, -charset_normalizer/cli/normalizer.py,sha256=H_2C2e81PqNCmUaXuwBJ5Lir_l0wwzELSxjPXcUL9O4,9453 -charset_normalizer/constant.py,sha256=W4u2KeC-RBJP4jxfTK_rOHUERMnejoY21VhIeFOgQKw,19387 -charset_normalizer/legacy.py,sha256=XKeZOts_HdYQU_Jb3C9ZfOjY2CiUL132k9_nXer8gig,3384 -charset_normalizer/md.py,sha256=iCAG4l121wZfZIgJhEDROnAaLcGsvlLNjXMitEBfOZY,17164 -charset_normalizer/models.py,sha256=wObDc9qaxz85xkqh6UozTpL0lLkQuvklO47e81lR89E,13332 -charset_normalizer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -charset_normalizer/utils.py,sha256=6vfdA59u0VQD3_dDXf2B8viuUWPo9xIfKq4b0nXX6Mo,9026 -charset_normalizer/version.py,sha256=l3uyLlrnBh9G0hbagT5ZkEBPiFCyYfNhUn1Ea_qbRus,79 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/WHEEL b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/WHEEL deleted file mode 100644 index 5bad85fd..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.37.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/entry_points.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/entry_points.txt deleted file mode 100644 index a67f60bc..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/entry_points.txt +++ /dev/null @@ -1,3 +0,0 @@ -[console_scripts] -normalizer = charset_normalizer.cli.normalizer:cli_detect - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/top_level.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/top_level.txt deleted file mode 100644 index 66958f0a..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer-2.0.7.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -charset_normalizer diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__init__.py deleted file mode 100644 index ed525034..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- coding: utf_8 -*- -""" -Charset-Normalizer -~~~~~~~~~~~~~~ -The Real First Universal Charset Detector. -A library that helps you read text from an unknown charset encoding. -Motivated by chardet, This package is trying to resolve the issue by taking a new approach. -All IANA character set names for which the Python core library provides codecs are supported. - -Basic usage: - >>> from charset_normalizer import from_bytes - >>> results = from_bytes('BÑеки човек има право на образование. Oбразованието!'.encode('utf_8')) - >>> best_guess = results.best() - >>> str(best_guess) - 'BÑеки човек има право на образование. Oбразованието!' - -Others methods and usages are available - see the full documentation -at . -:copyright: (c) 2021 by Ahmed TAHRI -:license: MIT, see LICENSE for more details. -""" -from .api import from_bytes, from_fp, from_path, normalize -from .legacy import ( - CharsetDetector, - CharsetDoctor, - CharsetNormalizerMatch, - CharsetNormalizerMatches, - detect, -) -from .models import CharsetMatch, CharsetMatches -from .version import VERSION, __version__ - -__all__ = ( - "from_fp", - "from_path", - "from_bytes", - "normalize", - "detect", - "CharsetMatch", - "CharsetMatches", - "CharsetNormalizerMatch", - "CharsetNormalizerMatches", - "CharsetDetector", - "CharsetDoctor", - "__version__", - "VERSION", -) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 0fe84bed5a0d28e02d137b1e7fbaafab23c81b73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1579 zcmcIk%WvaE7lT{)(R7?YEpz*; zBl9u-`b#9S6F6}#uJf9x2X(O$tcXU?5Y3=jv+@<*678TZIzdNtgRXD`SF8rBm9D|p zL@(%x^G2sXt1;6C!L;I+7Y==3}J%N56QpC&|UYPQEx3BuXeRJq^$>vNA1 z3I~*M_=G8K;PaHtsM5$UUEvuuG%`|o?hrVeD5BXpmfxMUZR6IB)@AXJ=<|;pHvE^!k;73F}s= z!^zxGedU1n$+^++(JkH9=9A0sidV%i#W`GlUtAPFSq6&p;@9E~ir+84EzVGO0mW~Y zzbJmJBtKV{XDB=`UU{&8m-zB!aq-TkSG_;o+RV*y_`znse}!g3wF##=)fUhanrDSu z*CyJitsApS{C~rB-F+;#(jX{SDaJ~q@#5VVMIkf7IGJz?+gRX5Zye`X{8&c0ps68- z$<)OjeViDR>4EQ0nMv}A7fIpo=UP$Y|F>v6o;!$SHdkz#m;r1>eR%ZlqxWpX4HJPK zdo+B0FmeZ+MKslP0DGgOhZTU&Mo)Lghr3me3ENWG)Cf^~?q4;W_I{&u?PM%Ep=?*@ z|XayzggZ}&feANmAxy0w3ArAwPbImdXJbWDIeS|r@Cw*Ae`cUyCHF_4?hZp8DUwm;do!BO zd|Y>aRxOJ6y$^lO4BcMtD1Ar!_+iLWX2Q_R=4IzG0?j$yv9F~q^u`+xYHznUY8&-` E09n{BrvLx| diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/api.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/api.cpython-39.pyc deleted file mode 100644 index 45b6fd1b9d44f0f96f7b88caf40adbc6e033ff91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9907 zcmb_iO>7)TcJA)!`Q?xtlEZ(BYDyx-k;tJW|Lm^gcvliBnOTW+mQ=LvwKLnCY7U$1 zpHX#>WR7~0Md$=U03!^L9AfN&0*nC8(b!9_!6Hb4V3A|chg znIR?HStZDfrrA~X>eZ|FUcJAP(c7Do@X7w8Z~XL{B>f9DPJVjO_zr&l`?4f4iOII) z$hzz(x*}6su@jD}t4>l+Iw?Knr1i8cH47L?*cm6QXJzTGZ9U4 zYmYhO`nWTpPl$HTo^+=4Dd&`a%9+-uozwbhN7FTDMxVjT`J*$e=aHf(`yS%Z6C- z-K4(6PMhl2!VLp%gpC6364T7=*TRhfZk$a3?lPPC8n|&b`Qr?mVy6Igg{d1->C9u2 zT1kmATZV7ChGRwbOPJDM^>$2dx>d8n4FE@jc=wp))+)B))*435^rK;CtMV1yBaX>yX5|?S zekl?4tzKQbTG_bs#vAM3+gw?W3Y+Wem20avD;q1nd1qzq`bsoNJsY>L-n!N81ARHi z^!=C+Y%+g+$Kbvh+%|&hPSkg@W%{L5G-9~>6~As)t!>L>T?(VDWw=H~aA(M3rW;t> zfU{~9k5{%lC+fBQO0|w2$FdC`jiBi??7*sQ?FZ&Dno)npYuK#f2izj)&U}5a8q-yA zJ?H*KP!-KL#^nI->m5}bc5DU$T__y3$efA{Up z8>MWN_04+?5`-Tm{lKjIQLehvaQCos(~Ppg4mUxN&7wSLw|vhgak-*pzEiauKDCN< zZki6He&4KYnLEaP%WLo`ZN5{t4a<#E+zcArt!7A#+Mgx-rSpFsKR=I=KnkTm4wP-> zK=HL8!Q>+~NQTk@&dY)PdvcieuLP;1bdcH2(z$W-?N*LjK@UN;GGT@(dkU{(EYA{R zq&MgbVGGC()FhTZNY#LELkd-vX~_J20hwjFqyA9E*(A%;!}9N9e3>w+u3D2?YM2zz ze=DGS1$43v-6x=v0y-(6lVK)QLb)jI$^2t6qd%0&=I8uRE*c5jp%%NXL{}3BI?g#xn zVTuhk2!A~&5~o~+Wm@?#PrE|zpN2h0!y#-FI7zdMcn2qfkprpKi}`){^zXALP zgI?{It+8jU`E(e#5vYdV}b zRK1CCRA>?205uA&$w2o{ho=u!UKSFXjyWC_Fg|@Cv(uAwN@4q$hV!HHTTE+dOli$P zW6$7sHk^h&$#1{XIu}aeIee8-iTKx=g-)pakJ_@Kx8ZPhl=^3P&w;x$?96*gYYucJ z`Hk=#?20PhfMr}_XZMn#rLc48SNTubIadjl*7r^h5V zdyoJxH(T?dX#N?Bl6=AoPnY7NDP5gS33&Fy*sgUYz5L zZYC(jZ{bB?`9q;k7hxyqTwKJt5PX8pEQDtupM}<3a3P#y3j+Et*&;l|#Ye(&LSGld z1?+N>ID447cNkm@7odmbunf%qEHIaWxlGzBxIYAG#5*j7W9Y{YML+gPd=Okn^D?-! z06oT=z_HZU_TF$P92u9$b18^&;k`z}B6y*2@`6L`($~QW{`7=HI4SUphZ&v;2M*JI zGc1VbAXa)881n40poZEXi1w9maCd=hTL{X-K^k=#&E-D{;RRqb)BZn+_Rk0x|5-S& zyBHRB7xxnJh&ab-{_g_Y3mqPW0|L_o=71J4_F{Le5WgYfe92f&NK#J2=O%&*S2Hwj zJkTgo^0iLHvY^Frg%<3X+Lq}DTHW(~Ys&^qJ66&fzJazDcz|`8$u)#knzyxURs+8* z@UL#ud~Rx%uek)~BPjD1G%KJUgC=-xO|x8PRgJ*ofHq)lv80b6rz+-ci2!)X^)P^# zI7WPJUt{LB0c;Bbyv{vDS-$qbvTY4e+^7cHOWKz0RejCd)?T`N#nViqx#3NV462DL^VV0D8BR<(hU69|}zcFoj`?Et*Ea%q-O zIi}$^xXHvyWo>iE^G&VF5#Dpl&-G zS2nb@^-VAoK_J*fTfv^073T)vO#mSvdek0xd~e(K9tfV-x#eML1fO3+yI6Ir5oj(# zT}Z>&g9rj`8XmMEEJ!1z5tP>(99r5T zV{->Ow%u^sj3CVralfE?WA!G~F5ZB#Z+iw?>PTCoz3*!j_(PSdW*zjn+MUfCiglzl@uK{EFwNPB5AGp z!>722#gK!Q1$zU*CEKV8Rx)#|QL7Q|4lgwuITh{DM*2zn>tv0ZVMDJl@QOC;mz&Ch zR;7(~K1hZnN{cq|^GEQ2=aJs-YsrY6WF{Rbf1=z&{`;YP<3pJbWBfxUQp%SirCR?` z;*03`thVHs%rcfARb*o(8k8-cl=Bl1kf9?BN_&@NIn$z3db432= zmtTb)F>L=eymSDY23UHmhyOn;75G?(r~emz@_EqCOZ2!v4`O8eDAOBdhFkP<36JJ) z;|z0-d>Yz=>{OedwMy|3>t?C#J{bMBqB0L2fX?VP3FIw(asEFiVELOx#9I2t-OhdUyINRh&8&p!u!qUE>Wh zyO>BbVh8$kOabe!p*mE?o|<+y>NSrdfZACM;oSq7oz&>R9!H*xdp*h&AeU*j6YK2*f`dKAUVl zd=IKZh$OHmQ4q6{!EBO3x{G3W<;N;m^Ej5GRYU#A1dmcUS048t|A@v4^mu|#vr5t= zOjO;WbKa36wBB;znGKuP^nAE|4}njutlc!?U;{cu9tkIn_UqIe!~sMXZhHcDI=ijS z&XylDS}uVH=tv{7s#$^G{B^8cr@#jr{_2Hp6RvK!5>wGZ+MIjX(&E|~f&!c@@Cj$O zQ%= z)l81^vl-l?QfHK=9xx=zT>swc=IZsUw<2|Q?Z$ePuuV5gKQK5kk`F=G_#hsUy6d4N znXue|lhX5k(P_C@r%10UI%zJ85?F#$P{8}BJ7F-!$Ei6+&7s>KYuF~Fal>o4ET*+* z)gc!VHev#9eo&; zXjr$3kE*g=>F8RwTeztc-NzC7aq-*N2fthq*Hf`K&`0CH6-4b7SSwc9HY^(%$5R8o zw;gl``Z@|&5gMiUblZ(@>Vw@zcg-l>&fD}nRm?lqU+PILm3qBD<}>S# z7g43BVm-|i{F&Y z7izMgsL!fh?OZ~6u3p4Oqop8rlJhR0w<@&K&5!wkxCOw8E%5^ z=h^+!c~_Jcv~JfwLW=B@E0p>au=OdieIlZ4Fj%eQO80pd`4WqXz2v_Hi-I0%0VYfA+r?21Q&Vgcij( zkzbazbvS<#C580FPkN6Ng_!Z!HsxV)#ZPnK)MFt~$%z{lgtWRYe0tm+DST@Um&&RK zylv+39qjsfcqat;jFhrMO2qwtAURQhBK75;mJ+o-;d>Z?Hay2-LPlxA)c#PA(LDw~ zL4#y;QZ5JSRvNOBQ1Z?MN{|RM2PjSBl2XOtkeQ4t*lR(ODMu-~oQ$t;kJ67My1Ha? zQo;pFBFyb(Mx}2^3F%cS$TIaP732uxQ4cP0lSlaw*Su6bXDR5$G_Ejvj(Wq~QQsqp z%EWhZmD|w?w*Z~^i<3GL=AaXqrX_S@lS+h!HczG5Mfx8Mtkgb5Buo-CWulbRLEy;C z4IDVs_F@QFDvWv52Z;ImZRFU%TUqoDJ~ZOM30h!`7JvDlK;+fNpWx zM`j6xs6JXj`7?R3sXT1)^I&(RJb>2p3lVaWwy9Lq;R%fK?-D?|t!11}P^4}l#1OF{ z-zAXW5+Lo$E5A*xmc3JRq($p(JCgq>9CbIIWn#%}hP0pVjwVKK(BXB=ca1DyQpz!;cRZ4TR#e9+; zQ+TYc;r}i?e`o5;(@QTzY0obUD@F_!tc_AgGH~FzNr079MBT_t{lY8Ks^dJuN z*YVJmDvRUs#hsCjdimZq2i?l=}C=tq{7COlIih5(5 ze^7EDvJR1dL|LS^w?rDmZ_vn1Jfh_C%C$Q;quve3!f;-PQrk$A((p&(C68q0235dO z+vZXB_%(;LrVY)n5wOK%)kcCMFhF(zf-JJjGASUdECL1D1(<~sOZm>d-OX-E zae%BsqN;D*cb$9AcfNDVo0uqQ_{;x;@c#3xru_>&jQ(Zua2c1_(KU@}Ob<1eevMF% z4A;=9oC!0L>6(${TJqTp?I`PJW!VaIQQpl*1-B3t-C|U7OQ^S5Hk^ni-AP%_g$JUt zTaKpOsc71rmUa1XCYp6;Ww{U@jON@qSuTc$qIq{dT5uPl!|vhehK@fK|CnF! zk1c4d^w3}vZ1SGz9v{{nKwX(lq3#twyF1=@n$6rZcXii!p=FjGlr1M%Ma}J>?5}a? zVTR4Kg?pC!>R=836g!OCBkU+v`QD(G9mC4U*(<0!jj?kJnm@-J)SX}_QTLjkWv{YR zU+e5FMx64C^akj0`k~2QV`uJJ*w-1RuW6OvctWSLTG7+#_nS5DM}9N*!fSr5#e7~d z((Kh%w;B69E#Bg&@!7SY7Nd7Qd{2s$^VqMheZfVfv!duuFo@MF2hEc^YwJ-I$!HTSOFZY#x z4SU@s{g~LfUU2aDk$gqR6QWfMNS;n#AdWl(;RH>`a|FZ~`VNWJAu&2_Z^K`d6YMNr z?>1|s8cwqnJEGfew>Zs%3V9gcA0M?-8k~z*{CO|6gNeCEA1V z<0?cDd10`FK{dA6-cAb_Ll|AH6}6XNr?(`vvkgNpKixSykl(mYFJ25=H7^v)C=7;H zN}ObrX5YWHLc)b|M|3*aQaYiE)u_d~A#}Y?TZlQyHnrs0r#6#tky>AI$ColFwV(=-5^HLE?Y7@!6^oyzru=ZSG&d-w zg`viZG(RjW5lcBs5g!@@G$3PtSMSj4PvWMTrcs70OL_sa1+=Omt=rIxm(8#@u-*rL`szL}9@| z35FsY!Vgen(OGLnBPio|+;xLd5&T>Xnlz?wH0^!sL zsKi=0lDI)r;+#Yqz>f{|(QDPUZjkTzrYL`$cRie4#gMusMT5g49d{?k#46-5xd%ks zuLbqMXMN?9$FP4E(}OqNKZf@(nV7~+vvW{!QfgDT@oO8Lj%e&C2nvTsv5tpC+XJO~ zq>IzC1m*0PGOEN(DDF9C(GkcVHtLrX9LJW%ud^)6Z5aKQJX(GmEe2XXV+9(qp+UWi zJAWY$>?S$V|99u*+M*HE<7!$6MC-!&3m2-Nw_VHc)~cTaciQi~aUsop;I+M`FZ|Tl zSO?E(1oW^obv1;O3jmQ$ziNMuO`h5x!V7s#Fqndfp<9CNmy^Bl5^fb;s=@Qdb}(8W z(v1Bx{wW3s(gH1;duAB9r$#3Ctzp{#W!SbpjT4`y(|#B2*DD$RoA^!3$_>I1G!>aq zI7VnZ_?tjs$UOcH55O_lTrcw#;n*y&OC#*!B?df$O`hzOVtvn$DECXCoYk|@JB!v_ zVq?VlUOvf6oBaeM^BDOF+OkQG8N@N-4Asf@WsJ*pmJ^+SfmX8eFFI%YV;>|L{-AR< zHWM3sr4XB6YIVtX?#We%ZS;ysF)19;dZnbqEcoTKU_4+pNvST;6{BsS2N=;k#0f0- zH86$cnR!o3z>p-06n0idF?WQm4DoGS-?2E)ZBogydq#zPy zqHJGju!|vq*gtn*!!dMB_1a@y9-V3;=;bGI`(s@HflJ#t?S<`zw+`<<+~do9^v0k6 z8U6kJrLmon<dKkM&f)yT3Q(V{;cnFIs2b@dVQUAr1qCT@U@C z9G=^;l-n-{o2_K|6qU(Ea^gOm&@#=0ev@CIM?{^VuS)jhqxc)Rxig~G=_iZPz(bcke>b*`lpf`5d&DD zeiu5&Rhb#;7Yqrm=l6{TP2vxFjn=b_Y_SjlVZQyo_hDdzXVvFEMJYNnnFZW1$?Eq^ z2ItkYC7xtuy){ywjV(euX29Y7V`8)1rjB3W##=x|>=O9WZ~ZlpiduiY-C1lvb3j)_?3`KO zc3QPs7vux9!>RLD@ti5GdKiARQSNVyT<(tl)o6Eo`B+KS*E~6)DJ_ z^3Qv6a&OBELJv?Oab)$S2sA^gJB~nv)e3?3k>&9Krzy0Y8rrF-uKS2|eASWcQlih_ z(4>TMw!ZWFNauE|O%yYTw_)1AG48(%!oS0*{O=p(A7NQab&M?Q)uCC>ZSn8+xA=s7 zmh=LB=kZUM5Q@o|qVS0qc6}M_@zdyqAOm#wF1Qf|w0xD;%1OZBKS9}@AFf)3I`Y;Q zsI2Nrur$&<5L{K^tb1rU5ZM^o?x0~>33wC`-6_>$1S93k!AE?IRrqbXNsK%;GV6=7 ze}sO5#HHnO`Xm6efFQ;&Yy!oMyqgFPQI>bJpc~KZjPb2$4NLYc*q33*duH+(Ld>TI zLQd1*E@nkASyrJeLx!l+C3}4_o|K_DdSAsQ{sgyJ+eJi~5DWVPxM@&iDOu9dP66>G z;vkAIC97KO6o9Ru73h1oXMtl`NoH3?PH!ew&jfG#sPkrG%HE$LxPM0cc~1Z3oY=pMOilj{&jC#+%>^it zc-5*qBIb+E$0}o{@<7g#GjLxR2tyzb*HnPCa(5A9<($Z>#a7{tKb!6Hfc5x@=a7a;{a7zVe1Yup(d@A+FN8n0tWTFXmjn*+s4$2 zN>ECJ*Z^v(eVQOMC2pAoq6XtZ9^mDX;EY^zn6VMgNL#lzvjQAa(TZezs$gbW6nr&_dF& zHxqklmPpGh7a!#{z1#@q}{=H!HRI7 zkuL-!9a~e3IuAv1t95w;;OfRaI6hIo6|0BVOg@jmc$;X40 zc20c9%PI^}5K>Al=~V~@B63AP!}6Rql@=-7AOah~-fC1$xg;4qO5UU5>UFA<16}gu+j!ftCb2rh2PYs4_-B)zKj%WtuRz^jgZ1sq#`d}6FvY5PAR(7 zh6e(7;lvd=Ib=?V#>p+aHnLYf95?ZGHwQk0d?TniWf|4E5CFPH#JD6UHa^h!n754D z)au3o%2U*Lnr;*g$!{`}&cJEJd_Q+B6Us8rn40U@oILT=s)sFn=pn+ZbcFC*zWj*F z4^1!4T~uF)mPwuE_Ypjx<*r~eJIOH#N1 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/constant.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/constant.cpython-39.pyc deleted file mode 100644 index 611f6539d2e698b4cfece841623edd0a9827373e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13723 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/legacy.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/legacy.cpython-39.pyc deleted file mode 100644 index dab285a80340fb7948b363395f3db356867a8176..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3049 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/models.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/models.cpython-39.pyc deleted file mode 100644 index 41b3b82372bad4e18513b7b7cedd7b010bdb2862..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13197 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 HW%K_5Q)?)Z diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/utils.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/__pycache__/utils.cpython-39.pyc deleted file mode 100644 index 0a65ef1f0f7ea58b013bba25a65fc803b432a50d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7438 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 CharsetMatches: - """ - Given a raw bytes sequence, return the best possibles charset usable to render str objects. - If there is no results, it is a strong indicator that the source is binary/not text. - By default, the process will extract 5 blocs of 512o each to assess the mess and coherence of a given sequence. - And will give up a particular code page after 20% of measured mess. Those criteria are customizable at will. - - The preemptive behavior DOES NOT replace the traditional detection workflow, it prioritize a particular code page - but never take it for granted. Can improve the performance. - - You may want to focus your attention to some code page or/and not others, use cp_isolation and cp_exclusion for that - purpose. - - This function will strip the SIG in the payload/sequence every time except on UTF-16, UTF-32. - """ - - if not isinstance(sequences, (bytearray, bytes)): - raise TypeError( - "Expected object of type bytes or bytearray, got: {0}".format( - type(sequences) - ) - ) - - if not explain: - logger.setLevel(logging.CRITICAL) - else: - logger.setLevel(logging.INFO) - - length = len(sequences) # type: int - - if length == 0: - logger.warning( - "Given content is empty, stopping the process very early, returning empty utf_8 str match" - ) - return CharsetMatches([CharsetMatch(sequences, "utf_8", 0.0, False, [], "")]) - - if cp_isolation is not None: - logger.warning( - "cp_isolation is set. use this flag for debugging purpose. " - "limited list of encoding allowed : %s.", - ", ".join(cp_isolation), - ) - cp_isolation = [iana_name(cp, False) for cp in cp_isolation] - else: - cp_isolation = [] - - if cp_exclusion is not None: - logger.warning( - "cp_exclusion is set. use this flag for debugging purpose. " - "limited list of encoding excluded : %s.", - ", ".join(cp_exclusion), - ) - cp_exclusion = [iana_name(cp, False) for cp in cp_exclusion] - else: - cp_exclusion = [] - - if length <= (chunk_size * steps): - logger.warning( - "override steps (%i) and chunk_size (%i) as content does not fit (%i byte(s) given) parameters.", - steps, - chunk_size, - length, - ) - steps = 1 - chunk_size = length - - if steps > 1 and length / steps < chunk_size: - chunk_size = int(length / steps) - - is_too_small_sequence = len(sequences) < TOO_SMALL_SEQUENCE # type: bool - is_too_large_sequence = len(sequences) >= TOO_BIG_SEQUENCE # type: bool - - if is_too_small_sequence: - logger.warning( - "Trying to detect encoding from a tiny portion of ({}) byte(s).".format( - length - ) - ) - elif is_too_large_sequence: - logger.info( - "Using lazy str decoding because the payload is quite large, ({}) byte(s).".format( - length - ) - ) - - prioritized_encodings = [] # type: List[str] - - specified_encoding = ( - any_specified_encoding(sequences) if preemptive_behaviour is True else None - ) # type: Optional[str] - - if specified_encoding is not None: - prioritized_encodings.append(specified_encoding) - logger.info( - "Detected declarative mark in sequence. Priority +1 given for %s.", - specified_encoding, - ) - - tested = set() # type: Set[str] - tested_but_hard_failure = [] # type: List[str] - tested_but_soft_failure = [] # type: List[str] - - fallback_ascii = None # type: Optional[CharsetMatch] - fallback_u8 = None # type: Optional[CharsetMatch] - fallback_specified = None # type: Optional[CharsetMatch] - - results = CharsetMatches() # type: CharsetMatches - - sig_encoding, sig_payload = identify_sig_or_bom(sequences) - - if sig_encoding is not None: - prioritized_encodings.append(sig_encoding) - logger.info( - "Detected a SIG or BOM mark on first %i byte(s). Priority +1 given for %s.", - len(sig_payload), - sig_encoding, - ) - - prioritized_encodings.append("ascii") - - if "utf_8" not in prioritized_encodings: - prioritized_encodings.append("utf_8") - - for encoding_iana in prioritized_encodings + IANA_SUPPORTED: - - if cp_isolation and encoding_iana not in cp_isolation: - continue - - if cp_exclusion and encoding_iana in cp_exclusion: - continue - - if encoding_iana in tested: - continue - - tested.add(encoding_iana) - - decoded_payload = None # type: Optional[str] - bom_or_sig_available = sig_encoding == encoding_iana # type: bool - strip_sig_or_bom = bom_or_sig_available and should_strip_sig_or_bom( - encoding_iana - ) # type: bool - - if encoding_iana in {"utf_16", "utf_32"} and bom_or_sig_available is False: - logger.info( - "Encoding %s wont be tested as-is because it require a BOM. Will try some sub-encoder LE/BE.", - encoding_iana, - ) - continue - - try: - is_multi_byte_decoder = is_multi_byte_encoding(encoding_iana) # type: bool - except (ModuleNotFoundError, ImportError): - logger.debug( - "Encoding %s does not provide an IncrementalDecoder", encoding_iana - ) - continue - - try: - if is_too_large_sequence and is_multi_byte_decoder is False: - str( - sequences[: int(50e4)] - if strip_sig_or_bom is False - else sequences[len(sig_payload) : int(50e4)], - encoding=encoding_iana, - ) - else: - decoded_payload = str( - sequences - if strip_sig_or_bom is False - else sequences[len(sig_payload) :], - encoding=encoding_iana, - ) - except (UnicodeDecodeError, LookupError) as e: - if not isinstance(e, LookupError): - logger.warning( - "Code page %s does not fit given bytes sequence at ALL. %s", - encoding_iana, - str(e), - ) - tested_but_hard_failure.append(encoding_iana) - continue - - similar_soft_failure_test = False # type: bool - - for encoding_soft_failed in tested_but_soft_failure: - if is_cp_similar(encoding_iana, encoding_soft_failed): - similar_soft_failure_test = True - break - - if similar_soft_failure_test: - logger.warning( - "%s is deemed too similar to code page %s and was consider unsuited already. Continuing!", - encoding_iana, - encoding_soft_failed, - ) - continue - - r_ = range( - 0 if bom_or_sig_available is False else len(sig_payload), - length, - int(length / steps), - ) - - multi_byte_bonus = ( - is_multi_byte_decoder - and decoded_payload is not None - and len(decoded_payload) < length - ) # type: bool - - if multi_byte_bonus: - logger.info( - "Code page %s is a multi byte encoding table and it appear that at least one character " - "was encoded using n-bytes.", - encoding_iana, - ) - - max_chunk_gave_up = int(len(r_) / 4) # type: int - - if max_chunk_gave_up < 2: - max_chunk_gave_up = 2 - - early_stop_count = 0 # type: int - - md_chunks = [] # type: List[str] - md_ratios = [] - - for i in r_: - cut_sequence = sequences[i : i + chunk_size] - - if bom_or_sig_available and strip_sig_or_bom is False: - cut_sequence = sig_payload + cut_sequence - - chunk = cut_sequence.decode(encoding_iana, errors="ignore") # type: str - - # multi-byte bad cutting detector and adjustment - # not the cleanest way to perform that fix but clever enough for now. - if is_multi_byte_decoder and i > 0 and sequences[i] >= 0x80: - - chunk_partial_size_chk = ( - 16 if chunk_size > 16 else chunk_size - ) # type: int - - if ( - decoded_payload - and chunk[:chunk_partial_size_chk] not in decoded_payload - ): - for j in range(i, i - 4, -1): - cut_sequence = sequences[j : i + chunk_size] - - if bom_or_sig_available and strip_sig_or_bom is False: - cut_sequence = sig_payload + cut_sequence - - chunk = cut_sequence.decode(encoding_iana, errors="ignore") - - if chunk[:chunk_partial_size_chk] in decoded_payload: - break - - md_chunks.append(chunk) - - md_ratios.append(mess_ratio(chunk, threshold)) - - if md_ratios[-1] >= threshold: - early_stop_count += 1 - - if (early_stop_count >= max_chunk_gave_up) or ( - bom_or_sig_available and strip_sig_or_bom is False - ): - break - - if md_ratios: - mean_mess_ratio = sum(md_ratios) / len(md_ratios) # type: float - else: - mean_mess_ratio = 0.0 - - if mean_mess_ratio >= threshold or early_stop_count >= max_chunk_gave_up: - tested_but_soft_failure.append(encoding_iana) - logger.warning( - "%s was excluded because of initial chaos probing. Gave up %i time(s). " - "Computed mean chaos is %f %%.", - encoding_iana, - early_stop_count, - round(mean_mess_ratio * 100, ndigits=3), - ) - # Preparing those fallbacks in case we got nothing. - if encoding_iana in ["ascii", "utf_8", specified_encoding]: - fallback_entry = CharsetMatch( - sequences, encoding_iana, threshold, False, [], decoded_payload - ) - if encoding_iana == specified_encoding: - fallback_specified = fallback_entry - elif encoding_iana == "ascii": - fallback_ascii = fallback_entry - else: - fallback_u8 = fallback_entry - continue - - logger.info( - "%s passed initial chaos probing. Mean measured chaos is %f %%", - encoding_iana, - round(mean_mess_ratio * 100, ndigits=3), - ) - - if not is_multi_byte_decoder: - target_languages = encoding_languages(encoding_iana) # type: List[str] - else: - target_languages = mb_encoding_languages(encoding_iana) - - if target_languages: - logger.info( - "{} should target any language(s) of {}".format( - encoding_iana, str(target_languages) - ) - ) - - cd_ratios = [] - - for chunk in md_chunks: - chunk_languages = coherence_ratio( - chunk, 0.1, ",".join(target_languages) if target_languages else None - ) - - cd_ratios.append(chunk_languages) - - cd_ratios_merged = merge_coherence_ratios(cd_ratios) - - if cd_ratios_merged: - logger.info( - "We detected language {} using {}".format( - cd_ratios_merged, encoding_iana - ) - ) - - results.append( - CharsetMatch( - sequences, - encoding_iana, - mean_mess_ratio, - bom_or_sig_available, - cd_ratios_merged, - decoded_payload, - ) - ) - - if ( - encoding_iana in [specified_encoding, "ascii", "utf_8"] - and mean_mess_ratio < 0.1 - ): - logger.info( - "%s is most likely the one. Stopping the process.", encoding_iana - ) - return CharsetMatches([results[encoding_iana]]) - - if encoding_iana == sig_encoding: - logger.info( - "%s is most likely the one as we detected a BOM or SIG within the beginning of the sequence.", - encoding_iana, - ) - return CharsetMatches([results[encoding_iana]]) - - if len(results) == 0: - if fallback_u8 or fallback_ascii or fallback_specified: - logger.warning( - "Nothing got out of the detection process. Using ASCII/UTF-8/Specified fallback." - ) - - if fallback_specified: - logger.warning( - "%s will be used as a fallback match", fallback_specified.encoding - ) - results.append(fallback_specified) - elif ( - (fallback_u8 and fallback_ascii is None) - or ( - fallback_u8 - and fallback_ascii - and fallback_u8.fingerprint != fallback_ascii.fingerprint - ) - or (fallback_u8 is not None) - ): - logger.warning("utf_8 will be used as a fallback match") - results.append(fallback_u8) - elif fallback_ascii: - logger.warning("ascii will be used as a fallback match") - results.append(fallback_ascii) - - return results - - -def from_fp( - fp: BinaryIO, - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.20, - cp_isolation: List[str] = None, - cp_exclusion: List[str] = None, - preemptive_behaviour: bool = True, - explain: bool = False, -) -> CharsetMatches: - """ - Same thing than the function from_bytes but using a file pointer that is already ready. - Will not close the file pointer. - """ - return from_bytes( - fp.read(), - steps, - chunk_size, - threshold, - cp_isolation, - cp_exclusion, - preemptive_behaviour, - explain, - ) - - -def from_path( - path: PathLike, - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.20, - cp_isolation: List[str] = None, - cp_exclusion: List[str] = None, - preemptive_behaviour: bool = True, - explain: bool = False, -) -> CharsetMatches: - """ - Same thing than the function from_bytes but with one extra step. Opening and reading given file path in binary mode. - Can raise IOError. - """ - with open(path, "rb") as fp: - return from_fp( - fp, - steps, - chunk_size, - threshold, - cp_isolation, - cp_exclusion, - preemptive_behaviour, - explain, - ) - - -def normalize( - path: PathLike, - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.20, - cp_isolation: List[str] = None, - cp_exclusion: List[str] = None, - preemptive_behaviour: bool = True, -) -> CharsetMatch: - """ - Take a (text-based) file path and try to create another file next to it, this time using UTF-8. - """ - results = from_path( - path, - steps, - chunk_size, - threshold, - cp_isolation, - cp_exclusion, - preemptive_behaviour, - ) - - filename = basename(path) - target_extensions = list(splitext(filename)) - - if len(results) == 0: - raise IOError( - 'Unable to normalize "{}", no encoding charset seems to fit.'.format( - filename - ) - ) - - result = results.best() - - target_extensions[0] += "-" + result.encoding # type: ignore - - with open( - "{}".format(str(path).replace(filename, "".join(target_extensions))), "wb" - ) as fp: - fp.write(result.output()) # type: ignore - - return result # type: ignore diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/assets/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/assets/__init__.py deleted file mode 100644 index b2e56ff3..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/assets/__init__.py +++ /dev/null @@ -1,1244 +0,0 @@ -# -*- coding: utf_8 -*- -from collections import OrderedDict - -FREQUENCIES = OrderedDict( - [ - ( - "English", - [ - "e", - "a", - "t", - "i", - "o", - "n", - "s", - "r", - "h", - "l", - "d", - "c", - "u", - "m", - "f", - "p", - "g", - "w", - "y", - "b", - "v", - "k", - "x", - "j", - "z", - "q", - ], - ), - ( - "German", - [ - "e", - "n", - "i", - "r", - "s", - "t", - "a", - "d", - "h", - "u", - "l", - "g", - "o", - "c", - "m", - "b", - "f", - "k", - "w", - "z", - "p", - "v", - "ü", - "ä", - "ö", - "j", - ], - ), - ( - "French", - [ - "e", - "a", - "s", - "n", - "i", - "t", - "r", - "l", - "u", - "o", - "d", - "c", - "p", - "m", - "é", - "v", - "g", - "f", - "b", - "h", - "q", - "à", - "x", - "è", - "y", - "j", - ], - ), - ( - "Dutch", - [ - "e", - "n", - "a", - "i", - "r", - "t", - "o", - "d", - "s", - "l", - "g", - "h", - "v", - "m", - "u", - "k", - "c", - "p", - "b", - "w", - "j", - "z", - "f", - "y", - "x", - "ë", - ], - ), - ( - "Italian", - [ - "e", - "i", - "a", - "o", - "n", - "l", - "t", - "r", - "s", - "c", - "d", - "u", - "p", - "m", - "g", - "v", - "f", - "b", - "z", - "h", - "q", - "è", - "à", - "k", - "y", - "ò", - ], - ), - ( - "Polish", - [ - "a", - "i", - "o", - "e", - "n", - "r", - "z", - "w", - "s", - "c", - "t", - "k", - "y", - "d", - "p", - "m", - "u", - "l", - "j", - "Å‚", - "g", - "b", - "h", - "Ä…", - "Ä™", - "ó", - ], - ), - ( - "Spanish", - [ - "e", - "a", - "o", - "n", - "s", - "r", - "i", - "l", - "d", - "t", - "c", - "u", - "m", - "p", - "b", - "g", - "v", - "f", - "y", - "ó", - "h", - "q", - "í", - "j", - "z", - "á", - ], - ), - ( - "Russian", - [ - "о", - "а", - "е", - "и", - "н", - "Ñ", - "Ñ‚", - "Ñ€", - "в", - "л", - "к", - "м", - "д", - "п", - "у", - "г", - "Ñ", - "Ñ‹", - "з", - "б", - "й", - "ÑŒ", - "ч", - "Ñ…", - "ж", - "ц", - ], - ), - ( - "Japanese", - [ - "ã®", - "ã«", - "ã‚‹", - "ãŸ", - "ã¯", - "ー", - "ã¨", - "ã—", - "ã‚’", - "ã§", - "ã¦", - "ãŒ", - "ã„", - "ン", - "れ", - "ãª", - "å¹´", - "ス", - "ã£", - "ル", - "ã‹", - "ら", - "ã‚", - "ã•", - "ã‚‚", - "り", - ], - ), - ( - "Portuguese", - [ - "a", - "e", - "o", - "s", - "i", - "r", - "d", - "n", - "t", - "m", - "u", - "c", - "l", - "p", - "g", - "v", - "b", - "f", - "h", - "ã", - "q", - "é", - "ç", - "á", - "z", - "í", - ], - ), - ( - "Swedish", - [ - "e", - "a", - "n", - "r", - "t", - "s", - "i", - "l", - "d", - "o", - "m", - "k", - "g", - "v", - "h", - "f", - "u", - "p", - "ä", - "c", - "b", - "ö", - "Ã¥", - "y", - "j", - "x", - ], - ), - ( - "Chinese", - [ - "çš„", - "一", - "是", - "ä¸", - "了", - "在", - "人", - "有", - "我", - "ä»–", - "è¿™", - "个", - "们", - "中", - "æ¥", - "上", - "大", - "为", - "å’Œ", - "国", - "地", - "到", - "以", - "说", - "æ—¶", - "è¦", - "å°±", - "出", - "会", - ], - ), - ( - "Ukrainian", - [ - "о", - "а", - "н", - "Ñ–", - "и", - "Ñ€", - "в", - "Ñ‚", - "е", - "Ñ", - "к", - "л", - "у", - "д", - "м", - "п", - "з", - "Ñ", - "ÑŒ", - "б", - "г", - "й", - "ч", - "Ñ…", - "ц", - "Ñ—", - ], - ), - ( - "Norwegian", - [ - "e", - "r", - "n", - "t", - "a", - "s", - "i", - "o", - "l", - "d", - "g", - "k", - "m", - "v", - "f", - "p", - "u", - "b", - "h", - "Ã¥", - "y", - "j", - "ø", - "c", - "æ", - "w", - ], - ), - ( - "Finnish", - [ - "a", - "i", - "n", - "t", - "e", - "s", - "l", - "o", - "u", - "k", - "ä", - "m", - "r", - "v", - "j", - "h", - "p", - "y", - "d", - "ö", - "g", - "c", - "b", - "f", - "w", - "z", - ], - ), - ( - "Vietnamese", - [ - "n", - "h", - "t", - "i", - "c", - "g", - "a", - "o", - "u", - "m", - "l", - "r", - "à", - "Ä‘", - "s", - "e", - "v", - "p", - "b", - "y", - "ư", - "d", - "á", - "k", - "á»™", - "ế", - ], - ), - ( - "Czech", - [ - "o", - "e", - "a", - "n", - "t", - "s", - "i", - "l", - "v", - "r", - "k", - "d", - "u", - "m", - "p", - "í", - "c", - "h", - "z", - "á", - "y", - "j", - "b", - "Ä›", - "é", - "Å™", - ], - ), - ( - "Hungarian", - [ - "e", - "a", - "t", - "l", - "s", - "n", - "k", - "r", - "i", - "o", - "z", - "á", - "é", - "g", - "m", - "b", - "y", - "v", - "d", - "h", - "u", - "p", - "j", - "ö", - "f", - "c", - ], - ), - ( - "Korean", - [ - "ì´", - "다", - "ì—", - "ì˜", - "는", - "로", - "하", - "ì„", - "ê°€", - "ê³ ", - "ì§€", - "서", - "한", - "ì€", - "기", - "으", - "ë…„", - "대", - "사", - "시", - "를", - "리", - "ë„", - "ì¸", - "스", - "ì¼", - ], - ), - ( - "Indonesian", - [ - "a", - "n", - "e", - "i", - "r", - "t", - "u", - "s", - "d", - "k", - "m", - "l", - "g", - "p", - "b", - "o", - "h", - "y", - "j", - "c", - "w", - "f", - "v", - "z", - "x", - "q", - ], - ), - ( - "Turkish", - [ - "a", - "e", - "i", - "n", - "r", - "l", - "ı", - "k", - "d", - "t", - "s", - "m", - "y", - "u", - "o", - "b", - "ü", - "ÅŸ", - "v", - "g", - "z", - "h", - "c", - "p", - "ç", - "ÄŸ", - ], - ), - ( - "Romanian", - [ - "e", - "i", - "a", - "r", - "n", - "t", - "u", - "l", - "o", - "c", - "s", - "d", - "p", - "m", - "ă", - "f", - "v", - "î", - "g", - "b", - "È™", - "È›", - "z", - "h", - "â", - "j", - ], - ), - ( - "Farsi", - [ - "ا", - "ÛŒ", - "ر", - "د", - "Ù†", - "Ù‡", - "Ùˆ", - "Ù…", - "ت", - "ب", - "س", - "Ù„", - "Ú©", - "Ø´", - "ز", - "Ù", - "Ú¯", - "ع", - "Ø®", - "Ù‚", - "ج", - "Ø¢", - "Ù¾", - "Ø­", - "Ø·", - "ص", - ], - ), - ( - "Arabic", - [ - "ا", - "Ù„", - "ÙŠ", - "Ù…", - "Ùˆ", - "Ù†", - "ر", - "ت", - "ب", - "Ø©", - "ع", - "د", - "س", - "Ù", - "Ù‡", - "Ùƒ", - "Ù‚", - "Ø£", - "Ø­", - "ج", - "Ø´", - "Ø·", - "ص", - "Ù‰", - "Ø®", - "Ø¥", - ], - ), - ( - "Danish", - [ - "e", - "r", - "n", - "t", - "a", - "i", - "s", - "d", - "l", - "o", - "g", - "m", - "k", - "f", - "v", - "u", - "b", - "h", - "p", - "Ã¥", - "y", - "ø", - "æ", - "c", - "j", - "w", - ], - ), - ( - "Serbian", - [ - "а", - "и", - "о", - "е", - "н", - "Ñ€", - "Ñ", - "у", - "Ñ‚", - "к", - "ј", - "в", - "д", - "м", - "п", - "л", - "г", - "з", - "б", - "a", - "i", - "e", - "o", - "n", - "ц", - "ш", - ], - ), - ( - "Lithuanian", - [ - "i", - "a", - "s", - "o", - "r", - "e", - "t", - "n", - "u", - "k", - "m", - "l", - "p", - "v", - "d", - "j", - "g", - "Ä—", - "b", - "y", - "ų", - "Å¡", - "ž", - "c", - "Ä…", - "į", - ], - ), - ( - "Slovene", - [ - "e", - "a", - "i", - "o", - "n", - "r", - "s", - "l", - "t", - "j", - "v", - "k", - "d", - "p", - "m", - "u", - "z", - "b", - "g", - "h", - "Ä", - "c", - "Å¡", - "ž", - "f", - "y", - ], - ), - ( - "Slovak", - [ - "o", - "a", - "e", - "n", - "i", - "r", - "v", - "t", - "s", - "l", - "k", - "d", - "m", - "p", - "u", - "c", - "h", - "j", - "b", - "z", - "á", - "y", - "ý", - "í", - "Ä", - "é", - ], - ), - ( - "Hebrew", - [ - "×™", - "ו", - "×”", - "ל", - "ר", - "ב", - "ת", - "מ", - "×", - "ש", - "× ", - "×¢", - "×", - "ד", - "×§", - "×—", - "פ", - "ס", - "×›", - "×’", - "ט", - "צ", - "ן", - "×–", - "ך", - ], - ), - ( - "Bulgarian", - [ - "а", - "и", - "о", - "е", - "н", - "Ñ‚", - "Ñ€", - "Ñ", - "в", - "л", - "к", - "д", - "п", - "м", - "з", - "г", - "Ñ", - "ÑŠ", - "у", - "б", - "ч", - "ц", - "й", - "ж", - "щ", - "Ñ…", - ], - ), - ( - "Croatian", - [ - "a", - "i", - "o", - "e", - "n", - "r", - "j", - "s", - "t", - "u", - "k", - "l", - "v", - "d", - "m", - "p", - "g", - "z", - "b", - "c", - "Ä", - "h", - "Å¡", - "ž", - "ć", - "f", - ], - ), - ( - "Hindi", - [ - "क", - "र", - "स", - "न", - "त", - "म", - "ह", - "प", - "य", - "ल", - "व", - "ज", - "द", - "ग", - "ब", - "श", - "ट", - "अ", - "à¤", - "थ", - "भ", - "ड", - "च", - "ध", - "ष", - "इ", - ], - ), - ( - "Estonian", - [ - "a", - "i", - "e", - "s", - "t", - "l", - "u", - "n", - "o", - "k", - "r", - "d", - "m", - "v", - "g", - "p", - "j", - "h", - "ä", - "b", - "õ", - "ü", - "f", - "c", - "ö", - "y", - ], - ), - ( - "Simple English", - [ - "e", - "a", - "t", - "i", - "o", - "n", - "s", - "r", - "h", - "l", - "d", - "c", - "m", - "u", - "f", - "p", - "g", - "w", - "b", - "y", - "v", - "k", - "j", - "x", - "z", - "q", - ], - ), - ( - "Thai", - [ - "า", - "น", - "ร", - "อ", - "à¸", - "เ", - "ง", - "ม", - "ย", - "ล", - "ว", - "ด", - "ท", - "ส", - "ต", - "ะ", - "ป", - "บ", - "ค", - "ห", - "à¹", - "จ", - "พ", - "ช", - "ข", - "ใ", - ], - ), - ( - "Greek", - [ - "α", - "Ï„", - "ο", - "ι", - "ε", - "ν", - "Ï", - "σ", - "κ", - "η", - "Ï€", - "Ï‚", - "Ï…", - "μ", - "λ", - "ί", - "ÏŒ", - "ά", - "γ", - "έ", - "δ", - "ή", - "ω", - "χ", - "θ", - "Ï", - ], - ), - ( - "Tamil", - [ - "க", - "த", - "ப", - "ட", - "à®°", - "à®®", - "ல", - "ன", - "வ", - "à®±", - "ய", - "ள", - "ச", - "ந", - "இ", - "ண", - "à®…", - "ஆ", - "à®´", - "à®™", - "எ", - "உ", - "à®’", - "ஸ", - ], - ), - ( - "Classical Chinese", - [ - "之", - "å¹´", - "為", - "也", - "以", - "一", - "人", - "å…¶", - "者", - "國", - "有", - "二", - "å", - "æ–¼", - "æ›°", - "三", - "ä¸", - "大", - "而", - "å­", - "中", - "五", - "å››", - ], - ), - ( - "Kazakh", - [ - "а", - "Ñ‹", - "е", - "н", - "Ñ‚", - "Ñ€", - "л", - "Ñ–", - "д", - "Ñ", - "м", - "Ò›", - "к", - "о", - "б", - "и", - "у", - "Ò“", - "ж", - "Ò£", - "з", - "ш", - "й", - "п", - "г", - "Ó©", - ], - ), - ] -) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/assets/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/assets/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 7a0755cc411ca01ba39e3aa5e3e48ae584034eb6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7368 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cd.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cd.py deleted file mode 100644 index a4512fbb..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cd.py +++ /dev/null @@ -1,341 +0,0 @@ -import importlib -from codecs import IncrementalDecoder -from collections import Counter, OrderedDict -from functools import lru_cache -from typing import Dict, List, Optional, Tuple - -from .assets import FREQUENCIES -from .constant import KO_NAMES, TOO_SMALL_SEQUENCE, ZH_NAMES -from .md import is_suspiciously_successive_range -from .models import CoherenceMatches -from .utils import ( - is_accentuated, - is_latin, - is_multi_byte_encoding, - is_unicode_range_secondary, - unicode_range, -) - - -def encoding_unicode_range(iana_name: str) -> List[str]: - """ - Return associated unicode ranges in a single byte code page. - """ - if is_multi_byte_encoding(iana_name): - raise IOError("Function not supported on multi-byte code page") - - decoder = importlib.import_module("encodings.{}".format(iana_name)).IncrementalDecoder # type: ignore - - p = decoder(errors="ignore") # type: IncrementalDecoder - seen_ranges = {} # type: Dict[str, int] - character_count = 0 # type: int - - for i in range(0x40, 0xFF): - chunk = p.decode(bytes([i])) # type: str - - if chunk: - character_range = unicode_range(chunk) # type: Optional[str] - - if character_range is None: - continue - - if is_unicode_range_secondary(character_range) is False: - if character_range not in seen_ranges: - seen_ranges[character_range] = 0 - seen_ranges[character_range] += 1 - character_count += 1 - - return sorted( - [ - character_range - for character_range in seen_ranges - if seen_ranges[character_range] / character_count >= 0.15 - ] - ) - - -def unicode_range_languages(primary_range: str) -> List[str]: - """ - Return inferred languages used with a unicode range. - """ - languages = [] # type: List[str] - - for language, characters in FREQUENCIES.items(): - for character in characters: - if unicode_range(character) == primary_range: - languages.append(language) - break - - return languages - - -@lru_cache() -def encoding_languages(iana_name: str) -> List[str]: - """ - Single-byte encoding language association. Some code page are heavily linked to particular language(s). - This function does the correspondence. - """ - unicode_ranges = encoding_unicode_range(iana_name) # type: List[str] - primary_range = None # type: Optional[str] - - for specified_range in unicode_ranges: - if "Latin" not in specified_range: - primary_range = specified_range - break - - if primary_range is None: - return ["Latin Based"] - - return unicode_range_languages(primary_range) - - -@lru_cache() -def mb_encoding_languages(iana_name: str) -> List[str]: - """ - Multi-byte encoding language association. Some code page are heavily linked to particular language(s). - This function does the correspondence. - """ - if ( - iana_name.startswith("shift_") - or iana_name.startswith("iso2022_jp") - or iana_name.startswith("euc_j") - or iana_name == "cp932" - ): - return ["Japanese"] - if iana_name.startswith("gb") or iana_name in ZH_NAMES: - return ["Chinese", "Classical Chinese"] - if iana_name.startswith("iso2022_kr") or iana_name in KO_NAMES: - return ["Korean"] - - return [] - - -def alphabet_languages( - characters: List[str], ignore_non_latin: bool = False -) -> List[str]: - """ - Return associated languages associated to given characters. - """ - languages = [] # type: List[Tuple[str, float]] - - source_have_accents = False # type: bool - - for character in characters: - if is_accentuated(character): - source_have_accents = True - break - - for language, language_characters in FREQUENCIES.items(): - - target_have_accents = False # type: bool - target_pure_latin = True # type: bool - - for language_character in language_characters: - if target_have_accents is False and is_accentuated(language_character): - target_have_accents = True - if target_pure_latin is True and is_latin(language_character) is False: - target_pure_latin = False - - if ignore_non_latin and target_pure_latin is False: - continue - - if target_have_accents is False and source_have_accents: - continue - - character_count = len(language_characters) # type: int - - character_match_count = len( - [c for c in language_characters if c in characters] - ) # type: int - - ratio = character_match_count / character_count # type: float - - if ratio >= 0.2: - languages.append((language, ratio)) - - languages = sorted(languages, key=lambda x: x[1], reverse=True) - - return [compatible_language[0] for compatible_language in languages] - - -def characters_popularity_compare( - language: str, ordered_characters: List[str] -) -> float: - """ - Determine if a ordered characters list (by occurrence from most appearance to rarest) match a particular language. - The result is a ratio between 0. (absolutely no correspondence) and 1. (near perfect fit). - Beware that is function is not strict on the match in order to ease the detection. (Meaning close match is 1.) - """ - if language not in FREQUENCIES: - raise ValueError("{} not available".format(language)) - - character_approved_count = 0 # type: int - - for character in ordered_characters: - if character not in FREQUENCIES[language]: - continue - - characters_before_source = FREQUENCIES[language][ - 0 : FREQUENCIES[language].index(character) - ] # type: List[str] - characters_after_source = FREQUENCIES[language][ - FREQUENCIES[language].index(character) : - ] # type: List[str] - - characters_before = ordered_characters[ - 0 : ordered_characters.index(character) - ] # type: List[str] - characters_after = ordered_characters[ - ordered_characters.index(character) : - ] # type: List[str] - - before_match_count = [ - e in characters_before for e in characters_before_source - ].count( - True - ) # type: int - after_match_count = [ - e in characters_after for e in characters_after_source - ].count( - True - ) # type: int - - if len(characters_before_source) == 0 and before_match_count <= 4: - character_approved_count += 1 - continue - - if len(characters_after_source) == 0 and after_match_count <= 4: - character_approved_count += 1 - continue - - if ( - before_match_count / len(characters_before_source) >= 0.4 - or after_match_count / len(characters_after_source) >= 0.4 - ): - character_approved_count += 1 - continue - - return character_approved_count / len(ordered_characters) - - -def alpha_unicode_split(decoded_sequence: str) -> List[str]: - """ - Given a decoded text sequence, return a list of str. Unicode range / alphabet separation. - Ex. a text containing English/Latin with a bit a Hebrew will return two items in the resulting list; - One containing the latin letters and the other hebrew. - """ - layers = OrderedDict() # type: Dict[str, str] - - for character in decoded_sequence: - if character.isalpha() is False: - continue - - character_range = unicode_range(character) # type: Optional[str] - - if character_range is None: - continue - - layer_target_range = None # type: Optional[str] - - for discovered_range in layers: - if ( - is_suspiciously_successive_range(discovered_range, character_range) - is False - ): - layer_target_range = discovered_range - break - - if layer_target_range is None: - layer_target_range = character_range - - if layer_target_range not in layers: - layers[layer_target_range] = character.lower() - continue - - layers[layer_target_range] += character.lower() - - return list(layers.values()) - - -def merge_coherence_ratios(results: List[CoherenceMatches]) -> CoherenceMatches: - """ - This function merge results previously given by the function coherence_ratio. - The return type is the same as coherence_ratio. - """ - per_language_ratios = OrderedDict() # type: Dict[str, List[float]] - merge = [] # type: CoherenceMatches - - for result in results: - for sub_result in result: - language, ratio = sub_result - if language not in per_language_ratios: - per_language_ratios[language] = [ratio] - continue - per_language_ratios[language].append(ratio) - - for language in per_language_ratios: - merge.append( - ( - language, - round( - sum(per_language_ratios[language]) - / len(per_language_ratios[language]), - 4, - ), - ) - ) - - return sorted(merge, key=lambda x: x[1], reverse=True) - - -@lru_cache(maxsize=2048) -def coherence_ratio( - decoded_sequence: str, threshold: float = 0.1, lg_inclusion: Optional[str] = None -) -> CoherenceMatches: - """ - Detect ANY language that can be identified in given sequence. The sequence will be analysed by layers. - A layer = Character extraction by alphabets/ranges. - """ - - results = [] # type: List[Tuple[str, float]] - lg_inclusion_list = [] # type: List[str] - ignore_non_latin = False # type: bool - - sufficient_match_count = 0 # type: int - - if lg_inclusion is not None: - lg_inclusion_list = lg_inclusion.split(",") - - if "Latin Based" in lg_inclusion_list: - ignore_non_latin = True - lg_inclusion_list.remove("Latin Based") - - for layer in alpha_unicode_split(decoded_sequence): - sequence_frequencies = Counter(layer) # type: Counter - most_common = sequence_frequencies.most_common() - - character_count = sum([o for c, o in most_common]) # type: int - - if character_count <= TOO_SMALL_SEQUENCE: - continue - - popular_character_ordered = [c for c, o in most_common] # type: List[str] - - for language in lg_inclusion_list or alphabet_languages( - popular_character_ordered, ignore_non_latin - ): - ratio = characters_popularity_compare( - language, popular_character_ordered - ) # type: float - - if ratio < threshold: - continue - elif ratio >= 0.8: - sufficient_match_count += 1 - - results.append((language, round(ratio, 4))) - - if sufficient_match_count >= 3: - break - - return sorted(results, key=lambda x: x[1], reverse=True) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 9ac03ecf6b6375d01d836ef6f5c902149263a0c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 203 zcmYk0F$w}P5Jj_Kp@IjoaHp^mL=+ShL4-X(7_-3)W|NS_h4n_>#@f~+*jbrW`r*IN z|1dB(8Vy;};1MwAE8|~9D$C^1OQ>1J#>L^ze4c;!IA~-|2wU{^+EXjx*r{j@R6<#+ zVYlC`cn}_)^ia0aw4mt(u8Yc=X*q*HQBE9+3!Y^Fq0k2zxv{=MO}BI;Gy&&iC~{u9 R?%AI=w!Uh)$e+BE*%!B|Hp>72 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__pycache__/normalizer.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__pycache__/normalizer.cpython-39.pyc deleted file mode 100644 index a1ac0cdfd18125e7fbd6daec92fa274ae3e4b2b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6151 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cli/normalizer.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cli/normalizer.py deleted file mode 100644 index f1911259..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/cli/normalizer.py +++ /dev/null @@ -1,291 +0,0 @@ -import argparse -import sys -from json import dumps -from os.path import abspath -from platform import python_version -from typing import List - -from charset_normalizer import from_fp -from charset_normalizer.models import CliDetectionResult -from charset_normalizer.version import __version__ - - -def query_yes_no(question: str, default: str = "yes") -> bool: - """Ask a yes/no question via input() and return their answer. - - "question" is a string that is presented to the user. - "default" is the presumed answer if the user just hits . - It must be "yes" (the default), "no" or None (meaning - an answer is required of the user). - - The "answer" return value is True for "yes" or False for "no". - - Credit goes to (c) https://stackoverflow.com/questions/3041986/apt-command-line-interface-like-yes-no-input - """ - valid = {"yes": True, "y": True, "ye": True, "no": False, "n": False} - if default is None: - prompt = " [y/n] " - elif default == "yes": - prompt = " [Y/n] " - elif default == "no": - prompt = " [y/N] " - else: - raise ValueError("invalid default answer: '%s'" % default) - - while True: - sys.stdout.write(question + prompt) - choice = input().lower() - if default is not None and choice == "": - return valid[default] - elif choice in valid: - return valid[choice] - else: - sys.stdout.write("Please respond with 'yes' or 'no' " "(or 'y' or 'n').\n") - - -def cli_detect(argv: List[str] = None) -> int: - """ - CLI assistant using ARGV and ArgumentParser - :param argv: - :return: 0 if everything is fine, anything else equal trouble - """ - parser = argparse.ArgumentParser( - description="The Real First Universal Charset Detector. " - "Discover originating encoding used on text file. " - "Normalize text to unicode." - ) - - parser.add_argument( - "files", type=argparse.FileType("rb"), nargs="+", help="File(s) to be analysed" - ) - parser.add_argument( - "-v", - "--verbose", - action="store_true", - default=False, - dest="verbose", - help="Display complementary information about file if any. " - "Stdout will contain logs about the detection process.", - ) - parser.add_argument( - "-a", - "--with-alternative", - action="store_true", - default=False, - dest="alternatives", - help="Output complementary possibilities if any. Top-level JSON WILL be a list.", - ) - parser.add_argument( - "-n", - "--normalize", - action="store_true", - default=False, - dest="normalize", - help="Permit to normalize input file. If not set, program does not write anything.", - ) - parser.add_argument( - "-m", - "--minimal", - action="store_true", - default=False, - dest="minimal", - help="Only output the charset detected to STDOUT. Disabling JSON output.", - ) - parser.add_argument( - "-r", - "--replace", - action="store_true", - default=False, - dest="replace", - help="Replace file when trying to normalize it instead of creating a new one.", - ) - parser.add_argument( - "-f", - "--force", - action="store_true", - default=False, - dest="force", - help="Replace file without asking if you are sure, use this flag with caution.", - ) - parser.add_argument( - "-t", - "--threshold", - action="store", - default=0.1, - type=float, - dest="threshold", - help="Define a custom maximum amount of chaos allowed in decoded content. 0. <= chaos <= 1.", - ) - parser.add_argument( - "--version", - action="version", - version="Charset-Normalizer {} - Python {}".format( - __version__, python_version() - ), - help="Show version information and exit.", - ) - - args = parser.parse_args(argv) - - if args.replace is True and args.normalize is False: - print("Use --replace in addition of --normalize only.", file=sys.stderr) - return 1 - - if args.force is True and args.replace is False: - print("Use --force in addition of --replace only.", file=sys.stderr) - return 1 - - if args.threshold < 0.0 or args.threshold > 1.0: - print("--threshold VALUE should be between 0. AND 1.", file=sys.stderr) - return 1 - - x_ = [] - - for my_file in args.files: - - matches = from_fp(my_file, threshold=args.threshold, explain=args.verbose) - - best_guess = matches.best() - - if best_guess is None: - print( - 'Unable to identify originating encoding for "{}". {}'.format( - my_file.name, - "Maybe try increasing maximum amount of chaos." - if args.threshold < 1.0 - else "", - ), - file=sys.stderr, - ) - x_.append( - CliDetectionResult( - abspath(my_file.name), - None, - [], - [], - "Unknown", - [], - False, - 1.0, - 0.0, - None, - True, - ) - ) - else: - x_.append( - CliDetectionResult( - abspath(my_file.name), - best_guess.encoding, - best_guess.encoding_aliases, - [ - cp - for cp in best_guess.could_be_from_charset - if cp != best_guess.encoding - ], - best_guess.language, - best_guess.alphabets, - best_guess.bom, - best_guess.percent_chaos, - best_guess.percent_coherence, - None, - True, - ) - ) - - if len(matches) > 1 and args.alternatives: - for el in matches: - if el != best_guess: - x_.append( - CliDetectionResult( - abspath(my_file.name), - el.encoding, - el.encoding_aliases, - [ - cp - for cp in el.could_be_from_charset - if cp != el.encoding - ], - el.language, - el.alphabets, - el.bom, - el.percent_chaos, - el.percent_coherence, - None, - False, - ) - ) - - if args.normalize is True: - - if best_guess.encoding.startswith("utf") is True: - print( - '"{}" file does not need to be normalized, as it already came from unicode.'.format( - my_file.name - ), - file=sys.stderr, - ) - if my_file.closed is False: - my_file.close() - continue - - o_ = my_file.name.split(".") # type: List[str] - - if args.replace is False: - o_.insert(-1, best_guess.encoding) - if my_file.closed is False: - my_file.close() - else: - if ( - args.force is False - and query_yes_no( - 'Are you sure to normalize "{}" by replacing it ?'.format( - my_file.name - ), - "no", - ) - is False - ): - if my_file.closed is False: - my_file.close() - continue - - try: - x_[0].unicode_path = abspath("./{}".format(".".join(o_))) - - with open(x_[0].unicode_path, "w", encoding="utf-8") as fp: - fp.write(str(best_guess)) - except IOError as e: - print(str(e), file=sys.stderr) - if my_file.closed is False: - my_file.close() - return 2 - - if my_file.closed is False: - my_file.close() - - if args.minimal is False: - print( - dumps( - [el.__dict__ for el in x_] if len(x_) > 1 else x_[0].__dict__, - ensure_ascii=True, - indent=4, - ) - ) - else: - for my_file in args.files: - print( - ", ".join( - [ - el.encoding if el.encoding else "undefined" - for el in x_ - if el.path == abspath(my_file.name) - ] - ) - ) - - return 0 - - -if __name__ == "__main__": - cli_detect() diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/constant.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/constant.py deleted file mode 100644 index 2e5974d9..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/constant.py +++ /dev/null @@ -1,496 +0,0 @@ -from codecs import BOM_UTF8, BOM_UTF16_BE, BOM_UTF16_LE, BOM_UTF32_BE, BOM_UTF32_LE -from collections import OrderedDict -from encodings.aliases import aliases -from re import IGNORECASE, compile as re_compile -from typing import Dict, List, Set, Union - -# Contain for each eligible encoding a list of/item bytes SIG/BOM -ENCODING_MARKS = OrderedDict( - [ - ("utf_8", BOM_UTF8), - ( - "utf_7", - [ - b"\x2b\x2f\x76\x38", - b"\x2b\x2f\x76\x39", - b"\x2b\x2f\x76\x2b", - b"\x2b\x2f\x76\x2f", - b"\x2b\x2f\x76\x38\x2d", - ], - ), - ("gb18030", b"\x84\x31\x95\x33"), - ("utf_32", [BOM_UTF32_BE, BOM_UTF32_LE]), - ("utf_16", [BOM_UTF16_BE, BOM_UTF16_LE]), - ] -) # type: Dict[str, Union[bytes, List[bytes]]] - -TOO_SMALL_SEQUENCE = 32 # type: int -TOO_BIG_SEQUENCE = int(10e6) # type: int - -UTF8_MAXIMAL_ALLOCATION = 1112064 # type: int - -UNICODE_RANGES_COMBINED = { - "Control character": range(0, 31 + 1), - "Basic Latin": range(32, 127 + 1), - "Latin-1 Supplement": range(128, 255 + 1), - "Latin Extended-A": range(256, 383 + 1), - "Latin Extended-B": range(384, 591 + 1), - "IPA Extensions": range(592, 687 + 1), - "Spacing Modifier Letters": range(688, 767 + 1), - "Combining Diacritical Marks": range(768, 879 + 1), - "Greek and Coptic": range(880, 1023 + 1), - "Cyrillic": range(1024, 1279 + 1), - "Cyrillic Supplement": range(1280, 1327 + 1), - "Armenian": range(1328, 1423 + 1), - "Hebrew": range(1424, 1535 + 1), - "Arabic": range(1536, 1791 + 1), - "Syriac": range(1792, 1871 + 1), - "Arabic Supplement": range(1872, 1919 + 1), - "Thaana": range(1920, 1983 + 1), - "NKo": range(1984, 2047 + 1), - "Samaritan": range(2048, 2111 + 1), - "Mandaic": range(2112, 2143 + 1), - "Syriac Supplement": range(2144, 2159 + 1), - "Arabic Extended-A": range(2208, 2303 + 1), - "Devanagari": range(2304, 2431 + 1), - "Bengali": range(2432, 2559 + 1), - "Gurmukhi": range(2560, 2687 + 1), - "Gujarati": range(2688, 2815 + 1), - "Oriya": range(2816, 2943 + 1), - "Tamil": range(2944, 3071 + 1), - "Telugu": range(3072, 3199 + 1), - "Kannada": range(3200, 3327 + 1), - "Malayalam": range(3328, 3455 + 1), - "Sinhala": range(3456, 3583 + 1), - "Thai": range(3584, 3711 + 1), - "Lao": range(3712, 3839 + 1), - "Tibetan": range(3840, 4095 + 1), - "Myanmar": range(4096, 4255 + 1), - "Georgian": range(4256, 4351 + 1), - "Hangul Jamo": range(4352, 4607 + 1), - "Ethiopic": range(4608, 4991 + 1), - "Ethiopic Supplement": range(4992, 5023 + 1), - "Cherokee": range(5024, 5119 + 1), - "Unified Canadian Aboriginal Syllabics": range(5120, 5759 + 1), - "Ogham": range(5760, 5791 + 1), - "Runic": range(5792, 5887 + 1), - "Tagalog": range(5888, 5919 + 1), - "Hanunoo": range(5920, 5951 + 1), - "Buhid": range(5952, 5983 + 1), - "Tagbanwa": range(5984, 6015 + 1), - "Khmer": range(6016, 6143 + 1), - "Mongolian": range(6144, 6319 + 1), - "Unified Canadian Aboriginal Syllabics Extended": range(6320, 6399 + 1), - "Limbu": range(6400, 6479 + 1), - "Tai Le": range(6480, 6527 + 1), - "New Tai Lue": range(6528, 6623 + 1), - "Khmer Symbols": range(6624, 6655 + 1), - "Buginese": range(6656, 6687 + 1), - "Tai Tham": range(6688, 6831 + 1), - "Combining Diacritical Marks Extended": range(6832, 6911 + 1), - "Balinese": range(6912, 7039 + 1), - "Sundanese": range(7040, 7103 + 1), - "Batak": range(7104, 7167 + 1), - "Lepcha": range(7168, 7247 + 1), - "Ol Chiki": range(7248, 7295 + 1), - "Cyrillic Extended C": range(7296, 7311 + 1), - "Sundanese Supplement": range(7360, 7375 + 1), - "Vedic Extensions": range(7376, 7423 + 1), - "Phonetic Extensions": range(7424, 7551 + 1), - "Phonetic Extensions Supplement": range(7552, 7615 + 1), - "Combining Diacritical Marks Supplement": range(7616, 7679 + 1), - "Latin Extended Additional": range(7680, 7935 + 1), - "Greek Extended": range(7936, 8191 + 1), - "General Punctuation": range(8192, 8303 + 1), - "Superscripts and Subscripts": range(8304, 8351 + 1), - "Currency Symbols": range(8352, 8399 + 1), - "Combining Diacritical Marks for Symbols": range(8400, 8447 + 1), - "Letterlike Symbols": range(8448, 8527 + 1), - "Number Forms": range(8528, 8591 + 1), - "Arrows": range(8592, 8703 + 1), - "Mathematical Operators": range(8704, 8959 + 1), - "Miscellaneous Technical": range(8960, 9215 + 1), - "Control Pictures": range(9216, 9279 + 1), - "Optical Character Recognition": range(9280, 9311 + 1), - "Enclosed Alphanumerics": range(9312, 9471 + 1), - "Box Drawing": range(9472, 9599 + 1), - "Block Elements": range(9600, 9631 + 1), - "Geometric Shapes": range(9632, 9727 + 1), - "Miscellaneous Symbols": range(9728, 9983 + 1), - "Dingbats": range(9984, 10175 + 1), - "Miscellaneous Mathematical Symbols-A": range(10176, 10223 + 1), - "Supplemental Arrows-A": range(10224, 10239 + 1), - "Braille Patterns": range(10240, 10495 + 1), - "Supplemental Arrows-B": range(10496, 10623 + 1), - "Miscellaneous Mathematical Symbols-B": range(10624, 10751 + 1), - "Supplemental Mathematical Operators": range(10752, 11007 + 1), - "Miscellaneous Symbols and Arrows": range(11008, 11263 + 1), - "Glagolitic": range(11264, 11359 + 1), - "Latin Extended-C": range(11360, 11391 + 1), - "Coptic": range(11392, 11519 + 1), - "Georgian Supplement": range(11520, 11567 + 1), - "Tifinagh": range(11568, 11647 + 1), - "Ethiopic Extended": range(11648, 11743 + 1), - "Cyrillic Extended-A": range(11744, 11775 + 1), - "Supplemental Punctuation": range(11776, 11903 + 1), - "CJK Radicals Supplement": range(11904, 12031 + 1), - "Kangxi Radicals": range(12032, 12255 + 1), - "Ideographic Description Characters": range(12272, 12287 + 1), - "CJK Symbols and Punctuation": range(12288, 12351 + 1), - "Hiragana": range(12352, 12447 + 1), - "Katakana": range(12448, 12543 + 1), - "Bopomofo": range(12544, 12591 + 1), - "Hangul Compatibility Jamo": range(12592, 12687 + 1), - "Kanbun": range(12688, 12703 + 1), - "Bopomofo Extended": range(12704, 12735 + 1), - "CJK Strokes": range(12736, 12783 + 1), - "Katakana Phonetic Extensions": range(12784, 12799 + 1), - "Enclosed CJK Letters and Months": range(12800, 13055 + 1), - "CJK Compatibility": range(13056, 13311 + 1), - "CJK Unified Ideographs Extension A": range(13312, 19903 + 1), - "Yijing Hexagram Symbols": range(19904, 19967 + 1), - "CJK Unified Ideographs": range(19968, 40959 + 1), - "Yi Syllables": range(40960, 42127 + 1), - "Yi Radicals": range(42128, 42191 + 1), - "Lisu": range(42192, 42239 + 1), - "Vai": range(42240, 42559 + 1), - "Cyrillic Extended-B": range(42560, 42655 + 1), - "Bamum": range(42656, 42751 + 1), - "Modifier Tone Letters": range(42752, 42783 + 1), - "Latin Extended-D": range(42784, 43007 + 1), - "Syloti Nagri": range(43008, 43055 + 1), - "Common Indic Number Forms": range(43056, 43071 + 1), - "Phags-pa": range(43072, 43135 + 1), - "Saurashtra": range(43136, 43231 + 1), - "Devanagari Extended": range(43232, 43263 + 1), - "Kayah Li": range(43264, 43311 + 1), - "Rejang": range(43312, 43359 + 1), - "Hangul Jamo Extended-A": range(43360, 43391 + 1), - "Javanese": range(43392, 43487 + 1), - "Myanmar Extended-B": range(43488, 43519 + 1), - "Cham": range(43520, 43615 + 1), - "Myanmar Extended-A": range(43616, 43647 + 1), - "Tai Viet": range(43648, 43743 + 1), - "Meetei Mayek Extensions": range(43744, 43775 + 1), - "Ethiopic Extended-A": range(43776, 43823 + 1), - "Latin Extended-E": range(43824, 43887 + 1), - "Cherokee Supplement": range(43888, 43967 + 1), - "Meetei Mayek": range(43968, 44031 + 1), - "Hangul Syllables": range(44032, 55215 + 1), - "Hangul Jamo Extended-B": range(55216, 55295 + 1), - "High Surrogates": range(55296, 56191 + 1), - "High Private Use Surrogates": range(56192, 56319 + 1), - "Low Surrogates": range(56320, 57343 + 1), - "Private Use Area": range(57344, 63743 + 1), - "CJK Compatibility Ideographs": range(63744, 64255 + 1), - "Alphabetic Presentation Forms": range(64256, 64335 + 1), - "Arabic Presentation Forms-A": range(64336, 65023 + 1), - "Variation Selectors": range(65024, 65039 + 1), - "Vertical Forms": range(65040, 65055 + 1), - "Combining Half Marks": range(65056, 65071 + 1), - "CJK Compatibility Forms": range(65072, 65103 + 1), - "Small Form Variants": range(65104, 65135 + 1), - "Arabic Presentation Forms-B": range(65136, 65279 + 1), - "Halfwidth and Fullwidth Forms": range(65280, 65519 + 1), - "Specials": range(65520, 65535 + 1), - "Linear B Syllabary": range(65536, 65663 + 1), - "Linear B Ideograms": range(65664, 65791 + 1), - "Aegean Numbers": range(65792, 65855 + 1), - "Ancient Greek Numbers": range(65856, 65935 + 1), - "Ancient Symbols": range(65936, 65999 + 1), - "Phaistos Disc": range(66000, 66047 + 1), - "Lycian": range(66176, 66207 + 1), - "Carian": range(66208, 66271 + 1), - "Coptic Epact Numbers": range(66272, 66303 + 1), - "Old Italic": range(66304, 66351 + 1), - "Gothic": range(66352, 66383 + 1), - "Old Permic": range(66384, 66431 + 1), - "Ugaritic": range(66432, 66463 + 1), - "Old Persian": range(66464, 66527 + 1), - "Deseret": range(66560, 66639 + 1), - "Shavian": range(66640, 66687 + 1), - "Osmanya": range(66688, 66735 + 1), - "Osage": range(66736, 66815 + 1), - "Elbasan": range(66816, 66863 + 1), - "Caucasian Albanian": range(66864, 66927 + 1), - "Linear A": range(67072, 67455 + 1), - "Cypriot Syllabary": range(67584, 67647 + 1), - "Imperial Aramaic": range(67648, 67679 + 1), - "Palmyrene": range(67680, 67711 + 1), - "Nabataean": range(67712, 67759 + 1), - "Hatran": range(67808, 67839 + 1), - "Phoenician": range(67840, 67871 + 1), - "Lydian": range(67872, 67903 + 1), - "Meroitic Hieroglyphs": range(67968, 67999 + 1), - "Meroitic Cursive": range(68000, 68095 + 1), - "Kharoshthi": range(68096, 68191 + 1), - "Old South Arabian": range(68192, 68223 + 1), - "Old North Arabian": range(68224, 68255 + 1), - "Manichaean": range(68288, 68351 + 1), - "Avestan": range(68352, 68415 + 1), - "Inscriptional Parthian": range(68416, 68447 + 1), - "Inscriptional Pahlavi": range(68448, 68479 + 1), - "Psalter Pahlavi": range(68480, 68527 + 1), - "Old Turkic": range(68608, 68687 + 1), - "Old Hungarian": range(68736, 68863 + 1), - "Rumi Numeral Symbols": range(69216, 69247 + 1), - "Brahmi": range(69632, 69759 + 1), - "Kaithi": range(69760, 69839 + 1), - "Sora Sompeng": range(69840, 69887 + 1), - "Chakma": range(69888, 69967 + 1), - "Mahajani": range(69968, 70015 + 1), - "Sharada": range(70016, 70111 + 1), - "Sinhala Archaic Numbers": range(70112, 70143 + 1), - "Khojki": range(70144, 70223 + 1), - "Multani": range(70272, 70319 + 1), - "Khudawadi": range(70320, 70399 + 1), - "Grantha": range(70400, 70527 + 1), - "Newa": range(70656, 70783 + 1), - "Tirhuta": range(70784, 70879 + 1), - "Siddham": range(71040, 71167 + 1), - "Modi": range(71168, 71263 + 1), - "Mongolian Supplement": range(71264, 71295 + 1), - "Takri": range(71296, 71375 + 1), - "Ahom": range(71424, 71487 + 1), - "Warang Citi": range(71840, 71935 + 1), - "Zanabazar Square": range(72192, 72271 + 1), - "Soyombo": range(72272, 72367 + 1), - "Pau Cin Hau": range(72384, 72447 + 1), - "Bhaiksuki": range(72704, 72815 + 1), - "Marchen": range(72816, 72895 + 1), - "Masaram Gondi": range(72960, 73055 + 1), - "Cuneiform": range(73728, 74751 + 1), - "Cuneiform Numbers and Punctuation": range(74752, 74879 + 1), - "Early Dynastic Cuneiform": range(74880, 75087 + 1), - "Egyptian Hieroglyphs": range(77824, 78895 + 1), - "Anatolian Hieroglyphs": range(82944, 83583 + 1), - "Bamum Supplement": range(92160, 92735 + 1), - "Mro": range(92736, 92783 + 1), - "Bassa Vah": range(92880, 92927 + 1), - "Pahawh Hmong": range(92928, 93071 + 1), - "Miao": range(93952, 94111 + 1), - "Ideographic Symbols and Punctuation": range(94176, 94207 + 1), - "Tangut": range(94208, 100351 + 1), - "Tangut Components": range(100352, 101119 + 1), - "Kana Supplement": range(110592, 110847 + 1), - "Kana Extended-A": range(110848, 110895 + 1), - "Nushu": range(110960, 111359 + 1), - "Duployan": range(113664, 113823 + 1), - "Shorthand Format Controls": range(113824, 113839 + 1), - "Byzantine Musical Symbols": range(118784, 119039 + 1), - "Musical Symbols": range(119040, 119295 + 1), - "Ancient Greek Musical Notation": range(119296, 119375 + 1), - "Tai Xuan Jing Symbols": range(119552, 119647 + 1), - "Counting Rod Numerals": range(119648, 119679 + 1), - "Mathematical Alphanumeric Symbols": range(119808, 120831 + 1), - "Sutton SignWriting": range(120832, 121519 + 1), - "Glagolitic Supplement": range(122880, 122927 + 1), - "Mende Kikakui": range(124928, 125151 + 1), - "Adlam": range(125184, 125279 + 1), - "Arabic Mathematical Alphabetic Symbols": range(126464, 126719 + 1), - "Mahjong Tiles": range(126976, 127023 + 1), - "Domino Tiles": range(127024, 127135 + 1), - "Playing Cards": range(127136, 127231 + 1), - "Enclosed Alphanumeric Supplement": range(127232, 127487 + 1), - "Enclosed Ideographic Supplement": range(127488, 127743 + 1), - "Miscellaneous Symbols and Pictographs": range(127744, 128511 + 1), - "Emoticons range(Emoji)": range(128512, 128591 + 1), - "Ornamental Dingbats": range(128592, 128639 + 1), - "Transport and Map Symbols": range(128640, 128767 + 1), - "Alchemical Symbols": range(128768, 128895 + 1), - "Geometric Shapes Extended": range(128896, 129023 + 1), - "Supplemental Arrows-C": range(129024, 129279 + 1), - "Supplemental Symbols and Pictographs": range(129280, 129535 + 1), - "CJK Unified Ideographs Extension B": range(131072, 173791 + 1), - "CJK Unified Ideographs Extension C": range(173824, 177983 + 1), - "CJK Unified Ideographs Extension D": range(177984, 178207 + 1), - "CJK Unified Ideographs Extension E": range(178208, 183983 + 1), - "CJK Unified Ideographs Extension F": range(183984, 191471 + 1), - "CJK Compatibility Ideographs Supplement": range(194560, 195103 + 1), - "Tags": range(917504, 917631 + 1), - "Variation Selectors Supplement": range(917760, 917999 + 1), -} # type: Dict[str, range] - -UNICODE_SECONDARY_RANGE_KEYWORD = [ - "Supplement", - "Extended", - "Extensions", - "Modifier", - "Marks", - "Punctuation", - "Symbols", - "Forms", - "Operators", - "Miscellaneous", - "Drawing", - "Block", - "Shapes", - "Supplemental", - "Tags", -] # type: List[str] - -RE_POSSIBLE_ENCODING_INDICATION = re_compile( - r"(?:(?:encoding)|(?:charset)|(?:coding))(?:[\:= ]{1,10})(?:[\"\']?)([a-zA-Z0-9\-_]+)(?:[\"\']?)", - IGNORECASE, -) - -IANA_SUPPORTED = sorted( - filter( - lambda x: x.endswith("_codec") is False - and x not in {"rot_13", "tactis", "mbcs"}, - list(set(aliases.values())), - ) -) # type: List[str] - -IANA_SUPPORTED_COUNT = len(IANA_SUPPORTED) # type: int - -# pre-computed code page that are similar using the function cp_similarity. -IANA_SUPPORTED_SIMILAR = { - "cp037": ["cp1026", "cp1140", "cp273", "cp500"], - "cp1026": ["cp037", "cp1140", "cp273", "cp500"], - "cp1125": ["cp866"], - "cp1140": ["cp037", "cp1026", "cp273", "cp500"], - "cp1250": ["iso8859_2"], - "cp1251": ["kz1048", "ptcp154"], - "cp1252": ["cp1258", "iso8859_15", "iso8859_9", "latin_1"], - "cp1253": ["iso8859_7"], - "cp1254": ["cp1258", "iso8859_15", "iso8859_9", "latin_1"], - "cp1257": ["iso8859_13"], - "cp1258": ["cp1252", "cp1254", "iso8859_9", "latin_1"], - "cp273": ["cp037", "cp1026", "cp1140", "cp500"], - "cp437": ["cp850", "cp858", "cp860", "cp861", "cp862", "cp863", "cp865"], - "cp500": ["cp037", "cp1026", "cp1140", "cp273"], - "cp850": ["cp437", "cp857", "cp858", "cp865"], - "cp857": ["cp850", "cp858", "cp865"], - "cp858": ["cp437", "cp850", "cp857", "cp865"], - "cp860": ["cp437", "cp861", "cp862", "cp863", "cp865"], - "cp861": ["cp437", "cp860", "cp862", "cp863", "cp865"], - "cp862": ["cp437", "cp860", "cp861", "cp863", "cp865"], - "cp863": ["cp437", "cp860", "cp861", "cp862", "cp865"], - "cp865": ["cp437", "cp850", "cp857", "cp858", "cp860", "cp861", "cp862", "cp863"], - "cp866": ["cp1125"], - "iso8859_10": ["iso8859_14", "iso8859_15", "iso8859_4", "iso8859_9", "latin_1"], - "iso8859_11": ["tis_620"], - "iso8859_13": ["cp1257"], - "iso8859_14": [ - "iso8859_10", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_9", - "latin_1", - ], - "iso8859_15": [ - "cp1252", - "cp1254", - "iso8859_10", - "iso8859_14", - "iso8859_16", - "iso8859_3", - "iso8859_9", - "latin_1", - ], - "iso8859_16": [ - "iso8859_14", - "iso8859_15", - "iso8859_2", - "iso8859_3", - "iso8859_9", - "latin_1", - ], - "iso8859_2": ["cp1250", "iso8859_16", "iso8859_4"], - "iso8859_3": ["iso8859_14", "iso8859_15", "iso8859_16", "iso8859_9", "latin_1"], - "iso8859_4": ["iso8859_10", "iso8859_2", "iso8859_9", "latin_1"], - "iso8859_7": ["cp1253"], - "iso8859_9": [ - "cp1252", - "cp1254", - "cp1258", - "iso8859_10", - "iso8859_14", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_4", - "latin_1", - ], - "kz1048": ["cp1251", "ptcp154"], - "latin_1": [ - "cp1252", - "cp1254", - "cp1258", - "iso8859_10", - "iso8859_14", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_4", - "iso8859_9", - ], - "mac_iceland": ["mac_roman", "mac_turkish"], - "mac_roman": ["mac_iceland", "mac_turkish"], - "mac_turkish": ["mac_iceland", "mac_roman"], - "ptcp154": ["cp1251", "kz1048"], - "tis_620": ["iso8859_11"], -} # type: Dict[str, List[str]] - - -CHARDET_CORRESPONDENCE = { - "iso2022_kr": "ISO-2022-KR", - "iso2022_jp": "ISO-2022-JP", - "euc_kr": "EUC-KR", - "tis_620": "TIS-620", - "utf_32": "UTF-32", - "euc_jp": "EUC-JP", - "koi8_r": "KOI8-R", - "iso8859_1": "ISO-8859-1", - "iso8859_2": "ISO-8859-2", - "iso8859_5": "ISO-8859-5", - "iso8859_6": "ISO-8859-6", - "iso8859_7": "ISO-8859-7", - "iso8859_8": "ISO-8859-8", - "utf_16": "UTF-16", - "cp855": "IBM855", - "mac_cyrillic": "MacCyrillic", - "gb2312": "GB2312", - "gb18030": "GB18030", - "cp932": "CP932", - "cp866": "IBM866", - "utf_8": "utf-8", - "utf_8_sig": "UTF-8-SIG", - "shift_jis": "SHIFT_JIS", - "big5": "Big5", - "cp1250": "windows-1250", - "cp1251": "windows-1251", - "cp1252": "Windows-1252", - "cp1253": "windows-1253", - "cp1255": "windows-1255", - "cp1256": "windows-1256", - "cp1254": "Windows-1254", - "cp949": "CP949", -} # type: Dict[str, str] - - -COMMON_SAFE_ASCII_CHARACTERS = { - "<", - ">", - "=", - ":", - "/", - "&", - ";", - "{", - "}", - "[", - "]", - ",", - "|", - '"', - "-", -} # type: Set[str] - - -KO_NAMES = {"johab", "cp949", "euc_kr"} # type: Set[str] -ZH_NAMES = {"big5", "cp950", "big5hkscs", "hz"} # type: Set[str] - -NOT_PRINTABLE_PATTERN = re_compile(r"[0-9\W\n\r\t]+") diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/legacy.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/legacy.py deleted file mode 100644 index cdebe2b8..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/legacy.py +++ /dev/null @@ -1,95 +0,0 @@ -import warnings -from typing import Dict, Optional, Union - -from .api import from_bytes, from_fp, from_path, normalize -from .constant import CHARDET_CORRESPONDENCE -from .models import CharsetMatch, CharsetMatches - - -def detect(byte_str: bytes) -> Dict[str, Optional[Union[str, float]]]: - """ - chardet legacy method - Detect the encoding of the given byte string. It should be mostly backward-compatible. - Encoding name will match Chardet own writing whenever possible. (Not on encoding name unsupported by it) - This function is deprecated and should be used to migrate your project easily, consult the documentation for - further information. Not planned for removal. - - :param byte_str: The byte sequence to examine. - """ - if not isinstance(byte_str, (bytearray, bytes)): - raise TypeError( # pragma: nocover - "Expected object of type bytes or bytearray, got: " - "{0}".format(type(byte_str)) - ) - - if isinstance(byte_str, bytearray): - byte_str = bytes(byte_str) - - r = from_bytes(byte_str).best() - - encoding = r.encoding if r is not None else None - language = r.language if r is not None and r.language != "Unknown" else "" - confidence = 1.0 - r.chaos if r is not None else None - - # Note: CharsetNormalizer does not return 'UTF-8-SIG' as the sig get stripped in the detection/normalization process - # but chardet does return 'utf-8-sig' and it is a valid codec name. - if r is not None and encoding == "utf_8" and r.bom: - encoding += "_sig" - - return { - "encoding": encoding - if encoding not in CHARDET_CORRESPONDENCE - else CHARDET_CORRESPONDENCE[encoding], - "language": language, - "confidence": confidence, - } - - -class CharsetNormalizerMatch(CharsetMatch): - pass - - -class CharsetNormalizerMatches(CharsetMatches): - @staticmethod - def from_fp(*args, **kwargs): # type: ignore - warnings.warn( # pragma: nocover - "staticmethod from_fp, from_bytes, from_path and normalize are deprecated " - "and scheduled to be removed in 3.0", - DeprecationWarning, - ) - return from_fp(*args, **kwargs) # pragma: nocover - - @staticmethod - def from_bytes(*args, **kwargs): # type: ignore - warnings.warn( # pragma: nocover - "staticmethod from_fp, from_bytes, from_path and normalize are deprecated " - "and scheduled to be removed in 3.0", - DeprecationWarning, - ) - return from_bytes(*args, **kwargs) # pragma: nocover - - @staticmethod - def from_path(*args, **kwargs): # type: ignore - warnings.warn( # pragma: nocover - "staticmethod from_fp, from_bytes, from_path and normalize are deprecated " - "and scheduled to be removed in 3.0", - DeprecationWarning, - ) - return from_path(*args, **kwargs) # pragma: nocover - - @staticmethod - def normalize(*args, **kwargs): # type: ignore - warnings.warn( # pragma: nocover - "staticmethod from_fp, from_bytes, from_path and normalize are deprecated " - "and scheduled to be removed in 3.0", - DeprecationWarning, - ) - return normalize(*args, **kwargs) # pragma: nocover - - -class CharsetDetector(CharsetNormalizerMatches): - pass - - -class CharsetDoctor(CharsetNormalizerMatches): - pass diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/md.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/md.py deleted file mode 100644 index 2146d61d..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/md.py +++ /dev/null @@ -1,540 +0,0 @@ -from functools import lru_cache -from typing import List, Optional - -from .constant import COMMON_SAFE_ASCII_CHARACTERS, UNICODE_SECONDARY_RANGE_KEYWORD -from .utils import ( - is_accentuated, - is_ascii, - is_case_variable, - is_cjk, - is_emoticon, - is_hangul, - is_hiragana, - is_katakana, - is_latin, - is_punctuation, - is_separator, - is_symbol, - is_thai, - remove_accent, - unicode_range, -) - - -class MessDetectorPlugin: - """ - Base abstract class used for mess detection plugins. - All detectors MUST extend and implement given methods. - """ - - def eligible(self, character: str) -> bool: - """ - Determine if given character should be fed in. - """ - raise NotImplementedError # pragma: nocover - - def feed(self, character: str) -> None: - """ - The main routine to be executed upon character. - Insert the logic in witch the text would be considered chaotic. - """ - raise NotImplementedError # pragma: nocover - - def reset(self) -> None: - """ - Permit to reset the plugin to the initial state. - """ - raise NotImplementedError # pragma: nocover - - @property - def ratio(self) -> float: - """ - Compute the chaos ratio based on what your feed() has seen. - Must NOT be lower than 0.; No restriction gt 0. - """ - raise NotImplementedError # pragma: nocover - - -class TooManySymbolOrPunctuationPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._punctuation_count = 0 # type: int - self._symbol_count = 0 # type: int - self._character_count = 0 # type: int - - self._last_printable_char = None # type: Optional[str] - self._frenzy_symbol_in_word = False # type: bool - - def eligible(self, character: str) -> bool: - return character.isprintable() - - def feed(self, character: str) -> None: - self._character_count += 1 - - if ( - character != self._last_printable_char - and character not in COMMON_SAFE_ASCII_CHARACTERS - ): - if is_punctuation(character): - self._punctuation_count += 1 - elif ( - character.isdigit() is False - and is_symbol(character) - and is_emoticon(character) is False - ): - self._symbol_count += 2 - - self._last_printable_char = character - - def reset(self) -> None: - self._punctuation_count = 0 - self._character_count = 0 - self._symbol_count = 0 - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - ratio_of_punctuation = ( - self._punctuation_count + self._symbol_count - ) / self._character_count # type: float - - return ratio_of_punctuation if ratio_of_punctuation >= 0.3 else 0.0 - - -class TooManyAccentuatedPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._character_count = 0 # type: int - self._accentuated_count = 0 # type: int - - def eligible(self, character: str) -> bool: - return character.isalpha() - - def feed(self, character: str) -> None: - self._character_count += 1 - - if is_accentuated(character): - self._accentuated_count += 1 - - def reset(self) -> None: - self._character_count = 0 - self._accentuated_count = 0 - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - ratio_of_accentuation = ( - self._accentuated_count / self._character_count - ) # type: float - return ratio_of_accentuation if ratio_of_accentuation >= 0.35 else 0.0 - - -class UnprintablePlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._unprintable_count = 0 # type: int - self._character_count = 0 # type: int - - def eligible(self, character: str) -> bool: - return True - - def feed(self, character: str) -> None: - if ( - character.isspace() is False # includes \n \t \r \v - and character.isprintable() is False - and character != "\x1A" # Why? Its the ASCII substitute character. - ): - self._unprintable_count += 1 - self._character_count += 1 - - def reset(self) -> None: - self._unprintable_count = 0 - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - return (self._unprintable_count * 8) / self._character_count - - -class SuspiciousDuplicateAccentPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._successive_count = 0 # type: int - self._character_count = 0 # type: int - - self._last_latin_character = None # type: Optional[str] - - def eligible(self, character: str) -> bool: - return character.isalpha() and is_latin(character) - - def feed(self, character: str) -> None: - self._character_count += 1 - if self._last_latin_character is not None: - if is_accentuated(character) and is_accentuated(self._last_latin_character): - if character.isupper() and self._last_latin_character.isupper(): - self._successive_count += 1 - # Worse if its the same char duplicated with different accent. - if remove_accent(character) == remove_accent( - self._last_latin_character - ): - self._successive_count += 1 - self._last_latin_character = character - - def reset(self) -> None: - self._successive_count = 0 - self._character_count = 0 - self._last_latin_character = None - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - return (self._successive_count * 2) / self._character_count - - -class SuspiciousRange(MessDetectorPlugin): - def __init__(self) -> None: - self._suspicious_successive_range_count = 0 # type: int - self._character_count = 0 # type: int - self._last_printable_seen = None # type: Optional[str] - - def eligible(self, character: str) -> bool: - return character.isprintable() - - def feed(self, character: str) -> None: - self._character_count += 1 - - if ( - character.isspace() - or is_punctuation(character) - or character in COMMON_SAFE_ASCII_CHARACTERS - ): - self._last_printable_seen = None - return - - if self._last_printable_seen is None: - self._last_printable_seen = character - return - - unicode_range_a = unicode_range( - self._last_printable_seen - ) # type: Optional[str] - unicode_range_b = unicode_range(character) # type: Optional[str] - - if is_suspiciously_successive_range(unicode_range_a, unicode_range_b): - self._suspicious_successive_range_count += 1 - - self._last_printable_seen = character - - def reset(self) -> None: - self._character_count = 0 - self._suspicious_successive_range_count = 0 - self._last_printable_seen = None - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - ratio_of_suspicious_range_usage = ( - self._suspicious_successive_range_count * 2 - ) / self._character_count # type: float - - if ratio_of_suspicious_range_usage < 0.1: - return 0.0 - - return ratio_of_suspicious_range_usage - - -class SuperWeirdWordPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._word_count = 0 # type: int - self._bad_word_count = 0 # type: int - self._is_current_word_bad = False # type: bool - self._foreign_long_watch = False # type: bool - - self._character_count = 0 # type: int - self._bad_character_count = 0 # type: int - - self._buffer = "" # type: str - self._buffer_accent_count = 0 # type: int - - def eligible(self, character: str) -> bool: - return True - - def feed(self, character: str) -> None: - if character.isalpha(): - self._buffer = "".join([self._buffer, character]) - if is_accentuated(character): - self._buffer_accent_count += 1 - if ( - self._foreign_long_watch is False - and is_latin(character) is False - and is_cjk(character) is False - and is_hangul(character) is False - and is_katakana(character) is False - and is_hiragana(character) is False - and is_thai(character) is False - ): - self._foreign_long_watch = True - return - if not self._buffer: - return - if ( - character.isspace() or is_punctuation(character) or is_separator(character) - ) and self._buffer: - self._word_count += 1 - buffer_length = len(self._buffer) # type: int - - self._character_count += buffer_length - - if buffer_length >= 4 and self._buffer_accent_count / buffer_length > 0.34: - self._is_current_word_bad = True - if buffer_length >= 24 and self._foreign_long_watch: - self._is_current_word_bad = True - - if self._is_current_word_bad: - self._bad_word_count += 1 - self._bad_character_count += len(self._buffer) - self._is_current_word_bad = False - - self._foreign_long_watch = False - self._buffer = "" - self._buffer_accent_count = 0 - elif ( - character not in {"<", ">", "-", "="} - and character.isdigit() is False - and is_symbol(character) - ): - self._is_current_word_bad = True - self._buffer += character - - def reset(self) -> None: - self._buffer = "" - self._is_current_word_bad = False - self._foreign_long_watch = False - self._bad_word_count = 0 - self._word_count = 0 - self._character_count = 0 - self._bad_character_count = 0 - - @property - def ratio(self) -> float: - if self._word_count <= 10: - return 0.0 - - return self._bad_character_count / self._character_count - - -class CjkInvalidStopPlugin(MessDetectorPlugin): - """ - GB(Chinese) based encoding often render the stop incorrectly when the content does not fit and - can be easily detected. Searching for the overuse of '丅' and '丄'. - """ - - def __init__(self) -> None: - self._wrong_stop_count = 0 # type: int - self._cjk_character_count = 0 # type: int - - def eligible(self, character: str) -> bool: - return True - - def feed(self, character: str) -> None: - if character in ["丅", "丄"]: - self._wrong_stop_count += 1 - return - if is_cjk(character): - self._cjk_character_count += 1 - - def reset(self) -> None: - self._wrong_stop_count = 0 - self._cjk_character_count = 0 - - @property - def ratio(self) -> float: - if self._cjk_character_count < 16: - return 0.0 - return self._wrong_stop_count / self._cjk_character_count - - -class ArchaicUpperLowerPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._buf = False # type: bool - - self._character_count_since_last_sep = 0 # type: int - - self._successive_upper_lower_count = 0 # type: int - self._successive_upper_lower_count_final = 0 # type: int - - self._character_count = 0 # type: int - - self._last_alpha_seen = None # type: Optional[str] - self._current_ascii_only = True # type: bool - - def eligible(self, character: str) -> bool: - return True - - def feed(self, character: str) -> None: - is_concerned = character.isalpha() and is_case_variable(character) - chunk_sep = is_concerned is False - - if chunk_sep and self._character_count_since_last_sep > 0: - if ( - self._character_count_since_last_sep <= 64 - and character.isdigit() is False - and self._current_ascii_only is False - ): - self._successive_upper_lower_count_final += ( - self._successive_upper_lower_count - ) - - self._successive_upper_lower_count = 0 - self._character_count_since_last_sep = 0 - self._last_alpha_seen = None - self._buf = False - self._character_count += 1 - self._current_ascii_only = True - - return - - if self._current_ascii_only is True and is_ascii(character) is False: - self._current_ascii_only = False - - if self._last_alpha_seen is not None: - if (character.isupper() and self._last_alpha_seen.islower()) or ( - character.islower() and self._last_alpha_seen.isupper() - ): - if self._buf is True: - self._successive_upper_lower_count += 2 - self._buf = False - else: - self._buf = True - else: - self._buf = False - - self._character_count += 1 - self._character_count_since_last_sep += 1 - self._last_alpha_seen = character - - def reset(self) -> None: - self._character_count = 0 - self._character_count_since_last_sep = 0 - self._successive_upper_lower_count = 0 - self._successive_upper_lower_count_final = 0 - self._last_alpha_seen = None - self._buf = False - self._current_ascii_only = True - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - return self._successive_upper_lower_count_final / self._character_count - - -def is_suspiciously_successive_range( - unicode_range_a: Optional[str], unicode_range_b: Optional[str] -) -> bool: - """ - Determine if two Unicode range seen next to each other can be considered as suspicious. - """ - if unicode_range_a is None or unicode_range_b is None: - return True - - if unicode_range_a == unicode_range_b: - return False - - if "Latin" in unicode_range_a and "Latin" in unicode_range_b: - return False - - if "Emoticons" in unicode_range_a or "Emoticons" in unicode_range_b: - return False - - keywords_range_a, keywords_range_b = unicode_range_a.split( - " " - ), unicode_range_b.split(" ") - - for el in keywords_range_a: - if el in UNICODE_SECONDARY_RANGE_KEYWORD: - continue - if el in keywords_range_b: - return False - - # Japanese Exception - range_a_jp_chars, range_b_jp_chars = ( - unicode_range_a - in ( - "Hiragana", - "Katakana", - ), - unicode_range_b in ("Hiragana", "Katakana"), - ) - if range_a_jp_chars or range_b_jp_chars: - if "CJK" in unicode_range_a or "CJK" in unicode_range_b: - return False - if range_a_jp_chars and range_b_jp_chars: - return False - - if "Hangul" in unicode_range_a or "Hangul" in unicode_range_b: - if "CJK" in unicode_range_a or "CJK" in unicode_range_b: - return False - if unicode_range_a == "Basic Latin" or unicode_range_b == "Basic Latin": - return False - - # Chinese/Japanese use dedicated range for punctuation and/or separators. - if ("CJK" in unicode_range_a or "CJK" in unicode_range_b) or ( - unicode_range_a in ["Katakana", "Hiragana"] - and unicode_range_b in ["Katakana", "Hiragana"] - ): - if "Punctuation" in unicode_range_a or "Punctuation" in unicode_range_b: - return False - if "Forms" in unicode_range_a or "Forms" in unicode_range_b: - return False - - return True - - -@lru_cache(maxsize=2048) -def mess_ratio( - decoded_sequence: str, maximum_threshold: float = 0.2, debug: bool = False -) -> float: - """ - Compute a mess ratio given a decoded bytes sequence. The maximum threshold does stop the computation earlier. - """ - - detectors = [ - md_class() for md_class in MessDetectorPlugin.__subclasses__() - ] # type: List[MessDetectorPlugin] - - length = len(decoded_sequence) # type: int - - mean_mess_ratio = 0.0 # type: float - - if length < 512: - intermediary_mean_mess_ratio_calc = 32 # type: int - elif length <= 1024: - intermediary_mean_mess_ratio_calc = 64 - else: - intermediary_mean_mess_ratio_calc = 128 - - for character, index in zip(decoded_sequence, range(0, length)): - for detector in detectors: - if detector.eligible(character): - detector.feed(character) - - if ( - index > 0 and index % intermediary_mean_mess_ratio_calc == 0 - ) or index == length - 1: - mean_mess_ratio = sum([dt.ratio for dt in detectors]) - - if mean_mess_ratio >= maximum_threshold: - break - - if debug: - for dt in detectors: # pragma: nocover - print(dt.__class__, dt.ratio) - - return round(mean_mess_ratio, 3) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/models.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/models.py deleted file mode 100644 index 68c27b89..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/models.py +++ /dev/null @@ -1,393 +0,0 @@ -import warnings -from collections import Counter -from encodings.aliases import aliases -from hashlib import sha256 -from json import dumps -from re import sub -from typing import Any, Dict, Iterator, List, Optional, Tuple, Union - -from .constant import NOT_PRINTABLE_PATTERN, TOO_BIG_SEQUENCE -from .md import mess_ratio -from .utils import iana_name, is_multi_byte_encoding, unicode_range - - -class CharsetMatch: - def __init__( - self, - payload: bytes, - guessed_encoding: str, - mean_mess_ratio: float, - has_sig_or_bom: bool, - languages: "CoherenceMatches", - decoded_payload: Optional[str] = None, - ): - self._payload = payload # type: bytes - - self._encoding = guessed_encoding # type: str - self._mean_mess_ratio = mean_mess_ratio # type: float - self._languages = languages # type: CoherenceMatches - self._has_sig_or_bom = has_sig_or_bom # type: bool - self._unicode_ranges = None # type: Optional[List[str]] - - self._leaves = [] # type: List[CharsetMatch] - self._mean_coherence_ratio = 0.0 # type: float - - self._output_payload = None # type: Optional[bytes] - self._output_encoding = None # type: Optional[str] - - self._string = decoded_payload # type: Optional[str] - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CharsetMatch): - raise TypeError( - "__eq__ cannot be invoked on {} and {}.".format( - str(other.__class__), str(self.__class__) - ) - ) - return self.encoding == other.encoding and self.fingerprint == other.fingerprint - - def __lt__(self, other: object) -> bool: - """ - Implemented to make sorted available upon CharsetMatches items. - """ - if not isinstance(other, CharsetMatch): - raise ValueError - - chaos_difference = abs(self.chaos - other.chaos) # type: float - coherence_difference = abs(self.coherence - other.coherence) # type: float - - # Bellow 1% difference --> Use Coherence - if chaos_difference < 0.01 and coherence_difference > 0.02: - # When having a tough decision, use the result that decoded as many multi-byte as possible. - if chaos_difference == 0.0 and self.coherence == other.coherence: - return self.multi_byte_usage > other.multi_byte_usage - return self.coherence > other.coherence - - return self.chaos < other.chaos - - @property - def multi_byte_usage(self) -> float: - return 1.0 - len(str(self)) / len(self.raw) - - @property - def chaos_secondary_pass(self) -> float: - """ - Check once again chaos in decoded text, except this time, with full content. - Use with caution, this can be very slow. - Notice: Will be removed in 3.0 - """ - warnings.warn( - "chaos_secondary_pass is deprecated and will be removed in 3.0", - DeprecationWarning, - ) - return mess_ratio(str(self), 1.0) - - @property - def coherence_non_latin(self) -> float: - """ - Coherence ratio on the first non-latin language detected if ANY. - Notice: Will be removed in 3.0 - """ - warnings.warn( - "coherence_non_latin is deprecated and will be removed in 3.0", - DeprecationWarning, - ) - return 0.0 - - @property - def w_counter(self) -> Counter: - """ - Word counter instance on decoded text. - Notice: Will be removed in 3.0 - """ - warnings.warn( - "w_counter is deprecated and will be removed in 3.0", DeprecationWarning - ) - - string_printable_only = sub(NOT_PRINTABLE_PATTERN, " ", str(self).lower()) - - return Counter(string_printable_only.split()) - - def __str__(self) -> str: - # Lazy Str Loading - if self._string is None: - self._string = str(self._payload, self._encoding, "strict") - return self._string - - def __repr__(self) -> str: - return "".format(self.encoding, self.fingerprint) - - def add_submatch(self, other: "CharsetMatch") -> None: - if not isinstance(other, CharsetMatch) or other == self: - raise ValueError( - "Unable to add instance <{}> as a submatch of a CharsetMatch".format( - other.__class__ - ) - ) - - other._string = None # Unload RAM usage; dirty trick. - self._leaves.append(other) - - @property - def encoding(self) -> str: - return self._encoding - - @property - def encoding_aliases(self) -> List[str]: - """ - Encoding name are known by many name, using this could help when searching for IBM855 when it's listed as CP855. - """ - also_known_as = [] # type: List[str] - for u, p in aliases.items(): - if self.encoding == u: - also_known_as.append(p) - elif self.encoding == p: - also_known_as.append(u) - return also_known_as - - @property - def bom(self) -> bool: - return self._has_sig_or_bom - - @property - def byte_order_mark(self) -> bool: - return self._has_sig_or_bom - - @property - def languages(self) -> List[str]: - """ - Return the complete list of possible languages found in decoded sequence. - Usually not really useful. Returned list may be empty even if 'language' property return something != 'Unknown'. - """ - return [e[0] for e in self._languages] - - @property - def language(self) -> str: - """ - Most probable language found in decoded sequence. If none were detected or inferred, the property will return - "Unknown". - """ - if not self._languages: - # Trying to infer the language based on the given encoding - # Its either English or we should not pronounce ourselves in certain cases. - if "ascii" in self.could_be_from_charset: - return "English" - - # doing it there to avoid circular import - from charset_normalizer.cd import encoding_languages, mb_encoding_languages - - languages = ( - mb_encoding_languages(self.encoding) - if is_multi_byte_encoding(self.encoding) - else encoding_languages(self.encoding) - ) - - if len(languages) == 0 or "Latin Based" in languages: - return "Unknown" - - return languages[0] - - return self._languages[0][0] - - @property - def chaos(self) -> float: - return self._mean_mess_ratio - - @property - def coherence(self) -> float: - if not self._languages: - return 0.0 - return self._languages[0][1] - - @property - def percent_chaos(self) -> float: - return round(self.chaos * 100, ndigits=3) - - @property - def percent_coherence(self) -> float: - return round(self.coherence * 100, ndigits=3) - - @property - def raw(self) -> bytes: - """ - Original untouched bytes. - """ - return self._payload - - @property - def submatch(self) -> List["CharsetMatch"]: - return self._leaves - - @property - def has_submatch(self) -> bool: - return len(self._leaves) > 0 - - @property - def alphabets(self) -> List[str]: - if self._unicode_ranges is not None: - return self._unicode_ranges - # list detected ranges - detected_ranges = [ - unicode_range(char) for char in str(self) - ] # type: List[Optional[str]] - # filter and sort - self._unicode_ranges = sorted(list({r for r in detected_ranges if r})) - return self._unicode_ranges - - @property - def could_be_from_charset(self) -> List[str]: - """ - The complete list of encoding that output the exact SAME str result and therefore could be the originating - encoding. - This list does include the encoding available in property 'encoding'. - """ - return [self._encoding] + [m.encoding for m in self._leaves] - - def first(self) -> "CharsetMatch": - """ - Kept for BC reasons. Will be removed in 3.0. - """ - return self - - def best(self) -> "CharsetMatch": - """ - Kept for BC reasons. Will be removed in 3.0. - """ - return self - - def output(self, encoding: str = "utf_8") -> bytes: - """ - Method to get re-encoded bytes payload using given target encoding. Default to UTF-8. - Any errors will be simply ignored by the encoder NOT replaced. - """ - if self._output_encoding is None or self._output_encoding != encoding: - self._output_encoding = encoding - self._output_payload = str(self).encode(encoding, "replace") - - return self._output_payload # type: ignore - - @property - def fingerprint(self) -> str: - """ - Retrieve the unique SHA256 computed using the transformed (re-encoded) payload. Not the original one. - """ - return sha256(self.output()).hexdigest() - - -class CharsetMatches: - """ - Container with every CharsetMatch items ordered by default from most probable to the less one. - Act like a list(iterable) but does not implements all related methods. - """ - - def __init__(self, results: List[CharsetMatch] = None): - self._results = sorted(results) if results else [] # type: List[CharsetMatch] - - def __iter__(self) -> Iterator[CharsetMatch]: - for result in self._results: - yield result - - def __getitem__(self, item: Union[int, str]) -> CharsetMatch: - """ - Retrieve a single item either by its position or encoding name (alias may be used here). - Raise KeyError upon invalid index or encoding not present in results. - """ - if isinstance(item, int): - return self._results[item] - if isinstance(item, str): - item = iana_name(item, False) - for result in self._results: - if item in result.could_be_from_charset: - return result - raise KeyError - - def __len__(self) -> int: - return len(self._results) - - def __bool__(self) -> bool: - return len(self._results) > 0 - - def append(self, item: CharsetMatch) -> None: - """ - Insert a single match. Will be inserted accordingly to preserve sort. - Can be inserted as a submatch. - """ - if not isinstance(item, CharsetMatch): - raise ValueError( - "Cannot append instance '{}' to CharsetMatches".format( - str(item.__class__) - ) - ) - # We should disable the submatch factoring when the input file is too heavy (conserve RAM usage) - if len(item.raw) <= TOO_BIG_SEQUENCE: - for match in self._results: - if match.fingerprint == item.fingerprint and match.chaos == item.chaos: - match.add_submatch(item) - return - self._results.append(item) - self._results = sorted(self._results) - - def best(self) -> Optional["CharsetMatch"]: - """ - Simply return the first match. Strict equivalent to matches[0]. - """ - if not self._results: - return None - return self._results[0] - - def first(self) -> Optional["CharsetMatch"]: - """ - Redundant method, call the method best(). Kept for BC reasons. - """ - return self.best() - - -CoherenceMatch = Tuple[str, float] -CoherenceMatches = List[CoherenceMatch] - - -class CliDetectionResult: - def __init__( - self, - path: str, - encoding: Optional[str], - encoding_aliases: List[str], - alternative_encodings: List[str], - language: str, - alphabets: List[str], - has_sig_or_bom: bool, - chaos: float, - coherence: float, - unicode_path: Optional[str], - is_preferred: bool, - ): - self.path = path # type: str - self.unicode_path = unicode_path # type: Optional[str] - self.encoding = encoding # type: Optional[str] - self.encoding_aliases = encoding_aliases # type: List[str] - self.alternative_encodings = alternative_encodings # type: List[str] - self.language = language # type: str - self.alphabets = alphabets # type: List[str] - self.has_sig_or_bom = has_sig_or_bom # type: bool - self.chaos = chaos # type: float - self.coherence = coherence # type: float - self.is_preferred = is_preferred # type: bool - - @property - def __dict__(self) -> Dict[str, Any]: # type: ignore - return { - "path": self.path, - "encoding": self.encoding, - "encoding_aliases": self.encoding_aliases, - "alternative_encodings": self.alternative_encodings, - "language": self.language, - "alphabets": self.alphabets, - "has_sig_or_bom": self.has_sig_or_bom, - "chaos": self.chaos, - "coherence": self.coherence, - "unicode_path": self.unicode_path, - "is_preferred": self.is_preferred, - } - - def to_json(self) -> str: - return dumps(self.__dict__, ensure_ascii=True, indent=4) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/py.typed b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/utils.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/utils.py deleted file mode 100644 index b9d12784..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/utils.py +++ /dev/null @@ -1,333 +0,0 @@ -try: - import unicodedata2 as unicodedata -except ImportError: - import unicodedata # type: ignore[no-redef] - -import importlib -from codecs import IncrementalDecoder -from encodings.aliases import aliases -from functools import lru_cache -from re import findall -from typing import List, Optional, Set, Tuple, Union - -from _multibytecodec import MultibyteIncrementalDecoder # type: ignore - -from .constant import ( - ENCODING_MARKS, - IANA_SUPPORTED_SIMILAR, - RE_POSSIBLE_ENCODING_INDICATION, - UNICODE_RANGES_COMBINED, - UNICODE_SECONDARY_RANGE_KEYWORD, - UTF8_MAXIMAL_ALLOCATION, -) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_accentuated(character: str) -> bool: - try: - description = unicodedata.name(character) # type: str - except ValueError: - return False - return ( - "WITH GRAVE" in description - or "WITH ACUTE" in description - or "WITH CEDILLA" in description - or "WITH DIAERESIS" in description - or "WITH CIRCUMFLEX" in description - or "WITH TILDE" in description - ) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def remove_accent(character: str) -> str: - decomposed = unicodedata.decomposition(character) # type: str - if not decomposed: - return character - - codes = decomposed.split(" ") # type: List[str] - - return chr(int(codes[0], 16)) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def unicode_range(character: str) -> Optional[str]: - """ - Retrieve the Unicode range official name from a single character. - """ - character_ord = ord(character) # type: int - - for range_name, ord_range in UNICODE_RANGES_COMBINED.items(): - if character_ord in ord_range: - return range_name - - return None - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_latin(character: str) -> bool: - try: - description = unicodedata.name(character) # type: str - except ValueError: - return False - return "LATIN" in description - - -def is_ascii(character: str) -> bool: - try: - character.encode("ascii") - except UnicodeEncodeError: - return False - return True - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_punctuation(character: str) -> bool: - character_category = unicodedata.category(character) # type: str - - if "P" in character_category: - return True - - character_range = unicode_range(character) # type: Optional[str] - - if character_range is None: - return False - - return "Punctuation" in character_range - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_symbol(character: str) -> bool: - character_category = unicodedata.category(character) # type: str - - if "S" in character_category or "N" in character_category: - return True - - character_range = unicode_range(character) # type: Optional[str] - - if character_range is None: - return False - - return "Forms" in character_range - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_emoticon(character: str) -> bool: - character_range = unicode_range(character) # type: Optional[str] - - if character_range is None: - return False - - return "Emoticons" in character_range - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_separator(character: str) -> bool: - if character.isspace() or character in ["|", "+", ",", ";", "<", ">"]: - return True - - character_category = unicodedata.category(character) # type: str - - return "Z" in character_category - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_case_variable(character: str) -> bool: - return character.islower() != character.isupper() - - -def is_private_use_only(character: str) -> bool: - character_category = unicodedata.category(character) # type: str - - return "Co" == character_category - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_cjk(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: - return False - - return "CJK" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_hiragana(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: - return False - - return "HIRAGANA" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_katakana(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: - return False - - return "KATAKANA" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_hangul(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: - return False - - return "HANGUL" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_thai(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: - return False - - return "THAI" in character_name - - -@lru_cache(maxsize=len(UNICODE_RANGES_COMBINED)) -def is_unicode_range_secondary(range_name: str) -> bool: - for keyword in UNICODE_SECONDARY_RANGE_KEYWORD: - if keyword in range_name: - return True - - return False - - -def any_specified_encoding(sequence: bytes, search_zone: int = 4096) -> Optional[str]: - """ - Extract using ASCII-only decoder any specified encoding in the first n-bytes. - """ - if not isinstance(sequence, bytes): - raise TypeError - - seq_len = len(sequence) # type: int - - results = findall( - RE_POSSIBLE_ENCODING_INDICATION, - sequence[: seq_len if seq_len <= search_zone else search_zone].decode( - "ascii", errors="ignore" - ), - ) # type: List[str] - - if len(results) == 0: - return None - - for specified_encoding in results: - specified_encoding = specified_encoding.lower().replace("-", "_") - - for encoding_alias, encoding_iana in aliases.items(): - if encoding_alias == specified_encoding: - return encoding_iana - if encoding_iana == specified_encoding: - return encoding_iana - - return None - - -@lru_cache(maxsize=128) -def is_multi_byte_encoding(name: str) -> bool: - """ - Verify is a specific encoding is a multi byte one based on it IANA name - """ - return name in { - "utf_8", - "utf_8_sig", - "utf_16", - "utf_16_be", - "utf_16_le", - "utf_32", - "utf_32_le", - "utf_32_be", - "utf_7", - } or issubclass( - importlib.import_module("encodings.{}".format(name)).IncrementalDecoder, # type: ignore - MultibyteIncrementalDecoder, - ) - - -def identify_sig_or_bom(sequence: bytes) -> Tuple[Optional[str], bytes]: - """ - Identify and extract SIG/BOM in given sequence. - """ - - for iana_encoding in ENCODING_MARKS: - marks = ENCODING_MARKS[iana_encoding] # type: Union[bytes, List[bytes]] - - if isinstance(marks, bytes): - marks = [marks] - - for mark in marks: - if sequence.startswith(mark): - return iana_encoding, mark - - return None, b"" - - -def should_strip_sig_or_bom(iana_encoding: str) -> bool: - return iana_encoding not in {"utf_16", "utf_32"} - - -def iana_name(cp_name: str, strict: bool = True) -> str: - cp_name = cp_name.lower().replace("-", "_") - - for encoding_alias, encoding_iana in aliases.items(): - if cp_name == encoding_alias or cp_name == encoding_iana: - return encoding_iana - - if strict: - raise ValueError("Unable to retrieve IANA for '{}'".format(cp_name)) - - return cp_name - - -def range_scan(decoded_sequence: str) -> List[str]: - ranges = set() # type: Set[str] - - for character in decoded_sequence: - character_range = unicode_range(character) # type: Optional[str] - - if character_range is None: - continue - - ranges.add(character_range) - - return list(ranges) - - -def cp_similarity(iana_name_a: str, iana_name_b: str) -> float: - - if is_multi_byte_encoding(iana_name_a) or is_multi_byte_encoding(iana_name_b): - return 0.0 - - decoder_a = importlib.import_module("encodings.{}".format(iana_name_a)).IncrementalDecoder # type: ignore - decoder_b = importlib.import_module("encodings.{}".format(iana_name_b)).IncrementalDecoder # type: ignore - - id_a = decoder_a(errors="ignore") # type: IncrementalDecoder - id_b = decoder_b(errors="ignore") # type: IncrementalDecoder - - character_match_count = 0 # type: int - - for i in range(0, 255): - to_be_decoded = bytes([i]) # type: bytes - if id_a.decode(to_be_decoded) == id_b.decode(to_be_decoded): - character_match_count += 1 - - return character_match_count / 254 - - -def is_cp_similar(iana_name_a: str, iana_name_b: str) -> bool: - """ - Determine if two code page are at least 80% similar. IANA_SUPPORTED_SIMILAR dict was generated using - the function cp_similarity. - """ - return ( - iana_name_a in IANA_SUPPORTED_SIMILAR - and iana_name_b in IANA_SUPPORTED_SIMILAR[iana_name_a] - ) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/version.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/version.py deleted file mode 100644 index 98e53fb3..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/charset_normalizer/version.py +++ /dev/null @@ -1,6 +0,0 @@ -""" -Expose version -""" - -__version__ = "2.0.7" -VERSION = __version__.split(".") diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/distutils-precedence.pth b/IKEA_scraper/.venv/lib/python3.9/site-packages/distutils-precedence.pth deleted file mode 100644 index 6de4198f..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/distutils-precedence.pth +++ /dev/null @@ -1 +0,0 @@ -import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'stdlib') == 'local'; enabled and __import__('_distutils_hack').add_shim(); diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__init__.py deleted file mode 100644 index f3df71bb..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__init__.py +++ /dev/null @@ -1,389 +0,0 @@ -from builtins import range -import traceback -from io import open - -from gevent.threading import Timer -import gevent as gvt -import json as jsn -import bottle as btl -import bottle.ext.websocket as wbs -import re as rgx -import os -import eel.browsers as brw -import pyparsing as pp -import random as rnd -import sys -import pkg_resources as pkg -import socket -import mimetypes - -mimetypes.add_type('application/javascript', '.js') -_eel_js_file = pkg.resource_filename('eel', 'eel.js') -_eel_js = open(_eel_js_file, encoding='utf-8').read() -_websockets = [] -_call_return_values = {} -_call_return_callbacks = {} -_call_number = 0 -_exposed_functions = {} -_js_functions = [] -_mock_queue = [] -_mock_queue_done = set() -_shutdown = None - -# The maximum time (in milliseconds) that Python will try to retrieve a return value for functions executing in JS -# Can be overridden through `eel.init` with the kwarg `js_result_timeout` (default: 10000) -_js_result_timeout = 10000 - -# All start() options must provide a default value and explanation here -_start_args = { - 'mode': 'chrome', # What browser is used - 'host': 'localhost', # Hostname use for Bottle server - 'port': 8000, # Port used for Bottle server (use 0 for auto) - 'block': True, # Whether start() blocks calling thread - 'jinja_templates': None, # Folder for jinja2 templates - 'cmdline_args': ['--disable-http-cache'], # Extra cmdline flags to pass to browser start - 'size': None, # (width, height) of main window - 'position': None, # (left, top) of main window - 'geometry': {}, # Dictionary of size/position for all windows - 'close_callback': None, # Callback for when all windows have closed - 'app_mode': True, # (Chrome specific option) - 'all_interfaces': False, # Allow bottle server to listen for connections on all interfaces - 'disable_cache': True, # Sets the no-store response header when serving assets - 'default_path': 'index.html', # The default file to retrieve for the root URL - 'app': btl.default_app(), # Allows passing in a custom Bottle instance, e.g. with middleware -} - -# == Temporary (suppressable) error message to inform users of breaking API change for v1.0.0 === -_start_args['suppress_error'] = False -api_error_message = ''' ----------------------------------------------------------------------------------- - 'options' argument deprecated in v1.0.0, see https://github.com/ChrisKnott/Eel - To suppress this error, add 'suppress_error=True' to start() call. - This option will be removed in future versions ----------------------------------------------------------------------------------- -''' -# =============================================================================================== - -# Public functions - -def expose(name_or_function=None): - # Deal with '@eel.expose()' - treat as '@eel.expose' - if name_or_function is None: - return expose - - if type(name_or_function) == str: # Called as '@eel.expose("my_name")' - name = name_or_function - - def decorator(function): - _expose(name, function) - return function - return decorator - else: - function = name_or_function - _expose(function.__name__, function) - return function - - -# PyParsing grammar for parsing exposed functions in JavaScript code -# Examples: `eel.expose(w, "func_name")`, `eel.expose(func_name)`, `eel.expose((function (e){}), "func_name")` -EXPOSED_JS_FUNCTIONS = pp.ZeroOrMore( - pp.Suppress( - pp.SkipTo(pp.Literal('eel.expose(')) - + pp.Literal('eel.expose(') - + pp.Optional( - pp.Or([pp.nestedExpr(), pp.Word(pp.printables, excludeChars=',')]) + pp.Literal(',') - ) - ) - + pp.Suppress(pp.Regex(r'["\']?')) - + pp.Word(pp.printables, excludeChars='"\')') - + pp.Suppress(pp.Regex(r'["\']?\s*\)')), -) - - -def init(path, allowed_extensions=['.js', '.html', '.txt', '.htm', - '.xhtml', '.vue'], js_result_timeout=10000): - global root_path, _js_functions, _js_result_timeout - root_path = _get_real_path(path) - - js_functions = set() - for root, _, files in os.walk(root_path): - for name in files: - if not any(name.endswith(ext) for ext in allowed_extensions): - continue - - try: - with open(os.path.join(root, name), encoding='utf-8') as file: - contents = file.read() - expose_calls = set() - matches = EXPOSED_JS_FUNCTIONS.parseString(contents).asList() - for expose_call in matches: - # Verify that function name is valid - msg = "eel.expose() call contains '(' or '='" - assert rgx.findall(r'[\(=]', expose_call) == [], msg - expose_calls.add(expose_call) - js_functions.update(expose_calls) - except UnicodeDecodeError: - pass # Malformed file probably - - _js_functions = list(js_functions) - for js_function in _js_functions: - _mock_js_function(js_function) - - _js_result_timeout = js_result_timeout - - -def start(*start_urls, **kwargs): - _start_args.update(kwargs) - - if 'options' in kwargs: - if _start_args['suppress_error']: - _start_args.update(kwargs['options']) - else: - raise RuntimeError(api_error_message) - - if _start_args['port'] == 0: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.bind(('localhost', 0)) - _start_args['port'] = sock.getsockname()[1] - sock.close() - - if _start_args['jinja_templates'] != None: - from jinja2 import Environment, FileSystemLoader, select_autoescape - templates_path = os.path.join(root_path, _start_args['jinja_templates']) - _start_args['jinja_env'] = Environment(loader=FileSystemLoader(templates_path), - autoescape=select_autoescape(['html', 'xml'])) - - - # Launch the browser to the starting URLs - show(*start_urls) - - def run_lambda(): - if _start_args['all_interfaces'] == True: - HOST = '0.0.0.0' - else: - HOST = _start_args['host'] - - app = _start_args['app'] # type: btl.Bottle - for route_path, route_params in BOTTLE_ROUTES.items(): - route_func, route_kwargs = route_params - btl.route(path=route_path, callback=route_func, **route_kwargs) - - return btl.run( - host=HOST, - port=_start_args['port'], - server=wbs.GeventWebSocketServer, - quiet=True, - app=app) - - # Start the webserver - if _start_args['block']: - run_lambda() - else: - spawn(run_lambda) - - -def show(*start_urls): - brw.open(start_urls, _start_args) - - -def sleep(seconds): - gvt.sleep(seconds) - - -def spawn(function, *args, **kwargs): - return gvt.spawn(function, *args, **kwargs) - -# Bottle Routes - -def _eel(): - start_geometry = {'default': {'size': _start_args['size'], - 'position': _start_args['position']}, - 'pages': _start_args['geometry']} - - page = _eel_js.replace('/** _py_functions **/', - '_py_functions: %s,' % list(_exposed_functions.keys())) - page = page.replace('/** _start_geometry **/', - '_start_geometry: %s,' % _safe_json(start_geometry)) - btl.response.content_type = 'application/javascript' - _set_response_headers(btl.response) - return page - -def _root(): - return _static(_start_args['default_path']) - -def _static(path): - response = None - if 'jinja_env' in _start_args and 'jinja_templates' in _start_args: - template_prefix = _start_args['jinja_templates'] + '/' - if path.startswith(template_prefix): - n = len(template_prefix) - template = _start_args['jinja_env'].get_template(path[n:]) - response = btl.HTTPResponse(template.render()) - - if response is None: - response = btl.static_file(path, root=root_path) - - _set_response_headers(response) - return response - -def _websocket(ws): - global _websockets - - for js_function in _js_functions: - _import_js_function(js_function) - - page = btl.request.query.page - if page not in _mock_queue_done: - for call in _mock_queue: - _repeated_send(ws, _safe_json(call)) - _mock_queue_done.add(page) - - _websockets += [(page, ws)] - - while True: - msg = ws.receive() - if msg is not None: - message = jsn.loads(msg) - spawn(_process_message, message, ws) - else: - _websockets.remove((page, ws)) - break - - _websocket_close(page) - - -BOTTLE_ROUTES = { - "/eel.js": (_eel, dict()), - "/": (_root, dict()), - "/": (_static, dict()), - "/eel": (_websocket, dict(apply=[wbs.websocket])) -} - -# Private functions - -def _safe_json(obj): - return jsn.dumps(obj, default=lambda o: None) - - -def _repeated_send(ws, msg): - for attempt in range(100): - try: - ws.send(msg) - break - except Exception: - sleep(0.001) - - -def _process_message(message, ws): - if 'call' in message: - error_info = {} - try: - return_val = _exposed_functions[message['name']](*message['args']) - status = 'ok' - except Exception as e: - err_traceback = traceback.format_exc() - traceback.print_exc() - return_val = None - status = 'error' - error_info['errorText'] = repr(e) - error_info['errorTraceback'] = err_traceback - _repeated_send(ws, _safe_json({ 'return': message['call'], - 'status': status, - 'value': return_val, - 'error': error_info,})) - elif 'return' in message: - call_id = message['return'] - if call_id in _call_return_callbacks: - callback, error_callback = _call_return_callbacks.pop(call_id) - if message['status'] == 'ok': - callback(message['value']) - elif message['status'] == 'error' and error_callback is not None: - error_callback(message['error'], message['stack']) - else: - _call_return_values[call_id] = message['value'] - - else: - print('Invalid message received: ', message) - - -def _get_real_path(path): - if getattr(sys, 'frozen', False): - return os.path.join(sys._MEIPASS, path) - else: - return os.path.abspath(path) - - -def _mock_js_function(f): - exec('%s = lambda *args: _mock_call("%s", args)' % (f, f), globals()) - - -def _import_js_function(f): - exec('%s = lambda *args: _js_call("%s", args)' % (f, f), globals()) - - -def _call_object(name, args): - global _call_number - _call_number += 1 - call_id = _call_number + rnd.random() - return {'call': call_id, 'name': name, 'args': args} - - -def _mock_call(name, args): - call_object = _call_object(name, args) - global _mock_queue - _mock_queue += [call_object] - return _call_return(call_object) - - -def _js_call(name, args): - call_object = _call_object(name, args) - for _, ws in _websockets: - _repeated_send(ws, _safe_json(call_object)) - return _call_return(call_object) - - -def _call_return(call): - global _js_result_timeout - call_id = call['call'] - - def return_func(callback=None, error_callback=None): - if callback is not None: - _call_return_callbacks[call_id] = (callback, error_callback) - else: - for w in range(_js_result_timeout): - if call_id in _call_return_values: - return _call_return_values.pop(call_id) - sleep(0.001) - return return_func - - -def _expose(name, function): - msg = 'Already exposed function with name "%s"' % name - assert name not in _exposed_functions, msg - _exposed_functions[name] = function - - -def _detect_shutdown(): - if len(_websockets) == 0: - sys.exit() - - -def _websocket_close(page): - global _shutdown - - close_callback = _start_args.get('close_callback') - - if close_callback is not None: - sockets = [p for _, p in _websockets] - close_callback(page, sockets) - else: - if _shutdown: - _shutdown.kill() - - _shutdown = gvt.spawn_later(1.0, _detect_shutdown) - - -def _set_response_headers(response): - if _start_args['disable_cache']: - # https://stackoverflow.com/a/24748094/280852 - response.set_header('Cache-Control', 'no-store') diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__main__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__main__.py deleted file mode 100644 index f5a81601..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__main__.py +++ /dev/null @@ -1,36 +0,0 @@ -import pkg_resources as pkg -import PyInstaller.__main__ as pyi -import os -from argparse import ArgumentParser - -parser = ArgumentParser(description=""" -Eel is a little Python library for making simple Electron-like offline HTML/JS GUI apps, - with full access to Python capabilities and libraries. -""") -parser.add_argument( - "main_script", - type=str, - help="Main python file to run app from" -) -parser.add_argument( - "web_folder", - type=str, - help="Folder including all web files including file as html, css, ico, etc." -) -args, unknown_args = parser.parse_known_args() -main_script = args.main_script -web_folder = args.web_folder - -print("Building executable with main script '%s' and web folder '%s'...\n" % - (main_script, web_folder)) - -eel_js_file = pkg.resource_filename('eel', 'eel.js') -js_file_arg = '%s%seel' % (eel_js_file, os.pathsep) -web_folder_arg = '%s%s%s' % (web_folder, os.pathsep, web_folder) - -needed_args = ['--hidden-import', 'bottle_websocket', - '--add-data', js_file_arg, '--add-data', web_folder_arg] -full_args = [main_script] + needed_args + unknown_args -print('Running:\npyinstaller', ' '.join(full_args), '\n') - -pyi.run(full_args) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 6e482a6094df9a680cb43f0707e81a016842cf72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10179 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/__main__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/__main__.cpython-39.pyc deleted file mode 100644 index 41a59c2f3980d00b9a6a997c7ccb2cee4d99985f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1226 zcmYjQ&2Hm15Ef-wcI2NpStsfC#7nRU5L<_y8WhE*XxdHNErO=K5G@2o9NV-dN+9WE z?dzWR(kDoceU!d}j_s-F8!S+4hjNUp1djO4jAp(W4tcK^dHDSM=UMU)c;4R*eEft6 zUg9!;p%9)Rna4=|_45V`$VS&>P1N~Wn73Gqc+e6J5xgU;4ej}kXhI}H(R$ZFi^FZi zJ6n9;dGLPqS(o*oJMXhT%3Zb#0}(+Vb|?O!_m<#)YwZBG6!X^xHh2Izeg94QE4crnY1 z@{-?3%qRg!4hGdr<#N)AF7gw< z3hb(cDwsi5R?!{YB$FZ&SnI_%^_NPOW=r8J;>Zmhs0(C1($|5xp);Fjr!+O@l*+U? zrC`(X`uwY<%qIJ8jZLRBf%|S4}V%Jh1HxziTh)^VI9b$^qpz1^_}2CDqRRBf)OWj!c9 z&QBF?_Ei^iCuph%scIOj*`PKh3su}HXEIgbB~of#-u=TYNgv;Nbe*0`ep1k{ua+i?^3NMT+`RW)!t{_ym3m)nW= zzr{HS$>UdCi?6l3h4^YUvxSPU(^*ku@#Xgy=WgD32|6C%g1U|IHpH8u`)vGqY@~%z z$#_>y2UbB3Z#x&)UW1p)Eh`(N06+I2}tLO&qkhp^El PeKNp(`$ORO$-e(Tm85#? diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/browsers.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/browsers.cpython-39.pyc deleted file mode 100644 index 2e884621f172e9ee847bf57eb5ee6eb17ba896f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1925 zcmZuyPjB2r6rUN7z21MxwnTwcPzq8hTZ#C<1*!^xnxIyoC{k{gs+HF>$-3ROmzmj= zR?dZTg%d}F5^lY6;3FXM6@2A{_y!y(zc-tNq{7y`_vVf5_wV96w7`MqL|`SmU7QU8h9>eVH)w}S!))j?Nhlk>!w}K3YPYCq}#fy#E(JR zSAmk>vUEj-Dgs|sEfs?gR9khxuc)r-fnQa9wF3T{;(Khk`X{Wjong>K`NUK+-NbsT zb7!iuky;(-T+jojR|Fnn-NUk5AT>LN=SrN2BjIKE06Fk-SaTI_Fzlt@N2&!mRBqA_C9#q7$`H-mq+!qcH|?Dy0lBF_hr9_6 zv>QfrexRFHHFrf-T0@SS77?VMnKsA^W>&!dewfU(DzfB$X4IA`4t28obmFQq*~=$Y zHBBCTvHjV|<|dnKlWZR9@-Uee<7EESO{((4&5sjXIQ_vq%OAs{O|+gSV^ck`+Stwc z(`IcnUKCR`TA1l*A4wglB6kBAwjL6^hkJl!NOvj00TcubpWrtkD2^nG4F(!)uzj)4 zkZ7d(bxNGvdDf?dooC}hxD@9wFg-BS2T9{`W;MCwQ?MkfIB3>i_0~F5lm~C%%#tsR zuW@q){F0{xg>z3o!sdWHV_WRu?W4f4I&l2CFxQ+Yg$n=xiIPKe6xQMxr9a`EeFY%w zu@`)27&e{47NvDrnF9orE}t83wu!R&T$c(J?&1mKMpS3Jj7lOP>!WOIjfW&ik;8XC|_7t&C*^zQ`4f1xUk(nm2bK8Ww70T7f-3Pnd&0uue7$ewTOto!R&NbA|Hf_^a zQBt&aU>0RF*e(wXjGvh<-L^56X=IsUGz3q^5b4vymRX0jB>*~0NO%}rgEn2cY|!_; zAuzv!dvgoAA(nj)gozI79}9s69K4JLZcdumuYni@G57CWLI+JpYFdvDYPOmaKJH>_ z?=f=~iXk`e5))1D%rlB-=@)hBg;tEMCUm>Wedw*9yxfqFIR`nrNItho6U8y0hn--a zFD(jh(U%0)-^UZL#NNc1nBLHrI6O0S8}e|i>Da}1ZmL{cYpz3;cAn_*sq2Pvm3A=j zHkZE(Lr$haJ^_|Ud-n4%fwVm2C9co4#omd4M+vR zQp)LBB9~ION}gWs83}ntQ^HwloF=;rw8mL-hJcifh@0P;lua3MsQ3 zto^WCs#HR;U)lFV9_^QNnW18p$vyQmUZ zCQe%hDl>p*CT@02Jr(q#1?R7C)^jKM|GK^`5)CLREzbs-E`3V%*6L*$xqNbs89<7W`&u z{uN`TKZ<4v=A~Ei1ZX@|k3V=5?WkKYL4Y}ZLF8P*4gpNPX3v?tlo*F3zL1zBX+Ot} zBPwqsd6XdV8hmrD%(5JyckQZ;!}U+HJa;9`vK*k_J}4KY1si2n9Jom%lC5mcX#m*H z;3^JUPD)y?m(7paK(sk18-T6$eel4zUO0Lqjrkax#a*ZLR_L_1V5v|>Xp!cDO7P1w zc@`uS7-3prKqF4-5im}B8kasx2_SGP#k;d_$v30{fefi-B}U)mxt9>yN3@?iBKn=df)Zd zw0g` zaN9xRa9`T9ZjcBWx{1K08eHam_>{yO$+v->15$Iq2sI%vOlHzckWwY8wf|RuT%8P% zQSp6KY>af>(rAWO|0yCQMvZ(InFb4s+5oW+V0Hyp9|G%M8gu?IS>NP-`4PT>F*ES~ z!t3529b8^So6E@bmVbK+Yu(2{Z}t>wCp=|>0I7zr+yKTYCjsd}Dsc7#Bxedq`t)26 z61z`RNZy{28l1E-$qhy*r0U)&UFM86ede;+o%65+!!;{AGTK){+SW+Yo!QmBvZ4i| zdun1?fnKo_2q>4u{ig3Vq3$;lD4jSjo3^pl@u;+Jh9JCEF;4aK7tMBvLyfX%Sr_^ROt!Y&O@RhKf7<(zHco)$0%n~GiXLvU*l zI7ufKb^+zjMaP~V`Vnnj_uMJiV--kYc46Z-*&Jnb9%>EN8>AwG#)Nj|=|S@>s`gGS zBTuM72^<{sJXq)+ubY@IR1$MQ2Y`NmZ12C4H?x-DpHe#z%UFYT{4AIzvr5)9P|2G8 E6W@@O3;+NC diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/edge.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/edge.cpython-39.pyc deleted file mode 100644 index ce831b99ff63ab5fd93edd3f3fd060c93c61e229..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 676 zcmYjP&2AGh5cc2hrV%L>LPFfQMxrqiCn{8dAczVnij+#Idx_k&)7^G=y&5|~f%e9g zcW9413a{WRC&U|YV!Uw3tY$ucD|>!ETa89Tg7*8z(ejKE@-vcMV@O`1yJr|Q(X=M{ zKcqS9InTLfxzJpTuOv@&qEp0F(*+r4zwi{9EAJ0{s@Mjgj+h>1`_)MQLpa33>;y%iqm zRTv?4DF#CCQNbV<8fWkTAq?I6yaQVp=OA7#?8U#LRgLM}?KkhFj-o-IfAF%gdR?32 QczZ5B$pOWW2|h^v0$FFFG5`Po diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/electron.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/__pycache__/electron.cpython-39.pyc deleted file mode 100644 index dfc5298f73f9fdd84851f76863b65f1ca6893195..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 862 zcmYjP%We}f6!mK+)2J1KDvI2(n@pIDN4AqTDBZE; zAKE4V!G>S(mKEX;y5ibtQ18h1<$HbXbBAwjbrHzlU$2Kh2|~Ypa91FQ-oi33K?x*q zjf$1U1r9MOXt1n^1Sw)c1^b4Ij)+7IHW7G$(#{{~M#n|ct7U1mYD(%8x5B{2u-?Nm z2cVW{gqK+0U9^KP>5>W}=oMO$Ydj(^AY<`_EUBdv1}lOn7VJC2a4*1$j*HE5D(b3{ zL#{{0;p_&#NvVsC6>4UKm0G(9;;KnIj!jh6IBHtsqP}XS_ZLR{Yv~A-WBsFkFJ+Fk z+>TvGwYE}?DZ0jTZHF^m8%F{12h#VT{N{5$m7?PLN3O*OUCm|QUyQA4@RFQjj`5r! zyp11X{Q$huhik#$_y+_|55ci$0*ua)er(AVFnWzIk!6$UDqbQ%ugPFl!JuM)T`>rZ z-m;n!7oAtl-m}7+{pT)OU+U|VEIVnGki)4Gvs#*y_0EZ?jQt;F@FvdoK=koJKGQ% z--B?Mbb%hwL_dN!-3U;67kt+>v(r|qQW~Q@o30xWVJc@<@Aw33_*4dFL-!p-KV= - (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) - ), - - _exposed_functions: {}, - - _mock_queue: [], - - _mock_py_functions: function() { - for(let i = 0; i < eel._py_functions.length; i++) { - let name = eel._py_functions[i]; - eel[name] = function() { - let call_object = eel._call_object(name, arguments); - eel._mock_queue.push(call_object); - return eel._call_return(call_object); - } - } - }, - - _import_py_function: function(name) { - let func_name = name; - eel[name] = function() { - let call_object = eel._call_object(func_name, arguments); - eel._websocket.send(eel._toJSON(call_object)); - return eel._call_return(call_object); - } - }, - - _call_number: 0, - - _call_return_callbacks: {}, - - _call_object: function(name, args) { - let arg_array = []; - for(let i = 0; i < args.length; i++){ - arg_array.push(args[i]); - } - - let call_id = (eel._call_number += 1) + Math.random(); - return {'call': call_id, 'name': name, 'args': arg_array}; - }, - - _sleep: function(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); - }, - - _toJSON: function(obj) { - return JSON.stringify(obj, (k, v) => v === undefined ? null : v); - }, - - _call_return: function(call) { - return function(callback = null) { - if(callback != null) { - eel._call_return_callbacks[call.call] = {resolve: callback}; - } else { - return new Promise(function(resolve, reject) { - eel._call_return_callbacks[call.call] = {resolve: resolve, reject: reject}; - }); - } - } - }, - - _position_window: function(page) { - let size = eel._start_geometry['default'].size; - let position = eel._start_geometry['default'].position; - - if(page in eel._start_geometry.pages) { - size = eel._start_geometry.pages[page].size; - position = eel._start_geometry.pages[page].position; - } - - if(size != null){ - window.resizeTo(size[0], size[1]); - } - - if(position != null){ - window.moveTo(position[0], position[1]); - } - }, - - _init: function() { - eel._mock_py_functions(); - - document.addEventListener("DOMContentLoaded", function(event) { - let page = window.location.pathname.substring(1); - eel._position_window(page); - - let websocket_addr = (eel._host + '/eel').replace('http', 'ws'); - websocket_addr += ('?page=' + page); - eel._websocket = new WebSocket(websocket_addr); - - eel._websocket.onopen = function() { - for(let i = 0; i < eel._py_functions.length; i++){ - let py_function = eel._py_functions[i]; - eel._import_py_function(py_function); - } - - while(eel._mock_queue.length > 0) { - let call = eel._mock_queue.shift(); - eel._websocket.send(eel._toJSON(call)); - } - }; - - eel._websocket.onmessage = function (e) { - let message = JSON.parse(e.data); - if(message.hasOwnProperty('call') ) { - // Python making a function call into us - if(message.name in eel._exposed_functions) { - try { - let return_val = eel._exposed_functions[message.name](...message.args); - eel._websocket.send(eel._toJSON({'return': message.call, 'status':'ok', 'value': return_val})); - } catch(err) { - debugger - eel._websocket.send(eel._toJSON( - {'return': message.call, - 'status':'error', - 'error': err.message, - 'stack': err.stack})); - } - } - } else if(message.hasOwnProperty('return')) { - // Python returning a value to us - if(message['return'] in eel._call_return_callbacks) { - if(message['status']==='ok'){ - eel._call_return_callbacks[message['return']].resolve(message.value); - } - else if(message['status']==='error' && eel._call_return_callbacks[message['return']].reject) { - eel._call_return_callbacks[message['return']].reject(message['error']); - } - } - } else { - throw 'Invalid message ' + message; - } - - }; - }); - } -}; - -eel._init(); - -if(typeof require !== 'undefined'){ - // Avoid name collisions when using Electron, so jQuery etc work normally - window.nodeRequire = require; - delete window.require; - delete window.exports; - delete window.module; -} diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/electron.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/electron.py deleted file mode 100644 index 7a443025..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/eel/electron.py +++ /dev/null @@ -1,24 +0,0 @@ -import sys -import os -import subprocess as sps -import whichcraft as wch - -name = 'Electron' - -def run(path, options, start_urls): - cmd = [path] + options['cmdline_args'] - cmd += ['.', ';'.join(start_urls)] - sps.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=sps.PIPE) - - -def find_path(): - if sys.platform in ['win32', 'win64']: - # It doesn't work well passing the .bat file to Popen, so we get the actual .exe - bat_path = wch.which('electron') - return os.path.join(bat_path, r'..\node_modules\electron\dist\electron.exe') - elif sys.platform in ['darwin', 'linux']: - # This should work find... - return wch.which('electron') - else: - return None - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/PKG-INFO b/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/PKG-INFO deleted file mode 100644 index b6f83573..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/PKG-INFO +++ /dev/null @@ -1,110 +0,0 @@ -Metadata-Version: 2.1 -Name: future -Version: 0.18.2 -Summary: Clean single-source support for Python 3 and 2 -Home-page: https://python-future.org -Author: Ed Schofield -Author-email: ed@pythoncharmers.com -License: MIT -Keywords: future past python3 migration futurize backport six 2to3 modernize pasteurize 3to2 -Platform: UNKNOWN -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: License :: OSI Approved -Classifier: License :: OSI Approved :: MIT License -Classifier: Development Status :: 4 - Beta -Classifier: Intended Audience :: Developers -Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.* -License-File: LICENSE.txt - - -future: Easy, safe support for Python 2/3 compatibility -======================================================= - -``future`` is the missing compatibility layer between Python 2 and Python -3. It allows you to use a single, clean Python 3.x-compatible codebase to -support both Python 2 and Python 3 with minimal overhead. - -It is designed to be used as follows:: - - from __future__ import (absolute_import, division, - print_function, unicode_literals) - from builtins import ( - bytes, dict, int, list, object, range, str, - ascii, chr, hex, input, next, oct, open, - pow, round, super, - filter, map, zip) - -followed by predominantly standard, idiomatic Python 3 code that then runs -similarly on Python 2.6/2.7 and Python 3.3+. - -The imports have no effect on Python 3. On Python 2, they shadow the -corresponding builtins, which normally have different semantics on Python 3 -versus 2, to provide their Python 3 semantics. - - -Standard library reorganization -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``future`` supports the standard library reorganization (PEP 3108) through the -following Py3 interfaces: - - >>> # Top-level packages with Py3 names provided on Py2: - >>> import html.parser - >>> import queue - >>> import tkinter.dialog - >>> import xmlrpc.client - >>> # etc. - - >>> # Aliases provided for extensions to existing Py2 module names: - >>> from future.standard_library import install_aliases - >>> install_aliases() - - >>> from collections import Counter, OrderedDict # backported to Py2.6 - >>> from collections import UserDict, UserList, UserString - >>> import urllib.request - >>> from itertools import filterfalse, zip_longest - >>> from subprocess import getoutput, getstatusoutput - - -Automatic conversion --------------------- - -An included script called `futurize -`_ aids in converting -code (from either Python 2 or Python 3) to code compatible with both -platforms. It is similar to ``python-modernize`` but goes further in -providing Python 3 compatibility through the use of the backported types -and builtin functions in ``future``. - - -Documentation -------------- - -See: http://python-future.org - - -Credits -------- - -:Author: Ed Schofield, Jordan M. Adler, et al -:Sponsor: Python Charmers Pty Ltd, Australia, and Python Charmers Pte - Ltd, Singapore. http://pythoncharmers.com -:Others: See docs/credits.rst or http://python-future.org/credits.html - - -Licensing ---------- -Copyright 2013-2019 Python Charmers Pty Ltd, Australia. -The software is distributed under an MIT licence. See LICENSE.txt. - - - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/SOURCES.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/SOURCES.txt deleted file mode 100644 index e6bf4197..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/SOURCES.txt +++ /dev/null @@ -1,390 +0,0 @@ -.travis.yml -LICENSE.txt -MANIFEST.in -README.rst -TESTING.txt -check_rst.sh -futurize.py -pasteurize.py -pytest.ini -setup.cfg -setup.py -docs/Makefile -docs/automatic_conversion.rst -docs/bind_method.rst -docs/bytes_object.rst -docs/changelog.rst -docs/compatible_idioms.rst -docs/conf.py -docs/contents.rst.inc -docs/conversion_limitations.rst -docs/credits.rst -docs/custom_iterators.rst -docs/custom_str_methods.rst -docs/dev_notes.rst -docs/development.rst -docs/dict_object.rst -docs/faq.rst -docs/func_annotations.rst -docs/future-builtins.rst -docs/futureext.py -docs/futurize.rst -docs/futurize_cheatsheet.rst -docs/futurize_overview.rst -docs/hindsight.rst -docs/imports.rst -docs/index.rst -docs/int_object.rst -docs/isinstance.rst -docs/limitations.rst -docs/metaclasses.rst -docs/older_interfaces.rst -docs/open_function.rst -docs/overview.rst -docs/pasteurize.rst -docs/quickstart.rst -docs/reference.rst -docs/roadmap.rst -docs/standard_library_imports.rst -docs/stdlib_incompatibilities.rst -docs/str_object.rst -docs/translation.rst -docs/unicode_literals.rst -docs/upgrading.rst -docs/utilities.rst -docs/what_else.rst -docs/whatsnew.rst -docs/why_python3.rst -docs/3rd-party-py3k-compat-code/astropy_py3compat.py -docs/3rd-party-py3k-compat-code/django_utils_encoding.py -docs/3rd-party-py3k-compat-code/gevent_py3k.py -docs/3rd-party-py3k-compat-code/ipython_py3compat.py -docs/3rd-party-py3k-compat-code/jinja2_compat.py -docs/3rd-party-py3k-compat-code/numpy_py3k.py -docs/3rd-party-py3k-compat-code/pandas_py3k.py -docs/3rd-party-py3k-compat-code/pycrypto_py3compat.py -docs/3rd-party-py3k-compat-code/statsmodels_py3k.py -docs/_static/python-future-icon-32.ico -docs/_static/python-future-icon-white-32.ico -docs/_static/python-future-logo-textless-transparent.png -docs/_static/python-future-logo.png -docs/_static/python-future-logo.tiff -docs/_templates/layout.html -docs/_templates/navbar.html -docs/_templates/sidebarintro.html -docs/_templates/sidebarlogo.html -docs/_templates/sidebartoc.html -docs/_themes/LICENSE -docs/_themes/README -docs/_themes/future/layout.html -docs/_themes/future/relations.html -docs/_themes/future/theme.conf -docs/_themes/future/static/future.css_t -docs/notebooks/Writing Python 2-3 compatible code.ipynb -docs/notebooks/bytes object.ipynb -docs/notebooks/object special methods (next, bool, ...).ipynb -docs/other/auto2to3.py -docs/other/find_pattern.py -docs/other/fix_notebook_html_colour.py -docs/other/lessons.txt -docs/other/todo.txt -docs/other/upload_future_docs.sh -docs/other/useful_links.txt -src/__init__.py -src/_dummy_thread/__init__.py -src/_markupbase/__init__.py -src/_thread/__init__.py -src/builtins/__init__.py -src/copyreg/__init__.py -src/future/__init__.py -src/future.egg-info/PKG-INFO -src/future.egg-info/SOURCES.txt -src/future.egg-info/dependency_links.txt -src/future.egg-info/entry_points.txt -src/future.egg-info/top_level.txt -src/future/backports/__init__.py -src/future/backports/_markupbase.py -src/future/backports/datetime.py -src/future/backports/misc.py -src/future/backports/socket.py -src/future/backports/socketserver.py -src/future/backports/total_ordering.py -src/future/backports/email/__init__.py -src/future/backports/email/_encoded_words.py -src/future/backports/email/_header_value_parser.py -src/future/backports/email/_parseaddr.py -src/future/backports/email/_policybase.py -src/future/backports/email/base64mime.py -src/future/backports/email/charset.py -src/future/backports/email/encoders.py -src/future/backports/email/errors.py -src/future/backports/email/feedparser.py -src/future/backports/email/generator.py -src/future/backports/email/header.py -src/future/backports/email/headerregistry.py -src/future/backports/email/iterators.py -src/future/backports/email/message.py -src/future/backports/email/parser.py -src/future/backports/email/policy.py -src/future/backports/email/quoprimime.py -src/future/backports/email/utils.py -src/future/backports/email/mime/__init__.py -src/future/backports/email/mime/application.py -src/future/backports/email/mime/audio.py -src/future/backports/email/mime/base.py -src/future/backports/email/mime/image.py -src/future/backports/email/mime/message.py -src/future/backports/email/mime/multipart.py -src/future/backports/email/mime/nonmultipart.py -src/future/backports/email/mime/text.py -src/future/backports/html/__init__.py -src/future/backports/html/entities.py -src/future/backports/html/parser.py -src/future/backports/http/__init__.py -src/future/backports/http/client.py -src/future/backports/http/cookiejar.py -src/future/backports/http/cookies.py -src/future/backports/http/server.py -src/future/backports/test/__init__.py -src/future/backports/test/badcert.pem -src/future/backports/test/badkey.pem -src/future/backports/test/dh512.pem -src/future/backports/test/https_svn_python_org_root.pem -src/future/backports/test/keycert.passwd.pem -src/future/backports/test/keycert.pem -src/future/backports/test/keycert2.pem -src/future/backports/test/nokia.pem -src/future/backports/test/nullbytecert.pem -src/future/backports/test/nullcert.pem -src/future/backports/test/pystone.py -src/future/backports/test/sha256.pem -src/future/backports/test/ssl_cert.pem -src/future/backports/test/ssl_key.passwd.pem -src/future/backports/test/ssl_key.pem -src/future/backports/test/ssl_servers.py -src/future/backports/test/support.py -src/future/backports/urllib/__init__.py -src/future/backports/urllib/error.py -src/future/backports/urllib/parse.py -src/future/backports/urllib/request.py -src/future/backports/urllib/response.py -src/future/backports/urllib/robotparser.py -src/future/backports/xmlrpc/__init__.py -src/future/backports/xmlrpc/client.py -src/future/backports/xmlrpc/server.py -src/future/builtins/__init__.py -src/future/builtins/disabled.py -src/future/builtins/iterators.py -src/future/builtins/misc.py -src/future/builtins/new_min_max.py -src/future/builtins/newnext.py -src/future/builtins/newround.py -src/future/builtins/newsuper.py -src/future/moves/__init__.py -src/future/moves/_dummy_thread.py -src/future/moves/_markupbase.py -src/future/moves/_thread.py -src/future/moves/builtins.py -src/future/moves/collections.py -src/future/moves/configparser.py -src/future/moves/copyreg.py -src/future/moves/itertools.py -src/future/moves/pickle.py -src/future/moves/queue.py -src/future/moves/reprlib.py -src/future/moves/socketserver.py -src/future/moves/subprocess.py -src/future/moves/sys.py -src/future/moves/winreg.py -src/future/moves/dbm/__init__.py -src/future/moves/dbm/dumb.py -src/future/moves/dbm/gnu.py -src/future/moves/dbm/ndbm.py -src/future/moves/html/__init__.py -src/future/moves/html/entities.py -src/future/moves/html/parser.py -src/future/moves/http/__init__.py -src/future/moves/http/client.py -src/future/moves/http/cookiejar.py -src/future/moves/http/cookies.py -src/future/moves/http/server.py -src/future/moves/test/__init__.py -src/future/moves/test/support.py -src/future/moves/tkinter/__init__.py -src/future/moves/tkinter/colorchooser.py -src/future/moves/tkinter/commondialog.py -src/future/moves/tkinter/constants.py -src/future/moves/tkinter/dialog.py -src/future/moves/tkinter/dnd.py -src/future/moves/tkinter/filedialog.py -src/future/moves/tkinter/font.py -src/future/moves/tkinter/messagebox.py -src/future/moves/tkinter/scrolledtext.py -src/future/moves/tkinter/simpledialog.py -src/future/moves/tkinter/tix.py -src/future/moves/tkinter/ttk.py -src/future/moves/urllib/__init__.py -src/future/moves/urllib/error.py -src/future/moves/urllib/parse.py -src/future/moves/urllib/request.py -src/future/moves/urllib/response.py -src/future/moves/urllib/robotparser.py -src/future/moves/xmlrpc/__init__.py -src/future/moves/xmlrpc/client.py -src/future/moves/xmlrpc/server.py -src/future/standard_library/__init__.py -src/future/tests/__init__.py -src/future/tests/base.py -src/future/types/__init__.py -src/future/types/newbytes.py -src/future/types/newdict.py -src/future/types/newint.py -src/future/types/newlist.py -src/future/types/newmemoryview.py -src/future/types/newobject.py -src/future/types/newopen.py -src/future/types/newrange.py -src/future/types/newstr.py -src/future/utils/__init__.py -src/future/utils/surrogateescape.py -src/html/__init__.py -src/html/entities.py -src/html/parser.py -src/http/__init__.py -src/http/client.py -src/http/cookiejar.py -src/http/cookies.py -src/http/server.py -src/libfuturize/__init__.py -src/libfuturize/fixer_util.py -src/libfuturize/main.py -src/libfuturize/fixes/__init__.py -src/libfuturize/fixes/fix_UserDict.py -src/libfuturize/fixes/fix_absolute_import.py -src/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py -src/libfuturize/fixes/fix_basestring.py -src/libfuturize/fixes/fix_bytes.py -src/libfuturize/fixes/fix_cmp.py -src/libfuturize/fixes/fix_division.py -src/libfuturize/fixes/fix_division_safe.py -src/libfuturize/fixes/fix_execfile.py -src/libfuturize/fixes/fix_future_builtins.py -src/libfuturize/fixes/fix_future_standard_library.py -src/libfuturize/fixes/fix_future_standard_library_urllib.py -src/libfuturize/fixes/fix_input.py -src/libfuturize/fixes/fix_metaclass.py -src/libfuturize/fixes/fix_next_call.py -src/libfuturize/fixes/fix_object.py -src/libfuturize/fixes/fix_oldstr_wrap.py -src/libfuturize/fixes/fix_order___future__imports.py -src/libfuturize/fixes/fix_print.py -src/libfuturize/fixes/fix_print_with_import.py -src/libfuturize/fixes/fix_raise.py -src/libfuturize/fixes/fix_remove_old__future__imports.py -src/libfuturize/fixes/fix_unicode_keep_u.py -src/libfuturize/fixes/fix_unicode_literals_import.py -src/libfuturize/fixes/fix_xrange_with_import.py -src/libpasteurize/__init__.py -src/libpasteurize/main.py -src/libpasteurize/fixes/__init__.py -src/libpasteurize/fixes/feature_base.py -src/libpasteurize/fixes/fix_add_all__future__imports.py -src/libpasteurize/fixes/fix_add_all_future_builtins.py -src/libpasteurize/fixes/fix_add_future_standard_library_import.py -src/libpasteurize/fixes/fix_annotations.py -src/libpasteurize/fixes/fix_division.py -src/libpasteurize/fixes/fix_features.py -src/libpasteurize/fixes/fix_fullargspec.py -src/libpasteurize/fixes/fix_future_builtins.py -src/libpasteurize/fixes/fix_getcwd.py -src/libpasteurize/fixes/fix_imports.py -src/libpasteurize/fixes/fix_imports2.py -src/libpasteurize/fixes/fix_kwargs.py -src/libpasteurize/fixes/fix_memoryview.py -src/libpasteurize/fixes/fix_metaclass.py -src/libpasteurize/fixes/fix_newstyle.py -src/libpasteurize/fixes/fix_next.py -src/libpasteurize/fixes/fix_printfunction.py -src/libpasteurize/fixes/fix_raise.py -src/libpasteurize/fixes/fix_raise_.py -src/libpasteurize/fixes/fix_throw.py -src/libpasteurize/fixes/fix_unpacking.py -src/past/__init__.py -src/past/builtins/__init__.py -src/past/builtins/misc.py -src/past/builtins/noniterators.py -src/past/translation/__init__.py -src/past/types/__init__.py -src/past/types/basestring.py -src/past/types/olddict.py -src/past/types/oldstr.py -src/past/utils/__init__.py -src/queue/__init__.py -src/reprlib/__init__.py -src/socketserver/__init__.py -src/tkinter/__init__.py -src/tkinter/colorchooser.py -src/tkinter/commondialog.py -src/tkinter/constants.py -src/tkinter/dialog.py -src/tkinter/dnd.py -src/tkinter/filedialog.py -src/tkinter/font.py -src/tkinter/messagebox.py -src/tkinter/scrolledtext.py -src/tkinter/simpledialog.py -src/tkinter/tix.py -src/tkinter/ttk.py -src/winreg/__init__.py -src/xmlrpc/__init__.py -src/xmlrpc/client.py -src/xmlrpc/server.py -tests/test_future/__init__.py -tests/test_future/test_backports.py -tests/test_future/test_buffer.py -tests/test_future/test_builtins.py -tests/test_future/test_builtins_explicit_import.py -tests/test_future/test_bytes.py -tests/test_future/test_chainmap.py -tests/test_future/test_common_iterators.py -tests/test_future/test_decorators.py -tests/test_future/test_dict.py -tests/test_future/test_email_multipart.py -tests/test_future/test_explicit_imports.py -tests/test_future/test_futurize.py -tests/test_future/test_html.py -tests/test_future/test_htmlparser.py -tests/test_future/test_http_cookiejar.py -tests/test_future/test_httplib.py -tests/test_future/test_import_star.py -tests/test_future/test_imports_httplib.py -tests/test_future/test_imports_urllib.py -tests/test_future/test_int.py -tests/test_future/test_int_old_division.py -tests/test_future/test_isinstance.py -tests/test_future/test_libfuturize_fixers.py -tests/test_future/test_list.py -tests/test_future/test_magicsuper.py -tests/test_future/test_object.py -tests/test_future/test_pasteurize.py -tests/test_future/test_py2_str_literals_to_bytes.py -tests/test_future/test_range.py -tests/test_future/test_requests.py -tests/test_future/test_standard_library.py -tests/test_future/test_str.py -tests/test_future/test_super.py -tests/test_future/test_surrogateescape.py -tests/test_future/test_urllib.py -tests/test_future/test_urllib2.py -tests/test_future/test_urllib_response.py -tests/test_future/test_urllib_toplevel.py -tests/test_future/test_urllibnet.py -tests/test_future/test_urlparse.py -tests/test_future/test_utils.py -tests/test_past/__init__.py -tests/test_past/test_basestring.py -tests/test_past/test_builtins.py -tests/test_past/test_noniterators.py -tests/test_past/test_olddict.py -tests/test_past/test_oldstr.py -tests/test_past/test_translation.py \ No newline at end of file diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/dependency_links.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/entry_points.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/entry_points.txt deleted file mode 100644 index 45d1a880..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/entry_points.txt +++ /dev/null @@ -1,4 +0,0 @@ -[console_scripts] -futurize = libfuturize.main:main -pasteurize = libpasteurize.main:main - diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/installed-files.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/installed-files.txt deleted file mode 100644 index c115b1d2..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/installed-files.txt +++ /dev/null @@ -1,413 +0,0 @@ -../../../../bin/futurize -../../../../bin/pasteurize -../future/__init__.py -../future/__pycache__/__init__.cpython-39.pyc -../future/backports/__init__.py -../future/backports/__pycache__/__init__.cpython-39.pyc -../future/backports/__pycache__/_markupbase.cpython-39.pyc -../future/backports/__pycache__/datetime.cpython-39.pyc -../future/backports/__pycache__/misc.cpython-39.pyc -../future/backports/__pycache__/socket.cpython-39.pyc -../future/backports/__pycache__/socketserver.cpython-39.pyc -../future/backports/__pycache__/total_ordering.cpython-39.pyc -../future/backports/_markupbase.py -../future/backports/datetime.py -../future/backports/email/__init__.py -../future/backports/email/__pycache__/__init__.cpython-39.pyc -../future/backports/email/__pycache__/_encoded_words.cpython-39.pyc -../future/backports/email/__pycache__/_header_value_parser.cpython-39.pyc -../future/backports/email/__pycache__/_parseaddr.cpython-39.pyc -../future/backports/email/__pycache__/_policybase.cpython-39.pyc -../future/backports/email/__pycache__/base64mime.cpython-39.pyc -../future/backports/email/__pycache__/charset.cpython-39.pyc -../future/backports/email/__pycache__/encoders.cpython-39.pyc -../future/backports/email/__pycache__/errors.cpython-39.pyc -../future/backports/email/__pycache__/feedparser.cpython-39.pyc -../future/backports/email/__pycache__/generator.cpython-39.pyc -../future/backports/email/__pycache__/header.cpython-39.pyc -../future/backports/email/__pycache__/headerregistry.cpython-39.pyc -../future/backports/email/__pycache__/iterators.cpython-39.pyc -../future/backports/email/__pycache__/message.cpython-39.pyc -../future/backports/email/__pycache__/parser.cpython-39.pyc -../future/backports/email/__pycache__/policy.cpython-39.pyc -../future/backports/email/__pycache__/quoprimime.cpython-39.pyc -../future/backports/email/__pycache__/utils.cpython-39.pyc -../future/backports/email/_encoded_words.py -../future/backports/email/_header_value_parser.py -../future/backports/email/_parseaddr.py -../future/backports/email/_policybase.py -../future/backports/email/base64mime.py -../future/backports/email/charset.py -../future/backports/email/encoders.py -../future/backports/email/errors.py -../future/backports/email/feedparser.py -../future/backports/email/generator.py -../future/backports/email/header.py -../future/backports/email/headerregistry.py -../future/backports/email/iterators.py -../future/backports/email/message.py -../future/backports/email/mime/__init__.py -../future/backports/email/mime/__pycache__/__init__.cpython-39.pyc -../future/backports/email/mime/__pycache__/application.cpython-39.pyc -../future/backports/email/mime/__pycache__/audio.cpython-39.pyc -../future/backports/email/mime/__pycache__/base.cpython-39.pyc -../future/backports/email/mime/__pycache__/image.cpython-39.pyc -../future/backports/email/mime/__pycache__/message.cpython-39.pyc -../future/backports/email/mime/__pycache__/multipart.cpython-39.pyc -../future/backports/email/mime/__pycache__/nonmultipart.cpython-39.pyc -../future/backports/email/mime/__pycache__/text.cpython-39.pyc -../future/backports/email/mime/application.py -../future/backports/email/mime/audio.py -../future/backports/email/mime/base.py -../future/backports/email/mime/image.py -../future/backports/email/mime/message.py -../future/backports/email/mime/multipart.py -../future/backports/email/mime/nonmultipart.py -../future/backports/email/mime/text.py -../future/backports/email/parser.py -../future/backports/email/policy.py -../future/backports/email/quoprimime.py -../future/backports/email/utils.py -../future/backports/html/__init__.py -../future/backports/html/__pycache__/__init__.cpython-39.pyc -../future/backports/html/__pycache__/entities.cpython-39.pyc -../future/backports/html/__pycache__/parser.cpython-39.pyc -../future/backports/html/entities.py -../future/backports/html/parser.py -../future/backports/http/__init__.py -../future/backports/http/__pycache__/__init__.cpython-39.pyc -../future/backports/http/__pycache__/client.cpython-39.pyc -../future/backports/http/__pycache__/cookiejar.cpython-39.pyc -../future/backports/http/__pycache__/cookies.cpython-39.pyc -../future/backports/http/__pycache__/server.cpython-39.pyc -../future/backports/http/client.py -../future/backports/http/cookiejar.py -../future/backports/http/cookies.py -../future/backports/http/server.py -../future/backports/misc.py -../future/backports/socket.py -../future/backports/socketserver.py -../future/backports/test/__init__.py -../future/backports/test/__pycache__/__init__.cpython-39.pyc -../future/backports/test/__pycache__/pystone.cpython-39.pyc -../future/backports/test/__pycache__/ssl_servers.cpython-39.pyc -../future/backports/test/__pycache__/support.cpython-39.pyc -../future/backports/test/badcert.pem -../future/backports/test/badkey.pem -../future/backports/test/dh512.pem -../future/backports/test/https_svn_python_org_root.pem -../future/backports/test/keycert.passwd.pem -../future/backports/test/keycert.pem -../future/backports/test/keycert2.pem -../future/backports/test/nokia.pem -../future/backports/test/nullbytecert.pem -../future/backports/test/nullcert.pem -../future/backports/test/pystone.py -../future/backports/test/sha256.pem -../future/backports/test/ssl_cert.pem -../future/backports/test/ssl_key.passwd.pem -../future/backports/test/ssl_key.pem -../future/backports/test/ssl_servers.py -../future/backports/test/support.py -../future/backports/total_ordering.py -../future/backports/urllib/__init__.py -../future/backports/urllib/__pycache__/__init__.cpython-39.pyc -../future/backports/urllib/__pycache__/error.cpython-39.pyc -../future/backports/urllib/__pycache__/parse.cpython-39.pyc -../future/backports/urllib/__pycache__/request.cpython-39.pyc -../future/backports/urllib/__pycache__/response.cpython-39.pyc -../future/backports/urllib/__pycache__/robotparser.cpython-39.pyc -../future/backports/urllib/error.py -../future/backports/urllib/parse.py -../future/backports/urllib/request.py -../future/backports/urllib/response.py -../future/backports/urllib/robotparser.py -../future/backports/xmlrpc/__init__.py -../future/backports/xmlrpc/__pycache__/__init__.cpython-39.pyc -../future/backports/xmlrpc/__pycache__/client.cpython-39.pyc -../future/backports/xmlrpc/__pycache__/server.cpython-39.pyc -../future/backports/xmlrpc/client.py -../future/backports/xmlrpc/server.py -../future/builtins/__init__.py -../future/builtins/__pycache__/__init__.cpython-39.pyc -../future/builtins/__pycache__/disabled.cpython-39.pyc -../future/builtins/__pycache__/iterators.cpython-39.pyc -../future/builtins/__pycache__/misc.cpython-39.pyc -../future/builtins/__pycache__/new_min_max.cpython-39.pyc -../future/builtins/__pycache__/newnext.cpython-39.pyc -../future/builtins/__pycache__/newround.cpython-39.pyc -../future/builtins/__pycache__/newsuper.cpython-39.pyc -../future/builtins/disabled.py -../future/builtins/iterators.py -../future/builtins/misc.py -../future/builtins/new_min_max.py -../future/builtins/newnext.py -../future/builtins/newround.py -../future/builtins/newsuper.py -../future/moves/__init__.py -../future/moves/__pycache__/__init__.cpython-39.pyc -../future/moves/__pycache__/_dummy_thread.cpython-39.pyc -../future/moves/__pycache__/_markupbase.cpython-39.pyc -../future/moves/__pycache__/_thread.cpython-39.pyc -../future/moves/__pycache__/builtins.cpython-39.pyc -../future/moves/__pycache__/collections.cpython-39.pyc -../future/moves/__pycache__/configparser.cpython-39.pyc -../future/moves/__pycache__/copyreg.cpython-39.pyc -../future/moves/__pycache__/itertools.cpython-39.pyc -../future/moves/__pycache__/pickle.cpython-39.pyc -../future/moves/__pycache__/queue.cpython-39.pyc -../future/moves/__pycache__/reprlib.cpython-39.pyc -../future/moves/__pycache__/socketserver.cpython-39.pyc -../future/moves/__pycache__/subprocess.cpython-39.pyc -../future/moves/__pycache__/sys.cpython-39.pyc -../future/moves/__pycache__/winreg.cpython-39.pyc -../future/moves/_dummy_thread.py -../future/moves/_markupbase.py -../future/moves/_thread.py -../future/moves/builtins.py -../future/moves/collections.py -../future/moves/configparser.py -../future/moves/copyreg.py -../future/moves/dbm/__init__.py -../future/moves/dbm/__pycache__/__init__.cpython-39.pyc -../future/moves/dbm/__pycache__/dumb.cpython-39.pyc -../future/moves/dbm/__pycache__/gnu.cpython-39.pyc -../future/moves/dbm/__pycache__/ndbm.cpython-39.pyc -../future/moves/dbm/dumb.py -../future/moves/dbm/gnu.py -../future/moves/dbm/ndbm.py -../future/moves/html/__init__.py -../future/moves/html/__pycache__/__init__.cpython-39.pyc -../future/moves/html/__pycache__/entities.cpython-39.pyc -../future/moves/html/__pycache__/parser.cpython-39.pyc -../future/moves/html/entities.py -../future/moves/html/parser.py -../future/moves/http/__init__.py -../future/moves/http/__pycache__/__init__.cpython-39.pyc -../future/moves/http/__pycache__/client.cpython-39.pyc -../future/moves/http/__pycache__/cookiejar.cpython-39.pyc -../future/moves/http/__pycache__/cookies.cpython-39.pyc -../future/moves/http/__pycache__/server.cpython-39.pyc -../future/moves/http/client.py -../future/moves/http/cookiejar.py -../future/moves/http/cookies.py -../future/moves/http/server.py -../future/moves/itertools.py -../future/moves/pickle.py -../future/moves/queue.py -../future/moves/reprlib.py -../future/moves/socketserver.py -../future/moves/subprocess.py -../future/moves/sys.py -../future/moves/test/__init__.py -../future/moves/test/__pycache__/__init__.cpython-39.pyc -../future/moves/test/__pycache__/support.cpython-39.pyc -../future/moves/test/support.py -../future/moves/tkinter/__init__.py -../future/moves/tkinter/__pycache__/__init__.cpython-39.pyc -../future/moves/tkinter/__pycache__/colorchooser.cpython-39.pyc -../future/moves/tkinter/__pycache__/commondialog.cpython-39.pyc -../future/moves/tkinter/__pycache__/constants.cpython-39.pyc -../future/moves/tkinter/__pycache__/dialog.cpython-39.pyc -../future/moves/tkinter/__pycache__/dnd.cpython-39.pyc -../future/moves/tkinter/__pycache__/filedialog.cpython-39.pyc -../future/moves/tkinter/__pycache__/font.cpython-39.pyc -../future/moves/tkinter/__pycache__/messagebox.cpython-39.pyc -../future/moves/tkinter/__pycache__/scrolledtext.cpython-39.pyc -../future/moves/tkinter/__pycache__/simpledialog.cpython-39.pyc -../future/moves/tkinter/__pycache__/tix.cpython-39.pyc -../future/moves/tkinter/__pycache__/ttk.cpython-39.pyc -../future/moves/tkinter/colorchooser.py -../future/moves/tkinter/commondialog.py -../future/moves/tkinter/constants.py -../future/moves/tkinter/dialog.py -../future/moves/tkinter/dnd.py -../future/moves/tkinter/filedialog.py -../future/moves/tkinter/font.py -../future/moves/tkinter/messagebox.py -../future/moves/tkinter/scrolledtext.py -../future/moves/tkinter/simpledialog.py -../future/moves/tkinter/tix.py -../future/moves/tkinter/ttk.py -../future/moves/urllib/__init__.py -../future/moves/urllib/__pycache__/__init__.cpython-39.pyc -../future/moves/urllib/__pycache__/error.cpython-39.pyc -../future/moves/urllib/__pycache__/parse.cpython-39.pyc -../future/moves/urllib/__pycache__/request.cpython-39.pyc -../future/moves/urllib/__pycache__/response.cpython-39.pyc -../future/moves/urllib/__pycache__/robotparser.cpython-39.pyc -../future/moves/urllib/error.py -../future/moves/urllib/parse.py -../future/moves/urllib/request.py -../future/moves/urllib/response.py -../future/moves/urllib/robotparser.py -../future/moves/winreg.py -../future/moves/xmlrpc/__init__.py -../future/moves/xmlrpc/__pycache__/__init__.cpython-39.pyc -../future/moves/xmlrpc/__pycache__/client.cpython-39.pyc -../future/moves/xmlrpc/__pycache__/server.cpython-39.pyc -../future/moves/xmlrpc/client.py -../future/moves/xmlrpc/server.py -../future/standard_library/__init__.py -../future/standard_library/__pycache__/__init__.cpython-39.pyc -../future/tests/__init__.py -../future/tests/__pycache__/__init__.cpython-39.pyc -../future/tests/__pycache__/base.cpython-39.pyc -../future/tests/base.py -../future/types/__init__.py -../future/types/__pycache__/__init__.cpython-39.pyc -../future/types/__pycache__/newbytes.cpython-39.pyc -../future/types/__pycache__/newdict.cpython-39.pyc -../future/types/__pycache__/newint.cpython-39.pyc -../future/types/__pycache__/newlist.cpython-39.pyc -../future/types/__pycache__/newmemoryview.cpython-39.pyc -../future/types/__pycache__/newobject.cpython-39.pyc -../future/types/__pycache__/newopen.cpython-39.pyc -../future/types/__pycache__/newrange.cpython-39.pyc -../future/types/__pycache__/newstr.cpython-39.pyc -../future/types/newbytes.py -../future/types/newdict.py -../future/types/newint.py -../future/types/newlist.py -../future/types/newmemoryview.py -../future/types/newobject.py -../future/types/newopen.py -../future/types/newrange.py -../future/types/newstr.py -../future/utils/__init__.py -../future/utils/__pycache__/__init__.cpython-39.pyc -../future/utils/__pycache__/surrogateescape.cpython-39.pyc -../future/utils/surrogateescape.py -../libfuturize/__init__.py -../libfuturize/__pycache__/__init__.cpython-39.pyc -../libfuturize/__pycache__/fixer_util.cpython-39.pyc -../libfuturize/__pycache__/main.cpython-39.pyc -../libfuturize/fixer_util.py -../libfuturize/fixes/__init__.py -../libfuturize/fixes/__pycache__/__init__.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_UserDict.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_absolute_import.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_add__future__imports_except_unicode_literals.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_basestring.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_bytes.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_cmp.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_division.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_division_safe.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_execfile.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_future_builtins.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_future_standard_library.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_future_standard_library_urllib.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_input.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_metaclass.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_next_call.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_object.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_oldstr_wrap.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_order___future__imports.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_print.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_print_with_import.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_raise.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_remove_old__future__imports.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_unicode_keep_u.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_unicode_literals_import.cpython-39.pyc -../libfuturize/fixes/__pycache__/fix_xrange_with_import.cpython-39.pyc -../libfuturize/fixes/fix_UserDict.py -../libfuturize/fixes/fix_absolute_import.py -../libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py -../libfuturize/fixes/fix_basestring.py -../libfuturize/fixes/fix_bytes.py -../libfuturize/fixes/fix_cmp.py -../libfuturize/fixes/fix_division.py -../libfuturize/fixes/fix_division_safe.py -../libfuturize/fixes/fix_execfile.py -../libfuturize/fixes/fix_future_builtins.py -../libfuturize/fixes/fix_future_standard_library.py -../libfuturize/fixes/fix_future_standard_library_urllib.py -../libfuturize/fixes/fix_input.py -../libfuturize/fixes/fix_metaclass.py -../libfuturize/fixes/fix_next_call.py -../libfuturize/fixes/fix_object.py -../libfuturize/fixes/fix_oldstr_wrap.py -../libfuturize/fixes/fix_order___future__imports.py -../libfuturize/fixes/fix_print.py -../libfuturize/fixes/fix_print_with_import.py -../libfuturize/fixes/fix_raise.py -../libfuturize/fixes/fix_remove_old__future__imports.py -../libfuturize/fixes/fix_unicode_keep_u.py -../libfuturize/fixes/fix_unicode_literals_import.py -../libfuturize/fixes/fix_xrange_with_import.py -../libfuturize/main.py -../libpasteurize/__init__.py -../libpasteurize/__pycache__/__init__.cpython-39.pyc -../libpasteurize/__pycache__/main.cpython-39.pyc -../libpasteurize/fixes/__init__.py -../libpasteurize/fixes/__pycache__/__init__.cpython-39.pyc -../libpasteurize/fixes/__pycache__/feature_base.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_add_all__future__imports.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_add_all_future_builtins.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_add_future_standard_library_import.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_annotations.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_division.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_features.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_fullargspec.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_future_builtins.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_getcwd.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_imports.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_imports2.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_kwargs.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_memoryview.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_metaclass.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_newstyle.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_next.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_printfunction.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_raise.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_raise_.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_throw.cpython-39.pyc -../libpasteurize/fixes/__pycache__/fix_unpacking.cpython-39.pyc -../libpasteurize/fixes/feature_base.py -../libpasteurize/fixes/fix_add_all__future__imports.py -../libpasteurize/fixes/fix_add_all_future_builtins.py -../libpasteurize/fixes/fix_add_future_standard_library_import.py -../libpasteurize/fixes/fix_annotations.py -../libpasteurize/fixes/fix_division.py -../libpasteurize/fixes/fix_features.py -../libpasteurize/fixes/fix_fullargspec.py -../libpasteurize/fixes/fix_future_builtins.py -../libpasteurize/fixes/fix_getcwd.py -../libpasteurize/fixes/fix_imports.py -../libpasteurize/fixes/fix_imports2.py -../libpasteurize/fixes/fix_kwargs.py -../libpasteurize/fixes/fix_memoryview.py -../libpasteurize/fixes/fix_metaclass.py -../libpasteurize/fixes/fix_newstyle.py -../libpasteurize/fixes/fix_next.py -../libpasteurize/fixes/fix_printfunction.py -../libpasteurize/fixes/fix_raise.py -../libpasteurize/fixes/fix_raise_.py -../libpasteurize/fixes/fix_throw.py -../libpasteurize/fixes/fix_unpacking.py -../libpasteurize/main.py -../past/__init__.py -../past/__pycache__/__init__.cpython-39.pyc -../past/builtins/__init__.py -../past/builtins/__pycache__/__init__.cpython-39.pyc -../past/builtins/__pycache__/misc.cpython-39.pyc -../past/builtins/__pycache__/noniterators.cpython-39.pyc -../past/builtins/misc.py -../past/builtins/noniterators.py -../past/translation/__init__.py -../past/translation/__pycache__/__init__.cpython-39.pyc -../past/types/__init__.py -../past/types/__pycache__/__init__.cpython-39.pyc -../past/types/__pycache__/basestring.cpython-39.pyc -../past/types/__pycache__/olddict.cpython-39.pyc -../past/types/__pycache__/oldstr.cpython-39.pyc -../past/types/basestring.py -../past/types/olddict.py -../past/types/oldstr.py -../past/utils/__init__.py -../past/utils/__pycache__/__init__.cpython-39.pyc -PKG-INFO -SOURCES.txt -dependency_links.txt -entry_points.txt -top_level.txt diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/top_level.txt b/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/top_level.txt deleted file mode 100644 index 58f5843c..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future-0.18.2-py3.9.egg-info/top_level.txt +++ /dev/null @@ -1,4 +0,0 @@ -future -libfuturize -libpasteurize -past diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/__init__.py deleted file mode 100644 index ad419d67..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/__init__.py +++ /dev/null @@ -1,93 +0,0 @@ -""" -future: Easy, safe support for Python 2/3 compatibility -======================================================= - -``future`` is the missing compatibility layer between Python 2 and Python -3. It allows you to use a single, clean Python 3.x-compatible codebase to -support both Python 2 and Python 3 with minimal overhead. - -It is designed to be used as follows:: - - from __future__ import (absolute_import, division, - print_function, unicode_literals) - from builtins import ( - bytes, dict, int, list, object, range, str, - ascii, chr, hex, input, next, oct, open, - pow, round, super, - filter, map, zip) - -followed by predominantly standard, idiomatic Python 3 code that then runs -similarly on Python 2.6/2.7 and Python 3.3+. - -The imports have no effect on Python 3. On Python 2, they shadow the -corresponding builtins, which normally have different semantics on Python 3 -versus 2, to provide their Python 3 semantics. - - -Standard library reorganization -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``future`` supports the standard library reorganization (PEP 3108) through the -following Py3 interfaces: - - >>> # Top-level packages with Py3 names provided on Py2: - >>> import html.parser - >>> import queue - >>> import tkinter.dialog - >>> import xmlrpc.client - >>> # etc. - - >>> # Aliases provided for extensions to existing Py2 module names: - >>> from future.standard_library import install_aliases - >>> install_aliases() - - >>> from collections import Counter, OrderedDict # backported to Py2.6 - >>> from collections import UserDict, UserList, UserString - >>> import urllib.request - >>> from itertools import filterfalse, zip_longest - >>> from subprocess import getoutput, getstatusoutput - - -Automatic conversion --------------------- - -An included script called `futurize -`_ aids in converting -code (from either Python 2 or Python 3) to code compatible with both -platforms. It is similar to ``python-modernize`` but goes further in -providing Python 3 compatibility through the use of the backported types -and builtin functions in ``future``. - - -Documentation -------------- - -See: http://python-future.org - - -Credits -------- - -:Author: Ed Schofield, Jordan M. Adler, et al -:Sponsor: Python Charmers Pty Ltd, Australia, and Python Charmers Pte - Ltd, Singapore. http://pythoncharmers.com -:Others: See docs/credits.rst or http://python-future.org/credits.html - - -Licensing ---------- -Copyright 2013-2019 Python Charmers Pty Ltd, Australia. -The software is distributed under an MIT licence. See LICENSE.txt. - -""" - -__title__ = 'future' -__author__ = 'Ed Schofield' -__license__ = 'MIT' -__copyright__ = 'Copyright 2013-2019 Python Charmers Pty Ltd' -__ver_major__ = 0 -__ver_minor__ = 18 -__ver_patch__ = 2 -__ver_sub__ = '' -__version__ = "%d.%d.%d%s" % (__ver_major__, __ver_minor__, - __ver_patch__, __ver_sub__) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 91e22c868f249217623b0a0700dd083ce80ff2b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3157 zcmbVOOK;pZ5MC#KMQIN0A?E>t05-Cg8ZVG0i`1y$7^vGcF|g@nfkNhr+!Yf^R7q-W z`OyE>cljN$CAP(m*d1@)?+owo zV9?pW%f?tH7+1E^qEDyH)Lk-cEXY)4skEiB(sW+iR26g>^(aw!$*dg7Oxim5^8Xz{ zaB<<6ytp7~$fkmFX^bo;s|!%ZYN2T)?4=OJJPu+7Z*PKLNN1K!7PVNU^OmA44+#S%rYJ+n|^31>d~ddt6UZ`XBnxf(5YZN3<5-e zG`KKwQV8xs7zq~zCuV?P6I{O^1cYO(RZem2^N(Yad4uyF8yS^VR>b~6m$;ltBURCD z%$(_mj#A6Q!dsD8_bpY0bVcIjG{`$6E51gfK&N-elTDOh2%8bN^)Ov9DrQsrQ1GAp6uEJuM;W*yNA(p6ys zBXgNC4O0+%#_#Y+bQpfI#4hTEy+^3;8xYi&#Zbzof(k`qJO(UF=3wIMg|Dt#41uML zt4ntsBuZ;xN>y;j^I0*vbeYN|g+~oiA)dxRm+-DdVabRbDaynwuM~iKrZRMhRTQ95 zQ`w{_JpvJ{;YQ8a@nTt5Rhu;&WB<IALWG~8>=K|THgIslsoGy?WcqP(!3 z*N5Sge{}Xe0Cx_$K|)~oU>MHCiWY;phu%_N=9Xq#GpyV&3(AaHP%t;mti+^h)z4X7T(m)gghE3P z1ZMK6vNTa(@VL^=qOu5lt9i;c+kv&sz;b&x6IhL1r?1ebE`TlpBsFg^i+r8Tn>20VO)HpG{^4X zyO54~=EjY1n~9)5fKD6teGB(V%5)B1(7CPYrG@2jg-Pi8uDd)imcH}i;@Xb9#Q@+j zbdm^HLr+@YA&M9DUpv4J0!5C*RbryVClhLeaidplm$^0j&d~`Nyp)L(9FS-ZUT~ty zTFXgl>G1Q%y#t({{fi94W&j#BwwFv}5@M@^j@A-10qrrtK}!eX*&9p&ESexg7yZk# zlhap&)6ia7RP<)s_rdii%O$wE4YTV<{~@sV*zk7Vf96iNaJuPS2M>AJ{693W_Jf<9 zI0kNUd~+v`t+biIWe~?`Jum>*cVoz?!^XY17o!tfQ2Z@gFV5N9=2N_FZY9R^;w^T- zN$MTBx6qpTjT4SI{<)(+MQF|G_lqb88Cmo-_9rj2oQmkYPOT~;M?;lGXWyP4$50Z6 zJx~-*MKO(_hOKt=!e^0z1|G~NVB|rJ;uzad8^>W;-|Rm36Xa+Ei<= 3: - import_top_level_modules() - - -from .misc import (ceil, - OrderedDict, - Counter, - ChainMap, - check_output, - count, - recursive_repr, - _count_elements, - cmp_to_key - ) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index e77f0a2a02feeb380cd025498225e226596ac630..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 654 zcmYk3%Zl4D6o%zX9LI5z$#l`ns@p&tExH;QO3Tc^l+p`KSAueItTT-#l4T@0;BNag zeT6=Xwq4~Fx~kL+Lk0f$eSF0Ef1heLD~LbIi(g+)V?ut1m;aBikvIM^!h;h=c*J87 z$(Y4a(1}P`!c&%dJrX0Cu`D8(+>Lqkl<;gf(&w1{Aij6ZbCJslo5+F{a>}N%WTl+3 znVhq^T(E^aVJC9QmcBQ+A*;#vXT@!AdyVT&Rqr~bt$E&gRei(a;N6f_&R@mGu_u9jM-Ry>+8HG`P9Oy4R-JBWUcjTfo0Mpg@VOHLj>-=f}eiht;VY18`LX zxB}pq2tbD)>k=~|;JS;h3FbkM_{3QBO&;ft$s6^gW^xBWP)IJjZJZ!CM=}o;=O3-)juik{hj})@Rf-7GK`)#n7X1`miwy-eqF}*4^Op^JBu-VFe~wNz*dU M<1{Lhd6Xr80pO0kLjV8( diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/_markupbase.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/_markupbase.cpython-39.pyc deleted file mode 100644 index 50c1abf36397791ba8e8e19326646e6f5a520070..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9481 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/datetime.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/datetime.cpython-39.pyc deleted file mode 100644 index 2e0ecf096c3675623cd4e0a77591e8ec84ba1b49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49360 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/socketserver.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/socketserver.cpython-39.pyc deleted file mode 100644 index 95b554e1eb56719c09360bf104586d30db7d40c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22306 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 JtSr3qe*qs={W<^u diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/total_ordering.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/__pycache__/total_ordering.cpython-39.pyc deleted file mode 100644 index c0d018de955baf1ca1f08bba03585515cbc029fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2287 zcmb7GOK;mo5Z)!1)FZOuB<>^ag@;}Qq@?QvE*dyOiagMwKoCGc4@Lum;;tmxB$wG; zs>Q8R)G*4?Pr})q}AEB%+tt+1Xk0o2O4D-(K zFMeqt^rzlj90qJ2!j!u}C^|$GQzOQQnBgOXn#Y#-3?w8r6LLr}N}JT8_A7Kqs4+mj z#_1pJr(8JCPt=5`&VjS%e(CPHj^ZkaeJ&^yQ95>_)R{6NBc4hp6A^r%`0`aJlFl$6 zk5An8fH7yHRCeF<+E{X9liIYolD24j#bJ7_I&aa=N@~-s6-Q{d zIw$n{N{Z?Bifhpu)%nd|$qXx`aDnD{4qO5BA(|P5A%9Z_y*0-_V`Ub`9M8-G7v^pB z7`@p2!I+VmRgl7%V(}VQ%O}cGc0uOGQ3G}j)hv*f&rOV8zAg}?f9G`nQ5;AK840-v z)FK^$az;@cOP%!uGN%ims)U3=p-@@czCTvJAL2WqtfD#d?6OO(&XRFOBjs;@0&o-$0C|C@A=6@@zfiH6V7Ar+wY!y?aNRE z855p6W$Dz5qoKE~w7c$Co{SXxJPX3(V9ca9%2h6yH-vr0g_7RoLVyY_n+I`_3~A8c z2K|4-M5k{Rzk3k#FoP}BaMwT^e&U&RFMuVwcK zn=z|0CKt=NM!Y}Vj9pbpg<\"'")])]) - File "/home/travis/build/edschofield/python-future/future/tests/test_htmlparser.py", line 93, in _run_check - collector = self.get_collector() - File "/home/travis/build/edschofield/python-future/future/tests/test_htmlparser.py", line 617, in get_collector - return EventCollector(strict=True) - File "/home/travis/build/edschofield/python-future/future/tests/test_htmlparser.py", line 27, in __init__ - html.parser.HTMLParser.__init__(self, *args, **kw) - File "/home/travis/build/edschofield/python-future/future/backports/html/parser.py", line 135, in __init__ - self.reset() - File "/home/travis/build/edschofield/python-future/future/backports/html/parser.py", line 143, in reset - _markupbase.ParserBase.reset(self) - -TypeError: unbound method reset() must be called with ParserBase instance as first argument (got EventCollector instance instead) - -This module is used as a foundation for the html.parser module. It has no -documented public API and should not be used directly. - -""" - -import re - -_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9]*\s*').match -_declstringlit_match = re.compile(r'(\'[^\']*\'|"[^"]*")\s*').match -_commentclose = re.compile(r'--\s*>') -_markedsectionclose = re.compile(r']\s*]\s*>') - -# An analysis of the MS-Word extensions is available at -# http://www.planetpublish.com/xmlarena/xap/Thursday/WordtoXML.pdf - -_msmarkedsectionclose = re.compile(r']\s*>') - -del re - - -class ParserBase(object): - """Parser base class which provides some common support methods used - by the SGML/HTML and XHTML parsers.""" - - def __init__(self): - if self.__class__ is ParserBase: - raise RuntimeError( - "_markupbase.ParserBase must be subclassed") - - def error(self, message): - raise NotImplementedError( - "subclasses of ParserBase must override error()") - - def reset(self): - self.lineno = 1 - self.offset = 0 - - def getpos(self): - """Return current line number and offset.""" - return self.lineno, self.offset - - # Internal -- update line number and offset. This should be - # called for each piece of data exactly once, in order -- in other - # words the concatenation of all the input strings to this - # function should be exactly the entire input. - def updatepos(self, i, j): - if i >= j: - return j - rawdata = self.rawdata - nlines = rawdata.count("\n", i, j) - if nlines: - self.lineno = self.lineno + nlines - pos = rawdata.rindex("\n", i, j) # Should not fail - self.offset = j-(pos+1) - else: - self.offset = self.offset + j-i - return j - - _decl_otherchars = '' - - # Internal -- parse declaration (for use by subclasses). - def parse_declaration(self, i): - # This is some sort of declaration; in "HTML as - # deployed," this should only be the document type - # declaration (""). - # ISO 8879:1986, however, has more complex - # declaration syntax for elements in , including: - # --comment-- - # [marked section] - # name in the following list: ENTITY, DOCTYPE, ELEMENT, - # ATTLIST, NOTATION, SHORTREF, USEMAP, - # LINKTYPE, LINK, IDLINK, USELINK, SYSTEM - rawdata = self.rawdata - j = i + 2 - assert rawdata[i:j] == "": - # the empty comment - return j + 1 - if rawdata[j:j+1] in ("-", ""): - # Start of comment followed by buffer boundary, - # or just a buffer boundary. - return -1 - # A simple, practical version could look like: ((name|stringlit) S*) + '>' - n = len(rawdata) - if rawdata[j:j+2] == '--': #comment - # Locate --.*-- as the body of the comment - return self.parse_comment(i) - elif rawdata[j] == '[': #marked section - # Locate [statusWord [...arbitrary SGML...]] as the body of the marked section - # Where statusWord is one of TEMP, CDATA, IGNORE, INCLUDE, RCDATA - # Note that this is extended by Microsoft Office "Save as Web" function - # to include [if...] and [endif]. - return self.parse_marked_section(i) - else: #all other declaration elements - decltype, j = self._scan_name(j, i) - if j < 0: - return j - if decltype == "doctype": - self._decl_otherchars = '' - while j < n: - c = rawdata[j] - if c == ">": - # end of declaration syntax - data = rawdata[i+2:j] - if decltype == "doctype": - self.handle_decl(data) - else: - # According to the HTML5 specs sections "8.2.4.44 Bogus - # comment state" and "8.2.4.45 Markup declaration open - # state", a comment token should be emitted. - # Calling unknown_decl provides more flexibility though. - self.unknown_decl(data) - return j + 1 - if c in "\"'": - m = _declstringlit_match(rawdata, j) - if not m: - return -1 # incomplete - j = m.end() - elif c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ": - name, j = self._scan_name(j, i) - elif c in self._decl_otherchars: - j = j + 1 - elif c == "[": - # this could be handled in a separate doctype parser - if decltype == "doctype": - j = self._parse_doctype_subset(j + 1, i) - elif decltype in set(["attlist", "linktype", "link", "element"]): - # must tolerate []'d groups in a content model in an element declaration - # also in data attribute specifications of attlist declaration - # also link type declaration subsets in linktype declarations - # also link attribute specification lists in link declarations - self.error("unsupported '[' char in %s declaration" % decltype) - else: - self.error("unexpected '[' char in declaration") - else: - self.error( - "unexpected %r char in declaration" % rawdata[j]) - if j < 0: - return j - return -1 # incomplete - - # Internal -- parse a marked section - # Override this to handle MS-word extension syntax content - def parse_marked_section(self, i, report=1): - rawdata= self.rawdata - assert rawdata[i:i+3] == ' ending - match= _markedsectionclose.search(rawdata, i+3) - elif sectName in set(["if", "else", "endif"]): - # look for MS Office ]> ending - match= _msmarkedsectionclose.search(rawdata, i+3) - else: - self.error('unknown status keyword %r in marked section' % rawdata[i+3:j]) - if not match: - return -1 - if report: - j = match.start(0) - self.unknown_decl(rawdata[i+3: j]) - return match.end(0) - - # Internal -- parse comment, return length or -1 if not terminated - def parse_comment(self, i, report=1): - rawdata = self.rawdata - if rawdata[i:i+4] != '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 O%Jn99p~btBoBs#u`?Ajf diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/_parseaddr.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/_parseaddr.cpython-39.pyc deleted file mode 100644 index 3d70c3a00ef9d46e62e437ef62a40b3874bf9a68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12598 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 B?-c+5 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/charset.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/charset.cpython-39.pyc deleted file mode 100644 index 9912ef3eefa700d9ff516e669783c18a26c15beb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11903 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 X>)%RmqJAOwvNTyeU9OaCrv(?om!Q*fJ z`TakT{<4!-pAeHT(e!WVIO!8kxtGz2*Y~KEeaL>_cX9wZ=m$=&WwlAYU#Db5c>Pz} zZ_L+W-N?epO20|Tw)ziu3+d8w zeVV7R6iJpEp;)G&3we5wYM4P@V*@R-sS!y!DWoz``P!&%PK8Pc`=9Sz>w4vC@~Z!`p+u3fp}-2&rH{4eL~X$L ziz1!~p0fBJQ~a(<4@CT6Ha0Sk_lILCv-tkkySEcPRICsx?j4ByAkNZ3T+GZ^=AZO# z#u_MY7i{<=8wnjhnwqH+@c?s+osPwXrCD4BqjayBm0|Lvn!alfXirLL+fx@Yp25L~ z!ZwG)7K8;kBFE&A8fv@+eMUKb<{f&_`U~%vf-sP|e~a+IVl&TnUA7`K7STj#jrcy& zQNd=JWIRgq=qs1DUb7kDxt(db6*11io`@BNvS|Z@qB}A-A~)L)70dM_p|+h{BrO^X z#)ay+?PZPWVVaipiVH6;y>{Ckh}0ShlPn`qH5?6~3@T8{mVUYR+%Nr!9+iPq>8NbL zf-}Qh;;L>FdsPG7`)Jx;TT%;Yhc>Cxz8=pHVfhkIi>5=;TOvt;qz5E@AQ{XL{tHRR zK)wyca_}${{|~0~_49t!TL^ov>nnRL9AnfwR<9ybpCg|8M1|0p~rM=QK`Ox5D#4SC4x6@yU>n<$1$KA*f({|;}4 z4d*xag>giGE$xs;76S+>8aD(x<|tBUl)F;gIT*^yIIOigk! zuVwP7P-PSDP&J;WX6!QC4b)}Cq~1eUHkQWy*2!jgI-mABEN`Ic56}@Wq#;`1dq{rU z>(Dj@4ZIDs4)h>|wLe1$TYjz55#nPACxkdn!&PW}mvVd+muaXjO6#tSa%sk>4m}5^ zviEYYTdN3bLvlz}ti6h9*L!)u^GC6?+(*+k9DA=3uGQ^zFP4oY;c}QHWiv^tnwuob zhOTVMRF>8KwQ8X^n5LOQrm7@iC(ANeziAEa{{VFfUD>+tzIHBGs%16x>f5C{*2b1+ zS5w>-a*xIJApbLP!`*~sL(RJivgSP#!9=7ni{4bZ} BA$kA+ diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/errors.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/errors.cpython-39.pyc deleted file mode 100644 index fbc4f4b5e7b7a6f4fa84edaa4ece7451c5da9a3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5954 zcmb7I-E!2(6_#dXdki)nFg9Q?v@u|2jXnGvykC3@Uo}_*A2b`d|TWBzGE;0zANql z-!pg)_)~Eo_`bnc#e*%Y_KO21+uRRWhkGyhj%4zsCp)nph0F`M)>1G3kJi3scdJS0 zdy$ZC;Kx$&Kx3>T{9RvTHtr{QQ%6A(OV@9AA{Aq3NGBbsJbFR#fZ3!5RK<<78a9lm zB*{o-t4*$Srm!3DD6E}o6HBbLAMRudT;5P4x#)*VP~$WP zueWGza%O}FW>)=Lav@cL*d?=|Y_H`9vd5-nX6b%(Ch+;Q1v-QWrZfI9N`KV|AdoveU|#xw?QzX^ z;4EQ3E@$vkllCZX{xAsP@ ztSI?Wi19-|Mp$tIKa{mn*U>WAR?~R+*6xnFt_hy5n>v>9sVO|WiBpr?TXw}Bvk#_^ z@L$f~TC+Fa4yy{LsZqL6I#ws?LNDo#XRr5U5agdQQH%^Sb>KyD9m*Jt3#V1tKiHl%wgn944U_2ji`*bu{1fD2mJ)N0>Tdp0uUZp zfY~R$?gZSEVhzDKh&nPACZqn_6}Ip^^j|J*tEjz@Gb2MWE0NbDZC?mvw`d!}W$c?l zsrq4j1(tAq!VI=mqBzamKsZ4@3WBsX?_x!j;P8lHklCdBs$1oj8 z&p6T{o_JA!BCsh@a>0rM9=`bKGR%SZ7X>*84=l%AFV3uoyIlD^jKB4xplC%{iafCA z{`>yO12J^cqy($ko`f79^=3*vD+Lh<4=lnGF%NvvlY~4$hVe7SL%l84T8KItTF=*o zvTFJd;}4k0;%q9D*<6!nk%$=1SW_5aLlmD8d5^HT^sk61;gckPr7^ zEE)UTslgfM91bj4lb(Vkk1>cM%fu(-1@m_)`qZ7*K-N|E z=u!ce&Nd20Ka|bpL8rx=GVVkE2FN}skSBbMd>MmuXogF%9y8TgNtEAI3e}j?9Xk>) zuaN!=ZCfuKinJE8%i+WdR6+D{ZABPRDvn$0%|2wgqd0h z1=%?absesDbhq?kPnjCXR71V0>p8vD6n^%>RFPw^;8HV~QfPt>3#kP>cuM~#cBYV` zkz=YNS-!-nsShZsD(h$`vvn?h{a`Wk98~m_!A;1x)r)LYQ)~E4jPAT|ck~H9`A7OT ziHJ6m@W2FTQZ88zc?!4il@#CdAdv?*SMp3|e7c=bV&=P4=z(*RI!_pdvX8+ng4CZ1 zBnclQS;in8n(E}D7K;Wa95M6;4S;C_hD{rAW+MtWl^@0wo3gO<%ayIw^>x#wWKI$# zvG#>D(UWV>_m?{Pe;A@I_C8E@_;8Dm+vV}rAhpZ1IY?{tQ_zRod%uRwlAS85Xy0d| zoTMfbv#2dV+5N6OJySj6He`0Bv{js%j9%D3Sg+tXKiW`CY2)AlrHbt+2%q`21+SNF=^Yg*Xd+D30 zwUH0DHL8x+sEc$t{LY~x-ok^w>%CO$SU zLY;QQqigw|q1nPRyGM#5xuR&;9euXnNq43cZ9ypN{ktbjXfeAArZrX6-zn;y6m=hp zDtN`{GKWid>Io0XAf+g-nz~98=IL^cE|h{5r7}f9+5|#HfyNk$qGeDdX~DjXrBR~j tR=!BL3V$;Mq(8^Sfh(F{x$309Gfs7=TB()~e@>})KU5ty_b036{{v!lBnkik diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/feedparser.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/feedparser.cpython-39.pyc deleted file mode 100644 index eba67a94a0be0a9088c1443be550538b42f4f226..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10704 zcmZ`~Kjg$<^JK$F^c=Q<1b~H+Eb_zDcy@*o}7M zXJ#pLJ4_JEZh~G6J>=0qmq8K0Xi)_2Q;j|rh9CV>AeaZgG!OMlUfPGkDC&Oye`c0T ziY75<&z$RjKmY%qaVIBj1%LYaPj7wkV@3H7dKv#S@bWyK_`gwjN>lMvPwS{%t*NP0 z*HPD-I@b-;;{El$wwb^NX$Cb8p&P7o%(SasVN6A8Sv z1^2m~&NeR*glja67q+{89J_1&d8ZSEzO%B;zYC6YV?BtSbr%bITW*+ONX*N%eUZ39 z=(xRJCuqA#5QXu1C+@e`9XGa<^(gio;kzDfB0`& z;i=t7_)d^GZD8+3&Yd7x=MCL0kl0=6_%>p}&RU!VSO!w_vsHzuWeE#3vwjHH;H5=e(p3=B);y7lS8V zUy`UF`tSAtfR8cgc0KgE&RNf2MdMi~iNIptY4?SIv=u;O;mbS#V?p7KehiqflN+`f zb%59jK*)$$Qp~a?s^HIpTpqKNLo^UQ>laen)Bxvh$&L@I`xa%fQUKo@n?Q}A=2i7@RX$WTTAk3anco+syTKJoPTZvDj*=Xah(PvYEaWJ;#mZNhu}Ps7Xec;cU;@Rg86)yr;yhlVn?<501+)#VlJ6gfS6ro3zcDpC=7sRgIl~n81n?6l@{nS_P=N zOf5+4Kc@&Wj(J~;#V#1&F23Rl@3Mf#E?(PSPoi+~dV4*J zI*V6-c;$sw+!ikMX>no854RRO!OCK98)iRza^d@nG1O|l2bR%svAEiYE%p~z@J^Z; zFY;|C;hp%~gGk`#Yb*YPlkYN({jhB8!B zbyvli4zhf#E;lq$M^9G5spW?rUXRhMK@!BM%i;tz*2ugQty)s51TV}xssraBNCyYE zzm{R2&AY$ue~n-Ej$pqD`qKb#UY$lssx5Yy=lMe@y2$P$4SC zb(E^8dFpi~(XLC%>AT7fAl)!LFxJyP&`&6rm0PEGv{XygyQ)~AnL|^uo*cUC%H~3f zve1mLzQJFkByl24vg$IV@pbqGmvQoohtR~5!U#9?TuexTG81+wT0O`ii+mmG{}HCf zBxDNEE~$oU=(bT;Ev>3r>iwczO#60GCX%OWqV(0c`l|9C;jIpVXCc;7f^_jH#_cib zV=3C=47HL{k4d)O4~uPG^u$DT1@H|{9%RzqzOrC@QbV!jf&q$HG#*|PAEge(|1*R8|t31tICnbcd+B!lLyVy&3eP0?pH+qCuLYTYo@x_U+( z)DJR?ChTJqfj?3xDMqX?Dk%&xgOx|S$kvfmgOC@5Z$o8C%I%|girV)Jk$3`aJ9wlS z(gr6E1Ba2@r{uIcJuEr|Tv`}Gxxrx;g^mXJe67|mM*AJ2NyVUj+Yt%q;q3?m$cDn! zL-o|#I1LgN8by{tlV>JSehkC1$?HkfyGouWo#Emv+73{FPAoXdII-SGr#!Ks;uci? zel;(KE;pulp4;_Xt;}wx5SA_5((zxiW-Ze?W*L^wW7_E6_ow6Sc|7qu zD8`*m-`Fr$5tzgERN-n$*R!0vo>uO9R&v+V&Rx%m-1V&HuIFU#dQN$DaNvlbqjB&c zd`#Hl`8e6`AO@5XFajQS8W#2mILQxr3KMwZ-=ct5q-}u&urKNfSP=UCff5=C)zx3Y zS4wr1+Tb+3ZNLisEv&>Z6<`7j^uC2Y{VgSYz)%undqst$!iJdWQK;u_CDa~Q67w#- zmDZ>%Czg2sSIV%mt8CbKp9rh@8-8089*vpga>^^AKF$7Fc{wZfB81J`AFJXL;J%OU zB(rqj#zYd2U{!)#N@=`!jtb`Qp2D!#VqNUG-4)NhL}vNF@F;`t?8Pu6&uQUeCqkea zUqVHWzYhe`((8EmcnJ1u>7FgvlduUYGMk%5OyRW5MOOGp4nva+PZzm zgD-kxyXRk_n2N;@K57v5`U!m0ZoI}ms;JN^74l+Ze+@>JWE|u8Ju^rFGou9uIx|}x zH%_2O>o|#h5l3NX8*U3Zc;ZDA&572NNp#`%9apTy49yq;SxZRQKExA0fsl6wh`BCV3E;KDN8 zpetB{jZQYvqCAIWL*Z*&px!;vV0ko1m;VVJdtS!$81&S8AIV6i8R9dj464#(s|plz z7K*8EpO%U#)scu?BVk>-}qhrvF;j|AZA|crt2CDE( zhbj@TAR>N8Ua2_4ewsQocR^g7GRvttEbS`sa#9{zIDHVQZB&MK3Tw5Cq^3N71{bf9 zj3YSiAVNj5FQgn1=b9)=mH{c}j}FTdWtDEflLSzUq%k4+#AgK|4V|F)A&)M_5;fJ= zAYN@m*GS(WzyY(NiCHy>TcdSEl?Fcida>@+aLW||a)56JQHPI3ne53mdmT{XB$Fwe zk;_c5W+mBpKsNX=`xopX-DS<@)6*vH`bN_%bxIx7#+Y+|z(CSXt|+s`LxrS!RSAeG zV9wvGAF3QCXyO8T*bQZNTmU-^j{_dGo8lEJJ@`b!l60%-9d0JO*kBS43@$BJTw{4q z8_{Ud3lQNZgQ=Xji(<9?%!Lc=Nx^4)5kPU=C|*S^tBn(#30@=Z1$A5yxkW9KJ0&E* z-1fQ*w|kT5$I>42~)KRp2GoRU3Ay1~G@VIyOej@frIhok&U# zlayQxkb;THgoGzT?{u~jQNohPzlHzw2*!i)BwsRuBBTq*OAz2Kw{rk^LE7{M9yX5( z!#kxNgmz4N_d!cqUc%F+1sv20Q-SD#<0QlkO^QGgtq->;(cXl{Zq6q9(17Nt@k(Om zwRe)zunf)AfuA+3ptKP|kZw}6H9@1v=xl}%ovM7>oW*XFBY~L`pJP{p$4|44%UwUp zcYTk15!iy?Zhn#)qcz^6orbk!W;mN70^*nj28~uzdU~bsEyGm z8nHnmB{o>$UA+Az&&jE@`Fm0mwAjPrz(8jVz5p~5W@ilMPe<$ihN1p9to0iSH;$$8 zRSc|Rii}r10j$bt*;9ZwVo7lBYgEfmre~Irpu{&x~O8^Nq)x_)lQX1+h`Bd z36#xr!kdOaJe@o;d^E+?)lMy~q5ZWK{%CS0tzjKpUfoqEln)S>)98x|%j5*2a?qvq z9l&ZctT~rKu}|)UT^E0o!>&V9#-Y{YaNlfw^LO`X3~5(AtsIc+JsHu(m!lOwgN*2# z|B2@_%zKQbi{!!(*XVuY?9NO&Lpp|*I$5TT$69B@XVHe-3EFa_(hb_uS50(n#l$@7|@Abyo)Le_-oiv-6PU6#r2TQo*q3D1W42j?E_gqIg)i#zyguQ zyJv1fdp0%ZDDbEGt+T9OllRP+H0b+Jmj|y${k6MaC;pbI8t!Hld^}gx$NzO^iXX!#s7|W{==9yo11?a zqY-Q-(TK2V%*ZHzaI845J)dr*#5TfBH|fV>5=V*na}=M1C0KCS=|ZaRCLV zEHj>23Fv~!cpAT=h0$4QB?xg>Ind_kasBh$wTrReyEJ&JK~Dm_|i|D zo5`*3au4T6Fz}|h8KyTydNXW1cjm@YrejK0&U?qwG7I7s!N+d+qqMChgPk|7WF}{+ zGm{fk(sgrQ4`RA#e1!v|7b9=`x(^4@ML0I*xrsMvNjj=!PJt3d*_8C`ivUE%DUVUl z1bHkycbhJFvWY9NU%T=4^&4+oU4A(;HliRDZ_`4Yuy~njbiNCR3W_DODtSOFQ9txR ztnAcs^kQDW8jk!-30tOPDAHrm9baVg4kZ#3p5>ABv>S#u@lOg>c;epaSiIows@ z&Mt1_q9QZbMAYwP6`8^FBix9@zANBmJx-8WxC1~W-0=nZTgc~Oa5y2MN+PG`X zZ!&O)+1q#bivP9VAK%9Dsu7JO#Glya&>NJkZtYA40TV=-f$3!tsyHVmr z++>hObgMwPBp)-oXavf$z!Ntu;CO3<%pICe{!uo^4)lolh%fyx)4EY+QQSF1^&I-*b7pPZO15peD&=|yV)n0H#%>F%|c!h&r(=wG6vt^P7Rx`KX2#1NR9St;?|nS}5aI!>?`9F4RJ+AWo-s z5w&aIln_nS(2M=?rhQ}RNXygzXl1NRvrK}K_CS2ETfEK#Scm2rBY0?aB=hwGUd%8l zH$eN!y{FVVc9akv=N?P9L60fmPB0R_awJ7LhAvwT>eY%Kz|W&;x05_-tOwcOac>^N zz{!z)u|FkFMoIc5aK%%1G?-C6LBL7r^n2>9`b}-eAcu_7@t%&<8Itkw5-0CX7;17J z$yr7`l)?dm16)G_zLcVm>zYWYZ==lPIO+Z&4jdd8h$410xYOpa;=urm|3S(g&aC*? z#D+UfYr};T|A?KHq?(q8lN65?4hSHk2yCj@C%l*%7=?b%Jg#7sFVR0etjT_9+5z(n zuS+nS8?%72ltXFa8y?hmtkePw#yvHzzY0h0E~S34e`=A_gJaXgA*TaFa=BmRRSx1s zM~Eel5YCeiG^a8!`BqmpB+58-*h6G>GAcp{LBR3kUWczZan&!1b6EEdCXha z8}+Q>hy5<4n|*1#A6+7ilcxJMDCgCfY+exd4@TZ&_M1(2nGCdW?__h*qO{_uFWSC@ zDzkCU;ogC7D*Av}Y*9fLP$Vd_D$T~H7Voa&Qw4d@P#Bm!h_j^E!M8--m2k`NL_J@E zmK(dWC%^m14Em4+kuRs+$be-wnHLF4R-rm9Im~K_EKV4WnY#}Nh(D#`fNfbv+rLAA zn}kAch`>2b|K+>0?&s)iC7JoF;6JaM1q6sO#B zV_H0m9mJneagmB2P(eOHR(@T6ASE;MuT%RqDk#As)AD>Y^LfW7A3;5a$)2gS(ec-K zie$Vt^&nF5CX(@!NXuK+&y}B%!QS_$=Wnrt%6J~0bJvuGx0=Q;k)K%)+VA$Z? zYr$>b*+cidgHLMY_hN3Fz6FZ$ofkf`*q>CV@SlQe+A=1n6Xh=F42K$hiMPw4FgsKF zGA={ob`WIKF+K!})*!s{utgStOb=6tvOo0Uj-Z=Sd}&Z=;j05Tj&ZrMM-w7t6+0Ih z&ia9mZ}`hFT-YAQ9-529vRt+8BP*G&%*%g7aBzB;Xi}j)_}TzQzVQ(Am#$&tCkmKI z*b72k(5U9L>{wXo2gt64v7metU-2=5&1&A?$5$P(u&9$0*0{f+^k_?L(ke%bRf;bl z7WguQ6`iH4`N-iT7Ld53Yl0Ow_Bo#}1Z}ZO#m}f<6`rRW<-f!RicDL6O@lg+A3Z3;+Da=Oj)noMCiomJEC-`E!N-)&B>osZkUF diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/generator.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/generator.cpython-39.pyc deleted file mode 100644 index 5371fce883813c4373c4a71d7272c8e0eaf3a466..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11790 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 XXtVnK50xJ(&ipHeHvbv|u=@W3>RX4P diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/header.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/header.cpython-39.pyc deleted file mode 100644 index d9245aefdf75f0d9b9e5ae1f6b9a1f0007145bd5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17037 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 L7x?$*h2#Gp-ja)3 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/iterators.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/iterators.cpython-39.pyc deleted file mode 100644 index c365a83ef65846c4171eabc7853a932f446e89cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2196 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 QE?3T$%H^5Tq0;#O0S)hNOaK4? diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/parser.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/parser.cpython-39.pyc deleted file mode 100644 index efa5e47026a5d3030ea07d5695a0f927692b72c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6103 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 Q*|eHewws?c-R9c=05?NTKmY&$ diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/policy.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/policy.cpython-39.pyc deleted file mode 100644 index 87d39b0b243a92cf9a01f43baab18909d16ecd31..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8378 zcmb7J&u=8hb?)xz84ia(ms)8hM{(M+g562Dq_eWESP+CPR=XBqYqi)VbQr{F_jJ{e zz3k~8RrM^F#B2Z3wi~&A|Z4TK?Vts!zf9A91LVsjFr?|ov)!Dr|XvM<1C?f!ZazQic*bhZIL`l zbdqNnuSI>Gr{h9~$#9gb0+*V4Jd&#Gm#qi9lCpg#AC98pom*u)o{ly``LvLk2t^+2 zLM7QANnQ*0BOU6oQu$s~NV>H8k&J{?59MBh=Z?BKM^zrV4(*J08_d$%O=l&Y@n-&;Ir{@9>o^X2&;q`pkgl$^Cb_T$N@SnGP z3!*7n&z)XdEQmHf7saCJ;1f0#%lKRpD`FL&%VJHe<8wt^6&vD}=WcKH*N*S>)?m9= z)iS1v9IXCtI|2S4JpAO-oku&L+`s+v4qw=XF>XKD30OO%V;#sW>Zei!eX=PGZ|xf? z8N|?(%!>3V7^&RYBuckf&_g+pz(daFg_6fZJQx*_$UqUYfYxAWJ8MlY;Q~+z_F3`4 zC{h~c38GBk%~bF>-OE){><{%;5bR{Sz@tGvU|AYKNM&G+x@pA#YP?=u1&;@bOa*2pWnEZOFc_!l5ewm!(7tu> z@ZKFA-1@-}-{pP$=$%`)?ACWO@`QvHW3{DaQIN`m*_whB=z|TSe*R?YKcf-!UgSIz z`4K}fk$Q99Or%cQ02Z3&hpat2UzFBnc+XfuN!n1T^RbF0Aq0OnZw86k_>(9d8%K|( z=CQv0vD7-+gN9hOye9MVg>xJcS16c)RyDOQG^>e3%*1vJ)&4J&m%vOhIcspZFCkI| z;GrxB!329U17{TYuJ`kCDgv{+%cL}byb&1DtY>Tlm)}+DTq1)Tgz+@zGY*?Ws9oh7~4p3 z*~6rRsV-&;v}TpUx>O}{ey|JcS$YmYH`i(5*X=8YYr;Vk11(49J<@p)V@mc#R!I>Z z0B6Nv4mR0iMuWFq&w%Lbh;1obtm{% zcqh&oa?_%5;ym^TPU1}5-@5(=n`O&HgmNhi5!Z;(!%)!-il!>t zVVGn|5r$o_^tDU}rFU@nrCTnBVVp)l{}H#503VWJllR0 zBf(6!cYb#FcBo?&A@AGXdLpwY+iB9@9vv0?dG^lMPqsCZ$D1QCHbRu%9*jxP+r;3M zGU{#4KDSL?vo$(WuVQ6C#i1#|a~f@T-95fqOY^p-i;hWU(?1&1aR-N{JZs`ShJxO3 z9=lMEd;CX}auF(timK#bmK1S%#oGp@8OJ1B6064fI73LACMg_1E0EGd7L~~{3~h

!}Uws$8trgDbDm6`x2;eFsBx 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 E7md@BTmS$7 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/utils.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/__pycache__/utils.cpython-39.pyc deleted file mode 100644 index 3cd55a0ea008fa9d633474a753905ac6ff49ec46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10399 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 TH$!>ddTN>WJBak3mX7`}HdFet diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_encoded_words.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_encoded_words.py deleted file mode 100644 index 7c4a5291..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_encoded_words.py +++ /dev/null @@ -1,232 +0,0 @@ -""" Routines for manipulating RFC2047 encoded words. - -This is currently a package-private API, but will be considered for promotion -to a public API if there is demand. - -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import bytes -from future.builtins import chr -from future.builtins import int -from future.builtins import str - -# An ecoded word looks like this: -# -# =?charset[*lang]?cte?encoded_string?= -# -# for more information about charset see the charset module. Here it is one -# of the preferred MIME charset names (hopefully; you never know when parsing). -# cte (Content Transfer Encoding) is either 'q' or 'b' (ignoring case). In -# theory other letters could be used for other encodings, but in practice this -# (almost?) never happens. There could be a public API for adding entries -# to the CTE tables, but YAGNI for now. 'q' is Quoted Printable, 'b' is -# Base64. The meaning of encoded_string should be obvious. 'lang' is optional -# as indicated by the brackets (they are not part of the syntax) but is almost -# never encountered in practice. -# -# The general interface for a CTE decoder is that it takes the encoded_string -# as its argument, and returns a tuple (cte_decoded_string, defects). The -# cte_decoded_string is the original binary that was encoded using the -# specified cte. 'defects' is a list of MessageDefect instances indicating any -# problems encountered during conversion. 'charset' and 'lang' are the -# corresponding strings extracted from the EW, case preserved. -# -# The general interface for a CTE encoder is that it takes a binary sequence -# as input and returns the cte_encoded_string, which is an ascii-only string. -# -# Each decoder must also supply a length function that takes the binary -# sequence as its argument and returns the length of the resulting encoded -# string. -# -# The main API functions for the module are decode, which calls the decoder -# referenced by the cte specifier, and encode, which adds the appropriate -# RFC 2047 "chrome" to the encoded string, and can optionally automatically -# select the shortest possible encoding. See their docstrings below for -# details. - -import re -import base64 -import binascii -import functools -from string import ascii_letters, digits -from future.backports.email import errors - -__all__ = ['decode_q', - 'encode_q', - 'decode_b', - 'encode_b', - 'len_q', - 'len_b', - 'decode', - 'encode', - ] - -# -# Quoted Printable -# - -# regex based decoder. -_q_byte_subber = functools.partial(re.compile(br'=([a-fA-F0-9]{2})').sub, - lambda m: bytes([int(m.group(1), 16)])) - -def decode_q(encoded): - encoded = bytes(encoded.replace(b'_', b' ')) - return _q_byte_subber(encoded), [] - - -# dict mapping bytes to their encoded form -class _QByteMap(dict): - - safe = bytes(b'-!*+/' + ascii_letters.encode('ascii') + digits.encode('ascii')) - - def __missing__(self, key): - if key in self.safe: - self[key] = chr(key) - else: - self[key] = "={:02X}".format(key) - return self[key] - -_q_byte_map = _QByteMap() - -# In headers spaces are mapped to '_'. -_q_byte_map[ord(' ')] = '_' - -def encode_q(bstring): - return str(''.join(_q_byte_map[x] for x in bytes(bstring))) - -def len_q(bstring): - return sum(len(_q_byte_map[x]) for x in bytes(bstring)) - - -# -# Base64 -# - -def decode_b(encoded): - defects = [] - pad_err = len(encoded) % 4 - if pad_err: - defects.append(errors.InvalidBase64PaddingDefect()) - padded_encoded = encoded + b'==='[:4-pad_err] - else: - padded_encoded = encoded - try: - # The validate kwarg to b64decode is not supported in Py2.x - if not re.match(b'^[A-Za-z0-9+/]*={0,2}$', padded_encoded): - raise binascii.Error('Non-base64 digit found') - return base64.b64decode(padded_encoded), defects - except binascii.Error: - # Since we had correct padding, this must an invalid char error. - defects = [errors.InvalidBase64CharactersDefect()] - # The non-alphabet characters are ignored as far as padding - # goes, but we don't know how many there are. So we'll just - # try various padding lengths until something works. - for i in 0, 1, 2, 3: - try: - return base64.b64decode(encoded+b'='*i), defects - except (binascii.Error, TypeError): # Py2 raises a TypeError - if i==0: - defects.append(errors.InvalidBase64PaddingDefect()) - else: - # This should never happen. - raise AssertionError("unexpected binascii.Error") - -def encode_b(bstring): - return base64.b64encode(bstring).decode('ascii') - -def len_b(bstring): - groups_of_3, leftover = divmod(len(bstring), 3) - # 4 bytes out for each 3 bytes (or nonzero fraction thereof) in. - return groups_of_3 * 4 + (4 if leftover else 0) - - -_cte_decoders = { - 'q': decode_q, - 'b': decode_b, - } - -def decode(ew): - """Decode encoded word and return (string, charset, lang, defects) tuple. - - An RFC 2047/2243 encoded word has the form: - - =?charset*lang?cte?encoded_string?= - - where '*lang' may be omitted but the other parts may not be. - - This function expects exactly such a string (that is, it does not check the - syntax and may raise errors if the string is not well formed), and returns - the encoded_string decoded first from its Content Transfer Encoding and - then from the resulting bytes into unicode using the specified charset. If - the cte-decoded string does not successfully decode using the specified - character set, a defect is added to the defects list and the unknown octets - are replaced by the unicode 'unknown' character \uFDFF. - - The specified charset and language are returned. The default for language, - which is rarely if ever encountered, is the empty string. - - """ - _, charset, cte, cte_string, _ = str(ew).split('?') - charset, _, lang = charset.partition('*') - cte = cte.lower() - # Recover the original bytes and do CTE decoding. - bstring = cte_string.encode('ascii', 'surrogateescape') - bstring, defects = _cte_decoders[cte](bstring) - # Turn the CTE decoded bytes into unicode. - try: - string = bstring.decode(charset) - except UnicodeError: - defects.append(errors.UndecodableBytesDefect("Encoded word " - "contains bytes not decodable using {} charset".format(charset))) - string = bstring.decode(charset, 'surrogateescape') - except LookupError: - string = bstring.decode('ascii', 'surrogateescape') - if charset.lower() != 'unknown-8bit': - defects.append(errors.CharsetError("Unknown charset {} " - "in encoded word; decoded as unknown bytes".format(charset))) - return string, charset, lang, defects - - -_cte_encoders = { - 'q': encode_q, - 'b': encode_b, - } - -_cte_encode_length = { - 'q': len_q, - 'b': len_b, - } - -def encode(string, charset='utf-8', encoding=None, lang=''): - """Encode string using the CTE encoding that produces the shorter result. - - Produces an RFC 2047/2243 encoded word of the form: - - =?charset*lang?cte?encoded_string?= - - where '*lang' is omitted unless the 'lang' parameter is given a value. - Optional argument charset (defaults to utf-8) specifies the charset to use - to encode the string to binary before CTE encoding it. Optional argument - 'encoding' is the cte specifier for the encoding that should be used ('q' - or 'b'); if it is None (the default) the encoding which produces the - shortest encoded sequence is used, except that 'q' is preferred if it is up - to five characters longer. Optional argument 'lang' (default '') gives the - RFC 2243 language string to specify in the encoded word. - - """ - string = str(string) - if charset == 'unknown-8bit': - bstring = string.encode('ascii', 'surrogateescape') - else: - bstring = string.encode(charset) - if encoding is None: - qlen = _cte_encode_length['q'](bstring) - blen = _cte_encode_length['b'](bstring) - # Bias toward q. 5 is arbitrary. - encoding = 'q' if qlen - blen < 5 else 'b' - encoded = _cte_encoders[encoding](bstring) - if lang: - lang = '*' + lang - return "=?{0}{1}?{2}?{3}?=".format(charset, lang, encoding, encoded) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_header_value_parser.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_header_value_parser.py deleted file mode 100644 index 43957edc..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_header_value_parser.py +++ /dev/null @@ -1,2965 +0,0 @@ -"""Header value parser implementing various email-related RFC parsing rules. - -The parsing methods defined in this module implement various email related -parsing rules. Principal among them is RFC 5322, which is the followon -to RFC 2822 and primarily a clarification of the former. It also implements -RFC 2047 encoded word decoding. - -RFC 5322 goes to considerable trouble to maintain backward compatibility with -RFC 822 in the parse phase, while cleaning up the structure on the generation -phase. This parser supports correct RFC 5322 generation by tagging white space -as folding white space only when folding is allowed in the non-obsolete rule -sets. Actually, the parser is even more generous when accepting input than RFC -5322 mandates, following the spirit of Postel's Law, which RFC 5322 encourages. -Where possible deviations from the standard are annotated on the 'defects' -attribute of tokens that deviate. - -The general structure of the parser follows RFC 5322, and uses its terminology -where there is a direct correspondence. Where the implementation requires a -somewhat different structure than that used by the formal grammar, new terms -that mimic the closest existing terms are used. Thus, it really helps to have -a copy of RFC 5322 handy when studying this code. - -Input to the parser is a string that has already been unfolded according to -RFC 5322 rules. According to the RFC this unfolding is the very first step, and -this parser leaves the unfolding step to a higher level message parser, which -will have already detected the line breaks that need unfolding while -determining the beginning and end of each header. - -The output of the parser is a TokenList object, which is a list subclass. A -TokenList is a recursive data structure. The terminal nodes of the structure -are Terminal objects, which are subclasses of str. These do not correspond -directly to terminal objects in the formal grammar, but are instead more -practical higher level combinations of true terminals. - -All TokenList and Terminal objects have a 'value' attribute, which produces the -semantically meaningful value of that part of the parse subtree. The value of -all whitespace tokens (no matter how many sub-tokens they may contain) is a -single space, as per the RFC rules. This includes 'CFWS', which is herein -included in the general class of whitespace tokens. There is one exception to -the rule that whitespace tokens are collapsed into single spaces in values: in -the value of a 'bare-quoted-string' (a quoted-string with no leading or -trailing whitespace), any whitespace that appeared between the quotation marks -is preserved in the returned value. Note that in all Terminal strings quoted -pairs are turned into their unquoted values. - -All TokenList and Terminal objects also have a string value, which attempts to -be a "canonical" representation of the RFC-compliant form of the substring that -produced the parsed subtree, including minimal use of quoted pair quoting. -Whitespace runs are not collapsed. - -Comment tokens also have a 'content' attribute providing the string found -between the parens (including any nested comments) with whitespace preserved. - -All TokenList and Terminal objects have a 'defects' attribute which is a -possibly empty list all of the defects found while creating the token. Defects -may appear on any token in the tree, and a composite list of all defects in the -subtree is available through the 'all_defects' attribute of any node. (For -Terminal notes x.defects == x.all_defects.) - -Each object in a parse tree is called a 'token', and each has a 'token_type' -attribute that gives the name from the RFC 5322 grammar that it represents. -Not all RFC 5322 nodes are produced, and there is one non-RFC 5322 node that -may be produced: 'ptext'. A 'ptext' is a string of printable ascii characters. -It is returned in place of lists of (ctext/quoted-pair) and -(qtext/quoted-pair). - -XXX: provide complete list of token types. -""" -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import int, range, str, super, list - -import re -from collections import namedtuple, OrderedDict - -from future.backports.urllib.parse import (unquote, unquote_to_bytes) -from future.backports.email import _encoded_words as _ew -from future.backports.email import errors -from future.backports.email import utils - -# -# Useful constants and functions -# - -WSP = set(' \t') -CFWS_LEADER = WSP | set('(') -SPECIALS = set(r'()<>@,:;.\"[]') -ATOM_ENDS = SPECIALS | WSP -DOT_ATOM_ENDS = ATOM_ENDS - set('.') -# '.', '"', and '(' do not end phrases in order to support obs-phrase -PHRASE_ENDS = SPECIALS - set('."(') -TSPECIALS = (SPECIALS | set('/?=')) - set('.') -TOKEN_ENDS = TSPECIALS | WSP -ASPECIALS = TSPECIALS | set("*'%") -ATTRIBUTE_ENDS = ASPECIALS | WSP -EXTENDED_ATTRIBUTE_ENDS = ATTRIBUTE_ENDS - set('%') - -def quote_string(value): - return '"'+str(value).replace('\\', '\\\\').replace('"', r'\"')+'"' - -# -# Accumulator for header folding -# - -class _Folded(object): - - def __init__(self, maxlen, policy): - self.maxlen = maxlen - self.policy = policy - self.lastlen = 0 - self.stickyspace = None - self.firstline = True - self.done = [] - self.current = list() # uses l.clear() - - def newline(self): - self.done.extend(self.current) - self.done.append(self.policy.linesep) - self.current.clear() - self.lastlen = 0 - - def finalize(self): - if self.current: - self.newline() - - def __str__(self): - return ''.join(self.done) - - def append(self, stoken): - self.current.append(stoken) - - def append_if_fits(self, token, stoken=None): - if stoken is None: - stoken = str(token) - l = len(stoken) - if self.stickyspace is not None: - stickyspace_len = len(self.stickyspace) - if self.lastlen + stickyspace_len + l <= self.maxlen: - self.current.append(self.stickyspace) - self.lastlen += stickyspace_len - self.current.append(stoken) - self.lastlen += l - self.stickyspace = None - self.firstline = False - return True - if token.has_fws: - ws = token.pop_leading_fws() - if ws is not None: - self.stickyspace += str(ws) - stickyspace_len += len(ws) - token._fold(self) - return True - if stickyspace_len and l + 1 <= self.maxlen: - margin = self.maxlen - l - if 0 < margin < stickyspace_len: - trim = stickyspace_len - margin - self.current.append(self.stickyspace[:trim]) - self.stickyspace = self.stickyspace[trim:] - stickyspace_len = trim - self.newline() - self.current.append(self.stickyspace) - self.current.append(stoken) - self.lastlen = l + stickyspace_len - self.stickyspace = None - self.firstline = False - return True - if not self.firstline: - self.newline() - self.current.append(self.stickyspace) - self.current.append(stoken) - self.stickyspace = None - self.firstline = False - return True - if self.lastlen + l <= self.maxlen: - self.current.append(stoken) - self.lastlen += l - return True - if l < self.maxlen: - self.newline() - self.current.append(stoken) - self.lastlen = l - return True - return False - -# -# TokenList and its subclasses -# - -class TokenList(list): - - token_type = None - - def __init__(self, *args, **kw): - super(TokenList, self).__init__(*args, **kw) - self.defects = [] - - def __str__(self): - return ''.join(str(x) for x in self) - - def __repr__(self): - return '{}({})'.format(self.__class__.__name__, - super(TokenList, self).__repr__()) - - @property - def value(self): - return ''.join(x.value for x in self if x.value) - - @property - def all_defects(self): - return sum((x.all_defects for x in self), self.defects) - - # - # Folding API - # - # parts(): - # - # return a list of objects that constitute the "higher level syntactic - # objects" specified by the RFC as the best places to fold a header line. - # The returned objects must include leading folding white space, even if - # this means mutating the underlying parse tree of the object. Each object - # is only responsible for returning *its* parts, and should not drill down - # to any lower level except as required to meet the leading folding white - # space constraint. - # - # _fold(folded): - # - # folded: the result accumulator. This is an instance of _Folded. - # (XXX: I haven't finished factoring this out yet, the folding code - # pretty much uses this as a state object.) When the folded.current - # contains as much text as will fit, the _fold method should call - # folded.newline. - # folded.lastlen: the current length of the test stored in folded.current. - # folded.maxlen: The maximum number of characters that may appear on a - # folded line. Differs from the policy setting in that "no limit" is - # represented by +inf, which means it can be used in the trivially - # logical fashion in comparisons. - # - # Currently no subclasses implement parts, and I think this will remain - # true. A subclass only needs to implement _fold when the generic version - # isn't sufficient. _fold will need to be implemented primarily when it is - # possible for encoded words to appear in the specialized token-list, since - # there is no generic algorithm that can know where exactly the encoded - # words are allowed. A _fold implementation is responsible for filling - # lines in the same general way that the top level _fold does. It may, and - # should, call the _fold method of sub-objects in a similar fashion to that - # of the top level _fold. - # - # XXX: I'm hoping it will be possible to factor the existing code further - # to reduce redundancy and make the logic clearer. - - @property - def parts(self): - klass = self.__class__ - this = list() - for token in self: - if token.startswith_fws(): - if this: - yield this[0] if len(this)==1 else klass(this) - this.clear() - end_ws = token.pop_trailing_ws() - this.append(token) - if end_ws: - yield klass(this) - this = [end_ws] - if this: - yield this[0] if len(this)==1 else klass(this) - - def startswith_fws(self): - return self[0].startswith_fws() - - def pop_leading_fws(self): - if self[0].token_type == 'fws': - return self.pop(0) - return self[0].pop_leading_fws() - - def pop_trailing_ws(self): - if self[-1].token_type == 'cfws': - return self.pop(-1) - return self[-1].pop_trailing_ws() - - @property - def has_fws(self): - for part in self: - if part.has_fws: - return True - return False - - def has_leading_comment(self): - return self[0].has_leading_comment() - - @property - def comments(self): - comments = [] - for token in self: - comments.extend(token.comments) - return comments - - def fold(self, **_3to2kwargs): - # max_line_length 0/None means no limit, ie: infinitely long. - policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - maxlen = policy.max_line_length or float("+inf") - folded = _Folded(maxlen, policy) - self._fold(folded) - folded.finalize() - return str(folded) - - def as_encoded_word(self, charset): - # This works only for things returned by 'parts', which include - # the leading fws, if any, that should be used. - res = [] - ws = self.pop_leading_fws() - if ws: - res.append(ws) - trailer = self.pop(-1) if self[-1].token_type=='fws' else '' - res.append(_ew.encode(str(self), charset)) - res.append(trailer) - return ''.join(res) - - def cte_encode(self, charset, policy): - res = [] - for part in self: - res.append(part.cte_encode(charset, policy)) - return ''.join(res) - - def _fold(self, folded): - for part in self.parts: - tstr = str(part) - tlen = len(tstr) - try: - str(part).encode('us-ascii') - except UnicodeEncodeError: - if any(isinstance(x, errors.UndecodableBytesDefect) - for x in part.all_defects): - charset = 'unknown-8bit' - else: - # XXX: this should be a policy setting - charset = 'utf-8' - tstr = part.cte_encode(charset, folded.policy) - tlen = len(tstr) - if folded.append_if_fits(part, tstr): - continue - # Peel off the leading whitespace if any and make it sticky, to - # avoid infinite recursion. - ws = part.pop_leading_fws() - if ws is not None: - # Peel off the leading whitespace and make it sticky, to - # avoid infinite recursion. - folded.stickyspace = str(part.pop(0)) - if folded.append_if_fits(part): - continue - if part.has_fws: - part._fold(folded) - continue - # There are no fold points in this one; it is too long for a single - # line and can't be split...we just have to put it on its own line. - folded.append(tstr) - folded.newline() - - def pprint(self, indent=''): - print('\n'.join(self._pp(indent=''))) - - def ppstr(self, indent=''): - return '\n'.join(self._pp(indent='')) - - def _pp(self, indent=''): - yield '{}{}/{}('.format( - indent, - self.__class__.__name__, - self.token_type) - for token in self: - if not hasattr(token, '_pp'): - yield (indent + ' !! invalid element in token ' - 'list: {!r}'.format(token)) - else: - for line in token._pp(indent+' '): - yield line - if self.defects: - extra = ' Defects: {}'.format(self.defects) - else: - extra = '' - yield '{}){}'.format(indent, extra) - - -class WhiteSpaceTokenList(TokenList): - - @property - def value(self): - return ' ' - - @property - def comments(self): - return [x.content for x in self if x.token_type=='comment'] - - -class UnstructuredTokenList(TokenList): - - token_type = 'unstructured' - - def _fold(self, folded): - if any(x.token_type=='encoded-word' for x in self): - return self._fold_encoded(folded) - # Here we can have either a pure ASCII string that may or may not - # have surrogateescape encoded bytes, or a unicode string. - last_ew = None - for part in self.parts: - tstr = str(part) - is_ew = False - try: - str(part).encode('us-ascii') - except UnicodeEncodeError: - if any(isinstance(x, errors.UndecodableBytesDefect) - for x in part.all_defects): - charset = 'unknown-8bit' - else: - charset = 'utf-8' - if last_ew is not None: - # We've already done an EW, combine this one with it - # if there's room. - chunk = get_unstructured( - ''.join(folded.current[last_ew:]+[tstr])).as_encoded_word(charset) - oldlastlen = sum(len(x) for x in folded.current[:last_ew]) - schunk = str(chunk) - lchunk = len(schunk) - if oldlastlen + lchunk <= folded.maxlen: - del folded.current[last_ew:] - folded.append(schunk) - folded.lastlen = oldlastlen + lchunk - continue - tstr = part.as_encoded_word(charset) - is_ew = True - if folded.append_if_fits(part, tstr): - if is_ew: - last_ew = len(folded.current) - 1 - continue - if is_ew or last_ew: - # It's too big to fit on the line, but since we've - # got encoded words we can use encoded word folding. - part._fold_as_ew(folded) - continue - # Peel off the leading whitespace if any and make it sticky, to - # avoid infinite recursion. - ws = part.pop_leading_fws() - if ws is not None: - folded.stickyspace = str(ws) - if folded.append_if_fits(part): - continue - if part.has_fws: - part.fold(folded) - continue - # It can't be split...we just have to put it on its own line. - folded.append(tstr) - folded.newline() - last_ew = None - - def cte_encode(self, charset, policy): - res = [] - last_ew = None - for part in self: - spart = str(part) - try: - spart.encode('us-ascii') - res.append(spart) - except UnicodeEncodeError: - if last_ew is None: - res.append(part.cte_encode(charset, policy)) - last_ew = len(res) - else: - tl = get_unstructured(''.join(res[last_ew:] + [spart])) - res.append(tl.as_encoded_word()) - return ''.join(res) - - -class Phrase(TokenList): - - token_type = 'phrase' - - def _fold(self, folded): - # As with Unstructured, we can have pure ASCII with or without - # surrogateescape encoded bytes, or we could have unicode. But this - # case is more complicated, since we have to deal with the various - # sub-token types and how they can be composed in the face of - # unicode-that-needs-CTE-encoding, and the fact that if a token a - # comment that becomes a barrier across which we can't compose encoded - # words. - last_ew = None - for part in self.parts: - tstr = str(part) - tlen = len(tstr) - has_ew = False - try: - str(part).encode('us-ascii') - except UnicodeEncodeError: - if any(isinstance(x, errors.UndecodableBytesDefect) - for x in part.all_defects): - charset = 'unknown-8bit' - else: - charset = 'utf-8' - if last_ew is not None and not part.has_leading_comment(): - # We've already done an EW, let's see if we can combine - # this one with it. The last_ew logic ensures that all we - # have at this point is atoms, no comments or quoted - # strings. So we can treat the text between the last - # encoded word and the content of this token as - # unstructured text, and things will work correctly. But - # we have to strip off any trailing comment on this token - # first, and if it is a quoted string we have to pull out - # the content (we're encoding it, so it no longer needs to - # be quoted). - if part[-1].token_type == 'cfws' and part.comments: - remainder = part.pop(-1) - else: - remainder = '' - for i, token in enumerate(part): - if token.token_type == 'bare-quoted-string': - part[i] = UnstructuredTokenList(token[:]) - chunk = get_unstructured( - ''.join(folded.current[last_ew:]+[tstr])).as_encoded_word(charset) - schunk = str(chunk) - lchunk = len(schunk) - if last_ew + lchunk <= folded.maxlen: - del folded.current[last_ew:] - folded.append(schunk) - folded.lastlen = sum(len(x) for x in folded.current) - continue - tstr = part.as_encoded_word(charset) - tlen = len(tstr) - has_ew = True - if folded.append_if_fits(part, tstr): - if has_ew and not part.comments: - last_ew = len(folded.current) - 1 - elif part.comments or part.token_type == 'quoted-string': - # If a comment is involved we can't combine EWs. And if a - # quoted string is involved, it's not worth the effort to - # try to combine them. - last_ew = None - continue - part._fold(folded) - - def cte_encode(self, charset, policy): - res = [] - last_ew = None - is_ew = False - for part in self: - spart = str(part) - try: - spart.encode('us-ascii') - res.append(spart) - except UnicodeEncodeError: - is_ew = True - if last_ew is None: - if not part.comments: - last_ew = len(res) - res.append(part.cte_encode(charset, policy)) - elif not part.has_leading_comment(): - if part[-1].token_type == 'cfws' and part.comments: - remainder = part.pop(-1) - else: - remainder = '' - for i, token in enumerate(part): - if token.token_type == 'bare-quoted-string': - part[i] = UnstructuredTokenList(token[:]) - tl = get_unstructured(''.join(res[last_ew:] + [spart])) - res[last_ew:] = [tl.as_encoded_word(charset)] - if part.comments or (not is_ew and part.token_type == 'quoted-string'): - last_ew = None - return ''.join(res) - -class Word(TokenList): - - token_type = 'word' - - -class CFWSList(WhiteSpaceTokenList): - - token_type = 'cfws' - - def has_leading_comment(self): - return bool(self.comments) - - -class Atom(TokenList): - - token_type = 'atom' - - -class Token(TokenList): - - token_type = 'token' - - -class EncodedWord(TokenList): - - token_type = 'encoded-word' - cte = None - charset = None - lang = None - - @property - def encoded(self): - if self.cte is not None: - return self.cte - _ew.encode(str(self), self.charset) - - - -class QuotedString(TokenList): - - token_type = 'quoted-string' - - @property - def content(self): - for x in self: - if x.token_type == 'bare-quoted-string': - return x.value - - @property - def quoted_value(self): - res = [] - for x in self: - if x.token_type == 'bare-quoted-string': - res.append(str(x)) - else: - res.append(x.value) - return ''.join(res) - - @property - def stripped_value(self): - for token in self: - if token.token_type == 'bare-quoted-string': - return token.value - - -class BareQuotedString(QuotedString): - - token_type = 'bare-quoted-string' - - def __str__(self): - return quote_string(''.join(str(x) for x in self)) - - @property - def value(self): - return ''.join(str(x) for x in self) - - -class Comment(WhiteSpaceTokenList): - - token_type = 'comment' - - def __str__(self): - return ''.join(sum([ - ["("], - [self.quote(x) for x in self], - [")"], - ], [])) - - def quote(self, value): - if value.token_type == 'comment': - return str(value) - return str(value).replace('\\', '\\\\').replace( - '(', '\(').replace( - ')', '\)') - - @property - def content(self): - return ''.join(str(x) for x in self) - - @property - def comments(self): - return [self.content] - -class AddressList(TokenList): - - token_type = 'address-list' - - @property - def addresses(self): - return [x for x in self if x.token_type=='address'] - - @property - def mailboxes(self): - return sum((x.mailboxes - for x in self if x.token_type=='address'), []) - - @property - def all_mailboxes(self): - return sum((x.all_mailboxes - for x in self if x.token_type=='address'), []) - - -class Address(TokenList): - - token_type = 'address' - - @property - def display_name(self): - if self[0].token_type == 'group': - return self[0].display_name - - @property - def mailboxes(self): - if self[0].token_type == 'mailbox': - return [self[0]] - elif self[0].token_type == 'invalid-mailbox': - return [] - return self[0].mailboxes - - @property - def all_mailboxes(self): - if self[0].token_type == 'mailbox': - return [self[0]] - elif self[0].token_type == 'invalid-mailbox': - return [self[0]] - return self[0].all_mailboxes - -class MailboxList(TokenList): - - token_type = 'mailbox-list' - - @property - def mailboxes(self): - return [x for x in self if x.token_type=='mailbox'] - - @property - def all_mailboxes(self): - return [x for x in self - if x.token_type in ('mailbox', 'invalid-mailbox')] - - -class GroupList(TokenList): - - token_type = 'group-list' - - @property - def mailboxes(self): - if not self or self[0].token_type != 'mailbox-list': - return [] - return self[0].mailboxes - - @property - def all_mailboxes(self): - if not self or self[0].token_type != 'mailbox-list': - return [] - return self[0].all_mailboxes - - -class Group(TokenList): - - token_type = "group" - - @property - def mailboxes(self): - if self[2].token_type != 'group-list': - return [] - return self[2].mailboxes - - @property - def all_mailboxes(self): - if self[2].token_type != 'group-list': - return [] - return self[2].all_mailboxes - - @property - def display_name(self): - return self[0].display_name - - -class NameAddr(TokenList): - - token_type = 'name-addr' - - @property - def display_name(self): - if len(self) == 1: - return None - return self[0].display_name - - @property - def local_part(self): - return self[-1].local_part - - @property - def domain(self): - return self[-1].domain - - @property - def route(self): - return self[-1].route - - @property - def addr_spec(self): - return self[-1].addr_spec - - -class AngleAddr(TokenList): - - token_type = 'angle-addr' - - @property - def local_part(self): - for x in self: - if x.token_type == 'addr-spec': - return x.local_part - - @property - def domain(self): - for x in self: - if x.token_type == 'addr-spec': - return x.domain - - @property - def route(self): - for x in self: - if x.token_type == 'obs-route': - return x.domains - - @property - def addr_spec(self): - for x in self: - if x.token_type == 'addr-spec': - return x.addr_spec - else: - return '<>' - - -class ObsRoute(TokenList): - - token_type = 'obs-route' - - @property - def domains(self): - return [x.domain for x in self if x.token_type == 'domain'] - - -class Mailbox(TokenList): - - token_type = 'mailbox' - - @property - def display_name(self): - if self[0].token_type == 'name-addr': - return self[0].display_name - - @property - def local_part(self): - return self[0].local_part - - @property - def domain(self): - return self[0].domain - - @property - def route(self): - if self[0].token_type == 'name-addr': - return self[0].route - - @property - def addr_spec(self): - return self[0].addr_spec - - -class InvalidMailbox(TokenList): - - token_type = 'invalid-mailbox' - - @property - def display_name(self): - return None - - local_part = domain = route = addr_spec = display_name - - -class Domain(TokenList): - - token_type = 'domain' - - @property - def domain(self): - return ''.join(super(Domain, self).value.split()) - - -class DotAtom(TokenList): - - token_type = 'dot-atom' - - -class DotAtomText(TokenList): - - token_type = 'dot-atom-text' - - -class AddrSpec(TokenList): - - token_type = 'addr-spec' - - @property - def local_part(self): - return self[0].local_part - - @property - def domain(self): - if len(self) < 3: - return None - return self[-1].domain - - @property - def value(self): - if len(self) < 3: - return self[0].value - return self[0].value.rstrip()+self[1].value+self[2].value.lstrip() - - @property - def addr_spec(self): - nameset = set(self.local_part) - if len(nameset) > len(nameset-DOT_ATOM_ENDS): - lp = quote_string(self.local_part) - else: - lp = self.local_part - if self.domain is not None: - return lp + '@' + self.domain - return lp - - -class ObsLocalPart(TokenList): - - token_type = 'obs-local-part' - - -class DisplayName(Phrase): - - token_type = 'display-name' - - @property - def display_name(self): - res = TokenList(self) - if res[0].token_type == 'cfws': - res.pop(0) - else: - if res[0][0].token_type == 'cfws': - res[0] = TokenList(res[0][1:]) - if res[-1].token_type == 'cfws': - res.pop() - else: - if res[-1][-1].token_type == 'cfws': - res[-1] = TokenList(res[-1][:-1]) - return res.value - - @property - def value(self): - quote = False - if self.defects: - quote = True - else: - for x in self: - if x.token_type == 'quoted-string': - quote = True - if quote: - pre = post = '' - if self[0].token_type=='cfws' or self[0][0].token_type=='cfws': - pre = ' ' - if self[-1].token_type=='cfws' or self[-1][-1].token_type=='cfws': - post = ' ' - return pre+quote_string(self.display_name)+post - else: - return super(DisplayName, self).value - - -class LocalPart(TokenList): - - token_type = 'local-part' - - @property - def value(self): - if self[0].token_type == "quoted-string": - return self[0].quoted_value - else: - return self[0].value - - @property - def local_part(self): - # Strip whitespace from front, back, and around dots. - res = [DOT] - last = DOT - last_is_tl = False - for tok in self[0] + [DOT]: - if tok.token_type == 'cfws': - continue - if (last_is_tl and tok.token_type == 'dot' and - last[-1].token_type == 'cfws'): - res[-1] = TokenList(last[:-1]) - is_tl = isinstance(tok, TokenList) - if (is_tl and last.token_type == 'dot' and - tok[0].token_type == 'cfws'): - res.append(TokenList(tok[1:])) - else: - res.append(tok) - last = res[-1] - last_is_tl = is_tl - res = TokenList(res[1:-1]) - return res.value - - -class DomainLiteral(TokenList): - - token_type = 'domain-literal' - - @property - def domain(self): - return ''.join(super(DomainLiteral, self).value.split()) - - @property - def ip(self): - for x in self: - if x.token_type == 'ptext': - return x.value - - -class MIMEVersion(TokenList): - - token_type = 'mime-version' - major = None - minor = None - - -class Parameter(TokenList): - - token_type = 'parameter' - sectioned = False - extended = False - charset = 'us-ascii' - - @property - def section_number(self): - # Because the first token, the attribute (name) eats CFWS, the second - # token is always the section if there is one. - return self[1].number if self.sectioned else 0 - - @property - def param_value(self): - # This is part of the "handle quoted extended parameters" hack. - for token in self: - if token.token_type == 'value': - return token.stripped_value - if token.token_type == 'quoted-string': - for token in token: - if token.token_type == 'bare-quoted-string': - for token in token: - if token.token_type == 'value': - return token.stripped_value - return '' - - -class InvalidParameter(Parameter): - - token_type = 'invalid-parameter' - - -class Attribute(TokenList): - - token_type = 'attribute' - - @property - def stripped_value(self): - for token in self: - if token.token_type.endswith('attrtext'): - return token.value - -class Section(TokenList): - - token_type = 'section' - number = None - - -class Value(TokenList): - - token_type = 'value' - - @property - def stripped_value(self): - token = self[0] - if token.token_type == 'cfws': - token = self[1] - if token.token_type.endswith( - ('quoted-string', 'attribute', 'extended-attribute')): - return token.stripped_value - return self.value - - -class MimeParameters(TokenList): - - token_type = 'mime-parameters' - - @property - def params(self): - # The RFC specifically states that the ordering of parameters is not - # guaranteed and may be reordered by the transport layer. So we have - # to assume the RFC 2231 pieces can come in any order. However, we - # output them in the order that we first see a given name, which gives - # us a stable __str__. - params = OrderedDict() - for token in self: - if not token.token_type.endswith('parameter'): - continue - if token[0].token_type != 'attribute': - continue - name = token[0].value.strip() - if name not in params: - params[name] = [] - params[name].append((token.section_number, token)) - for name, parts in params.items(): - parts = sorted(parts) - # XXX: there might be more recovery we could do here if, for - # example, this is really a case of a duplicate attribute name. - value_parts = [] - charset = parts[0][1].charset - for i, (section_number, param) in enumerate(parts): - if section_number != i: - param.defects.append(errors.InvalidHeaderDefect( - "inconsistent multipart parameter numbering")) - value = param.param_value - if param.extended: - try: - value = unquote_to_bytes(value) - except UnicodeEncodeError: - # source had surrogate escaped bytes. What we do now - # is a bit of an open question. I'm not sure this is - # the best choice, but it is what the old algorithm did - value = unquote(value, encoding='latin-1') - else: - try: - value = value.decode(charset, 'surrogateescape') - except LookupError: - # XXX: there should really be a custom defect for - # unknown character set to make it easy to find, - # because otherwise unknown charset is a silent - # failure. - value = value.decode('us-ascii', 'surrogateescape') - if utils._has_surrogates(value): - param.defects.append(errors.UndecodableBytesDefect()) - value_parts.append(value) - value = ''.join(value_parts) - yield name, value - - def __str__(self): - params = [] - for name, value in self.params: - if value: - params.append('{}={}'.format(name, quote_string(value))) - else: - params.append(name) - params = '; '.join(params) - return ' ' + params if params else '' - - -class ParameterizedHeaderValue(TokenList): - - @property - def params(self): - for token in reversed(self): - if token.token_type == 'mime-parameters': - return token.params - return {} - - @property - def parts(self): - if self and self[-1].token_type == 'mime-parameters': - # We don't want to start a new line if all of the params don't fit - # after the value, so unwrap the parameter list. - return TokenList(self[:-1] + self[-1]) - return TokenList(self).parts - - -class ContentType(ParameterizedHeaderValue): - - token_type = 'content-type' - maintype = 'text' - subtype = 'plain' - - -class ContentDisposition(ParameterizedHeaderValue): - - token_type = 'content-disposition' - content_disposition = None - - -class ContentTransferEncoding(TokenList): - - token_type = 'content-transfer-encoding' - cte = '7bit' - - -class HeaderLabel(TokenList): - - token_type = 'header-label' - - -class Header(TokenList): - - token_type = 'header' - - def _fold(self, folded): - folded.append(str(self.pop(0))) - folded.lastlen = len(folded.current[0]) - # The first line of the header is different from all others: we don't - # want to start a new object on a new line if it has any fold points in - # it that would allow part of it to be on the first header line. - # Further, if the first fold point would fit on the new line, we want - # to do that, but if it doesn't we want to put it on the first line. - # Folded supports this via the stickyspace attribute. If this - # attribute is not None, it does the special handling. - folded.stickyspace = str(self.pop(0)) if self[0].token_type == 'cfws' else '' - rest = self.pop(0) - if self: - raise ValueError("Malformed Header token list") - rest._fold(folded) - - -# -# Terminal classes and instances -# - -class Terminal(str): - - def __new__(cls, value, token_type): - self = super(Terminal, cls).__new__(cls, value) - self.token_type = token_type - self.defects = [] - return self - - def __repr__(self): - return "{}({})".format(self.__class__.__name__, super(Terminal, self).__repr__()) - - @property - def all_defects(self): - return list(self.defects) - - def _pp(self, indent=''): - return ["{}{}/{}({}){}".format( - indent, - self.__class__.__name__, - self.token_type, - super(Terminal, self).__repr__(), - '' if not self.defects else ' {}'.format(self.defects), - )] - - def cte_encode(self, charset, policy): - value = str(self) - try: - value.encode('us-ascii') - return value - except UnicodeEncodeError: - return _ew.encode(value, charset) - - def pop_trailing_ws(self): - # This terminates the recursion. - return None - - def pop_leading_fws(self): - # This terminates the recursion. - return None - - @property - def comments(self): - return [] - - def has_leading_comment(self): - return False - - def __getnewargs__(self): - return(str(self), self.token_type) - - -class WhiteSpaceTerminal(Terminal): - - @property - def value(self): - return ' ' - - def startswith_fws(self): - return True - - has_fws = True - - -class ValueTerminal(Terminal): - - @property - def value(self): - return self - - def startswith_fws(self): - return False - - has_fws = False - - def as_encoded_word(self, charset): - return _ew.encode(str(self), charset) - - -class EWWhiteSpaceTerminal(WhiteSpaceTerminal): - - @property - def value(self): - return '' - - @property - def encoded(self): - return self[:] - - def __str__(self): - return '' - - has_fws = True - - -# XXX these need to become classes and used as instances so -# that a program can't change them in a parse tree and screw -# up other parse trees. Maybe should have tests for that, too. -DOT = ValueTerminal('.', 'dot') -ListSeparator = ValueTerminal(',', 'list-separator') -RouteComponentMarker = ValueTerminal('@', 'route-component-marker') - -# -# Parser -# - -"""Parse strings according to RFC822/2047/2822/5322 rules. - -This is a stateless parser. Each get_XXX function accepts a string and -returns either a Terminal or a TokenList representing the RFC object named -by the method and a string containing the remaining unparsed characters -from the input. Thus a parser method consumes the next syntactic construct -of a given type and returns a token representing the construct plus the -unparsed remainder of the input string. - -For example, if the first element of a structured header is a 'phrase', -then: - - phrase, value = get_phrase(value) - -returns the complete phrase from the start of the string value, plus any -characters left in the string after the phrase is removed. - -""" - -_wsp_splitter = re.compile(r'([{}]+)'.format(''.join(WSP))).split -_non_atom_end_matcher = re.compile(r"[^{}]+".format( - ''.join(ATOM_ENDS).replace('\\','\\\\').replace(']','\]'))).match -_non_printable_finder = re.compile(r"[\x00-\x20\x7F]").findall -_non_token_end_matcher = re.compile(r"[^{}]+".format( - ''.join(TOKEN_ENDS).replace('\\','\\\\').replace(']','\]'))).match -_non_attribute_end_matcher = re.compile(r"[^{}]+".format( - ''.join(ATTRIBUTE_ENDS).replace('\\','\\\\').replace(']','\]'))).match -_non_extended_attribute_end_matcher = re.compile(r"[^{}]+".format( - ''.join(EXTENDED_ATTRIBUTE_ENDS).replace( - '\\','\\\\').replace(']','\]'))).match - -def _validate_xtext(xtext): - """If input token contains ASCII non-printables, register a defect.""" - - non_printables = _non_printable_finder(xtext) - if non_printables: - xtext.defects.append(errors.NonPrintableDefect(non_printables)) - if utils._has_surrogates(xtext): - xtext.defects.append(errors.UndecodableBytesDefect( - "Non-ASCII characters found in header token")) - -def _get_ptext_to_endchars(value, endchars): - """Scan printables/quoted-pairs until endchars and return unquoted ptext. - - This function turns a run of qcontent, ccontent-without-comments, or - dtext-with-quoted-printables into a single string by unquoting any - quoted printables. It returns the string, the remaining value, and - a flag that is True iff there were any quoted printables decoded. - - """ - _3to2list = list(_wsp_splitter(value, 1)) - fragment, remainder, = _3to2list[:1] + [_3to2list[1:]] - vchars = [] - escape = False - had_qp = False - for pos in range(len(fragment)): - if fragment[pos] == '\\': - if escape: - escape = False - had_qp = True - else: - escape = True - continue - if escape: - escape = False - elif fragment[pos] in endchars: - break - vchars.append(fragment[pos]) - else: - pos = pos + 1 - return ''.join(vchars), ''.join([fragment[pos:]] + remainder), had_qp - -def _decode_ew_run(value): - """ Decode a run of RFC2047 encoded words. - - _decode_ew_run(value) -> (text, value, defects) - - Scans the supplied value for a run of tokens that look like they are RFC - 2047 encoded words, decodes those words into text according to RFC 2047 - rules (whitespace between encoded words is discarded), and returns the text - and the remaining value (including any leading whitespace on the remaining - value), as well as a list of any defects encountered while decoding. The - input value may not have any leading whitespace. - - """ - res = [] - defects = [] - last_ws = '' - while value: - try: - tok, ws, value = _wsp_splitter(value, 1) - except ValueError: - tok, ws, value = value, '', '' - if not (tok.startswith('=?') and tok.endswith('?=')): - return ''.join(res), last_ws + tok + ws + value, defects - text, charset, lang, new_defects = _ew.decode(tok) - res.append(text) - defects.extend(new_defects) - last_ws = ws - return ''.join(res), last_ws, defects - -def get_fws(value): - """FWS = 1*WSP - - This isn't the RFC definition. We're using fws to represent tokens where - folding can be done, but when we are parsing the *un*folding has already - been done so we don't need to watch out for CRLF. - - """ - newvalue = value.lstrip() - fws = WhiteSpaceTerminal(value[:len(value)-len(newvalue)], 'fws') - return fws, newvalue - -def get_encoded_word(value): - """ encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" - - """ - ew = EncodedWord() - if not value.startswith('=?'): - raise errors.HeaderParseError( - "expected encoded word but found {}".format(value)) - _3to2list1 = list(value[2:].split('?=', 1)) - tok, remainder, = _3to2list1[:1] + [_3to2list1[1:]] - if tok == value[2:]: - raise errors.HeaderParseError( - "expected encoded word but found {}".format(value)) - remstr = ''.join(remainder) - if remstr[:2].isdigit(): - _3to2list3 = list(remstr.split('?=', 1)) - rest, remainder, = _3to2list3[:1] + [_3to2list3[1:]] - tok = tok + '?=' + rest - if len(tok.split()) > 1: - ew.defects.append(errors.InvalidHeaderDefect( - "whitespace inside encoded word")) - ew.cte = value - value = ''.join(remainder) - try: - text, charset, lang, defects = _ew.decode('=?' + tok + '?=') - except ValueError: - raise errors.HeaderParseError( - "encoded word format invalid: '{}'".format(ew.cte)) - ew.charset = charset - ew.lang = lang - ew.defects.extend(defects) - while text: - if text[0] in WSP: - token, text = get_fws(text) - ew.append(token) - continue - _3to2list5 = list(_wsp_splitter(text, 1)) - chars, remainder, = _3to2list5[:1] + [_3to2list5[1:]] - vtext = ValueTerminal(chars, 'vtext') - _validate_xtext(vtext) - ew.append(vtext) - text = ''.join(remainder) - return ew, value - -def get_unstructured(value): - """unstructured = (*([FWS] vchar) *WSP) / obs-unstruct - obs-unstruct = *((*LF *CR *(obs-utext) *LF *CR)) / FWS) - obs-utext = %d0 / obs-NO-WS-CTL / LF / CR - - obs-NO-WS-CTL is control characters except WSP/CR/LF. - - So, basically, we have printable runs, plus control characters or nulls in - the obsolete syntax, separated by whitespace. Since RFC 2047 uses the - obsolete syntax in its specification, but requires whitespace on either - side of the encoded words, I can see no reason to need to separate the - non-printable-non-whitespace from the printable runs if they occur, so we - parse this into xtext tokens separated by WSP tokens. - - Because an 'unstructured' value must by definition constitute the entire - value, this 'get' routine does not return a remaining value, only the - parsed TokenList. - - """ - # XXX: but what about bare CR and LF? They might signal the start or - # end of an encoded word. YAGNI for now, since out current parsers - # will never send us strings with bard CR or LF. - - unstructured = UnstructuredTokenList() - while value: - if value[0] in WSP: - token, value = get_fws(value) - unstructured.append(token) - continue - if value.startswith('=?'): - try: - token, value = get_encoded_word(value) - except errors.HeaderParseError: - pass - else: - have_ws = True - if len(unstructured) > 0: - if unstructured[-1].token_type != 'fws': - unstructured.defects.append(errors.InvalidHeaderDefect( - "missing whitespace before encoded word")) - have_ws = False - if have_ws and len(unstructured) > 1: - if unstructured[-2].token_type == 'encoded-word': - unstructured[-1] = EWWhiteSpaceTerminal( - unstructured[-1], 'fws') - unstructured.append(token) - continue - _3to2list7 = list(_wsp_splitter(value, 1)) - tok, remainder, = _3to2list7[:1] + [_3to2list7[1:]] - vtext = ValueTerminal(tok, 'vtext') - _validate_xtext(vtext) - unstructured.append(vtext) - value = ''.join(remainder) - return unstructured - -def get_qp_ctext(value): - """ctext = - - This is not the RFC ctext, since we are handling nested comments in comment - and unquoting quoted-pairs here. We allow anything except the '()' - characters, but if we find any ASCII other than the RFC defined printable - ASCII an NonPrintableDefect is added to the token's defects list. Since - quoted pairs are converted to their unquoted values, what is returned is - a 'ptext' token. In this case it is a WhiteSpaceTerminal, so it's value - is ' '. - - """ - ptext, value, _ = _get_ptext_to_endchars(value, '()') - ptext = WhiteSpaceTerminal(ptext, 'ptext') - _validate_xtext(ptext) - return ptext, value - -def get_qcontent(value): - """qcontent = qtext / quoted-pair - - We allow anything except the DQUOTE character, but if we find any ASCII - other than the RFC defined printable ASCII an NonPrintableDefect is - added to the token's defects list. Any quoted pairs are converted to their - unquoted values, so what is returned is a 'ptext' token. In this case it - is a ValueTerminal. - - """ - ptext, value, _ = _get_ptext_to_endchars(value, '"') - ptext = ValueTerminal(ptext, 'ptext') - _validate_xtext(ptext) - return ptext, value - -def get_atext(value): - """atext = - - We allow any non-ATOM_ENDS in atext, but add an InvalidATextDefect to - the token's defects list if we find non-atext characters. - """ - m = _non_atom_end_matcher(value) - if not m: - raise errors.HeaderParseError( - "expected atext but found '{}'".format(value)) - atext = m.group() - value = value[len(atext):] - atext = ValueTerminal(atext, 'atext') - _validate_xtext(atext) - return atext, value - -def get_bare_quoted_string(value): - """bare-quoted-string = DQUOTE *([FWS] qcontent) [FWS] DQUOTE - - A quoted-string without the leading or trailing white space. Its - value is the text between the quote marks, with whitespace - preserved and quoted pairs decoded. - """ - if value[0] != '"': - raise errors.HeaderParseError( - "expected '\"' but found '{}'".format(value)) - bare_quoted_string = BareQuotedString() - value = value[1:] - while value and value[0] != '"': - if value[0] in WSP: - token, value = get_fws(value) - else: - token, value = get_qcontent(value) - bare_quoted_string.append(token) - if not value: - bare_quoted_string.defects.append(errors.InvalidHeaderDefect( - "end of header inside quoted string")) - return bare_quoted_string, value - return bare_quoted_string, value[1:] - -def get_comment(value): - """comment = "(" *([FWS] ccontent) [FWS] ")" - ccontent = ctext / quoted-pair / comment - - We handle nested comments here, and quoted-pair in our qp-ctext routine. - """ - if value and value[0] != '(': - raise errors.HeaderParseError( - "expected '(' but found '{}'".format(value)) - comment = Comment() - value = value[1:] - while value and value[0] != ")": - if value[0] in WSP: - token, value = get_fws(value) - elif value[0] == '(': - token, value = get_comment(value) - else: - token, value = get_qp_ctext(value) - comment.append(token) - if not value: - comment.defects.append(errors.InvalidHeaderDefect( - "end of header inside comment")) - return comment, value - return comment, value[1:] - -def get_cfws(value): - """CFWS = (1*([FWS] comment) [FWS]) / FWS - - """ - cfws = CFWSList() - while value and value[0] in CFWS_LEADER: - if value[0] in WSP: - token, value = get_fws(value) - else: - token, value = get_comment(value) - cfws.append(token) - return cfws, value - -def get_quoted_string(value): - """quoted-string = [CFWS] [CFWS] - - 'bare-quoted-string' is an intermediate class defined by this - parser and not by the RFC grammar. It is the quoted string - without any attached CFWS. - """ - quoted_string = QuotedString() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - quoted_string.append(token) - token, value = get_bare_quoted_string(value) - quoted_string.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - quoted_string.append(token) - return quoted_string, value - -def get_atom(value): - """atom = [CFWS] 1*atext [CFWS] - - """ - atom = Atom() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - atom.append(token) - if value and value[0] in ATOM_ENDS: - raise errors.HeaderParseError( - "expected atom but found '{}'".format(value)) - token, value = get_atext(value) - atom.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - atom.append(token) - return atom, value - -def get_dot_atom_text(value): - """ dot-text = 1*atext *("." 1*atext) - - """ - dot_atom_text = DotAtomText() - if not value or value[0] in ATOM_ENDS: - raise errors.HeaderParseError("expected atom at a start of " - "dot-atom-text but found '{}'".format(value)) - while value and value[0] not in ATOM_ENDS: - token, value = get_atext(value) - dot_atom_text.append(token) - if value and value[0] == '.': - dot_atom_text.append(DOT) - value = value[1:] - if dot_atom_text[-1] is DOT: - raise errors.HeaderParseError("expected atom at end of dot-atom-text " - "but found '{}'".format('.'+value)) - return dot_atom_text, value - -def get_dot_atom(value): - """ dot-atom = [CFWS] dot-atom-text [CFWS] - - """ - dot_atom = DotAtom() - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - dot_atom.append(token) - token, value = get_dot_atom_text(value) - dot_atom.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - dot_atom.append(token) - return dot_atom, value - -def get_word(value): - """word = atom / quoted-string - - Either atom or quoted-string may start with CFWS. We have to peel off this - CFWS first to determine which type of word to parse. Afterward we splice - the leading CFWS, if any, into the parsed sub-token. - - If neither an atom or a quoted-string is found before the next special, a - HeaderParseError is raised. - - The token returned is either an Atom or a QuotedString, as appropriate. - This means the 'word' level of the formal grammar is not represented in the - parse tree; this is because having that extra layer when manipulating the - parse tree is more confusing than it is helpful. - - """ - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - else: - leader = None - if value[0]=='"': - token, value = get_quoted_string(value) - elif value[0] in SPECIALS: - raise errors.HeaderParseError("Expected 'atom' or 'quoted-string' " - "but found '{}'".format(value)) - else: - token, value = get_atom(value) - if leader is not None: - token[:0] = [leader] - return token, value - -def get_phrase(value): - """ phrase = 1*word / obs-phrase - obs-phrase = word *(word / "." / CFWS) - - This means a phrase can be a sequence of words, periods, and CFWS in any - order as long as it starts with at least one word. If anything other than - words is detected, an ObsoleteHeaderDefect is added to the token's defect - list. We also accept a phrase that starts with CFWS followed by a dot; - this is registered as an InvalidHeaderDefect, since it is not supported by - even the obsolete grammar. - - """ - phrase = Phrase() - try: - token, value = get_word(value) - phrase.append(token) - except errors.HeaderParseError: - phrase.defects.append(errors.InvalidHeaderDefect( - "phrase does not start with word")) - while value and value[0] not in PHRASE_ENDS: - if value[0]=='.': - phrase.append(DOT) - phrase.defects.append(errors.ObsoleteHeaderDefect( - "period in 'phrase'")) - value = value[1:] - else: - try: - token, value = get_word(value) - except errors.HeaderParseError: - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - phrase.defects.append(errors.ObsoleteHeaderDefect( - "comment found without atom")) - else: - raise - phrase.append(token) - return phrase, value - -def get_local_part(value): - """ local-part = dot-atom / quoted-string / obs-local-part - - """ - local_part = LocalPart() - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - raise errors.HeaderParseError( - "expected local-part but found '{}'".format(value)) - try: - token, value = get_dot_atom(value) - except errors.HeaderParseError: - try: - token, value = get_word(value) - except errors.HeaderParseError: - if value[0] != '\\' and value[0] in PHRASE_ENDS: - raise - token = TokenList() - if leader is not None: - token[:0] = [leader] - local_part.append(token) - if value and (value[0]=='\\' or value[0] not in PHRASE_ENDS): - obs_local_part, value = get_obs_local_part(str(local_part) + value) - if obs_local_part.token_type == 'invalid-obs-local-part': - local_part.defects.append(errors.InvalidHeaderDefect( - "local-part is not dot-atom, quoted-string, or obs-local-part")) - else: - local_part.defects.append(errors.ObsoleteHeaderDefect( - "local-part is not a dot-atom (contains CFWS)")) - local_part[0] = obs_local_part - try: - local_part.value.encode('ascii') - except UnicodeEncodeError: - local_part.defects.append(errors.NonASCIILocalPartDefect( - "local-part contains non-ASCII characters)")) - return local_part, value - -def get_obs_local_part(value): - """ obs-local-part = word *("." word) - """ - obs_local_part = ObsLocalPart() - last_non_ws_was_dot = False - while value and (value[0]=='\\' or value[0] not in PHRASE_ENDS): - if value[0] == '.': - if last_non_ws_was_dot: - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "invalid repeated '.'")) - obs_local_part.append(DOT) - last_non_ws_was_dot = True - value = value[1:] - continue - elif value[0]=='\\': - obs_local_part.append(ValueTerminal(value[0], - 'misplaced-special')) - value = value[1:] - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "'\\' character outside of quoted-string/ccontent")) - last_non_ws_was_dot = False - continue - if obs_local_part and obs_local_part[-1].token_type != 'dot': - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "missing '.' between words")) - try: - token, value = get_word(value) - last_non_ws_was_dot = False - except errors.HeaderParseError: - if value[0] not in CFWS_LEADER: - raise - token, value = get_cfws(value) - obs_local_part.append(token) - if (obs_local_part[0].token_type == 'dot' or - obs_local_part[0].token_type=='cfws' and - obs_local_part[1].token_type=='dot'): - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "Invalid leading '.' in local part")) - if (obs_local_part[-1].token_type == 'dot' or - obs_local_part[-1].token_type=='cfws' and - obs_local_part[-2].token_type=='dot'): - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "Invalid trailing '.' in local part")) - if obs_local_part.defects: - obs_local_part.token_type = 'invalid-obs-local-part' - return obs_local_part, value - -def get_dtext(value): - """ dtext = / obs-dtext - obs-dtext = obs-NO-WS-CTL / quoted-pair - - We allow anything except the excluded characters, but if we find any - ASCII other than the RFC defined printable ASCII an NonPrintableDefect is - added to the token's defects list. Quoted pairs are converted to their - unquoted values, so what is returned is a ptext token, in this case a - ValueTerminal. If there were quoted-printables, an ObsoleteHeaderDefect is - added to the returned token's defect list. - - """ - ptext, value, had_qp = _get_ptext_to_endchars(value, '[]') - ptext = ValueTerminal(ptext, 'ptext') - if had_qp: - ptext.defects.append(errors.ObsoleteHeaderDefect( - "quoted printable found in domain-literal")) - _validate_xtext(ptext) - return ptext, value - -def _check_for_early_dl_end(value, domain_literal): - if value: - return False - domain_literal.append(errors.InvalidHeaderDefect( - "end of input inside domain-literal")) - domain_literal.append(ValueTerminal(']', 'domain-literal-end')) - return True - -def get_domain_literal(value): - """ domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS] - - """ - domain_literal = DomainLiteral() - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - domain_literal.append(token) - if not value: - raise errors.HeaderParseError("expected domain-literal") - if value[0] != '[': - raise errors.HeaderParseError("expected '[' at start of domain-literal " - "but found '{}'".format(value)) - value = value[1:] - if _check_for_early_dl_end(value, domain_literal): - return domain_literal, value - domain_literal.append(ValueTerminal('[', 'domain-literal-start')) - if value[0] in WSP: - token, value = get_fws(value) - domain_literal.append(token) - token, value = get_dtext(value) - domain_literal.append(token) - if _check_for_early_dl_end(value, domain_literal): - return domain_literal, value - if value[0] in WSP: - token, value = get_fws(value) - domain_literal.append(token) - if _check_for_early_dl_end(value, domain_literal): - return domain_literal, value - if value[0] != ']': - raise errors.HeaderParseError("expected ']' at end of domain-literal " - "but found '{}'".format(value)) - domain_literal.append(ValueTerminal(']', 'domain-literal-end')) - value = value[1:] - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - domain_literal.append(token) - return domain_literal, value - -def get_domain(value): - """ domain = dot-atom / domain-literal / obs-domain - obs-domain = atom *("." atom)) - - """ - domain = Domain() - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - raise errors.HeaderParseError( - "expected domain but found '{}'".format(value)) - if value[0] == '[': - token, value = get_domain_literal(value) - if leader is not None: - token[:0] = [leader] - domain.append(token) - return domain, value - try: - token, value = get_dot_atom(value) - except errors.HeaderParseError: - token, value = get_atom(value) - if leader is not None: - token[:0] = [leader] - domain.append(token) - if value and value[0] == '.': - domain.defects.append(errors.ObsoleteHeaderDefect( - "domain is not a dot-atom (contains CFWS)")) - if domain[0].token_type == 'dot-atom': - domain[:] = domain[0] - while value and value[0] == '.': - domain.append(DOT) - token, value = get_atom(value[1:]) - domain.append(token) - return domain, value - -def get_addr_spec(value): - """ addr-spec = local-part "@" domain - - """ - addr_spec = AddrSpec() - token, value = get_local_part(value) - addr_spec.append(token) - if not value or value[0] != '@': - addr_spec.defects.append(errors.InvalidHeaderDefect( - "add-spec local part with no domain")) - return addr_spec, value - addr_spec.append(ValueTerminal('@', 'address-at-symbol')) - token, value = get_domain(value[1:]) - addr_spec.append(token) - return addr_spec, value - -def get_obs_route(value): - """ obs-route = obs-domain-list ":" - obs-domain-list = *(CFWS / ",") "@" domain *("," [CFWS] ["@" domain]) - - Returns an obs-route token with the appropriate sub-tokens (that is, - there is no obs-domain-list in the parse tree). - """ - obs_route = ObsRoute() - while value and (value[0]==',' or value[0] in CFWS_LEADER): - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - obs_route.append(token) - elif value[0] == ',': - obs_route.append(ListSeparator) - value = value[1:] - if not value or value[0] != '@': - raise errors.HeaderParseError( - "expected obs-route domain but found '{}'".format(value)) - obs_route.append(RouteComponentMarker) - token, value = get_domain(value[1:]) - obs_route.append(token) - while value and value[0]==',': - obs_route.append(ListSeparator) - value = value[1:] - if not value: - break - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - obs_route.append(token) - if value[0] == '@': - obs_route.append(RouteComponentMarker) - token, value = get_domain(value[1:]) - obs_route.append(token) - if not value: - raise errors.HeaderParseError("end of header while parsing obs-route") - if value[0] != ':': - raise errors.HeaderParseError( "expected ':' marking end of " - "obs-route but found '{}'".format(value)) - obs_route.append(ValueTerminal(':', 'end-of-obs-route-marker')) - return obs_route, value[1:] - -def get_angle_addr(value): - """ angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr - obs-angle-addr = [CFWS] "<" obs-route addr-spec ">" [CFWS] - - """ - angle_addr = AngleAddr() - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - angle_addr.append(token) - if not value or value[0] != '<': - raise errors.HeaderParseError( - "expected angle-addr but found '{}'".format(value)) - angle_addr.append(ValueTerminal('<', 'angle-addr-start')) - value = value[1:] - # Although it is not legal per RFC5322, SMTP uses '<>' in certain - # circumstances. - if value[0] == '>': - angle_addr.append(ValueTerminal('>', 'angle-addr-end')) - angle_addr.defects.append(errors.InvalidHeaderDefect( - "null addr-spec in angle-addr")) - value = value[1:] - return angle_addr, value - try: - token, value = get_addr_spec(value) - except errors.HeaderParseError: - try: - token, value = get_obs_route(value) - angle_addr.defects.append(errors.ObsoleteHeaderDefect( - "obsolete route specification in angle-addr")) - except errors.HeaderParseError: - raise errors.HeaderParseError( - "expected addr-spec or obs-route but found '{}'".format(value)) - angle_addr.append(token) - token, value = get_addr_spec(value) - angle_addr.append(token) - if value and value[0] == '>': - value = value[1:] - else: - angle_addr.defects.append(errors.InvalidHeaderDefect( - "missing trailing '>' on angle-addr")) - angle_addr.append(ValueTerminal('>', 'angle-addr-end')) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - angle_addr.append(token) - return angle_addr, value - -def get_display_name(value): - """ display-name = phrase - - Because this is simply a name-rule, we don't return a display-name - token containing a phrase, but rather a display-name token with - the content of the phrase. - - """ - display_name = DisplayName() - token, value = get_phrase(value) - display_name.extend(token[:]) - display_name.defects = token.defects[:] - return display_name, value - - -def get_name_addr(value): - """ name-addr = [display-name] angle-addr - - """ - name_addr = NameAddr() - # Both the optional display name and the angle-addr can start with cfws. - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - raise errors.HeaderParseError( - "expected name-addr but found '{}'".format(leader)) - if value[0] != '<': - if value[0] in PHRASE_ENDS: - raise errors.HeaderParseError( - "expected name-addr but found '{}'".format(value)) - token, value = get_display_name(value) - if not value: - raise errors.HeaderParseError( - "expected name-addr but found '{}'".format(token)) - if leader is not None: - token[0][:0] = [leader] - leader = None - name_addr.append(token) - token, value = get_angle_addr(value) - if leader is not None: - token[:0] = [leader] - name_addr.append(token) - return name_addr, value - -def get_mailbox(value): - """ mailbox = name-addr / addr-spec - - """ - # The only way to figure out if we are dealing with a name-addr or an - # addr-spec is to try parsing each one. - mailbox = Mailbox() - try: - token, value = get_name_addr(value) - except errors.HeaderParseError: - try: - token, value = get_addr_spec(value) - except errors.HeaderParseError: - raise errors.HeaderParseError( - "expected mailbox but found '{}'".format(value)) - if any(isinstance(x, errors.InvalidHeaderDefect) - for x in token.all_defects): - mailbox.token_type = 'invalid-mailbox' - mailbox.append(token) - return mailbox, value - -def get_invalid_mailbox(value, endchars): - """ Read everything up to one of the chars in endchars. - - This is outside the formal grammar. The InvalidMailbox TokenList that is - returned acts like a Mailbox, but the data attributes are None. - - """ - invalid_mailbox = InvalidMailbox() - while value and value[0] not in endchars: - if value[0] in PHRASE_ENDS: - invalid_mailbox.append(ValueTerminal(value[0], - 'misplaced-special')) - value = value[1:] - else: - token, value = get_phrase(value) - invalid_mailbox.append(token) - return invalid_mailbox, value - -def get_mailbox_list(value): - """ mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list - obs-mbox-list = *([CFWS] ",") mailbox *("," [mailbox / CFWS]) - - For this routine we go outside the formal grammar in order to improve error - handling. We recognize the end of the mailbox list only at the end of the - value or at a ';' (the group terminator). This is so that we can turn - invalid mailboxes into InvalidMailbox tokens and continue parsing any - remaining valid mailboxes. We also allow all mailbox entries to be null, - and this condition is handled appropriately at a higher level. - - """ - mailbox_list = MailboxList() - while value and value[0] != ';': - try: - token, value = get_mailbox(value) - mailbox_list.append(token) - except errors.HeaderParseError: - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value or value[0] in ',;': - mailbox_list.append(leader) - mailbox_list.defects.append(errors.ObsoleteHeaderDefect( - "empty element in mailbox-list")) - else: - token, value = get_invalid_mailbox(value, ',;') - if leader is not None: - token[:0] = [leader] - mailbox_list.append(token) - mailbox_list.defects.append(errors.InvalidHeaderDefect( - "invalid mailbox in mailbox-list")) - elif value[0] == ',': - mailbox_list.defects.append(errors.ObsoleteHeaderDefect( - "empty element in mailbox-list")) - else: - token, value = get_invalid_mailbox(value, ',;') - if leader is not None: - token[:0] = [leader] - mailbox_list.append(token) - mailbox_list.defects.append(errors.InvalidHeaderDefect( - "invalid mailbox in mailbox-list")) - if value and value[0] not in ',;': - # Crap after mailbox; treat it as an invalid mailbox. - # The mailbox info will still be available. - mailbox = mailbox_list[-1] - mailbox.token_type = 'invalid-mailbox' - token, value = get_invalid_mailbox(value, ',;') - mailbox.extend(token) - mailbox_list.defects.append(errors.InvalidHeaderDefect( - "invalid mailbox in mailbox-list")) - if value and value[0] == ',': - mailbox_list.append(ListSeparator) - value = value[1:] - return mailbox_list, value - - -def get_group_list(value): - """ group-list = mailbox-list / CFWS / obs-group-list - obs-group-list = 1*([CFWS] ",") [CFWS] - - """ - group_list = GroupList() - if not value: - group_list.defects.append(errors.InvalidHeaderDefect( - "end of header before group-list")) - return group_list, value - leader = None - if value and value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - # This should never happen in email parsing, since CFWS-only is a - # legal alternative to group-list in a group, which is the only - # place group-list appears. - group_list.defects.append(errors.InvalidHeaderDefect( - "end of header in group-list")) - group_list.append(leader) - return group_list, value - if value[0] == ';': - group_list.append(leader) - return group_list, value - token, value = get_mailbox_list(value) - if len(token.all_mailboxes)==0: - if leader is not None: - group_list.append(leader) - group_list.extend(token) - group_list.defects.append(errors.ObsoleteHeaderDefect( - "group-list with empty entries")) - return group_list, value - if leader is not None: - token[:0] = [leader] - group_list.append(token) - return group_list, value - -def get_group(value): - """ group = display-name ":" [group-list] ";" [CFWS] - - """ - group = Group() - token, value = get_display_name(value) - if not value or value[0] != ':': - raise errors.HeaderParseError("expected ':' at end of group " - "display name but found '{}'".format(value)) - group.append(token) - group.append(ValueTerminal(':', 'group-display-name-terminator')) - value = value[1:] - if value and value[0] == ';': - group.append(ValueTerminal(';', 'group-terminator')) - return group, value[1:] - token, value = get_group_list(value) - group.append(token) - if not value: - group.defects.append(errors.InvalidHeaderDefect( - "end of header in group")) - if value[0] != ';': - raise errors.HeaderParseError( - "expected ';' at end of group but found {}".format(value)) - group.append(ValueTerminal(';', 'group-terminator')) - value = value[1:] - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - group.append(token) - return group, value - -def get_address(value): - """ address = mailbox / group - - Note that counter-intuitively, an address can be either a single address or - a list of addresses (a group). This is why the returned Address object has - a 'mailboxes' attribute which treats a single address as a list of length - one. When you need to differentiate between to two cases, extract the single - element, which is either a mailbox or a group token. - - """ - # The formal grammar isn't very helpful when parsing an address. mailbox - # and group, especially when allowing for obsolete forms, start off very - # similarly. It is only when you reach one of @, <, or : that you know - # what you've got. So, we try each one in turn, starting with the more - # likely of the two. We could perhaps make this more efficient by looking - # for a phrase and then branching based on the next character, but that - # would be a premature optimization. - address = Address() - try: - token, value = get_group(value) - except errors.HeaderParseError: - try: - token, value = get_mailbox(value) - except errors.HeaderParseError: - raise errors.HeaderParseError( - "expected address but found '{}'".format(value)) - address.append(token) - return address, value - -def get_address_list(value): - """ address_list = (address *("," address)) / obs-addr-list - obs-addr-list = *([CFWS] ",") address *("," [address / CFWS]) - - We depart from the formal grammar here by continuing to parse until the end - of the input, assuming the input to be entirely composed of an - address-list. This is always true in email parsing, and allows us - to skip invalid addresses to parse additional valid ones. - - """ - address_list = AddressList() - while value: - try: - token, value = get_address(value) - address_list.append(token) - except errors.HeaderParseError as err: - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value or value[0] == ',': - address_list.append(leader) - address_list.defects.append(errors.ObsoleteHeaderDefect( - "address-list entry with no content")) - else: - token, value = get_invalid_mailbox(value, ',') - if leader is not None: - token[:0] = [leader] - address_list.append(Address([token])) - address_list.defects.append(errors.InvalidHeaderDefect( - "invalid address in address-list")) - elif value[0] == ',': - address_list.defects.append(errors.ObsoleteHeaderDefect( - "empty element in address-list")) - else: - token, value = get_invalid_mailbox(value, ',') - if leader is not None: - token[:0] = [leader] - address_list.append(Address([token])) - address_list.defects.append(errors.InvalidHeaderDefect( - "invalid address in address-list")) - if value and value[0] != ',': - # Crap after address; treat it as an invalid mailbox. - # The mailbox info will still be available. - mailbox = address_list[-1][0] - mailbox.token_type = 'invalid-mailbox' - token, value = get_invalid_mailbox(value, ',') - mailbox.extend(token) - address_list.defects.append(errors.InvalidHeaderDefect( - "invalid address in address-list")) - if value: # Must be a , at this point. - address_list.append(ValueTerminal(',', 'list-separator')) - value = value[1:] - return address_list, value - -# -# XXX: As I begin to add additional header parsers, I'm realizing we probably -# have two level of parser routines: the get_XXX methods that get a token in -# the grammar, and parse_XXX methods that parse an entire field value. So -# get_address_list above should really be a parse_ method, as probably should -# be get_unstructured. -# - -def parse_mime_version(value): - """ mime-version = [CFWS] 1*digit [CFWS] "." [CFWS] 1*digit [CFWS] - - """ - # The [CFWS] is implicit in the RFC 2045 BNF. - # XXX: This routine is a bit verbose, should factor out a get_int method. - mime_version = MIMEVersion() - if not value: - mime_version.defects.append(errors.HeaderMissingRequiredValue( - "Missing MIME version number (eg: 1.0)")) - return mime_version - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mime_version.append(token) - if not value: - mime_version.defects.append(errors.HeaderMissingRequiredValue( - "Expected MIME version number but found only CFWS")) - digits = '' - while value and value[0] != '.' and value[0] not in CFWS_LEADER: - digits += value[0] - value = value[1:] - if not digits.isdigit(): - mime_version.defects.append(errors.InvalidHeaderDefect( - "Expected MIME major version number but found {!r}".format(digits))) - mime_version.append(ValueTerminal(digits, 'xtext')) - else: - mime_version.major = int(digits) - mime_version.append(ValueTerminal(digits, 'digits')) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mime_version.append(token) - if not value or value[0] != '.': - if mime_version.major is not None: - mime_version.defects.append(errors.InvalidHeaderDefect( - "Incomplete MIME version; found only major number")) - if value: - mime_version.append(ValueTerminal(value, 'xtext')) - return mime_version - mime_version.append(ValueTerminal('.', 'version-separator')) - value = value[1:] - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mime_version.append(token) - if not value: - if mime_version.major is not None: - mime_version.defects.append(errors.InvalidHeaderDefect( - "Incomplete MIME version; found only major number")) - return mime_version - digits = '' - while value and value[0] not in CFWS_LEADER: - digits += value[0] - value = value[1:] - if not digits.isdigit(): - mime_version.defects.append(errors.InvalidHeaderDefect( - "Expected MIME minor version number but found {!r}".format(digits))) - mime_version.append(ValueTerminal(digits, 'xtext')) - else: - mime_version.minor = int(digits) - mime_version.append(ValueTerminal(digits, 'digits')) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mime_version.append(token) - if value: - mime_version.defects.append(errors.InvalidHeaderDefect( - "Excess non-CFWS text after MIME version")) - mime_version.append(ValueTerminal(value, 'xtext')) - return mime_version - -def get_invalid_parameter(value): - """ Read everything up to the next ';'. - - This is outside the formal grammar. The InvalidParameter TokenList that is - returned acts like a Parameter, but the data attributes are None. - - """ - invalid_parameter = InvalidParameter() - while value and value[0] != ';': - if value[0] in PHRASE_ENDS: - invalid_parameter.append(ValueTerminal(value[0], - 'misplaced-special')) - value = value[1:] - else: - token, value = get_phrase(value) - invalid_parameter.append(token) - return invalid_parameter, value - -def get_ttext(value): - """ttext = - - We allow any non-TOKEN_ENDS in ttext, but add defects to the token's - defects list if we find non-ttext characters. We also register defects for - *any* non-printables even though the RFC doesn't exclude all of them, - because we follow the spirit of RFC 5322. - - """ - m = _non_token_end_matcher(value) - if not m: - raise errors.HeaderParseError( - "expected ttext but found '{}'".format(value)) - ttext = m.group() - value = value[len(ttext):] - ttext = ValueTerminal(ttext, 'ttext') - _validate_xtext(ttext) - return ttext, value - -def get_token(value): - """token = [CFWS] 1*ttext [CFWS] - - The RFC equivalent of ttext is any US-ASCII chars except space, ctls, or - tspecials. We also exclude tabs even though the RFC doesn't. - - The RFC implies the CFWS but is not explicit about it in the BNF. - - """ - mtoken = Token() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mtoken.append(token) - if value and value[0] in TOKEN_ENDS: - raise errors.HeaderParseError( - "expected token but found '{}'".format(value)) - token, value = get_ttext(value) - mtoken.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mtoken.append(token) - return mtoken, value - -def get_attrtext(value): - """attrtext = 1*(any non-ATTRIBUTE_ENDS character) - - We allow any non-ATTRIBUTE_ENDS in attrtext, but add defects to the - token's defects list if we find non-attrtext characters. We also register - defects for *any* non-printables even though the RFC doesn't exclude all of - them, because we follow the spirit of RFC 5322. - - """ - m = _non_attribute_end_matcher(value) - if not m: - raise errors.HeaderParseError( - "expected attrtext but found {!r}".format(value)) - attrtext = m.group() - value = value[len(attrtext):] - attrtext = ValueTerminal(attrtext, 'attrtext') - _validate_xtext(attrtext) - return attrtext, value - -def get_attribute(value): - """ [CFWS] 1*attrtext [CFWS] - - This version of the BNF makes the CFWS explicit, and as usual we use a - value terminal for the actual run of characters. The RFC equivalent of - attrtext is the token characters, with the subtraction of '*', "'", and '%'. - We include tab in the excluded set just as we do for token. - - """ - attribute = Attribute() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - attribute.append(token) - if value and value[0] in ATTRIBUTE_ENDS: - raise errors.HeaderParseError( - "expected token but found '{}'".format(value)) - token, value = get_attrtext(value) - attribute.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - attribute.append(token) - return attribute, value - -def get_extended_attrtext(value): - """attrtext = 1*(any non-ATTRIBUTE_ENDS character plus '%') - - This is a special parsing routine so that we get a value that - includes % escapes as a single string (which we decode as a single - string later). - - """ - m = _non_extended_attribute_end_matcher(value) - if not m: - raise errors.HeaderParseError( - "expected extended attrtext but found {!r}".format(value)) - attrtext = m.group() - value = value[len(attrtext):] - attrtext = ValueTerminal(attrtext, 'extended-attrtext') - _validate_xtext(attrtext) - return attrtext, value - -def get_extended_attribute(value): - """ [CFWS] 1*extended_attrtext [CFWS] - - This is like the non-extended version except we allow % characters, so that - we can pick up an encoded value as a single string. - - """ - # XXX: should we have an ExtendedAttribute TokenList? - attribute = Attribute() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - attribute.append(token) - if value and value[0] in EXTENDED_ATTRIBUTE_ENDS: - raise errors.HeaderParseError( - "expected token but found '{}'".format(value)) - token, value = get_extended_attrtext(value) - attribute.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - attribute.append(token) - return attribute, value - -def get_section(value): - """ '*' digits - - The formal BNF is more complicated because leading 0s are not allowed. We - check for that and add a defect. We also assume no CFWS is allowed between - the '*' and the digits, though the RFC is not crystal clear on that. - The caller should already have dealt with leading CFWS. - - """ - section = Section() - if not value or value[0] != '*': - raise errors.HeaderParseError("Expected section but found {}".format( - value)) - section.append(ValueTerminal('*', 'section-marker')) - value = value[1:] - if not value or not value[0].isdigit(): - raise errors.HeaderParseError("Expected section number but " - "found {}".format(value)) - digits = '' - while value and value[0].isdigit(): - digits += value[0] - value = value[1:] - if digits[0] == '0' and digits != '0': - section.defects.append(errors.InvalidHeaderError("section number" - "has an invalid leading 0")) - section.number = int(digits) - section.append(ValueTerminal(digits, 'digits')) - return section, value - - -def get_value(value): - """ quoted-string / attribute - - """ - v = Value() - if not value: - raise errors.HeaderParseError("Expected value but found end of string") - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - raise errors.HeaderParseError("Expected value but found " - "only {}".format(leader)) - if value[0] == '"': - token, value = get_quoted_string(value) - else: - token, value = get_extended_attribute(value) - if leader is not None: - token[:0] = [leader] - v.append(token) - return v, value - -def get_parameter(value): - """ attribute [section] ["*"] [CFWS] "=" value - - The CFWS is implied by the RFC but not made explicit in the BNF. This - simplified form of the BNF from the RFC is made to conform with the RFC BNF - through some extra checks. We do it this way because it makes both error - recovery and working with the resulting parse tree easier. - """ - # It is possible CFWS would also be implicitly allowed between the section - # and the 'extended-attribute' marker (the '*') , but we've never seen that - # in the wild and we will therefore ignore the possibility. - param = Parameter() - token, value = get_attribute(value) - param.append(token) - if not value or value[0] == ';': - param.defects.append(errors.InvalidHeaderDefect("Parameter contains " - "name ({}) but no value".format(token))) - return param, value - if value[0] == '*': - try: - token, value = get_section(value) - param.sectioned = True - param.append(token) - except errors.HeaderParseError: - pass - if not value: - raise errors.HeaderParseError("Incomplete parameter") - if value[0] == '*': - param.append(ValueTerminal('*', 'extended-parameter-marker')) - value = value[1:] - param.extended = True - if value[0] != '=': - raise errors.HeaderParseError("Parameter not followed by '='") - param.append(ValueTerminal('=', 'parameter-separator')) - value = value[1:] - leader = None - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - param.append(token) - remainder = None - appendto = param - if param.extended and value and value[0] == '"': - # Now for some serious hackery to handle the common invalid case of - # double quotes around an extended value. We also accept (with defect) - # a value marked as encoded that isn't really. - qstring, remainder = get_quoted_string(value) - inner_value = qstring.stripped_value - semi_valid = False - if param.section_number == 0: - if inner_value and inner_value[0] == "'": - semi_valid = True - else: - token, rest = get_attrtext(inner_value) - if rest and rest[0] == "'": - semi_valid = True - else: - try: - token, rest = get_extended_attrtext(inner_value) - except: - pass - else: - if not rest: - semi_valid = True - if semi_valid: - param.defects.append(errors.InvalidHeaderDefect( - "Quoted string value for extended parameter is invalid")) - param.append(qstring) - for t in qstring: - if t.token_type == 'bare-quoted-string': - t[:] = [] - appendto = t - break - value = inner_value - else: - remainder = None - param.defects.append(errors.InvalidHeaderDefect( - "Parameter marked as extended but appears to have a " - "quoted string value that is non-encoded")) - if value and value[0] == "'": - token = None - else: - token, value = get_value(value) - if not param.extended or param.section_number > 0: - if not value or value[0] != "'": - appendto.append(token) - if remainder is not None: - assert not value, value - value = remainder - return param, value - param.defects.append(errors.InvalidHeaderDefect( - "Apparent initial-extended-value but attribute " - "was not marked as extended or was not initial section")) - if not value: - # Assume the charset/lang is missing and the token is the value. - param.defects.append(errors.InvalidHeaderDefect( - "Missing required charset/lang delimiters")) - appendto.append(token) - if remainder is None: - return param, value - else: - if token is not None: - for t in token: - if t.token_type == 'extended-attrtext': - break - t.token_type == 'attrtext' - appendto.append(t) - param.charset = t.value - if value[0] != "'": - raise errors.HeaderParseError("Expected RFC2231 char/lang encoding " - "delimiter, but found {!r}".format(value)) - appendto.append(ValueTerminal("'", 'RFC2231 delimiter')) - value = value[1:] - if value and value[0] != "'": - token, value = get_attrtext(value) - appendto.append(token) - param.lang = token.value - if not value or value[0] != "'": - raise errors.HeaderParseError("Expected RFC2231 char/lang encoding " - "delimiter, but found {}".format(value)) - appendto.append(ValueTerminal("'", 'RFC2231 delimiter')) - value = value[1:] - if remainder is not None: - # Treat the rest of value as bare quoted string content. - v = Value() - while value: - if value[0] in WSP: - token, value = get_fws(value) - else: - token, value = get_qcontent(value) - v.append(token) - token = v - else: - token, value = get_value(value) - appendto.append(token) - if remainder is not None: - assert not value, value - value = remainder - return param, value - -def parse_mime_parameters(value): - """ parameter *( ";" parameter ) - - That BNF is meant to indicate this routine should only be called after - finding and handling the leading ';'. There is no corresponding rule in - the formal RFC grammar, but it is more convenient for us for the set of - parameters to be treated as its own TokenList. - - This is 'parse' routine because it consumes the reminaing value, but it - would never be called to parse a full header. Instead it is called to - parse everything after the non-parameter value of a specific MIME header. - - """ - mime_parameters = MimeParameters() - while value: - try: - token, value = get_parameter(value) - mime_parameters.append(token) - except errors.HeaderParseError as err: - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - mime_parameters.append(leader) - return mime_parameters - if value[0] == ';': - if leader is not None: - mime_parameters.append(leader) - mime_parameters.defects.append(errors.InvalidHeaderDefect( - "parameter entry with no content")) - else: - token, value = get_invalid_parameter(value) - if leader: - token[:0] = [leader] - mime_parameters.append(token) - mime_parameters.defects.append(errors.InvalidHeaderDefect( - "invalid parameter {!r}".format(token))) - if value and value[0] != ';': - # Junk after the otherwise valid parameter. Mark it as - # invalid, but it will have a value. - param = mime_parameters[-1] - param.token_type = 'invalid-parameter' - token, value = get_invalid_parameter(value) - param.extend(token) - mime_parameters.defects.append(errors.InvalidHeaderDefect( - "parameter with invalid trailing text {!r}".format(token))) - if value: - # Must be a ';' at this point. - mime_parameters.append(ValueTerminal(';', 'parameter-separator')) - value = value[1:] - return mime_parameters - -def _find_mime_parameters(tokenlist, value): - """Do our best to find the parameters in an invalid MIME header - - """ - while value and value[0] != ';': - if value[0] in PHRASE_ENDS: - tokenlist.append(ValueTerminal(value[0], 'misplaced-special')) - value = value[1:] - else: - token, value = get_phrase(value) - tokenlist.append(token) - if not value: - return - tokenlist.append(ValueTerminal(';', 'parameter-separator')) - tokenlist.append(parse_mime_parameters(value[1:])) - -def parse_content_type_header(value): - """ maintype "/" subtype *( ";" parameter ) - - The maintype and substype are tokens. Theoretically they could - be checked against the official IANA list + x-token, but we - don't do that. - """ - ctype = ContentType() - recover = False - if not value: - ctype.defects.append(errors.HeaderMissingRequiredValue( - "Missing content type specification")) - return ctype - try: - token, value = get_token(value) - except errors.HeaderParseError: - ctype.defects.append(errors.InvalidHeaderDefect( - "Expected content maintype but found {!r}".format(value))) - _find_mime_parameters(ctype, value) - return ctype - ctype.append(token) - # XXX: If we really want to follow the formal grammer we should make - # mantype and subtype specialized TokenLists here. Probably not worth it. - if not value or value[0] != '/': - ctype.defects.append(errors.InvalidHeaderDefect( - "Invalid content type")) - if value: - _find_mime_parameters(ctype, value) - return ctype - ctype.maintype = token.value.strip().lower() - ctype.append(ValueTerminal('/', 'content-type-separator')) - value = value[1:] - try: - token, value = get_token(value) - except errors.HeaderParseError: - ctype.defects.append(errors.InvalidHeaderDefect( - "Expected content subtype but found {!r}".format(value))) - _find_mime_parameters(ctype, value) - return ctype - ctype.append(token) - ctype.subtype = token.value.strip().lower() - if not value: - return ctype - if value[0] != ';': - ctype.defects.append(errors.InvalidHeaderDefect( - "Only parameters are valid after content type, but " - "found {!r}".format(value))) - # The RFC requires that a syntactically invalid content-type be treated - # as text/plain. Perhaps we should postel this, but we should probably - # only do that if we were checking the subtype value against IANA. - del ctype.maintype, ctype.subtype - _find_mime_parameters(ctype, value) - return ctype - ctype.append(ValueTerminal(';', 'parameter-separator')) - ctype.append(parse_mime_parameters(value[1:])) - return ctype - -def parse_content_disposition_header(value): - """ disposition-type *( ";" parameter ) - - """ - disp_header = ContentDisposition() - if not value: - disp_header.defects.append(errors.HeaderMissingRequiredValue( - "Missing content disposition")) - return disp_header - try: - token, value = get_token(value) - except errors.HeaderParseError: - ctype.defects.append(errors.InvalidHeaderDefect( - "Expected content disposition but found {!r}".format(value))) - _find_mime_parameters(disp_header, value) - return disp_header - disp_header.append(token) - disp_header.content_disposition = token.value.strip().lower() - if not value: - return disp_header - if value[0] != ';': - disp_header.defects.append(errors.InvalidHeaderDefect( - "Only parameters are valid after content disposition, but " - "found {!r}".format(value))) - _find_mime_parameters(disp_header, value) - return disp_header - disp_header.append(ValueTerminal(';', 'parameter-separator')) - disp_header.append(parse_mime_parameters(value[1:])) - return disp_header - -def parse_content_transfer_encoding_header(value): - """ mechanism - - """ - # We should probably validate the values, since the list is fixed. - cte_header = ContentTransferEncoding() - if not value: - cte_header.defects.append(errors.HeaderMissingRequiredValue( - "Missing content transfer encoding")) - return cte_header - try: - token, value = get_token(value) - except errors.HeaderParseError: - ctype.defects.append(errors.InvalidHeaderDefect( - "Expected content trnasfer encoding but found {!r}".format(value))) - else: - cte_header.append(token) - cte_header.cte = token.value.strip().lower() - if not value: - return cte_header - while value: - cte_header.defects.append(errors.InvalidHeaderDefect( - "Extra text after content transfer encoding")) - if value[0] in PHRASE_ENDS: - cte_header.append(ValueTerminal(value[0], 'misplaced-special')) - value = value[1:] - else: - token, value = get_phrase(value) - cte_header.append(token) - return cte_header diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_parseaddr.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_parseaddr.py deleted file mode 100644 index 5b50cc6b..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_parseaddr.py +++ /dev/null @@ -1,546 +0,0 @@ -# Copyright (C) 2002-2007 Python Software Foundation -# Contact: email-sig@python.org - -"""Email address parsing code. - -Lifted directly from rfc822.py. This should eventually be rewritten. -""" - -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import -from future.builtins import int - -__all__ = [ - 'mktime_tz', - 'parsedate', - 'parsedate_tz', - 'quote', - ] - -import time, calendar - -SPACE = ' ' -EMPTYSTRING = '' -COMMASPACE = ', ' - -# Parse a date field -_monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', - 'aug', 'sep', 'oct', 'nov', 'dec', - 'january', 'february', 'march', 'april', 'may', 'june', 'july', - 'august', 'september', 'october', 'november', 'december'] - -_daynames = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] - -# The timezone table does not include the military time zones defined -# in RFC822, other than Z. According to RFC1123, the description in -# RFC822 gets the signs wrong, so we can't rely on any such time -# zones. RFC1123 recommends that numeric timezone indicators be used -# instead of timezone names. - -_timezones = {'UT':0, 'UTC':0, 'GMT':0, 'Z':0, - 'AST': -400, 'ADT': -300, # Atlantic (used in Canada) - 'EST': -500, 'EDT': -400, # Eastern - 'CST': -600, 'CDT': -500, # Central - 'MST': -700, 'MDT': -600, # Mountain - 'PST': -800, 'PDT': -700 # Pacific - } - - -def parsedate_tz(data): - """Convert a date string to a time tuple. - - Accounts for military timezones. - """ - res = _parsedate_tz(data) - if not res: - return - if res[9] is None: - res[9] = 0 - return tuple(res) - -def _parsedate_tz(data): - """Convert date to extended time tuple. - - The last (additional) element is the time zone offset in seconds, except if - the timezone was specified as -0000. In that case the last element is - None. This indicates a UTC timestamp that explicitly declaims knowledge of - the source timezone, as opposed to a +0000 timestamp that indicates the - source timezone really was UTC. - - """ - if not data: - return - data = data.split() - # The FWS after the comma after the day-of-week is optional, so search and - # adjust for this. - if data[0].endswith(',') or data[0].lower() in _daynames: - # There's a dayname here. Skip it - del data[0] - else: - i = data[0].rfind(',') - if i >= 0: - data[0] = data[0][i+1:] - if len(data) == 3: # RFC 850 date, deprecated - stuff = data[0].split('-') - if len(stuff) == 3: - data = stuff + data[1:] - if len(data) == 4: - s = data[3] - i = s.find('+') - if i == -1: - i = s.find('-') - if i > 0: - data[3:] = [s[:i], s[i:]] - else: - data.append('') # Dummy tz - if len(data) < 5: - return None - data = data[:5] - [dd, mm, yy, tm, tz] = data - mm = mm.lower() - if mm not in _monthnames: - dd, mm = mm, dd.lower() - if mm not in _monthnames: - return None - mm = _monthnames.index(mm) + 1 - if mm > 12: - mm -= 12 - if dd[-1] == ',': - dd = dd[:-1] - i = yy.find(':') - if i > 0: - yy, tm = tm, yy - if yy[-1] == ',': - yy = yy[:-1] - if not yy[0].isdigit(): - yy, tz = tz, yy - if tm[-1] == ',': - tm = tm[:-1] - tm = tm.split(':') - if len(tm) == 2: - [thh, tmm] = tm - tss = '0' - elif len(tm) == 3: - [thh, tmm, tss] = tm - elif len(tm) == 1 and '.' in tm[0]: - # Some non-compliant MUAs use '.' to separate time elements. - tm = tm[0].split('.') - if len(tm) == 2: - [thh, tmm] = tm - tss = 0 - elif len(tm) == 3: - [thh, tmm, tss] = tm - else: - return None - try: - yy = int(yy) - dd = int(dd) - thh = int(thh) - tmm = int(tmm) - tss = int(tss) - except ValueError: - return None - # Check for a yy specified in two-digit format, then convert it to the - # appropriate four-digit format, according to the POSIX standard. RFC 822 - # calls for a two-digit yy, but RFC 2822 (which obsoletes RFC 822) - # mandates a 4-digit yy. For more information, see the documentation for - # the time module. - if yy < 100: - # The year is between 1969 and 1999 (inclusive). - if yy > 68: - yy += 1900 - # The year is between 2000 and 2068 (inclusive). - else: - yy += 2000 - tzoffset = None - tz = tz.upper() - if tz in _timezones: - tzoffset = _timezones[tz] - else: - try: - tzoffset = int(tz) - except ValueError: - pass - if tzoffset==0 and tz.startswith('-'): - tzoffset = None - # Convert a timezone offset into seconds ; -0500 -> -18000 - if tzoffset: - if tzoffset < 0: - tzsign = -1 - tzoffset = -tzoffset - else: - tzsign = 1 - tzoffset = tzsign * ( (tzoffset//100)*3600 + (tzoffset % 100)*60) - # Daylight Saving Time flag is set to -1, since DST is unknown. - return [yy, mm, dd, thh, tmm, tss, 0, 1, -1, tzoffset] - - -def parsedate(data): - """Convert a time string to a time tuple.""" - t = parsedate_tz(data) - if isinstance(t, tuple): - return t[:9] - else: - return t - - -def mktime_tz(data): - """Turn a 10-tuple as returned by parsedate_tz() into a POSIX timestamp.""" - if data[9] is None: - # No zone info, so localtime is better assumption than GMT - return time.mktime(data[:8] + (-1,)) - else: - t = calendar.timegm(data) - return t - data[9] - - -def quote(str): - """Prepare string to be used in a quoted string. - - Turns backslash and double quote characters into quoted pairs. These - are the only characters that need to be quoted inside a quoted string. - Does not add the surrounding double quotes. - """ - return str.replace('\\', '\\\\').replace('"', '\\"') - - -class AddrlistClass(object): - """Address parser class by Ben Escoto. - - To understand what this class does, it helps to have a copy of RFC 2822 in - front of you. - - Note: this class interface is deprecated and may be removed in the future. - Use email.utils.AddressList instead. - """ - - def __init__(self, field): - """Initialize a new instance. - - `field' is an unparsed address header field, containing - one or more addresses. - """ - self.specials = '()<>@,:;.\"[]' - self.pos = 0 - self.LWS = ' \t' - self.CR = '\r\n' - self.FWS = self.LWS + self.CR - self.atomends = self.specials + self.LWS + self.CR - # Note that RFC 2822 now specifies `.' as obs-phrase, meaning that it - # is obsolete syntax. RFC 2822 requires that we recognize obsolete - # syntax, so allow dots in phrases. - self.phraseends = self.atomends.replace('.', '') - self.field = field - self.commentlist = [] - - def gotonext(self): - """Skip white space and extract comments.""" - wslist = [] - while self.pos < len(self.field): - if self.field[self.pos] in self.LWS + '\n\r': - if self.field[self.pos] not in '\n\r': - wslist.append(self.field[self.pos]) - self.pos += 1 - elif self.field[self.pos] == '(': - self.commentlist.append(self.getcomment()) - else: - break - return EMPTYSTRING.join(wslist) - - def getaddrlist(self): - """Parse all addresses. - - Returns a list containing all of the addresses. - """ - result = [] - while self.pos < len(self.field): - ad = self.getaddress() - if ad: - result += ad - else: - result.append(('', '')) - return result - - def getaddress(self): - """Parse the next address.""" - self.commentlist = [] - self.gotonext() - - oldpos = self.pos - oldcl = self.commentlist - plist = self.getphraselist() - - self.gotonext() - returnlist = [] - - if self.pos >= len(self.field): - # Bad email address technically, no domain. - if plist: - returnlist = [(SPACE.join(self.commentlist), plist[0])] - - elif self.field[self.pos] in '.@': - # email address is just an addrspec - # this isn't very efficient since we start over - self.pos = oldpos - self.commentlist = oldcl - addrspec = self.getaddrspec() - returnlist = [(SPACE.join(self.commentlist), addrspec)] - - elif self.field[self.pos] == ':': - # address is a group - returnlist = [] - - fieldlen = len(self.field) - self.pos += 1 - while self.pos < len(self.field): - self.gotonext() - if self.pos < fieldlen and self.field[self.pos] == ';': - self.pos += 1 - break - returnlist = returnlist + self.getaddress() - - elif self.field[self.pos] == '<': - # Address is a phrase then a route addr - routeaddr = self.getrouteaddr() - - if self.commentlist: - returnlist = [(SPACE.join(plist) + ' (' + - ' '.join(self.commentlist) + ')', routeaddr)] - else: - returnlist = [(SPACE.join(plist), routeaddr)] - - else: - if plist: - returnlist = [(SPACE.join(self.commentlist), plist[0])] - elif self.field[self.pos] in self.specials: - self.pos += 1 - - self.gotonext() - if self.pos < len(self.field) and self.field[self.pos] == ',': - self.pos += 1 - return returnlist - - def getrouteaddr(self): - """Parse a route address (Return-path value). - - This method just skips all the route stuff and returns the addrspec. - """ - if self.field[self.pos] != '<': - return - - expectroute = False - self.pos += 1 - self.gotonext() - adlist = '' - while self.pos < len(self.field): - if expectroute: - self.getdomain() - expectroute = False - elif self.field[self.pos] == '>': - self.pos += 1 - break - elif self.field[self.pos] == '@': - self.pos += 1 - expectroute = True - elif self.field[self.pos] == ':': - self.pos += 1 - else: - adlist = self.getaddrspec() - self.pos += 1 - break - self.gotonext() - - return adlist - - def getaddrspec(self): - """Parse an RFC 2822 addr-spec.""" - aslist = [] - - self.gotonext() - while self.pos < len(self.field): - preserve_ws = True - if self.field[self.pos] == '.': - if aslist and not aslist[-1].strip(): - aslist.pop() - aslist.append('.') - self.pos += 1 - preserve_ws = False - elif self.field[self.pos] == '"': - aslist.append('"%s"' % quote(self.getquote())) - elif self.field[self.pos] in self.atomends: - if aslist and not aslist[-1].strip(): - aslist.pop() - break - else: - aslist.append(self.getatom()) - ws = self.gotonext() - if preserve_ws and ws: - aslist.append(ws) - - if self.pos >= len(self.field) or self.field[self.pos] != '@': - return EMPTYSTRING.join(aslist) - - aslist.append('@') - self.pos += 1 - self.gotonext() - return EMPTYSTRING.join(aslist) + self.getdomain() - - def getdomain(self): - """Get the complete domain name from an address.""" - sdlist = [] - while self.pos < len(self.field): - if self.field[self.pos] in self.LWS: - self.pos += 1 - elif self.field[self.pos] == '(': - self.commentlist.append(self.getcomment()) - elif self.field[self.pos] == '[': - sdlist.append(self.getdomainliteral()) - elif self.field[self.pos] == '.': - self.pos += 1 - sdlist.append('.') - elif self.field[self.pos] in self.atomends: - break - else: - sdlist.append(self.getatom()) - return EMPTYSTRING.join(sdlist) - - def getdelimited(self, beginchar, endchars, allowcomments=True): - """Parse a header fragment delimited by special characters. - - `beginchar' is the start character for the fragment. - If self is not looking at an instance of `beginchar' then - getdelimited returns the empty string. - - `endchars' is a sequence of allowable end-delimiting characters. - Parsing stops when one of these is encountered. - - If `allowcomments' is non-zero, embedded RFC 2822 comments are allowed - within the parsed fragment. - """ - if self.field[self.pos] != beginchar: - return '' - - slist = [''] - quote = False - self.pos += 1 - while self.pos < len(self.field): - if quote: - slist.append(self.field[self.pos]) - quote = False - elif self.field[self.pos] in endchars: - self.pos += 1 - break - elif allowcomments and self.field[self.pos] == '(': - slist.append(self.getcomment()) - continue # have already advanced pos from getcomment - elif self.field[self.pos] == '\\': - quote = True - else: - slist.append(self.field[self.pos]) - self.pos += 1 - - return EMPTYSTRING.join(slist) - - def getquote(self): - """Get a quote-delimited fragment from self's field.""" - return self.getdelimited('"', '"\r', False) - - def getcomment(self): - """Get a parenthesis-delimited fragment from self's field.""" - return self.getdelimited('(', ')\r', True) - - def getdomainliteral(self): - """Parse an RFC 2822 domain-literal.""" - return '[%s]' % self.getdelimited('[', ']\r', False) - - def getatom(self, atomends=None): - """Parse an RFC 2822 atom. - - Optional atomends specifies a different set of end token delimiters - (the default is to use self.atomends). This is used e.g. in - getphraselist() since phrase endings must not include the `.' (which - is legal in phrases).""" - atomlist = [''] - if atomends is None: - atomends = self.atomends - - while self.pos < len(self.field): - if self.field[self.pos] in atomends: - break - else: - atomlist.append(self.field[self.pos]) - self.pos += 1 - - return EMPTYSTRING.join(atomlist) - - def getphraselist(self): - """Parse a sequence of RFC 2822 phrases. - - A phrase is a sequence of words, which are in turn either RFC 2822 - atoms or quoted-strings. Phrases are canonicalized by squeezing all - runs of continuous whitespace into one space. - """ - plist = [] - - while self.pos < len(self.field): - if self.field[self.pos] in self.FWS: - self.pos += 1 - elif self.field[self.pos] == '"': - plist.append(self.getquote()) - elif self.field[self.pos] == '(': - self.commentlist.append(self.getcomment()) - elif self.field[self.pos] in self.phraseends: - break - else: - plist.append(self.getatom(self.phraseends)) - - return plist - -class AddressList(AddrlistClass): - """An AddressList encapsulates a list of parsed RFC 2822 addresses.""" - def __init__(self, field): - AddrlistClass.__init__(self, field) - if field: - self.addresslist = self.getaddrlist() - else: - self.addresslist = [] - - def __len__(self): - return len(self.addresslist) - - def __add__(self, other): - # Set union - newaddr = AddressList(None) - newaddr.addresslist = self.addresslist[:] - for x in other.addresslist: - if not x in self.addresslist: - newaddr.addresslist.append(x) - return newaddr - - def __iadd__(self, other): - # Set union, in-place - for x in other.addresslist: - if not x in self.addresslist: - self.addresslist.append(x) - return self - - def __sub__(self, other): - # Set difference - newaddr = AddressList(None) - for x in self.addresslist: - if not x in other.addresslist: - newaddr.addresslist.append(x) - return newaddr - - def __isub__(self, other): - # Set difference, in-place - for x in other.addresslist: - if x in self.addresslist: - self.addresslist.remove(x) - return self - - def __getitem__(self, index): - # Make indexing, slices, and 'in' work - return self.addresslist[index] diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_policybase.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_policybase.py deleted file mode 100644 index c66aea90..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/_policybase.py +++ /dev/null @@ -1,365 +0,0 @@ -"""Policy framework for the email package. - -Allows fine grained feature control of how the package parses and emits data. -""" -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import -from future.builtins import super -from future.builtins import str -from future.utils import with_metaclass - -import abc -from future.backports.email import header -from future.backports.email import charset as _charset -from future.backports.email.utils import _has_surrogates - -__all__ = [ - 'Policy', - 'Compat32', - 'compat32', - ] - - -class _PolicyBase(object): - - """Policy Object basic framework. - - This class is useless unless subclassed. A subclass should define - class attributes with defaults for any values that are to be - managed by the Policy object. The constructor will then allow - non-default values to be set for these attributes at instance - creation time. The instance will be callable, taking these same - attributes keyword arguments, and returning a new instance - identical to the called instance except for those values changed - by the keyword arguments. Instances may be added, yielding new - instances with any non-default values from the right hand - operand overriding those in the left hand operand. That is, - - A + B == A() - - The repr of an instance can be used to reconstruct the object - if and only if the repr of the values can be used to reconstruct - those values. - - """ - - def __init__(self, **kw): - """Create new Policy, possibly overriding some defaults. - - See class docstring for a list of overridable attributes. - - """ - for name, value in kw.items(): - if hasattr(self, name): - super(_PolicyBase,self).__setattr__(name, value) - else: - raise TypeError( - "{!r} is an invalid keyword argument for {}".format( - name, self.__class__.__name__)) - - def __repr__(self): - args = [ "{}={!r}".format(name, value) - for name, value in self.__dict__.items() ] - return "{}({})".format(self.__class__.__name__, ', '.join(args)) - - def clone(self, **kw): - """Return a new instance with specified attributes changed. - - The new instance has the same attribute values as the current object, - except for the changes passed in as keyword arguments. - - """ - newpolicy = self.__class__.__new__(self.__class__) - for attr, value in self.__dict__.items(): - object.__setattr__(newpolicy, attr, value) - for attr, value in kw.items(): - if not hasattr(self, attr): - raise TypeError( - "{!r} is an invalid keyword argument for {}".format( - attr, self.__class__.__name__)) - object.__setattr__(newpolicy, attr, value) - return newpolicy - - def __setattr__(self, name, value): - if hasattr(self, name): - msg = "{!r} object attribute {!r} is read-only" - else: - msg = "{!r} object has no attribute {!r}" - raise AttributeError(msg.format(self.__class__.__name__, name)) - - def __add__(self, other): - """Non-default values from right operand override those from left. - - The object returned is a new instance of the subclass. - - """ - return self.clone(**other.__dict__) - - -def _append_doc(doc, added_doc): - doc = doc.rsplit('\n', 1)[0] - added_doc = added_doc.split('\n', 1)[1] - return doc + '\n' + added_doc - -def _extend_docstrings(cls): - if cls.__doc__ and cls.__doc__.startswith('+'): - cls.__doc__ = _append_doc(cls.__bases__[0].__doc__, cls.__doc__) - for name, attr in cls.__dict__.items(): - if attr.__doc__ and attr.__doc__.startswith('+'): - for c in (c for base in cls.__bases__ for c in base.mro()): - doc = getattr(getattr(c, name), '__doc__') - if doc: - attr.__doc__ = _append_doc(doc, attr.__doc__) - break - return cls - - -class Policy(with_metaclass(abc.ABCMeta, _PolicyBase)): - - r"""Controls for how messages are interpreted and formatted. - - Most of the classes and many of the methods in the email package accept - Policy objects as parameters. A Policy object contains a set of values and - functions that control how input is interpreted and how output is rendered. - For example, the parameter 'raise_on_defect' controls whether or not an RFC - violation results in an error being raised or not, while 'max_line_length' - controls the maximum length of output lines when a Message is serialized. - - Any valid attribute may be overridden when a Policy is created by passing - it as a keyword argument to the constructor. Policy objects are immutable, - but a new Policy object can be created with only certain values changed by - calling the Policy instance with keyword arguments. Policy objects can - also be added, producing a new Policy object in which the non-default - attributes set in the right hand operand overwrite those specified in the - left operand. - - Settable attributes: - - raise_on_defect -- If true, then defects should be raised as errors. - Default: False. - - linesep -- string containing the value to use as separation - between output lines. Default '\n'. - - cte_type -- Type of allowed content transfer encodings - - 7bit -- ASCII only - 8bit -- Content-Transfer-Encoding: 8bit is allowed - - Default: 8bit. Also controls the disposition of - (RFC invalid) binary data in headers; see the - documentation of the binary_fold method. - - max_line_length -- maximum length of lines, excluding 'linesep', - during serialization. None or 0 means no line - wrapping is done. Default is 78. - - """ - - raise_on_defect = False - linesep = '\n' - cte_type = '8bit' - max_line_length = 78 - - def handle_defect(self, obj, defect): - """Based on policy, either raise defect or call register_defect. - - handle_defect(obj, defect) - - defect should be a Defect subclass, but in any case must be an - Exception subclass. obj is the object on which the defect should be - registered if it is not raised. If the raise_on_defect is True, the - defect is raised as an error, otherwise the object and the defect are - passed to register_defect. - - This method is intended to be called by parsers that discover defects. - The email package parsers always call it with Defect instances. - - """ - if self.raise_on_defect: - raise defect - self.register_defect(obj, defect) - - def register_defect(self, obj, defect): - """Record 'defect' on 'obj'. - - Called by handle_defect if raise_on_defect is False. This method is - part of the Policy API so that Policy subclasses can implement custom - defect handling. The default implementation calls the append method of - the defects attribute of obj. The objects used by the email package by - default that get passed to this method will always have a defects - attribute with an append method. - - """ - obj.defects.append(defect) - - def header_max_count(self, name): - """Return the maximum allowed number of headers named 'name'. - - Called when a header is added to a Message object. If the returned - value is not 0 or None, and there are already a number of headers with - the name 'name' equal to the value returned, a ValueError is raised. - - Because the default behavior of Message's __setitem__ is to append the - value to the list of headers, it is easy to create duplicate headers - without realizing it. This method allows certain headers to be limited - in the number of instances of that header that may be added to a - Message programmatically. (The limit is not observed by the parser, - which will faithfully produce as many headers as exist in the message - being parsed.) - - The default implementation returns None for all header names. - """ - return None - - @abc.abstractmethod - def header_source_parse(self, sourcelines): - """Given a list of linesep terminated strings constituting the lines of - a single header, return the (name, value) tuple that should be stored - in the model. The input lines should retain their terminating linesep - characters. The lines passed in by the email package may contain - surrogateescaped binary data. - """ - raise NotImplementedError - - @abc.abstractmethod - def header_store_parse(self, name, value): - """Given the header name and the value provided by the application - program, return the (name, value) that should be stored in the model. - """ - raise NotImplementedError - - @abc.abstractmethod - def header_fetch_parse(self, name, value): - """Given the header name and the value from the model, return the value - to be returned to the application program that is requesting that - header. The value passed in by the email package may contain - surrogateescaped binary data if the lines were parsed by a BytesParser. - The returned value should not contain any surrogateescaped data. - - """ - raise NotImplementedError - - @abc.abstractmethod - def fold(self, name, value): - """Given the header name and the value from the model, return a string - containing linesep characters that implement the folding of the header - according to the policy controls. The value passed in by the email - package may contain surrogateescaped binary data if the lines were - parsed by a BytesParser. The returned value should not contain any - surrogateescaped data. - - """ - raise NotImplementedError - - @abc.abstractmethod - def fold_binary(self, name, value): - """Given the header name and the value from the model, return binary - data containing linesep characters that implement the folding of the - header according to the policy controls. The value passed in by the - email package may contain surrogateescaped binary data. - - """ - raise NotImplementedError - - -@_extend_docstrings -class Compat32(Policy): - - """+ - This particular policy is the backward compatibility Policy. It - replicates the behavior of the email package version 5.1. - """ - - def _sanitize_header(self, name, value): - # If the header value contains surrogates, return a Header using - # the unknown-8bit charset to encode the bytes as encoded words. - if not isinstance(value, str): - # Assume it is already a header object - return value - if _has_surrogates(value): - return header.Header(value, charset=_charset.UNKNOWN8BIT, - header_name=name) - else: - return value - - def header_source_parse(self, sourcelines): - """+ - The name is parsed as everything up to the ':' and returned unmodified. - The value is determined by stripping leading whitespace off the - remainder of the first line, joining all subsequent lines together, and - stripping any trailing carriage return or linefeed characters. - - """ - name, value = sourcelines[0].split(':', 1) - value = value.lstrip(' \t') + ''.join(sourcelines[1:]) - return (name, value.rstrip('\r\n')) - - def header_store_parse(self, name, value): - """+ - The name and value are returned unmodified. - """ - return (name, value) - - def header_fetch_parse(self, name, value): - """+ - If the value contains binary data, it is converted into a Header object - using the unknown-8bit charset. Otherwise it is returned unmodified. - """ - return self._sanitize_header(name, value) - - def fold(self, name, value): - """+ - Headers are folded using the Header folding algorithm, which preserves - existing line breaks in the value, and wraps each resulting line to the - max_line_length. Non-ASCII binary data are CTE encoded using the - unknown-8bit charset. - - """ - return self._fold(name, value, sanitize=True) - - def fold_binary(self, name, value): - """+ - Headers are folded using the Header folding algorithm, which preserves - existing line breaks in the value, and wraps each resulting line to the - max_line_length. If cte_type is 7bit, non-ascii binary data is CTE - encoded using the unknown-8bit charset. Otherwise the original source - header is used, with its existing line breaks and/or binary data. - - """ - folded = self._fold(name, value, sanitize=self.cte_type=='7bit') - return folded.encode('ascii', 'surrogateescape') - - def _fold(self, name, value, sanitize): - parts = [] - parts.append('%s: ' % name) - if isinstance(value, str): - if _has_surrogates(value): - if sanitize: - h = header.Header(value, - charset=_charset.UNKNOWN8BIT, - header_name=name) - else: - # If we have raw 8bit data in a byte string, we have no idea - # what the encoding is. There is no safe way to split this - # string. If it's ascii-subset, then we could do a normal - # ascii split, but if it's multibyte then we could break the - # string. There's no way to know so the least harm seems to - # be to not split the string and risk it being too long. - parts.append(value) - h = None - else: - h = header.Header(value, header_name=name) - else: - # Assume it is a Header-like object. - h = value - if h is not None: - parts.append(h.encode(linesep=self.linesep, - maxlinelen=self.max_line_length)) - parts.append(self.linesep) - return ''.join(parts) - - -compat32 = Compat32() diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/base64mime.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/base64mime.py deleted file mode 100644 index 416d612e..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/base64mime.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (C) 2002-2007 Python Software Foundation -# Author: Ben Gertzfield -# Contact: email-sig@python.org - -"""Base64 content transfer encoding per RFCs 2045-2047. - -This module handles the content transfer encoding method defined in RFC 2045 -to encode arbitrary 8-bit data using the three 8-bit bytes in four 7-bit -characters encoding known as Base64. - -It is used in the MIME standards for email to attach images, audio, and text -using some 8-bit character sets to messages. - -This module provides an interface to encode and decode both headers and bodies -with Base64 encoding. - -RFC 2045 defines a method for including character set information in an -`encoded-word' in a header. This method is commonly used for 8-bit real names -in To:, From:, Cc:, etc. fields, as well as Subject: lines. - -This module does not do the line wrapping or end-of-line character conversion -necessary for proper internationalized headers; it only does dumb encoding and -decoding. To deal with the various line wrapping issues, use the email.header -module. -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import range -from future.builtins import bytes - -__all__ = [ - 'body_decode', - 'body_encode', - 'decode', - 'decodestring', - 'header_encode', - 'header_length', - ] - - -from base64 import b64encode -from binascii import b2a_base64, a2b_base64 - -CRLF = '\r\n' -NL = '\n' -EMPTYSTRING = '' - -# See also Charset.py -MISC_LEN = 7 - - -# Helpers -def header_length(bytearray): - """Return the length of s when it is encoded with base64.""" - groups_of_3, leftover = divmod(len(bytearray), 3) - # 4 bytes out for each 3 bytes (or nonzero fraction thereof) in. - n = groups_of_3 * 4 - if leftover: - n += 4 - return n - - -def header_encode(header_bytes, charset='iso-8859-1'): - """Encode a single header line with Base64 encoding in a given charset. - - charset names the character set to use to encode the header. It defaults - to iso-8859-1. Base64 encoding is defined in RFC 2045. - """ - if not header_bytes: - return "" - if isinstance(header_bytes, str): - header_bytes = header_bytes.encode(charset) - encoded = b64encode(header_bytes).decode("ascii") - return '=?%s?b?%s?=' % (charset, encoded) - - -def body_encode(s, maxlinelen=76, eol=NL): - r"""Encode a string with base64. - - Each line will be wrapped at, at most, maxlinelen characters (defaults to - 76 characters). - - Each line of encoded text will end with eol, which defaults to "\n". Set - this to "\r\n" if you will be using the result of this function directly - in an email. - """ - if not s: - return s - - encvec = [] - max_unencoded = maxlinelen * 3 // 4 - for i in range(0, len(s), max_unencoded): - # BAW: should encode() inherit b2a_base64()'s dubious behavior in - # adding a newline to the encoded string? - enc = b2a_base64(s[i:i + max_unencoded]).decode("ascii") - if enc.endswith(NL) and eol != NL: - enc = enc[:-1] + eol - encvec.append(enc) - return EMPTYSTRING.join(encvec) - - -def decode(string): - """Decode a raw base64 string, returning a bytes object. - - This function does not parse a full MIME header value encoded with - base64 (like =?iso-8895-1?b?bmloISBuaWgh?=) -- please use the high - level email.header class for that functionality. - """ - if not string: - return bytes() - elif isinstance(string, str): - return a2b_base64(string.encode('raw-unicode-escape')) - else: - return a2b_base64(string) - - -# For convenience and backwards compatibility w/ standard base64 module -body_decode = decode -decodestring = decode diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/charset.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/charset.py deleted file mode 100644 index 2385ce68..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/charset.py +++ /dev/null @@ -1,409 +0,0 @@ -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import str -from future.builtins import next - -# Copyright (C) 2001-2007 Python Software Foundation -# Author: Ben Gertzfield, Barry Warsaw -# Contact: email-sig@python.org - -__all__ = [ - 'Charset', - 'add_alias', - 'add_charset', - 'add_codec', - ] - -from functools import partial - -from future.backports import email -from future.backports.email import errors -from future.backports.email.encoders import encode_7or8bit - - -# Flags for types of header encodings -QP = 1 # Quoted-Printable -BASE64 = 2 # Base64 -SHORTEST = 3 # the shorter of QP and base64, but only for headers - -# In "=?charset?q?hello_world?=", the =?, ?q?, and ?= add up to 7 -RFC2047_CHROME_LEN = 7 - -DEFAULT_CHARSET = 'us-ascii' -UNKNOWN8BIT = 'unknown-8bit' -EMPTYSTRING = '' - - -# Defaults -CHARSETS = { - # input header enc body enc output conv - 'iso-8859-1': (QP, QP, None), - 'iso-8859-2': (QP, QP, None), - 'iso-8859-3': (QP, QP, None), - 'iso-8859-4': (QP, QP, None), - # iso-8859-5 is Cyrillic, and not especially used - # iso-8859-6 is Arabic, also not particularly used - # iso-8859-7 is Greek, QP will not make it readable - # iso-8859-8 is Hebrew, QP will not make it readable - 'iso-8859-9': (QP, QP, None), - 'iso-8859-10': (QP, QP, None), - # iso-8859-11 is Thai, QP will not make it readable - 'iso-8859-13': (QP, QP, None), - 'iso-8859-14': (QP, QP, None), - 'iso-8859-15': (QP, QP, None), - 'iso-8859-16': (QP, QP, None), - 'windows-1252':(QP, QP, None), - 'viscii': (QP, QP, None), - 'us-ascii': (None, None, None), - 'big5': (BASE64, BASE64, None), - 'gb2312': (BASE64, BASE64, None), - 'euc-jp': (BASE64, None, 'iso-2022-jp'), - 'shift_jis': (BASE64, None, 'iso-2022-jp'), - 'iso-2022-jp': (BASE64, None, None), - 'koi8-r': (BASE64, BASE64, None), - 'utf-8': (SHORTEST, BASE64, 'utf-8'), - } - -# Aliases for other commonly-used names for character sets. Map -# them to the real ones used in email. -ALIASES = { - 'latin_1': 'iso-8859-1', - 'latin-1': 'iso-8859-1', - 'latin_2': 'iso-8859-2', - 'latin-2': 'iso-8859-2', - 'latin_3': 'iso-8859-3', - 'latin-3': 'iso-8859-3', - 'latin_4': 'iso-8859-4', - 'latin-4': 'iso-8859-4', - 'latin_5': 'iso-8859-9', - 'latin-5': 'iso-8859-9', - 'latin_6': 'iso-8859-10', - 'latin-6': 'iso-8859-10', - 'latin_7': 'iso-8859-13', - 'latin-7': 'iso-8859-13', - 'latin_8': 'iso-8859-14', - 'latin-8': 'iso-8859-14', - 'latin_9': 'iso-8859-15', - 'latin-9': 'iso-8859-15', - 'latin_10':'iso-8859-16', - 'latin-10':'iso-8859-16', - 'cp949': 'ks_c_5601-1987', - 'euc_jp': 'euc-jp', - 'euc_kr': 'euc-kr', - 'ascii': 'us-ascii', - } - - -# Map charsets to their Unicode codec strings. -CODEC_MAP = { - 'gb2312': 'eucgb2312_cn', - 'big5': 'big5_tw', - # Hack: We don't want *any* conversion for stuff marked us-ascii, as all - # sorts of garbage might be sent to us in the guise of 7-bit us-ascii. - # Let that stuff pass through without conversion to/from Unicode. - 'us-ascii': None, - } - - -# Convenience functions for extending the above mappings -def add_charset(charset, header_enc=None, body_enc=None, output_charset=None): - """Add character set properties to the global registry. - - charset is the input character set, and must be the canonical name of a - character set. - - Optional header_enc and body_enc is either Charset.QP for - quoted-printable, Charset.BASE64 for base64 encoding, Charset.SHORTEST for - the shortest of qp or base64 encoding, or None for no encoding. SHORTEST - is only valid for header_enc. It describes how message headers and - message bodies in the input charset are to be encoded. Default is no - encoding. - - Optional output_charset is the character set that the output should be - in. Conversions will proceed from input charset, to Unicode, to the - output charset when the method Charset.convert() is called. The default - is to output in the same character set as the input. - - Both input_charset and output_charset must have Unicode codec entries in - the module's charset-to-codec mapping; use add_codec(charset, codecname) - to add codecs the module does not know about. See the codecs module's - documentation for more information. - """ - if body_enc == SHORTEST: - raise ValueError('SHORTEST not allowed for body_enc') - CHARSETS[charset] = (header_enc, body_enc, output_charset) - - -def add_alias(alias, canonical): - """Add a character set alias. - - alias is the alias name, e.g. latin-1 - canonical is the character set's canonical name, e.g. iso-8859-1 - """ - ALIASES[alias] = canonical - - -def add_codec(charset, codecname): - """Add a codec that map characters in the given charset to/from Unicode. - - charset is the canonical name of a character set. codecname is the name - of a Python codec, as appropriate for the second argument to the unicode() - built-in, or to the encode() method of a Unicode string. - """ - CODEC_MAP[charset] = codecname - - -# Convenience function for encoding strings, taking into account -# that they might be unknown-8bit (ie: have surrogate-escaped bytes) -def _encode(string, codec): - string = str(string) - if codec == UNKNOWN8BIT: - return string.encode('ascii', 'surrogateescape') - else: - return string.encode(codec) - - -class Charset(object): - """Map character sets to their email properties. - - This class provides information about the requirements imposed on email - for a specific character set. It also provides convenience routines for - converting between character sets, given the availability of the - applicable codecs. Given a character set, it will do its best to provide - information on how to use that character set in an email in an - RFC-compliant way. - - Certain character sets must be encoded with quoted-printable or base64 - when used in email headers or bodies. Certain character sets must be - converted outright, and are not allowed in email. Instances of this - module expose the following information about a character set: - - input_charset: The initial character set specified. Common aliases - are converted to their `official' email names (e.g. latin_1 - is converted to iso-8859-1). Defaults to 7-bit us-ascii. - - header_encoding: If the character set must be encoded before it can be - used in an email header, this attribute will be set to - Charset.QP (for quoted-printable), Charset.BASE64 (for - base64 encoding), or Charset.SHORTEST for the shortest of - QP or BASE64 encoding. Otherwise, it will be None. - - body_encoding: Same as header_encoding, but describes the encoding for the - mail message's body, which indeed may be different than the - header encoding. Charset.SHORTEST is not allowed for - body_encoding. - - output_charset: Some character sets must be converted before they can be - used in email headers or bodies. If the input_charset is - one of them, this attribute will contain the name of the - charset output will be converted to. Otherwise, it will - be None. - - input_codec: The name of the Python codec used to convert the - input_charset to Unicode. If no conversion codec is - necessary, this attribute will be None. - - output_codec: The name of the Python codec used to convert Unicode - to the output_charset. If no conversion codec is necessary, - this attribute will have the same value as the input_codec. - """ - def __init__(self, input_charset=DEFAULT_CHARSET): - # RFC 2046, $4.1.2 says charsets are not case sensitive. We coerce to - # unicode because its .lower() is locale insensitive. If the argument - # is already a unicode, we leave it at that, but ensure that the - # charset is ASCII, as the standard (RFC XXX) requires. - try: - if isinstance(input_charset, str): - input_charset.encode('ascii') - else: - input_charset = str(input_charset, 'ascii') - except UnicodeError: - raise errors.CharsetError(input_charset) - input_charset = input_charset.lower() - # Set the input charset after filtering through the aliases - self.input_charset = ALIASES.get(input_charset, input_charset) - # We can try to guess which encoding and conversion to use by the - # charset_map dictionary. Try that first, but let the user override - # it. - henc, benc, conv = CHARSETS.get(self.input_charset, - (SHORTEST, BASE64, None)) - if not conv: - conv = self.input_charset - # Set the attributes, allowing the arguments to override the default. - self.header_encoding = henc - self.body_encoding = benc - self.output_charset = ALIASES.get(conv, conv) - # Now set the codecs. If one isn't defined for input_charset, - # guess and try a Unicode codec with the same name as input_codec. - self.input_codec = CODEC_MAP.get(self.input_charset, - self.input_charset) - self.output_codec = CODEC_MAP.get(self.output_charset, - self.output_charset) - - def __str__(self): - return self.input_charset.lower() - - __repr__ = __str__ - - def __eq__(self, other): - return str(self) == str(other).lower() - - def __ne__(self, other): - return not self.__eq__(other) - - def get_body_encoding(self): - """Return the content-transfer-encoding used for body encoding. - - This is either the string `quoted-printable' or `base64' depending on - the encoding used, or it is a function in which case you should call - the function with a single argument, the Message object being - encoded. The function should then set the Content-Transfer-Encoding - header itself to whatever is appropriate. - - Returns "quoted-printable" if self.body_encoding is QP. - Returns "base64" if self.body_encoding is BASE64. - Returns conversion function otherwise. - """ - assert self.body_encoding != SHORTEST - if self.body_encoding == QP: - return 'quoted-printable' - elif self.body_encoding == BASE64: - return 'base64' - else: - return encode_7or8bit - - def get_output_charset(self): - """Return the output character set. - - This is self.output_charset if that is not None, otherwise it is - self.input_charset. - """ - return self.output_charset or self.input_charset - - def header_encode(self, string): - """Header-encode a string by converting it first to bytes. - - The type of encoding (base64 or quoted-printable) will be based on - this charset's `header_encoding`. - - :param string: A unicode string for the header. It must be possible - to encode this string to bytes using the character set's - output codec. - :return: The encoded string, with RFC 2047 chrome. - """ - codec = self.output_codec or 'us-ascii' - header_bytes = _encode(string, codec) - # 7bit/8bit encodings return the string unchanged (modulo conversions) - encoder_module = self._get_encoder(header_bytes) - if encoder_module is None: - return string - return encoder_module.header_encode(header_bytes, codec) - - def header_encode_lines(self, string, maxlengths): - """Header-encode a string by converting it first to bytes. - - This is similar to `header_encode()` except that the string is fit - into maximum line lengths as given by the argument. - - :param string: A unicode string for the header. It must be possible - to encode this string to bytes using the character set's - output codec. - :param maxlengths: Maximum line length iterator. Each element - returned from this iterator will provide the next maximum line - length. This parameter is used as an argument to built-in next() - and should never be exhausted. The maximum line lengths should - not count the RFC 2047 chrome. These line lengths are only a - hint; the splitter does the best it can. - :return: Lines of encoded strings, each with RFC 2047 chrome. - """ - # See which encoding we should use. - codec = self.output_codec or 'us-ascii' - header_bytes = _encode(string, codec) - encoder_module = self._get_encoder(header_bytes) - encoder = partial(encoder_module.header_encode, charset=codec) - # Calculate the number of characters that the RFC 2047 chrome will - # contribute to each line. - charset = self.get_output_charset() - extra = len(charset) + RFC2047_CHROME_LEN - # Now comes the hard part. We must encode bytes but we can't split on - # bytes because some character sets are variable length and each - # encoded word must stand on its own. So the problem is you have to - # encode to bytes to figure out this word's length, but you must split - # on characters. This causes two problems: first, we don't know how - # many octets a specific substring of unicode characters will get - # encoded to, and second, we don't know how many ASCII characters - # those octets will get encoded to. Unless we try it. Which seems - # inefficient. In the interest of being correct rather than fast (and - # in the hope that there will be few encoded headers in any such - # message), brute force it. :( - lines = [] - current_line = [] - maxlen = next(maxlengths) - extra - for character in string: - current_line.append(character) - this_line = EMPTYSTRING.join(current_line) - length = encoder_module.header_length(_encode(this_line, charset)) - if length > maxlen: - # This last character doesn't fit so pop it off. - current_line.pop() - # Does nothing fit on the first line? - if not lines and not current_line: - lines.append(None) - else: - separator = (' ' if lines else '') - joined_line = EMPTYSTRING.join(current_line) - header_bytes = _encode(joined_line, codec) - lines.append(encoder(header_bytes)) - current_line = [character] - maxlen = next(maxlengths) - extra - joined_line = EMPTYSTRING.join(current_line) - header_bytes = _encode(joined_line, codec) - lines.append(encoder(header_bytes)) - return lines - - def _get_encoder(self, header_bytes): - if self.header_encoding == BASE64: - return email.base64mime - elif self.header_encoding == QP: - return email.quoprimime - elif self.header_encoding == SHORTEST: - len64 = email.base64mime.header_length(header_bytes) - lenqp = email.quoprimime.header_length(header_bytes) - if len64 < lenqp: - return email.base64mime - else: - return email.quoprimime - else: - return None - - def body_encode(self, string): - """Body-encode a string by converting it first to bytes. - - The type of encoding (base64 or quoted-printable) will be based on - self.body_encoding. If body_encoding is None, we assume the - output charset is a 7bit encoding, so re-encoding the decoded - string using the ascii codec produces the correct string version - of the content. - """ - if not string: - return string - if self.body_encoding is BASE64: - if isinstance(string, str): - string = string.encode(self.output_charset) - return email.base64mime.body_encode(string) - elif self.body_encoding is QP: - # quopromime.body_encode takes a string, but operates on it as if - # it were a list of byte codes. For a (minimal) history on why - # this is so, see changeset 0cf700464177. To correctly encode a - # character set, then, we must turn it into pseudo bytes via the - # latin1 charset, which will encode any byte as a single code point - # between 0 and 255, which is what body_encode is expecting. - if isinstance(string, str): - string = string.encode(self.output_charset) - string = string.decode('latin1') - return email.quoprimime.body_encode(string) - else: - if isinstance(string, str): - string = string.encode(self.output_charset).decode('ascii') - return string diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/encoders.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/encoders.py deleted file mode 100644 index 15d2eb46..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/encoders.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Encodings and related functions.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import str - -__all__ = [ - 'encode_7or8bit', - 'encode_base64', - 'encode_noop', - 'encode_quopri', - ] - - -try: - from base64 import encodebytes as _bencode -except ImportError: - # Py2 compatibility. TODO: test this! - from base64 import encodestring as _bencode -from quopri import encodestring as _encodestring - - -def _qencode(s): - enc = _encodestring(s, quotetabs=True) - # Must encode spaces, which quopri.encodestring() doesn't do - return enc.replace(' ', '=20') - - -def encode_base64(msg): - """Encode the message's payload in Base64. - - Also, add an appropriate Content-Transfer-Encoding header. - """ - orig = msg.get_payload() - encdata = str(_bencode(orig), 'ascii') - msg.set_payload(encdata) - msg['Content-Transfer-Encoding'] = 'base64' - - -def encode_quopri(msg): - """Encode the message's payload in quoted-printable. - - Also, add an appropriate Content-Transfer-Encoding header. - """ - orig = msg.get_payload() - encdata = _qencode(orig) - msg.set_payload(encdata) - msg['Content-Transfer-Encoding'] = 'quoted-printable' - - -def encode_7or8bit(msg): - """Set the Content-Transfer-Encoding header to 7bit or 8bit.""" - orig = msg.get_payload() - if orig is None: - # There's no payload. For backwards compatibility we use 7bit - msg['Content-Transfer-Encoding'] = '7bit' - return - # We play a trick to make this go fast. If encoding/decode to ASCII - # succeeds, we know the data must be 7bit, otherwise treat it as 8bit. - try: - if isinstance(orig, str): - orig.encode('ascii') - else: - orig.decode('ascii') - except UnicodeError: - charset = msg.get_charset() - output_cset = charset and charset.output_charset - # iso-2022-* is non-ASCII but encodes to a 7-bit representation - if output_cset and output_cset.lower().startswith('iso-2022-'): - msg['Content-Transfer-Encoding'] = '7bit' - else: - msg['Content-Transfer-Encoding'] = '8bit' - else: - msg['Content-Transfer-Encoding'] = '7bit' - if not isinstance(orig, str): - msg.set_payload(orig.decode('ascii', 'surrogateescape')) - - -def encode_noop(msg): - """Do nothing.""" - # Well, not quite *nothing*: in Python3 we have to turn bytes into a string - # in our internal surrogateescaped form in order to keep the model - # consistent. - orig = msg.get_payload() - if not isinstance(orig, str): - msg.set_payload(orig.decode('ascii', 'surrogateescape')) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/errors.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/errors.py deleted file mode 100644 index 0fe599cf..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/errors.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""email package exception classes.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import super - - -class MessageError(Exception): - """Base class for errors in the email package.""" - - -class MessageParseError(MessageError): - """Base class for message parsing errors.""" - - -class HeaderParseError(MessageParseError): - """Error while parsing headers.""" - - -class BoundaryError(MessageParseError): - """Couldn't find terminating boundary.""" - - -class MultipartConversionError(MessageError, TypeError): - """Conversion to a multipart is prohibited.""" - - -class CharsetError(MessageError): - """An illegal charset was given.""" - - -# These are parsing defects which the parser was able to work around. -class MessageDefect(ValueError): - """Base class for a message defect.""" - - def __init__(self, line=None): - if line is not None: - super().__init__(line) - self.line = line - -class NoBoundaryInMultipartDefect(MessageDefect): - """A message claimed to be a multipart but had no boundary parameter.""" - -class StartBoundaryNotFoundDefect(MessageDefect): - """The claimed start boundary was never found.""" - -class CloseBoundaryNotFoundDefect(MessageDefect): - """A start boundary was found, but not the corresponding close boundary.""" - -class FirstHeaderLineIsContinuationDefect(MessageDefect): - """A message had a continuation line as its first header line.""" - -class MisplacedEnvelopeHeaderDefect(MessageDefect): - """A 'Unix-from' header was found in the middle of a header block.""" - -class MissingHeaderBodySeparatorDefect(MessageDefect): - """Found line with no leading whitespace and no colon before blank line.""" -# XXX: backward compatibility, just in case (it was never emitted). -MalformedHeaderDefect = MissingHeaderBodySeparatorDefect - -class MultipartInvariantViolationDefect(MessageDefect): - """A message claimed to be a multipart but no subparts were found.""" - -class InvalidMultipartContentTransferEncodingDefect(MessageDefect): - """An invalid content transfer encoding was set on the multipart itself.""" - -class UndecodableBytesDefect(MessageDefect): - """Header contained bytes that could not be decoded""" - -class InvalidBase64PaddingDefect(MessageDefect): - """base64 encoded sequence had an incorrect length""" - -class InvalidBase64CharactersDefect(MessageDefect): - """base64 encoded sequence had characters not in base64 alphabet""" - -# These errors are specific to header parsing. - -class HeaderDefect(MessageDefect): - """Base class for a header defect.""" - - def __init__(self, *args, **kw): - super().__init__(*args, **kw) - -class InvalidHeaderDefect(HeaderDefect): - """Header is not valid, message gives details.""" - -class HeaderMissingRequiredValue(HeaderDefect): - """A header that must have a value had none""" - -class NonPrintableDefect(HeaderDefect): - """ASCII characters outside the ascii-printable range found""" - - def __init__(self, non_printables): - super().__init__(non_printables) - self.non_printables = non_printables - - def __str__(self): - return ("the following ASCII non-printables found in header: " - "{}".format(self.non_printables)) - -class ObsoleteHeaderDefect(HeaderDefect): - """Header uses syntax declared obsolete by RFC 5322""" - -class NonASCIILocalPartDefect(HeaderDefect): - """local_part contains non-ASCII characters""" - # This defect only occurs during unicode parsing, not when - # parsing messages decoded from binary. diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/feedparser.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/feedparser.py deleted file mode 100644 index 935c26e3..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/feedparser.py +++ /dev/null @@ -1,525 +0,0 @@ -# Copyright (C) 2004-2006 Python Software Foundation -# Authors: Baxter, Wouters and Warsaw -# Contact: email-sig@python.org - -"""FeedParser - An email feed parser. - -The feed parser implements an interface for incrementally parsing an email -message, line by line. This has advantages for certain applications, such as -those reading email messages off a socket. - -FeedParser.feed() is the primary interface for pushing new data into the -parser. It returns when there's nothing more it can do with the available -data. When you have no more data to push into the parser, call .close(). -This completes the parsing and returns the root message object. - -The other advantage of this parser is that it will never raise a parsing -exception. Instead, when it finds something unexpected, it adds a 'defect' to -the current message. Defects are just instances that live on the message -object's .defects attribute. -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import object, range, super -from future.utils import implements_iterator, PY3 - -__all__ = ['FeedParser', 'BytesFeedParser'] - -import re - -from future.backports.email import errors -from future.backports.email import message -from future.backports.email._policybase import compat32 - -NLCRE = re.compile('\r\n|\r|\n') -NLCRE_bol = re.compile('(\r\n|\r|\n)') -NLCRE_eol = re.compile('(\r\n|\r|\n)\Z') -NLCRE_crack = re.compile('(\r\n|\r|\n)') -# RFC 2822 $3.6.8 Optional fields. ftext is %d33-57 / %d59-126, Any character -# except controls, SP, and ":". -headerRE = re.compile(r'^(From |[\041-\071\073-\176]{1,}:|[\t ])') -EMPTYSTRING = '' -NL = '\n' - -NeedMoreData = object() - - -# @implements_iterator -class BufferedSubFile(object): - """A file-ish object that can have new data loaded into it. - - You can also push and pop line-matching predicates onto a stack. When the - current predicate matches the current line, a false EOF response - (i.e. empty string) is returned instead. This lets the parser adhere to a - simple abstraction -- it parses until EOF closes the current message. - """ - def __init__(self): - # The last partial line pushed into this object. - self._partial = '' - # The list of full, pushed lines, in reverse order - self._lines = [] - # The stack of false-EOF checking predicates. - self._eofstack = [] - # A flag indicating whether the file has been closed or not. - self._closed = False - - def push_eof_matcher(self, pred): - self._eofstack.append(pred) - - def pop_eof_matcher(self): - return self._eofstack.pop() - - def close(self): - # Don't forget any trailing partial line. - self._lines.append(self._partial) - self._partial = '' - self._closed = True - - def readline(self): - if not self._lines: - if self._closed: - return '' - return NeedMoreData - # Pop the line off the stack and see if it matches the current - # false-EOF predicate. - line = self._lines.pop() - # RFC 2046, section 5.1.2 requires us to recognize outer level - # boundaries at any level of inner nesting. Do this, but be sure it's - # in the order of most to least nested. - for ateof in self._eofstack[::-1]: - if ateof(line): - # We're at the false EOF. But push the last line back first. - self._lines.append(line) - return '' - return line - - def unreadline(self, line): - # Let the consumer push a line back into the buffer. - assert line is not NeedMoreData - self._lines.append(line) - - def push(self, data): - """Push some new data into this object.""" - # Handle any previous leftovers - data, self._partial = self._partial + data, '' - # Crack into lines, but preserve the newlines on the end of each - parts = NLCRE_crack.split(data) - # The *ahem* interesting behaviour of re.split when supplied grouping - # parentheses is that the last element of the resulting list is the - # data after the final RE. In the case of a NL/CR terminated string, - # this is the empty string. - self._partial = parts.pop() - #GAN 29Mar09 bugs 1555570, 1721862 Confusion at 8K boundary ending with \r: - # is there a \n to follow later? - if not self._partial and parts and parts[-1].endswith('\r'): - self._partial = parts.pop(-2)+parts.pop() - # parts is a list of strings, alternating between the line contents - # and the eol character(s). Gather up a list of lines after - # re-attaching the newlines. - lines = [] - for i in range(len(parts) // 2): - lines.append(parts[i*2] + parts[i*2+1]) - self.pushlines(lines) - - def pushlines(self, lines): - # Reverse and insert at the front of the lines. - self._lines[:0] = lines[::-1] - - def __iter__(self): - return self - - def __next__(self): - line = self.readline() - if line == '': - raise StopIteration - return line - - -class FeedParser(object): - """A feed-style parser of email.""" - - def __init__(self, _factory=message.Message, **_3to2kwargs): - if 'policy' in _3to2kwargs: policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - else: policy = compat32 - """_factory is called with no arguments to create a new message obj - - The policy keyword specifies a policy object that controls a number of - aspects of the parser's operation. The default policy maintains - backward compatibility. - - """ - self._factory = _factory - self.policy = policy - try: - _factory(policy=self.policy) - self._factory_kwds = lambda: {'policy': self.policy} - except TypeError: - # Assume this is an old-style factory - self._factory_kwds = lambda: {} - self._input = BufferedSubFile() - self._msgstack = [] - if PY3: - self._parse = self._parsegen().__next__ - else: - self._parse = self._parsegen().next - self._cur = None - self._last = None - self._headersonly = False - - # Non-public interface for supporting Parser's headersonly flag - def _set_headersonly(self): - self._headersonly = True - - def feed(self, data): - """Push more data into the parser.""" - self._input.push(data) - self._call_parse() - - def _call_parse(self): - try: - self._parse() - except StopIteration: - pass - - def close(self): - """Parse all remaining data and return the root message object.""" - self._input.close() - self._call_parse() - root = self._pop_message() - assert not self._msgstack - # Look for final set of defects - if root.get_content_maintype() == 'multipart' \ - and not root.is_multipart(): - defect = errors.MultipartInvariantViolationDefect() - self.policy.handle_defect(root, defect) - return root - - def _new_message(self): - msg = self._factory(**self._factory_kwds()) - if self._cur and self._cur.get_content_type() == 'multipart/digest': - msg.set_default_type('message/rfc822') - if self._msgstack: - self._msgstack[-1].attach(msg) - self._msgstack.append(msg) - self._cur = msg - self._last = msg - - def _pop_message(self): - retval = self._msgstack.pop() - if self._msgstack: - self._cur = self._msgstack[-1] - else: - self._cur = None - return retval - - def _parsegen(self): - # Create a new message and start by parsing headers. - self._new_message() - headers = [] - # Collect the headers, searching for a line that doesn't match the RFC - # 2822 header or continuation pattern (including an empty line). - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - if not headerRE.match(line): - # If we saw the RFC defined header/body separator - # (i.e. newline), just throw it away. Otherwise the line is - # part of the body so push it back. - if not NLCRE.match(line): - defect = errors.MissingHeaderBodySeparatorDefect() - self.policy.handle_defect(self._cur, defect) - self._input.unreadline(line) - break - headers.append(line) - # Done with the headers, so parse them and figure out what we're - # supposed to see in the body of the message. - self._parse_headers(headers) - # Headers-only parsing is a backwards compatibility hack, which was - # necessary in the older parser, which could raise errors. All - # remaining lines in the input are thrown into the message body. - if self._headersonly: - lines = [] - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - if line == '': - break - lines.append(line) - self._cur.set_payload(EMPTYSTRING.join(lines)) - return - if self._cur.get_content_type() == 'message/delivery-status': - # message/delivery-status contains blocks of headers separated by - # a blank line. We'll represent each header block as a separate - # nested message object, but the processing is a bit different - # than standard message/* types because there is no body for the - # nested messages. A blank line separates the subparts. - while True: - self._input.push_eof_matcher(NLCRE.match) - for retval in self._parsegen(): - if retval is NeedMoreData: - yield NeedMoreData - continue - break - msg = self._pop_message() - # We need to pop the EOF matcher in order to tell if we're at - # the end of the current file, not the end of the last block - # of message headers. - self._input.pop_eof_matcher() - # The input stream must be sitting at the newline or at the - # EOF. We want to see if we're at the end of this subpart, so - # first consume the blank line, then test the next line to see - # if we're at this subpart's EOF. - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - break - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - break - if line == '': - break - # Not at EOF so this is a line we're going to need. - self._input.unreadline(line) - return - if self._cur.get_content_maintype() == 'message': - # The message claims to be a message/* type, then what follows is - # another RFC 2822 message. - for retval in self._parsegen(): - if retval is NeedMoreData: - yield NeedMoreData - continue - break - self._pop_message() - return - if self._cur.get_content_maintype() == 'multipart': - boundary = self._cur.get_boundary() - if boundary is None: - # The message /claims/ to be a multipart but it has not - # defined a boundary. That's a problem which we'll handle by - # reading everything until the EOF and marking the message as - # defective. - defect = errors.NoBoundaryInMultipartDefect() - self.policy.handle_defect(self._cur, defect) - lines = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - lines.append(line) - self._cur.set_payload(EMPTYSTRING.join(lines)) - return - # Make sure a valid content type was specified per RFC 2045:6.4. - if (self._cur.get('content-transfer-encoding', '8bit').lower() - not in ('7bit', '8bit', 'binary')): - defect = errors.InvalidMultipartContentTransferEncodingDefect() - self.policy.handle_defect(self._cur, defect) - # Create a line match predicate which matches the inter-part - # boundary as well as the end-of-multipart boundary. Don't push - # this onto the input stream until we've scanned past the - # preamble. - separator = '--' + boundary - boundaryre = re.compile( - '(?P' + re.escape(separator) + - r')(?P--)?(?P[ \t]*)(?P\r\n|\r|\n)?$') - capturing_preamble = True - preamble = [] - linesep = False - close_boundary_seen = False - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - if line == '': - break - mo = boundaryre.match(line) - if mo: - # If we're looking at the end boundary, we're done with - # this multipart. If there was a newline at the end of - # the closing boundary, then we need to initialize the - # epilogue with the empty string (see below). - if mo.group('end'): - close_boundary_seen = True - linesep = mo.group('linesep') - break - # We saw an inter-part boundary. Were we in the preamble? - if capturing_preamble: - if preamble: - # According to RFC 2046, the last newline belongs - # to the boundary. - lastline = preamble[-1] - eolmo = NLCRE_eol.search(lastline) - if eolmo: - preamble[-1] = lastline[:-len(eolmo.group(0))] - self._cur.preamble = EMPTYSTRING.join(preamble) - capturing_preamble = False - self._input.unreadline(line) - continue - # We saw a boundary separating two parts. Consume any - # multiple boundary lines that may be following. Our - # interpretation of RFC 2046 BNF grammar does not produce - # body parts within such double boundaries. - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - mo = boundaryre.match(line) - if not mo: - self._input.unreadline(line) - break - # Recurse to parse this subpart; the input stream points - # at the subpart's first line. - self._input.push_eof_matcher(boundaryre.match) - for retval in self._parsegen(): - if retval is NeedMoreData: - yield NeedMoreData - continue - break - # Because of RFC 2046, the newline preceding the boundary - # separator actually belongs to the boundary, not the - # previous subpart's payload (or epilogue if the previous - # part is a multipart). - if self._last.get_content_maintype() == 'multipart': - epilogue = self._last.epilogue - if epilogue == '': - self._last.epilogue = None - elif epilogue is not None: - mo = NLCRE_eol.search(epilogue) - if mo: - end = len(mo.group(0)) - self._last.epilogue = epilogue[:-end] - else: - payload = self._last._payload - if isinstance(payload, str): - mo = NLCRE_eol.search(payload) - if mo: - payload = payload[:-len(mo.group(0))] - self._last._payload = payload - self._input.pop_eof_matcher() - self._pop_message() - # Set the multipart up for newline cleansing, which will - # happen if we're in a nested multipart. - self._last = self._cur - else: - # I think we must be in the preamble - assert capturing_preamble - preamble.append(line) - # We've seen either the EOF or the end boundary. If we're still - # capturing the preamble, we never saw the start boundary. Note - # that as a defect and store the captured text as the payload. - if capturing_preamble: - defect = errors.StartBoundaryNotFoundDefect() - self.policy.handle_defect(self._cur, defect) - self._cur.set_payload(EMPTYSTRING.join(preamble)) - epilogue = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - self._cur.epilogue = EMPTYSTRING.join(epilogue) - return - # If we're not processing the preamble, then we might have seen - # EOF without seeing that end boundary...that is also a defect. - if not close_boundary_seen: - defect = errors.CloseBoundaryNotFoundDefect() - self.policy.handle_defect(self._cur, defect) - return - # Everything from here to the EOF is epilogue. If the end boundary - # ended in a newline, we'll need to make sure the epilogue isn't - # None - if linesep: - epilogue = [''] - else: - epilogue = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - epilogue.append(line) - # Any CRLF at the front of the epilogue is not technically part of - # the epilogue. Also, watch out for an empty string epilogue, - # which means a single newline. - if epilogue: - firstline = epilogue[0] - bolmo = NLCRE_bol.match(firstline) - if bolmo: - epilogue[0] = firstline[len(bolmo.group(0)):] - self._cur.epilogue = EMPTYSTRING.join(epilogue) - return - # Otherwise, it's some non-multipart type, so the entire rest of the - # file contents becomes the payload. - lines = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - lines.append(line) - self._cur.set_payload(EMPTYSTRING.join(lines)) - - def _parse_headers(self, lines): - # Passed a list of lines that make up the headers for the current msg - lastheader = '' - lastvalue = [] - for lineno, line in enumerate(lines): - # Check for continuation - if line[0] in ' \t': - if not lastheader: - # The first line of the headers was a continuation. This - # is illegal, so let's note the defect, store the illegal - # line, and ignore it for purposes of headers. - defect = errors.FirstHeaderLineIsContinuationDefect(line) - self.policy.handle_defect(self._cur, defect) - continue - lastvalue.append(line) - continue - if lastheader: - self._cur.set_raw(*self.policy.header_source_parse(lastvalue)) - lastheader, lastvalue = '', [] - # Check for envelope header, i.e. unix-from - if line.startswith('From '): - if lineno == 0: - # Strip off the trailing newline - mo = NLCRE_eol.search(line) - if mo: - line = line[:-len(mo.group(0))] - self._cur.set_unixfrom(line) - continue - elif lineno == len(lines) - 1: - # Something looking like a unix-from at the end - it's - # probably the first line of the body, so push back the - # line and stop. - self._input.unreadline(line) - return - else: - # Weirdly placed unix-from line. Note this as a defect - # and ignore it. - defect = errors.MisplacedEnvelopeHeaderDefect(line) - self._cur.defects.append(defect) - continue - # Split the line on the colon separating field name from value. - # There will always be a colon, because if there wasn't the part of - # the parser that calls us would have started parsing the body. - i = line.find(':') - assert i>0, "_parse_headers fed line with no : and no leading WS" - lastheader = line[:i] - lastvalue = [line] - # Done with all the lines, so handle the last header. - if lastheader: - self._cur.set_raw(*self.policy.header_source_parse(lastvalue)) - - -class BytesFeedParser(FeedParser): - """Like FeedParser, but feed accepts bytes.""" - - def feed(self, data): - super().feed(data.decode('ascii', 'surrogateescape')) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/generator.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/generator.py deleted file mode 100644 index 53493d0a..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/generator.py +++ /dev/null @@ -1,498 +0,0 @@ -# Copyright (C) 2001-2010 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Classes to generate plain text from a message object tree.""" -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import super -from future.builtins import str - -__all__ = ['Generator', 'DecodedGenerator', 'BytesGenerator'] - -import re -import sys -import time -import random -import warnings - -from io import StringIO, BytesIO -from future.backports.email._policybase import compat32 -from future.backports.email.header import Header -from future.backports.email.utils import _has_surrogates -import future.backports.email.charset as _charset - -UNDERSCORE = '_' -NL = '\n' # XXX: no longer used by the code below. - -fcre = re.compile(r'^From ', re.MULTILINE) - - -class Generator(object): - """Generates output from a Message object tree. - - This basic generator writes the message to the given file object as plain - text. - """ - # - # Public interface - # - - def __init__(self, outfp, mangle_from_=True, maxheaderlen=None, **_3to2kwargs): - if 'policy' in _3to2kwargs: policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - else: policy = None - """Create the generator for message flattening. - - outfp is the output file-like object for writing the message to. It - must have a write() method. - - Optional mangle_from_ is a flag that, when True (the default), escapes - From_ lines in the body of the message by putting a `>' in front of - them. - - Optional maxheaderlen specifies the longest length for a non-continued - header. When a header line is longer (in characters, with tabs - expanded to 8 spaces) than maxheaderlen, the header will split as - defined in the Header class. Set maxheaderlen to zero to disable - header wrapping. The default is 78, as recommended (but not required) - by RFC 2822. - - The policy keyword specifies a policy object that controls a number of - aspects of the generator's operation. The default policy maintains - backward compatibility. - - """ - self._fp = outfp - self._mangle_from_ = mangle_from_ - self.maxheaderlen = maxheaderlen - self.policy = policy - - def write(self, s): - # Just delegate to the file object - self._fp.write(s) - - def flatten(self, msg, unixfrom=False, linesep=None): - r"""Print the message object tree rooted at msg to the output file - specified when the Generator instance was created. - - unixfrom is a flag that forces the printing of a Unix From_ delimiter - before the first object in the message tree. If the original message - has no From_ delimiter, a `standard' one is crafted. By default, this - is False to inhibit the printing of any From_ delimiter. - - Note that for subobjects, no From_ line is printed. - - linesep specifies the characters used to indicate a new line in - the output. The default value is determined by the policy. - - """ - # We use the _XXX constants for operating on data that comes directly - # from the msg, and _encoded_XXX constants for operating on data that - # has already been converted (to bytes in the BytesGenerator) and - # inserted into a temporary buffer. - policy = msg.policy if self.policy is None else self.policy - if linesep is not None: - policy = policy.clone(linesep=linesep) - if self.maxheaderlen is not None: - policy = policy.clone(max_line_length=self.maxheaderlen) - self._NL = policy.linesep - self._encoded_NL = self._encode(self._NL) - self._EMPTY = '' - self._encoded_EMTPY = self._encode('') - # Because we use clone (below) when we recursively process message - # subparts, and because clone uses the computed policy (not None), - # submessages will automatically get set to the computed policy when - # they are processed by this code. - old_gen_policy = self.policy - old_msg_policy = msg.policy - try: - self.policy = policy - msg.policy = policy - if unixfrom: - ufrom = msg.get_unixfrom() - if not ufrom: - ufrom = 'From nobody ' + time.ctime(time.time()) - self.write(ufrom + self._NL) - self._write(msg) - finally: - self.policy = old_gen_policy - msg.policy = old_msg_policy - - def clone(self, fp): - """Clone this generator with the exact same options.""" - return self.__class__(fp, - self._mangle_from_, - None, # Use policy setting, which we've adjusted - policy=self.policy) - - # - # Protected interface - undocumented ;/ - # - - # Note that we use 'self.write' when what we are writing is coming from - # the source, and self._fp.write when what we are writing is coming from a - # buffer (because the Bytes subclass has already had a chance to transform - # the data in its write method in that case). This is an entirely - # pragmatic split determined by experiment; we could be more general by - # always using write and having the Bytes subclass write method detect when - # it has already transformed the input; but, since this whole thing is a - # hack anyway this seems good enough. - - # Similarly, we have _XXX and _encoded_XXX attributes that are used on - # source and buffer data, respectively. - _encoded_EMPTY = '' - - def _new_buffer(self): - # BytesGenerator overrides this to return BytesIO. - return StringIO() - - def _encode(self, s): - # BytesGenerator overrides this to encode strings to bytes. - return s - - def _write_lines(self, lines): - # We have to transform the line endings. - if not lines: - return - lines = lines.splitlines(True) - for line in lines[:-1]: - self.write(line.rstrip('\r\n')) - self.write(self._NL) - laststripped = lines[-1].rstrip('\r\n') - self.write(laststripped) - if len(lines[-1]) != len(laststripped): - self.write(self._NL) - - def _write(self, msg): - # We can't write the headers yet because of the following scenario: - # say a multipart message includes the boundary string somewhere in - # its body. We'd have to calculate the new boundary /before/ we write - # the headers so that we can write the correct Content-Type: - # parameter. - # - # The way we do this, so as to make the _handle_*() methods simpler, - # is to cache any subpart writes into a buffer. The we write the - # headers and the buffer contents. That way, subpart handlers can - # Do The Right Thing, and can still modify the Content-Type: header if - # necessary. - oldfp = self._fp - try: - self._fp = sfp = self._new_buffer() - self._dispatch(msg) - finally: - self._fp = oldfp - # Write the headers. First we see if the message object wants to - # handle that itself. If not, we'll do it generically. - meth = getattr(msg, '_write_headers', None) - if meth is None: - self._write_headers(msg) - else: - meth(self) - self._fp.write(sfp.getvalue()) - - def _dispatch(self, msg): - # Get the Content-Type: for the message, then try to dispatch to - # self._handle__(). If there's no handler for the - # full MIME type, then dispatch to self._handle_(). If - # that's missing too, then dispatch to self._writeBody(). - main = msg.get_content_maintype() - sub = msg.get_content_subtype() - specific = UNDERSCORE.join((main, sub)).replace('-', '_') - meth = getattr(self, '_handle_' + specific, None) - if meth is None: - generic = main.replace('-', '_') - meth = getattr(self, '_handle_' + generic, None) - if meth is None: - meth = self._writeBody - meth(msg) - - # - # Default handlers - # - - def _write_headers(self, msg): - for h, v in msg.raw_items(): - self.write(self.policy.fold(h, v)) - # A blank line always separates headers from body - self.write(self._NL) - - # - # Handlers for writing types and subtypes - # - - def _handle_text(self, msg): - payload = msg.get_payload() - if payload is None: - return - if not isinstance(payload, str): - raise TypeError('string payload expected: %s' % type(payload)) - if _has_surrogates(msg._payload): - charset = msg.get_param('charset') - if charset is not None: - del msg['content-transfer-encoding'] - msg.set_payload(payload, charset) - payload = msg.get_payload() - if self._mangle_from_: - payload = fcre.sub('>From ', payload) - self._write_lines(payload) - - # Default body handler - _writeBody = _handle_text - - def _handle_multipart(self, msg): - # The trick here is to write out each part separately, merge them all - # together, and then make sure that the boundary we've chosen isn't - # present in the payload. - msgtexts = [] - subparts = msg.get_payload() - if subparts is None: - subparts = [] - elif isinstance(subparts, str): - # e.g. a non-strict parse of a message with no starting boundary. - self.write(subparts) - return - elif not isinstance(subparts, list): - # Scalar payload - subparts = [subparts] - for part in subparts: - s = self._new_buffer() - g = self.clone(s) - g.flatten(part, unixfrom=False, linesep=self._NL) - msgtexts.append(s.getvalue()) - # BAW: What about boundaries that are wrapped in double-quotes? - boundary = msg.get_boundary() - if not boundary: - # Create a boundary that doesn't appear in any of the - # message texts. - alltext = self._encoded_NL.join(msgtexts) - boundary = self._make_boundary(alltext) - msg.set_boundary(boundary) - # If there's a preamble, write it out, with a trailing CRLF - if msg.preamble is not None: - if self._mangle_from_: - preamble = fcre.sub('>From ', msg.preamble) - else: - preamble = msg.preamble - self._write_lines(preamble) - self.write(self._NL) - # dash-boundary transport-padding CRLF - self.write('--' + boundary + self._NL) - # body-part - if msgtexts: - self._fp.write(msgtexts.pop(0)) - # *encapsulation - # --> delimiter transport-padding - # --> CRLF body-part - for body_part in msgtexts: - # delimiter transport-padding CRLF - self.write(self._NL + '--' + boundary + self._NL) - # body-part - self._fp.write(body_part) - # close-delimiter transport-padding - self.write(self._NL + '--' + boundary + '--') - if msg.epilogue is not None: - self.write(self._NL) - if self._mangle_from_: - epilogue = fcre.sub('>From ', msg.epilogue) - else: - epilogue = msg.epilogue - self._write_lines(epilogue) - - def _handle_multipart_signed(self, msg): - # The contents of signed parts has to stay unmodified in order to keep - # the signature intact per RFC1847 2.1, so we disable header wrapping. - # RDM: This isn't enough to completely preserve the part, but it helps. - p = self.policy - self.policy = p.clone(max_line_length=0) - try: - self._handle_multipart(msg) - finally: - self.policy = p - - def _handle_message_delivery_status(self, msg): - # We can't just write the headers directly to self's file object - # because this will leave an extra newline between the last header - # block and the boundary. Sigh. - blocks = [] - for part in msg.get_payload(): - s = self._new_buffer() - g = self.clone(s) - g.flatten(part, unixfrom=False, linesep=self._NL) - text = s.getvalue() - lines = text.split(self._encoded_NL) - # Strip off the unnecessary trailing empty line - if lines and lines[-1] == self._encoded_EMPTY: - blocks.append(self._encoded_NL.join(lines[:-1])) - else: - blocks.append(text) - # Now join all the blocks with an empty line. This has the lovely - # effect of separating each block with an empty line, but not adding - # an extra one after the last one. - self._fp.write(self._encoded_NL.join(blocks)) - - def _handle_message(self, msg): - s = self._new_buffer() - g = self.clone(s) - # The payload of a message/rfc822 part should be a multipart sequence - # of length 1. The zeroth element of the list should be the Message - # object for the subpart. Extract that object, stringify it, and - # write it out. - # Except, it turns out, when it's a string instead, which happens when - # and only when HeaderParser is used on a message of mime type - # message/rfc822. Such messages are generated by, for example, - # Groupwise when forwarding unadorned messages. (Issue 7970.) So - # in that case we just emit the string body. - payload = msg._payload - if isinstance(payload, list): - g.flatten(msg.get_payload(0), unixfrom=False, linesep=self._NL) - payload = s.getvalue() - else: - payload = self._encode(payload) - self._fp.write(payload) - - # This used to be a module level function; we use a classmethod for this - # and _compile_re so we can continue to provide the module level function - # for backward compatibility by doing - # _make_boudary = Generator._make_boundary - # at the end of the module. It *is* internal, so we could drop that... - @classmethod - def _make_boundary(cls, text=None): - # Craft a random boundary. If text is given, ensure that the chosen - # boundary doesn't appear in the text. - token = random.randrange(sys.maxsize) - boundary = ('=' * 15) + (_fmt % token) + '==' - if text is None: - return boundary - b = boundary - counter = 0 - while True: - cre = cls._compile_re('^--' + re.escape(b) + '(--)?$', re.MULTILINE) - if not cre.search(text): - break - b = boundary + '.' + str(counter) - counter += 1 - return b - - @classmethod - def _compile_re(cls, s, flags): - return re.compile(s, flags) - -class BytesGenerator(Generator): - """Generates a bytes version of a Message object tree. - - Functionally identical to the base Generator except that the output is - bytes and not string. When surrogates were used in the input to encode - bytes, these are decoded back to bytes for output. If the policy has - cte_type set to 7bit, then the message is transformed such that the - non-ASCII bytes are properly content transfer encoded, using the charset - unknown-8bit. - - The outfp object must accept bytes in its write method. - """ - - # Bytes versions of this constant for use in manipulating data from - # the BytesIO buffer. - _encoded_EMPTY = b'' - - def write(self, s): - self._fp.write(str(s).encode('ascii', 'surrogateescape')) - - def _new_buffer(self): - return BytesIO() - - def _encode(self, s): - return s.encode('ascii') - - def _write_headers(self, msg): - # This is almost the same as the string version, except for handling - # strings with 8bit bytes. - for h, v in msg.raw_items(): - self._fp.write(self.policy.fold_binary(h, v)) - # A blank line always separates headers from body - self.write(self._NL) - - def _handle_text(self, msg): - # If the string has surrogates the original source was bytes, so - # just write it back out. - if msg._payload is None: - return - if _has_surrogates(msg._payload) and not self.policy.cte_type=='7bit': - if self._mangle_from_: - msg._payload = fcre.sub(">From ", msg._payload) - self._write_lines(msg._payload) - else: - super(BytesGenerator,self)._handle_text(msg) - - # Default body handler - _writeBody = _handle_text - - @classmethod - def _compile_re(cls, s, flags): - return re.compile(s.encode('ascii'), flags) - - -_FMT = '[Non-text (%(type)s) part of message omitted, filename %(filename)s]' - -class DecodedGenerator(Generator): - """Generates a text representation of a message. - - Like the Generator base class, except that non-text parts are substituted - with a format string representing the part. - """ - def __init__(self, outfp, mangle_from_=True, maxheaderlen=78, fmt=None): - """Like Generator.__init__() except that an additional optional - argument is allowed. - - Walks through all subparts of a message. If the subpart is of main - type `text', then it prints the decoded payload of the subpart. - - Otherwise, fmt is a format string that is used instead of the message - payload. fmt is expanded with the following keywords (in - %(keyword)s format): - - type : Full MIME type of the non-text part - maintype : Main MIME type of the non-text part - subtype : Sub-MIME type of the non-text part - filename : Filename of the non-text part - description: Description associated with the non-text part - encoding : Content transfer encoding of the non-text part - - The default value for fmt is None, meaning - - [Non-text (%(type)s) part of message omitted, filename %(filename)s] - """ - Generator.__init__(self, outfp, mangle_from_, maxheaderlen) - if fmt is None: - self._fmt = _FMT - else: - self._fmt = fmt - - def _dispatch(self, msg): - for part in msg.walk(): - maintype = part.get_content_maintype() - if maintype == 'text': - print(part.get_payload(decode=False), file=self) - elif maintype == 'multipart': - # Just skip this - pass - else: - print(self._fmt % { - 'type' : part.get_content_type(), - 'maintype' : part.get_content_maintype(), - 'subtype' : part.get_content_subtype(), - 'filename' : part.get_filename('[no filename]'), - 'description': part.get('Content-Description', - '[no description]'), - 'encoding' : part.get('Content-Transfer-Encoding', - '[no encoding]'), - }, file=self) - - -# Helper used by Generator._make_boundary -_width = len(repr(sys.maxsize-1)) -_fmt = '%%0%dd' % _width - -# Backward compatibility -_make_boundary = Generator._make_boundary diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/header.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/header.py deleted file mode 100644 index 63bf038c..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/header.py +++ /dev/null @@ -1,581 +0,0 @@ -# Copyright (C) 2002-2007 Python Software Foundation -# Author: Ben Gertzfield, Barry Warsaw -# Contact: email-sig@python.org - -"""Header encoding and decoding functionality.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import bytes, range, str, super, zip - -__all__ = [ - 'Header', - 'decode_header', - 'make_header', - ] - -import re -import binascii - -from future.backports import email -from future.backports.email import base64mime -from future.backports.email.errors import HeaderParseError -import future.backports.email.charset as _charset - -# Helpers -from future.backports.email.quoprimime import _max_append, header_decode - -Charset = _charset.Charset - -NL = '\n' -SPACE = ' ' -BSPACE = b' ' -SPACE8 = ' ' * 8 -EMPTYSTRING = '' -MAXLINELEN = 78 -FWS = ' \t' - -USASCII = Charset('us-ascii') -UTF8 = Charset('utf-8') - -# Match encoded-word strings in the form =?charset?q?Hello_World?= -ecre = re.compile(r''' - =\? # literal =? - (?P[^?]*?) # non-greedy up to the next ? is the charset - \? # literal ? - (?P[qb]) # either a "q" or a "b", case insensitive - \? # literal ? - (?P.*?) # non-greedy up to the next ?= is the encoded string - \?= # literal ?= - ''', re.VERBOSE | re.IGNORECASE | re.MULTILINE) - -# Field name regexp, including trailing colon, but not separating whitespace, -# according to RFC 2822. Character range is from tilde to exclamation mark. -# For use with .match() -fcre = re.compile(r'[\041-\176]+:$') - -# Find a header embedded in a putative header value. Used to check for -# header injection attack. -_embeded_header = re.compile(r'\n[^ \t]+:') - - -def decode_header(header): - """Decode a message header value without converting charset. - - Returns a list of (string, charset) pairs containing each of the decoded - parts of the header. Charset is None for non-encoded parts of the header, - otherwise a lower-case string containing the name of the character set - specified in the encoded string. - - header may be a string that may or may not contain RFC2047 encoded words, - or it may be a Header object. - - An email.errors.HeaderParseError may be raised when certain decoding error - occurs (e.g. a base64 decoding exception). - """ - # If it is a Header object, we can just return the encoded chunks. - if hasattr(header, '_chunks'): - return [(_charset._encode(string, str(charset)), str(charset)) - for string, charset in header._chunks] - # If no encoding, just return the header with no charset. - if not ecre.search(header): - return [(header, None)] - # First step is to parse all the encoded parts into triplets of the form - # (encoded_string, encoding, charset). For unencoded strings, the last - # two parts will be None. - words = [] - for line in header.splitlines(): - parts = ecre.split(line) - first = True - while parts: - unencoded = parts.pop(0) - if first: - unencoded = unencoded.lstrip() - first = False - if unencoded: - words.append((unencoded, None, None)) - if parts: - charset = parts.pop(0).lower() - encoding = parts.pop(0).lower() - encoded = parts.pop(0) - words.append((encoded, encoding, charset)) - # Now loop over words and remove words that consist of whitespace - # between two encoded strings. - import sys - droplist = [] - for n, w in enumerate(words): - if n>1 and w[1] and words[n-2][1] and words[n-1][0].isspace(): - droplist.append(n-1) - for d in reversed(droplist): - del words[d] - - # The next step is to decode each encoded word by applying the reverse - # base64 or quopri transformation. decoded_words is now a list of the - # form (decoded_word, charset). - decoded_words = [] - for encoded_string, encoding, charset in words: - if encoding is None: - # This is an unencoded word. - decoded_words.append((encoded_string, charset)) - elif encoding == 'q': - word = header_decode(encoded_string) - decoded_words.append((word, charset)) - elif encoding == 'b': - paderr = len(encoded_string) % 4 # Postel's law: add missing padding - if paderr: - encoded_string += '==='[:4 - paderr] - try: - word = base64mime.decode(encoded_string) - except binascii.Error: - raise HeaderParseError('Base64 decoding error') - else: - decoded_words.append((word, charset)) - else: - raise AssertionError('Unexpected encoding: ' + encoding) - # Now convert all words to bytes and collapse consecutive runs of - # similarly encoded words. - collapsed = [] - last_word = last_charset = None - for word, charset in decoded_words: - if isinstance(word, str): - word = bytes(word, 'raw-unicode-escape') - if last_word is None: - last_word = word - last_charset = charset - elif charset != last_charset: - collapsed.append((last_word, last_charset)) - last_word = word - last_charset = charset - elif last_charset is None: - last_word += BSPACE + word - else: - last_word += word - collapsed.append((last_word, last_charset)) - return collapsed - - -def make_header(decoded_seq, maxlinelen=None, header_name=None, - continuation_ws=' '): - """Create a Header from a sequence of pairs as returned by decode_header() - - decode_header() takes a header value string and returns a sequence of - pairs of the format (decoded_string, charset) where charset is the string - name of the character set. - - This function takes one of those sequence of pairs and returns a Header - instance. Optional maxlinelen, header_name, and continuation_ws are as in - the Header constructor. - """ - h = Header(maxlinelen=maxlinelen, header_name=header_name, - continuation_ws=continuation_ws) - for s, charset in decoded_seq: - # None means us-ascii but we can simply pass it on to h.append() - if charset is not None and not isinstance(charset, Charset): - charset = Charset(charset) - h.append(s, charset) - return h - - -class Header(object): - def __init__(self, s=None, charset=None, - maxlinelen=None, header_name=None, - continuation_ws=' ', errors='strict'): - """Create a MIME-compliant header that can contain many character sets. - - Optional s is the initial header value. If None, the initial header - value is not set. You can later append to the header with .append() - method calls. s may be a byte string or a Unicode string, but see the - .append() documentation for semantics. - - Optional charset serves two purposes: it has the same meaning as the - charset argument to the .append() method. It also sets the default - character set for all subsequent .append() calls that omit the charset - argument. If charset is not provided in the constructor, the us-ascii - charset is used both as s's initial charset and as the default for - subsequent .append() calls. - - The maximum line length can be specified explicitly via maxlinelen. For - splitting the first line to a shorter value (to account for the field - header which isn't included in s, e.g. `Subject') pass in the name of - the field in header_name. The default maxlinelen is 78 as recommended - by RFC 2822. - - continuation_ws must be RFC 2822 compliant folding whitespace (usually - either a space or a hard tab) which will be prepended to continuation - lines. - - errors is passed through to the .append() call. - """ - if charset is None: - charset = USASCII - elif not isinstance(charset, Charset): - charset = Charset(charset) - self._charset = charset - self._continuation_ws = continuation_ws - self._chunks = [] - if s is not None: - self.append(s, charset, errors) - if maxlinelen is None: - maxlinelen = MAXLINELEN - self._maxlinelen = maxlinelen - if header_name is None: - self._headerlen = 0 - else: - # Take the separating colon and space into account. - self._headerlen = len(header_name) + 2 - - def __str__(self): - """Return the string value of the header.""" - self._normalize() - uchunks = [] - lastcs = None - lastspace = None - for string, charset in self._chunks: - # We must preserve spaces between encoded and non-encoded word - # boundaries, which means for us we need to add a space when we go - # from a charset to None/us-ascii, or from None/us-ascii to a - # charset. Only do this for the second and subsequent chunks. - # Don't add a space if the None/us-ascii string already has - # a space (trailing or leading depending on transition) - nextcs = charset - if nextcs == _charset.UNKNOWN8BIT: - original_bytes = string.encode('ascii', 'surrogateescape') - string = original_bytes.decode('ascii', 'replace') - if uchunks: - hasspace = string and self._nonctext(string[0]) - if lastcs not in (None, 'us-ascii'): - if nextcs in (None, 'us-ascii') and not hasspace: - uchunks.append(SPACE) - nextcs = None - elif nextcs not in (None, 'us-ascii') and not lastspace: - uchunks.append(SPACE) - lastspace = string and self._nonctext(string[-1]) - lastcs = nextcs - uchunks.append(string) - return EMPTYSTRING.join(uchunks) - - # Rich comparison operators for equality only. BAW: does it make sense to - # have or explicitly disable <, <=, >, >= operators? - def __eq__(self, other): - # other may be a Header or a string. Both are fine so coerce - # ourselves to a unicode (of the unencoded header value), swap the - # args and do another comparison. - return other == str(self) - - def __ne__(self, other): - return not self == other - - def append(self, s, charset=None, errors='strict'): - """Append a string to the MIME header. - - Optional charset, if given, should be a Charset instance or the name - of a character set (which will be converted to a Charset instance). A - value of None (the default) means that the charset given in the - constructor is used. - - s may be a byte string or a Unicode string. If it is a byte string - (i.e. isinstance(s, str) is false), then charset is the encoding of - that byte string, and a UnicodeError will be raised if the string - cannot be decoded with that charset. If s is a Unicode string, then - charset is a hint specifying the character set of the characters in - the string. In either case, when producing an RFC 2822 compliant - header using RFC 2047 rules, the string will be encoded using the - output codec of the charset. If the string cannot be encoded to the - output codec, a UnicodeError will be raised. - - Optional `errors' is passed as the errors argument to the decode - call if s is a byte string. - """ - if charset is None: - charset = self._charset - elif not isinstance(charset, Charset): - charset = Charset(charset) - if not isinstance(s, str): - input_charset = charset.input_codec or 'us-ascii' - if input_charset == _charset.UNKNOWN8BIT: - s = s.decode('us-ascii', 'surrogateescape') - else: - s = s.decode(input_charset, errors) - # Ensure that the bytes we're storing can be decoded to the output - # character set, otherwise an early error is raised. - output_charset = charset.output_codec or 'us-ascii' - if output_charset != _charset.UNKNOWN8BIT: - try: - s.encode(output_charset, errors) - except UnicodeEncodeError: - if output_charset!='us-ascii': - raise - charset = UTF8 - self._chunks.append((s, charset)) - - def _nonctext(self, s): - """True if string s is not a ctext character of RFC822. - """ - return s.isspace() or s in ('(', ')', '\\') - - def encode(self, splitchars=';, \t', maxlinelen=None, linesep='\n'): - r"""Encode a message header into an RFC-compliant format. - - There are many issues involved in converting a given string for use in - an email header. Only certain character sets are readable in most - email clients, and as header strings can only contain a subset of - 7-bit ASCII, care must be taken to properly convert and encode (with - Base64 or quoted-printable) header strings. In addition, there is a - 75-character length limit on any given encoded header field, so - line-wrapping must be performed, even with double-byte character sets. - - Optional maxlinelen specifies the maximum length of each generated - line, exclusive of the linesep string. Individual lines may be longer - than maxlinelen if a folding point cannot be found. The first line - will be shorter by the length of the header name plus ": " if a header - name was specified at Header construction time. The default value for - maxlinelen is determined at header construction time. - - Optional splitchars is a string containing characters which should be - given extra weight by the splitting algorithm during normal header - wrapping. This is in very rough support of RFC 2822's `higher level - syntactic breaks': split points preceded by a splitchar are preferred - during line splitting, with the characters preferred in the order in - which they appear in the string. Space and tab may be included in the - string to indicate whether preference should be given to one over the - other as a split point when other split chars do not appear in the line - being split. Splitchars does not affect RFC 2047 encoded lines. - - Optional linesep is a string to be used to separate the lines of - the value. The default value is the most useful for typical - Python applications, but it can be set to \r\n to produce RFC-compliant - line separators when needed. - """ - self._normalize() - if maxlinelen is None: - maxlinelen = self._maxlinelen - # A maxlinelen of 0 means don't wrap. For all practical purposes, - # choosing a huge number here accomplishes that and makes the - # _ValueFormatter algorithm much simpler. - if maxlinelen == 0: - maxlinelen = 1000000 - formatter = _ValueFormatter(self._headerlen, maxlinelen, - self._continuation_ws, splitchars) - lastcs = None - hasspace = lastspace = None - for string, charset in self._chunks: - if hasspace is not None: - hasspace = string and self._nonctext(string[0]) - import sys - if lastcs not in (None, 'us-ascii'): - if not hasspace or charset not in (None, 'us-ascii'): - formatter.add_transition() - elif charset not in (None, 'us-ascii') and not lastspace: - formatter.add_transition() - lastspace = string and self._nonctext(string[-1]) - lastcs = charset - hasspace = False - lines = string.splitlines() - if lines: - formatter.feed('', lines[0], charset) - else: - formatter.feed('', '', charset) - for line in lines[1:]: - formatter.newline() - if charset.header_encoding is not None: - formatter.feed(self._continuation_ws, ' ' + line.lstrip(), - charset) - else: - sline = line.lstrip() - fws = line[:len(line)-len(sline)] - formatter.feed(fws, sline, charset) - if len(lines) > 1: - formatter.newline() - if self._chunks: - formatter.add_transition() - value = formatter._str(linesep) - if _embeded_header.search(value): - raise HeaderParseError("header value appears to contain " - "an embedded header: {!r}".format(value)) - return value - - def _normalize(self): - # Step 1: Normalize the chunks so that all runs of identical charsets - # get collapsed into a single unicode string. - chunks = [] - last_charset = None - last_chunk = [] - for string, charset in self._chunks: - if charset == last_charset: - last_chunk.append(string) - else: - if last_charset is not None: - chunks.append((SPACE.join(last_chunk), last_charset)) - last_chunk = [string] - last_charset = charset - if last_chunk: - chunks.append((SPACE.join(last_chunk), last_charset)) - self._chunks = chunks - - -class _ValueFormatter(object): - def __init__(self, headerlen, maxlen, continuation_ws, splitchars): - self._maxlen = maxlen - self._continuation_ws = continuation_ws - self._continuation_ws_len = len(continuation_ws) - self._splitchars = splitchars - self._lines = [] - self._current_line = _Accumulator(headerlen) - - def _str(self, linesep): - self.newline() - return linesep.join(self._lines) - - def __str__(self): - return self._str(NL) - - def newline(self): - end_of_line = self._current_line.pop() - if end_of_line != (' ', ''): - self._current_line.push(*end_of_line) - if len(self._current_line) > 0: - if self._current_line.is_onlyws(): - self._lines[-1] += str(self._current_line) - else: - self._lines.append(str(self._current_line)) - self._current_line.reset() - - def add_transition(self): - self._current_line.push(' ', '') - - def feed(self, fws, string, charset): - # If the charset has no header encoding (i.e. it is an ASCII encoding) - # then we must split the header at the "highest level syntactic break" - # possible. Note that we don't have a lot of smarts about field - # syntax; we just try to break on semi-colons, then commas, then - # whitespace. Eventually, this should be pluggable. - if charset.header_encoding is None: - self._ascii_split(fws, string, self._splitchars) - return - # Otherwise, we're doing either a Base64 or a quoted-printable - # encoding which means we don't need to split the line on syntactic - # breaks. We can basically just find enough characters to fit on the - # current line, minus the RFC 2047 chrome. What makes this trickier - # though is that we have to split at octet boundaries, not character - # boundaries but it's only safe to split at character boundaries so at - # best we can only get close. - encoded_lines = charset.header_encode_lines(string, self._maxlengths()) - # The first element extends the current line, but if it's None then - # nothing more fit on the current line so start a new line. - try: - first_line = encoded_lines.pop(0) - except IndexError: - # There are no encoded lines, so we're done. - return - if first_line is not None: - self._append_chunk(fws, first_line) - try: - last_line = encoded_lines.pop() - except IndexError: - # There was only one line. - return - self.newline() - self._current_line.push(self._continuation_ws, last_line) - # Everything else are full lines in themselves. - for line in encoded_lines: - self._lines.append(self._continuation_ws + line) - - def _maxlengths(self): - # The first line's length. - yield self._maxlen - len(self._current_line) - while True: - yield self._maxlen - self._continuation_ws_len - - def _ascii_split(self, fws, string, splitchars): - # The RFC 2822 header folding algorithm is simple in principle but - # complex in practice. Lines may be folded any place where "folding - # white space" appears by inserting a linesep character in front of the - # FWS. The complication is that not all spaces or tabs qualify as FWS, - # and we are also supposed to prefer to break at "higher level - # syntactic breaks". We can't do either of these without intimate - # knowledge of the structure of structured headers, which we don't have - # here. So the best we can do here is prefer to break at the specified - # splitchars, and hope that we don't choose any spaces or tabs that - # aren't legal FWS. (This is at least better than the old algorithm, - # where we would sometimes *introduce* FWS after a splitchar, or the - # algorithm before that, where we would turn all white space runs into - # single spaces or tabs.) - parts = re.split("(["+FWS+"]+)", fws+string) - if parts[0]: - parts[:0] = [''] - else: - parts.pop(0) - for fws, part in zip(*[iter(parts)]*2): - self._append_chunk(fws, part) - - def _append_chunk(self, fws, string): - self._current_line.push(fws, string) - if len(self._current_line) > self._maxlen: - # Find the best split point, working backward from the end. - # There might be none, on a long first line. - for ch in self._splitchars: - for i in range(self._current_line.part_count()-1, 0, -1): - if ch.isspace(): - fws = self._current_line[i][0] - if fws and fws[0]==ch: - break - prevpart = self._current_line[i-1][1] - if prevpart and prevpart[-1]==ch: - break - else: - continue - break - else: - fws, part = self._current_line.pop() - if self._current_line._initial_size > 0: - # There will be a header, so leave it on a line by itself. - self.newline() - if not fws: - # We don't use continuation_ws here because the whitespace - # after a header should always be a space. - fws = ' ' - self._current_line.push(fws, part) - return - remainder = self._current_line.pop_from(i) - self._lines.append(str(self._current_line)) - self._current_line.reset(remainder) - - -class _Accumulator(list): - - def __init__(self, initial_size=0): - self._initial_size = initial_size - super().__init__() - - def push(self, fws, string): - self.append((fws, string)) - - def pop_from(self, i=0): - popped = self[i:] - self[i:] = [] - return popped - - def pop(self): - if self.part_count()==0: - return ('', '') - return super().pop() - - def __len__(self): - return sum((len(fws)+len(part) for fws, part in self), - self._initial_size) - - def __str__(self): - return EMPTYSTRING.join((EMPTYSTRING.join((fws, part)) - for fws, part in self)) - - def reset(self, startval=None): - if startval is None: - startval = [] - self[:] = startval - self._initial_size = 0 - - def is_onlyws(self): - return self._initial_size==0 and (not self or str(self).isspace()) - - def part_count(self): - return super().__len__() diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/headerregistry.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/headerregistry.py deleted file mode 100644 index 9aaad65a..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/headerregistry.py +++ /dev/null @@ -1,592 +0,0 @@ -"""Representing and manipulating email headers via custom objects. - -This module provides an implementation of the HeaderRegistry API. -The implementation is designed to flexibly follow RFC5322 rules. - -Eventually HeaderRegistry will be a public API, but it isn't yet, -and will probably change some before that happens. - -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -from future.builtins import super -from future.builtins import str -from future.utils import text_to_native_str -from future.backports.email import utils -from future.backports.email import errors -from future.backports.email import _header_value_parser as parser - -class Address(object): - - def __init__(self, display_name='', username='', domain='', addr_spec=None): - """Create an object represeting a full email address. - - An address can have a 'display_name', a 'username', and a 'domain'. In - addition to specifying the username and domain separately, they may be - specified together by using the addr_spec keyword *instead of* the - username and domain keywords. If an addr_spec string is specified it - must be properly quoted according to RFC 5322 rules; an error will be - raised if it is not. - - An Address object has display_name, username, domain, and addr_spec - attributes, all of which are read-only. The addr_spec and the string - value of the object are both quoted according to RFC5322 rules, but - without any Content Transfer Encoding. - - """ - # This clause with its potential 'raise' may only happen when an - # application program creates an Address object using an addr_spec - # keyword. The email library code itself must always supply username - # and domain. - if addr_spec is not None: - if username or domain: - raise TypeError("addrspec specified when username and/or " - "domain also specified") - a_s, rest = parser.get_addr_spec(addr_spec) - if rest: - raise ValueError("Invalid addr_spec; only '{}' " - "could be parsed from '{}'".format( - a_s, addr_spec)) - if a_s.all_defects: - raise a_s.all_defects[0] - username = a_s.local_part - domain = a_s.domain - self._display_name = display_name - self._username = username - self._domain = domain - - @property - def display_name(self): - return self._display_name - - @property - def username(self): - return self._username - - @property - def domain(self): - return self._domain - - @property - def addr_spec(self): - """The addr_spec (username@domain) portion of the address, quoted - according to RFC 5322 rules, but with no Content Transfer Encoding. - """ - nameset = set(self.username) - if len(nameset) > len(nameset-parser.DOT_ATOM_ENDS): - lp = parser.quote_string(self.username) - else: - lp = self.username - if self.domain: - return lp + '@' + self.domain - if not lp: - return '<>' - return lp - - def __repr__(self): - return "Address(display_name={!r}, username={!r}, domain={!r})".format( - self.display_name, self.username, self.domain) - - def __str__(self): - nameset = set(self.display_name) - if len(nameset) > len(nameset-parser.SPECIALS): - disp = parser.quote_string(self.display_name) - else: - disp = self.display_name - if disp: - addr_spec = '' if self.addr_spec=='<>' else self.addr_spec - return "{} <{}>".format(disp, addr_spec) - return self.addr_spec - - def __eq__(self, other): - if type(other) != type(self): - return False - return (self.display_name == other.display_name and - self.username == other.username and - self.domain == other.domain) - - -class Group(object): - - def __init__(self, display_name=None, addresses=None): - """Create an object representing an address group. - - An address group consists of a display_name followed by colon and an - list of addresses (see Address) terminated by a semi-colon. The Group - is created by specifying a display_name and a possibly empty list of - Address objects. A Group can also be used to represent a single - address that is not in a group, which is convenient when manipulating - lists that are a combination of Groups and individual Addresses. In - this case the display_name should be set to None. In particular, the - string representation of a Group whose display_name is None is the same - as the Address object, if there is one and only one Address object in - the addresses list. - - """ - self._display_name = display_name - self._addresses = tuple(addresses) if addresses else tuple() - - @property - def display_name(self): - return self._display_name - - @property - def addresses(self): - return self._addresses - - def __repr__(self): - return "Group(display_name={!r}, addresses={!r}".format( - self.display_name, self.addresses) - - def __str__(self): - if self.display_name is None and len(self.addresses)==1: - return str(self.addresses[0]) - disp = self.display_name - if disp is not None: - nameset = set(disp) - if len(nameset) > len(nameset-parser.SPECIALS): - disp = parser.quote_string(disp) - adrstr = ", ".join(str(x) for x in self.addresses) - adrstr = ' ' + adrstr if adrstr else adrstr - return "{}:{};".format(disp, adrstr) - - def __eq__(self, other): - if type(other) != type(self): - return False - return (self.display_name == other.display_name and - self.addresses == other.addresses) - - -# Header Classes # - -class BaseHeader(str): - - """Base class for message headers. - - Implements generic behavior and provides tools for subclasses. - - A subclass must define a classmethod named 'parse' that takes an unfolded - value string and a dictionary as its arguments. The dictionary will - contain one key, 'defects', initialized to an empty list. After the call - the dictionary must contain two additional keys: parse_tree, set to the - parse tree obtained from parsing the header, and 'decoded', set to the - string value of the idealized representation of the data from the value. - (That is, encoded words are decoded, and values that have canonical - representations are so represented.) - - The defects key is intended to collect parsing defects, which the message - parser will subsequently dispose of as appropriate. The parser should not, - insofar as practical, raise any errors. Defects should be added to the - list instead. The standard header parsers register defects for RFC - compliance issues, for obsolete RFC syntax, and for unrecoverable parsing - errors. - - The parse method may add additional keys to the dictionary. In this case - the subclass must define an 'init' method, which will be passed the - dictionary as its keyword arguments. The method should use (usually by - setting them as the value of similarly named attributes) and remove all the - extra keys added by its parse method, and then use super to call its parent - class with the remaining arguments and keywords. - - The subclass should also make sure that a 'max_count' attribute is defined - that is either None or 1. XXX: need to better define this API. - - """ - - def __new__(cls, name, value): - kwds = {'defects': []} - cls.parse(value, kwds) - if utils._has_surrogates(kwds['decoded']): - kwds['decoded'] = utils._sanitize(kwds['decoded']) - self = str.__new__(cls, kwds['decoded']) - # del kwds['decoded'] - self.init(name, **kwds) - return self - - def init(self, name, **_3to2kwargs): - defects = _3to2kwargs['defects']; del _3to2kwargs['defects'] - parse_tree = _3to2kwargs['parse_tree']; del _3to2kwargs['parse_tree'] - self._name = name - self._parse_tree = parse_tree - self._defects = defects - - @property - def name(self): - return self._name - - @property - def defects(self): - return tuple(self._defects) - - def __reduce__(self): - return ( - _reconstruct_header, - ( - self.__class__.__name__, - self.__class__.__bases__, - str(self), - ), - self.__dict__) - - @classmethod - def _reconstruct(cls, value): - return str.__new__(cls, value) - - def fold(self, **_3to2kwargs): - policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - """Fold header according to policy. - - The parsed representation of the header is folded according to - RFC5322 rules, as modified by the policy. If the parse tree - contains surrogateescaped bytes, the bytes are CTE encoded using - the charset 'unknown-8bit". - - Any non-ASCII characters in the parse tree are CTE encoded using - charset utf-8. XXX: make this a policy setting. - - The returned value is an ASCII-only string possibly containing linesep - characters, and ending with a linesep character. The string includes - the header name and the ': ' separator. - - """ - # At some point we need to only put fws here if it was in the source. - header = parser.Header([ - parser.HeaderLabel([ - parser.ValueTerminal(self.name, 'header-name'), - parser.ValueTerminal(':', 'header-sep')]), - parser.CFWSList([parser.WhiteSpaceTerminal(' ', 'fws')]), - self._parse_tree]) - return header.fold(policy=policy) - - -def _reconstruct_header(cls_name, bases, value): - return type(text_to_native_str(cls_name), bases, {})._reconstruct(value) - - -class UnstructuredHeader(object): - - max_count = None - value_parser = staticmethod(parser.get_unstructured) - - @classmethod - def parse(cls, value, kwds): - kwds['parse_tree'] = cls.value_parser(value) - kwds['decoded'] = str(kwds['parse_tree']) - - -class UniqueUnstructuredHeader(UnstructuredHeader): - - max_count = 1 - - -class DateHeader(object): - - """Header whose value consists of a single timestamp. - - Provides an additional attribute, datetime, which is either an aware - datetime using a timezone, or a naive datetime if the timezone - in the input string is -0000. Also accepts a datetime as input. - The 'value' attribute is the normalized form of the timestamp, - which means it is the output of format_datetime on the datetime. - """ - - max_count = None - - # This is used only for folding, not for creating 'decoded'. - value_parser = staticmethod(parser.get_unstructured) - - @classmethod - def parse(cls, value, kwds): - if not value: - kwds['defects'].append(errors.HeaderMissingRequiredValue()) - kwds['datetime'] = None - kwds['decoded'] = '' - kwds['parse_tree'] = parser.TokenList() - return - if isinstance(value, str): - value = utils.parsedate_to_datetime(value) - kwds['datetime'] = value - kwds['decoded'] = utils.format_datetime(kwds['datetime']) - kwds['parse_tree'] = cls.value_parser(kwds['decoded']) - - def init(self, *args, **kw): - self._datetime = kw.pop('datetime') - super().init(*args, **kw) - - @property - def datetime(self): - return self._datetime - - -class UniqueDateHeader(DateHeader): - - max_count = 1 - - -class AddressHeader(object): - - max_count = None - - @staticmethod - def value_parser(value): - address_list, value = parser.get_address_list(value) - assert not value, 'this should not happen' - return address_list - - @classmethod - def parse(cls, value, kwds): - if isinstance(value, str): - # We are translating here from the RFC language (address/mailbox) - # to our API language (group/address). - kwds['parse_tree'] = address_list = cls.value_parser(value) - groups = [] - for addr in address_list.addresses: - groups.append(Group(addr.display_name, - [Address(mb.display_name or '', - mb.local_part or '', - mb.domain or '') - for mb in addr.all_mailboxes])) - defects = list(address_list.all_defects) - else: - # Assume it is Address/Group stuff - if not hasattr(value, '__iter__'): - value = [value] - groups = [Group(None, [item]) if not hasattr(item, 'addresses') - else item - for item in value] - defects = [] - kwds['groups'] = groups - kwds['defects'] = defects - kwds['decoded'] = ', '.join([str(item) for item in groups]) - if 'parse_tree' not in kwds: - kwds['parse_tree'] = cls.value_parser(kwds['decoded']) - - def init(self, *args, **kw): - self._groups = tuple(kw.pop('groups')) - self._addresses = None - super().init(*args, **kw) - - @property - def groups(self): - return self._groups - - @property - def addresses(self): - if self._addresses is None: - self._addresses = tuple([address for group in self._groups - for address in group.addresses]) - return self._addresses - - -class UniqueAddressHeader(AddressHeader): - - max_count = 1 - - -class SingleAddressHeader(AddressHeader): - - @property - def address(self): - if len(self.addresses)!=1: - raise ValueError(("value of single address header {} is not " - "a single address").format(self.name)) - return self.addresses[0] - - -class UniqueSingleAddressHeader(SingleAddressHeader): - - max_count = 1 - - -class MIMEVersionHeader(object): - - max_count = 1 - - value_parser = staticmethod(parser.parse_mime_version) - - @classmethod - def parse(cls, value, kwds): - kwds['parse_tree'] = parse_tree = cls.value_parser(value) - kwds['decoded'] = str(parse_tree) - kwds['defects'].extend(parse_tree.all_defects) - kwds['major'] = None if parse_tree.minor is None else parse_tree.major - kwds['minor'] = parse_tree.minor - if parse_tree.minor is not None: - kwds['version'] = '{}.{}'.format(kwds['major'], kwds['minor']) - else: - kwds['version'] = None - - def init(self, *args, **kw): - self._version = kw.pop('version') - self._major = kw.pop('major') - self._minor = kw.pop('minor') - super().init(*args, **kw) - - @property - def major(self): - return self._major - - @property - def minor(self): - return self._minor - - @property - def version(self): - return self._version - - -class ParameterizedMIMEHeader(object): - - # Mixin that handles the params dict. Must be subclassed and - # a property value_parser for the specific header provided. - - max_count = 1 - - @classmethod - def parse(cls, value, kwds): - kwds['parse_tree'] = parse_tree = cls.value_parser(value) - kwds['decoded'] = str(parse_tree) - kwds['defects'].extend(parse_tree.all_defects) - if parse_tree.params is None: - kwds['params'] = {} - else: - # The MIME RFCs specify that parameter ordering is arbitrary. - kwds['params'] = dict((utils._sanitize(name).lower(), - utils._sanitize(value)) - for name, value in parse_tree.params) - - def init(self, *args, **kw): - self._params = kw.pop('params') - super().init(*args, **kw) - - @property - def params(self): - return self._params.copy() - - -class ContentTypeHeader(ParameterizedMIMEHeader): - - value_parser = staticmethod(parser.parse_content_type_header) - - def init(self, *args, **kw): - super().init(*args, **kw) - self._maintype = utils._sanitize(self._parse_tree.maintype) - self._subtype = utils._sanitize(self._parse_tree.subtype) - - @property - def maintype(self): - return self._maintype - - @property - def subtype(self): - return self._subtype - - @property - def content_type(self): - return self.maintype + '/' + self.subtype - - -class ContentDispositionHeader(ParameterizedMIMEHeader): - - value_parser = staticmethod(parser.parse_content_disposition_header) - - def init(self, *args, **kw): - super().init(*args, **kw) - cd = self._parse_tree.content_disposition - self._content_disposition = cd if cd is None else utils._sanitize(cd) - - @property - def content_disposition(self): - return self._content_disposition - - -class ContentTransferEncodingHeader(object): - - max_count = 1 - - value_parser = staticmethod(parser.parse_content_transfer_encoding_header) - - @classmethod - def parse(cls, value, kwds): - kwds['parse_tree'] = parse_tree = cls.value_parser(value) - kwds['decoded'] = str(parse_tree) - kwds['defects'].extend(parse_tree.all_defects) - - def init(self, *args, **kw): - super().init(*args, **kw) - self._cte = utils._sanitize(self._parse_tree.cte) - - @property - def cte(self): - return self._cte - - -# The header factory # - -_default_header_map = { - 'subject': UniqueUnstructuredHeader, - 'date': UniqueDateHeader, - 'resent-date': DateHeader, - 'orig-date': UniqueDateHeader, - 'sender': UniqueSingleAddressHeader, - 'resent-sender': SingleAddressHeader, - 'to': UniqueAddressHeader, - 'resent-to': AddressHeader, - 'cc': UniqueAddressHeader, - 'resent-cc': AddressHeader, - 'bcc': UniqueAddressHeader, - 'resent-bcc': AddressHeader, - 'from': UniqueAddressHeader, - 'resent-from': AddressHeader, - 'reply-to': UniqueAddressHeader, - 'mime-version': MIMEVersionHeader, - 'content-type': ContentTypeHeader, - 'content-disposition': ContentDispositionHeader, - 'content-transfer-encoding': ContentTransferEncodingHeader, - } - -class HeaderRegistry(object): - - """A header_factory and header registry.""" - - def __init__(self, base_class=BaseHeader, default_class=UnstructuredHeader, - use_default_map=True): - """Create a header_factory that works with the Policy API. - - base_class is the class that will be the last class in the created - header class's __bases__ list. default_class is the class that will be - used if "name" (see __call__) does not appear in the registry. - use_default_map controls whether or not the default mapping of names to - specialized classes is copied in to the registry when the factory is - created. The default is True. - - """ - self.registry = {} - self.base_class = base_class - self.default_class = default_class - if use_default_map: - self.registry.update(_default_header_map) - - def map_to_type(self, name, cls): - """Register cls as the specialized class for handling "name" headers. - - """ - self.registry[name.lower()] = cls - - def __getitem__(self, name): - cls = self.registry.get(name.lower(), self.default_class) - return type(text_to_native_str('_'+cls.__name__), (cls, self.base_class), {}) - - def __call__(self, name, value): - """Create a header instance for header 'name' from 'value'. - - Creates a header instance by creating a specialized class for parsing - and representing the specified header by combining the factory - base_class with a specialized class from the registry or the - default_class, and passing the name and value to the constructed - class's constructor. - - """ - return self[name](name, value) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/iterators.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/iterators.py deleted file mode 100644 index 82d320f8..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/iterators.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Various types of useful iterators and generators.""" -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = [ - 'body_line_iterator', - 'typed_subpart_iterator', - 'walk', - # Do not include _structure() since it's part of the debugging API. - ] - -import sys -from io import StringIO - - -# This function will become a method of the Message class -def walk(self): - """Walk over the message tree, yielding each subpart. - - The walk is performed in depth-first order. This method is a - generator. - """ - yield self - if self.is_multipart(): - for subpart in self.get_payload(): - for subsubpart in subpart.walk(): - yield subsubpart - - -# These two functions are imported into the Iterators.py interface module. -def body_line_iterator(msg, decode=False): - """Iterate over the parts, returning string payloads line-by-line. - - Optional decode (default False) is passed through to .get_payload(). - """ - for subpart in msg.walk(): - payload = subpart.get_payload(decode=decode) - if isinstance(payload, str): - for line in StringIO(payload): - yield line - - -def typed_subpart_iterator(msg, maintype='text', subtype=None): - """Iterate over the subparts with a given MIME type. - - Use `maintype' as the main MIME type to match against; this defaults to - "text". Optional `subtype' is the MIME subtype to match against; if - omitted, only the main type is matched. - """ - for subpart in msg.walk(): - if subpart.get_content_maintype() == maintype: - if subtype is None or subpart.get_content_subtype() == subtype: - yield subpart - - -def _structure(msg, fp=None, level=0, include_default=False): - """A handy debugging aid""" - if fp is None: - fp = sys.stdout - tab = ' ' * (level * 4) - print(tab + msg.get_content_type(), end='', file=fp) - if include_default: - print(' [%s]' % msg.get_default_type(), file=fp) - else: - print(file=fp) - if msg.is_multipart(): - for subpart in msg.get_payload(): - _structure(subpart, fp, level+1, include_default) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/message.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/message.py deleted file mode 100644 index d8d9615d..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/message.py +++ /dev/null @@ -1,882 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2001-2007 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Basic message object for the email package object model.""" -from __future__ import absolute_import, division, unicode_literals -from future.builtins import list, range, str, zip - -__all__ = ['Message'] - -import re -import uu -import base64 -import binascii -from io import BytesIO, StringIO - -# Intrapackage imports -from future.utils import as_native_str -from future.backports.email import utils -from future.backports.email import errors -from future.backports.email._policybase import compat32 -from future.backports.email import charset as _charset -from future.backports.email._encoded_words import decode_b -Charset = _charset.Charset - -SEMISPACE = '; ' - -# Regular expression that matches `special' characters in parameters, the -# existence of which force quoting of the parameter value. -tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]') - - -def _splitparam(param): - # Split header parameters. BAW: this may be too simple. It isn't - # strictly RFC 2045 (section 5.1) compliant, but it catches most headers - # found in the wild. We may eventually need a full fledged parser. - # RDM: we might have a Header here; for now just stringify it. - a, sep, b = str(param).partition(';') - if not sep: - return a.strip(), None - return a.strip(), b.strip() - -def _formatparam(param, value=None, quote=True): - """Convenience function to format and return a key=value pair. - - This will quote the value if needed or if quote is true. If value is a - three tuple (charset, language, value), it will be encoded according - to RFC2231 rules. If it contains non-ascii characters it will likewise - be encoded according to RFC2231 rules, using the utf-8 charset and - a null language. - """ - if value is not None and len(value) > 0: - # A tuple is used for RFC 2231 encoded parameter values where items - # are (charset, language, value). charset is a string, not a Charset - # instance. RFC 2231 encoded values are never quoted, per RFC. - if isinstance(value, tuple): - # Encode as per RFC 2231 - param += '*' - value = utils.encode_rfc2231(value[2], value[0], value[1]) - return '%s=%s' % (param, value) - else: - try: - value.encode('ascii') - except UnicodeEncodeError: - param += '*' - value = utils.encode_rfc2231(value, 'utf-8', '') - return '%s=%s' % (param, value) - # BAW: Please check this. I think that if quote is set it should - # force quoting even if not necessary. - if quote or tspecials.search(value): - return '%s="%s"' % (param, utils.quote(value)) - else: - return '%s=%s' % (param, value) - else: - return param - -def _parseparam(s): - # RDM This might be a Header, so for now stringify it. - s = ';' + str(s) - plist = [] - while s[:1] == ';': - s = s[1:] - end = s.find(';') - while end > 0 and (s.count('"', 0, end) - s.count('\\"', 0, end)) % 2: - end = s.find(';', end + 1) - if end < 0: - end = len(s) - f = s[:end] - if '=' in f: - i = f.index('=') - f = f[:i].strip().lower() + '=' + f[i+1:].strip() - plist.append(f.strip()) - s = s[end:] - return plist - - -def _unquotevalue(value): - # This is different than utils.collapse_rfc2231_value() because it doesn't - # try to convert the value to a unicode. Message.get_param() and - # Message.get_params() are both currently defined to return the tuple in - # the face of RFC 2231 parameters. - if isinstance(value, tuple): - return value[0], value[1], utils.unquote(value[2]) - else: - return utils.unquote(value) - - -class Message(object): - """Basic message object. - - A message object is defined as something that has a bunch of RFC 2822 - headers and a payload. It may optionally have an envelope header - (a.k.a. Unix-From or From_ header). If the message is a container (i.e. a - multipart or a message/rfc822), then the payload is a list of Message - objects, otherwise it is a string. - - Message objects implement part of the `mapping' interface, which assumes - there is exactly one occurrence of the header per message. Some headers - do in fact appear multiple times (e.g. Received) and for those headers, - you must use the explicit API to set or get all the headers. Not all of - the mapping methods are implemented. - """ - def __init__(self, policy=compat32): - self.policy = policy - self._headers = list() - self._unixfrom = None - self._payload = None - self._charset = None - # Defaults for multipart messages - self.preamble = self.epilogue = None - self.defects = [] - # Default content type - self._default_type = 'text/plain' - - @as_native_str(encoding='utf-8') - def __str__(self): - """Return the entire formatted message as a string. - This includes the headers, body, and envelope header. - """ - return self.as_string() - - def as_string(self, unixfrom=False, maxheaderlen=0): - """Return the entire formatted message as a (unicode) string. - Optional `unixfrom' when True, means include the Unix From_ envelope - header. - - This is a convenience method and may not generate the message exactly - as you intend. For more flexibility, use the flatten() method of a - Generator instance. - """ - from future.backports.email.generator import Generator - fp = StringIO() - g = Generator(fp, mangle_from_=False, maxheaderlen=maxheaderlen) - g.flatten(self, unixfrom=unixfrom) - return fp.getvalue() - - def is_multipart(self): - """Return True if the message consists of multiple parts.""" - return isinstance(self._payload, list) - - # - # Unix From_ line - # - def set_unixfrom(self, unixfrom): - self._unixfrom = unixfrom - - def get_unixfrom(self): - return self._unixfrom - - # - # Payload manipulation. - # - def attach(self, payload): - """Add the given payload to the current payload. - - The current payload will always be a list of objects after this method - is called. If you want to set the payload to a scalar object, use - set_payload() instead. - """ - if self._payload is None: - self._payload = [payload] - else: - self._payload.append(payload) - - def get_payload(self, i=None, decode=False): - """Return a reference to the payload. - - The payload will either be a list object or a string. If you mutate - the list object, you modify the message's payload in place. Optional - i returns that index into the payload. - - Optional decode is a flag indicating whether the payload should be - decoded or not, according to the Content-Transfer-Encoding header - (default is False). - - When True and the message is not a multipart, the payload will be - decoded if this header's value is `quoted-printable' or `base64'. If - some other encoding is used, or the header is missing, or if the - payload has bogus data (i.e. bogus base64 or uuencoded data), the - payload is returned as-is. - - If the message is a multipart and the decode flag is True, then None - is returned. - """ - # Here is the logic table for this code, based on the email5.0.0 code: - # i decode is_multipart result - # ------ ------ ------------ ------------------------------ - # None True True None - # i True True None - # None False True _payload (a list) - # i False True _payload element i (a Message) - # i False False error (not a list) - # i True False error (not a list) - # None False False _payload - # None True False _payload decoded (bytes) - # Note that Barry planned to factor out the 'decode' case, but that - # isn't so easy now that we handle the 8 bit data, which needs to be - # converted in both the decode and non-decode path. - if self.is_multipart(): - if decode: - return None - if i is None: - return self._payload - else: - return self._payload[i] - # For backward compatibility, Use isinstance and this error message - # instead of the more logical is_multipart test. - if i is not None and not isinstance(self._payload, list): - raise TypeError('Expected list, got %s' % type(self._payload)) - payload = self._payload - # cte might be a Header, so for now stringify it. - cte = str(self.get('content-transfer-encoding', '')).lower() - # payload may be bytes here. - if isinstance(payload, str): - payload = str(payload) # for Python-Future, so surrogateescape works - if utils._has_surrogates(payload): - bpayload = payload.encode('ascii', 'surrogateescape') - if not decode: - try: - payload = bpayload.decode(self.get_param('charset', 'ascii'), 'replace') - except LookupError: - payload = bpayload.decode('ascii', 'replace') - elif decode: - try: - bpayload = payload.encode('ascii') - except UnicodeError: - # This won't happen for RFC compliant messages (messages - # containing only ASCII codepoints in the unicode input). - # If it does happen, turn the string into bytes in a way - # guaranteed not to fail. - bpayload = payload.encode('raw-unicode-escape') - if not decode: - return payload - if cte == 'quoted-printable': - return utils._qdecode(bpayload) - elif cte == 'base64': - # XXX: this is a bit of a hack; decode_b should probably be factored - # out somewhere, but I haven't figured out where yet. - value, defects = decode_b(b''.join(bpayload.splitlines())) - for defect in defects: - self.policy.handle_defect(self, defect) - return value - elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'): - in_file = BytesIO(bpayload) - out_file = BytesIO() - try: - uu.decode(in_file, out_file, quiet=True) - return out_file.getvalue() - except uu.Error: - # Some decoding problem - return bpayload - if isinstance(payload, str): - return bpayload - return payload - - def set_payload(self, payload, charset=None): - """Set the payload to the given value. - - Optional charset sets the message's default character set. See - set_charset() for details. - """ - self._payload = payload - if charset is not None: - self.set_charset(charset) - - def set_charset(self, charset): - """Set the charset of the payload to a given character set. - - charset can be a Charset instance, a string naming a character set, or - None. If it is a string it will be converted to a Charset instance. - If charset is None, the charset parameter will be removed from the - Content-Type field. Anything else will generate a TypeError. - - The message will be assumed to be of type text/* encoded with - charset.input_charset. It will be converted to charset.output_charset - and encoded properly, if needed, when generating the plain text - representation of the message. MIME headers (MIME-Version, - Content-Type, Content-Transfer-Encoding) will be added as needed. - """ - if charset is None: - self.del_param('charset') - self._charset = None - return - if not isinstance(charset, Charset): - charset = Charset(charset) - self._charset = charset - if 'MIME-Version' not in self: - self.add_header('MIME-Version', '1.0') - if 'Content-Type' not in self: - self.add_header('Content-Type', 'text/plain', - charset=charset.get_output_charset()) - else: - self.set_param('charset', charset.get_output_charset()) - if charset != charset.get_output_charset(): - self._payload = charset.body_encode(self._payload) - if 'Content-Transfer-Encoding' not in self: - cte = charset.get_body_encoding() - try: - cte(self) - except TypeError: - self._payload = charset.body_encode(self._payload) - self.add_header('Content-Transfer-Encoding', cte) - - def get_charset(self): - """Return the Charset instance associated with the message's payload. - """ - return self._charset - - # - # MAPPING INTERFACE (partial) - # - def __len__(self): - """Return the total number of headers, including duplicates.""" - return len(self._headers) - - def __getitem__(self, name): - """Get a header value. - - Return None if the header is missing instead of raising an exception. - - Note that if the header appeared multiple times, exactly which - occurrence gets returned is undefined. Use get_all() to get all - the values matching a header field name. - """ - return self.get(name) - - def __setitem__(self, name, val): - """Set the value of a header. - - Note: this does not overwrite an existing header with the same field - name. Use __delitem__() first to delete any existing headers. - """ - max_count = self.policy.header_max_count(name) - if max_count: - lname = name.lower() - found = 0 - for k, v in self._headers: - if k.lower() == lname: - found += 1 - if found >= max_count: - raise ValueError("There may be at most {} {} headers " - "in a message".format(max_count, name)) - self._headers.append(self.policy.header_store_parse(name, val)) - - def __delitem__(self, name): - """Delete all occurrences of a header, if present. - - Does not raise an exception if the header is missing. - """ - name = name.lower() - newheaders = list() - for k, v in self._headers: - if k.lower() != name: - newheaders.append((k, v)) - self._headers = newheaders - - def __contains__(self, name): - return name.lower() in [k.lower() for k, v in self._headers] - - def __iter__(self): - for field, value in self._headers: - yield field - - def keys(self): - """Return a list of all the message's header field names. - - These will be sorted in the order they appeared in the original - message, or were added to the message, and may contain duplicates. - Any fields deleted and re-inserted are always appended to the header - list. - """ - return [k for k, v in self._headers] - - def values(self): - """Return a list of all the message's header values. - - These will be sorted in the order they appeared in the original - message, or were added to the message, and may contain duplicates. - Any fields deleted and re-inserted are always appended to the header - list. - """ - return [self.policy.header_fetch_parse(k, v) - for k, v in self._headers] - - def items(self): - """Get all the message's header fields and values. - - These will be sorted in the order they appeared in the original - message, or were added to the message, and may contain duplicates. - Any fields deleted and re-inserted are always appended to the header - list. - """ - return [(k, self.policy.header_fetch_parse(k, v)) - for k, v in self._headers] - - def get(self, name, failobj=None): - """Get a header value. - - Like __getitem__() but return failobj instead of None when the field - is missing. - """ - name = name.lower() - for k, v in self._headers: - if k.lower() == name: - return self.policy.header_fetch_parse(k, v) - return failobj - - # - # "Internal" methods (public API, but only intended for use by a parser - # or generator, not normal application code. - # - - def set_raw(self, name, value): - """Store name and value in the model without modification. - - This is an "internal" API, intended only for use by a parser. - """ - self._headers.append((name, value)) - - def raw_items(self): - """Return the (name, value) header pairs without modification. - - This is an "internal" API, intended only for use by a generator. - """ - return iter(self._headers.copy()) - - # - # Additional useful stuff - # - - def get_all(self, name, failobj=None): - """Return a list of all the values for the named field. - - These will be sorted in the order they appeared in the original - message, and may contain duplicates. Any fields deleted and - re-inserted are always appended to the header list. - - If no such fields exist, failobj is returned (defaults to None). - """ - values = [] - name = name.lower() - for k, v in self._headers: - if k.lower() == name: - values.append(self.policy.header_fetch_parse(k, v)) - if not values: - return failobj - return values - - def add_header(self, _name, _value, **_params): - """Extended header setting. - - name is the header field to add. keyword arguments can be used to set - additional parameters for the header field, with underscores converted - to dashes. Normally the parameter will be added as key="value" unless - value is None, in which case only the key will be added. If a - parameter value contains non-ASCII characters it can be specified as a - three-tuple of (charset, language, value), in which case it will be - encoded according to RFC2231 rules. Otherwise it will be encoded using - the utf-8 charset and a language of ''. - - Examples: - - msg.add_header('content-disposition', 'attachment', filename='bud.gif') - msg.add_header('content-disposition', 'attachment', - filename=('utf-8', '', 'Fußballer.ppt')) - msg.add_header('content-disposition', 'attachment', - filename='Fußballer.ppt')) - """ - parts = [] - for k, v in _params.items(): - if v is None: - parts.append(k.replace('_', '-')) - else: - parts.append(_formatparam(k.replace('_', '-'), v)) - if _value is not None: - parts.insert(0, _value) - self[_name] = SEMISPACE.join(parts) - - def replace_header(self, _name, _value): - """Replace a header. - - Replace the first matching header found in the message, retaining - header order and case. If no matching header was found, a KeyError is - raised. - """ - _name = _name.lower() - for i, (k, v) in zip(range(len(self._headers)), self._headers): - if k.lower() == _name: - self._headers[i] = self.policy.header_store_parse(k, _value) - break - else: - raise KeyError(_name) - - # - # Use these three methods instead of the three above. - # - - def get_content_type(self): - """Return the message's content type. - - The returned string is coerced to lower case of the form - `maintype/subtype'. If there was no Content-Type header in the - message, the default type as given by get_default_type() will be - returned. Since according to RFC 2045, messages always have a default - type this will always return a value. - - RFC 2045 defines a message's default type to be text/plain unless it - appears inside a multipart/digest container, in which case it would be - message/rfc822. - """ - missing = object() - value = self.get('content-type', missing) - if value is missing: - # This should have no parameters - return self.get_default_type() - ctype = _splitparam(value)[0].lower() - # RFC 2045, section 5.2 says if its invalid, use text/plain - if ctype.count('/') != 1: - return 'text/plain' - return ctype - - def get_content_maintype(self): - """Return the message's main content type. - - This is the `maintype' part of the string returned by - get_content_type(). - """ - ctype = self.get_content_type() - return ctype.split('/')[0] - - def get_content_subtype(self): - """Returns the message's sub-content type. - - This is the `subtype' part of the string returned by - get_content_type(). - """ - ctype = self.get_content_type() - return ctype.split('/')[1] - - def get_default_type(self): - """Return the `default' content type. - - Most messages have a default content type of text/plain, except for - messages that are subparts of multipart/digest containers. Such - subparts have a default content type of message/rfc822. - """ - return self._default_type - - def set_default_type(self, ctype): - """Set the `default' content type. - - ctype should be either "text/plain" or "message/rfc822", although this - is not enforced. The default content type is not stored in the - Content-Type header. - """ - self._default_type = ctype - - def _get_params_preserve(self, failobj, header): - # Like get_params() but preserves the quoting of values. BAW: - # should this be part of the public interface? - missing = object() - value = self.get(header, missing) - if value is missing: - return failobj - params = [] - for p in _parseparam(value): - try: - name, val = p.split('=', 1) - name = name.strip() - val = val.strip() - except ValueError: - # Must have been a bare attribute - name = p.strip() - val = '' - params.append((name, val)) - params = utils.decode_params(params) - return params - - def get_params(self, failobj=None, header='content-type', unquote=True): - """Return the message's Content-Type parameters, as a list. - - The elements of the returned list are 2-tuples of key/value pairs, as - split on the `=' sign. The left hand side of the `=' is the key, - while the right hand side is the value. If there is no `=' sign in - the parameter the value is the empty string. The value is as - described in the get_param() method. - - Optional failobj is the object to return if there is no Content-Type - header. Optional header is the header to search instead of - Content-Type. If unquote is True, the value is unquoted. - """ - missing = object() - params = self._get_params_preserve(missing, header) - if params is missing: - return failobj - if unquote: - return [(k, _unquotevalue(v)) for k, v in params] - else: - return params - - def get_param(self, param, failobj=None, header='content-type', - unquote=True): - """Return the parameter value if found in the Content-Type header. - - Optional failobj is the object to return if there is no Content-Type - header, or the Content-Type header has no such parameter. Optional - header is the header to search instead of Content-Type. - - Parameter keys are always compared case insensitively. The return - value can either be a string, or a 3-tuple if the parameter was RFC - 2231 encoded. When it's a 3-tuple, the elements of the value are of - the form (CHARSET, LANGUAGE, VALUE). Note that both CHARSET and - LANGUAGE can be None, in which case you should consider VALUE to be - encoded in the us-ascii charset. You can usually ignore LANGUAGE. - The parameter value (either the returned string, or the VALUE item in - the 3-tuple) is always unquoted, unless unquote is set to False. - - If your application doesn't care whether the parameter was RFC 2231 - encoded, it can turn the return value into a string as follows: - - param = msg.get_param('foo') - param = email.utils.collapse_rfc2231_value(rawparam) - - """ - if header not in self: - return failobj - for k, v in self._get_params_preserve(failobj, header): - if k.lower() == param.lower(): - if unquote: - return _unquotevalue(v) - else: - return v - return failobj - - def set_param(self, param, value, header='Content-Type', requote=True, - charset=None, language=''): - """Set a parameter in the Content-Type header. - - If the parameter already exists in the header, its value will be - replaced with the new value. - - If header is Content-Type and has not yet been defined for this - message, it will be set to "text/plain" and the new parameter and - value will be appended as per RFC 2045. - - An alternate header can specified in the header argument, and all - parameters will be quoted as necessary unless requote is False. - - If charset is specified, the parameter will be encoded according to RFC - 2231. Optional language specifies the RFC 2231 language, defaulting - to the empty string. Both charset and language should be strings. - """ - if not isinstance(value, tuple) and charset: - value = (charset, language, value) - - if header not in self and header.lower() == 'content-type': - ctype = 'text/plain' - else: - ctype = self.get(header) - if not self.get_param(param, header=header): - if not ctype: - ctype = _formatparam(param, value, requote) - else: - ctype = SEMISPACE.join( - [ctype, _formatparam(param, value, requote)]) - else: - ctype = '' - for old_param, old_value in self.get_params(header=header, - unquote=requote): - append_param = '' - if old_param.lower() == param.lower(): - append_param = _formatparam(param, value, requote) - else: - append_param = _formatparam(old_param, old_value, requote) - if not ctype: - ctype = append_param - else: - ctype = SEMISPACE.join([ctype, append_param]) - if ctype != self.get(header): - del self[header] - self[header] = ctype - - def del_param(self, param, header='content-type', requote=True): - """Remove the given parameter completely from the Content-Type header. - - The header will be re-written in place without the parameter or its - value. All values will be quoted as necessary unless requote is - False. Optional header specifies an alternative to the Content-Type - header. - """ - if header not in self: - return - new_ctype = '' - for p, v in self.get_params(header=header, unquote=requote): - if p.lower() != param.lower(): - if not new_ctype: - new_ctype = _formatparam(p, v, requote) - else: - new_ctype = SEMISPACE.join([new_ctype, - _formatparam(p, v, requote)]) - if new_ctype != self.get(header): - del self[header] - self[header] = new_ctype - - def set_type(self, type, header='Content-Type', requote=True): - """Set the main type and subtype for the Content-Type header. - - type must be a string in the form "maintype/subtype", otherwise a - ValueError is raised. - - This method replaces the Content-Type header, keeping all the - parameters in place. If requote is False, this leaves the existing - header's quoting as is. Otherwise, the parameters will be quoted (the - default). - - An alternative header can be specified in the header argument. When - the Content-Type header is set, we'll always also add a MIME-Version - header. - """ - # BAW: should we be strict? - if not type.count('/') == 1: - raise ValueError - # Set the Content-Type, you get a MIME-Version - if header.lower() == 'content-type': - del self['mime-version'] - self['MIME-Version'] = '1.0' - if header not in self: - self[header] = type - return - params = self.get_params(header=header, unquote=requote) - del self[header] - self[header] = type - # Skip the first param; it's the old type. - for p, v in params[1:]: - self.set_param(p, v, header, requote) - - def get_filename(self, failobj=None): - """Return the filename associated with the payload if present. - - The filename is extracted from the Content-Disposition header's - `filename' parameter, and it is unquoted. If that header is missing - the `filename' parameter, this method falls back to looking for the - `name' parameter. - """ - missing = object() - filename = self.get_param('filename', missing, 'content-disposition') - if filename is missing: - filename = self.get_param('name', missing, 'content-type') - if filename is missing: - return failobj - return utils.collapse_rfc2231_value(filename).strip() - - def get_boundary(self, failobj=None): - """Return the boundary associated with the payload if present. - - The boundary is extracted from the Content-Type header's `boundary' - parameter, and it is unquoted. - """ - missing = object() - boundary = self.get_param('boundary', missing) - if boundary is missing: - return failobj - # RFC 2046 says that boundaries may begin but not end in w/s - return utils.collapse_rfc2231_value(boundary).rstrip() - - def set_boundary(self, boundary): - """Set the boundary parameter in Content-Type to 'boundary'. - - This is subtly different than deleting the Content-Type header and - adding a new one with a new boundary parameter via add_header(). The - main difference is that using the set_boundary() method preserves the - order of the Content-Type header in the original message. - - HeaderParseError is raised if the message has no Content-Type header. - """ - missing = object() - params = self._get_params_preserve(missing, 'content-type') - if params is missing: - # There was no Content-Type header, and we don't know what type - # to set it to, so raise an exception. - raise errors.HeaderParseError('No Content-Type header found') - newparams = list() - foundp = False - for pk, pv in params: - if pk.lower() == 'boundary': - newparams.append(('boundary', '"%s"' % boundary)) - foundp = True - else: - newparams.append((pk, pv)) - if not foundp: - # The original Content-Type header had no boundary attribute. - # Tack one on the end. BAW: should we raise an exception - # instead??? - newparams.append(('boundary', '"%s"' % boundary)) - # Replace the existing Content-Type header with the new value - newheaders = list() - for h, v in self._headers: - if h.lower() == 'content-type': - parts = list() - for k, v in newparams: - if v == '': - parts.append(k) - else: - parts.append('%s=%s' % (k, v)) - val = SEMISPACE.join(parts) - newheaders.append(self.policy.header_store_parse(h, val)) - - else: - newheaders.append((h, v)) - self._headers = newheaders - - def get_content_charset(self, failobj=None): - """Return the charset parameter of the Content-Type header. - - The returned string is always coerced to lower case. If there is no - Content-Type header, or if that header has no charset parameter, - failobj is returned. - """ - missing = object() - charset = self.get_param('charset', missing) - if charset is missing: - return failobj - if isinstance(charset, tuple): - # RFC 2231 encoded, so decode it, and it better end up as ascii. - pcharset = charset[0] or 'us-ascii' - try: - # LookupError will be raised if the charset isn't known to - # Python. UnicodeError will be raised if the encoded text - # contains a character not in the charset. - as_bytes = charset[2].encode('raw-unicode-escape') - charset = str(as_bytes, pcharset) - except (LookupError, UnicodeError): - charset = charset[2] - # charset characters must be in us-ascii range - try: - charset.encode('us-ascii') - except UnicodeError: - return failobj - # RFC 2046, $4.1.2 says charsets are not case sensitive - return charset.lower() - - def get_charsets(self, failobj=None): - """Return a list containing the charset(s) used in this message. - - The returned list of items describes the Content-Type headers' - charset parameter for this message and all the subparts in its - payload. - - Each item will either be a string (the value of the charset parameter - in the Content-Type header of that part) or the value of the - 'failobj' parameter (defaults to None), if the part does not have a - main MIME type of "text", or the charset is not defined. - - The list will contain one string for each part of the message, plus - one for the container message (i.e. self), so that a non-multipart - message will still return a list of length 1. - """ - return [part.get_content_charset(failobj) for part in self.walk()] - - # I.e. def walk(self): ... - from future.backports.email.iterators import walk diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 77a3c617af9c788594e6eecdea8102b910016904..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 208 zcmYk0I|>3Z7=*KEA%X`HbpOIi5K&N21QE8Dklp;c!Oep_7WPP9!K2uE1UoB}N(bf} zW~yPM(U3&-CXaKsY?^=g1dzxDgB`PcV?~Q_>T0hQ6jiNt z1N*~vOJy0fj(|w9Q?LR7uLI z4WF7l_ZeECm%c(D#n+zt3ca*5lH(PE1xkbTP#n&D-^_5>@$oo-5uN_|!)g!&f8nM- zV%R)~>2g?jkO!RbP?0LkLxOSya+F7{9D^L^aVrnhpc>}GRvxNRmE;M?qdCbBz6p53 zM{i&!lDjA$^MjYcWW4_TLNVv46}1&kXfO4gvbt8XWL_GbeM0@J7WDe+`jT=}HWlc& z^lyL_Ovv`6(XupL6iRwwnR4(g;c_V*+y(g&n>wQ!FA7=J#(F#!8gE!sJwdZKMqf9| z%bHogeS}}m-}*1nQGZ0Rc@EQ2SVWK$h)&2!9`R7buLiS_M?8K*@*y7pzo8w0+WKin za%L=@3k^WuLGuB_^^qyP@Mq3j!KxAyaz7wp0q$SGbkAVf0Dva2CqCfe9r>Af@+#Z} zJlce_NCunuck&A%!6x`M+K}H+iZ;O={Br^jV-|-&Z~vkN9ry$u1HfE;&e-zl6@E@ljqgoe!?z>|KA zp7bs6lH7rtHM+!NqPGiKF6gaPiq^tHI;z$$D}BQhZSSB5IM}=JmT8BjL(QyHep?(Z zp9k3JlsWOmXM1BkQ0IC@8RrtAfVZ>(-3SW&6rCR8%31Uv{wfiy<;q6_NT_wbhXmKK7+J4pBYn>D@COQs}X z>XB@-vP(pM*gKiT+XFOsX|1t#2!*&!iUP9li(-4|gfD8gQik!#U>iH3X4^qqF`%_G z2huK4+Yu-OYWD}!FqiOvg})C`PWu1MfTll;h`bCEEmS8?CQnkdErXSAdy+R z6ndE{InC;oUl{#q`eo*zBb?Q&e9h*&F_|tC6_YBfWF@j5rL8&}$nAM#LqsBqR<)690QMIkX?574X(k1GDgq1B?C1Vt2?NcL(X7 zbJ}ulz2_gl`!L0Tq}u4}z9V*d1?WsE@PX~DhY*2Tgt<+gb*#Taa_&7?VseDP5a%%Z E50a7IIRF3v diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/audio.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/audio.cpython-39.pyc deleted file mode 100644 index 19d6fb1d8f424df6ae5358a99f4bf3ffb317e1c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2816 zcmaJ@U2hyU6rHc#Y_>_8R;Usnkaj%GVelay`wWc;(m;eFD#I#DqmcSB`f(cjeu93ICg}GjVcNsne%Vj^_`g+dRfBZk zR}ISTYM2gvf4jU^?W8;C55+aHgM0VKB;6I)#f@h{di|3??1|np*poY#ae71a9|Q+C zFUH3ux0V?>Gtx@!l%6o&2vv{YWNtB&?EcC9drZ`MQ{k;0{Q=j5gK)LiXqDGOW~Fk{ z@X}&#Ak?|Cs@CY=;Agfj8z(bW&1&OT+hpi2Sw6+c=k!KZj8e1`mR7 zUlAcdOZZJ}^w2DrtC`z^XbAHn@dFMNMKvXrqW0(IdN=WH>F@_ zl1X4C4Y_6HIS|E+o+vG8ZYtSVT0LR={wNn*DL0~8b2R+lrzg53I{HljoJG;7(wd0JUT=Cf| zo=A7lTP1=!zKQ`;Iw<&KNgk$s`lisU7`*&7Fnh4~rx{mLC_era4t{LZxg3AFn7Uez zALP@zF2^UI-MgFF-0+z+J^cQMe-~}LPua!&k$2D4%=J=1=vJtWYr7V_^(7f%@cgHzUX@z ze}>0?j%JD78o;(@dN*bcl5-h;7rN+CjEsnvQ4yzJb5)jPkTEOws7vLAD_?DxV_30S+<;p6-5X|VFC1y_5GD zO)1IG$x{*Y#wl|bC0~!yq3^L%dY<2BQAgLT!Sr+N=xqe`2 zXA-@RxvMRZ0j^tL8QSJ1%$k?bjPSHleR}|rru#PJ`r_8+H^^wc`N0-BqLF%VWSC{G zbfCYJW#5AJ-J7i}!*8H0Gh5{PZtFxwkiYwm-|~tZRH}^kg;e=1@`VmO2$LuZlfMu4 z%xf6ZW^Dn2RTR#^P|YwT;y7CdLBjktjUrY1a+=<5*N-*@Yvc=-_bI*e-!Dg0%|^P` y)%quuN8end%_uIu^!%6itHJv%7$49v?CWTfaEI=w9}Obki=)urfAt50y?+1zp)PL# diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/base.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/base.cpython-39.pyc deleted file mode 100644 index a9c6a06dfe50e3a086d2467e856817b27ca607b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1140 zcmZ8gO=}b}7*1wpcecxxDwN_e2YXnsGl~~2MOw9JDJ?>ahXD!MNw%AA=A%hwWta72 z&;AAlFa8C8%3MA97Yc%J_KT%~yyTrE&+|S{-eH@YZ3M%A^83@!N9d1B)<*!zAxz@} zM+xGXdkSY>;$?o~XF(ER_sv(0teG@n9bDlge1~|02e*)k=5_0yl$#g&`g;wuER_i!>&Z#a;T0dbwKOGgX9pVc?3bA1cM}xd!GvpMec>%$qYy91tq953qFvU}^@9m&3!4zEs;xy=c5>5S|_?z4Jr(nkc z7y}H=$-m2iG$aMNR#&M7D;Vd7F!H9zt;p@(<+v34gykH*IX!zHg?7^S$v`kJbkuH> z#kd@Zd)MYi2ryVS6glbZA|n=#(TvI50`v@-nyTlL6%x}|RR-)PBIJm3>E44WQZmgl z0Y=Xs&AQST6ikbI94oM)7=JD_)V~58y)A?*_UFM%Url z@@*-VxwMqlZ7__^tLQfBzzEe(HdgxT)>2#@(pmkcZlq4^FQm5s^6nh-8oXO06G$Ja;dINYJw`oB@VF!2cHl93OwrjN1b`FACl;&W%6G~}T z@JhLLhtivhsl`r6DKAn=XTUR6YvBP<%o@4l7+v4CaNwPH+j<+;4s~8KTz5^b-3jel tJlUCVL@O=POv~aWC}m2)C8uHjqc}Hb?w(zF zWiBLVenXJBaOKEP@s$&QfeXB9&rTeKV618P=c}q$uX^~^tK$eydi%F;+(8umPJ{g? z!{9z%H$x+$A`-Dk>bOaYB&L3fep;lVpP`=>8Tx}oTnw-}tcS%A{|EI!Gb%=5*QmbK zjEiv`eHn>MV(=2X<@iNfTo%J8(PVs{AJ*JCX64FCC$(34!BoQ+GXIeI^-8iw$B&Mf zFjd=N)lL6^W6>nuUTd|gjF4rmytKS_m>UUos+=+!{kQqdnY#6|RL#m*zr932$Ao-B zTcxJcmX2Mcl@p^MwY66(Zhb{I_Ma4k`*iVT*mY?n5810-gc09*C4$!d2s$g^`nUItV!Dj1l z)_JZ4w}L%h`=!zBTh%O=w_pp`RK*96F0ZyNAj@w#3!rK`Fn zd=*ZyL%0ep-FZs#T1pNXOna!Wfoso{gPFBy8F}bdvQqQTx6oHr7u<#6Q^kYpWjE2o zK~B7pQ0P8`o%VM8&0Af0XqKHVRkdUxgq5_AO%ot_xB&NbSIcMVYrK1PzcQ%7Er#1aUlS_?s57wQIK?*Y%FA9X5KMhPMZh1tji%iR?Ro`_tx)6pH6xp@6@z>Qv^B*Gq47 z{-jzOQ|HGIj~9b%=CzvTt5E76Pd~{W?(;hoQq8p?5N<6~vW;~A1ulLvf^div70R^p5mk4@Xmb+^O z{VQep9cryxIVekfcFNKY0lK}q3%I02-@6-3hJg2)>)02re(Japno*o3Nu2#Pxnge; z2#oCl0%}6L>BC~_k_V3^E-TE_x#H$-+q92+8sZMF6u16;aZ032wb9MRcPu10c(msw eWyz9<+tH`H$Umbmjk|^>19SK#!(^1;Km8k0s%UNi diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/message.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/message.cpython-39.pyc deleted file mode 100644 index 6f80ac16dc75bc847197a50c127482a3f83ea5bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1482 zcmZuxL5~|X6dun^l1bWbSAam`WbFYpXql0g3rMJHwJcDfT@dYQBxE^bCvlna1lzMH ztDM;W1umR+kNgFG%3L||7kc9JOg2qXoy^mFe)jj?`<`w7;K3xoh>rjM`Hw*m{Ns)N z5yNQRf|%Nj1*LB=|NE zi5R@XDmlK6@`)Hf52lmX$EPK?mKwP-(n{@=UeHQf%NH{Hl%793Kck{9nhGwP{)=pa zDQOQIt%_PmRw^eAFD>R0p)QqGwTAwX&um>bPBK-kYUA2_9_YM>g@3e-;Gw+dqSoh4 z>C}oFSNItFBZBb+pZyp@1~~yAp&)rAh9VO2E0V`zfLcaohy{i~v_ms5zBoB4{NB6a zb#NV=;Kpv*P%Uf0})=6-#hzexCu7o7QAnQn~{ia33!bW zZ@~XVtWzVola%j~=GH6uHgoA@Lbroec0rYWvw_yLm$Gn_YeB2VIywWIrb=7Kbs=dz zr(5EVX>BOqAqi>Jr4+R28u->Sx0L2LWqn#Nkj$Ae*omNqV^Zv8^TW!aW4xryHnT2o zA?HdFR9;Gr`MOywcW^!=PFM8?3@}a>OPoDkmTOu!PJsIk^QUi4=?OYMO?Ugv2tB^~ z^t!BwQp?p<9g|8ojos> zbzNr9zCU})Y+?9Hnk)rfmszQ1*=h~e^ylf9nZ;H7Y{iRTaC2=oZ(L(!HiPXipv`2( zRhd<)k_gmY?{u{`{uO#3pY5Kb$pN`flIS43`e5(ArgscnOo(Y%tL6~?Ms zG^N+?Gxlr4%dKa`7+wy>x*%PVd%_3p?(3nC?w`Aa#NoyCp7{v6N9`-X_Q)^wD~!Lr zEnv}o#_|vQ73r?Vw5ze}P5$WJ&eRi->RMNK&rR3m{^js}_+N@`@>O5mV;|cdK*Zn( Kzi=2rM*jg4`kcl9 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/multipart.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/__pycache__/multipart.cpython-39.pyc deleted file mode 100644 index 7ac03c465e7793327ef74f9cd150801ea0d99eab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1681 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/application.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/application.py deleted file mode 100644 index 5cbfb174..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/application.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Keith Dart -# Contact: email-sig@python.org - -"""Class representing application/* type MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -from future.backports.email import encoders -from future.backports.email.mime.nonmultipart import MIMENonMultipart - -__all__ = ["MIMEApplication"] - - -class MIMEApplication(MIMENonMultipart): - """Class for generating application/* MIME documents.""" - - def __init__(self, _data, _subtype='octet-stream', - _encoder=encoders.encode_base64, **_params): - """Create an application/* type MIME document. - - _data is a string containing the raw application data. - - _subtype is the MIME content type subtype, defaulting to - 'octet-stream'. - - _encoder is a function which will perform the actual encoding for - transport of the application data, defaulting to base64 encoding. - - Any additional keyword arguments are passed to the base class - constructor, which turns them into parameters on the Content-Type - header. - """ - if _subtype is None: - raise TypeError('Invalid application MIME subtype') - MIMENonMultipart.__init__(self, 'application', _subtype, **_params) - self.set_payload(_data) - _encoder(self) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/audio.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/audio.py deleted file mode 100644 index 4989c114..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/audio.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (C) 2001-2007 Python Software Foundation -# Author: Anthony Baxter -# Contact: email-sig@python.org - -"""Class representing audio/* type MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEAudio'] - -import sndhdr - -from io import BytesIO -from future.backports.email import encoders -from future.backports.email.mime.nonmultipart import MIMENonMultipart - - -_sndhdr_MIMEmap = {'au' : 'basic', - 'wav' :'x-wav', - 'aiff':'x-aiff', - 'aifc':'x-aiff', - } - -# There are others in sndhdr that don't have MIME types. :( -# Additional ones to be added to sndhdr? midi, mp3, realaudio, wma?? -def _whatsnd(data): - """Try to identify a sound file type. - - sndhdr.what() has a pretty cruddy interface, unfortunately. This is why - we re-do it here. It would be easier to reverse engineer the Unix 'file' - command and use the standard 'magic' file, as shipped with a modern Unix. - """ - hdr = data[:512] - fakefile = BytesIO(hdr) - for testfn in sndhdr.tests: - res = testfn(hdr, fakefile) - if res is not None: - return _sndhdr_MIMEmap.get(res[0]) - return None - - -class MIMEAudio(MIMENonMultipart): - """Class for generating audio/* MIME documents.""" - - def __init__(self, _audiodata, _subtype=None, - _encoder=encoders.encode_base64, **_params): - """Create an audio/* type MIME document. - - _audiodata is a string containing the raw audio data. If this data - can be decoded by the standard Python `sndhdr' module, then the - subtype will be automatically included in the Content-Type header. - Otherwise, you can specify the specific audio subtype via the - _subtype parameter. If _subtype is not given, and no subtype can be - guessed, a TypeError is raised. - - _encoder is a function which will perform the actual encoding for - transport of the image data. It takes one argument, which is this - Image instance. It should use get_payload() and set_payload() to - change the payload to the encoded form. It should also add any - Content-Transfer-Encoding or other headers to the message as - necessary. The default encoding is Base64. - - Any additional keyword arguments are passed to the base class - constructor, which turns them into parameters on the Content-Type - header. - """ - if _subtype is None: - _subtype = _whatsnd(_audiodata) - if _subtype is None: - raise TypeError('Could not find audio MIME subtype') - MIMENonMultipart.__init__(self, 'audio', _subtype, **_params) - self.set_payload(_audiodata) - _encoder(self) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/base.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/base.py deleted file mode 100644 index e77f3ca4..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/base.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Base class for MIME specializations.""" -from __future__ import absolute_import, division, unicode_literals -from future.backports.email import message - -__all__ = ['MIMEBase'] - - -class MIMEBase(message.Message): - """Base class for MIME specializations.""" - - def __init__(self, _maintype, _subtype, **_params): - """This constructor adds a Content-Type: and a MIME-Version: header. - - The Content-Type: header is taken from the _maintype and _subtype - arguments. Additional parameters for this header are taken from the - keyword arguments. - """ - message.Message.__init__(self) - ctype = '%s/%s' % (_maintype, _subtype) - self.add_header('Content-Type', ctype, **_params) - self['MIME-Version'] = '1.0' diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/image.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/image.py deleted file mode 100644 index a0360246..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/image.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Class representing image/* type MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEImage'] - -import imghdr - -from future.backports.email import encoders -from future.backports.email.mime.nonmultipart import MIMENonMultipart - - -class MIMEImage(MIMENonMultipart): - """Class for generating image/* type MIME documents.""" - - def __init__(self, _imagedata, _subtype=None, - _encoder=encoders.encode_base64, **_params): - """Create an image/* type MIME document. - - _imagedata is a string containing the raw image data. If this data - can be decoded by the standard Python `imghdr' module, then the - subtype will be automatically included in the Content-Type header. - Otherwise, you can specify the specific image subtype via the _subtype - parameter. - - _encoder is a function which will perform the actual encoding for - transport of the image data. It takes one argument, which is this - Image instance. It should use get_payload() and set_payload() to - change the payload to the encoded form. It should also add any - Content-Transfer-Encoding or other headers to the message as - necessary. The default encoding is Base64. - - Any additional keyword arguments are passed to the base class - constructor, which turns them into parameters on the Content-Type - header. - """ - if _subtype is None: - _subtype = imghdr.what(None, _imagedata) - if _subtype is None: - raise TypeError('Could not guess image MIME subtype') - MIMENonMultipart.__init__(self, 'image', _subtype, **_params) - self.set_payload(_imagedata) - _encoder(self) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/message.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/message.py deleted file mode 100644 index 7f920751..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/message.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Class representing message/* MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEMessage'] - -from future.backports.email import message -from future.backports.email.mime.nonmultipart import MIMENonMultipart - - -class MIMEMessage(MIMENonMultipart): - """Class representing message/* MIME documents.""" - - def __init__(self, _msg, _subtype='rfc822'): - """Create a message/* type MIME document. - - _msg is a message object and must be an instance of Message, or a - derived class of Message, otherwise a TypeError is raised. - - Optional _subtype defines the subtype of the contained message. The - default is "rfc822" (this is defined by the MIME standard, even though - the term "rfc822" is technically outdated by RFC 2822). - """ - MIMENonMultipart.__init__(self, 'message', _subtype) - if not isinstance(_msg, message.Message): - raise TypeError('Argument is not an instance of Message') - # It's convenient to use this base class method. We need to do it - # this way or we'll get an exception - message.Message.attach(self, _msg) - # And be sure our default type is set correctly - self.set_default_type('message/rfc822') diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/multipart.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/multipart.py deleted file mode 100644 index 6d7ed3dc..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/multipart.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (C) 2002-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Base class for MIME multipart/* type messages.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEMultipart'] - -from future.backports.email.mime.base import MIMEBase - - -class MIMEMultipart(MIMEBase): - """Base class for MIME multipart/* type messages.""" - - def __init__(self, _subtype='mixed', boundary=None, _subparts=None, - **_params): - """Creates a multipart/* type message. - - By default, creates a multipart/mixed message, with proper - Content-Type and MIME-Version headers. - - _subtype is the subtype of the multipart content type, defaulting to - `mixed'. - - boundary is the multipart boundary string. By default it is - calculated as needed. - - _subparts is a sequence of initial subparts for the payload. It - must be an iterable object, such as a list. You can always - attach new subparts to the message by using the attach() method. - - Additional parameters for the Content-Type header are taken from the - keyword arguments (or passed into the _params argument). - """ - MIMEBase.__init__(self, 'multipart', _subtype, **_params) - - # Initialise _payload to an empty list as the Message superclass's - # implementation of is_multipart assumes that _payload is a list for - # multipart messages. - self._payload = [] - - if _subparts: - for p in _subparts: - self.attach(p) - if boundary: - self.set_boundary(boundary) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/nonmultipart.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/nonmultipart.py deleted file mode 100644 index 08c37c36..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/nonmultipart.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) 2002-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Base class for MIME type messages that are not multipart.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMENonMultipart'] - -from future.backports.email import errors -from future.backports.email.mime.base import MIMEBase - - -class MIMENonMultipart(MIMEBase): - """Base class for MIME multipart/* type messages.""" - - def attach(self, payload): - # The public API prohibits attaching multiple subparts to MIMEBase - # derived subtypes since none of them are, by definition, of content - # type multipart/* - raise errors.MultipartConversionError( - 'Cannot attach additional subparts to non-multipart/*') diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/text.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/text.py deleted file mode 100644 index 6269f4a6..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/mime/text.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Class representing text/* type MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEText'] - -from future.backports.email.encoders import encode_7or8bit -from future.backports.email.mime.nonmultipart import MIMENonMultipart - - -class MIMEText(MIMENonMultipart): - """Class for generating text/* type MIME documents.""" - - def __init__(self, _text, _subtype='plain', _charset=None): - """Create a text/* type MIME document. - - _text is the string for this message object. - - _subtype is the MIME sub content type, defaulting to "plain". - - _charset is the character set parameter added to the Content-Type - header. This defaults to "us-ascii". Note that as a side-effect, the - Content-Transfer-Encoding header will also be set. - """ - - # If no _charset was specified, check to see if there are non-ascii - # characters present. If not, use 'us-ascii', otherwise use utf-8. - # XXX: This can be removed once #7304 is fixed. - if _charset is None: - try: - _text.encode('us-ascii') - _charset = 'us-ascii' - except UnicodeEncodeError: - _charset = 'utf-8' - - MIMENonMultipart.__init__(self, 'text', _subtype, - **{'charset': _charset}) - - self.set_payload(_text, _charset) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/parser.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/parser.py deleted file mode 100644 index df1c6e28..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/parser.py +++ /dev/null @@ -1,135 +0,0 @@ -# Copyright (C) 2001-2007 Python Software Foundation -# Author: Barry Warsaw, Thomas Wouters, Anthony Baxter -# Contact: email-sig@python.org - -"""A parser of RFC 2822 and MIME email messages.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['Parser', 'HeaderParser', 'BytesParser', 'BytesHeaderParser'] - -import warnings -from io import StringIO, TextIOWrapper - -from future.backports.email.feedparser import FeedParser, BytesFeedParser -from future.backports.email.message import Message -from future.backports.email._policybase import compat32 - - -class Parser(object): - def __init__(self, _class=Message, **_3to2kwargs): - """Parser of RFC 2822 and MIME email messages. - - Creates an in-memory object tree representing the email message, which - can then be manipulated and turned over to a Generator to return the - textual representation of the message. - - The string must be formatted as a block of RFC 2822 headers and header - continuation lines, optionally preceeded by a `Unix-from' header. The - header block is terminated either by the end of the string or by a - blank line. - - _class is the class to instantiate for new message objects when they - must be created. This class must have a constructor that can take - zero arguments. Default is Message.Message. - - The policy keyword specifies a policy object that controls a number of - aspects of the parser's operation. The default policy maintains - backward compatibility. - - """ - if 'policy' in _3to2kwargs: policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - else: policy = compat32 - self._class = _class - self.policy = policy - - def parse(self, fp, headersonly=False): - """Create a message structure from the data in a file. - - Reads all the data from the file and returns the root of the message - structure. Optional headersonly is a flag specifying whether to stop - parsing after reading the headers or not. The default is False, - meaning it parses the entire contents of the file. - """ - feedparser = FeedParser(self._class, policy=self.policy) - if headersonly: - feedparser._set_headersonly() - while True: - data = fp.read(8192) - if not data: - break - feedparser.feed(data) - return feedparser.close() - - def parsestr(self, text, headersonly=False): - """Create a message structure from a string. - - Returns the root of the message structure. Optional headersonly is a - flag specifying whether to stop parsing after reading the headers or - not. The default is False, meaning it parses the entire contents of - the file. - """ - return self.parse(StringIO(text), headersonly=headersonly) - - - -class HeaderParser(Parser): - def parse(self, fp, headersonly=True): - return Parser.parse(self, fp, True) - - def parsestr(self, text, headersonly=True): - return Parser.parsestr(self, text, True) - - -class BytesParser(object): - - def __init__(self, *args, **kw): - """Parser of binary RFC 2822 and MIME email messages. - - Creates an in-memory object tree representing the email message, which - can then be manipulated and turned over to a Generator to return the - textual representation of the message. - - The input must be formatted as a block of RFC 2822 headers and header - continuation lines, optionally preceeded by a `Unix-from' header. The - header block is terminated either by the end of the input or by a - blank line. - - _class is the class to instantiate for new message objects when they - must be created. This class must have a constructor that can take - zero arguments. Default is Message.Message. - """ - self.parser = Parser(*args, **kw) - - def parse(self, fp, headersonly=False): - """Create a message structure from the data in a binary file. - - Reads all the data from the file and returns the root of the message - structure. Optional headersonly is a flag specifying whether to stop - parsing after reading the headers or not. The default is False, - meaning it parses the entire contents of the file. - """ - fp = TextIOWrapper(fp, encoding='ascii', errors='surrogateescape') - with fp: - return self.parser.parse(fp, headersonly) - - - def parsebytes(self, text, headersonly=False): - """Create a message structure from a byte string. - - Returns the root of the message structure. Optional headersonly is a - flag specifying whether to stop parsing after reading the headers or - not. The default is False, meaning it parses the entire contents of - the file. - """ - text = text.decode('ASCII', errors='surrogateescape') - return self.parser.parsestr(text, headersonly) - - -class BytesHeaderParser(BytesParser): - def parse(self, fp, headersonly=True): - return BytesParser.parse(self, fp, headersonly=True) - - def parsebytes(self, text, headersonly=True): - return BytesParser.parsebytes(self, text, headersonly=True) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/policy.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/policy.py deleted file mode 100644 index 2f609a23..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/policy.py +++ /dev/null @@ -1,193 +0,0 @@ -"""This will be the home for the policy that hooks in the new -code that adds all the email6 features. -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import super - -from future.standard_library.email._policybase import (Policy, Compat32, - compat32, _extend_docstrings) -from future.standard_library.email.utils import _has_surrogates -from future.standard_library.email.headerregistry import HeaderRegistry as HeaderRegistry - -__all__ = [ - 'Compat32', - 'compat32', - 'Policy', - 'EmailPolicy', - 'default', - 'strict', - 'SMTP', - 'HTTP', - ] - -@_extend_docstrings -class EmailPolicy(Policy): - - """+ - PROVISIONAL - - The API extensions enabled by this policy are currently provisional. - Refer to the documentation for details. - - This policy adds new header parsing and folding algorithms. Instead of - simple strings, headers are custom objects with custom attributes - depending on the type of the field. The folding algorithm fully - implements RFCs 2047 and 5322. - - In addition to the settable attributes listed above that apply to - all Policies, this policy adds the following additional attributes: - - refold_source -- if the value for a header in the Message object - came from the parsing of some source, this attribute - indicates whether or not a generator should refold - that value when transforming the message back into - stream form. The possible values are: - - none -- all source values use original folding - long -- source values that have any line that is - longer than max_line_length will be - refolded - all -- all values are refolded. - - The default is 'long'. - - header_factory -- a callable that takes two arguments, 'name' and - 'value', where 'name' is a header field name and - 'value' is an unfolded header field value, and - returns a string-like object that represents that - header. A default header_factory is provided that - understands some of the RFC5322 header field types. - (Currently address fields and date fields have - special treatment, while all other fields are - treated as unstructured. This list will be - completed before the extension is marked stable.) - """ - - refold_source = 'long' - header_factory = HeaderRegistry() - - def __init__(self, **kw): - # Ensure that each new instance gets a unique header factory - # (as opposed to clones, which share the factory). - if 'header_factory' not in kw: - object.__setattr__(self, 'header_factory', HeaderRegistry()) - super().__init__(**kw) - - def header_max_count(self, name): - """+ - The implementation for this class returns the max_count attribute from - the specialized header class that would be used to construct a header - of type 'name'. - """ - return self.header_factory[name].max_count - - # The logic of the next three methods is chosen such that it is possible to - # switch a Message object between a Compat32 policy and a policy derived - # from this class and have the results stay consistent. This allows a - # Message object constructed with this policy to be passed to a library - # that only handles Compat32 objects, or to receive such an object and - # convert it to use the newer style by just changing its policy. It is - # also chosen because it postpones the relatively expensive full rfc5322 - # parse until as late as possible when parsing from source, since in many - # applications only a few headers will actually be inspected. - - def header_source_parse(self, sourcelines): - """+ - The name is parsed as everything up to the ':' and returned unmodified. - The value is determined by stripping leading whitespace off the - remainder of the first line, joining all subsequent lines together, and - stripping any trailing carriage return or linefeed characters. (This - is the same as Compat32). - - """ - name, value = sourcelines[0].split(':', 1) - value = value.lstrip(' \t') + ''.join(sourcelines[1:]) - return (name, value.rstrip('\r\n')) - - def header_store_parse(self, name, value): - """+ - The name is returned unchanged. If the input value has a 'name' - attribute and it matches the name ignoring case, the value is returned - unchanged. Otherwise the name and value are passed to header_factory - method, and the resulting custom header object is returned as the - value. In this case a ValueError is raised if the input value contains - CR or LF characters. - - """ - if hasattr(value, 'name') and value.name.lower() == name.lower(): - return (name, value) - if isinstance(value, str) and len(value.splitlines())>1: - raise ValueError("Header values may not contain linefeed " - "or carriage return characters") - return (name, self.header_factory(name, value)) - - def header_fetch_parse(self, name, value): - """+ - If the value has a 'name' attribute, it is returned to unmodified. - Otherwise the name and the value with any linesep characters removed - are passed to the header_factory method, and the resulting custom - header object is returned. Any surrogateescaped bytes get turned - into the unicode unknown-character glyph. - - """ - if hasattr(value, 'name'): - return value - return self.header_factory(name, ''.join(value.splitlines())) - - def fold(self, name, value): - """+ - Header folding is controlled by the refold_source policy setting. A - value is considered to be a 'source value' if and only if it does not - have a 'name' attribute (having a 'name' attribute means it is a header - object of some sort). If a source value needs to be refolded according - to the policy, it is converted into a custom header object by passing - the name and the value with any linesep characters removed to the - header_factory method. Folding of a custom header object is done by - calling its fold method with the current policy. - - Source values are split into lines using splitlines. If the value is - not to be refolded, the lines are rejoined using the linesep from the - policy and returned. The exception is lines containing non-ascii - binary data. In that case the value is refolded regardless of the - refold_source setting, which causes the binary data to be CTE encoded - using the unknown-8bit charset. - - """ - return self._fold(name, value, refold_binary=True) - - def fold_binary(self, name, value): - """+ - The same as fold if cte_type is 7bit, except that the returned value is - bytes. - - If cte_type is 8bit, non-ASCII binary data is converted back into - bytes. Headers with binary data are not refolded, regardless of the - refold_header setting, since there is no way to know whether the binary - data consists of single byte characters or multibyte characters. - - """ - folded = self._fold(name, value, refold_binary=self.cte_type=='7bit') - return folded.encode('ascii', 'surrogateescape') - - def _fold(self, name, value, refold_binary=False): - if hasattr(value, 'name'): - return value.fold(policy=self) - maxlen = self.max_line_length if self.max_line_length else float('inf') - lines = value.splitlines() - refold = (self.refold_source == 'all' or - self.refold_source == 'long' and - (lines and len(lines[0])+len(name)+2 > maxlen or - any(len(x) > maxlen for x in lines[1:]))) - if refold or refold_binary and _has_surrogates(value): - return self.header_factory(name, ''.join(lines)).fold(policy=self) - return name + ': ' + self.linesep.join(lines) + self.linesep - - -default = EmailPolicy() -# Make the default policy use the class default header_factory -del default.header_factory -strict = default.clone(raise_on_defect=True) -SMTP = default.clone(linesep='\r\n') -HTTP = default.clone(linesep='\r\n', max_line_length=None) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/quoprimime.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/quoprimime.py deleted file mode 100644 index b69d158b..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/quoprimime.py +++ /dev/null @@ -1,326 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Ben Gertzfield -# Contact: email-sig@python.org - -"""Quoted-printable content transfer encoding per RFCs 2045-2047. - -This module handles the content transfer encoding method defined in RFC 2045 -to encode US ASCII-like 8-bit data called `quoted-printable'. It is used to -safely encode text that is in a character set similar to the 7-bit US ASCII -character set, but that includes some 8-bit characters that are normally not -allowed in email bodies or headers. - -Quoted-printable is very space-inefficient for encoding binary files; use the -email.base64mime module for that instead. - -This module provides an interface to encode and decode both headers and bodies -with quoted-printable encoding. - -RFC 2045 defines a method for including character set information in an -`encoded-word' in a header. This method is commonly used for 8-bit real names -in To:/From:/Cc: etc. fields, as well as Subject: lines. - -This module does not do the line wrapping or end-of-line character -conversion necessary for proper internationalized headers; it only -does dumb encoding and decoding. To deal with the various line -wrapping issues, use the email.header module. -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import bytes, chr, dict, int, range, super - -__all__ = [ - 'body_decode', - 'body_encode', - 'body_length', - 'decode', - 'decodestring', - 'header_decode', - 'header_encode', - 'header_length', - 'quote', - 'unquote', - ] - -import re -import io - -from string import ascii_letters, digits, hexdigits - -CRLF = '\r\n' -NL = '\n' -EMPTYSTRING = '' - -# Build a mapping of octets to the expansion of that octet. Since we're only -# going to have 256 of these things, this isn't terribly inefficient -# space-wise. Remember that headers and bodies have different sets of safe -# characters. Initialize both maps with the full expansion, and then override -# the safe bytes with the more compact form. -_QUOPRI_HEADER_MAP = dict((c, '=%02X' % c) for c in range(256)) -_QUOPRI_BODY_MAP = _QUOPRI_HEADER_MAP.copy() - -# Safe header bytes which need no encoding. -for c in bytes(b'-!*+/' + ascii_letters.encode('ascii') + digits.encode('ascii')): - _QUOPRI_HEADER_MAP[c] = chr(c) -# Headers have one other special encoding; spaces become underscores. -_QUOPRI_HEADER_MAP[ord(' ')] = '_' - -# Safe body bytes which need no encoding. -for c in bytes(b' !"#$%&\'()*+,-./0123456789:;<>' - b'?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`' - b'abcdefghijklmnopqrstuvwxyz{|}~\t'): - _QUOPRI_BODY_MAP[c] = chr(c) - - - -# Helpers -def header_check(octet): - """Return True if the octet should be escaped with header quopri.""" - return chr(octet) != _QUOPRI_HEADER_MAP[octet] - - -def body_check(octet): - """Return True if the octet should be escaped with body quopri.""" - return chr(octet) != _QUOPRI_BODY_MAP[octet] - - -def header_length(bytearray): - """Return a header quoted-printable encoding length. - - Note that this does not include any RFC 2047 chrome added by - `header_encode()`. - - :param bytearray: An array of bytes (a.k.a. octets). - :return: The length in bytes of the byte array when it is encoded with - quoted-printable for headers. - """ - return sum(len(_QUOPRI_HEADER_MAP[octet]) for octet in bytearray) - - -def body_length(bytearray): - """Return a body quoted-printable encoding length. - - :param bytearray: An array of bytes (a.k.a. octets). - :return: The length in bytes of the byte array when it is encoded with - quoted-printable for bodies. - """ - return sum(len(_QUOPRI_BODY_MAP[octet]) for octet in bytearray) - - -def _max_append(L, s, maxlen, extra=''): - if not isinstance(s, str): - s = chr(s) - if not L: - L.append(s.lstrip()) - elif len(L[-1]) + len(s) <= maxlen: - L[-1] += extra + s - else: - L.append(s.lstrip()) - - -def unquote(s): - """Turn a string in the form =AB to the ASCII character with value 0xab""" - return chr(int(s[1:3], 16)) - - -def quote(c): - return '=%02X' % ord(c) - - - -def header_encode(header_bytes, charset='iso-8859-1'): - """Encode a single header line with quoted-printable (like) encoding. - - Defined in RFC 2045, this `Q' encoding is similar to quoted-printable, but - used specifically for email header fields to allow charsets with mostly 7 - bit characters (and some 8 bit) to remain more or less readable in non-RFC - 2045 aware mail clients. - - charset names the character set to use in the RFC 2046 header. It - defaults to iso-8859-1. - """ - # Return empty headers as an empty string. - if not header_bytes: - return '' - # Iterate over every byte, encoding if necessary. - encoded = [] - for octet in header_bytes: - encoded.append(_QUOPRI_HEADER_MAP[octet]) - # Now add the RFC chrome to each encoded chunk and glue the chunks - # together. - return '=?%s?q?%s?=' % (charset, EMPTYSTRING.join(encoded)) - - -class _body_accumulator(io.StringIO): - - def __init__(self, maxlinelen, eol, *args, **kw): - super().__init__(*args, **kw) - self.eol = eol - self.maxlinelen = self.room = maxlinelen - - def write_str(self, s): - """Add string s to the accumulated body.""" - self.write(s) - self.room -= len(s) - - def newline(self): - """Write eol, then start new line.""" - self.write_str(self.eol) - self.room = self.maxlinelen - - def write_soft_break(self): - """Write a soft break, then start a new line.""" - self.write_str('=') - self.newline() - - def write_wrapped(self, s, extra_room=0): - """Add a soft line break if needed, then write s.""" - if self.room < len(s) + extra_room: - self.write_soft_break() - self.write_str(s) - - def write_char(self, c, is_last_char): - if not is_last_char: - # Another character follows on this line, so we must leave - # extra room, either for it or a soft break, and whitespace - # need not be quoted. - self.write_wrapped(c, extra_room=1) - elif c not in ' \t': - # For this and remaining cases, no more characters follow, - # so there is no need to reserve extra room (since a hard - # break will immediately follow). - self.write_wrapped(c) - elif self.room >= 3: - # It's a whitespace character at end-of-line, and we have room - # for the three-character quoted encoding. - self.write(quote(c)) - elif self.room == 2: - # There's room for the whitespace character and a soft break. - self.write(c) - self.write_soft_break() - else: - # There's room only for a soft break. The quoted whitespace - # will be the only content on the subsequent line. - self.write_soft_break() - self.write(quote(c)) - - -def body_encode(body, maxlinelen=76, eol=NL): - """Encode with quoted-printable, wrapping at maxlinelen characters. - - Each line of encoded text will end with eol, which defaults to "\\n". Set - this to "\\r\\n" if you will be using the result of this function directly - in an email. - - Each line will be wrapped at, at most, maxlinelen characters before the - eol string (maxlinelen defaults to 76 characters, the maximum value - permitted by RFC 2045). Long lines will have the 'soft line break' - quoted-printable character "=" appended to them, so the decoded text will - be identical to the original text. - - The minimum maxlinelen is 4 to have room for a quoted character ("=XX") - followed by a soft line break. Smaller values will generate a - ValueError. - - """ - - if maxlinelen < 4: - raise ValueError("maxlinelen must be at least 4") - if not body: - return body - - # The last line may or may not end in eol, but all other lines do. - last_has_eol = (body[-1] in '\r\n') - - # This accumulator will make it easier to build the encoded body. - encoded_body = _body_accumulator(maxlinelen, eol) - - lines = body.splitlines() - last_line_no = len(lines) - 1 - for line_no, line in enumerate(lines): - last_char_index = len(line) - 1 - for i, c in enumerate(line): - if body_check(ord(c)): - c = quote(c) - encoded_body.write_char(c, i==last_char_index) - # Add an eol if input line had eol. All input lines have eol except - # possibly the last one. - if line_no < last_line_no or last_has_eol: - encoded_body.newline() - - return encoded_body.getvalue() - - - -# BAW: I'm not sure if the intent was for the signature of this function to be -# the same as base64MIME.decode() or not... -def decode(encoded, eol=NL): - """Decode a quoted-printable string. - - Lines are separated with eol, which defaults to \\n. - """ - if not encoded: - return encoded - # BAW: see comment in encode() above. Again, we're building up the - # decoded string with string concatenation, which could be done much more - # efficiently. - decoded = '' - - for line in encoded.splitlines(): - line = line.rstrip() - if not line: - decoded += eol - continue - - i = 0 - n = len(line) - while i < n: - c = line[i] - if c != '=': - decoded += c - i += 1 - # Otherwise, c == "=". Are we at the end of the line? If so, add - # a soft line break. - elif i+1 == n: - i += 1 - continue - # Decode if in form =AB - elif i+2 < n and line[i+1] in hexdigits and line[i+2] in hexdigits: - decoded += unquote(line[i:i+3]) - i += 3 - # Otherwise, not in form =AB, pass literally - else: - decoded += c - i += 1 - - if i == n: - decoded += eol - # Special case if original string did not end with eol - if encoded[-1] not in '\r\n' and decoded.endswith(eol): - decoded = decoded[:-1] - return decoded - - -# For convenience and backwards compatibility w/ standard base64 module -body_decode = decode -decodestring = decode - - - -def _unquote_match(match): - """Turn a match in the form =AB to the ASCII character with value 0xab""" - s = match.group(0) - return unquote(s) - - -# Header decoding is done a bit differently -def header_decode(s): - """Decode a string encoded with RFC 2045 MIME header `Q' encoding. - - This function does not parse a full MIME header value encoded with - quoted-printable (like =?iso-8895-1?q?Hello_World?=) -- please use - the high level email.header class for that functionality. - """ - s = s.replace('_', ' ') - return re.sub(r'=[a-fA-F0-9]{2}', _unquote_match, s, re.ASCII) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/utils.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/utils.py deleted file mode 100644 index 4abebf7c..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/email/utils.py +++ /dev/null @@ -1,400 +0,0 @@ -# Copyright (C) 2001-2010 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Miscellaneous utilities.""" - -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future import utils -from future.builtins import bytes, int, str - -__all__ = [ - 'collapse_rfc2231_value', - 'decode_params', - 'decode_rfc2231', - 'encode_rfc2231', - 'formataddr', - 'formatdate', - 'format_datetime', - 'getaddresses', - 'make_msgid', - 'mktime_tz', - 'parseaddr', - 'parsedate', - 'parsedate_tz', - 'parsedate_to_datetime', - 'unquote', - ] - -import os -import re -if utils.PY2: - re.ASCII = 0 -import time -import base64 -import random -import socket -from future.backports import datetime -from future.backports.urllib.parse import quote as url_quote, unquote as url_unquote -import warnings -from io import StringIO - -from future.backports.email._parseaddr import quote -from future.backports.email._parseaddr import AddressList as _AddressList -from future.backports.email._parseaddr import mktime_tz - -from future.backports.email._parseaddr import parsedate, parsedate_tz, _parsedate_tz - -from quopri import decodestring as _qdecode - -# Intrapackage imports -from future.backports.email.encoders import _bencode, _qencode -from future.backports.email.charset import Charset - -COMMASPACE = ', ' -EMPTYSTRING = '' -UEMPTYSTRING = '' -CRLF = '\r\n' -TICK = "'" - -specialsre = re.compile(r'[][\\()<>@,:;".]') -escapesre = re.compile(r'[\\"]') - -# How to figure out if we are processing strings that come from a byte -# source with undecodable characters. -_has_surrogates = re.compile( - '([^\ud800-\udbff]|\A)[\udc00-\udfff]([^\udc00-\udfff]|\Z)').search - -# How to deal with a string containing bytes before handing it to the -# application through the 'normal' interface. -def _sanitize(string): - # Turn any escaped bytes into unicode 'unknown' char. - original_bytes = string.encode('ascii', 'surrogateescape') - return original_bytes.decode('ascii', 'replace') - - -# Helpers - -def formataddr(pair, charset='utf-8'): - """The inverse of parseaddr(), this takes a 2-tuple of the form - (realname, email_address) and returns the string value suitable - for an RFC 2822 From, To or Cc header. - - If the first element of pair is false, then the second element is - returned unmodified. - - Optional charset if given is the character set that is used to encode - realname in case realname is not ASCII safe. Can be an instance of str or - a Charset-like object which has a header_encode method. Default is - 'utf-8'. - """ - name, address = pair - # The address MUST (per RFC) be ascii, so raise an UnicodeError if it isn't. - address.encode('ascii') - if name: - try: - name.encode('ascii') - except UnicodeEncodeError: - if isinstance(charset, str): - charset = Charset(charset) - encoded_name = charset.header_encode(name) - return "%s <%s>" % (encoded_name, address) - else: - quotes = '' - if specialsre.search(name): - quotes = '"' - name = escapesre.sub(r'\\\g<0>', name) - return '%s%s%s <%s>' % (quotes, name, quotes, address) - return address - - - -def getaddresses(fieldvalues): - """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" - all = COMMASPACE.join(fieldvalues) - a = _AddressList(all) - return a.addresslist - - - -ecre = re.compile(r''' - =\? # literal =? - (?P[^?]*?) # non-greedy up to the next ? is the charset - \? # literal ? - (?P[qb]) # either a "q" or a "b", case insensitive - \? # literal ? - (?P.*?) # non-greedy up to the next ?= is the atom - \?= # literal ?= - ''', re.VERBOSE | re.IGNORECASE) - - -def _format_timetuple_and_zone(timetuple, zone): - return '%s, %02d %s %04d %02d:%02d:%02d %s' % ( - ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][timetuple[6]], - timetuple[2], - ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][timetuple[1] - 1], - timetuple[0], timetuple[3], timetuple[4], timetuple[5], - zone) - -def formatdate(timeval=None, localtime=False, usegmt=False): - """Returns a date string as specified by RFC 2822, e.g.: - - Fri, 09 Nov 2001 01:08:47 -0000 - - Optional timeval if given is a floating point time value as accepted by - gmtime() and localtime(), otherwise the current time is used. - - Optional localtime is a flag that when True, interprets timeval, and - returns a date relative to the local timezone instead of UTC, properly - taking daylight savings time into account. - - Optional argument usegmt means that the timezone is written out as - an ascii string, not numeric one (so "GMT" instead of "+0000"). This - is needed for HTTP, and is only used when localtime==False. - """ - # Note: we cannot use strftime() because that honors the locale and RFC - # 2822 requires that day and month names be the English abbreviations. - if timeval is None: - timeval = time.time() - if localtime: - now = time.localtime(timeval) - # Calculate timezone offset, based on whether the local zone has - # daylight savings time, and whether DST is in effect. - if time.daylight and now[-1]: - offset = time.altzone - else: - offset = time.timezone - hours, minutes = divmod(abs(offset), 3600) - # Remember offset is in seconds west of UTC, but the timezone is in - # minutes east of UTC, so the signs differ. - if offset > 0: - sign = '-' - else: - sign = '+' - zone = '%s%02d%02d' % (sign, hours, minutes // 60) - else: - now = time.gmtime(timeval) - # Timezone offset is always -0000 - if usegmt: - zone = 'GMT' - else: - zone = '-0000' - return _format_timetuple_and_zone(now, zone) - -def format_datetime(dt, usegmt=False): - """Turn a datetime into a date string as specified in RFC 2822. - - If usegmt is True, dt must be an aware datetime with an offset of zero. In - this case 'GMT' will be rendered instead of the normal +0000 required by - RFC2822. This is to support HTTP headers involving date stamps. - """ - now = dt.timetuple() - if usegmt: - if dt.tzinfo is None or dt.tzinfo != datetime.timezone.utc: - raise ValueError("usegmt option requires a UTC datetime") - zone = 'GMT' - elif dt.tzinfo is None: - zone = '-0000' - else: - zone = dt.strftime("%z") - return _format_timetuple_and_zone(now, zone) - - -def make_msgid(idstring=None, domain=None): - """Returns a string suitable for RFC 2822 compliant Message-ID, e.g: - - <20020201195627.33539.96671@nightshade.la.mastaler.com> - - Optional idstring if given is a string used to strengthen the - uniqueness of the message id. Optional domain if given provides the - portion of the message id after the '@'. It defaults to the locally - defined hostname. - """ - timeval = time.time() - utcdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(timeval)) - pid = os.getpid() - randint = random.randrange(100000) - if idstring is None: - idstring = '' - else: - idstring = '.' + idstring - if domain is None: - domain = socket.getfqdn() - msgid = '<%s.%s.%s%s@%s>' % (utcdate, pid, randint, idstring, domain) - return msgid - - -def parsedate_to_datetime(data): - _3to2list = list(_parsedate_tz(data)) - dtuple, tz, = [_3to2list[:-1]] + _3to2list[-1:] - if tz is None: - return datetime.datetime(*dtuple[:6]) - return datetime.datetime(*dtuple[:6], - tzinfo=datetime.timezone(datetime.timedelta(seconds=tz))) - - -def parseaddr(addr): - addrs = _AddressList(addr).addresslist - if not addrs: - return '', '' - return addrs[0] - - -# rfc822.unquote() doesn't properly de-backslash-ify in Python pre-2.3. -def unquote(str): - """Remove quotes from a string.""" - if len(str) > 1: - if str.startswith('"') and str.endswith('"'): - return str[1:-1].replace('\\\\', '\\').replace('\\"', '"') - if str.startswith('<') and str.endswith('>'): - return str[1:-1] - return str - - - -# RFC2231-related functions - parameter encoding and decoding -def decode_rfc2231(s): - """Decode string according to RFC 2231""" - parts = s.split(TICK, 2) - if len(parts) <= 2: - return None, None, s - return parts - - -def encode_rfc2231(s, charset=None, language=None): - """Encode string according to RFC 2231. - - If neither charset nor language is given, then s is returned as-is. If - charset is given but not language, the string is encoded using the empty - string for language. - """ - s = url_quote(s, safe='', encoding=charset or 'ascii') - if charset is None and language is None: - return s - if language is None: - language = '' - return "%s'%s'%s" % (charset, language, s) - - -rfc2231_continuation = re.compile(r'^(?P\w+)\*((?P[0-9]+)\*?)?$', - re.ASCII) - -def decode_params(params): - """Decode parameters list according to RFC 2231. - - params is a sequence of 2-tuples containing (param name, string value). - """ - # Copy params so we don't mess with the original - params = params[:] - new_params = [] - # Map parameter's name to a list of continuations. The values are a - # 3-tuple of the continuation number, the string value, and a flag - # specifying whether a particular segment is %-encoded. - rfc2231_params = {} - name, value = params.pop(0) - new_params.append((name, value)) - while params: - name, value = params.pop(0) - if name.endswith('*'): - encoded = True - else: - encoded = False - value = unquote(value) - mo = rfc2231_continuation.match(name) - if mo: - name, num = mo.group('name', 'num') - if num is not None: - num = int(num) - rfc2231_params.setdefault(name, []).append((num, value, encoded)) - else: - new_params.append((name, '"%s"' % quote(value))) - if rfc2231_params: - for name, continuations in rfc2231_params.items(): - value = [] - extended = False - # Sort by number - continuations.sort() - # And now append all values in numerical order, converting - # %-encodings for the encoded segments. If any of the - # continuation names ends in a *, then the entire string, after - # decoding segments and concatenating, must have the charset and - # language specifiers at the beginning of the string. - for num, s, encoded in continuations: - if encoded: - # Decode as "latin-1", so the characters in s directly - # represent the percent-encoded octet values. - # collapse_rfc2231_value treats this as an octet sequence. - s = url_unquote(s, encoding="latin-1") - extended = True - value.append(s) - value = quote(EMPTYSTRING.join(value)) - if extended: - charset, language, value = decode_rfc2231(value) - new_params.append((name, (charset, language, '"%s"' % value))) - else: - new_params.append((name, '"%s"' % value)) - return new_params - -def collapse_rfc2231_value(value, errors='replace', - fallback_charset='us-ascii'): - if not isinstance(value, tuple) or len(value) != 3: - return unquote(value) - # While value comes to us as a unicode string, we need it to be a bytes - # object. We do not want bytes() normal utf-8 decoder, we want a straight - # interpretation of the string as character bytes. - charset, language, text = value - rawbytes = bytes(text, 'raw-unicode-escape') - try: - return str(rawbytes, charset, errors) - except LookupError: - # charset is not a known codec. - return unquote(text) - - -# -# datetime doesn't provide a localtime function yet, so provide one. Code -# adapted from the patch in issue 9527. This may not be perfect, but it is -# better than not having it. -# - -def localtime(dt=None, isdst=-1): - """Return local time as an aware datetime object. - - If called without arguments, return current time. Otherwise *dt* - argument should be a datetime instance, and it is converted to the - local time zone according to the system time zone database. If *dt* is - naive (that is, dt.tzinfo is None), it is assumed to be in local time. - In this case, a positive or zero value for *isdst* causes localtime to - presume initially that summer time (for example, Daylight Saving Time) - is or is not (respectively) in effect for the specified time. A - negative value for *isdst* causes the localtime() function to attempt - to divine whether summer time is in effect for the specified time. - - """ - if dt is None: - return datetime.datetime.now(datetime.timezone.utc).astimezone() - if dt.tzinfo is not None: - return dt.astimezone() - # We have a naive datetime. Convert to a (localtime) timetuple and pass to - # system mktime together with the isdst hint. System mktime will return - # seconds since epoch. - tm = dt.timetuple()[:-1] + (isdst,) - seconds = time.mktime(tm) - localtm = time.localtime(seconds) - try: - delta = datetime.timedelta(seconds=localtm.tm_gmtoff) - tz = datetime.timezone(delta, localtm.tm_zone) - except AttributeError: - # Compute UTC offset and compare with the value implied by tm_isdst. - # If the values match, use the zone name implied by tm_isdst. - delta = dt - datetime.datetime(*time.gmtime(seconds)[:6]) - dst = time.daylight and localtm.tm_isdst > 0 - gmtoff = -(time.altzone if dst else time.timezone) - if delta == datetime.timedelta(seconds=gmtoff): - tz = datetime.timezone(delta, time.tzname[dst]) - else: - tz = datetime.timezone(delta) - return dt.replace(tzinfo=tz) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/__init__.py deleted file mode 100644 index 58e133fd..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -General functions for HTML manipulation, backported from Py3. - -Note that this uses Python 2.7 code with the corresponding Python 3 -module names and locations. -""" - -from __future__ import unicode_literals - - -_escape_map = {ord('&'): '&', ord('<'): '<', ord('>'): '>'} -_escape_map_full = {ord('&'): '&', ord('<'): '<', ord('>'): '>', - ord('"'): '"', ord('\''): '''} - -# NB: this is a candidate for a bytes/string polymorphic interface - -def escape(s, quote=True): - """ - Replace special characters "&", "<" and ">" to HTML-safe sequences. - If the optional flag quote is true (the default), the quotation mark - characters, both double quote (") and single quote (') characters are also - translated. - """ - assert not isinstance(s, bytes), 'Pass a unicode string' - if quote: - return s.translate(_escape_map_full) - return s.translate(_escape_map) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 072736c48bcb77eafe7feb6424ced2cf3f40c99d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1060 zcmZuv%}(1u5cb+}0tQMzs?dwkq@op~5LrWo^9cWQFZDyKCA+ zoJt>~2YT!)^ih26sSnT#mv+{Hl0#SW%6EAJJ{nItJr$~@Lw@?k*mX__PpGGl~-_GJG`N z>UrLw&+KV$(juz^Rrc!|R4W$`VqhI_TEvCZVPY1|?5xpt}ZOUIJLBq8tFV zQ>=AAsM}G%{+)9znC+WLhJjsC!)Zx!1@mWg+UMl{5m7KAd@~HJv;?fctvn8(N)vcf z;|c5vsmiT;{BFr*n8mdSGe$WH_lcx0BtK{2 z(Ridq9v;UdA=2>R&C6$ziY0)`uy@Y#^DyOuu&6h0tM?>SuqGP?tSlKa6(*%FB@1s? zScM~|M3v=*I*=mBc~X}Es@B7-#- diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/__pycache__/entities.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/__pycache__/entities.cpython-39.pyc deleted file mode 100644 index 0e8919b2790ceeb7ab2e4505feea077ac32ed487..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50767 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 Y`|?EY%KSh7E%ZFEczRJu(e&c~0~fp{IRF3v diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/__pycache__/parser.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/__pycache__/parser.cpython-39.pyc deleted file mode 100644 index 991e6b4094382b5109e3103415048f0065708b7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13649 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/entities.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/entities.py deleted file mode 100644 index 5c73f692..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/entities.py +++ /dev/null @@ -1,2514 +0,0 @@ -"""HTML character entity references. - -Backported for python-future from Python 3.3 -""" - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future.builtins import * - - -# maps the HTML entity name to the Unicode codepoint -name2codepoint = { - 'AElig': 0x00c6, # latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1 - 'Aacute': 0x00c1, # latin capital letter A with acute, U+00C1 ISOlat1 - 'Acirc': 0x00c2, # latin capital letter A with circumflex, U+00C2 ISOlat1 - 'Agrave': 0x00c0, # latin capital letter A with grave = latin capital letter A grave, U+00C0 ISOlat1 - 'Alpha': 0x0391, # greek capital letter alpha, U+0391 - 'Aring': 0x00c5, # latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1 - 'Atilde': 0x00c3, # latin capital letter A with tilde, U+00C3 ISOlat1 - 'Auml': 0x00c4, # latin capital letter A with diaeresis, U+00C4 ISOlat1 - 'Beta': 0x0392, # greek capital letter beta, U+0392 - 'Ccedil': 0x00c7, # latin capital letter C with cedilla, U+00C7 ISOlat1 - 'Chi': 0x03a7, # greek capital letter chi, U+03A7 - 'Dagger': 0x2021, # double dagger, U+2021 ISOpub - 'Delta': 0x0394, # greek capital letter delta, U+0394 ISOgrk3 - 'ETH': 0x00d0, # latin capital letter ETH, U+00D0 ISOlat1 - 'Eacute': 0x00c9, # latin capital letter E with acute, U+00C9 ISOlat1 - 'Ecirc': 0x00ca, # latin capital letter E with circumflex, U+00CA ISOlat1 - 'Egrave': 0x00c8, # latin capital letter E with grave, U+00C8 ISOlat1 - 'Epsilon': 0x0395, # greek capital letter epsilon, U+0395 - 'Eta': 0x0397, # greek capital letter eta, U+0397 - 'Euml': 0x00cb, # latin capital letter E with diaeresis, U+00CB ISOlat1 - 'Gamma': 0x0393, # greek capital letter gamma, U+0393 ISOgrk3 - 'Iacute': 0x00cd, # latin capital letter I with acute, U+00CD ISOlat1 - 'Icirc': 0x00ce, # latin capital letter I with circumflex, U+00CE ISOlat1 - 'Igrave': 0x00cc, # latin capital letter I with grave, U+00CC ISOlat1 - 'Iota': 0x0399, # greek capital letter iota, U+0399 - 'Iuml': 0x00cf, # latin capital letter I with diaeresis, U+00CF ISOlat1 - 'Kappa': 0x039a, # greek capital letter kappa, U+039A - 'Lambda': 0x039b, # greek capital letter lambda, U+039B ISOgrk3 - 'Mu': 0x039c, # greek capital letter mu, U+039C - 'Ntilde': 0x00d1, # latin capital letter N with tilde, U+00D1 ISOlat1 - 'Nu': 0x039d, # greek capital letter nu, U+039D - 'OElig': 0x0152, # latin capital ligature OE, U+0152 ISOlat2 - 'Oacute': 0x00d3, # latin capital letter O with acute, U+00D3 ISOlat1 - 'Ocirc': 0x00d4, # latin capital letter O with circumflex, U+00D4 ISOlat1 - 'Ograve': 0x00d2, # latin capital letter O with grave, U+00D2 ISOlat1 - 'Omega': 0x03a9, # greek capital letter omega, U+03A9 ISOgrk3 - 'Omicron': 0x039f, # greek capital letter omicron, U+039F - 'Oslash': 0x00d8, # latin capital letter O with stroke = latin capital letter O slash, U+00D8 ISOlat1 - 'Otilde': 0x00d5, # latin capital letter O with tilde, U+00D5 ISOlat1 - 'Ouml': 0x00d6, # latin capital letter O with diaeresis, U+00D6 ISOlat1 - 'Phi': 0x03a6, # greek capital letter phi, U+03A6 ISOgrk3 - 'Pi': 0x03a0, # greek capital letter pi, U+03A0 ISOgrk3 - 'Prime': 0x2033, # double prime = seconds = inches, U+2033 ISOtech - 'Psi': 0x03a8, # greek capital letter psi, U+03A8 ISOgrk3 - 'Rho': 0x03a1, # greek capital letter rho, U+03A1 - 'Scaron': 0x0160, # latin capital letter S with caron, U+0160 ISOlat2 - 'Sigma': 0x03a3, # greek capital letter sigma, U+03A3 ISOgrk3 - 'THORN': 0x00de, # latin capital letter THORN, U+00DE ISOlat1 - 'Tau': 0x03a4, # greek capital letter tau, U+03A4 - 'Theta': 0x0398, # greek capital letter theta, U+0398 ISOgrk3 - 'Uacute': 0x00da, # latin capital letter U with acute, U+00DA ISOlat1 - 'Ucirc': 0x00db, # latin capital letter U with circumflex, U+00DB ISOlat1 - 'Ugrave': 0x00d9, # latin capital letter U with grave, U+00D9 ISOlat1 - 'Upsilon': 0x03a5, # greek capital letter upsilon, U+03A5 ISOgrk3 - 'Uuml': 0x00dc, # latin capital letter U with diaeresis, U+00DC ISOlat1 - 'Xi': 0x039e, # greek capital letter xi, U+039E ISOgrk3 - 'Yacute': 0x00dd, # latin capital letter Y with acute, U+00DD ISOlat1 - 'Yuml': 0x0178, # latin capital letter Y with diaeresis, U+0178 ISOlat2 - 'Zeta': 0x0396, # greek capital letter zeta, U+0396 - 'aacute': 0x00e1, # latin small letter a with acute, U+00E1 ISOlat1 - 'acirc': 0x00e2, # latin small letter a with circumflex, U+00E2 ISOlat1 - 'acute': 0x00b4, # acute accent = spacing acute, U+00B4 ISOdia - 'aelig': 0x00e6, # latin small letter ae = latin small ligature ae, U+00E6 ISOlat1 - 'agrave': 0x00e0, # latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1 - 'alefsym': 0x2135, # alef symbol = first transfinite cardinal, U+2135 NEW - 'alpha': 0x03b1, # greek small letter alpha, U+03B1 ISOgrk3 - 'amp': 0x0026, # ampersand, U+0026 ISOnum - 'and': 0x2227, # logical and = wedge, U+2227 ISOtech - 'ang': 0x2220, # angle, U+2220 ISOamso - 'aring': 0x00e5, # latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1 - 'asymp': 0x2248, # almost equal to = asymptotic to, U+2248 ISOamsr - 'atilde': 0x00e3, # latin small letter a with tilde, U+00E3 ISOlat1 - 'auml': 0x00e4, # latin small letter a with diaeresis, U+00E4 ISOlat1 - 'bdquo': 0x201e, # double low-9 quotation mark, U+201E NEW - 'beta': 0x03b2, # greek small letter beta, U+03B2 ISOgrk3 - 'brvbar': 0x00a6, # broken bar = broken vertical bar, U+00A6 ISOnum - 'bull': 0x2022, # bullet = black small circle, U+2022 ISOpub - 'cap': 0x2229, # intersection = cap, U+2229 ISOtech - 'ccedil': 0x00e7, # latin small letter c with cedilla, U+00E7 ISOlat1 - 'cedil': 0x00b8, # cedilla = spacing cedilla, U+00B8 ISOdia - 'cent': 0x00a2, # cent sign, U+00A2 ISOnum - 'chi': 0x03c7, # greek small letter chi, U+03C7 ISOgrk3 - 'circ': 0x02c6, # modifier letter circumflex accent, U+02C6 ISOpub - 'clubs': 0x2663, # black club suit = shamrock, U+2663 ISOpub - 'cong': 0x2245, # approximately equal to, U+2245 ISOtech - 'copy': 0x00a9, # copyright sign, U+00A9 ISOnum - 'crarr': 0x21b5, # downwards arrow with corner leftwards = carriage return, U+21B5 NEW - 'cup': 0x222a, # union = cup, U+222A ISOtech - 'curren': 0x00a4, # currency sign, U+00A4 ISOnum - 'dArr': 0x21d3, # downwards double arrow, U+21D3 ISOamsa - 'dagger': 0x2020, # dagger, U+2020 ISOpub - 'darr': 0x2193, # downwards arrow, U+2193 ISOnum - 'deg': 0x00b0, # degree sign, U+00B0 ISOnum - 'delta': 0x03b4, # greek small letter delta, U+03B4 ISOgrk3 - 'diams': 0x2666, # black diamond suit, U+2666 ISOpub - 'divide': 0x00f7, # division sign, U+00F7 ISOnum - 'eacute': 0x00e9, # latin small letter e with acute, U+00E9 ISOlat1 - 'ecirc': 0x00ea, # latin small letter e with circumflex, U+00EA ISOlat1 - 'egrave': 0x00e8, # latin small letter e with grave, U+00E8 ISOlat1 - 'empty': 0x2205, # empty set = null set = diameter, U+2205 ISOamso - 'emsp': 0x2003, # em space, U+2003 ISOpub - 'ensp': 0x2002, # en space, U+2002 ISOpub - 'epsilon': 0x03b5, # greek small letter epsilon, U+03B5 ISOgrk3 - 'equiv': 0x2261, # identical to, U+2261 ISOtech - 'eta': 0x03b7, # greek small letter eta, U+03B7 ISOgrk3 - 'eth': 0x00f0, # latin small letter eth, U+00F0 ISOlat1 - 'euml': 0x00eb, # latin small letter e with diaeresis, U+00EB ISOlat1 - 'euro': 0x20ac, # euro sign, U+20AC NEW - 'exist': 0x2203, # there exists, U+2203 ISOtech - 'fnof': 0x0192, # latin small f with hook = function = florin, U+0192 ISOtech - 'forall': 0x2200, # for all, U+2200 ISOtech - 'frac12': 0x00bd, # vulgar fraction one half = fraction one half, U+00BD ISOnum - 'frac14': 0x00bc, # vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum - 'frac34': 0x00be, # vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum - 'frasl': 0x2044, # fraction slash, U+2044 NEW - 'gamma': 0x03b3, # greek small letter gamma, U+03B3 ISOgrk3 - 'ge': 0x2265, # greater-than or equal to, U+2265 ISOtech - 'gt': 0x003e, # greater-than sign, U+003E ISOnum - 'hArr': 0x21d4, # left right double arrow, U+21D4 ISOamsa - 'harr': 0x2194, # left right arrow, U+2194 ISOamsa - 'hearts': 0x2665, # black heart suit = valentine, U+2665 ISOpub - 'hellip': 0x2026, # horizontal ellipsis = three dot leader, U+2026 ISOpub - 'iacute': 0x00ed, # latin small letter i with acute, U+00ED ISOlat1 - 'icirc': 0x00ee, # latin small letter i with circumflex, U+00EE ISOlat1 - 'iexcl': 0x00a1, # inverted exclamation mark, U+00A1 ISOnum - 'igrave': 0x00ec, # latin small letter i with grave, U+00EC ISOlat1 - 'image': 0x2111, # blackletter capital I = imaginary part, U+2111 ISOamso - 'infin': 0x221e, # infinity, U+221E ISOtech - 'int': 0x222b, # integral, U+222B ISOtech - 'iota': 0x03b9, # greek small letter iota, U+03B9 ISOgrk3 - 'iquest': 0x00bf, # inverted question mark = turned question mark, U+00BF ISOnum - 'isin': 0x2208, # element of, U+2208 ISOtech - 'iuml': 0x00ef, # latin small letter i with diaeresis, U+00EF ISOlat1 - 'kappa': 0x03ba, # greek small letter kappa, U+03BA ISOgrk3 - 'lArr': 0x21d0, # leftwards double arrow, U+21D0 ISOtech - 'lambda': 0x03bb, # greek small letter lambda, U+03BB ISOgrk3 - 'lang': 0x2329, # left-pointing angle bracket = bra, U+2329 ISOtech - 'laquo': 0x00ab, # left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum - 'larr': 0x2190, # leftwards arrow, U+2190 ISOnum - 'lceil': 0x2308, # left ceiling = apl upstile, U+2308 ISOamsc - 'ldquo': 0x201c, # left double quotation mark, U+201C ISOnum - 'le': 0x2264, # less-than or equal to, U+2264 ISOtech - 'lfloor': 0x230a, # left floor = apl downstile, U+230A ISOamsc - 'lowast': 0x2217, # asterisk operator, U+2217 ISOtech - 'loz': 0x25ca, # lozenge, U+25CA ISOpub - 'lrm': 0x200e, # left-to-right mark, U+200E NEW RFC 2070 - 'lsaquo': 0x2039, # single left-pointing angle quotation mark, U+2039 ISO proposed - 'lsquo': 0x2018, # left single quotation mark, U+2018 ISOnum - 'lt': 0x003c, # less-than sign, U+003C ISOnum - 'macr': 0x00af, # macron = spacing macron = overline = APL overbar, U+00AF ISOdia - 'mdash': 0x2014, # em dash, U+2014 ISOpub - 'micro': 0x00b5, # micro sign, U+00B5 ISOnum - 'middot': 0x00b7, # middle dot = Georgian comma = Greek middle dot, U+00B7 ISOnum - 'minus': 0x2212, # minus sign, U+2212 ISOtech - 'mu': 0x03bc, # greek small letter mu, U+03BC ISOgrk3 - 'nabla': 0x2207, # nabla = backward difference, U+2207 ISOtech - 'nbsp': 0x00a0, # no-break space = non-breaking space, U+00A0 ISOnum - 'ndash': 0x2013, # en dash, U+2013 ISOpub - 'ne': 0x2260, # not equal to, U+2260 ISOtech - 'ni': 0x220b, # contains as member, U+220B ISOtech - 'not': 0x00ac, # not sign, U+00AC ISOnum - 'notin': 0x2209, # not an element of, U+2209 ISOtech - 'nsub': 0x2284, # not a subset of, U+2284 ISOamsn - 'ntilde': 0x00f1, # latin small letter n with tilde, U+00F1 ISOlat1 - 'nu': 0x03bd, # greek small letter nu, U+03BD ISOgrk3 - 'oacute': 0x00f3, # latin small letter o with acute, U+00F3 ISOlat1 - 'ocirc': 0x00f4, # latin small letter o with circumflex, U+00F4 ISOlat1 - 'oelig': 0x0153, # latin small ligature oe, U+0153 ISOlat2 - 'ograve': 0x00f2, # latin small letter o with grave, U+00F2 ISOlat1 - 'oline': 0x203e, # overline = spacing overscore, U+203E NEW - 'omega': 0x03c9, # greek small letter omega, U+03C9 ISOgrk3 - 'omicron': 0x03bf, # greek small letter omicron, U+03BF NEW - 'oplus': 0x2295, # circled plus = direct sum, U+2295 ISOamsb - 'or': 0x2228, # logical or = vee, U+2228 ISOtech - 'ordf': 0x00aa, # feminine ordinal indicator, U+00AA ISOnum - 'ordm': 0x00ba, # masculine ordinal indicator, U+00BA ISOnum - 'oslash': 0x00f8, # latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1 - 'otilde': 0x00f5, # latin small letter o with tilde, U+00F5 ISOlat1 - 'otimes': 0x2297, # circled times = vector product, U+2297 ISOamsb - 'ouml': 0x00f6, # latin small letter o with diaeresis, U+00F6 ISOlat1 - 'para': 0x00b6, # pilcrow sign = paragraph sign, U+00B6 ISOnum - 'part': 0x2202, # partial differential, U+2202 ISOtech - 'permil': 0x2030, # per mille sign, U+2030 ISOtech - 'perp': 0x22a5, # up tack = orthogonal to = perpendicular, U+22A5 ISOtech - 'phi': 0x03c6, # greek small letter phi, U+03C6 ISOgrk3 - 'pi': 0x03c0, # greek small letter pi, U+03C0 ISOgrk3 - 'piv': 0x03d6, # greek pi symbol, U+03D6 ISOgrk3 - 'plusmn': 0x00b1, # plus-minus sign = plus-or-minus sign, U+00B1 ISOnum - 'pound': 0x00a3, # pound sign, U+00A3 ISOnum - 'prime': 0x2032, # prime = minutes = feet, U+2032 ISOtech - 'prod': 0x220f, # n-ary product = product sign, U+220F ISOamsb - 'prop': 0x221d, # proportional to, U+221D ISOtech - 'psi': 0x03c8, # greek small letter psi, U+03C8 ISOgrk3 - 'quot': 0x0022, # quotation mark = APL quote, U+0022 ISOnum - 'rArr': 0x21d2, # rightwards double arrow, U+21D2 ISOtech - 'radic': 0x221a, # square root = radical sign, U+221A ISOtech - 'rang': 0x232a, # right-pointing angle bracket = ket, U+232A ISOtech - 'raquo': 0x00bb, # right-pointing double angle quotation mark = right pointing guillemet, U+00BB ISOnum - 'rarr': 0x2192, # rightwards arrow, U+2192 ISOnum - 'rceil': 0x2309, # right ceiling, U+2309 ISOamsc - 'rdquo': 0x201d, # right double quotation mark, U+201D ISOnum - 'real': 0x211c, # blackletter capital R = real part symbol, U+211C ISOamso - 'reg': 0x00ae, # registered sign = registered trade mark sign, U+00AE ISOnum - 'rfloor': 0x230b, # right floor, U+230B ISOamsc - 'rho': 0x03c1, # greek small letter rho, U+03C1 ISOgrk3 - 'rlm': 0x200f, # right-to-left mark, U+200F NEW RFC 2070 - 'rsaquo': 0x203a, # single right-pointing angle quotation mark, U+203A ISO proposed - 'rsquo': 0x2019, # right single quotation mark, U+2019 ISOnum - 'sbquo': 0x201a, # single low-9 quotation mark, U+201A NEW - 'scaron': 0x0161, # latin small letter s with caron, U+0161 ISOlat2 - 'sdot': 0x22c5, # dot operator, U+22C5 ISOamsb - 'sect': 0x00a7, # section sign, U+00A7 ISOnum - 'shy': 0x00ad, # soft hyphen = discretionary hyphen, U+00AD ISOnum - 'sigma': 0x03c3, # greek small letter sigma, U+03C3 ISOgrk3 - 'sigmaf': 0x03c2, # greek small letter final sigma, U+03C2 ISOgrk3 - 'sim': 0x223c, # tilde operator = varies with = similar to, U+223C ISOtech - 'spades': 0x2660, # black spade suit, U+2660 ISOpub - 'sub': 0x2282, # subset of, U+2282 ISOtech - 'sube': 0x2286, # subset of or equal to, U+2286 ISOtech - 'sum': 0x2211, # n-ary sumation, U+2211 ISOamsb - 'sup': 0x2283, # superset of, U+2283 ISOtech - 'sup1': 0x00b9, # superscript one = superscript digit one, U+00B9 ISOnum - 'sup2': 0x00b2, # superscript two = superscript digit two = squared, U+00B2 ISOnum - 'sup3': 0x00b3, # superscript three = superscript digit three = cubed, U+00B3 ISOnum - 'supe': 0x2287, # superset of or equal to, U+2287 ISOtech - 'szlig': 0x00df, # latin small letter sharp s = ess-zed, U+00DF ISOlat1 - 'tau': 0x03c4, # greek small letter tau, U+03C4 ISOgrk3 - 'there4': 0x2234, # therefore, U+2234 ISOtech - 'theta': 0x03b8, # greek small letter theta, U+03B8 ISOgrk3 - 'thetasym': 0x03d1, # greek small letter theta symbol, U+03D1 NEW - 'thinsp': 0x2009, # thin space, U+2009 ISOpub - 'thorn': 0x00fe, # latin small letter thorn with, U+00FE ISOlat1 - 'tilde': 0x02dc, # small tilde, U+02DC ISOdia - 'times': 0x00d7, # multiplication sign, U+00D7 ISOnum - 'trade': 0x2122, # trade mark sign, U+2122 ISOnum - 'uArr': 0x21d1, # upwards double arrow, U+21D1 ISOamsa - 'uacute': 0x00fa, # latin small letter u with acute, U+00FA ISOlat1 - 'uarr': 0x2191, # upwards arrow, U+2191 ISOnum - 'ucirc': 0x00fb, # latin small letter u with circumflex, U+00FB ISOlat1 - 'ugrave': 0x00f9, # latin small letter u with grave, U+00F9 ISOlat1 - 'uml': 0x00a8, # diaeresis = spacing diaeresis, U+00A8 ISOdia - 'upsih': 0x03d2, # greek upsilon with hook symbol, U+03D2 NEW - 'upsilon': 0x03c5, # greek small letter upsilon, U+03C5 ISOgrk3 - 'uuml': 0x00fc, # latin small letter u with diaeresis, U+00FC ISOlat1 - 'weierp': 0x2118, # script capital P = power set = Weierstrass p, U+2118 ISOamso - 'xi': 0x03be, # greek small letter xi, U+03BE ISOgrk3 - 'yacute': 0x00fd, # latin small letter y with acute, U+00FD ISOlat1 - 'yen': 0x00a5, # yen sign = yuan sign, U+00A5 ISOnum - 'yuml': 0x00ff, # latin small letter y with diaeresis, U+00FF ISOlat1 - 'zeta': 0x03b6, # greek small letter zeta, U+03B6 ISOgrk3 - 'zwj': 0x200d, # zero width joiner, U+200D NEW RFC 2070 - 'zwnj': 0x200c, # zero width non-joiner, U+200C NEW RFC 2070 -} - - -# maps the HTML5 named character references to the equivalent Unicode character(s) -html5 = { - 'Aacute': '\xc1', - 'aacute': '\xe1', - 'Aacute;': '\xc1', - 'aacute;': '\xe1', - 'Abreve;': '\u0102', - 'abreve;': '\u0103', - 'ac;': '\u223e', - 'acd;': '\u223f', - 'acE;': '\u223e\u0333', - 'Acirc': '\xc2', - 'acirc': '\xe2', - 'Acirc;': '\xc2', - 'acirc;': '\xe2', - 'acute': '\xb4', - 'acute;': '\xb4', - 'Acy;': '\u0410', - 'acy;': '\u0430', - 'AElig': '\xc6', - 'aelig': '\xe6', - 'AElig;': '\xc6', - 'aelig;': '\xe6', - 'af;': '\u2061', - 'Afr;': '\U0001d504', - 'afr;': '\U0001d51e', - 'Agrave': '\xc0', - 'agrave': '\xe0', - 'Agrave;': '\xc0', - 'agrave;': '\xe0', - 'alefsym;': '\u2135', - 'aleph;': '\u2135', - 'Alpha;': '\u0391', - 'alpha;': '\u03b1', - 'Amacr;': '\u0100', - 'amacr;': '\u0101', - 'amalg;': '\u2a3f', - 'AMP': '&', - 'amp': '&', - 'AMP;': '&', - 'amp;': '&', - 'And;': '\u2a53', - 'and;': '\u2227', - 'andand;': '\u2a55', - 'andd;': '\u2a5c', - 'andslope;': '\u2a58', - 'andv;': '\u2a5a', - 'ang;': '\u2220', - 'ange;': '\u29a4', - 'angle;': '\u2220', - 'angmsd;': '\u2221', - 'angmsdaa;': '\u29a8', - 'angmsdab;': '\u29a9', - 'angmsdac;': '\u29aa', - 'angmsdad;': '\u29ab', - 'angmsdae;': '\u29ac', - 'angmsdaf;': '\u29ad', - 'angmsdag;': '\u29ae', - 'angmsdah;': '\u29af', - 'angrt;': '\u221f', - 'angrtvb;': '\u22be', - 'angrtvbd;': '\u299d', - 'angsph;': '\u2222', - 'angst;': '\xc5', - 'angzarr;': '\u237c', - 'Aogon;': '\u0104', - 'aogon;': '\u0105', - 'Aopf;': '\U0001d538', - 'aopf;': '\U0001d552', - 'ap;': '\u2248', - 'apacir;': '\u2a6f', - 'apE;': '\u2a70', - 'ape;': '\u224a', - 'apid;': '\u224b', - 'apos;': "'", - 'ApplyFunction;': '\u2061', - 'approx;': '\u2248', - 'approxeq;': '\u224a', - 'Aring': '\xc5', - 'aring': '\xe5', - 'Aring;': '\xc5', - 'aring;': '\xe5', - 'Ascr;': '\U0001d49c', - 'ascr;': '\U0001d4b6', - 'Assign;': '\u2254', - 'ast;': '*', - 'asymp;': '\u2248', - 'asympeq;': '\u224d', - 'Atilde': '\xc3', - 'atilde': '\xe3', - 'Atilde;': '\xc3', - 'atilde;': '\xe3', - 'Auml': '\xc4', - 'auml': '\xe4', - 'Auml;': '\xc4', - 'auml;': '\xe4', - 'awconint;': '\u2233', - 'awint;': '\u2a11', - 'backcong;': '\u224c', - 'backepsilon;': '\u03f6', - 'backprime;': '\u2035', - 'backsim;': '\u223d', - 'backsimeq;': '\u22cd', - 'Backslash;': '\u2216', - 'Barv;': '\u2ae7', - 'barvee;': '\u22bd', - 'Barwed;': '\u2306', - 'barwed;': '\u2305', - 'barwedge;': '\u2305', - 'bbrk;': '\u23b5', - 'bbrktbrk;': '\u23b6', - 'bcong;': '\u224c', - 'Bcy;': '\u0411', - 'bcy;': '\u0431', - 'bdquo;': '\u201e', - 'becaus;': '\u2235', - 'Because;': '\u2235', - 'because;': '\u2235', - 'bemptyv;': '\u29b0', - 'bepsi;': '\u03f6', - 'bernou;': '\u212c', - 'Bernoullis;': '\u212c', - 'Beta;': '\u0392', - 'beta;': '\u03b2', - 'beth;': '\u2136', - 'between;': '\u226c', - 'Bfr;': '\U0001d505', - 'bfr;': '\U0001d51f', - 'bigcap;': '\u22c2', - 'bigcirc;': '\u25ef', - 'bigcup;': '\u22c3', - 'bigodot;': '\u2a00', - 'bigoplus;': '\u2a01', - 'bigotimes;': '\u2a02', - 'bigsqcup;': '\u2a06', - 'bigstar;': '\u2605', - 'bigtriangledown;': '\u25bd', - 'bigtriangleup;': '\u25b3', - 'biguplus;': '\u2a04', - 'bigvee;': '\u22c1', - 'bigwedge;': '\u22c0', - 'bkarow;': '\u290d', - 'blacklozenge;': '\u29eb', - 'blacksquare;': '\u25aa', - 'blacktriangle;': '\u25b4', - 'blacktriangledown;': '\u25be', - 'blacktriangleleft;': '\u25c2', - 'blacktriangleright;': '\u25b8', - 'blank;': '\u2423', - 'blk12;': '\u2592', - 'blk14;': '\u2591', - 'blk34;': '\u2593', - 'block;': '\u2588', - 'bne;': '=\u20e5', - 'bnequiv;': '\u2261\u20e5', - 'bNot;': '\u2aed', - 'bnot;': '\u2310', - 'Bopf;': '\U0001d539', - 'bopf;': '\U0001d553', - 'bot;': '\u22a5', - 'bottom;': '\u22a5', - 'bowtie;': '\u22c8', - 'boxbox;': '\u29c9', - 'boxDL;': '\u2557', - 'boxDl;': '\u2556', - 'boxdL;': '\u2555', - 'boxdl;': '\u2510', - 'boxDR;': '\u2554', - 'boxDr;': '\u2553', - 'boxdR;': '\u2552', - 'boxdr;': '\u250c', - 'boxH;': '\u2550', - 'boxh;': '\u2500', - 'boxHD;': '\u2566', - 'boxHd;': '\u2564', - 'boxhD;': '\u2565', - 'boxhd;': '\u252c', - 'boxHU;': '\u2569', - 'boxHu;': '\u2567', - 'boxhU;': '\u2568', - 'boxhu;': '\u2534', - 'boxminus;': '\u229f', - 'boxplus;': '\u229e', - 'boxtimes;': '\u22a0', - 'boxUL;': '\u255d', - 'boxUl;': '\u255c', - 'boxuL;': '\u255b', - 'boxul;': '\u2518', - 'boxUR;': '\u255a', - 'boxUr;': '\u2559', - 'boxuR;': '\u2558', - 'boxur;': '\u2514', - 'boxV;': '\u2551', - 'boxv;': '\u2502', - 'boxVH;': '\u256c', - 'boxVh;': '\u256b', - 'boxvH;': '\u256a', - 'boxvh;': '\u253c', - 'boxVL;': '\u2563', - 'boxVl;': '\u2562', - 'boxvL;': '\u2561', - 'boxvl;': '\u2524', - 'boxVR;': '\u2560', - 'boxVr;': '\u255f', - 'boxvR;': '\u255e', - 'boxvr;': '\u251c', - 'bprime;': '\u2035', - 'Breve;': '\u02d8', - 'breve;': '\u02d8', - 'brvbar': '\xa6', - 'brvbar;': '\xa6', - 'Bscr;': '\u212c', - 'bscr;': '\U0001d4b7', - 'bsemi;': '\u204f', - 'bsim;': '\u223d', - 'bsime;': '\u22cd', - 'bsol;': '\\', - 'bsolb;': '\u29c5', - 'bsolhsub;': '\u27c8', - 'bull;': '\u2022', - 'bullet;': '\u2022', - 'bump;': '\u224e', - 'bumpE;': '\u2aae', - 'bumpe;': '\u224f', - 'Bumpeq;': '\u224e', - 'bumpeq;': '\u224f', - 'Cacute;': '\u0106', - 'cacute;': '\u0107', - 'Cap;': '\u22d2', - 'cap;': '\u2229', - 'capand;': '\u2a44', - 'capbrcup;': '\u2a49', - 'capcap;': '\u2a4b', - 'capcup;': '\u2a47', - 'capdot;': '\u2a40', - 'CapitalDifferentialD;': '\u2145', - 'caps;': '\u2229\ufe00', - 'caret;': '\u2041', - 'caron;': '\u02c7', - 'Cayleys;': '\u212d', - 'ccaps;': '\u2a4d', - 'Ccaron;': '\u010c', - 'ccaron;': '\u010d', - 'Ccedil': '\xc7', - 'ccedil': '\xe7', - 'Ccedil;': '\xc7', - 'ccedil;': '\xe7', - 'Ccirc;': '\u0108', - 'ccirc;': '\u0109', - 'Cconint;': '\u2230', - 'ccups;': '\u2a4c', - 'ccupssm;': '\u2a50', - 'Cdot;': '\u010a', - 'cdot;': '\u010b', - 'cedil': '\xb8', - 'cedil;': '\xb8', - 'Cedilla;': '\xb8', - 'cemptyv;': '\u29b2', - 'cent': '\xa2', - 'cent;': '\xa2', - 'CenterDot;': '\xb7', - 'centerdot;': '\xb7', - 'Cfr;': '\u212d', - 'cfr;': '\U0001d520', - 'CHcy;': '\u0427', - 'chcy;': '\u0447', - 'check;': '\u2713', - 'checkmark;': '\u2713', - 'Chi;': '\u03a7', - 'chi;': '\u03c7', - 'cir;': '\u25cb', - 'circ;': '\u02c6', - 'circeq;': '\u2257', - 'circlearrowleft;': '\u21ba', - 'circlearrowright;': '\u21bb', - 'circledast;': '\u229b', - 'circledcirc;': '\u229a', - 'circleddash;': '\u229d', - 'CircleDot;': '\u2299', - 'circledR;': '\xae', - 'circledS;': '\u24c8', - 'CircleMinus;': '\u2296', - 'CirclePlus;': '\u2295', - 'CircleTimes;': '\u2297', - 'cirE;': '\u29c3', - 'cire;': '\u2257', - 'cirfnint;': '\u2a10', - 'cirmid;': '\u2aef', - 'cirscir;': '\u29c2', - 'ClockwiseContourIntegral;': '\u2232', - 'CloseCurlyDoubleQuote;': '\u201d', - 'CloseCurlyQuote;': '\u2019', - 'clubs;': '\u2663', - 'clubsuit;': '\u2663', - 'Colon;': '\u2237', - 'colon;': ':', - 'Colone;': '\u2a74', - 'colone;': '\u2254', - 'coloneq;': '\u2254', - 'comma;': ',', - 'commat;': '@', - 'comp;': '\u2201', - 'compfn;': '\u2218', - 'complement;': '\u2201', - 'complexes;': '\u2102', - 'cong;': '\u2245', - 'congdot;': '\u2a6d', - 'Congruent;': '\u2261', - 'Conint;': '\u222f', - 'conint;': '\u222e', - 'ContourIntegral;': '\u222e', - 'Copf;': '\u2102', - 'copf;': '\U0001d554', - 'coprod;': '\u2210', - 'Coproduct;': '\u2210', - 'COPY': '\xa9', - 'copy': '\xa9', - 'COPY;': '\xa9', - 'copy;': '\xa9', - 'copysr;': '\u2117', - 'CounterClockwiseContourIntegral;': '\u2233', - 'crarr;': '\u21b5', - 'Cross;': '\u2a2f', - 'cross;': '\u2717', - 'Cscr;': '\U0001d49e', - 'cscr;': '\U0001d4b8', - 'csub;': '\u2acf', - 'csube;': '\u2ad1', - 'csup;': '\u2ad0', - 'csupe;': '\u2ad2', - 'ctdot;': '\u22ef', - 'cudarrl;': '\u2938', - 'cudarrr;': '\u2935', - 'cuepr;': '\u22de', - 'cuesc;': '\u22df', - 'cularr;': '\u21b6', - 'cularrp;': '\u293d', - 'Cup;': '\u22d3', - 'cup;': '\u222a', - 'cupbrcap;': '\u2a48', - 'CupCap;': '\u224d', - 'cupcap;': '\u2a46', - 'cupcup;': '\u2a4a', - 'cupdot;': '\u228d', - 'cupor;': '\u2a45', - 'cups;': '\u222a\ufe00', - 'curarr;': '\u21b7', - 'curarrm;': '\u293c', - 'curlyeqprec;': '\u22de', - 'curlyeqsucc;': '\u22df', - 'curlyvee;': '\u22ce', - 'curlywedge;': '\u22cf', - 'curren': '\xa4', - 'curren;': '\xa4', - 'curvearrowleft;': '\u21b6', - 'curvearrowright;': '\u21b7', - 'cuvee;': '\u22ce', - 'cuwed;': '\u22cf', - 'cwconint;': '\u2232', - 'cwint;': '\u2231', - 'cylcty;': '\u232d', - 'Dagger;': '\u2021', - 'dagger;': '\u2020', - 'daleth;': '\u2138', - 'Darr;': '\u21a1', - 'dArr;': '\u21d3', - 'darr;': '\u2193', - 'dash;': '\u2010', - 'Dashv;': '\u2ae4', - 'dashv;': '\u22a3', - 'dbkarow;': '\u290f', - 'dblac;': '\u02dd', - 'Dcaron;': '\u010e', - 'dcaron;': '\u010f', - 'Dcy;': '\u0414', - 'dcy;': '\u0434', - 'DD;': '\u2145', - 'dd;': '\u2146', - 'ddagger;': '\u2021', - 'ddarr;': '\u21ca', - 'DDotrahd;': '\u2911', - 'ddotseq;': '\u2a77', - 'deg': '\xb0', - 'deg;': '\xb0', - 'Del;': '\u2207', - 'Delta;': '\u0394', - 'delta;': '\u03b4', - 'demptyv;': '\u29b1', - 'dfisht;': '\u297f', - 'Dfr;': '\U0001d507', - 'dfr;': '\U0001d521', - 'dHar;': '\u2965', - 'dharl;': '\u21c3', - 'dharr;': '\u21c2', - 'DiacriticalAcute;': '\xb4', - 'DiacriticalDot;': '\u02d9', - 'DiacriticalDoubleAcute;': '\u02dd', - 'DiacriticalGrave;': '`', - 'DiacriticalTilde;': '\u02dc', - 'diam;': '\u22c4', - 'Diamond;': '\u22c4', - 'diamond;': '\u22c4', - 'diamondsuit;': '\u2666', - 'diams;': '\u2666', - 'die;': '\xa8', - 'DifferentialD;': '\u2146', - 'digamma;': '\u03dd', - 'disin;': '\u22f2', - 'div;': '\xf7', - 'divide': '\xf7', - 'divide;': '\xf7', - 'divideontimes;': '\u22c7', - 'divonx;': '\u22c7', - 'DJcy;': '\u0402', - 'djcy;': '\u0452', - 'dlcorn;': '\u231e', - 'dlcrop;': '\u230d', - 'dollar;': '$', - 'Dopf;': '\U0001d53b', - 'dopf;': '\U0001d555', - 'Dot;': '\xa8', - 'dot;': '\u02d9', - 'DotDot;': '\u20dc', - 'doteq;': '\u2250', - 'doteqdot;': '\u2251', - 'DotEqual;': '\u2250', - 'dotminus;': '\u2238', - 'dotplus;': '\u2214', - 'dotsquare;': '\u22a1', - 'doublebarwedge;': '\u2306', - 'DoubleContourIntegral;': '\u222f', - 'DoubleDot;': '\xa8', - 'DoubleDownArrow;': '\u21d3', - 'DoubleLeftArrow;': '\u21d0', - 'DoubleLeftRightArrow;': '\u21d4', - 'DoubleLeftTee;': '\u2ae4', - 'DoubleLongLeftArrow;': '\u27f8', - 'DoubleLongLeftRightArrow;': '\u27fa', - 'DoubleLongRightArrow;': '\u27f9', - 'DoubleRightArrow;': '\u21d2', - 'DoubleRightTee;': '\u22a8', - 'DoubleUpArrow;': '\u21d1', - 'DoubleUpDownArrow;': '\u21d5', - 'DoubleVerticalBar;': '\u2225', - 'DownArrow;': '\u2193', - 'Downarrow;': '\u21d3', - 'downarrow;': '\u2193', - 'DownArrowBar;': '\u2913', - 'DownArrowUpArrow;': '\u21f5', - 'DownBreve;': '\u0311', - 'downdownarrows;': '\u21ca', - 'downharpoonleft;': '\u21c3', - 'downharpoonright;': '\u21c2', - 'DownLeftRightVector;': '\u2950', - 'DownLeftTeeVector;': '\u295e', - 'DownLeftVector;': '\u21bd', - 'DownLeftVectorBar;': '\u2956', - 'DownRightTeeVector;': '\u295f', - 'DownRightVector;': '\u21c1', - 'DownRightVectorBar;': '\u2957', - 'DownTee;': '\u22a4', - 'DownTeeArrow;': '\u21a7', - 'drbkarow;': '\u2910', - 'drcorn;': '\u231f', - 'drcrop;': '\u230c', - 'Dscr;': '\U0001d49f', - 'dscr;': '\U0001d4b9', - 'DScy;': '\u0405', - 'dscy;': '\u0455', - 'dsol;': '\u29f6', - 'Dstrok;': '\u0110', - 'dstrok;': '\u0111', - 'dtdot;': '\u22f1', - 'dtri;': '\u25bf', - 'dtrif;': '\u25be', - 'duarr;': '\u21f5', - 'duhar;': '\u296f', - 'dwangle;': '\u29a6', - 'DZcy;': '\u040f', - 'dzcy;': '\u045f', - 'dzigrarr;': '\u27ff', - 'Eacute': '\xc9', - 'eacute': '\xe9', - 'Eacute;': '\xc9', - 'eacute;': '\xe9', - 'easter;': '\u2a6e', - 'Ecaron;': '\u011a', - 'ecaron;': '\u011b', - 'ecir;': '\u2256', - 'Ecirc': '\xca', - 'ecirc': '\xea', - 'Ecirc;': '\xca', - 'ecirc;': '\xea', - 'ecolon;': '\u2255', - 'Ecy;': '\u042d', - 'ecy;': '\u044d', - 'eDDot;': '\u2a77', - 'Edot;': '\u0116', - 'eDot;': '\u2251', - 'edot;': '\u0117', - 'ee;': '\u2147', - 'efDot;': '\u2252', - 'Efr;': '\U0001d508', - 'efr;': '\U0001d522', - 'eg;': '\u2a9a', - 'Egrave': '\xc8', - 'egrave': '\xe8', - 'Egrave;': '\xc8', - 'egrave;': '\xe8', - 'egs;': '\u2a96', - 'egsdot;': '\u2a98', - 'el;': '\u2a99', - 'Element;': '\u2208', - 'elinters;': '\u23e7', - 'ell;': '\u2113', - 'els;': '\u2a95', - 'elsdot;': '\u2a97', - 'Emacr;': '\u0112', - 'emacr;': '\u0113', - 'empty;': '\u2205', - 'emptyset;': '\u2205', - 'EmptySmallSquare;': '\u25fb', - 'emptyv;': '\u2205', - 'EmptyVerySmallSquare;': '\u25ab', - 'emsp13;': '\u2004', - 'emsp14;': '\u2005', - 'emsp;': '\u2003', - 'ENG;': '\u014a', - 'eng;': '\u014b', - 'ensp;': '\u2002', - 'Eogon;': '\u0118', - 'eogon;': '\u0119', - 'Eopf;': '\U0001d53c', - 'eopf;': '\U0001d556', - 'epar;': '\u22d5', - 'eparsl;': '\u29e3', - 'eplus;': '\u2a71', - 'epsi;': '\u03b5', - 'Epsilon;': '\u0395', - 'epsilon;': '\u03b5', - 'epsiv;': '\u03f5', - 'eqcirc;': '\u2256', - 'eqcolon;': '\u2255', - 'eqsim;': '\u2242', - 'eqslantgtr;': '\u2a96', - 'eqslantless;': '\u2a95', - 'Equal;': '\u2a75', - 'equals;': '=', - 'EqualTilde;': '\u2242', - 'equest;': '\u225f', - 'Equilibrium;': '\u21cc', - 'equiv;': '\u2261', - 'equivDD;': '\u2a78', - 'eqvparsl;': '\u29e5', - 'erarr;': '\u2971', - 'erDot;': '\u2253', - 'Escr;': '\u2130', - 'escr;': '\u212f', - 'esdot;': '\u2250', - 'Esim;': '\u2a73', - 'esim;': '\u2242', - 'Eta;': '\u0397', - 'eta;': '\u03b7', - 'ETH': '\xd0', - 'eth': '\xf0', - 'ETH;': '\xd0', - 'eth;': '\xf0', - 'Euml': '\xcb', - 'euml': '\xeb', - 'Euml;': '\xcb', - 'euml;': '\xeb', - 'euro;': '\u20ac', - 'excl;': '!', - 'exist;': '\u2203', - 'Exists;': '\u2203', - 'expectation;': '\u2130', - 'ExponentialE;': '\u2147', - 'exponentiale;': '\u2147', - 'fallingdotseq;': '\u2252', - 'Fcy;': '\u0424', - 'fcy;': '\u0444', - 'female;': '\u2640', - 'ffilig;': '\ufb03', - 'fflig;': '\ufb00', - 'ffllig;': '\ufb04', - 'Ffr;': '\U0001d509', - 'ffr;': '\U0001d523', - 'filig;': '\ufb01', - 'FilledSmallSquare;': '\u25fc', - 'FilledVerySmallSquare;': '\u25aa', - 'fjlig;': 'fj', - 'flat;': '\u266d', - 'fllig;': '\ufb02', - 'fltns;': '\u25b1', - 'fnof;': '\u0192', - 'Fopf;': '\U0001d53d', - 'fopf;': '\U0001d557', - 'ForAll;': '\u2200', - 'forall;': '\u2200', - 'fork;': '\u22d4', - 'forkv;': '\u2ad9', - 'Fouriertrf;': '\u2131', - 'fpartint;': '\u2a0d', - 'frac12': '\xbd', - 'frac12;': '\xbd', - 'frac13;': '\u2153', - 'frac14': '\xbc', - 'frac14;': '\xbc', - 'frac15;': '\u2155', - 'frac16;': '\u2159', - 'frac18;': '\u215b', - 'frac23;': '\u2154', - 'frac25;': '\u2156', - 'frac34': '\xbe', - 'frac34;': '\xbe', - 'frac35;': '\u2157', - 'frac38;': '\u215c', - 'frac45;': '\u2158', - 'frac56;': '\u215a', - 'frac58;': '\u215d', - 'frac78;': '\u215e', - 'frasl;': '\u2044', - 'frown;': '\u2322', - 'Fscr;': '\u2131', - 'fscr;': '\U0001d4bb', - 'gacute;': '\u01f5', - 'Gamma;': '\u0393', - 'gamma;': '\u03b3', - 'Gammad;': '\u03dc', - 'gammad;': '\u03dd', - 'gap;': '\u2a86', - 'Gbreve;': '\u011e', - 'gbreve;': '\u011f', - 'Gcedil;': '\u0122', - 'Gcirc;': '\u011c', - 'gcirc;': '\u011d', - 'Gcy;': '\u0413', - 'gcy;': '\u0433', - 'Gdot;': '\u0120', - 'gdot;': '\u0121', - 'gE;': '\u2267', - 'ge;': '\u2265', - 'gEl;': '\u2a8c', - 'gel;': '\u22db', - 'geq;': '\u2265', - 'geqq;': '\u2267', - 'geqslant;': '\u2a7e', - 'ges;': '\u2a7e', - 'gescc;': '\u2aa9', - 'gesdot;': '\u2a80', - 'gesdoto;': '\u2a82', - 'gesdotol;': '\u2a84', - 'gesl;': '\u22db\ufe00', - 'gesles;': '\u2a94', - 'Gfr;': '\U0001d50a', - 'gfr;': '\U0001d524', - 'Gg;': '\u22d9', - 'gg;': '\u226b', - 'ggg;': '\u22d9', - 'gimel;': '\u2137', - 'GJcy;': '\u0403', - 'gjcy;': '\u0453', - 'gl;': '\u2277', - 'gla;': '\u2aa5', - 'glE;': '\u2a92', - 'glj;': '\u2aa4', - 'gnap;': '\u2a8a', - 'gnapprox;': '\u2a8a', - 'gnE;': '\u2269', - 'gne;': '\u2a88', - 'gneq;': '\u2a88', - 'gneqq;': '\u2269', - 'gnsim;': '\u22e7', - 'Gopf;': '\U0001d53e', - 'gopf;': '\U0001d558', - 'grave;': '`', - 'GreaterEqual;': '\u2265', - 'GreaterEqualLess;': '\u22db', - 'GreaterFullEqual;': '\u2267', - 'GreaterGreater;': '\u2aa2', - 'GreaterLess;': '\u2277', - 'GreaterSlantEqual;': '\u2a7e', - 'GreaterTilde;': '\u2273', - 'Gscr;': '\U0001d4a2', - 'gscr;': '\u210a', - 'gsim;': '\u2273', - 'gsime;': '\u2a8e', - 'gsiml;': '\u2a90', - 'GT': '>', - 'gt': '>', - 'GT;': '>', - 'Gt;': '\u226b', - 'gt;': '>', - 'gtcc;': '\u2aa7', - 'gtcir;': '\u2a7a', - 'gtdot;': '\u22d7', - 'gtlPar;': '\u2995', - 'gtquest;': '\u2a7c', - 'gtrapprox;': '\u2a86', - 'gtrarr;': '\u2978', - 'gtrdot;': '\u22d7', - 'gtreqless;': '\u22db', - 'gtreqqless;': '\u2a8c', - 'gtrless;': '\u2277', - 'gtrsim;': '\u2273', - 'gvertneqq;': '\u2269\ufe00', - 'gvnE;': '\u2269\ufe00', - 'Hacek;': '\u02c7', - 'hairsp;': '\u200a', - 'half;': '\xbd', - 'hamilt;': '\u210b', - 'HARDcy;': '\u042a', - 'hardcy;': '\u044a', - 'hArr;': '\u21d4', - 'harr;': '\u2194', - 'harrcir;': '\u2948', - 'harrw;': '\u21ad', - 'Hat;': '^', - 'hbar;': '\u210f', - 'Hcirc;': '\u0124', - 'hcirc;': '\u0125', - 'hearts;': '\u2665', - 'heartsuit;': '\u2665', - 'hellip;': '\u2026', - 'hercon;': '\u22b9', - 'Hfr;': '\u210c', - 'hfr;': '\U0001d525', - 'HilbertSpace;': '\u210b', - 'hksearow;': '\u2925', - 'hkswarow;': '\u2926', - 'hoarr;': '\u21ff', - 'homtht;': '\u223b', - 'hookleftarrow;': '\u21a9', - 'hookrightarrow;': '\u21aa', - 'Hopf;': '\u210d', - 'hopf;': '\U0001d559', - 'horbar;': '\u2015', - 'HorizontalLine;': '\u2500', - 'Hscr;': '\u210b', - 'hscr;': '\U0001d4bd', - 'hslash;': '\u210f', - 'Hstrok;': '\u0126', - 'hstrok;': '\u0127', - 'HumpDownHump;': '\u224e', - 'HumpEqual;': '\u224f', - 'hybull;': '\u2043', - 'hyphen;': '\u2010', - 'Iacute': '\xcd', - 'iacute': '\xed', - 'Iacute;': '\xcd', - 'iacute;': '\xed', - 'ic;': '\u2063', - 'Icirc': '\xce', - 'icirc': '\xee', - 'Icirc;': '\xce', - 'icirc;': '\xee', - 'Icy;': '\u0418', - 'icy;': '\u0438', - 'Idot;': '\u0130', - 'IEcy;': '\u0415', - 'iecy;': '\u0435', - 'iexcl': '\xa1', - 'iexcl;': '\xa1', - 'iff;': '\u21d4', - 'Ifr;': '\u2111', - 'ifr;': '\U0001d526', - 'Igrave': '\xcc', - 'igrave': '\xec', - 'Igrave;': '\xcc', - 'igrave;': '\xec', - 'ii;': '\u2148', - 'iiiint;': '\u2a0c', - 'iiint;': '\u222d', - 'iinfin;': '\u29dc', - 'iiota;': '\u2129', - 'IJlig;': '\u0132', - 'ijlig;': '\u0133', - 'Im;': '\u2111', - 'Imacr;': '\u012a', - 'imacr;': '\u012b', - 'image;': '\u2111', - 'ImaginaryI;': '\u2148', - 'imagline;': '\u2110', - 'imagpart;': '\u2111', - 'imath;': '\u0131', - 'imof;': '\u22b7', - 'imped;': '\u01b5', - 'Implies;': '\u21d2', - 'in;': '\u2208', - 'incare;': '\u2105', - 'infin;': '\u221e', - 'infintie;': '\u29dd', - 'inodot;': '\u0131', - 'Int;': '\u222c', - 'int;': '\u222b', - 'intcal;': '\u22ba', - 'integers;': '\u2124', - 'Integral;': '\u222b', - 'intercal;': '\u22ba', - 'Intersection;': '\u22c2', - 'intlarhk;': '\u2a17', - 'intprod;': '\u2a3c', - 'InvisibleComma;': '\u2063', - 'InvisibleTimes;': '\u2062', - 'IOcy;': '\u0401', - 'iocy;': '\u0451', - 'Iogon;': '\u012e', - 'iogon;': '\u012f', - 'Iopf;': '\U0001d540', - 'iopf;': '\U0001d55a', - 'Iota;': '\u0399', - 'iota;': '\u03b9', - 'iprod;': '\u2a3c', - 'iquest': '\xbf', - 'iquest;': '\xbf', - 'Iscr;': '\u2110', - 'iscr;': '\U0001d4be', - 'isin;': '\u2208', - 'isindot;': '\u22f5', - 'isinE;': '\u22f9', - 'isins;': '\u22f4', - 'isinsv;': '\u22f3', - 'isinv;': '\u2208', - 'it;': '\u2062', - 'Itilde;': '\u0128', - 'itilde;': '\u0129', - 'Iukcy;': '\u0406', - 'iukcy;': '\u0456', - 'Iuml': '\xcf', - 'iuml': '\xef', - 'Iuml;': '\xcf', - 'iuml;': '\xef', - 'Jcirc;': '\u0134', - 'jcirc;': '\u0135', - 'Jcy;': '\u0419', - 'jcy;': '\u0439', - 'Jfr;': '\U0001d50d', - 'jfr;': '\U0001d527', - 'jmath;': '\u0237', - 'Jopf;': '\U0001d541', - 'jopf;': '\U0001d55b', - 'Jscr;': '\U0001d4a5', - 'jscr;': '\U0001d4bf', - 'Jsercy;': '\u0408', - 'jsercy;': '\u0458', - 'Jukcy;': '\u0404', - 'jukcy;': '\u0454', - 'Kappa;': '\u039a', - 'kappa;': '\u03ba', - 'kappav;': '\u03f0', - 'Kcedil;': '\u0136', - 'kcedil;': '\u0137', - 'Kcy;': '\u041a', - 'kcy;': '\u043a', - 'Kfr;': '\U0001d50e', - 'kfr;': '\U0001d528', - 'kgreen;': '\u0138', - 'KHcy;': '\u0425', - 'khcy;': '\u0445', - 'KJcy;': '\u040c', - 'kjcy;': '\u045c', - 'Kopf;': '\U0001d542', - 'kopf;': '\U0001d55c', - 'Kscr;': '\U0001d4a6', - 'kscr;': '\U0001d4c0', - 'lAarr;': '\u21da', - 'Lacute;': '\u0139', - 'lacute;': '\u013a', - 'laemptyv;': '\u29b4', - 'lagran;': '\u2112', - 'Lambda;': '\u039b', - 'lambda;': '\u03bb', - 'Lang;': '\u27ea', - 'lang;': '\u27e8', - 'langd;': '\u2991', - 'langle;': '\u27e8', - 'lap;': '\u2a85', - 'Laplacetrf;': '\u2112', - 'laquo': '\xab', - 'laquo;': '\xab', - 'Larr;': '\u219e', - 'lArr;': '\u21d0', - 'larr;': '\u2190', - 'larrb;': '\u21e4', - 'larrbfs;': '\u291f', - 'larrfs;': '\u291d', - 'larrhk;': '\u21a9', - 'larrlp;': '\u21ab', - 'larrpl;': '\u2939', - 'larrsim;': '\u2973', - 'larrtl;': '\u21a2', - 'lat;': '\u2aab', - 'lAtail;': '\u291b', - 'latail;': '\u2919', - 'late;': '\u2aad', - 'lates;': '\u2aad\ufe00', - 'lBarr;': '\u290e', - 'lbarr;': '\u290c', - 'lbbrk;': '\u2772', - 'lbrace;': '{', - 'lbrack;': '[', - 'lbrke;': '\u298b', - 'lbrksld;': '\u298f', - 'lbrkslu;': '\u298d', - 'Lcaron;': '\u013d', - 'lcaron;': '\u013e', - 'Lcedil;': '\u013b', - 'lcedil;': '\u013c', - 'lceil;': '\u2308', - 'lcub;': '{', - 'Lcy;': '\u041b', - 'lcy;': '\u043b', - 'ldca;': '\u2936', - 'ldquo;': '\u201c', - 'ldquor;': '\u201e', - 'ldrdhar;': '\u2967', - 'ldrushar;': '\u294b', - 'ldsh;': '\u21b2', - 'lE;': '\u2266', - 'le;': '\u2264', - 'LeftAngleBracket;': '\u27e8', - 'LeftArrow;': '\u2190', - 'Leftarrow;': '\u21d0', - 'leftarrow;': '\u2190', - 'LeftArrowBar;': '\u21e4', - 'LeftArrowRightArrow;': '\u21c6', - 'leftarrowtail;': '\u21a2', - 'LeftCeiling;': '\u2308', - 'LeftDoubleBracket;': '\u27e6', - 'LeftDownTeeVector;': '\u2961', - 'LeftDownVector;': '\u21c3', - 'LeftDownVectorBar;': '\u2959', - 'LeftFloor;': '\u230a', - 'leftharpoondown;': '\u21bd', - 'leftharpoonup;': '\u21bc', - 'leftleftarrows;': '\u21c7', - 'LeftRightArrow;': '\u2194', - 'Leftrightarrow;': '\u21d4', - 'leftrightarrow;': '\u2194', - 'leftrightarrows;': '\u21c6', - 'leftrightharpoons;': '\u21cb', - 'leftrightsquigarrow;': '\u21ad', - 'LeftRightVector;': '\u294e', - 'LeftTee;': '\u22a3', - 'LeftTeeArrow;': '\u21a4', - 'LeftTeeVector;': '\u295a', - 'leftthreetimes;': '\u22cb', - 'LeftTriangle;': '\u22b2', - 'LeftTriangleBar;': '\u29cf', - 'LeftTriangleEqual;': '\u22b4', - 'LeftUpDownVector;': '\u2951', - 'LeftUpTeeVector;': '\u2960', - 'LeftUpVector;': '\u21bf', - 'LeftUpVectorBar;': '\u2958', - 'LeftVector;': '\u21bc', - 'LeftVectorBar;': '\u2952', - 'lEg;': '\u2a8b', - 'leg;': '\u22da', - 'leq;': '\u2264', - 'leqq;': '\u2266', - 'leqslant;': '\u2a7d', - 'les;': '\u2a7d', - 'lescc;': '\u2aa8', - 'lesdot;': '\u2a7f', - 'lesdoto;': '\u2a81', - 'lesdotor;': '\u2a83', - 'lesg;': '\u22da\ufe00', - 'lesges;': '\u2a93', - 'lessapprox;': '\u2a85', - 'lessdot;': '\u22d6', - 'lesseqgtr;': '\u22da', - 'lesseqqgtr;': '\u2a8b', - 'LessEqualGreater;': '\u22da', - 'LessFullEqual;': '\u2266', - 'LessGreater;': '\u2276', - 'lessgtr;': '\u2276', - 'LessLess;': '\u2aa1', - 'lesssim;': '\u2272', - 'LessSlantEqual;': '\u2a7d', - 'LessTilde;': '\u2272', - 'lfisht;': '\u297c', - 'lfloor;': '\u230a', - 'Lfr;': '\U0001d50f', - 'lfr;': '\U0001d529', - 'lg;': '\u2276', - 'lgE;': '\u2a91', - 'lHar;': '\u2962', - 'lhard;': '\u21bd', - 'lharu;': '\u21bc', - 'lharul;': '\u296a', - 'lhblk;': '\u2584', - 'LJcy;': '\u0409', - 'ljcy;': '\u0459', - 'Ll;': '\u22d8', - 'll;': '\u226a', - 'llarr;': '\u21c7', - 'llcorner;': '\u231e', - 'Lleftarrow;': '\u21da', - 'llhard;': '\u296b', - 'lltri;': '\u25fa', - 'Lmidot;': '\u013f', - 'lmidot;': '\u0140', - 'lmoust;': '\u23b0', - 'lmoustache;': '\u23b0', - 'lnap;': '\u2a89', - 'lnapprox;': '\u2a89', - 'lnE;': '\u2268', - 'lne;': '\u2a87', - 'lneq;': '\u2a87', - 'lneqq;': '\u2268', - 'lnsim;': '\u22e6', - 'loang;': '\u27ec', - 'loarr;': '\u21fd', - 'lobrk;': '\u27e6', - 'LongLeftArrow;': '\u27f5', - 'Longleftarrow;': '\u27f8', - 'longleftarrow;': '\u27f5', - 'LongLeftRightArrow;': '\u27f7', - 'Longleftrightarrow;': '\u27fa', - 'longleftrightarrow;': '\u27f7', - 'longmapsto;': '\u27fc', - 'LongRightArrow;': '\u27f6', - 'Longrightarrow;': '\u27f9', - 'longrightarrow;': '\u27f6', - 'looparrowleft;': '\u21ab', - 'looparrowright;': '\u21ac', - 'lopar;': '\u2985', - 'Lopf;': '\U0001d543', - 'lopf;': '\U0001d55d', - 'loplus;': '\u2a2d', - 'lotimes;': '\u2a34', - 'lowast;': '\u2217', - 'lowbar;': '_', - 'LowerLeftArrow;': '\u2199', - 'LowerRightArrow;': '\u2198', - 'loz;': '\u25ca', - 'lozenge;': '\u25ca', - 'lozf;': '\u29eb', - 'lpar;': '(', - 'lparlt;': '\u2993', - 'lrarr;': '\u21c6', - 'lrcorner;': '\u231f', - 'lrhar;': '\u21cb', - 'lrhard;': '\u296d', - 'lrm;': '\u200e', - 'lrtri;': '\u22bf', - 'lsaquo;': '\u2039', - 'Lscr;': '\u2112', - 'lscr;': '\U0001d4c1', - 'Lsh;': '\u21b0', - 'lsh;': '\u21b0', - 'lsim;': '\u2272', - 'lsime;': '\u2a8d', - 'lsimg;': '\u2a8f', - 'lsqb;': '[', - 'lsquo;': '\u2018', - 'lsquor;': '\u201a', - 'Lstrok;': '\u0141', - 'lstrok;': '\u0142', - 'LT': '<', - 'lt': '<', - 'LT;': '<', - 'Lt;': '\u226a', - 'lt;': '<', - 'ltcc;': '\u2aa6', - 'ltcir;': '\u2a79', - 'ltdot;': '\u22d6', - 'lthree;': '\u22cb', - 'ltimes;': '\u22c9', - 'ltlarr;': '\u2976', - 'ltquest;': '\u2a7b', - 'ltri;': '\u25c3', - 'ltrie;': '\u22b4', - 'ltrif;': '\u25c2', - 'ltrPar;': '\u2996', - 'lurdshar;': '\u294a', - 'luruhar;': '\u2966', - 'lvertneqq;': '\u2268\ufe00', - 'lvnE;': '\u2268\ufe00', - 'macr': '\xaf', - 'macr;': '\xaf', - 'male;': '\u2642', - 'malt;': '\u2720', - 'maltese;': '\u2720', - 'Map;': '\u2905', - 'map;': '\u21a6', - 'mapsto;': '\u21a6', - 'mapstodown;': '\u21a7', - 'mapstoleft;': '\u21a4', - 'mapstoup;': '\u21a5', - 'marker;': '\u25ae', - 'mcomma;': '\u2a29', - 'Mcy;': '\u041c', - 'mcy;': '\u043c', - 'mdash;': '\u2014', - 'mDDot;': '\u223a', - 'measuredangle;': '\u2221', - 'MediumSpace;': '\u205f', - 'Mellintrf;': '\u2133', - 'Mfr;': '\U0001d510', - 'mfr;': '\U0001d52a', - 'mho;': '\u2127', - 'micro': '\xb5', - 'micro;': '\xb5', - 'mid;': '\u2223', - 'midast;': '*', - 'midcir;': '\u2af0', - 'middot': '\xb7', - 'middot;': '\xb7', - 'minus;': '\u2212', - 'minusb;': '\u229f', - 'minusd;': '\u2238', - 'minusdu;': '\u2a2a', - 'MinusPlus;': '\u2213', - 'mlcp;': '\u2adb', - 'mldr;': '\u2026', - 'mnplus;': '\u2213', - 'models;': '\u22a7', - 'Mopf;': '\U0001d544', - 'mopf;': '\U0001d55e', - 'mp;': '\u2213', - 'Mscr;': '\u2133', - 'mscr;': '\U0001d4c2', - 'mstpos;': '\u223e', - 'Mu;': '\u039c', - 'mu;': '\u03bc', - 'multimap;': '\u22b8', - 'mumap;': '\u22b8', - 'nabla;': '\u2207', - 'Nacute;': '\u0143', - 'nacute;': '\u0144', - 'nang;': '\u2220\u20d2', - 'nap;': '\u2249', - 'napE;': '\u2a70\u0338', - 'napid;': '\u224b\u0338', - 'napos;': '\u0149', - 'napprox;': '\u2249', - 'natur;': '\u266e', - 'natural;': '\u266e', - 'naturals;': '\u2115', - 'nbsp': '\xa0', - 'nbsp;': '\xa0', - 'nbump;': '\u224e\u0338', - 'nbumpe;': '\u224f\u0338', - 'ncap;': '\u2a43', - 'Ncaron;': '\u0147', - 'ncaron;': '\u0148', - 'Ncedil;': '\u0145', - 'ncedil;': '\u0146', - 'ncong;': '\u2247', - 'ncongdot;': '\u2a6d\u0338', - 'ncup;': '\u2a42', - 'Ncy;': '\u041d', - 'ncy;': '\u043d', - 'ndash;': '\u2013', - 'ne;': '\u2260', - 'nearhk;': '\u2924', - 'neArr;': '\u21d7', - 'nearr;': '\u2197', - 'nearrow;': '\u2197', - 'nedot;': '\u2250\u0338', - 'NegativeMediumSpace;': '\u200b', - 'NegativeThickSpace;': '\u200b', - 'NegativeThinSpace;': '\u200b', - 'NegativeVeryThinSpace;': '\u200b', - 'nequiv;': '\u2262', - 'nesear;': '\u2928', - 'nesim;': '\u2242\u0338', - 'NestedGreaterGreater;': '\u226b', - 'NestedLessLess;': '\u226a', - 'NewLine;': '\n', - 'nexist;': '\u2204', - 'nexists;': '\u2204', - 'Nfr;': '\U0001d511', - 'nfr;': '\U0001d52b', - 'ngE;': '\u2267\u0338', - 'nge;': '\u2271', - 'ngeq;': '\u2271', - 'ngeqq;': '\u2267\u0338', - 'ngeqslant;': '\u2a7e\u0338', - 'nges;': '\u2a7e\u0338', - 'nGg;': '\u22d9\u0338', - 'ngsim;': '\u2275', - 'nGt;': '\u226b\u20d2', - 'ngt;': '\u226f', - 'ngtr;': '\u226f', - 'nGtv;': '\u226b\u0338', - 'nhArr;': '\u21ce', - 'nharr;': '\u21ae', - 'nhpar;': '\u2af2', - 'ni;': '\u220b', - 'nis;': '\u22fc', - 'nisd;': '\u22fa', - 'niv;': '\u220b', - 'NJcy;': '\u040a', - 'njcy;': '\u045a', - 'nlArr;': '\u21cd', - 'nlarr;': '\u219a', - 'nldr;': '\u2025', - 'nlE;': '\u2266\u0338', - 'nle;': '\u2270', - 'nLeftarrow;': '\u21cd', - 'nleftarrow;': '\u219a', - 'nLeftrightarrow;': '\u21ce', - 'nleftrightarrow;': '\u21ae', - 'nleq;': '\u2270', - 'nleqq;': '\u2266\u0338', - 'nleqslant;': '\u2a7d\u0338', - 'nles;': '\u2a7d\u0338', - 'nless;': '\u226e', - 'nLl;': '\u22d8\u0338', - 'nlsim;': '\u2274', - 'nLt;': '\u226a\u20d2', - 'nlt;': '\u226e', - 'nltri;': '\u22ea', - 'nltrie;': '\u22ec', - 'nLtv;': '\u226a\u0338', - 'nmid;': '\u2224', - 'NoBreak;': '\u2060', - 'NonBreakingSpace;': '\xa0', - 'Nopf;': '\u2115', - 'nopf;': '\U0001d55f', - 'not': '\xac', - 'Not;': '\u2aec', - 'not;': '\xac', - 'NotCongruent;': '\u2262', - 'NotCupCap;': '\u226d', - 'NotDoubleVerticalBar;': '\u2226', - 'NotElement;': '\u2209', - 'NotEqual;': '\u2260', - 'NotEqualTilde;': '\u2242\u0338', - 'NotExists;': '\u2204', - 'NotGreater;': '\u226f', - 'NotGreaterEqual;': '\u2271', - 'NotGreaterFullEqual;': '\u2267\u0338', - 'NotGreaterGreater;': '\u226b\u0338', - 'NotGreaterLess;': '\u2279', - 'NotGreaterSlantEqual;': '\u2a7e\u0338', - 'NotGreaterTilde;': '\u2275', - 'NotHumpDownHump;': '\u224e\u0338', - 'NotHumpEqual;': '\u224f\u0338', - 'notin;': '\u2209', - 'notindot;': '\u22f5\u0338', - 'notinE;': '\u22f9\u0338', - 'notinva;': '\u2209', - 'notinvb;': '\u22f7', - 'notinvc;': '\u22f6', - 'NotLeftTriangle;': '\u22ea', - 'NotLeftTriangleBar;': '\u29cf\u0338', - 'NotLeftTriangleEqual;': '\u22ec', - 'NotLess;': '\u226e', - 'NotLessEqual;': '\u2270', - 'NotLessGreater;': '\u2278', - 'NotLessLess;': '\u226a\u0338', - 'NotLessSlantEqual;': '\u2a7d\u0338', - 'NotLessTilde;': '\u2274', - 'NotNestedGreaterGreater;': '\u2aa2\u0338', - 'NotNestedLessLess;': '\u2aa1\u0338', - 'notni;': '\u220c', - 'notniva;': '\u220c', - 'notnivb;': '\u22fe', - 'notnivc;': '\u22fd', - 'NotPrecedes;': '\u2280', - 'NotPrecedesEqual;': '\u2aaf\u0338', - 'NotPrecedesSlantEqual;': '\u22e0', - 'NotReverseElement;': '\u220c', - 'NotRightTriangle;': '\u22eb', - 'NotRightTriangleBar;': '\u29d0\u0338', - 'NotRightTriangleEqual;': '\u22ed', - 'NotSquareSubset;': '\u228f\u0338', - 'NotSquareSubsetEqual;': '\u22e2', - 'NotSquareSuperset;': '\u2290\u0338', - 'NotSquareSupersetEqual;': '\u22e3', - 'NotSubset;': '\u2282\u20d2', - 'NotSubsetEqual;': '\u2288', - 'NotSucceeds;': '\u2281', - 'NotSucceedsEqual;': '\u2ab0\u0338', - 'NotSucceedsSlantEqual;': '\u22e1', - 'NotSucceedsTilde;': '\u227f\u0338', - 'NotSuperset;': '\u2283\u20d2', - 'NotSupersetEqual;': '\u2289', - 'NotTilde;': '\u2241', - 'NotTildeEqual;': '\u2244', - 'NotTildeFullEqual;': '\u2247', - 'NotTildeTilde;': '\u2249', - 'NotVerticalBar;': '\u2224', - 'npar;': '\u2226', - 'nparallel;': '\u2226', - 'nparsl;': '\u2afd\u20e5', - 'npart;': '\u2202\u0338', - 'npolint;': '\u2a14', - 'npr;': '\u2280', - 'nprcue;': '\u22e0', - 'npre;': '\u2aaf\u0338', - 'nprec;': '\u2280', - 'npreceq;': '\u2aaf\u0338', - 'nrArr;': '\u21cf', - 'nrarr;': '\u219b', - 'nrarrc;': '\u2933\u0338', - 'nrarrw;': '\u219d\u0338', - 'nRightarrow;': '\u21cf', - 'nrightarrow;': '\u219b', - 'nrtri;': '\u22eb', - 'nrtrie;': '\u22ed', - 'nsc;': '\u2281', - 'nsccue;': '\u22e1', - 'nsce;': '\u2ab0\u0338', - 'Nscr;': '\U0001d4a9', - 'nscr;': '\U0001d4c3', - 'nshortmid;': '\u2224', - 'nshortparallel;': '\u2226', - 'nsim;': '\u2241', - 'nsime;': '\u2244', - 'nsimeq;': '\u2244', - 'nsmid;': '\u2224', - 'nspar;': '\u2226', - 'nsqsube;': '\u22e2', - 'nsqsupe;': '\u22e3', - 'nsub;': '\u2284', - 'nsubE;': '\u2ac5\u0338', - 'nsube;': '\u2288', - 'nsubset;': '\u2282\u20d2', - 'nsubseteq;': '\u2288', - 'nsubseteqq;': '\u2ac5\u0338', - 'nsucc;': '\u2281', - 'nsucceq;': '\u2ab0\u0338', - 'nsup;': '\u2285', - 'nsupE;': '\u2ac6\u0338', - 'nsupe;': '\u2289', - 'nsupset;': '\u2283\u20d2', - 'nsupseteq;': '\u2289', - 'nsupseteqq;': '\u2ac6\u0338', - 'ntgl;': '\u2279', - 'Ntilde': '\xd1', - 'ntilde': '\xf1', - 'Ntilde;': '\xd1', - 'ntilde;': '\xf1', - 'ntlg;': '\u2278', - 'ntriangleleft;': '\u22ea', - 'ntrianglelefteq;': '\u22ec', - 'ntriangleright;': '\u22eb', - 'ntrianglerighteq;': '\u22ed', - 'Nu;': '\u039d', - 'nu;': '\u03bd', - 'num;': '#', - 'numero;': '\u2116', - 'numsp;': '\u2007', - 'nvap;': '\u224d\u20d2', - 'nVDash;': '\u22af', - 'nVdash;': '\u22ae', - 'nvDash;': '\u22ad', - 'nvdash;': '\u22ac', - 'nvge;': '\u2265\u20d2', - 'nvgt;': '>\u20d2', - 'nvHarr;': '\u2904', - 'nvinfin;': '\u29de', - 'nvlArr;': '\u2902', - 'nvle;': '\u2264\u20d2', - 'nvlt;': '<\u20d2', - 'nvltrie;': '\u22b4\u20d2', - 'nvrArr;': '\u2903', - 'nvrtrie;': '\u22b5\u20d2', - 'nvsim;': '\u223c\u20d2', - 'nwarhk;': '\u2923', - 'nwArr;': '\u21d6', - 'nwarr;': '\u2196', - 'nwarrow;': '\u2196', - 'nwnear;': '\u2927', - 'Oacute': '\xd3', - 'oacute': '\xf3', - 'Oacute;': '\xd3', - 'oacute;': '\xf3', - 'oast;': '\u229b', - 'ocir;': '\u229a', - 'Ocirc': '\xd4', - 'ocirc': '\xf4', - 'Ocirc;': '\xd4', - 'ocirc;': '\xf4', - 'Ocy;': '\u041e', - 'ocy;': '\u043e', - 'odash;': '\u229d', - 'Odblac;': '\u0150', - 'odblac;': '\u0151', - 'odiv;': '\u2a38', - 'odot;': '\u2299', - 'odsold;': '\u29bc', - 'OElig;': '\u0152', - 'oelig;': '\u0153', - 'ofcir;': '\u29bf', - 'Ofr;': '\U0001d512', - 'ofr;': '\U0001d52c', - 'ogon;': '\u02db', - 'Ograve': '\xd2', - 'ograve': '\xf2', - 'Ograve;': '\xd2', - 'ograve;': '\xf2', - 'ogt;': '\u29c1', - 'ohbar;': '\u29b5', - 'ohm;': '\u03a9', - 'oint;': '\u222e', - 'olarr;': '\u21ba', - 'olcir;': '\u29be', - 'olcross;': '\u29bb', - 'oline;': '\u203e', - 'olt;': '\u29c0', - 'Omacr;': '\u014c', - 'omacr;': '\u014d', - 'Omega;': '\u03a9', - 'omega;': '\u03c9', - 'Omicron;': '\u039f', - 'omicron;': '\u03bf', - 'omid;': '\u29b6', - 'ominus;': '\u2296', - 'Oopf;': '\U0001d546', - 'oopf;': '\U0001d560', - 'opar;': '\u29b7', - 'OpenCurlyDoubleQuote;': '\u201c', - 'OpenCurlyQuote;': '\u2018', - 'operp;': '\u29b9', - 'oplus;': '\u2295', - 'Or;': '\u2a54', - 'or;': '\u2228', - 'orarr;': '\u21bb', - 'ord;': '\u2a5d', - 'order;': '\u2134', - 'orderof;': '\u2134', - 'ordf': '\xaa', - 'ordf;': '\xaa', - 'ordm': '\xba', - 'ordm;': '\xba', - 'origof;': '\u22b6', - 'oror;': '\u2a56', - 'orslope;': '\u2a57', - 'orv;': '\u2a5b', - 'oS;': '\u24c8', - 'Oscr;': '\U0001d4aa', - 'oscr;': '\u2134', - 'Oslash': '\xd8', - 'oslash': '\xf8', - 'Oslash;': '\xd8', - 'oslash;': '\xf8', - 'osol;': '\u2298', - 'Otilde': '\xd5', - 'otilde': '\xf5', - 'Otilde;': '\xd5', - 'otilde;': '\xf5', - 'Otimes;': '\u2a37', - 'otimes;': '\u2297', - 'otimesas;': '\u2a36', - 'Ouml': '\xd6', - 'ouml': '\xf6', - 'Ouml;': '\xd6', - 'ouml;': '\xf6', - 'ovbar;': '\u233d', - 'OverBar;': '\u203e', - 'OverBrace;': '\u23de', - 'OverBracket;': '\u23b4', - 'OverParenthesis;': '\u23dc', - 'par;': '\u2225', - 'para': '\xb6', - 'para;': '\xb6', - 'parallel;': '\u2225', - 'parsim;': '\u2af3', - 'parsl;': '\u2afd', - 'part;': '\u2202', - 'PartialD;': '\u2202', - 'Pcy;': '\u041f', - 'pcy;': '\u043f', - 'percnt;': '%', - 'period;': '.', - 'permil;': '\u2030', - 'perp;': '\u22a5', - 'pertenk;': '\u2031', - 'Pfr;': '\U0001d513', - 'pfr;': '\U0001d52d', - 'Phi;': '\u03a6', - 'phi;': '\u03c6', - 'phiv;': '\u03d5', - 'phmmat;': '\u2133', - 'phone;': '\u260e', - 'Pi;': '\u03a0', - 'pi;': '\u03c0', - 'pitchfork;': '\u22d4', - 'piv;': '\u03d6', - 'planck;': '\u210f', - 'planckh;': '\u210e', - 'plankv;': '\u210f', - 'plus;': '+', - 'plusacir;': '\u2a23', - 'plusb;': '\u229e', - 'pluscir;': '\u2a22', - 'plusdo;': '\u2214', - 'plusdu;': '\u2a25', - 'pluse;': '\u2a72', - 'PlusMinus;': '\xb1', - 'plusmn': '\xb1', - 'plusmn;': '\xb1', - 'plussim;': '\u2a26', - 'plustwo;': '\u2a27', - 'pm;': '\xb1', - 'Poincareplane;': '\u210c', - 'pointint;': '\u2a15', - 'Popf;': '\u2119', - 'popf;': '\U0001d561', - 'pound': '\xa3', - 'pound;': '\xa3', - 'Pr;': '\u2abb', - 'pr;': '\u227a', - 'prap;': '\u2ab7', - 'prcue;': '\u227c', - 'prE;': '\u2ab3', - 'pre;': '\u2aaf', - 'prec;': '\u227a', - 'precapprox;': '\u2ab7', - 'preccurlyeq;': '\u227c', - 'Precedes;': '\u227a', - 'PrecedesEqual;': '\u2aaf', - 'PrecedesSlantEqual;': '\u227c', - 'PrecedesTilde;': '\u227e', - 'preceq;': '\u2aaf', - 'precnapprox;': '\u2ab9', - 'precneqq;': '\u2ab5', - 'precnsim;': '\u22e8', - 'precsim;': '\u227e', - 'Prime;': '\u2033', - 'prime;': '\u2032', - 'primes;': '\u2119', - 'prnap;': '\u2ab9', - 'prnE;': '\u2ab5', - 'prnsim;': '\u22e8', - 'prod;': '\u220f', - 'Product;': '\u220f', - 'profalar;': '\u232e', - 'profline;': '\u2312', - 'profsurf;': '\u2313', - 'prop;': '\u221d', - 'Proportion;': '\u2237', - 'Proportional;': '\u221d', - 'propto;': '\u221d', - 'prsim;': '\u227e', - 'prurel;': '\u22b0', - 'Pscr;': '\U0001d4ab', - 'pscr;': '\U0001d4c5', - 'Psi;': '\u03a8', - 'psi;': '\u03c8', - 'puncsp;': '\u2008', - 'Qfr;': '\U0001d514', - 'qfr;': '\U0001d52e', - 'qint;': '\u2a0c', - 'Qopf;': '\u211a', - 'qopf;': '\U0001d562', - 'qprime;': '\u2057', - 'Qscr;': '\U0001d4ac', - 'qscr;': '\U0001d4c6', - 'quaternions;': '\u210d', - 'quatint;': '\u2a16', - 'quest;': '?', - 'questeq;': '\u225f', - 'QUOT': '"', - 'quot': '"', - 'QUOT;': '"', - 'quot;': '"', - 'rAarr;': '\u21db', - 'race;': '\u223d\u0331', - 'Racute;': '\u0154', - 'racute;': '\u0155', - 'radic;': '\u221a', - 'raemptyv;': '\u29b3', - 'Rang;': '\u27eb', - 'rang;': '\u27e9', - 'rangd;': '\u2992', - 'range;': '\u29a5', - 'rangle;': '\u27e9', - 'raquo': '\xbb', - 'raquo;': '\xbb', - 'Rarr;': '\u21a0', - 'rArr;': '\u21d2', - 'rarr;': '\u2192', - 'rarrap;': '\u2975', - 'rarrb;': '\u21e5', - 'rarrbfs;': '\u2920', - 'rarrc;': '\u2933', - 'rarrfs;': '\u291e', - 'rarrhk;': '\u21aa', - 'rarrlp;': '\u21ac', - 'rarrpl;': '\u2945', - 'rarrsim;': '\u2974', - 'Rarrtl;': '\u2916', - 'rarrtl;': '\u21a3', - 'rarrw;': '\u219d', - 'rAtail;': '\u291c', - 'ratail;': '\u291a', - 'ratio;': '\u2236', - 'rationals;': '\u211a', - 'RBarr;': '\u2910', - 'rBarr;': '\u290f', - 'rbarr;': '\u290d', - 'rbbrk;': '\u2773', - 'rbrace;': '}', - 'rbrack;': ']', - 'rbrke;': '\u298c', - 'rbrksld;': '\u298e', - 'rbrkslu;': '\u2990', - 'Rcaron;': '\u0158', - 'rcaron;': '\u0159', - 'Rcedil;': '\u0156', - 'rcedil;': '\u0157', - 'rceil;': '\u2309', - 'rcub;': '}', - 'Rcy;': '\u0420', - 'rcy;': '\u0440', - 'rdca;': '\u2937', - 'rdldhar;': '\u2969', - 'rdquo;': '\u201d', - 'rdquor;': '\u201d', - 'rdsh;': '\u21b3', - 'Re;': '\u211c', - 'real;': '\u211c', - 'realine;': '\u211b', - 'realpart;': '\u211c', - 'reals;': '\u211d', - 'rect;': '\u25ad', - 'REG': '\xae', - 'reg': '\xae', - 'REG;': '\xae', - 'reg;': '\xae', - 'ReverseElement;': '\u220b', - 'ReverseEquilibrium;': '\u21cb', - 'ReverseUpEquilibrium;': '\u296f', - 'rfisht;': '\u297d', - 'rfloor;': '\u230b', - 'Rfr;': '\u211c', - 'rfr;': '\U0001d52f', - 'rHar;': '\u2964', - 'rhard;': '\u21c1', - 'rharu;': '\u21c0', - 'rharul;': '\u296c', - 'Rho;': '\u03a1', - 'rho;': '\u03c1', - 'rhov;': '\u03f1', - 'RightAngleBracket;': '\u27e9', - 'RightArrow;': '\u2192', - 'Rightarrow;': '\u21d2', - 'rightarrow;': '\u2192', - 'RightArrowBar;': '\u21e5', - 'RightArrowLeftArrow;': '\u21c4', - 'rightarrowtail;': '\u21a3', - 'RightCeiling;': '\u2309', - 'RightDoubleBracket;': '\u27e7', - 'RightDownTeeVector;': '\u295d', - 'RightDownVector;': '\u21c2', - 'RightDownVectorBar;': '\u2955', - 'RightFloor;': '\u230b', - 'rightharpoondown;': '\u21c1', - 'rightharpoonup;': '\u21c0', - 'rightleftarrows;': '\u21c4', - 'rightleftharpoons;': '\u21cc', - 'rightrightarrows;': '\u21c9', - 'rightsquigarrow;': '\u219d', - 'RightTee;': '\u22a2', - 'RightTeeArrow;': '\u21a6', - 'RightTeeVector;': '\u295b', - 'rightthreetimes;': '\u22cc', - 'RightTriangle;': '\u22b3', - 'RightTriangleBar;': '\u29d0', - 'RightTriangleEqual;': '\u22b5', - 'RightUpDownVector;': '\u294f', - 'RightUpTeeVector;': '\u295c', - 'RightUpVector;': '\u21be', - 'RightUpVectorBar;': '\u2954', - 'RightVector;': '\u21c0', - 'RightVectorBar;': '\u2953', - 'ring;': '\u02da', - 'risingdotseq;': '\u2253', - 'rlarr;': '\u21c4', - 'rlhar;': '\u21cc', - 'rlm;': '\u200f', - 'rmoust;': '\u23b1', - 'rmoustache;': '\u23b1', - 'rnmid;': '\u2aee', - 'roang;': '\u27ed', - 'roarr;': '\u21fe', - 'robrk;': '\u27e7', - 'ropar;': '\u2986', - 'Ropf;': '\u211d', - 'ropf;': '\U0001d563', - 'roplus;': '\u2a2e', - 'rotimes;': '\u2a35', - 'RoundImplies;': '\u2970', - 'rpar;': ')', - 'rpargt;': '\u2994', - 'rppolint;': '\u2a12', - 'rrarr;': '\u21c9', - 'Rrightarrow;': '\u21db', - 'rsaquo;': '\u203a', - 'Rscr;': '\u211b', - 'rscr;': '\U0001d4c7', - 'Rsh;': '\u21b1', - 'rsh;': '\u21b1', - 'rsqb;': ']', - 'rsquo;': '\u2019', - 'rsquor;': '\u2019', - 'rthree;': '\u22cc', - 'rtimes;': '\u22ca', - 'rtri;': '\u25b9', - 'rtrie;': '\u22b5', - 'rtrif;': '\u25b8', - 'rtriltri;': '\u29ce', - 'RuleDelayed;': '\u29f4', - 'ruluhar;': '\u2968', - 'rx;': '\u211e', - 'Sacute;': '\u015a', - 'sacute;': '\u015b', - 'sbquo;': '\u201a', - 'Sc;': '\u2abc', - 'sc;': '\u227b', - 'scap;': '\u2ab8', - 'Scaron;': '\u0160', - 'scaron;': '\u0161', - 'sccue;': '\u227d', - 'scE;': '\u2ab4', - 'sce;': '\u2ab0', - 'Scedil;': '\u015e', - 'scedil;': '\u015f', - 'Scirc;': '\u015c', - 'scirc;': '\u015d', - 'scnap;': '\u2aba', - 'scnE;': '\u2ab6', - 'scnsim;': '\u22e9', - 'scpolint;': '\u2a13', - 'scsim;': '\u227f', - 'Scy;': '\u0421', - 'scy;': '\u0441', - 'sdot;': '\u22c5', - 'sdotb;': '\u22a1', - 'sdote;': '\u2a66', - 'searhk;': '\u2925', - 'seArr;': '\u21d8', - 'searr;': '\u2198', - 'searrow;': '\u2198', - 'sect': '\xa7', - 'sect;': '\xa7', - 'semi;': ';', - 'seswar;': '\u2929', - 'setminus;': '\u2216', - 'setmn;': '\u2216', - 'sext;': '\u2736', - 'Sfr;': '\U0001d516', - 'sfr;': '\U0001d530', - 'sfrown;': '\u2322', - 'sharp;': '\u266f', - 'SHCHcy;': '\u0429', - 'shchcy;': '\u0449', - 'SHcy;': '\u0428', - 'shcy;': '\u0448', - 'ShortDownArrow;': '\u2193', - 'ShortLeftArrow;': '\u2190', - 'shortmid;': '\u2223', - 'shortparallel;': '\u2225', - 'ShortRightArrow;': '\u2192', - 'ShortUpArrow;': '\u2191', - 'shy': '\xad', - 'shy;': '\xad', - 'Sigma;': '\u03a3', - 'sigma;': '\u03c3', - 'sigmaf;': '\u03c2', - 'sigmav;': '\u03c2', - 'sim;': '\u223c', - 'simdot;': '\u2a6a', - 'sime;': '\u2243', - 'simeq;': '\u2243', - 'simg;': '\u2a9e', - 'simgE;': '\u2aa0', - 'siml;': '\u2a9d', - 'simlE;': '\u2a9f', - 'simne;': '\u2246', - 'simplus;': '\u2a24', - 'simrarr;': '\u2972', - 'slarr;': '\u2190', - 'SmallCircle;': '\u2218', - 'smallsetminus;': '\u2216', - 'smashp;': '\u2a33', - 'smeparsl;': '\u29e4', - 'smid;': '\u2223', - 'smile;': '\u2323', - 'smt;': '\u2aaa', - 'smte;': '\u2aac', - 'smtes;': '\u2aac\ufe00', - 'SOFTcy;': '\u042c', - 'softcy;': '\u044c', - 'sol;': '/', - 'solb;': '\u29c4', - 'solbar;': '\u233f', - 'Sopf;': '\U0001d54a', - 'sopf;': '\U0001d564', - 'spades;': '\u2660', - 'spadesuit;': '\u2660', - 'spar;': '\u2225', - 'sqcap;': '\u2293', - 'sqcaps;': '\u2293\ufe00', - 'sqcup;': '\u2294', - 'sqcups;': '\u2294\ufe00', - 'Sqrt;': '\u221a', - 'sqsub;': '\u228f', - 'sqsube;': '\u2291', - 'sqsubset;': '\u228f', - 'sqsubseteq;': '\u2291', - 'sqsup;': '\u2290', - 'sqsupe;': '\u2292', - 'sqsupset;': '\u2290', - 'sqsupseteq;': '\u2292', - 'squ;': '\u25a1', - 'Square;': '\u25a1', - 'square;': '\u25a1', - 'SquareIntersection;': '\u2293', - 'SquareSubset;': '\u228f', - 'SquareSubsetEqual;': '\u2291', - 'SquareSuperset;': '\u2290', - 'SquareSupersetEqual;': '\u2292', - 'SquareUnion;': '\u2294', - 'squarf;': '\u25aa', - 'squf;': '\u25aa', - 'srarr;': '\u2192', - 'Sscr;': '\U0001d4ae', - 'sscr;': '\U0001d4c8', - 'ssetmn;': '\u2216', - 'ssmile;': '\u2323', - 'sstarf;': '\u22c6', - 'Star;': '\u22c6', - 'star;': '\u2606', - 'starf;': '\u2605', - 'straightepsilon;': '\u03f5', - 'straightphi;': '\u03d5', - 'strns;': '\xaf', - 'Sub;': '\u22d0', - 'sub;': '\u2282', - 'subdot;': '\u2abd', - 'subE;': '\u2ac5', - 'sube;': '\u2286', - 'subedot;': '\u2ac3', - 'submult;': '\u2ac1', - 'subnE;': '\u2acb', - 'subne;': '\u228a', - 'subplus;': '\u2abf', - 'subrarr;': '\u2979', - 'Subset;': '\u22d0', - 'subset;': '\u2282', - 'subseteq;': '\u2286', - 'subseteqq;': '\u2ac5', - 'SubsetEqual;': '\u2286', - 'subsetneq;': '\u228a', - 'subsetneqq;': '\u2acb', - 'subsim;': '\u2ac7', - 'subsub;': '\u2ad5', - 'subsup;': '\u2ad3', - 'succ;': '\u227b', - 'succapprox;': '\u2ab8', - 'succcurlyeq;': '\u227d', - 'Succeeds;': '\u227b', - 'SucceedsEqual;': '\u2ab0', - 'SucceedsSlantEqual;': '\u227d', - 'SucceedsTilde;': '\u227f', - 'succeq;': '\u2ab0', - 'succnapprox;': '\u2aba', - 'succneqq;': '\u2ab6', - 'succnsim;': '\u22e9', - 'succsim;': '\u227f', - 'SuchThat;': '\u220b', - 'Sum;': '\u2211', - 'sum;': '\u2211', - 'sung;': '\u266a', - 'sup1': '\xb9', - 'sup1;': '\xb9', - 'sup2': '\xb2', - 'sup2;': '\xb2', - 'sup3': '\xb3', - 'sup3;': '\xb3', - 'Sup;': '\u22d1', - 'sup;': '\u2283', - 'supdot;': '\u2abe', - 'supdsub;': '\u2ad8', - 'supE;': '\u2ac6', - 'supe;': '\u2287', - 'supedot;': '\u2ac4', - 'Superset;': '\u2283', - 'SupersetEqual;': '\u2287', - 'suphsol;': '\u27c9', - 'suphsub;': '\u2ad7', - 'suplarr;': '\u297b', - 'supmult;': '\u2ac2', - 'supnE;': '\u2acc', - 'supne;': '\u228b', - 'supplus;': '\u2ac0', - 'Supset;': '\u22d1', - 'supset;': '\u2283', - 'supseteq;': '\u2287', - 'supseteqq;': '\u2ac6', - 'supsetneq;': '\u228b', - 'supsetneqq;': '\u2acc', - 'supsim;': '\u2ac8', - 'supsub;': '\u2ad4', - 'supsup;': '\u2ad6', - 'swarhk;': '\u2926', - 'swArr;': '\u21d9', - 'swarr;': '\u2199', - 'swarrow;': '\u2199', - 'swnwar;': '\u292a', - 'szlig': '\xdf', - 'szlig;': '\xdf', - 'Tab;': '\t', - 'target;': '\u2316', - 'Tau;': '\u03a4', - 'tau;': '\u03c4', - 'tbrk;': '\u23b4', - 'Tcaron;': '\u0164', - 'tcaron;': '\u0165', - 'Tcedil;': '\u0162', - 'tcedil;': '\u0163', - 'Tcy;': '\u0422', - 'tcy;': '\u0442', - 'tdot;': '\u20db', - 'telrec;': '\u2315', - 'Tfr;': '\U0001d517', - 'tfr;': '\U0001d531', - 'there4;': '\u2234', - 'Therefore;': '\u2234', - 'therefore;': '\u2234', - 'Theta;': '\u0398', - 'theta;': '\u03b8', - 'thetasym;': '\u03d1', - 'thetav;': '\u03d1', - 'thickapprox;': '\u2248', - 'thicksim;': '\u223c', - 'ThickSpace;': '\u205f\u200a', - 'thinsp;': '\u2009', - 'ThinSpace;': '\u2009', - 'thkap;': '\u2248', - 'thksim;': '\u223c', - 'THORN': '\xde', - 'thorn': '\xfe', - 'THORN;': '\xde', - 'thorn;': '\xfe', - 'Tilde;': '\u223c', - 'tilde;': '\u02dc', - 'TildeEqual;': '\u2243', - 'TildeFullEqual;': '\u2245', - 'TildeTilde;': '\u2248', - 'times': '\xd7', - 'times;': '\xd7', - 'timesb;': '\u22a0', - 'timesbar;': '\u2a31', - 'timesd;': '\u2a30', - 'tint;': '\u222d', - 'toea;': '\u2928', - 'top;': '\u22a4', - 'topbot;': '\u2336', - 'topcir;': '\u2af1', - 'Topf;': '\U0001d54b', - 'topf;': '\U0001d565', - 'topfork;': '\u2ada', - 'tosa;': '\u2929', - 'tprime;': '\u2034', - 'TRADE;': '\u2122', - 'trade;': '\u2122', - 'triangle;': '\u25b5', - 'triangledown;': '\u25bf', - 'triangleleft;': '\u25c3', - 'trianglelefteq;': '\u22b4', - 'triangleq;': '\u225c', - 'triangleright;': '\u25b9', - 'trianglerighteq;': '\u22b5', - 'tridot;': '\u25ec', - 'trie;': '\u225c', - 'triminus;': '\u2a3a', - 'TripleDot;': '\u20db', - 'triplus;': '\u2a39', - 'trisb;': '\u29cd', - 'tritime;': '\u2a3b', - 'trpezium;': '\u23e2', - 'Tscr;': '\U0001d4af', - 'tscr;': '\U0001d4c9', - 'TScy;': '\u0426', - 'tscy;': '\u0446', - 'TSHcy;': '\u040b', - 'tshcy;': '\u045b', - 'Tstrok;': '\u0166', - 'tstrok;': '\u0167', - 'twixt;': '\u226c', - 'twoheadleftarrow;': '\u219e', - 'twoheadrightarrow;': '\u21a0', - 'Uacute': '\xda', - 'uacute': '\xfa', - 'Uacute;': '\xda', - 'uacute;': '\xfa', - 'Uarr;': '\u219f', - 'uArr;': '\u21d1', - 'uarr;': '\u2191', - 'Uarrocir;': '\u2949', - 'Ubrcy;': '\u040e', - 'ubrcy;': '\u045e', - 'Ubreve;': '\u016c', - 'ubreve;': '\u016d', - 'Ucirc': '\xdb', - 'ucirc': '\xfb', - 'Ucirc;': '\xdb', - 'ucirc;': '\xfb', - 'Ucy;': '\u0423', - 'ucy;': '\u0443', - 'udarr;': '\u21c5', - 'Udblac;': '\u0170', - 'udblac;': '\u0171', - 'udhar;': '\u296e', - 'ufisht;': '\u297e', - 'Ufr;': '\U0001d518', - 'ufr;': '\U0001d532', - 'Ugrave': '\xd9', - 'ugrave': '\xf9', - 'Ugrave;': '\xd9', - 'ugrave;': '\xf9', - 'uHar;': '\u2963', - 'uharl;': '\u21bf', - 'uharr;': '\u21be', - 'uhblk;': '\u2580', - 'ulcorn;': '\u231c', - 'ulcorner;': '\u231c', - 'ulcrop;': '\u230f', - 'ultri;': '\u25f8', - 'Umacr;': '\u016a', - 'umacr;': '\u016b', - 'uml': '\xa8', - 'uml;': '\xa8', - 'UnderBar;': '_', - 'UnderBrace;': '\u23df', - 'UnderBracket;': '\u23b5', - 'UnderParenthesis;': '\u23dd', - 'Union;': '\u22c3', - 'UnionPlus;': '\u228e', - 'Uogon;': '\u0172', - 'uogon;': '\u0173', - 'Uopf;': '\U0001d54c', - 'uopf;': '\U0001d566', - 'UpArrow;': '\u2191', - 'Uparrow;': '\u21d1', - 'uparrow;': '\u2191', - 'UpArrowBar;': '\u2912', - 'UpArrowDownArrow;': '\u21c5', - 'UpDownArrow;': '\u2195', - 'Updownarrow;': '\u21d5', - 'updownarrow;': '\u2195', - 'UpEquilibrium;': '\u296e', - 'upharpoonleft;': '\u21bf', - 'upharpoonright;': '\u21be', - 'uplus;': '\u228e', - 'UpperLeftArrow;': '\u2196', - 'UpperRightArrow;': '\u2197', - 'Upsi;': '\u03d2', - 'upsi;': '\u03c5', - 'upsih;': '\u03d2', - 'Upsilon;': '\u03a5', - 'upsilon;': '\u03c5', - 'UpTee;': '\u22a5', - 'UpTeeArrow;': '\u21a5', - 'upuparrows;': '\u21c8', - 'urcorn;': '\u231d', - 'urcorner;': '\u231d', - 'urcrop;': '\u230e', - 'Uring;': '\u016e', - 'uring;': '\u016f', - 'urtri;': '\u25f9', - 'Uscr;': '\U0001d4b0', - 'uscr;': '\U0001d4ca', - 'utdot;': '\u22f0', - 'Utilde;': '\u0168', - 'utilde;': '\u0169', - 'utri;': '\u25b5', - 'utrif;': '\u25b4', - 'uuarr;': '\u21c8', - 'Uuml': '\xdc', - 'uuml': '\xfc', - 'Uuml;': '\xdc', - 'uuml;': '\xfc', - 'uwangle;': '\u29a7', - 'vangrt;': '\u299c', - 'varepsilon;': '\u03f5', - 'varkappa;': '\u03f0', - 'varnothing;': '\u2205', - 'varphi;': '\u03d5', - 'varpi;': '\u03d6', - 'varpropto;': '\u221d', - 'vArr;': '\u21d5', - 'varr;': '\u2195', - 'varrho;': '\u03f1', - 'varsigma;': '\u03c2', - 'varsubsetneq;': '\u228a\ufe00', - 'varsubsetneqq;': '\u2acb\ufe00', - 'varsupsetneq;': '\u228b\ufe00', - 'varsupsetneqq;': '\u2acc\ufe00', - 'vartheta;': '\u03d1', - 'vartriangleleft;': '\u22b2', - 'vartriangleright;': '\u22b3', - 'Vbar;': '\u2aeb', - 'vBar;': '\u2ae8', - 'vBarv;': '\u2ae9', - 'Vcy;': '\u0412', - 'vcy;': '\u0432', - 'VDash;': '\u22ab', - 'Vdash;': '\u22a9', - 'vDash;': '\u22a8', - 'vdash;': '\u22a2', - 'Vdashl;': '\u2ae6', - 'Vee;': '\u22c1', - 'vee;': '\u2228', - 'veebar;': '\u22bb', - 'veeeq;': '\u225a', - 'vellip;': '\u22ee', - 'Verbar;': '\u2016', - 'verbar;': '|', - 'Vert;': '\u2016', - 'vert;': '|', - 'VerticalBar;': '\u2223', - 'VerticalLine;': '|', - 'VerticalSeparator;': '\u2758', - 'VerticalTilde;': '\u2240', - 'VeryThinSpace;': '\u200a', - 'Vfr;': '\U0001d519', - 'vfr;': '\U0001d533', - 'vltri;': '\u22b2', - 'vnsub;': '\u2282\u20d2', - 'vnsup;': '\u2283\u20d2', - 'Vopf;': '\U0001d54d', - 'vopf;': '\U0001d567', - 'vprop;': '\u221d', - 'vrtri;': '\u22b3', - 'Vscr;': '\U0001d4b1', - 'vscr;': '\U0001d4cb', - 'vsubnE;': '\u2acb\ufe00', - 'vsubne;': '\u228a\ufe00', - 'vsupnE;': '\u2acc\ufe00', - 'vsupne;': '\u228b\ufe00', - 'Vvdash;': '\u22aa', - 'vzigzag;': '\u299a', - 'Wcirc;': '\u0174', - 'wcirc;': '\u0175', - 'wedbar;': '\u2a5f', - 'Wedge;': '\u22c0', - 'wedge;': '\u2227', - 'wedgeq;': '\u2259', - 'weierp;': '\u2118', - 'Wfr;': '\U0001d51a', - 'wfr;': '\U0001d534', - 'Wopf;': '\U0001d54e', - 'wopf;': '\U0001d568', - 'wp;': '\u2118', - 'wr;': '\u2240', - 'wreath;': '\u2240', - 'Wscr;': '\U0001d4b2', - 'wscr;': '\U0001d4cc', - 'xcap;': '\u22c2', - 'xcirc;': '\u25ef', - 'xcup;': '\u22c3', - 'xdtri;': '\u25bd', - 'Xfr;': '\U0001d51b', - 'xfr;': '\U0001d535', - 'xhArr;': '\u27fa', - 'xharr;': '\u27f7', - 'Xi;': '\u039e', - 'xi;': '\u03be', - 'xlArr;': '\u27f8', - 'xlarr;': '\u27f5', - 'xmap;': '\u27fc', - 'xnis;': '\u22fb', - 'xodot;': '\u2a00', - 'Xopf;': '\U0001d54f', - 'xopf;': '\U0001d569', - 'xoplus;': '\u2a01', - 'xotime;': '\u2a02', - 'xrArr;': '\u27f9', - 'xrarr;': '\u27f6', - 'Xscr;': '\U0001d4b3', - 'xscr;': '\U0001d4cd', - 'xsqcup;': '\u2a06', - 'xuplus;': '\u2a04', - 'xutri;': '\u25b3', - 'xvee;': '\u22c1', - 'xwedge;': '\u22c0', - 'Yacute': '\xdd', - 'yacute': '\xfd', - 'Yacute;': '\xdd', - 'yacute;': '\xfd', - 'YAcy;': '\u042f', - 'yacy;': '\u044f', - 'Ycirc;': '\u0176', - 'ycirc;': '\u0177', - 'Ycy;': '\u042b', - 'ycy;': '\u044b', - 'yen': '\xa5', - 'yen;': '\xa5', - 'Yfr;': '\U0001d51c', - 'yfr;': '\U0001d536', - 'YIcy;': '\u0407', - 'yicy;': '\u0457', - 'Yopf;': '\U0001d550', - 'yopf;': '\U0001d56a', - 'Yscr;': '\U0001d4b4', - 'yscr;': '\U0001d4ce', - 'YUcy;': '\u042e', - 'yucy;': '\u044e', - 'yuml': '\xff', - 'Yuml;': '\u0178', - 'yuml;': '\xff', - 'Zacute;': '\u0179', - 'zacute;': '\u017a', - 'Zcaron;': '\u017d', - 'zcaron;': '\u017e', - 'Zcy;': '\u0417', - 'zcy;': '\u0437', - 'Zdot;': '\u017b', - 'zdot;': '\u017c', - 'zeetrf;': '\u2128', - 'ZeroWidthSpace;': '\u200b', - 'Zeta;': '\u0396', - 'zeta;': '\u03b6', - 'Zfr;': '\u2128', - 'zfr;': '\U0001d537', - 'ZHcy;': '\u0416', - 'zhcy;': '\u0436', - 'zigrarr;': '\u21dd', - 'Zopf;': '\u2124', - 'zopf;': '\U0001d56b', - 'Zscr;': '\U0001d4b5', - 'zscr;': '\U0001d4cf', - 'zwj;': '\u200d', - 'zwnj;': '\u200c', -} - -# maps the Unicode codepoint to the HTML entity name -codepoint2name = {} - -# maps the HTML entity name to the character -# (or a character reference if the character is outside the Latin-1 range) -entitydefs = {} - -for (name, codepoint) in name2codepoint.items(): - codepoint2name[codepoint] = name - entitydefs[name] = chr(codepoint) - -del name, codepoint diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/parser.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/parser.py deleted file mode 100644 index fb652636..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/html/parser.py +++ /dev/null @@ -1,536 +0,0 @@ -"""A parser for HTML and XHTML. - -Backported for python-future from Python 3.3. -""" - -# This file is based on sgmllib.py, but the API is slightly different. - -# XXX There should be a way to distinguish between PCDATA (parsed -# character data -- the normal case), RCDATA (replaceable character -# data -- only char and entity references and end tags are special) -# and CDATA (character data -- only end tags are special). - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future.builtins import * -from future.backports import _markupbase -import re -import warnings - -# Regular expressions used for parsing - -interesting_normal = re.compile('[&<]') -incomplete = re.compile('&[a-zA-Z#]') - -entityref = re.compile('&([a-zA-Z][-.a-zA-Z0-9]*)[^a-zA-Z0-9]') -charref = re.compile('&#(?:[0-9]+|[xX][0-9a-fA-F]+)[^0-9a-fA-F]') - -starttagopen = re.compile('<[a-zA-Z]') -piclose = re.compile('>') -commentclose = re.compile(r'--\s*>') -tagfind = re.compile('([a-zA-Z][-.a-zA-Z0-9:_]*)(?:\s|/(?!>))*') -# see http://www.w3.org/TR/html5/tokenization.html#tag-open-state -# and http://www.w3.org/TR/html5/tokenization.html#tag-name-state -tagfind_tolerant = re.compile('[a-zA-Z][^\t\n\r\f />\x00]*') -# Note: -# 1) the strict attrfind isn't really strict, but we can't make it -# correctly strict without breaking backward compatibility; -# 2) if you change attrfind remember to update locatestarttagend too; -# 3) if you change attrfind and/or locatestarttagend the parser will -# explode, so don't do it. -attrfind = re.compile( - r'\s*([a-zA-Z_][-.:a-zA-Z_0-9]*)(\s*=\s*' - r'(\'[^\']*\'|"[^"]*"|[^\s"\'=<>`]*))?') -attrfind_tolerant = re.compile( - r'((?<=[\'"\s/])[^\s/>][^\s/=>]*)(\s*=+\s*' - r'(\'[^\']*\'|"[^"]*"|(?![\'"])[^>\s]*))?(?:\s|/(?!>))*') -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) -locatestarttagend_tolerant = re.compile(r""" - <[a-zA-Z][-.a-zA-Z0-9:_]* # tag name - (?:[\s/]* # optional whitespace before attribute name - (?:(?<=['"\s/])[^\s/>][^\s/=>]* # attribute name - (?:\s*=+\s* # value indicator - (?:'[^']*' # LITA-enclosed value - |"[^"]*" # LIT-enclosed value - |(?!['"])[^>\s]* # bare value - ) - (?:\s*,)* # possibly followed by a comma - )?(?:\s|/(?!>))* - )* - )? - \s* # trailing whitespace -""", re.VERBOSE) -endendtag = re.compile('>') -# the HTML 5 spec, section 8.1.2.2, doesn't allow spaces between -# ') - - -class HTMLParseError(Exception): - """Exception raised for all parse errors.""" - - def __init__(self, msg, position=(None, None)): - assert msg - self.msg = msg - self.lineno = position[0] - self.offset = position[1] - - def __str__(self): - result = self.msg - if self.lineno is not None: - result = result + ", at line %d" % self.lineno - if self.offset is not None: - result = result + ", column %d" % (self.offset + 1) - return result - - -class HTMLParser(_markupbase.ParserBase): - """Find tags and other markup and call handler functions. - - Usage: - p = HTMLParser() - p.feed(data) - ... - p.close() - - Start tags are handled by calling self.handle_starttag() or - self.handle_startendtag(); end tags by self.handle_endtag(). The - data between tags is passed from the parser to the derived class - by calling self.handle_data() with the data as argument (the data - may be split up in arbitrary chunks). Entity references are - passed by calling self.handle_entityref() with the entity - reference as the argument. Numeric character references are - passed to self.handle_charref() with the string containing the - reference as the argument. - """ - - CDATA_CONTENT_ELEMENTS = ("script", "style") - - def __init__(self, strict=False): - """Initialize and reset this instance. - - If strict is set to False (the default) the parser will parse invalid - markup, otherwise it will raise an error. Note that the strict mode - is deprecated. - """ - if strict: - warnings.warn("The strict mode is deprecated.", - DeprecationWarning, stacklevel=2) - self.strict = strict - self.reset() - - def reset(self): - """Reset this instance. Loses all unprocessed data.""" - self.rawdata = '' - self.lasttag = '???' - self.interesting = interesting_normal - self.cdata_elem = None - _markupbase.ParserBase.reset(self) - - def feed(self, data): - r"""Feed data to the parser. - - Call this as often as you want, with as little or as much text - as you want (may include '\n'). - """ - self.rawdata = self.rawdata + data - self.goahead(0) - - def close(self): - """Handle any buffered data.""" - self.goahead(1) - - def error(self, message): - raise HTMLParseError(message, self.getpos()) - - __starttag_text = None - - def get_starttag_text(self): - """Return full source of start tag: '<...>'.""" - return self.__starttag_text - - def set_cdata_mode(self, elem): - self.cdata_elem = elem.lower() - self.interesting = re.compile(r'' % self.cdata_elem, re.I) - - def clear_cdata_mode(self): - self.interesting = interesting_normal - self.cdata_elem = None - - # Internal -- handle data as far as reasonable. May leave state - # and data to be processed by a subsequent call. If 'end' is - # true, force handling all data as if followed by EOF marker. - def goahead(self, end): - rawdata = self.rawdata - i = 0 - n = len(rawdata) - while i < n: - match = self.interesting.search(rawdata, i) # < or & - if match: - j = match.start() - else: - if self.cdata_elem: - break - j = n - if i < j: self.handle_data(rawdata[i:j]) - i = self.updatepos(i, j) - if i == n: break - startswith = rawdata.startswith - if startswith('<', i): - if starttagopen.match(rawdata, i): # < + letter - k = self.parse_starttag(i) - elif startswith("', i + 1) - if k < 0: - k = rawdata.find('<', i + 1) - if k < 0: - k = i + 1 - else: - k += 1 - self.handle_data(rawdata[i:k]) - i = self.updatepos(i, k) - elif startswith("&#", i): - match = charref.match(rawdata, i) - if match: - name = match.group()[2:-1] - self.handle_charref(name) - k = match.end() - if not startswith(';', k-1): - k = k - 1 - i = self.updatepos(i, k) - continue - else: - if ";" in rawdata[i:]: #bail by consuming &# - self.handle_data(rawdata[0:2]) - i = self.updatepos(i, 2) - break - elif startswith('&', i): - match = entityref.match(rawdata, i) - if match: - name = match.group(1) - self.handle_entityref(name) - k = match.end() - if not startswith(';', k-1): - k = k - 1 - i = self.updatepos(i, k) - continue - match = incomplete.match(rawdata, i) - if match: - # match.group() will contain at least 2 chars - if end and match.group() == rawdata[i:]: - if self.strict: - self.error("EOF in middle of entity or char ref") - else: - if k <= i: - k = n - i = self.updatepos(i, i + 1) - # incomplete - break - elif (i + 1) < n: - # not the end of the buffer, and can't be confused - # with some other construct - self.handle_data("&") - i = self.updatepos(i, i + 1) - else: - break - else: - assert 0, "interesting.search() lied" - # end while - if end and i < n and not self.cdata_elem: - self.handle_data(rawdata[i:n]) - i = self.updatepos(i, n) - self.rawdata = rawdata[i:] - - # Internal -- parse html declarations, return length or -1 if not terminated - # See w3.org/TR/html5/tokenization.html#markup-declaration-open-state - # See also parse_declaration in _markupbase - def parse_html_declaration(self, i): - rawdata = self.rawdata - assert rawdata[i:i+2] == ' - gtpos = rawdata.find('>', i+9) - if gtpos == -1: - return -1 - self.handle_decl(rawdata[i+2:gtpos]) - return gtpos+1 - else: - return self.parse_bogus_comment(i) - - # Internal -- parse bogus comment, return length or -1 if not terminated - # see http://www.w3.org/TR/html5/tokenization.html#bogus-comment-state - def parse_bogus_comment(self, i, report=1): - rawdata = self.rawdata - assert rawdata[i:i+2] in ('', i+2) - if pos == -1: - return -1 - if report: - self.handle_comment(rawdata[i+2:pos]) - return pos + 1 - - # Internal -- parse processing instr, return end or -1 if not terminated - def parse_pi(self, i): - rawdata = self.rawdata - assert rawdata[i:i+2] == ' - if not match: - return -1 - j = match.start() - self.handle_pi(rawdata[i+2: j]) - j = match.end() - return j - - # Internal -- handle starttag, return end or -1 if not terminated - 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 = match.group(1).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 - - # Internal -- check to see if we have a complete starttag; return end - # or -1 if incomplete. - def check_for_whole_start_tag(self, i): - rawdata = self.rawdata - if self.strict: - m = locatestarttagend.match(rawdata, i) - else: - m = locatestarttagend_tolerant.match(rawdata, i) - if m: - j = m.end() - next = rawdata[j:j+1] - if next == ">": - return j + 1 - if next == "/": - if rawdata.startswith("/>", j): - return j + 2 - if rawdata.startswith("/", j): - # buffer boundary - return -1 - # else bogus input - if self.strict: - self.updatepos(i, j + 1) - self.error("malformed empty start tag") - if j > i: - return j - else: - return i + 1 - if next == "": - # end of input - return -1 - if next in ("abcdefghijklmnopqrstuvwxyz=/" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"): - # end of input in or before attribute value, or we have the - # '/' from a '/>' ending - return -1 - if self.strict: - self.updatepos(i, j) - self.error("malformed start tag") - if j > i: - return j - else: - return i + 1 - raise AssertionError("we should not get here!") - - # Internal -- parse endtag, return end or -1 if incomplete - def parse_endtag(self, i): - rawdata = self.rawdata - assert rawdata[i:i+2] == " - if not match: - return -1 - gtpos = match.end() - match = endtagfind.match(rawdata, i) # - if not match: - if self.cdata_elem is not None: - self.handle_data(rawdata[i:gtpos]) - return gtpos - if self.strict: - self.error("bad end tag: %r" % (rawdata[i:gtpos],)) - # find the name: w3.org/TR/html5/tokenization.html#tag-name-state - namematch = tagfind_tolerant.match(rawdata, i+2) - if not namematch: - # w3.org/TR/html5/tokenization.html#end-tag-open-state - if rawdata[i:i+3] == '': - return i+3 - else: - return self.parse_bogus_comment(i) - tagname = namematch.group().lower() - # consume and ignore other stuff between the name and the > - # Note: this is not 100% correct, since we might have things like - # , but looking for > after tha name should cover - # most of the cases and is much simpler - gtpos = rawdata.find('>', namematch.end()) - self.handle_endtag(tagname) - return gtpos+1 - - elem = match.group(1).lower() # script or style - if self.cdata_elem is not None: - if elem != self.cdata_elem: - self.handle_data(rawdata[i:gtpos]) - return gtpos - - self.handle_endtag(elem.lower()) - self.clear_cdata_mode() - return gtpos - - # Overridable -- finish processing of start+end tag: - def handle_startendtag(self, tag, attrs): - self.handle_starttag(tag, attrs) - self.handle_endtag(tag) - - # Overridable -- handle start tag - def handle_starttag(self, tag, attrs): - pass - - # Overridable -- handle end tag - def handle_endtag(self, tag): - pass - - # Overridable -- handle character reference - def handle_charref(self, name): - pass - - # Overridable -- handle entity reference - def handle_entityref(self, name): - pass - - # Overridable -- handle data - def handle_data(self, data): - pass - - # Overridable -- handle comment - def handle_comment(self, data): - pass - - # Overridable -- handle declaration - def handle_decl(self, decl): - pass - - # Overridable -- handle processing instruction - def handle_pi(self, data): - pass - - def unknown_decl(self, data): - if self.strict: - self.error("unknown declaration: %r" % (data,)) - - # Internal -- helper to remove special character quoting - def unescape(self, s): - if '&' not in s: - return s - def replaceEntities(s): - s = s.groups()[0] - try: - if s[0] == "#": - s = s[1:] - if s[0] in ['x','X']: - c = int(s[1:].rstrip(';'), 16) - else: - c = int(s.rstrip(';')) - return chr(c) - except ValueError: - return '&#' + s - else: - from future.backports.html.entities import html5 - if s in html5: - return html5[s] - elif s.endswith(';'): - return '&' + s - for x in range(2, len(s)): - if s[:x] in html5: - return html5[s[:x]] + s[x:] - else: - return '&' + s - - return re.sub(r"&(#?[xX]?(?:[0-9a-fA-F]+;|\w{1,32};?))", - replaceEntities, s) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index bc03fa9a633fadb2931493b817ecbd3725d66435..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202 zcmYk0K?=e!6hu>WA%X`Hw144B5K&N21QEKHkTzcvY@3k$7J4MF;8EOq1b41XRyr_m zm|2Y&jfO0!H+h_6z9RmWgt7|u{Q$`_HcpOr=HmRr$3TTr3)l%KH%_+*r%sWnAuE(I zCF~E|HTQ)R79FH5)-9Ae2iuV{^(>u(*Mw6mikrBihf70^L&!t7#*qg_WPx*CYvMe$ R-E*+0jBH9=#v|`w_5~~?HSz!e diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__pycache__/client.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__pycache__/client.cpython-39.pyc deleted file mode 100644 index b182755753b07a96c3549546f3c194157435df98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30769 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__pycache__/cookiejar.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__pycache__/cookiejar.cpython-39.pyc deleted file mode 100644 index 2d50d2ebe92455850d96eec294a7bb2b7aa5f20b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53872 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__pycache__/server.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/__pycache__/server.cpython-39.pyc deleted file mode 100644 index b3b806ed903e96fdc7abf612721b83525f2662f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34465 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/client.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/client.py deleted file mode 100644 index e663d125..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/client.py +++ /dev/null @@ -1,1346 +0,0 @@ -"""HTTP/1.1 client library - -A backport of the Python 3.3 http/client.py module for python-future. - - - - -HTTPConnection goes through a number of "states", which define when a client -may legally make another request or fetch the response for a particular -request. This diagram details these state transitions: - - (null) - | - | HTTPConnection() - v - Idle - | - | putrequest() - v - Request-started - | - | ( putheader() )* endheaders() - v - Request-sent - | - | response = getresponse() - v - Unread-response [Response-headers-read] - |\____________________ - | | - | response.read() | putrequest() - v v - Idle Req-started-unread-response - ______/| - / | - response.read() | | ( putheader() )* endheaders() - v v - Request-started Req-sent-unread-response - | - | response.read() - v - Request-sent - -This diagram presents the following rules: - -- a second request may not be started until {response-headers-read} - -- a response [object] cannot be retrieved until {request-sent} - -- there is no differentiation between an unread response body and a - partially read response body - -Note: this enforcement is applied by the HTTPConnection class. The - HTTPResponse class does not enforce this state machine, which - implies sophisticated clients may accelerate the request/response - pipeline. Caution should be taken, though: accelerating the states - beyond the above pattern may imply knowledge of the server's - connection-close behavior for certain requests. For example, it - is impossible to tell whether the server will close the connection - UNTIL the response headers have been read; this means that further - requests cannot be placed into the pipeline until it is known that - the server will NOT be closing the connection. - -Logical State __state __response -------------- ------- ---------- -Idle _CS_IDLE None -Request-started _CS_REQ_STARTED None -Request-sent _CS_REQ_SENT None -Unread-response _CS_IDLE -Req-started-unread-response _CS_REQ_STARTED -Req-sent-unread-response _CS_REQ_SENT -""" - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future.builtins import bytes, int, str, super -from future.utils import PY2 - -from future.backports.email import parser as email_parser -from future.backports.email import message as email_message -from future.backports.misc import create_connection as socket_create_connection -import io -import os -import socket -from future.backports.urllib.parse import urlsplit -import warnings -from array import array - -if PY2: - from collections import Iterable -else: - from collections.abc import Iterable - -__all__ = ["HTTPResponse", "HTTPConnection", - "HTTPException", "NotConnected", "UnknownProtocol", - "UnknownTransferEncoding", "UnimplementedFileMode", - "IncompleteRead", "InvalidURL", "ImproperConnectionState", - "CannotSendRequest", "CannotSendHeader", "ResponseNotReady", - "BadStatusLine", "error", "responses"] - -HTTP_PORT = 80 -HTTPS_PORT = 443 - -_UNKNOWN = 'UNKNOWN' - -# connection states -_CS_IDLE = 'Idle' -_CS_REQ_STARTED = 'Request-started' -_CS_REQ_SENT = 'Request-sent' - -# status codes -# informational -CONTINUE = 100 -SWITCHING_PROTOCOLS = 101 -PROCESSING = 102 - -# successful -OK = 200 -CREATED = 201 -ACCEPTED = 202 -NON_AUTHORITATIVE_INFORMATION = 203 -NO_CONTENT = 204 -RESET_CONTENT = 205 -PARTIAL_CONTENT = 206 -MULTI_STATUS = 207 -IM_USED = 226 - -# redirection -MULTIPLE_CHOICES = 300 -MOVED_PERMANENTLY = 301 -FOUND = 302 -SEE_OTHER = 303 -NOT_MODIFIED = 304 -USE_PROXY = 305 -TEMPORARY_REDIRECT = 307 - -# client error -BAD_REQUEST = 400 -UNAUTHORIZED = 401 -PAYMENT_REQUIRED = 402 -FORBIDDEN = 403 -NOT_FOUND = 404 -METHOD_NOT_ALLOWED = 405 -NOT_ACCEPTABLE = 406 -PROXY_AUTHENTICATION_REQUIRED = 407 -REQUEST_TIMEOUT = 408 -CONFLICT = 409 -GONE = 410 -LENGTH_REQUIRED = 411 -PRECONDITION_FAILED = 412 -REQUEST_ENTITY_TOO_LARGE = 413 -REQUEST_URI_TOO_LONG = 414 -UNSUPPORTED_MEDIA_TYPE = 415 -REQUESTED_RANGE_NOT_SATISFIABLE = 416 -EXPECTATION_FAILED = 417 -UNPROCESSABLE_ENTITY = 422 -LOCKED = 423 -FAILED_DEPENDENCY = 424 -UPGRADE_REQUIRED = 426 -PRECONDITION_REQUIRED = 428 -TOO_MANY_REQUESTS = 429 -REQUEST_HEADER_FIELDS_TOO_LARGE = 431 - -# server error -INTERNAL_SERVER_ERROR = 500 -NOT_IMPLEMENTED = 501 -BAD_GATEWAY = 502 -SERVICE_UNAVAILABLE = 503 -GATEWAY_TIMEOUT = 504 -HTTP_VERSION_NOT_SUPPORTED = 505 -INSUFFICIENT_STORAGE = 507 -NOT_EXTENDED = 510 -NETWORK_AUTHENTICATION_REQUIRED = 511 - -# Mapping status codes to official W3C names -responses = { - 100: 'Continue', - 101: 'Switching Protocols', - - 200: 'OK', - 201: 'Created', - 202: 'Accepted', - 203: 'Non-Authoritative Information', - 204: 'No Content', - 205: 'Reset Content', - 206: 'Partial Content', - - 300: 'Multiple Choices', - 301: 'Moved Permanently', - 302: 'Found', - 303: 'See Other', - 304: 'Not Modified', - 305: 'Use Proxy', - 306: '(Unused)', - 307: 'Temporary Redirect', - - 400: 'Bad Request', - 401: 'Unauthorized', - 402: 'Payment Required', - 403: 'Forbidden', - 404: 'Not Found', - 405: 'Method Not Allowed', - 406: 'Not Acceptable', - 407: 'Proxy Authentication Required', - 408: 'Request Timeout', - 409: 'Conflict', - 410: 'Gone', - 411: 'Length Required', - 412: 'Precondition Failed', - 413: 'Request Entity Too Large', - 414: 'Request-URI Too Long', - 415: 'Unsupported Media Type', - 416: 'Requested Range Not Satisfiable', - 417: 'Expectation Failed', - 428: 'Precondition Required', - 429: 'Too Many Requests', - 431: 'Request Header Fields Too Large', - - 500: 'Internal Server Error', - 501: 'Not Implemented', - 502: 'Bad Gateway', - 503: 'Service Unavailable', - 504: 'Gateway Timeout', - 505: 'HTTP Version Not Supported', - 511: 'Network Authentication Required', -} - -# maximal amount of data to read at one time in _safe_read -MAXAMOUNT = 1048576 - -# maximal line length when calling readline(). -_MAXLINE = 65536 -_MAXHEADERS = 100 - - -class HTTPMessage(email_message.Message): - # XXX The only usage of this method is in - # http.server.CGIHTTPRequestHandler. Maybe move the code there so - # that it doesn't need to be part of the public API. The API has - # never been defined so this could cause backwards compatibility - # issues. - - def getallmatchingheaders(self, name): - """Find all header lines matching a given header name. - - Look through the list of headers and find all lines matching a given - header name (and their continuation lines). A list of the lines is - returned, without interpretation. If the header does not occur, an - empty list is returned. If the header occurs multiple times, all - occurrences are returned. Case is not important in the header name. - - """ - name = name.lower() + ':' - n = len(name) - lst = [] - hit = 0 - for line in self.keys(): - if line[:n].lower() == name: - hit = 1 - elif not line[:1].isspace(): - hit = 0 - if hit: - lst.append(line) - return lst - -def parse_headers(fp, _class=HTTPMessage): - """Parses only RFC2822 headers from a file pointer. - - email Parser wants to see strings rather than bytes. - But a TextIOWrapper around self.rfile would buffer too many bytes - from the stream, bytes which we later need to read as bytes. - So we read the correct bytes here, as bytes, for email Parser - to parse. - - """ - headers = [] - while True: - line = fp.readline(_MAXLINE + 1) - if len(line) > _MAXLINE: - raise LineTooLong("header line") - headers.append(line) - if len(headers) > _MAXHEADERS: - raise HTTPException("got more than %d headers" % _MAXHEADERS) - if line in (b'\r\n', b'\n', b''): - break - hstring = bytes(b'').join(headers).decode('iso-8859-1') - return email_parser.Parser(_class=_class).parsestr(hstring) - - -_strict_sentinel = object() - -class HTTPResponse(io.RawIOBase): - - # See RFC 2616 sec 19.6 and RFC 1945 sec 6 for details. - - # The bytes from the socket object are iso-8859-1 strings. - # See RFC 2616 sec 2.2 which notes an exception for MIME-encoded - # text following RFC 2047. The basic status line parsing only - # accepts iso-8859-1. - - def __init__(self, sock, debuglevel=0, strict=_strict_sentinel, method=None, url=None): - # If the response includes a content-length header, we need to - # make sure that the client doesn't read more than the - # specified number of bytes. If it does, it will block until - # the server times out and closes the connection. This will - # happen if a self.fp.read() is done (without a size) whether - # self.fp is buffered or not. So, no self.fp.read() by - # clients unless they know what they are doing. - self.fp = sock.makefile("rb") - self.debuglevel = debuglevel - if strict is not _strict_sentinel: - warnings.warn("the 'strict' argument isn't supported anymore; " - "http.client now always assumes HTTP/1.x compliant servers.", - DeprecationWarning, 2) - self._method = method - - # The HTTPResponse object is returned via urllib. The clients - # of http and urllib expect different attributes for the - # headers. headers is used here and supports urllib. msg is - # provided as a backwards compatibility layer for http - # clients. - - self.headers = self.msg = None - - # from the Status-Line of the response - self.version = _UNKNOWN # HTTP-Version - self.status = _UNKNOWN # Status-Code - self.reason = _UNKNOWN # Reason-Phrase - - self.chunked = _UNKNOWN # is "chunked" being used? - self.chunk_left = _UNKNOWN # bytes left to read in current chunk - self.length = _UNKNOWN # number of bytes left in response - self.will_close = _UNKNOWN # conn will close at end of response - - def _read_status(self): - line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") - if len(line) > _MAXLINE: - raise LineTooLong("status line") - if self.debuglevel > 0: - print("reply:", repr(line)) - if not line: - # Presumably, the server closed the connection before - # sending a valid response. - raise BadStatusLine(line) - try: - version, status, reason = line.split(None, 2) - except ValueError: - try: - version, status = line.split(None, 1) - reason = "" - except ValueError: - # empty version will cause next test to fail. - version = "" - if not version.startswith("HTTP/"): - self._close_conn() - raise BadStatusLine(line) - - # The status code is a three-digit number - try: - status = int(status) - if status < 100 or status > 999: - raise BadStatusLine(line) - except ValueError: - raise BadStatusLine(line) - return version, status, reason - - def begin(self): - if self.headers is not None: - # we've already started reading the response - return - - # read until we get a non-100 response - while True: - version, status, reason = self._read_status() - if status != CONTINUE: - break - # skip the header from the 100 response - while True: - skip = self.fp.readline(_MAXLINE + 1) - if len(skip) > _MAXLINE: - raise LineTooLong("header line") - skip = skip.strip() - if not skip: - break - if self.debuglevel > 0: - print("header:", skip) - - self.code = self.status = status - self.reason = reason.strip() - if version in ("HTTP/1.0", "HTTP/0.9"): - # Some servers might still return "0.9", treat it as 1.0 anyway - self.version = 10 - elif version.startswith("HTTP/1."): - self.version = 11 # use HTTP/1.1 code for HTTP/1.x where x>=1 - else: - raise UnknownProtocol(version) - - self.headers = self.msg = parse_headers(self.fp) - - if self.debuglevel > 0: - for hdr in self.headers: - print("header:", hdr, end=" ") - - # are we using the chunked-style of transfer encoding? - tr_enc = self.headers.get("transfer-encoding") - if tr_enc and tr_enc.lower() == "chunked": - self.chunked = True - self.chunk_left = None - else: - self.chunked = False - - # will the connection close at the end of the response? - self.will_close = self._check_close() - - # do we have a Content-Length? - # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked" - self.length = None - length = self.headers.get("content-length") - - # are we using the chunked-style of transfer encoding? - tr_enc = self.headers.get("transfer-encoding") - if length and not self.chunked: - try: - self.length = int(length) - except ValueError: - self.length = None - else: - if self.length < 0: # ignore nonsensical negative lengths - self.length = None - else: - self.length = None - - # does the body have a fixed length? (of zero) - if (status == NO_CONTENT or status == NOT_MODIFIED or - 100 <= status < 200 or # 1xx codes - self._method == "HEAD"): - self.length = 0 - - # if the connection remains open, and we aren't using chunked, and - # a content-length was not provided, then assume that the connection - # WILL close. - if (not self.will_close and - not self.chunked and - self.length is None): - self.will_close = True - - def _check_close(self): - conn = self.headers.get("connection") - if self.version == 11: - # An HTTP/1.1 proxy is assumed to stay open unless - # explicitly closed. - conn = self.headers.get("connection") - if conn and "close" in conn.lower(): - return True - return False - - # Some HTTP/1.0 implementations have support for persistent - # connections, using rules different than HTTP/1.1. - - # For older HTTP, Keep-Alive indicates persistent connection. - if self.headers.get("keep-alive"): - return False - - # At least Akamai returns a "Connection: Keep-Alive" header, - # which was supposed to be sent by the client. - if conn and "keep-alive" in conn.lower(): - return False - - # Proxy-Connection is a netscape hack. - pconn = self.headers.get("proxy-connection") - if pconn and "keep-alive" in pconn.lower(): - return False - - # otherwise, assume it will close - return True - - def _close_conn(self): - fp = self.fp - self.fp = None - fp.close() - - def close(self): - super().close() # set "closed" flag - if self.fp: - self._close_conn() - - # These implementations are for the benefit of io.BufferedReader. - - # XXX This class should probably be revised to act more like - # the "raw stream" that BufferedReader expects. - - def flush(self): - super().flush() - if self.fp: - self.fp.flush() - - def readable(self): - return True - - # End of "raw stream" methods - - def isclosed(self): - """True if the connection is closed.""" - # NOTE: it is possible that we will not ever call self.close(). This - # case occurs when will_close is TRUE, length is None, and we - # read up to the last byte, but NOT past it. - # - # IMPLIES: if will_close is FALSE, then self.close() will ALWAYS be - # called, meaning self.isclosed() is meaningful. - return self.fp is None - - def read(self, amt=None): - if self.fp is None: - return bytes(b"") - - if self._method == "HEAD": - self._close_conn() - return bytes(b"") - - if amt is not None: - # Amount is given, so call base class version - # (which is implemented in terms of self.readinto) - return bytes(super(HTTPResponse, self).read(amt)) - else: - # Amount is not given (unbounded read) so we must check self.length - # and self.chunked - - if self.chunked: - return self._readall_chunked() - - if self.length is None: - s = self.fp.read() - else: - try: - s = self._safe_read(self.length) - except IncompleteRead: - self._close_conn() - raise - self.length = 0 - self._close_conn() # we read everything - return bytes(s) - - def readinto(self, b): - if self.fp is None: - return 0 - - if self._method == "HEAD": - self._close_conn() - return 0 - - if self.chunked: - return self._readinto_chunked(b) - - if self.length is not None: - if len(b) > self.length: - # clip the read to the "end of response" - b = memoryview(b)[0:self.length] - - # we do not use _safe_read() here because this may be a .will_close - # connection, and the user is reading more bytes than will be provided - # (for example, reading in 1k chunks) - - if PY2: - data = self.fp.read(len(b)) - n = len(data) - b[:n] = data - else: - n = self.fp.readinto(b) - - if not n and b: - # Ideally, we would raise IncompleteRead if the content-length - # wasn't satisfied, but it might break compatibility. - self._close_conn() - elif self.length is not None: - self.length -= n - if not self.length: - self._close_conn() - return n - - def _read_next_chunk_size(self): - # Read the next chunk size from the file - line = self.fp.readline(_MAXLINE + 1) - if len(line) > _MAXLINE: - raise LineTooLong("chunk size") - i = line.find(b";") - if i >= 0: - line = line[:i] # strip chunk-extensions - try: - return int(line, 16) - except ValueError: - # close the connection as protocol synchronisation is - # probably lost - self._close_conn() - raise - - def _read_and_discard_trailer(self): - # read and discard trailer up to the CRLF terminator - ### note: we shouldn't have any trailers! - while True: - line = self.fp.readline(_MAXLINE + 1) - if len(line) > _MAXLINE: - raise LineTooLong("trailer line") - if not line: - # a vanishingly small number of sites EOF without - # sending the trailer - break - if line in (b'\r\n', b'\n', b''): - break - - def _readall_chunked(self): - assert self.chunked != _UNKNOWN - chunk_left = self.chunk_left - value = [] - while True: - if chunk_left is None: - try: - chunk_left = self._read_next_chunk_size() - if chunk_left == 0: - break - except ValueError: - raise IncompleteRead(bytes(b'').join(value)) - value.append(self._safe_read(chunk_left)) - - # we read the whole chunk, get another - self._safe_read(2) # toss the CRLF at the end of the chunk - chunk_left = None - - self._read_and_discard_trailer() - - # we read everything; close the "file" - self._close_conn() - - return bytes(b'').join(value) - - def _readinto_chunked(self, b): - assert self.chunked != _UNKNOWN - chunk_left = self.chunk_left - - total_bytes = 0 - mvb = memoryview(b) - while True: - if chunk_left is None: - try: - chunk_left = self._read_next_chunk_size() - if chunk_left == 0: - break - except ValueError: - raise IncompleteRead(bytes(b[0:total_bytes])) - - if len(mvb) < chunk_left: - n = self._safe_readinto(mvb) - self.chunk_left = chunk_left - n - return total_bytes + n - elif len(mvb) == chunk_left: - n = self._safe_readinto(mvb) - self._safe_read(2) # toss the CRLF at the end of the chunk - self.chunk_left = None - return total_bytes + n - else: - temp_mvb = mvb[0:chunk_left] - n = self._safe_readinto(temp_mvb) - mvb = mvb[n:] - total_bytes += n - - # we read the whole chunk, get another - self._safe_read(2) # toss the CRLF at the end of the chunk - chunk_left = None - - self._read_and_discard_trailer() - - # we read everything; close the "file" - self._close_conn() - - return total_bytes - - def _safe_read(self, amt): - """Read the number of bytes requested, compensating for partial reads. - - Normally, we have a blocking socket, but a read() can be interrupted - by a signal (resulting in a partial read). - - Note that we cannot distinguish between EOF and an interrupt when zero - bytes have been read. IncompleteRead() will be raised in this - situation. - - This function should be used when bytes "should" be present for - reading. If the bytes are truly not available (due to EOF), then the - IncompleteRead exception can be used to detect the problem. - """ - s = [] - while amt > 0: - chunk = self.fp.read(min(amt, MAXAMOUNT)) - if not chunk: - raise IncompleteRead(bytes(b'').join(s), amt) - s.append(chunk) - amt -= len(chunk) - return bytes(b"").join(s) - - def _safe_readinto(self, b): - """Same as _safe_read, but for reading into a buffer.""" - total_bytes = 0 - mvb = memoryview(b) - while total_bytes < len(b): - if MAXAMOUNT < len(mvb): - temp_mvb = mvb[0:MAXAMOUNT] - if PY2: - data = self.fp.read(len(temp_mvb)) - n = len(data) - temp_mvb[:n] = data - else: - n = self.fp.readinto(temp_mvb) - else: - if PY2: - data = self.fp.read(len(mvb)) - n = len(data) - mvb[:n] = data - else: - n = self.fp.readinto(mvb) - if not n: - raise IncompleteRead(bytes(mvb[0:total_bytes]), len(b)) - mvb = mvb[n:] - total_bytes += n - return total_bytes - - def fileno(self): - return self.fp.fileno() - - def getheader(self, name, default=None): - if self.headers is None: - raise ResponseNotReady() - headers = self.headers.get_all(name) or default - if isinstance(headers, str) or not hasattr(headers, '__iter__'): - return headers - else: - return ', '.join(headers) - - def getheaders(self): - """Return list of (header, value) tuples.""" - if self.headers is None: - raise ResponseNotReady() - return list(self.headers.items()) - - # We override IOBase.__iter__ so that it doesn't check for closed-ness - - def __iter__(self): - return self - - # For compatibility with old-style urllib responses. - - def info(self): - return self.headers - - def geturl(self): - return self.url - - def getcode(self): - return self.status - -class HTTPConnection(object): - - _http_vsn = 11 - _http_vsn_str = 'HTTP/1.1' - - response_class = HTTPResponse - default_port = HTTP_PORT - auto_open = 1 - debuglevel = 0 - - def __init__(self, host, port=None, strict=_strict_sentinel, - timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None): - if strict is not _strict_sentinel: - warnings.warn("the 'strict' argument isn't supported anymore; " - "http.client now always assumes HTTP/1.x compliant servers.", - DeprecationWarning, 2) - self.timeout = timeout - self.source_address = source_address - self.sock = None - self._buffer = [] - self.__response = None - self.__state = _CS_IDLE - self._method = None - self._tunnel_host = None - self._tunnel_port = None - self._tunnel_headers = {} - - self._set_hostport(host, port) - - def set_tunnel(self, host, port=None, headers=None): - """ Sets up the host and the port for the HTTP CONNECT Tunnelling. - - The headers argument should be a mapping of extra HTTP headers - to send with the CONNECT request. - """ - self._tunnel_host = host - self._tunnel_port = port - if headers: - self._tunnel_headers = headers - else: - self._tunnel_headers.clear() - - def _set_hostport(self, host, port): - if port is None: - i = host.rfind(':') - j = host.rfind(']') # ipv6 addresses have [...] - if i > j: - try: - port = int(host[i+1:]) - except ValueError: - if host[i+1:] == "": # http://foo.com:/ == http://foo.com/ - port = self.default_port - else: - raise InvalidURL("nonnumeric port: '%s'" % host[i+1:]) - host = host[:i] - else: - port = self.default_port - if host and host[0] == '[' and host[-1] == ']': - host = host[1:-1] - self.host = host - self.port = port - - def set_debuglevel(self, level): - self.debuglevel = level - - def _tunnel(self): - self._set_hostport(self._tunnel_host, self._tunnel_port) - connect_str = "CONNECT %s:%d HTTP/1.0\r\n" % (self.host, self.port) - connect_bytes = connect_str.encode("ascii") - self.send(connect_bytes) - for header, value in self._tunnel_headers.items(): - header_str = "%s: %s\r\n" % (header, value) - header_bytes = header_str.encode("latin-1") - self.send(header_bytes) - self.send(bytes(b'\r\n')) - - response = self.response_class(self.sock, method=self._method) - (version, code, message) = response._read_status() - - if code != 200: - self.close() - raise socket.error("Tunnel connection failed: %d %s" % (code, - message.strip())) - while True: - line = response.fp.readline(_MAXLINE + 1) - if len(line) > _MAXLINE: - raise LineTooLong("header line") - if not line: - # for sites which EOF without sending a trailer - break - if line in (b'\r\n', b'\n', b''): - break - - def connect(self): - """Connect to the host and port specified in __init__.""" - self.sock = socket_create_connection((self.host,self.port), - self.timeout, self.source_address) - if self._tunnel_host: - self._tunnel() - - def close(self): - """Close the connection to the HTTP server.""" - if self.sock: - self.sock.close() # close it manually... there may be other refs - self.sock = None - if self.__response: - self.__response.close() - self.__response = None - self.__state = _CS_IDLE - - def send(self, data): - """Send `data' to the server. - ``data`` can be a string object, a bytes object, an array object, a - file-like object that supports a .read() method, or an iterable object. - """ - - if self.sock is None: - if self.auto_open: - self.connect() - else: - raise NotConnected() - - if self.debuglevel > 0: - print("send:", repr(data)) - blocksize = 8192 - # Python 2.7 array objects have a read method which is incompatible - # with the 2-arg calling syntax below. - if hasattr(data, "read") and not isinstance(data, array): - if self.debuglevel > 0: - print("sendIng a read()able") - encode = False - try: - mode = data.mode - except AttributeError: - # io.BytesIO and other file-like objects don't have a `mode` - # attribute. - pass - else: - if "b" not in mode: - encode = True - if self.debuglevel > 0: - print("encoding file using iso-8859-1") - while 1: - datablock = data.read(blocksize) - if not datablock: - break - if encode: - datablock = datablock.encode("iso-8859-1") - self.sock.sendall(datablock) - return - try: - self.sock.sendall(data) - except TypeError: - if isinstance(data, Iterable): - for d in data: - self.sock.sendall(d) - else: - raise TypeError("data should be a bytes-like object " - "or an iterable, got %r" % type(data)) - - def _output(self, s): - """Add a line of output to the current request buffer. - - Assumes that the line does *not* end with \\r\\n. - """ - self._buffer.append(s) - - def _send_output(self, message_body=None): - """Send the currently buffered request and clear the buffer. - - Appends an extra \\r\\n to the buffer. - A message_body may be specified, to be appended to the request. - """ - self._buffer.extend((bytes(b""), bytes(b""))) - msg = bytes(b"\r\n").join(self._buffer) - del self._buffer[:] - # If msg and message_body are sent in a single send() call, - # it will avoid performance problems caused by the interaction - # between delayed ack and the Nagle algorithm. - if isinstance(message_body, bytes): - msg += message_body - message_body = None - self.send(msg) - if message_body is not None: - # message_body was not a string (i.e. it is a file), and - # we must run the risk of Nagle. - self.send(message_body) - - def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0): - """Send a request to the server. - - `method' specifies an HTTP request method, e.g. 'GET'. - `url' specifies the object being requested, e.g. '/index.html'. - `skip_host' if True does not add automatically a 'Host:' header - `skip_accept_encoding' if True does not add automatically an - 'Accept-Encoding:' header - """ - - # if a prior response has been completed, then forget about it. - if self.__response and self.__response.isclosed(): - self.__response = None - - - # in certain cases, we cannot issue another request on this connection. - # this occurs when: - # 1) we are in the process of sending a request. (_CS_REQ_STARTED) - # 2) a response to a previous request has signalled that it is going - # to close the connection upon completion. - # 3) the headers for the previous response have not been read, thus - # we cannot determine whether point (2) is true. (_CS_REQ_SENT) - # - # if there is no prior response, then we can request at will. - # - # if point (2) is true, then we will have passed the socket to the - # response (effectively meaning, "there is no prior response"), and - # will open a new one when a new request is made. - # - # Note: if a prior response exists, then we *can* start a new request. - # We are not allowed to begin fetching the response to this new - # request, however, until that prior response is complete. - # - if self.__state == _CS_IDLE: - self.__state = _CS_REQ_STARTED - else: - raise CannotSendRequest(self.__state) - - # Save the method we use, we need it later in the response phase - self._method = method - if not url: - url = '/' - request = '%s %s %s' % (method, url, self._http_vsn_str) - - # Non-ASCII characters should have been eliminated earlier - self._output(request.encode('ascii')) - - if self._http_vsn == 11: - # Issue some standard headers for better HTTP/1.1 compliance - - if not skip_host: - # this header is issued *only* for HTTP/1.1 - # connections. more specifically, this means it is - # only issued when the client uses the new - # HTTPConnection() class. backwards-compat clients - # will be using HTTP/1.0 and those clients may be - # issuing this header themselves. we should NOT issue - # it twice; some web servers (such as Apache) barf - # when they see two Host: headers - - # If we need a non-standard port,include it in the - # header. If the request is going through a proxy, - # but the host of the actual URL, not the host of the - # proxy. - - netloc = '' - if url.startswith('http'): - nil, netloc, nil, nil, nil = urlsplit(url) - - if netloc: - try: - netloc_enc = netloc.encode("ascii") - except UnicodeEncodeError: - netloc_enc = netloc.encode("idna") - self.putheader('Host', netloc_enc) - else: - try: - host_enc = self.host.encode("ascii") - except UnicodeEncodeError: - host_enc = self.host.encode("idna") - - # As per RFC 273, IPv6 address should be wrapped with [] - # when used as Host header - - if self.host.find(':') >= 0: - host_enc = bytes(b'[' + host_enc + b']') - - if self.port == self.default_port: - self.putheader('Host', host_enc) - else: - host_enc = host_enc.decode("ascii") - self.putheader('Host', "%s:%s" % (host_enc, self.port)) - - # note: we are assuming that clients will not attempt to set these - # headers since *this* library must deal with the - # consequences. this also means that when the supporting - # libraries are updated to recognize other forms, then this - # code should be changed (removed or updated). - - # we only want a Content-Encoding of "identity" since we don't - # support encodings such as x-gzip or x-deflate. - if not skip_accept_encoding: - self.putheader('Accept-Encoding', 'identity') - - # we can accept "chunked" Transfer-Encodings, but no others - # NOTE: no TE header implies *only* "chunked" - #self.putheader('TE', 'chunked') - - # if TE is supplied in the header, then it must appear in a - # Connection header. - #self.putheader('Connection', 'TE') - - else: - # For HTTP/1.0, the server will assume "not chunked" - pass - - def putheader(self, header, *values): - """Send a request header line to the server. - - For example: h.putheader('Accept', 'text/html') - """ - if self.__state != _CS_REQ_STARTED: - raise CannotSendHeader() - - if hasattr(header, 'encode'): - header = header.encode('ascii') - values = list(values) - for i, one_value in enumerate(values): - if hasattr(one_value, 'encode'): - values[i] = one_value.encode('latin-1') - elif isinstance(one_value, int): - values[i] = str(one_value).encode('ascii') - value = bytes(b'\r\n\t').join(values) - header = header + bytes(b': ') + value - self._output(header) - - def endheaders(self, message_body=None): - """Indicate that the last header line has been sent to the server. - - This method sends the request to the server. The optional message_body - argument can be used to pass a message body associated with the - request. The message body will be sent in the same packet as the - message headers if it is a string, otherwise it is sent as a separate - packet. - """ - if self.__state == _CS_REQ_STARTED: - self.__state = _CS_REQ_SENT - else: - raise CannotSendHeader() - self._send_output(message_body) - - def request(self, method, url, body=None, headers={}): - """Send a complete request to the server.""" - self._send_request(method, url, body, headers) - - def _set_content_length(self, body): - # Set the content-length based on the body. - thelen = None - try: - thelen = str(len(body)) - except TypeError as te: - # If this is a file-like object, try to - # fstat its file descriptor - try: - thelen = str(os.fstat(body.fileno()).st_size) - except (AttributeError, OSError): - # Don't send a length if this failed - if self.debuglevel > 0: print("Cannot stat!!") - - if thelen is not None: - self.putheader('Content-Length', thelen) - - def _send_request(self, method, url, body, headers): - # Honor explicitly requested Host: and Accept-Encoding: headers. - header_names = dict.fromkeys([k.lower() for k in headers]) - skips = {} - if 'host' in header_names: - skips['skip_host'] = 1 - if 'accept-encoding' in header_names: - skips['skip_accept_encoding'] = 1 - - self.putrequest(method, url, **skips) - - if body is not None and ('content-length' not in header_names): - self._set_content_length(body) - for hdr, value in headers.items(): - self.putheader(hdr, value) - if isinstance(body, str): - # RFC 2616 Section 3.7.1 says that text default has a - # default charset of iso-8859-1. - body = body.encode('iso-8859-1') - self.endheaders(body) - - def getresponse(self): - """Get the response from the server. - - If the HTTPConnection is in the correct state, returns an - instance of HTTPResponse or of whatever object is returned by - class the response_class variable. - - If a request has not been sent or if a previous response has - not be handled, ResponseNotReady is raised. If the HTTP - response indicates that the connection should be closed, then - it will be closed before the response is returned. When the - connection is closed, the underlying socket is closed. - """ - - # if a prior response has been completed, then forget about it. - if self.__response and self.__response.isclosed(): - self.__response = None - - # if a prior response exists, then it must be completed (otherwise, we - # cannot read this response's header to determine the connection-close - # behavior) - # - # note: if a prior response existed, but was connection-close, then the - # socket and response were made independent of this HTTPConnection - # object since a new request requires that we open a whole new - # connection - # - # this means the prior response had one of two states: - # 1) will_close: this connection was reset and the prior socket and - # response operate independently - # 2) persistent: the response was retained and we await its - # isclosed() status to become true. - # - if self.__state != _CS_REQ_SENT or self.__response: - raise ResponseNotReady(self.__state) - - if self.debuglevel > 0: - response = self.response_class(self.sock, self.debuglevel, - method=self._method) - else: - response = self.response_class(self.sock, method=self._method) - - response.begin() - assert response.will_close != _UNKNOWN - self.__state = _CS_IDLE - - if response.will_close: - # this effectively passes the connection to the response - self.close() - else: - # remember this, so we can tell when it is complete - self.__response = response - - return response - -try: - import ssl - from ssl import SSLContext -except ImportError: - pass -else: - class HTTPSConnection(HTTPConnection): - "This class allows communication via SSL." - - default_port = HTTPS_PORT - - # XXX Should key_file and cert_file be deprecated in favour of context? - - def __init__(self, host, port=None, key_file=None, cert_file=None, - strict=_strict_sentinel, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, - source_address=None, **_3to2kwargs): - if 'check_hostname' in _3to2kwargs: check_hostname = _3to2kwargs['check_hostname']; del _3to2kwargs['check_hostname'] - else: check_hostname = None - if 'context' in _3to2kwargs: context = _3to2kwargs['context']; del _3to2kwargs['context'] - else: context = None - super(HTTPSConnection, self).__init__(host, port, strict, timeout, - source_address) - self.key_file = key_file - self.cert_file = cert_file - if context is None: - # Some reasonable defaults - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - context.options |= ssl.OP_NO_SSLv2 - will_verify = context.verify_mode != ssl.CERT_NONE - if check_hostname is None: - check_hostname = will_verify - elif check_hostname and not will_verify: - raise ValueError("check_hostname needs a SSL context with " - "either CERT_OPTIONAL or CERT_REQUIRED") - if key_file or cert_file: - context.load_cert_chain(cert_file, key_file) - self._context = context - self._check_hostname = check_hostname - - def connect(self): - "Connect to a host on a given (SSL) port." - - sock = socket_create_connection((self.host, self.port), - self.timeout, self.source_address) - - if self._tunnel_host: - self.sock = sock - self._tunnel() - - server_hostname = self.host if ssl.HAS_SNI else None - self.sock = self._context.wrap_socket(sock, - server_hostname=server_hostname) - try: - if self._check_hostname: - ssl.match_hostname(self.sock.getpeercert(), self.host) - except Exception: - self.sock.shutdown(socket.SHUT_RDWR) - self.sock.close() - raise - - __all__.append("HTTPSConnection") - - - # ###################################### - # # We use the old HTTPSConnection class from Py2.7, because ssl.SSLContext - # # doesn't exist in the Py2.7 stdlib - # class HTTPSConnection(HTTPConnection): - # "This class allows communication via SSL." - - # default_port = HTTPS_PORT - - # def __init__(self, host, port=None, key_file=None, cert_file=None, - # strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, - # source_address=None): - # HTTPConnection.__init__(self, host, port, strict, timeout, - # source_address) - # self.key_file = key_file - # self.cert_file = cert_file - - # def connect(self): - # "Connect to a host on a given (SSL) port." - - # sock = socket_create_connection((self.host, self.port), - # self.timeout, self.source_address) - # if self._tunnel_host: - # self.sock = sock - # self._tunnel() - # self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file) - - # __all__.append("HTTPSConnection") - # ###################################### - - -class HTTPException(Exception): - # Subclasses that define an __init__ must call Exception.__init__ - # or define self.args. Otherwise, str() will fail. - pass - -class NotConnected(HTTPException): - pass - -class InvalidURL(HTTPException): - pass - -class UnknownProtocol(HTTPException): - def __init__(self, version): - self.args = version, - self.version = version - -class UnknownTransferEncoding(HTTPException): - pass - -class UnimplementedFileMode(HTTPException): - pass - -class IncompleteRead(HTTPException): - def __init__(self, partial, expected=None): - self.args = partial, - self.partial = partial - self.expected = expected - def __repr__(self): - if self.expected is not None: - e = ', %i more expected' % self.expected - else: - e = '' - return 'IncompleteRead(%i bytes read%s)' % (len(self.partial), e) - def __str__(self): - return repr(self) - -class ImproperConnectionState(HTTPException): - pass - -class CannotSendRequest(ImproperConnectionState): - pass - -class CannotSendHeader(ImproperConnectionState): - pass - -class ResponseNotReady(ImproperConnectionState): - pass - -class BadStatusLine(HTTPException): - def __init__(self, line): - if not line: - line = repr(line) - self.args = line, - self.line = line - -class LineTooLong(HTTPException): - def __init__(self, line_type): - HTTPException.__init__(self, "got more than %d bytes when reading %s" - % (_MAXLINE, line_type)) - -# for backwards compatibility -error = HTTPException diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/cookiejar.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/cookiejar.py deleted file mode 100644 index af3ef415..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/cookiejar.py +++ /dev/null @@ -1,2110 +0,0 @@ -r"""HTTP cookie handling for web clients. - -This is a backport of the Py3.3 ``http.cookiejar`` module for -python-future. - -This module has (now fairly distant) origins in Gisle Aas' Perl module -HTTP::Cookies, from the libwww-perl library. - -Docstrings, comments and debug strings in this code refer to the -attributes of the HTTP cookie system as cookie-attributes, to distinguish -them clearly from Python attributes. - -Class diagram (note that BSDDBCookieJar and the MSIE* classes are not -distributed with the Python standard library, but are available from -http://wwwsearch.sf.net/): - - CookieJar____ - / \ \ - FileCookieJar \ \ - / | \ \ \ - MozillaCookieJar | LWPCookieJar \ \ - | | \ - | ---MSIEBase | \ - | / | | \ - | / MSIEDBCookieJar BSDDBCookieJar - |/ - MSIECookieJar - -""" - -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import -from future.builtins import filter, int, map, open, str -from future.utils import as_native_str, PY2 - -__all__ = ['Cookie', 'CookieJar', 'CookiePolicy', 'DefaultCookiePolicy', - 'FileCookieJar', 'LWPCookieJar', 'LoadError', 'MozillaCookieJar'] - -import copy -import datetime -import re -if PY2: - re.ASCII = 0 -import time -from future.backports.urllib.parse import urlparse, urlsplit, quote -from future.backports.http.client import HTTP_PORT -try: - import threading as _threading -except ImportError: - import dummy_threading as _threading -from calendar import timegm - -debug = False # set to True to enable debugging via the logging module -logger = None - -def _debug(*args): - if not debug: - return - global logger - if not logger: - import logging - logger = logging.getLogger("http.cookiejar") - return logger.debug(*args) - - -DEFAULT_HTTP_PORT = str(HTTP_PORT) -MISSING_FILENAME_TEXT = ("a filename was not supplied (nor was the CookieJar " - "instance initialised with one)") - -def _warn_unhandled_exception(): - # There are a few catch-all except: statements in this module, for - # catching input that's bad in unexpected ways. Warn if any - # exceptions are caught there. - import io, warnings, traceback - f = io.StringIO() - traceback.print_exc(None, f) - msg = f.getvalue() - warnings.warn("http.cookiejar bug!\n%s" % msg, stacklevel=2) - - -# Date/time conversion -# ----------------------------------------------------------------------------- - -EPOCH_YEAR = 1970 -def _timegm(tt): - year, month, mday, hour, min, sec = tt[:6] - if ((year >= EPOCH_YEAR) and (1 <= month <= 12) and (1 <= mday <= 31) and - (0 <= hour <= 24) and (0 <= min <= 59) and (0 <= sec <= 61)): - return timegm(tt) - else: - return None - -DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] -MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] -MONTHS_LOWER = [] -for month in MONTHS: MONTHS_LOWER.append(month.lower()) - -def time2isoz(t=None): - """Return a string representing time in seconds since epoch, t. - - If the function is called without an argument, it will use the current - time. - - The format of the returned string is like "YYYY-MM-DD hh:mm:ssZ", - representing Universal Time (UTC, aka GMT). An example of this format is: - - 1994-11-24 08:49:37Z - - """ - if t is None: - dt = datetime.datetime.utcnow() - else: - dt = datetime.datetime.utcfromtimestamp(t) - return "%04d-%02d-%02d %02d:%02d:%02dZ" % ( - dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second) - -def time2netscape(t=None): - """Return a string representing time in seconds since epoch, t. - - If the function is called without an argument, it will use the current - time. - - The format of the returned string is like this: - - Wed, DD-Mon-YYYY HH:MM:SS GMT - - """ - if t is None: - dt = datetime.datetime.utcnow() - else: - dt = datetime.datetime.utcfromtimestamp(t) - return "%s %02d-%s-%04d %02d:%02d:%02d GMT" % ( - DAYS[dt.weekday()], dt.day, MONTHS[dt.month-1], - dt.year, dt.hour, dt.minute, dt.second) - - -UTC_ZONES = {"GMT": None, "UTC": None, "UT": None, "Z": None} - -TIMEZONE_RE = re.compile(r"^([-+])?(\d\d?):?(\d\d)?$", re.ASCII) -def offset_from_tz_string(tz): - offset = None - if tz in UTC_ZONES: - offset = 0 - else: - m = TIMEZONE_RE.search(tz) - if m: - offset = 3600 * int(m.group(2)) - if m.group(3): - offset = offset + 60 * int(m.group(3)) - if m.group(1) == '-': - offset = -offset - return offset - -def _str2time(day, mon, yr, hr, min, sec, tz): - # translate month name to number - # month numbers start with 1 (January) - try: - mon = MONTHS_LOWER.index(mon.lower())+1 - except ValueError: - # maybe it's already a number - try: - imon = int(mon) - except ValueError: - return None - if 1 <= imon <= 12: - mon = imon - else: - return None - - # make sure clock elements are defined - if hr is None: hr = 0 - if min is None: min = 0 - if sec is None: sec = 0 - - yr = int(yr) - day = int(day) - hr = int(hr) - min = int(min) - sec = int(sec) - - if yr < 1000: - # find "obvious" year - cur_yr = time.localtime(time.time())[0] - m = cur_yr % 100 - tmp = yr - yr = yr + cur_yr - m - m = m - tmp - if abs(m) > 50: - if m > 0: yr = yr + 100 - else: yr = yr - 100 - - # convert UTC time tuple to seconds since epoch (not timezone-adjusted) - t = _timegm((yr, mon, day, hr, min, sec, tz)) - - if t is not None: - # adjust time using timezone string, to get absolute time since epoch - if tz is None: - tz = "UTC" - tz = tz.upper() - offset = offset_from_tz_string(tz) - if offset is None: - return None - t = t - offset - - return t - -STRICT_DATE_RE = re.compile( - r"^[SMTWF][a-z][a-z], (\d\d) ([JFMASOND][a-z][a-z]) " - "(\d\d\d\d) (\d\d):(\d\d):(\d\d) GMT$", re.ASCII) -WEEKDAY_RE = re.compile( - r"^(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)[a-z]*,?\s*", re.I | re.ASCII) -LOOSE_HTTP_DATE_RE = re.compile( - r"""^ - (\d\d?) # day - (?:\s+|[-\/]) - (\w+) # month - (?:\s+|[-\/]) - (\d+) # year - (?: - (?:\s+|:) # separator before clock - (\d\d?):(\d\d) # hour:min - (?::(\d\d))? # optional seconds - )? # optional clock - \s* - ([-+]?\d{2,4}|(?![APap][Mm]\b)[A-Za-z]+)? # timezone - \s* - (?:\(\w+\))? # ASCII representation of timezone in parens. - \s*$""", re.X | re.ASCII) -def http2time(text): - """Returns time in seconds since epoch of time represented by a string. - - Return value is an integer. - - None is returned if the format of str is unrecognized, the time is outside - the representable range, or the timezone string is not recognized. If the - string contains no timezone, UTC is assumed. - - The timezone in the string may be numerical (like "-0800" or "+0100") or a - string timezone (like "UTC", "GMT", "BST" or "EST"). Currently, only the - timezone strings equivalent to UTC (zero offset) are known to the function. - - The function loosely parses the following formats: - - Wed, 09 Feb 1994 22:23:32 GMT -- HTTP format - Tuesday, 08-Feb-94 14:15:29 GMT -- old rfc850 HTTP format - Tuesday, 08-Feb-1994 14:15:29 GMT -- broken rfc850 HTTP format - 09 Feb 1994 22:23:32 GMT -- HTTP format (no weekday) - 08-Feb-94 14:15:29 GMT -- rfc850 format (no weekday) - 08-Feb-1994 14:15:29 GMT -- broken rfc850 format (no weekday) - - The parser ignores leading and trailing whitespace. The time may be - absent. - - If the year is given with only 2 digits, the function will select the - century that makes the year closest to the current date. - - """ - # fast exit for strictly conforming string - m = STRICT_DATE_RE.search(text) - if m: - g = m.groups() - mon = MONTHS_LOWER.index(g[1].lower()) + 1 - tt = (int(g[2]), mon, int(g[0]), - int(g[3]), int(g[4]), float(g[5])) - return _timegm(tt) - - # No, we need some messy parsing... - - # clean up - text = text.lstrip() - text = WEEKDAY_RE.sub("", text, 1) # Useless weekday - - # tz is time zone specifier string - day, mon, yr, hr, min, sec, tz = [None]*7 - - # loose regexp parse - m = LOOSE_HTTP_DATE_RE.search(text) - if m is not None: - day, mon, yr, hr, min, sec, tz = m.groups() - else: - return None # bad format - - return _str2time(day, mon, yr, hr, min, sec, tz) - -ISO_DATE_RE = re.compile( - """^ - (\d{4}) # year - [-\/]? - (\d\d?) # numerical month - [-\/]? - (\d\d?) # day - (?: - (?:\s+|[-:Tt]) # separator before clock - (\d\d?):?(\d\d) # hour:min - (?::?(\d\d(?:\.\d*)?))? # optional seconds (and fractional) - )? # optional clock - \s* - ([-+]?\d\d?:?(:?\d\d)? - |Z|z)? # timezone (Z is "zero meridian", i.e. GMT) - \s*$""", re.X | re. ASCII) -def iso2time(text): - """ - As for http2time, but parses the ISO 8601 formats: - - 1994-02-03 14:15:29 -0100 -- ISO 8601 format - 1994-02-03 14:15:29 -- zone is optional - 1994-02-03 -- only date - 1994-02-03T14:15:29 -- Use T as separator - 19940203T141529Z -- ISO 8601 compact format - 19940203 -- only date - - """ - # clean up - text = text.lstrip() - - # tz is time zone specifier string - day, mon, yr, hr, min, sec, tz = [None]*7 - - # loose regexp parse - m = ISO_DATE_RE.search(text) - if m is not None: - # XXX there's an extra bit of the timezone I'm ignoring here: is - # this the right thing to do? - yr, mon, day, hr, min, sec, tz, _ = m.groups() - else: - return None # bad format - - return _str2time(day, mon, yr, hr, min, sec, tz) - - -# Header parsing -# ----------------------------------------------------------------------------- - -def unmatched(match): - """Return unmatched part of re.Match object.""" - start, end = match.span(0) - return match.string[:start]+match.string[end:] - -HEADER_TOKEN_RE = re.compile(r"^\s*([^=\s;,]+)") -HEADER_QUOTED_VALUE_RE = re.compile(r"^\s*=\s*\"([^\"\\]*(?:\\.[^\"\\]*)*)\"") -HEADER_VALUE_RE = re.compile(r"^\s*=\s*([^\s;,]*)") -HEADER_ESCAPE_RE = re.compile(r"\\(.)") -def split_header_words(header_values): - r"""Parse header values into a list of lists containing key,value pairs. - - The function knows how to deal with ",", ";" and "=" as well as quoted - values after "=". A list of space separated tokens are parsed as if they - were separated by ";". - - If the header_values passed as argument contains multiple values, then they - are treated as if they were a single value separated by comma ",". - - This means that this function is useful for parsing header fields that - follow this syntax (BNF as from the HTTP/1.1 specification, but we relax - the requirement for tokens). - - headers = #header - header = (token | parameter) *( [";"] (token | parameter)) - - token = 1* - separators = "(" | ")" | "<" | ">" | "@" - | "," | ";" | ":" | "\" | <"> - | "/" | "[" | "]" | "?" | "=" - | "{" | "}" | SP | HT - - quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) - qdtext = > - quoted-pair = "\" CHAR - - parameter = attribute "=" value - attribute = token - value = token | quoted-string - - Each header is represented by a list of key/value pairs. The value for a - simple token (not part of a parameter) is None. Syntactically incorrect - headers will not necessarily be parsed as you would want. - - This is easier to describe with some examples: - - >>> split_header_words(['foo="bar"; port="80,81"; discard, bar=baz']) - [[('foo', 'bar'), ('port', '80,81'), ('discard', None)], [('bar', 'baz')]] - >>> split_header_words(['text/html; charset="iso-8859-1"']) - [[('text/html', None), ('charset', 'iso-8859-1')]] - >>> split_header_words([r'Basic realm="\"foo\bar\""']) - [[('Basic', None), ('realm', '"foobar"')]] - - """ - assert not isinstance(header_values, str) - result = [] - for text in header_values: - orig_text = text - pairs = [] - while text: - m = HEADER_TOKEN_RE.search(text) - if m: - text = unmatched(m) - name = m.group(1) - m = HEADER_QUOTED_VALUE_RE.search(text) - if m: # quoted value - text = unmatched(m) - value = m.group(1) - value = HEADER_ESCAPE_RE.sub(r"\1", value) - else: - m = HEADER_VALUE_RE.search(text) - if m: # unquoted value - text = unmatched(m) - value = m.group(1) - value = value.rstrip() - else: - # no value, a lone token - value = None - pairs.append((name, value)) - elif text.lstrip().startswith(","): - # concatenated headers, as per RFC 2616 section 4.2 - text = text.lstrip()[1:] - if pairs: result.append(pairs) - pairs = [] - else: - # skip junk - non_junk, nr_junk_chars = re.subn("^[=\s;]*", "", text) - assert nr_junk_chars > 0, ( - "split_header_words bug: '%s', '%s', %s" % - (orig_text, text, pairs)) - text = non_junk - if pairs: result.append(pairs) - return result - -HEADER_JOIN_ESCAPE_RE = re.compile(r"([\"\\])") -def join_header_words(lists): - """Do the inverse (almost) of the conversion done by split_header_words. - - Takes a list of lists of (key, value) pairs and produces a single header - value. Attribute values are quoted if needed. - - >>> join_header_words([[("text/plain", None), ("charset", "iso-8859/1")]]) - 'text/plain; charset="iso-8859/1"' - >>> join_header_words([[("text/plain", None)], [("charset", "iso-8859/1")]]) - 'text/plain, charset="iso-8859/1"' - - """ - headers = [] - for pairs in lists: - attr = [] - for k, v in pairs: - if v is not None: - if not re.search(r"^\w+$", v): - v = HEADER_JOIN_ESCAPE_RE.sub(r"\\\1", v) # escape " and \ - v = '"%s"' % v - k = "%s=%s" % (k, v) - attr.append(k) - if attr: headers.append("; ".join(attr)) - return ", ".join(headers) - -def strip_quotes(text): - if text.startswith('"'): - text = text[1:] - if text.endswith('"'): - text = text[:-1] - return text - -def parse_ns_headers(ns_headers): - """Ad-hoc parser for Netscape protocol cookie-attributes. - - The old Netscape cookie format for Set-Cookie can for instance contain - an unquoted "," in the expires field, so we have to use this ad-hoc - parser instead of split_header_words. - - XXX This may not make the best possible effort to parse all the crap - that Netscape Cookie headers contain. Ronald Tschalar's HTTPClient - parser is probably better, so could do worse than following that if - this ever gives any trouble. - - Currently, this is also used for parsing RFC 2109 cookies. - - """ - known_attrs = ("expires", "domain", "path", "secure", - # RFC 2109 attrs (may turn up in Netscape cookies, too) - "version", "port", "max-age") - - result = [] - for ns_header in ns_headers: - pairs = [] - version_set = False - for ii, param in enumerate(re.split(r";\s*", ns_header)): - param = param.rstrip() - if param == "": continue - if "=" not in param: - k, v = param, None - else: - k, v = re.split(r"\s*=\s*", param, 1) - k = k.lstrip() - if ii != 0: - lc = k.lower() - if lc in known_attrs: - k = lc - if k == "version": - # This is an RFC 2109 cookie. - v = strip_quotes(v) - version_set = True - if k == "expires": - # convert expires date to seconds since epoch - v = http2time(strip_quotes(v)) # None if invalid - pairs.append((k, v)) - - if pairs: - if not version_set: - pairs.append(("version", "0")) - result.append(pairs) - - return result - - -IPV4_RE = re.compile(r"\.\d+$", re.ASCII) -def is_HDN(text): - """Return True if text is a host domain name.""" - # XXX - # This may well be wrong. Which RFC is HDN defined in, if any (for - # the purposes of RFC 2965)? - # For the current implementation, what about IPv6? Remember to look - # at other uses of IPV4_RE also, if change this. - if IPV4_RE.search(text): - return False - if text == "": - return False - if text[0] == "." or text[-1] == ".": - return False - return True - -def domain_match(A, B): - """Return True if domain A domain-matches domain B, according to RFC 2965. - - A and B may be host domain names or IP addresses. - - RFC 2965, section 1: - - Host names can be specified either as an IP address or a HDN string. - Sometimes we compare one host name with another. (Such comparisons SHALL - be case-insensitive.) Host A's name domain-matches host B's if - - * their host name strings string-compare equal; or - - * A is a HDN string and has the form NB, where N is a non-empty - name string, B has the form .B', and B' is a HDN string. (So, - x.y.com domain-matches .Y.com but not Y.com.) - - Note that domain-match is not a commutative operation: a.b.c.com - domain-matches .c.com, but not the reverse. - - """ - # Note that, if A or B are IP addresses, the only relevant part of the - # definition of the domain-match algorithm is the direct string-compare. - A = A.lower() - B = B.lower() - if A == B: - return True - if not is_HDN(A): - return False - i = A.rfind(B) - if i == -1 or i == 0: - # A does not have form NB, or N is the empty string - return False - if not B.startswith("."): - return False - if not is_HDN(B[1:]): - return False - return True - -def liberal_is_HDN(text): - """Return True if text is a sort-of-like a host domain name. - - For accepting/blocking domains. - - """ - if IPV4_RE.search(text): - return False - return True - -def user_domain_match(A, B): - """For blocking/accepting domains. - - A and B may be host domain names or IP addresses. - - """ - A = A.lower() - B = B.lower() - if not (liberal_is_HDN(A) and liberal_is_HDN(B)): - if A == B: - # equal IP addresses - return True - return False - initial_dot = B.startswith(".") - if initial_dot and A.endswith(B): - return True - if not initial_dot and A == B: - return True - return False - -cut_port_re = re.compile(r":\d+$", re.ASCII) -def request_host(request): - """Return request-host, as defined by RFC 2965. - - Variation from RFC: returned value is lowercased, for convenient - comparison. - - """ - url = request.get_full_url() - host = urlparse(url)[1] - if host == "": - host = request.get_header("Host", "") - - # remove port, if present - host = cut_port_re.sub("", host, 1) - return host.lower() - -def eff_request_host(request): - """Return a tuple (request-host, effective request-host name). - - As defined by RFC 2965, except both are lowercased. - - """ - erhn = req_host = request_host(request) - if req_host.find(".") == -1 and not IPV4_RE.search(req_host): - erhn = req_host + ".local" - return req_host, erhn - -def request_path(request): - """Path component of request-URI, as defined by RFC 2965.""" - url = request.get_full_url() - parts = urlsplit(url) - path = escape_path(parts.path) - if not path.startswith("/"): - # fix bad RFC 2396 absoluteURI - path = "/" + path - return path - -def request_port(request): - host = request.host - i = host.find(':') - if i >= 0: - port = host[i+1:] - try: - int(port) - except ValueError: - _debug("nonnumeric port: '%s'", port) - return None - else: - port = DEFAULT_HTTP_PORT - return port - -# Characters in addition to A-Z, a-z, 0-9, '_', '.', and '-' that don't -# need to be escaped to form a valid HTTP URL (RFCs 2396 and 1738). -HTTP_PATH_SAFE = "%/;:@&=+$,!~*'()" -ESCAPED_CHAR_RE = re.compile(r"%([0-9a-fA-F][0-9a-fA-F])") -def uppercase_escaped_char(match): - return "%%%s" % match.group(1).upper() -def escape_path(path): - """Escape any invalid characters in HTTP URL, and uppercase all escapes.""" - # There's no knowing what character encoding was used to create URLs - # containing %-escapes, but since we have to pick one to escape invalid - # path characters, we pick UTF-8, as recommended in the HTML 4.0 - # specification: - # http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.2.1 - # And here, kind of: draft-fielding-uri-rfc2396bis-03 - # (And in draft IRI specification: draft-duerst-iri-05) - # (And here, for new URI schemes: RFC 2718) - path = quote(path, HTTP_PATH_SAFE) - path = ESCAPED_CHAR_RE.sub(uppercase_escaped_char, path) - return path - -def reach(h): - """Return reach of host h, as defined by RFC 2965, section 1. - - The reach R of a host name H is defined as follows: - - * If - - - H is the host domain name of a host; and, - - - H has the form A.B; and - - - A has no embedded (that is, interior) dots; and - - - B has at least one embedded dot, or B is the string "local". - then the reach of H is .B. - - * Otherwise, the reach of H is H. - - >>> reach("www.acme.com") - '.acme.com' - >>> reach("acme.com") - 'acme.com' - >>> reach("acme.local") - '.local' - - """ - i = h.find(".") - if i >= 0: - #a = h[:i] # this line is only here to show what a is - b = h[i+1:] - i = b.find(".") - if is_HDN(h) and (i >= 0 or b == "local"): - return "."+b - return h - -def is_third_party(request): - """ - - RFC 2965, section 3.3.6: - - An unverifiable transaction is to a third-party host if its request- - host U does not domain-match the reach R of the request-host O in the - origin transaction. - - """ - req_host = request_host(request) - if not domain_match(req_host, reach(request.get_origin_req_host())): - return True - else: - return False - - -class Cookie(object): - """HTTP Cookie. - - This class represents both Netscape and RFC 2965 cookies. - - This is deliberately a very simple class. It just holds attributes. It's - possible to construct Cookie instances that don't comply with the cookie - standards. CookieJar.make_cookies is the factory function for Cookie - objects -- it deals with cookie parsing, supplying defaults, and - normalising to the representation used in this class. CookiePolicy is - responsible for checking them to see whether they should be accepted from - and returned to the server. - - Note that the port may be present in the headers, but unspecified ("Port" - rather than"Port=80", for example); if this is the case, port is None. - - """ - - def __init__(self, version, name, value, - port, port_specified, - domain, domain_specified, domain_initial_dot, - path, path_specified, - secure, - expires, - discard, - comment, - comment_url, - rest, - rfc2109=False, - ): - - if version is not None: version = int(version) - if expires is not None: expires = int(expires) - if port is None and port_specified is True: - raise ValueError("if port is None, port_specified must be false") - - self.version = version - self.name = name - self.value = value - self.port = port - self.port_specified = port_specified - # normalise case, as per RFC 2965 section 3.3.3 - self.domain = domain.lower() - self.domain_specified = domain_specified - # Sigh. We need to know whether the domain given in the - # cookie-attribute had an initial dot, in order to follow RFC 2965 - # (as clarified in draft errata). Needed for the returned $Domain - # value. - self.domain_initial_dot = domain_initial_dot - self.path = path - self.path_specified = path_specified - self.secure = secure - self.expires = expires - self.discard = discard - self.comment = comment - self.comment_url = comment_url - self.rfc2109 = rfc2109 - - self._rest = copy.copy(rest) - - def has_nonstandard_attr(self, name): - return name in self._rest - def get_nonstandard_attr(self, name, default=None): - return self._rest.get(name, default) - def set_nonstandard_attr(self, name, value): - self._rest[name] = value - - def is_expired(self, now=None): - if now is None: now = time.time() - if (self.expires is not None) and (self.expires <= now): - return True - return False - - def __str__(self): - if self.port is None: p = "" - else: p = ":"+self.port - limit = self.domain + p + self.path - if self.value is not None: - namevalue = "%s=%s" % (self.name, self.value) - else: - namevalue = self.name - return "" % (namevalue, limit) - - @as_native_str() - def __repr__(self): - args = [] - for name in ("version", "name", "value", - "port", "port_specified", - "domain", "domain_specified", "domain_initial_dot", - "path", "path_specified", - "secure", "expires", "discard", "comment", "comment_url", - ): - attr = getattr(self, name) - ### Python-Future: - # Avoid u'...' prefixes for unicode strings: - if isinstance(attr, str): - attr = str(attr) - ### - args.append(str("%s=%s") % (name, repr(attr))) - args.append("rest=%s" % repr(self._rest)) - args.append("rfc2109=%s" % repr(self.rfc2109)) - return "Cookie(%s)" % ", ".join(args) - - -class CookiePolicy(object): - """Defines which cookies get accepted from and returned to server. - - May also modify cookies, though this is probably a bad idea. - - The subclass DefaultCookiePolicy defines the standard rules for Netscape - and RFC 2965 cookies -- override that if you want a customised policy. - - """ - def set_ok(self, cookie, request): - """Return true if (and only if) cookie should be accepted from server. - - Currently, pre-expired cookies never get this far -- the CookieJar - class deletes such cookies itself. - - """ - raise NotImplementedError() - - def return_ok(self, cookie, request): - """Return true if (and only if) cookie should be returned to server.""" - raise NotImplementedError() - - def domain_return_ok(self, domain, request): - """Return false if cookies should not be returned, given cookie domain. - """ - return True - - def path_return_ok(self, path, request): - """Return false if cookies should not be returned, given cookie path. - """ - return True - - -class DefaultCookiePolicy(CookiePolicy): - """Implements the standard rules for accepting and returning cookies.""" - - DomainStrictNoDots = 1 - DomainStrictNonDomain = 2 - DomainRFC2965Match = 4 - - DomainLiberal = 0 - DomainStrict = DomainStrictNoDots|DomainStrictNonDomain - - def __init__(self, - blocked_domains=None, allowed_domains=None, - netscape=True, rfc2965=False, - rfc2109_as_netscape=None, - hide_cookie2=False, - strict_domain=False, - strict_rfc2965_unverifiable=True, - strict_ns_unverifiable=False, - strict_ns_domain=DomainLiberal, - strict_ns_set_initial_dollar=False, - strict_ns_set_path=False, - ): - """Constructor arguments should be passed as keyword arguments only.""" - self.netscape = netscape - self.rfc2965 = rfc2965 - self.rfc2109_as_netscape = rfc2109_as_netscape - self.hide_cookie2 = hide_cookie2 - self.strict_domain = strict_domain - self.strict_rfc2965_unverifiable = strict_rfc2965_unverifiable - self.strict_ns_unverifiable = strict_ns_unverifiable - self.strict_ns_domain = strict_ns_domain - self.strict_ns_set_initial_dollar = strict_ns_set_initial_dollar - self.strict_ns_set_path = strict_ns_set_path - - if blocked_domains is not None: - self._blocked_domains = tuple(blocked_domains) - else: - self._blocked_domains = () - - if allowed_domains is not None: - allowed_domains = tuple(allowed_domains) - self._allowed_domains = allowed_domains - - def blocked_domains(self): - """Return the sequence of blocked domains (as a tuple).""" - return self._blocked_domains - def set_blocked_domains(self, blocked_domains): - """Set the sequence of blocked domains.""" - self._blocked_domains = tuple(blocked_domains) - - def is_blocked(self, domain): - for blocked_domain in self._blocked_domains: - if user_domain_match(domain, blocked_domain): - return True - return False - - def allowed_domains(self): - """Return None, or the sequence of allowed domains (as a tuple).""" - return self._allowed_domains - def set_allowed_domains(self, allowed_domains): - """Set the sequence of allowed domains, or None.""" - if allowed_domains is not None: - allowed_domains = tuple(allowed_domains) - self._allowed_domains = allowed_domains - - def is_not_allowed(self, domain): - if self._allowed_domains is None: - return False - for allowed_domain in self._allowed_domains: - if user_domain_match(domain, allowed_domain): - return False - return True - - def set_ok(self, cookie, request): - """ - If you override .set_ok(), be sure to call this method. If it returns - false, so should your subclass (assuming your subclass wants to be more - strict about which cookies to accept). - - """ - _debug(" - checking cookie %s=%s", cookie.name, cookie.value) - - assert cookie.name is not None - - for n in "version", "verifiability", "name", "path", "domain", "port": - fn_name = "set_ok_"+n - fn = getattr(self, fn_name) - if not fn(cookie, request): - return False - - return True - - def set_ok_version(self, cookie, request): - if cookie.version is None: - # Version is always set to 0 by parse_ns_headers if it's a Netscape - # cookie, so this must be an invalid RFC 2965 cookie. - _debug(" Set-Cookie2 without version attribute (%s=%s)", - cookie.name, cookie.value) - return False - if cookie.version > 0 and not self.rfc2965: - _debug(" RFC 2965 cookies are switched off") - return False - elif cookie.version == 0 and not self.netscape: - _debug(" Netscape cookies are switched off") - return False - return True - - def set_ok_verifiability(self, cookie, request): - if request.unverifiable and is_third_party(request): - if cookie.version > 0 and self.strict_rfc2965_unverifiable: - _debug(" third-party RFC 2965 cookie during " - "unverifiable transaction") - return False - elif cookie.version == 0 and self.strict_ns_unverifiable: - _debug(" third-party Netscape cookie during " - "unverifiable transaction") - return False - return True - - def set_ok_name(self, cookie, request): - # Try and stop servers setting V0 cookies designed to hack other - # servers that know both V0 and V1 protocols. - if (cookie.version == 0 and self.strict_ns_set_initial_dollar and - cookie.name.startswith("$")): - _debug(" illegal name (starts with '$'): '%s'", cookie.name) - return False - return True - - def set_ok_path(self, cookie, request): - if cookie.path_specified: - req_path = request_path(request) - if ((cookie.version > 0 or - (cookie.version == 0 and self.strict_ns_set_path)) and - not req_path.startswith(cookie.path)): - _debug(" path attribute %s is not a prefix of request " - "path %s", cookie.path, req_path) - return False - return True - - def set_ok_domain(self, cookie, request): - if self.is_blocked(cookie.domain): - _debug(" domain %s is in user block-list", cookie.domain) - return False - if self.is_not_allowed(cookie.domain): - _debug(" domain %s is not in user allow-list", cookie.domain) - return False - if cookie.domain_specified: - req_host, erhn = eff_request_host(request) - domain = cookie.domain - if self.strict_domain and (domain.count(".") >= 2): - # XXX This should probably be compared with the Konqueror - # (kcookiejar.cpp) and Mozilla implementations, but it's a - # losing battle. - i = domain.rfind(".") - j = domain.rfind(".", 0, i) - if j == 0: # domain like .foo.bar - tld = domain[i+1:] - sld = domain[j+1:i] - if sld.lower() in ("co", "ac", "com", "edu", "org", "net", - "gov", "mil", "int", "aero", "biz", "cat", "coop", - "info", "jobs", "mobi", "museum", "name", "pro", - "travel", "eu") and len(tld) == 2: - # domain like .co.uk - _debug(" country-code second level domain %s", domain) - return False - if domain.startswith("."): - undotted_domain = domain[1:] - else: - undotted_domain = domain - embedded_dots = (undotted_domain.find(".") >= 0) - if not embedded_dots and domain != ".local": - _debug(" non-local domain %s contains no embedded dot", - domain) - return False - if cookie.version == 0: - if (not erhn.endswith(domain) and - (not erhn.startswith(".") and - not ("."+erhn).endswith(domain))): - _debug(" effective request-host %s (even with added " - "initial dot) does not end with %s", - erhn, domain) - return False - if (cookie.version > 0 or - (self.strict_ns_domain & self.DomainRFC2965Match)): - if not domain_match(erhn, domain): - _debug(" effective request-host %s does not domain-match " - "%s", erhn, domain) - return False - if (cookie.version > 0 or - (self.strict_ns_domain & self.DomainStrictNoDots)): - host_prefix = req_host[:-len(domain)] - if (host_prefix.find(".") >= 0 and - not IPV4_RE.search(req_host)): - _debug(" host prefix %s for domain %s contains a dot", - host_prefix, domain) - return False - return True - - def set_ok_port(self, cookie, request): - if cookie.port_specified: - req_port = request_port(request) - if req_port is None: - req_port = "80" - else: - req_port = str(req_port) - for p in cookie.port.split(","): - try: - int(p) - except ValueError: - _debug(" bad port %s (not numeric)", p) - return False - if p == req_port: - break - else: - _debug(" request port (%s) not found in %s", - req_port, cookie.port) - return False - return True - - def return_ok(self, cookie, request): - """ - If you override .return_ok(), be sure to call this method. If it - returns false, so should your subclass (assuming your subclass wants to - be more strict about which cookies to return). - - """ - # Path has already been checked by .path_return_ok(), and domain - # blocking done by .domain_return_ok(). - _debug(" - checking cookie %s=%s", cookie.name, cookie.value) - - for n in "version", "verifiability", "secure", "expires", "port", "domain": - fn_name = "return_ok_"+n - fn = getattr(self, fn_name) - if not fn(cookie, request): - return False - return True - - def return_ok_version(self, cookie, request): - if cookie.version > 0 and not self.rfc2965: - _debug(" RFC 2965 cookies are switched off") - return False - elif cookie.version == 0 and not self.netscape: - _debug(" Netscape cookies are switched off") - return False - return True - - def return_ok_verifiability(self, cookie, request): - if request.unverifiable and is_third_party(request): - if cookie.version > 0 and self.strict_rfc2965_unverifiable: - _debug(" third-party RFC 2965 cookie during unverifiable " - "transaction") - return False - elif cookie.version == 0 and self.strict_ns_unverifiable: - _debug(" third-party Netscape cookie during unverifiable " - "transaction") - return False - return True - - def return_ok_secure(self, cookie, request): - if cookie.secure and request.type != "https": - _debug(" secure cookie with non-secure request") - return False - return True - - def return_ok_expires(self, cookie, request): - if cookie.is_expired(self._now): - _debug(" cookie expired") - return False - return True - - def return_ok_port(self, cookie, request): - if cookie.port: - req_port = request_port(request) - if req_port is None: - req_port = "80" - for p in cookie.port.split(","): - if p == req_port: - break - else: - _debug(" request port %s does not match cookie port %s", - req_port, cookie.port) - return False - return True - - def return_ok_domain(self, cookie, request): - req_host, erhn = eff_request_host(request) - domain = cookie.domain - - # strict check of non-domain cookies: Mozilla does this, MSIE5 doesn't - if (cookie.version == 0 and - (self.strict_ns_domain & self.DomainStrictNonDomain) and - not cookie.domain_specified and domain != erhn): - _debug(" cookie with unspecified domain does not string-compare " - "equal to request domain") - return False - - if cookie.version > 0 and not domain_match(erhn, domain): - _debug(" effective request-host name %s does not domain-match " - "RFC 2965 cookie domain %s", erhn, domain) - return False - if cookie.version == 0 and not ("."+erhn).endswith(domain): - _debug(" request-host %s does not match Netscape cookie domain " - "%s", req_host, domain) - return False - return True - - def domain_return_ok(self, domain, request): - # Liberal check of. This is here as an optimization to avoid - # having to load lots of MSIE cookie files unless necessary. - req_host, erhn = eff_request_host(request) - if not req_host.startswith("."): - req_host = "."+req_host - if not erhn.startswith("."): - erhn = "."+erhn - if not (req_host.endswith(domain) or erhn.endswith(domain)): - #_debug(" request domain %s does not match cookie domain %s", - # req_host, domain) - return False - - if self.is_blocked(domain): - _debug(" domain %s is in user block-list", domain) - return False - if self.is_not_allowed(domain): - _debug(" domain %s is not in user allow-list", domain) - return False - - return True - - def path_return_ok(self, path, request): - _debug("- checking cookie path=%s", path) - req_path = request_path(request) - if not req_path.startswith(path): - _debug(" %s does not path-match %s", req_path, path) - return False - return True - - -def vals_sorted_by_key(adict): - keys = sorted(adict.keys()) - return map(adict.get, keys) - -def deepvalues(mapping): - """Iterates over nested mapping, depth-first, in sorted order by key.""" - values = vals_sorted_by_key(mapping) - for obj in values: - mapping = False - try: - obj.items - except AttributeError: - pass - else: - mapping = True - for subobj in deepvalues(obj): - yield subobj - if not mapping: - yield obj - - -# Used as second parameter to dict.get() method, to distinguish absent -# dict key from one with a None value. -class Absent(object): pass - -class CookieJar(object): - """Collection of HTTP cookies. - - You may not need to know about this class: try - urllib.request.build_opener(HTTPCookieProcessor).open(url). - """ - - non_word_re = re.compile(r"\W") - quote_re = re.compile(r"([\"\\])") - strict_domain_re = re.compile(r"\.?[^.]*") - domain_re = re.compile(r"[^.]*") - dots_re = re.compile(r"^\.+") - - magic_re = re.compile(r"^\#LWP-Cookies-(\d+\.\d+)", re.ASCII) - - def __init__(self, policy=None): - if policy is None: - policy = DefaultCookiePolicy() - self._policy = policy - - self._cookies_lock = _threading.RLock() - self._cookies = {} - - def set_policy(self, policy): - self._policy = policy - - def _cookies_for_domain(self, domain, request): - cookies = [] - if not self._policy.domain_return_ok(domain, request): - return [] - _debug("Checking %s for cookies to return", domain) - cookies_by_path = self._cookies[domain] - for path in cookies_by_path.keys(): - if not self._policy.path_return_ok(path, request): - continue - cookies_by_name = cookies_by_path[path] - for cookie in cookies_by_name.values(): - if not self._policy.return_ok(cookie, request): - _debug(" not returning cookie") - continue - _debug(" it's a match") - cookies.append(cookie) - return cookies - - def _cookies_for_request(self, request): - """Return a list of cookies to be returned to server.""" - cookies = [] - for domain in self._cookies.keys(): - cookies.extend(self._cookies_for_domain(domain, request)) - return cookies - - def _cookie_attrs(self, cookies): - """Return a list of cookie-attributes to be returned to server. - - like ['foo="bar"; $Path="/"', ...] - - The $Version attribute is also added when appropriate (currently only - once per request). - - """ - # add cookies in order of most specific (ie. longest) path first - cookies.sort(key=lambda a: len(a.path), reverse=True) - - version_set = False - - attrs = [] - for cookie in cookies: - # set version of Cookie header - # XXX - # What should it be if multiple matching Set-Cookie headers have - # different versions themselves? - # Answer: there is no answer; was supposed to be settled by - # RFC 2965 errata, but that may never appear... - version = cookie.version - if not version_set: - version_set = True - if version > 0: - attrs.append("$Version=%s" % version) - - # quote cookie value if necessary - # (not for Netscape protocol, which already has any quotes - # intact, due to the poorly-specified Netscape Cookie: syntax) - if ((cookie.value is not None) and - self.non_word_re.search(cookie.value) and version > 0): - value = self.quote_re.sub(r"\\\1", cookie.value) - else: - value = cookie.value - - # add cookie-attributes to be returned in Cookie header - if cookie.value is None: - attrs.append(cookie.name) - else: - attrs.append("%s=%s" % (cookie.name, value)) - if version > 0: - if cookie.path_specified: - attrs.append('$Path="%s"' % cookie.path) - if cookie.domain.startswith("."): - domain = cookie.domain - if (not cookie.domain_initial_dot and - domain.startswith(".")): - domain = domain[1:] - attrs.append('$Domain="%s"' % domain) - if cookie.port is not None: - p = "$Port" - if cookie.port_specified: - p = p + ('="%s"' % cookie.port) - attrs.append(p) - - return attrs - - def add_cookie_header(self, request): - """Add correct Cookie: header to request (urllib.request.Request object). - - The Cookie2 header is also added unless policy.hide_cookie2 is true. - - """ - _debug("add_cookie_header") - self._cookies_lock.acquire() - try: - - self._policy._now = self._now = int(time.time()) - - cookies = self._cookies_for_request(request) - - attrs = self._cookie_attrs(cookies) - if attrs: - if not request.has_header("Cookie"): - request.add_unredirected_header( - "Cookie", "; ".join(attrs)) - - # if necessary, advertise that we know RFC 2965 - if (self._policy.rfc2965 and not self._policy.hide_cookie2 and - not request.has_header("Cookie2")): - for cookie in cookies: - if cookie.version != 1: - request.add_unredirected_header("Cookie2", '$Version="1"') - break - - finally: - self._cookies_lock.release() - - self.clear_expired_cookies() - - def _normalized_cookie_tuples(self, attrs_set): - """Return list of tuples containing normalised cookie information. - - attrs_set is the list of lists of key,value pairs extracted from - the Set-Cookie or Set-Cookie2 headers. - - Tuples are name, value, standard, rest, where name and value are the - cookie name and value, standard is a dictionary containing the standard - cookie-attributes (discard, secure, version, expires or max-age, - domain, path and port) and rest is a dictionary containing the rest of - the cookie-attributes. - - """ - cookie_tuples = [] - - boolean_attrs = "discard", "secure" - value_attrs = ("version", - "expires", "max-age", - "domain", "path", "port", - "comment", "commenturl") - - for cookie_attrs in attrs_set: - name, value = cookie_attrs[0] - - # Build dictionary of standard cookie-attributes (standard) and - # dictionary of other cookie-attributes (rest). - - # Note: expiry time is normalised to seconds since epoch. V0 - # cookies should have the Expires cookie-attribute, and V1 cookies - # should have Max-Age, but since V1 includes RFC 2109 cookies (and - # since V0 cookies may be a mish-mash of Netscape and RFC 2109), we - # accept either (but prefer Max-Age). - max_age_set = False - - bad_cookie = False - - standard = {} - rest = {} - for k, v in cookie_attrs[1:]: - lc = k.lower() - # don't lose case distinction for unknown fields - if lc in value_attrs or lc in boolean_attrs: - k = lc - if k in boolean_attrs and v is None: - # boolean cookie-attribute is present, but has no value - # (like "discard", rather than "port=80") - v = True - if k in standard: - # only first value is significant - continue - if k == "domain": - if v is None: - _debug(" missing value for domain attribute") - bad_cookie = True - break - # RFC 2965 section 3.3.3 - v = v.lower() - if k == "expires": - if max_age_set: - # Prefer max-age to expires (like Mozilla) - continue - if v is None: - _debug(" missing or invalid value for expires " - "attribute: treating as session cookie") - continue - if k == "max-age": - max_age_set = True - try: - v = int(v) - except ValueError: - _debug(" missing or invalid (non-numeric) value for " - "max-age attribute") - bad_cookie = True - break - # convert RFC 2965 Max-Age to seconds since epoch - # XXX Strictly you're supposed to follow RFC 2616 - # age-calculation rules. Remember that zero Max-Age is a - # is a request to discard (old and new) cookie, though. - k = "expires" - v = self._now + v - if (k in value_attrs) or (k in boolean_attrs): - if (v is None and - k not in ("port", "comment", "commenturl")): - _debug(" missing value for %s attribute" % k) - bad_cookie = True - break - standard[k] = v - else: - rest[k] = v - - if bad_cookie: - continue - - cookie_tuples.append((name, value, standard, rest)) - - return cookie_tuples - - def _cookie_from_cookie_tuple(self, tup, request): - # standard is dict of standard cookie-attributes, rest is dict of the - # rest of them - name, value, standard, rest = tup - - domain = standard.get("domain", Absent) - path = standard.get("path", Absent) - port = standard.get("port", Absent) - expires = standard.get("expires", Absent) - - # set the easy defaults - version = standard.get("version", None) - if version is not None: - try: - version = int(version) - except ValueError: - return None # invalid version, ignore cookie - secure = standard.get("secure", False) - # (discard is also set if expires is Absent) - discard = standard.get("discard", False) - comment = standard.get("comment", None) - comment_url = standard.get("commenturl", None) - - # set default path - if path is not Absent and path != "": - path_specified = True - path = escape_path(path) - else: - path_specified = False - path = request_path(request) - i = path.rfind("/") - if i != -1: - if version == 0: - # Netscape spec parts company from reality here - path = path[:i] - else: - path = path[:i+1] - if len(path) == 0: path = "/" - - # set default domain - domain_specified = domain is not Absent - # but first we have to remember whether it starts with a dot - domain_initial_dot = False - if domain_specified: - domain_initial_dot = bool(domain.startswith(".")) - if domain is Absent: - req_host, erhn = eff_request_host(request) - domain = erhn - elif not domain.startswith("."): - domain = "."+domain - - # set default port - port_specified = False - if port is not Absent: - if port is None: - # Port attr present, but has no value: default to request port. - # Cookie should then only be sent back on that port. - port = request_port(request) - else: - port_specified = True - port = re.sub(r"\s+", "", port) - else: - # No port attr present. Cookie can be sent back on any port. - port = None - - # set default expires and discard - if expires is Absent: - expires = None - discard = True - elif expires <= self._now: - # Expiry date in past is request to delete cookie. This can't be - # in DefaultCookiePolicy, because can't delete cookies there. - try: - self.clear(domain, path, name) - except KeyError: - pass - _debug("Expiring cookie, domain='%s', path='%s', name='%s'", - domain, path, name) - return None - - return Cookie(version, - name, value, - port, port_specified, - domain, domain_specified, domain_initial_dot, - path, path_specified, - secure, - expires, - discard, - comment, - comment_url, - rest) - - def _cookies_from_attrs_set(self, attrs_set, request): - cookie_tuples = self._normalized_cookie_tuples(attrs_set) - - cookies = [] - for tup in cookie_tuples: - cookie = self._cookie_from_cookie_tuple(tup, request) - if cookie: cookies.append(cookie) - return cookies - - def _process_rfc2109_cookies(self, cookies): - rfc2109_as_ns = getattr(self._policy, 'rfc2109_as_netscape', None) - if rfc2109_as_ns is None: - rfc2109_as_ns = not self._policy.rfc2965 - for cookie in cookies: - if cookie.version == 1: - cookie.rfc2109 = True - if rfc2109_as_ns: - # treat 2109 cookies as Netscape cookies rather than - # as RFC2965 cookies - cookie.version = 0 - - def make_cookies(self, response, request): - """Return sequence of Cookie objects extracted from response object.""" - # get cookie-attributes for RFC 2965 and Netscape protocols - headers = response.info() - rfc2965_hdrs = headers.get_all("Set-Cookie2", []) - ns_hdrs = headers.get_all("Set-Cookie", []) - - rfc2965 = self._policy.rfc2965 - netscape = self._policy.netscape - - if ((not rfc2965_hdrs and not ns_hdrs) or - (not ns_hdrs and not rfc2965) or - (not rfc2965_hdrs and not netscape) or - (not netscape and not rfc2965)): - return [] # no relevant cookie headers: quick exit - - try: - cookies = self._cookies_from_attrs_set( - split_header_words(rfc2965_hdrs), request) - except Exception: - _warn_unhandled_exception() - cookies = [] - - if ns_hdrs and netscape: - try: - # RFC 2109 and Netscape cookies - ns_cookies = self._cookies_from_attrs_set( - parse_ns_headers(ns_hdrs), request) - except Exception: - _warn_unhandled_exception() - ns_cookies = [] - self._process_rfc2109_cookies(ns_cookies) - - # Look for Netscape cookies (from Set-Cookie headers) that match - # corresponding RFC 2965 cookies (from Set-Cookie2 headers). - # For each match, keep the RFC 2965 cookie and ignore the Netscape - # cookie (RFC 2965 section 9.1). Actually, RFC 2109 cookies are - # bundled in with the Netscape cookies for this purpose, which is - # reasonable behaviour. - if rfc2965: - lookup = {} - for cookie in cookies: - lookup[(cookie.domain, cookie.path, cookie.name)] = None - - def no_matching_rfc2965(ns_cookie, lookup=lookup): - key = ns_cookie.domain, ns_cookie.path, ns_cookie.name - return key not in lookup - ns_cookies = filter(no_matching_rfc2965, ns_cookies) - - if ns_cookies: - cookies.extend(ns_cookies) - - return cookies - - def set_cookie_if_ok(self, cookie, request): - """Set a cookie if policy says it's OK to do so.""" - self._cookies_lock.acquire() - try: - self._policy._now = self._now = int(time.time()) - - if self._policy.set_ok(cookie, request): - self.set_cookie(cookie) - - - finally: - self._cookies_lock.release() - - def set_cookie(self, cookie): - """Set a cookie, without checking whether or not it should be set.""" - c = self._cookies - self._cookies_lock.acquire() - try: - if cookie.domain not in c: c[cookie.domain] = {} - c2 = c[cookie.domain] - if cookie.path not in c2: c2[cookie.path] = {} - c3 = c2[cookie.path] - c3[cookie.name] = cookie - finally: - self._cookies_lock.release() - - def extract_cookies(self, response, request): - """Extract cookies from response, where allowable given the request.""" - _debug("extract_cookies: %s", response.info()) - self._cookies_lock.acquire() - try: - self._policy._now = self._now = int(time.time()) - - for cookie in self.make_cookies(response, request): - if self._policy.set_ok(cookie, request): - _debug(" setting cookie: %s", cookie) - self.set_cookie(cookie) - finally: - self._cookies_lock.release() - - def clear(self, domain=None, path=None, name=None): - """Clear some cookies. - - Invoking this method without arguments will clear all cookies. If - given a single argument, only cookies belonging to that domain will be - removed. If given two arguments, cookies belonging to the specified - path within that domain are removed. If given three arguments, then - the cookie with the specified name, path and domain is removed. - - Raises KeyError if no matching cookie exists. - - """ - if name is not None: - if (domain is None) or (path is None): - raise ValueError( - "domain and path must be given to remove a cookie by name") - del self._cookies[domain][path][name] - elif path is not None: - if domain is None: - raise ValueError( - "domain must be given to remove cookies by path") - del self._cookies[domain][path] - elif domain is not None: - del self._cookies[domain] - else: - self._cookies = {} - - def clear_session_cookies(self): - """Discard all session cookies. - - Note that the .save() method won't save session cookies anyway, unless - you ask otherwise by passing a true ignore_discard argument. - - """ - self._cookies_lock.acquire() - try: - for cookie in self: - if cookie.discard: - self.clear(cookie.domain, cookie.path, cookie.name) - finally: - self._cookies_lock.release() - - def clear_expired_cookies(self): - """Discard all expired cookies. - - You probably don't need to call this method: expired cookies are never - sent back to the server (provided you're using DefaultCookiePolicy), - this method is called by CookieJar itself every so often, and the - .save() method won't save expired cookies anyway (unless you ask - otherwise by passing a true ignore_expires argument). - - """ - self._cookies_lock.acquire() - try: - now = time.time() - for cookie in self: - if cookie.is_expired(now): - self.clear(cookie.domain, cookie.path, cookie.name) - finally: - self._cookies_lock.release() - - def __iter__(self): - return deepvalues(self._cookies) - - def __len__(self): - """Return number of contained cookies.""" - i = 0 - for cookie in self: i = i + 1 - return i - - @as_native_str() - def __repr__(self): - r = [] - for cookie in self: r.append(repr(cookie)) - return "<%s[%s]>" % (self.__class__, ", ".join(r)) - - def __str__(self): - r = [] - for cookie in self: r.append(str(cookie)) - return "<%s[%s]>" % (self.__class__, ", ".join(r)) - - -# derives from IOError for backwards-compatibility with Python 2.4.0 -class LoadError(IOError): pass - -class FileCookieJar(CookieJar): - """CookieJar that can be loaded from and saved to a file.""" - - def __init__(self, filename=None, delayload=False, policy=None): - """ - Cookies are NOT loaded from the named file until either the .load() or - .revert() method is called. - - """ - CookieJar.__init__(self, policy) - if filename is not None: - try: - filename+"" - except: - raise ValueError("filename must be string-like") - self.filename = filename - self.delayload = bool(delayload) - - def save(self, filename=None, ignore_discard=False, ignore_expires=False): - """Save cookies to a file.""" - raise NotImplementedError() - - def load(self, filename=None, ignore_discard=False, ignore_expires=False): - """Load cookies from a file.""" - if filename is None: - if self.filename is not None: filename = self.filename - else: raise ValueError(MISSING_FILENAME_TEXT) - - f = open(filename) - try: - self._really_load(f, filename, ignore_discard, ignore_expires) - finally: - f.close() - - def revert(self, filename=None, - ignore_discard=False, ignore_expires=False): - """Clear all cookies and reload cookies from a saved file. - - Raises LoadError (or IOError) if reversion is not successful; the - object's state will not be altered if this happens. - - """ - if filename is None: - if self.filename is not None: filename = self.filename - else: raise ValueError(MISSING_FILENAME_TEXT) - - self._cookies_lock.acquire() - try: - - old_state = copy.deepcopy(self._cookies) - self._cookies = {} - try: - self.load(filename, ignore_discard, ignore_expires) - except (LoadError, IOError): - self._cookies = old_state - raise - - finally: - self._cookies_lock.release() - - -def lwp_cookie_str(cookie): - """Return string representation of Cookie in an the LWP cookie file format. - - Actually, the format is extended a bit -- see module docstring. - - """ - h = [(cookie.name, cookie.value), - ("path", cookie.path), - ("domain", cookie.domain)] - if cookie.port is not None: h.append(("port", cookie.port)) - if cookie.path_specified: h.append(("path_spec", None)) - if cookie.port_specified: h.append(("port_spec", None)) - if cookie.domain_initial_dot: h.append(("domain_dot", None)) - if cookie.secure: h.append(("secure", None)) - if cookie.expires: h.append(("expires", - time2isoz(float(cookie.expires)))) - if cookie.discard: h.append(("discard", None)) - if cookie.comment: h.append(("comment", cookie.comment)) - if cookie.comment_url: h.append(("commenturl", cookie.comment_url)) - - keys = sorted(cookie._rest.keys()) - for k in keys: - h.append((k, str(cookie._rest[k]))) - - h.append(("version", str(cookie.version))) - - return join_header_words([h]) - -class LWPCookieJar(FileCookieJar): - """ - The LWPCookieJar saves a sequence of "Set-Cookie3" lines. - "Set-Cookie3" is the format used by the libwww-perl libary, not known - to be compatible with any browser, but which is easy to read and - doesn't lose information about RFC 2965 cookies. - - Additional methods - - as_lwp_str(ignore_discard=True, ignore_expired=True) - - """ - - def as_lwp_str(self, ignore_discard=True, ignore_expires=True): - """Return cookies as a string of "\\n"-separated "Set-Cookie3" headers. - - ignore_discard and ignore_expires: see docstring for FileCookieJar.save - - """ - now = time.time() - r = [] - for cookie in self: - if not ignore_discard and cookie.discard: - continue - if not ignore_expires and cookie.is_expired(now): - continue - r.append("Set-Cookie3: %s" % lwp_cookie_str(cookie)) - return "\n".join(r+[""]) - - def save(self, filename=None, ignore_discard=False, ignore_expires=False): - if filename is None: - if self.filename is not None: filename = self.filename - else: raise ValueError(MISSING_FILENAME_TEXT) - - f = open(filename, "w") - try: - # There really isn't an LWP Cookies 2.0 format, but this indicates - # that there is extra information in here (domain_dot and - # port_spec) while still being compatible with libwww-perl, I hope. - f.write("#LWP-Cookies-2.0\n") - f.write(self.as_lwp_str(ignore_discard, ignore_expires)) - finally: - f.close() - - def _really_load(self, f, filename, ignore_discard, ignore_expires): - magic = f.readline() - if not self.magic_re.search(magic): - msg = ("%r does not look like a Set-Cookie3 (LWP) format " - "file" % filename) - raise LoadError(msg) - - now = time.time() - - header = "Set-Cookie3:" - boolean_attrs = ("port_spec", "path_spec", "domain_dot", - "secure", "discard") - value_attrs = ("version", - "port", "path", "domain", - "expires", - "comment", "commenturl") - - try: - while 1: - line = f.readline() - if line == "": break - if not line.startswith(header): - continue - line = line[len(header):].strip() - - for data in split_header_words([line]): - name, value = data[0] - standard = {} - rest = {} - for k in boolean_attrs: - standard[k] = False - for k, v in data[1:]: - if k is not None: - lc = k.lower() - else: - lc = None - # don't lose case distinction for unknown fields - if (lc in value_attrs) or (lc in boolean_attrs): - k = lc - if k in boolean_attrs: - if v is None: v = True - standard[k] = v - elif k in value_attrs: - standard[k] = v - else: - rest[k] = v - - h = standard.get - expires = h("expires") - discard = h("discard") - if expires is not None: - expires = iso2time(expires) - if expires is None: - discard = True - domain = h("domain") - domain_specified = domain.startswith(".") - c = Cookie(h("version"), name, value, - h("port"), h("port_spec"), - domain, domain_specified, h("domain_dot"), - h("path"), h("path_spec"), - h("secure"), - expires, - discard, - h("comment"), - h("commenturl"), - rest) - if not ignore_discard and c.discard: - continue - if not ignore_expires and c.is_expired(now): - continue - self.set_cookie(c) - - except IOError: - raise - except Exception: - _warn_unhandled_exception() - raise LoadError("invalid Set-Cookie3 format file %r: %r" % - (filename, line)) - - -class MozillaCookieJar(FileCookieJar): - """ - - WARNING: you may want to backup your browser's cookies file if you use - this class to save cookies. I *think* it works, but there have been - bugs in the past! - - This class differs from CookieJar only in the format it uses to save and - load cookies to and from a file. This class uses the Mozilla/Netscape - `cookies.txt' format. lynx uses this file format, too. - - Don't expect cookies saved while the browser is running to be noticed by - the browser (in fact, Mozilla on unix will overwrite your saved cookies if - you change them on disk while it's running; on Windows, you probably can't - save at all while the browser is running). - - Note that the Mozilla/Netscape format will downgrade RFC2965 cookies to - Netscape cookies on saving. - - In particular, the cookie version and port number information is lost, - together with information about whether or not Path, Port and Discard were - specified by the Set-Cookie2 (or Set-Cookie) header, and whether or not the - domain as set in the HTTP header started with a dot (yes, I'm aware some - domains in Netscape files start with a dot and some don't -- trust me, you - really don't want to know any more about this). - - Note that though Mozilla and Netscape use the same format, they use - slightly different headers. The class saves cookies using the Netscape - header by default (Mozilla can cope with that). - - """ - magic_re = re.compile("#( Netscape)? HTTP Cookie File") - header = """\ -# Netscape HTTP Cookie File -# http://www.netscape.com/newsref/std/cookie_spec.html -# This is a generated file! Do not edit. - -""" - - def _really_load(self, f, filename, ignore_discard, ignore_expires): - now = time.time() - - magic = f.readline() - if not self.magic_re.search(magic): - f.close() - raise LoadError( - "%r does not look like a Netscape format cookies file" % - filename) - - try: - while 1: - line = f.readline() - if line == "": break - - # last field may be absent, so keep any trailing tab - if line.endswith("\n"): line = line[:-1] - - # skip comments and blank lines XXX what is $ for? - if (line.strip().startswith(("#", "$")) or - line.strip() == ""): - continue - - domain, domain_specified, path, secure, expires, name, value = \ - line.split("\t") - secure = (secure == "TRUE") - domain_specified = (domain_specified == "TRUE") - if name == "": - # cookies.txt regards 'Set-Cookie: foo' as a cookie - # with no name, whereas http.cookiejar regards it as a - # cookie with no value. - name = value - value = None - - initial_dot = domain.startswith(".") - assert domain_specified == initial_dot - - discard = False - if expires == "": - expires = None - discard = True - - # assume path_specified is false - c = Cookie(0, name, value, - None, False, - domain, domain_specified, initial_dot, - path, False, - secure, - expires, - discard, - None, - None, - {}) - if not ignore_discard and c.discard: - continue - if not ignore_expires and c.is_expired(now): - continue - self.set_cookie(c) - - except IOError: - raise - except Exception: - _warn_unhandled_exception() - raise LoadError("invalid Netscape format cookies file %r: %r" % - (filename, line)) - - def save(self, filename=None, ignore_discard=False, ignore_expires=False): - if filename is None: - if self.filename is not None: filename = self.filename - else: raise ValueError(MISSING_FILENAME_TEXT) - - f = open(filename, "w") - try: - f.write(self.header) - now = time.time() - for cookie in self: - if not ignore_discard and cookie.discard: - continue - if not ignore_expires and cookie.is_expired(now): - continue - if cookie.secure: secure = "TRUE" - else: secure = "FALSE" - if cookie.domain.startswith("."): initial_dot = "TRUE" - else: initial_dot = "FALSE" - if cookie.expires is not None: - expires = str(cookie.expires) - else: - expires = "" - if cookie.value is None: - # cookies.txt regards 'Set-Cookie: foo' as a cookie - # with no name, whereas http.cookiejar regards it as a - # cookie with no value. - name = "" - value = cookie.name - else: - name = cookie.name - value = cookie.value - f.write( - "\t".join([cookie.domain, initial_dot, cookie.path, - secure, expires, name, value])+ - "\n") - finally: - f.close() diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/cookies.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/cookies.py deleted file mode 100644 index 8bb61e22..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/cookies.py +++ /dev/null @@ -1,598 +0,0 @@ -#### -# Copyright 2000 by Timothy O'Malley -# -# All Rights Reserved -# -# Permission to use, copy, modify, and distribute this software -# and its documentation for any purpose and without fee is hereby -# granted, provided that the above copyright notice appear in all -# copies and that both that copyright notice and this permission -# notice appear in supporting documentation, and that the name of -# Timothy O'Malley not be used in advertising or publicity -# pertaining to distribution of the software without specific, written -# prior permission. -# -# Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS -# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -# AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR -# ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS -# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -# PERFORMANCE OF THIS SOFTWARE. -# -#### -# -# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp -# by Timothy O'Malley -# -# Cookie.py is a Python module for the handling of HTTP -# cookies as a Python dictionary. See RFC 2109 for more -# information on cookies. -# -# The original idea to treat Cookies as a dictionary came from -# Dave Mitchell (davem@magnet.com) in 1995, when he released the -# first version of nscookie.py. -# -#### - -r""" -http.cookies module ported to python-future from Py3.3 - -Here's a sample session to show how to use this module. -At the moment, this is the only documentation. - -The Basics ----------- - -Importing is easy... - - >>> from http import cookies - -Most of the time you start by creating a cookie. - - >>> C = cookies.SimpleCookie() - -Once you've created your Cookie, you can add values just as if it were -a dictionary. - - >>> C = cookies.SimpleCookie() - >>> C["fig"] = "newton" - >>> C["sugar"] = "wafer" - >>> C.output() - 'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer' - -Notice that the printable representation of a Cookie is the -appropriate format for a Set-Cookie: header. This is the -default behavior. You can change the header and printed -attributes by using the .output() function - - >>> C = cookies.SimpleCookie() - >>> C["rocky"] = "road" - >>> C["rocky"]["path"] = "/cookie" - >>> print(C.output(header="Cookie:")) - Cookie: rocky=road; Path=/cookie - >>> print(C.output(attrs=[], header="Cookie:")) - Cookie: rocky=road - -The load() method of a Cookie extracts cookies from a string. In a -CGI script, you would use this method to extract the cookies from the -HTTP_COOKIE environment variable. - - >>> C = cookies.SimpleCookie() - >>> C.load("chips=ahoy; vienna=finger") - >>> C.output() - 'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger' - -The load() method is darn-tootin smart about identifying cookies -within a string. Escaped quotation marks, nested semicolons, and other -such trickeries do not confuse it. - - >>> C = cookies.SimpleCookie() - >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";') - >>> print(C) - Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;" - -Each element of the Cookie also supports all of the RFC 2109 -Cookie attributes. Here's an example which sets the Path -attribute. - - >>> C = cookies.SimpleCookie() - >>> C["oreo"] = "doublestuff" - >>> C["oreo"]["path"] = "/" - >>> print(C) - Set-Cookie: oreo=doublestuff; Path=/ - -Each dictionary element has a 'value' attribute, which gives you -back the value associated with the key. - - >>> C = cookies.SimpleCookie() - >>> C["twix"] = "none for you" - >>> C["twix"].value - 'none for you' - -The SimpleCookie expects that all values should be standard strings. -Just to be sure, SimpleCookie invokes the str() builtin to convert -the value to a string, when the values are set dictionary-style. - - >>> C = cookies.SimpleCookie() - >>> C["number"] = 7 - >>> C["string"] = "seven" - >>> C["number"].value - '7' - >>> C["string"].value - 'seven' - >>> C.output() - 'Set-Cookie: number=7\r\nSet-Cookie: string=seven' - -Finis. -""" -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import -from future.builtins import chr, dict, int, str -from future.utils import PY2, as_native_str - -# -# Import our required modules -# -import re -if PY2: - re.ASCII = 0 # for py2 compatibility -import string - -__all__ = ["CookieError", "BaseCookie", "SimpleCookie"] - -_nulljoin = ''.join -_semispacejoin = '; '.join -_spacejoin = ' '.join - -# -# Define an exception visible to External modules -# -class CookieError(Exception): - pass - - -# These quoting routines conform to the RFC2109 specification, which in -# turn references the character definitions from RFC2068. They provide -# a two-way quoting algorithm. Any non-text character is translated -# into a 4 character sequence: a forward-slash followed by the -# three-digit octal equivalent of the character. Any '\' or '"' is -# quoted with a preceeding '\' slash. -# -# These are taken from RFC2068 and RFC2109. -# _LegalChars is the list of chars which don't require "'s -# _Translator hash-table for fast quoting -# -_LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~:" -_Translator = { - '\000' : '\\000', '\001' : '\\001', '\002' : '\\002', - '\003' : '\\003', '\004' : '\\004', '\005' : '\\005', - '\006' : '\\006', '\007' : '\\007', '\010' : '\\010', - '\011' : '\\011', '\012' : '\\012', '\013' : '\\013', - '\014' : '\\014', '\015' : '\\015', '\016' : '\\016', - '\017' : '\\017', '\020' : '\\020', '\021' : '\\021', - '\022' : '\\022', '\023' : '\\023', '\024' : '\\024', - '\025' : '\\025', '\026' : '\\026', '\027' : '\\027', - '\030' : '\\030', '\031' : '\\031', '\032' : '\\032', - '\033' : '\\033', '\034' : '\\034', '\035' : '\\035', - '\036' : '\\036', '\037' : '\\037', - - # Because of the way browsers really handle cookies (as opposed - # to what the RFC says) we also encode , and ; - - ',' : '\\054', ';' : '\\073', - - '"' : '\\"', '\\' : '\\\\', - - '\177' : '\\177', '\200' : '\\200', '\201' : '\\201', - '\202' : '\\202', '\203' : '\\203', '\204' : '\\204', - '\205' : '\\205', '\206' : '\\206', '\207' : '\\207', - '\210' : '\\210', '\211' : '\\211', '\212' : '\\212', - '\213' : '\\213', '\214' : '\\214', '\215' : '\\215', - '\216' : '\\216', '\217' : '\\217', '\220' : '\\220', - '\221' : '\\221', '\222' : '\\222', '\223' : '\\223', - '\224' : '\\224', '\225' : '\\225', '\226' : '\\226', - '\227' : '\\227', '\230' : '\\230', '\231' : '\\231', - '\232' : '\\232', '\233' : '\\233', '\234' : '\\234', - '\235' : '\\235', '\236' : '\\236', '\237' : '\\237', - '\240' : '\\240', '\241' : '\\241', '\242' : '\\242', - '\243' : '\\243', '\244' : '\\244', '\245' : '\\245', - '\246' : '\\246', '\247' : '\\247', '\250' : '\\250', - '\251' : '\\251', '\252' : '\\252', '\253' : '\\253', - '\254' : '\\254', '\255' : '\\255', '\256' : '\\256', - '\257' : '\\257', '\260' : '\\260', '\261' : '\\261', - '\262' : '\\262', '\263' : '\\263', '\264' : '\\264', - '\265' : '\\265', '\266' : '\\266', '\267' : '\\267', - '\270' : '\\270', '\271' : '\\271', '\272' : '\\272', - '\273' : '\\273', '\274' : '\\274', '\275' : '\\275', - '\276' : '\\276', '\277' : '\\277', '\300' : '\\300', - '\301' : '\\301', '\302' : '\\302', '\303' : '\\303', - '\304' : '\\304', '\305' : '\\305', '\306' : '\\306', - '\307' : '\\307', '\310' : '\\310', '\311' : '\\311', - '\312' : '\\312', '\313' : '\\313', '\314' : '\\314', - '\315' : '\\315', '\316' : '\\316', '\317' : '\\317', - '\320' : '\\320', '\321' : '\\321', '\322' : '\\322', - '\323' : '\\323', '\324' : '\\324', '\325' : '\\325', - '\326' : '\\326', '\327' : '\\327', '\330' : '\\330', - '\331' : '\\331', '\332' : '\\332', '\333' : '\\333', - '\334' : '\\334', '\335' : '\\335', '\336' : '\\336', - '\337' : '\\337', '\340' : '\\340', '\341' : '\\341', - '\342' : '\\342', '\343' : '\\343', '\344' : '\\344', - '\345' : '\\345', '\346' : '\\346', '\347' : '\\347', - '\350' : '\\350', '\351' : '\\351', '\352' : '\\352', - '\353' : '\\353', '\354' : '\\354', '\355' : '\\355', - '\356' : '\\356', '\357' : '\\357', '\360' : '\\360', - '\361' : '\\361', '\362' : '\\362', '\363' : '\\363', - '\364' : '\\364', '\365' : '\\365', '\366' : '\\366', - '\367' : '\\367', '\370' : '\\370', '\371' : '\\371', - '\372' : '\\372', '\373' : '\\373', '\374' : '\\374', - '\375' : '\\375', '\376' : '\\376', '\377' : '\\377' - } - -def _quote(str, LegalChars=_LegalChars): - r"""Quote a string for use in a cookie header. - - If the string does not need to be double-quoted, then just return the - string. Otherwise, surround the string in doublequotes and quote - (with a \) special characters. - """ - if all(c in LegalChars for c in str): - return str - else: - return '"' + _nulljoin(_Translator.get(s, s) for s in str) + '"' - - -_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]") -_QuotePatt = re.compile(r"[\\].") - -def _unquote(mystr): - # If there aren't any doublequotes, - # then there can't be any special characters. See RFC 2109. - if len(mystr) < 2: - return mystr - if mystr[0] != '"' or mystr[-1] != '"': - return mystr - - # We have to assume that we must decode this string. - # Down to work. - - # Remove the "s - mystr = mystr[1:-1] - - # Check for special sequences. Examples: - # \012 --> \n - # \" --> " - # - i = 0 - n = len(mystr) - res = [] - while 0 <= i < n: - o_match = _OctalPatt.search(mystr, i) - q_match = _QuotePatt.search(mystr, i) - if not o_match and not q_match: # Neither matched - res.append(mystr[i:]) - break - # else: - j = k = -1 - if o_match: - j = o_match.start(0) - if q_match: - k = q_match.start(0) - if q_match and (not o_match or k < j): # QuotePatt matched - res.append(mystr[i:k]) - res.append(mystr[k+1]) - i = k + 2 - else: # OctalPatt matched - res.append(mystr[i:j]) - res.append(chr(int(mystr[j+1:j+4], 8))) - i = j + 4 - return _nulljoin(res) - -# The _getdate() routine is used to set the expiration time in the cookie's HTTP -# header. By default, _getdate() returns the current time in the appropriate -# "expires" format for a Set-Cookie header. The one optional argument is an -# offset from now, in seconds. For example, an offset of -3600 means "one hour -# ago". The offset may be a floating point number. -# - -_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] - -_monthname = [None, - 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] - -def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname): - from time import gmtime, time - now = time() - year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future) - return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % \ - (weekdayname[wd], day, monthname[month], year, hh, mm, ss) - - -class Morsel(dict): - """A class to hold ONE (key, value) pair. - - In a cookie, each such pair may have several attributes, so this class is - used to keep the attributes associated with the appropriate key,value pair. - This class also includes a coded_value attribute, which is used to hold - the network representation of the value. This is most useful when Python - objects are pickled for network transit. - """ - # RFC 2109 lists these attributes as reserved: - # path comment domain - # max-age secure version - # - # For historical reasons, these attributes are also reserved: - # expires - # - # This is an extension from Microsoft: - # httponly - # - # This dictionary provides a mapping from the lowercase - # variant on the left to the appropriate traditional - # formatting on the right. - _reserved = { - "expires" : "expires", - "path" : "Path", - "comment" : "Comment", - "domain" : "Domain", - "max-age" : "Max-Age", - "secure" : "secure", - "httponly" : "httponly", - "version" : "Version", - } - - _flags = set(['secure', 'httponly']) - - def __init__(self): - # Set defaults - self.key = self.value = self.coded_value = None - - # Set default attributes - for key in self._reserved: - dict.__setitem__(self, key, "") - - def __setitem__(self, K, V): - K = K.lower() - if not K in self._reserved: - raise CookieError("Invalid Attribute %s" % K) - dict.__setitem__(self, K, V) - - def isReservedKey(self, K): - return K.lower() in self._reserved - - def set(self, key, val, coded_val, LegalChars=_LegalChars): - # First we verify that the key isn't a reserved word - # Second we make sure it only contains legal characters - if key.lower() in self._reserved: - raise CookieError("Attempt to set a reserved key: %s" % key) - if any(c not in LegalChars for c in key): - raise CookieError("Illegal key value: %s" % key) - - # It's a good key, so save it. - self.key = key - self.value = val - self.coded_value = coded_val - - def output(self, attrs=None, header="Set-Cookie:"): - return "%s %s" % (header, self.OutputString(attrs)) - - __str__ = output - - @as_native_str() - def __repr__(self): - if PY2 and isinstance(self.value, unicode): - val = str(self.value) # make it a newstr to remove the u prefix - else: - val = self.value - return '<%s: %s=%s>' % (self.__class__.__name__, - str(self.key), repr(val)) - - def js_output(self, attrs=None): - # Print javascript - return """ - - """ % (self.OutputString(attrs).replace('"', r'\"')) - - def OutputString(self, attrs=None): - # Build up our result - # - result = [] - append = result.append - - # First, the key=value pair - append("%s=%s" % (self.key, self.coded_value)) - - # Now add any defined attributes - if attrs is None: - attrs = self._reserved - items = sorted(self.items()) - for key, value in items: - if value == "": - continue - if key not in attrs: - continue - if key == "expires" and isinstance(value, int): - append("%s=%s" % (self._reserved[key], _getdate(value))) - elif key == "max-age" and isinstance(value, int): - append("%s=%d" % (self._reserved[key], value)) - elif key == "secure": - append(str(self._reserved[key])) - elif key == "httponly": - append(str(self._reserved[key])) - else: - append("%s=%s" % (self._reserved[key], value)) - - # Return the result - return _semispacejoin(result) - - -# -# Pattern for finding cookie -# -# This used to be strict parsing based on the RFC2109 and RFC2068 -# specifications. I have since discovered that MSIE 3.0x doesn't -# follow the character rules outlined in those specs. As a -# result, the parsing rules here are less strict. -# - -_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]" -_CookiePattern = re.compile(r""" - (?x) # This is a verbose pattern - (?P # Start of group 'key' - """ + _LegalCharsPatt + r"""+? # Any word of at least one letter - ) # End of group 'key' - ( # Optional group: there may not be a value. - \s*=\s* # Equal Sign - (?P # Start of group 'val' - "(?:[^\\"]|\\.)*" # Any doublequoted string - | # or - \w{3},\s[\w\d\s-]{9,11}\s[\d:]{8}\sGMT # Special case for "expires" attr - | # or - """ + _LegalCharsPatt + r"""* # Any word or empty string - ) # End of group 'val' - )? # End of optional value group - \s* # Any number of spaces. - (\s+|;|$) # Ending either at space, semicolon, or EOS. - """, re.ASCII) # May be removed if safe. - - -# At long last, here is the cookie class. Using this class is almost just like -# using a dictionary. See this module's docstring for example usage. -# -class BaseCookie(dict): - """A container class for a set of Morsels.""" - - def value_decode(self, val): - """real_value, coded_value = value_decode(STRING) - Called prior to setting a cookie's value from the network - representation. The VALUE is the value read from HTTP - header. - Override this function to modify the behavior of cookies. - """ - return val, val - - def value_encode(self, val): - """real_value, coded_value = value_encode(VALUE) - Called prior to setting a cookie's value from the dictionary - representation. The VALUE is the value being assigned. - Override this function to modify the behavior of cookies. - """ - strval = str(val) - return strval, strval - - def __init__(self, input=None): - if input: - self.load(input) - - def __set(self, key, real_value, coded_value): - """Private method for setting a cookie's value""" - M = self.get(key, Morsel()) - M.set(key, real_value, coded_value) - dict.__setitem__(self, key, M) - - def __setitem__(self, key, value): - """Dictionary style assignment.""" - rval, cval = self.value_encode(value) - self.__set(key, rval, cval) - - def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"): - """Return a string suitable for HTTP.""" - result = [] - items = sorted(self.items()) - for key, value in items: - result.append(value.output(attrs, header)) - return sep.join(result) - - __str__ = output - - @as_native_str() - def __repr__(self): - l = [] - items = sorted(self.items()) - for key, value in items: - if PY2 and isinstance(value.value, unicode): - val = str(value.value) # make it a newstr to remove the u prefix - else: - val = value.value - l.append('%s=%s' % (str(key), repr(val))) - return '<%s: %s>' % (self.__class__.__name__, _spacejoin(l)) - - def js_output(self, attrs=None): - """Return a string suitable for JavaScript.""" - result = [] - items = sorted(self.items()) - for key, value in items: - result.append(value.js_output(attrs)) - return _nulljoin(result) - - def load(self, rawdata): - """Load cookies from a string (presumably HTTP_COOKIE) or - from a dictionary. Loading cookies from a dictionary 'd' - is equivalent to calling: - map(Cookie.__setitem__, d.keys(), d.values()) - """ - if isinstance(rawdata, str): - self.__parse_string(rawdata) - else: - # self.update() wouldn't call our custom __setitem__ - for key, value in rawdata.items(): - self[key] = value - return - - def __parse_string(self, mystr, patt=_CookiePattern): - i = 0 # Our starting point - n = len(mystr) # Length of string - M = None # current morsel - - while 0 <= i < n: - # Start looking for a cookie - match = patt.search(mystr, i) - if not match: - # No more cookies - break - - key, value = match.group("key"), match.group("val") - - i = match.end(0) - - # Parse the key, value in case it's metainfo - if key[0] == "$": - # We ignore attributes which pertain to the cookie - # mechanism as a whole. See RFC 2109. - # (Does anyone care?) - if M: - M[key[1:]] = value - elif key.lower() in Morsel._reserved: - if M: - if value is None: - if key.lower() in Morsel._flags: - M[key] = True - else: - M[key] = _unquote(value) - elif value is not None: - rval, cval = self.value_decode(value) - self.__set(key, rval, cval) - M = self[key] - - -class SimpleCookie(BaseCookie): - """ - SimpleCookie supports strings as cookie values. When setting - the value using the dictionary assignment notation, SimpleCookie - calls the builtin str() to convert the value to a string. Values - received from HTTP are kept as strings. - """ - def value_decode(self, val): - return _unquote(val), val - - def value_encode(self, val): - strval = str(val) - return strval, _quote(strval) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/server.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/server.py deleted file mode 100644 index b1c11e0c..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/http/server.py +++ /dev/null @@ -1,1226 +0,0 @@ -"""HTTP server classes. - -From Python 3.3 - -Note: BaseHTTPRequestHandler doesn't implement any HTTP request; see -SimpleHTTPRequestHandler for simple implementations of GET, HEAD and POST, -and CGIHTTPRequestHandler for CGI scripts. - -It does, however, optionally implement HTTP/1.1 persistent connections, -as of version 0.3. - -Notes on CGIHTTPRequestHandler ------------------------------- - -This class implements GET and POST requests to cgi-bin scripts. - -If the os.fork() function is not present (e.g. on Windows), -subprocess.Popen() is used as a fallback, with slightly altered semantics. - -In all cases, the implementation is intentionally naive -- all -requests are executed synchronously. - -SECURITY WARNING: DON'T USE THIS CODE UNLESS YOU ARE INSIDE A FIREWALL --- it may execute arbitrary Python code or external programs. - -Note that status code 200 is sent prior to execution of a CGI script, so -scripts cannot send other status codes such as 302 (redirect). - -XXX To do: - -- log requests even later (to capture byte count) -- log user-agent header and other interesting goodies -- send error log to separate file -""" - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future import utils -from future.builtins import * - - -# See also: -# -# HTTP Working Group T. Berners-Lee -# INTERNET-DRAFT R. T. Fielding -# H. Frystyk Nielsen -# Expires September 8, 1995 March 8, 1995 -# -# URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt -# -# and -# -# Network Working Group R. Fielding -# Request for Comments: 2616 et al -# Obsoletes: 2068 June 1999 -# Category: Standards Track -# -# URL: http://www.faqs.org/rfcs/rfc2616.html - -# Log files -# --------- -# -# Here's a quote from the NCSA httpd docs about log file format. -# -# | The logfile format is as follows. Each line consists of: -# | -# | host rfc931 authuser [DD/Mon/YYYY:hh:mm:ss] "request" ddd bbbb -# | -# | host: Either the DNS name or the IP number of the remote client -# | rfc931: Any information returned by identd for this person, -# | - otherwise. -# | authuser: If user sent a userid for authentication, the user name, -# | - otherwise. -# | DD: Day -# | Mon: Month (calendar name) -# | YYYY: Year -# | hh: hour (24-hour format, the machine's timezone) -# | mm: minutes -# | ss: seconds -# | request: The first line of the HTTP request as sent by the client. -# | ddd: the status code returned by the server, - if not available. -# | bbbb: the total number of bytes sent, -# | *not including the HTTP/1.0 header*, - if not available -# | -# | You can determine the name of the file accessed through request. -# -# (Actually, the latter is only true if you know the server configuration -# at the time the request was made!) - -__version__ = "0.6" - -__all__ = ["HTTPServer", "BaseHTTPRequestHandler"] - -from future.backports import html -from future.backports.http import client as http_client -from future.backports.urllib import parse as urllib_parse -from future.backports import socketserver - -import io -import mimetypes -import os -import posixpath -import select -import shutil -import socket # For gethostbyaddr() -import sys -import time -import copy -import argparse - - -# Default error message template -DEFAULT_ERROR_MESSAGE = """\ - - - - - Error response - - -

Error response

-

Error code: %(code)d

-

Message: %(message)s.

-

Error code explanation: %(code)s - %(explain)s.

- - -""" - -DEFAULT_ERROR_CONTENT_TYPE = "text/html;charset=utf-8" - -def _quote_html(html): - return html.replace("&", "&").replace("<", "<").replace(">", ">") - -class HTTPServer(socketserver.TCPServer): - - allow_reuse_address = 1 # Seems to make sense in testing environment - - def server_bind(self): - """Override server_bind to store the server name.""" - socketserver.TCPServer.server_bind(self) - host, port = self.socket.getsockname()[:2] - self.server_name = socket.getfqdn(host) - self.server_port = port - - -class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): - - """HTTP request handler base class. - - The following explanation of HTTP serves to guide you through the - code as well as to expose any misunderstandings I may have about - HTTP (so you don't need to read the code to figure out I'm wrong - :-). - - HTTP (HyperText Transfer Protocol) is an extensible protocol on - top of a reliable stream transport (e.g. TCP/IP). The protocol - recognizes three parts to a request: - - 1. One line identifying the request type and path - 2. An optional set of RFC-822-style headers - 3. An optional data part - - The headers and data are separated by a blank line. - - The first line of the request has the form - - - - where is a (case-sensitive) keyword such as GET or POST, - is a string containing path information for the request, - and should be the string "HTTP/1.0" or "HTTP/1.1". - is encoded using the URL encoding scheme (using %xx to signify - the ASCII character with hex code xx). - - The specification specifies that lines are separated by CRLF but - for compatibility with the widest range of clients recommends - servers also handle LF. Similarly, whitespace in the request line - is treated sensibly (allowing multiple spaces between components - and allowing trailing whitespace). - - Similarly, for output, lines ought to be separated by CRLF pairs - but most clients grok LF characters just fine. - - If the first line of the request has the form - - - - (i.e. is left out) then this is assumed to be an HTTP - 0.9 request; this form has no optional headers and data part and - the reply consists of just the data. - - The reply form of the HTTP 1.x protocol again has three parts: - - 1. One line giving the response code - 2. An optional set of RFC-822-style headers - 3. The data - - Again, the headers and data are separated by a blank line. - - The response code line has the form - - - - where is the protocol version ("HTTP/1.0" or "HTTP/1.1"), - is a 3-digit response code indicating success or - failure of the request, and is an optional - human-readable string explaining what the response code means. - - This server parses the request and the headers, and then calls a - function specific to the request type (). Specifically, - a request SPAM will be handled by a method do_SPAM(). If no - such method exists the server sends an error response to the - client. If it exists, it is called with no arguments: - - do_SPAM() - - Note that the request name is case sensitive (i.e. SPAM and spam - are different requests). - - The various request details are stored in instance variables: - - - client_address is the client IP address in the form (host, - port); - - - command, path and version are the broken-down request line; - - - headers is an instance of email.message.Message (or a derived - class) containing the header information; - - - rfile is a file object open for reading positioned at the - start of the optional input data part; - - - wfile is a file object open for writing. - - IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING! - - The first thing to be written must be the response line. Then - follow 0 or more header lines, then a blank line, and then the - actual data (if any). The meaning of the header lines depends on - the command executed by the server; in most cases, when data is - returned, there should be at least one header line of the form - - Content-type: / - - where and should be registered MIME types, - e.g. "text/html" or "text/plain". - - """ - - # The Python system version, truncated to its first component. - sys_version = "Python/" + sys.version.split()[0] - - # The server software version. You may want to override this. - # The format is multiple whitespace-separated strings, - # where each string is of the form name[/version]. - server_version = "BaseHTTP/" + __version__ - - error_message_format = DEFAULT_ERROR_MESSAGE - error_content_type = DEFAULT_ERROR_CONTENT_TYPE - - # The default request version. This only affects responses up until - # the point where the request line is parsed, so it mainly decides what - # the client gets back when sending a malformed request line. - # Most web servers default to HTTP 0.9, i.e. don't send a status line. - default_request_version = "HTTP/0.9" - - def parse_request(self): - """Parse a request (internal). - - The request should be stored in self.raw_requestline; the results - are in self.command, self.path, self.request_version and - self.headers. - - Return True for success, False for failure; on failure, an - error is sent back. - - """ - self.command = None # set in case of error on the first line - self.request_version = version = self.default_request_version - self.close_connection = 1 - requestline = str(self.raw_requestline, 'iso-8859-1') - requestline = requestline.rstrip('\r\n') - self.requestline = requestline - words = requestline.split() - if len(words) == 3: - command, path, version = words - if version[:5] != 'HTTP/': - self.send_error(400, "Bad request version (%r)" % version) - return False - try: - base_version_number = version.split('/', 1)[1] - version_number = base_version_number.split(".") - # RFC 2145 section 3.1 says there can be only one "." and - # - major and minor numbers MUST be treated as - # separate integers; - # - HTTP/2.4 is a lower version than HTTP/2.13, which in - # turn is lower than HTTP/12.3; - # - Leading zeros MUST be ignored by recipients. - if len(version_number) != 2: - raise ValueError - version_number = int(version_number[0]), int(version_number[1]) - except (ValueError, IndexError): - self.send_error(400, "Bad request version (%r)" % version) - return False - if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1": - self.close_connection = 0 - if version_number >= (2, 0): - self.send_error(505, - "Invalid HTTP Version (%s)" % base_version_number) - return False - elif len(words) == 2: - command, path = words - self.close_connection = 1 - if command != 'GET': - self.send_error(400, - "Bad HTTP/0.9 request type (%r)" % command) - return False - elif not words: - return False - else: - self.send_error(400, "Bad request syntax (%r)" % requestline) - return False - self.command, self.path, self.request_version = command, path, version - - # Examine the headers and look for a Connection directive. - try: - self.headers = http_client.parse_headers(self.rfile, - _class=self.MessageClass) - except http_client.LineTooLong: - self.send_error(400, "Line too long") - return False - - conntype = self.headers.get('Connection', "") - if conntype.lower() == 'close': - self.close_connection = 1 - elif (conntype.lower() == 'keep-alive' and - self.protocol_version >= "HTTP/1.1"): - self.close_connection = 0 - # Examine the headers and look for an Expect directive - expect = self.headers.get('Expect', "") - if (expect.lower() == "100-continue" and - self.protocol_version >= "HTTP/1.1" and - self.request_version >= "HTTP/1.1"): - if not self.handle_expect_100(): - return False - return True - - def handle_expect_100(self): - """Decide what to do with an "Expect: 100-continue" header. - - If the client is expecting a 100 Continue response, we must - respond with either a 100 Continue or a final response before - waiting for the request body. The default is to always respond - with a 100 Continue. You can behave differently (for example, - reject unauthorized requests) by overriding this method. - - This method should either return True (possibly after sending - a 100 Continue response) or send an error response and return - False. - - """ - self.send_response_only(100) - self.flush_headers() - return True - - def handle_one_request(self): - """Handle a single HTTP request. - - You normally don't need to override this method; see the class - __doc__ string for information on how to handle specific HTTP - commands such as GET and POST. - - """ - try: - self.raw_requestline = self.rfile.readline(65537) - if len(self.raw_requestline) > 65536: - self.requestline = '' - self.request_version = '' - self.command = '' - self.send_error(414) - return - if not self.raw_requestline: - self.close_connection = 1 - return - if not self.parse_request(): - # An error code has been sent, just exit - return - mname = 'do_' + self.command - if not hasattr(self, mname): - self.send_error(501, "Unsupported method (%r)" % self.command) - return - method = getattr(self, mname) - method() - self.wfile.flush() #actually send the response if not already done. - except socket.timeout as e: - #a read or a write timed out. Discard this connection - self.log_error("Request timed out: %r", e) - self.close_connection = 1 - return - - def handle(self): - """Handle multiple requests if necessary.""" - self.close_connection = 1 - - self.handle_one_request() - while not self.close_connection: - self.handle_one_request() - - def send_error(self, code, message=None): - """Send and log an error reply. - - Arguments are the error code, and a detailed message. - The detailed message defaults to the short entry matching the - response code. - - This sends an error response (so it must be called before any - output has been generated), logs the error, and finally sends - a piece of HTML explaining the error to the user. - - """ - - try: - shortmsg, longmsg = self.responses[code] - except KeyError: - shortmsg, longmsg = '???', '???' - if message is None: - message = shortmsg - explain = longmsg - self.log_error("code %d, message %s", code, message) - # using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201) - content = (self.error_message_format % - {'code': code, 'message': _quote_html(message), 'explain': explain}) - self.send_response(code, message) - self.send_header("Content-Type", self.error_content_type) - self.send_header('Connection', 'close') - self.end_headers() - if self.command != 'HEAD' and code >= 200 and code not in (204, 304): - self.wfile.write(content.encode('UTF-8', 'replace')) - - def send_response(self, code, message=None): - """Add the response header to the headers buffer and log the - response code. - - Also send two standard headers with the server software - version and the current date. - - """ - self.log_request(code) - self.send_response_only(code, message) - self.send_header('Server', self.version_string()) - self.send_header('Date', self.date_time_string()) - - def send_response_only(self, code, message=None): - """Send the response header only.""" - if message is None: - if code in self.responses: - message = self.responses[code][0] - else: - message = '' - if self.request_version != 'HTTP/0.9': - if not hasattr(self, '_headers_buffer'): - self._headers_buffer = [] - self._headers_buffer.append(("%s %d %s\r\n" % - (self.protocol_version, code, message)).encode( - 'latin-1', 'strict')) - - def send_header(self, keyword, value): - """Send a MIME header to the headers buffer.""" - if self.request_version != 'HTTP/0.9': - if not hasattr(self, '_headers_buffer'): - self._headers_buffer = [] - self._headers_buffer.append( - ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict')) - - if keyword.lower() == 'connection': - if value.lower() == 'close': - self.close_connection = 1 - elif value.lower() == 'keep-alive': - self.close_connection = 0 - - def end_headers(self): - """Send the blank line ending the MIME headers.""" - if self.request_version != 'HTTP/0.9': - self._headers_buffer.append(b"\r\n") - self.flush_headers() - - def flush_headers(self): - if hasattr(self, '_headers_buffer'): - self.wfile.write(b"".join(self._headers_buffer)) - self._headers_buffer = [] - - def log_request(self, code='-', size='-'): - """Log an accepted request. - - This is called by send_response(). - - """ - - self.log_message('"%s" %s %s', - self.requestline, str(code), str(size)) - - def log_error(self, format, *args): - """Log an error. - - This is called when a request cannot be fulfilled. By - default it passes the message on to log_message(). - - Arguments are the same as for log_message(). - - XXX This should go to the separate error log. - - """ - - self.log_message(format, *args) - - def log_message(self, format, *args): - """Log an arbitrary message. - - This is used by all other logging functions. Override - it if you have specific logging wishes. - - The first argument, FORMAT, is a format string for the - message to be logged. If the format string contains - any % escapes requiring parameters, they should be - specified as subsequent arguments (it's just like - printf!). - - The client ip and current date/time are prefixed to - every message. - - """ - - sys.stderr.write("%s - - [%s] %s\n" % - (self.address_string(), - self.log_date_time_string(), - format%args)) - - def version_string(self): - """Return the server software version string.""" - return self.server_version + ' ' + self.sys_version - - def date_time_string(self, timestamp=None): - """Return the current date and time formatted for a message header.""" - if timestamp is None: - timestamp = time.time() - year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp) - s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( - self.weekdayname[wd], - day, self.monthname[month], year, - hh, mm, ss) - return s - - def log_date_time_string(self): - """Return the current time formatted for logging.""" - now = time.time() - year, month, day, hh, mm, ss, x, y, z = time.localtime(now) - s = "%02d/%3s/%04d %02d:%02d:%02d" % ( - day, self.monthname[month], year, hh, mm, ss) - return s - - weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] - - monthname = [None, - 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] - - def address_string(self): - """Return the client address.""" - - return self.client_address[0] - - # Essentially static class variables - - # The version of the HTTP protocol we support. - # Set this to HTTP/1.1 to enable automatic keepalive - protocol_version = "HTTP/1.0" - - # MessageClass used to parse headers - MessageClass = http_client.HTTPMessage - - # Table mapping response codes to messages; entries have the - # form {code: (shortmessage, longmessage)}. - # See RFC 2616 and 6585. - responses = { - 100: ('Continue', 'Request received, please continue'), - 101: ('Switching Protocols', - 'Switching to new protocol; obey Upgrade header'), - - 200: ('OK', 'Request fulfilled, document follows'), - 201: ('Created', 'Document created, URL follows'), - 202: ('Accepted', - 'Request accepted, processing continues off-line'), - 203: ('Non-Authoritative Information', 'Request fulfilled from cache'), - 204: ('No Content', 'Request fulfilled, nothing follows'), - 205: ('Reset Content', 'Clear input form for further input.'), - 206: ('Partial Content', 'Partial content follows.'), - - 300: ('Multiple Choices', - 'Object has several resources -- see URI list'), - 301: ('Moved Permanently', 'Object moved permanently -- see URI list'), - 302: ('Found', 'Object moved temporarily -- see URI list'), - 303: ('See Other', 'Object moved -- see Method and URL list'), - 304: ('Not Modified', - 'Document has not changed since given time'), - 305: ('Use Proxy', - 'You must use proxy specified in Location to access this ' - 'resource.'), - 307: ('Temporary Redirect', - 'Object moved temporarily -- see URI list'), - - 400: ('Bad Request', - 'Bad request syntax or unsupported method'), - 401: ('Unauthorized', - 'No permission -- see authorization schemes'), - 402: ('Payment Required', - 'No payment -- see charging schemes'), - 403: ('Forbidden', - 'Request forbidden -- authorization will not help'), - 404: ('Not Found', 'Nothing matches the given URI'), - 405: ('Method Not Allowed', - 'Specified method is invalid for this resource.'), - 406: ('Not Acceptable', 'URI not available in preferred format.'), - 407: ('Proxy Authentication Required', 'You must authenticate with ' - 'this proxy before proceeding.'), - 408: ('Request Timeout', 'Request timed out; try again later.'), - 409: ('Conflict', 'Request conflict.'), - 410: ('Gone', - 'URI no longer exists and has been permanently removed.'), - 411: ('Length Required', 'Client must specify Content-Length.'), - 412: ('Precondition Failed', 'Precondition in headers is false.'), - 413: ('Request Entity Too Large', 'Entity is too large.'), - 414: ('Request-URI Too Long', 'URI is too long.'), - 415: ('Unsupported Media Type', 'Entity body in unsupported format.'), - 416: ('Requested Range Not Satisfiable', - 'Cannot satisfy request range.'), - 417: ('Expectation Failed', - 'Expect condition could not be satisfied.'), - 428: ('Precondition Required', - 'The origin server requires the request to be conditional.'), - 429: ('Too Many Requests', 'The user has sent too many requests ' - 'in a given amount of time ("rate limiting").'), - 431: ('Request Header Fields Too Large', 'The server is unwilling to ' - 'process the request because its header fields are too large.'), - - 500: ('Internal Server Error', 'Server got itself in trouble'), - 501: ('Not Implemented', - 'Server does not support this operation'), - 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'), - 503: ('Service Unavailable', - 'The server cannot process the request due to a high load'), - 504: ('Gateway Timeout', - 'The gateway server did not receive a timely response'), - 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'), - 511: ('Network Authentication Required', - 'The client needs to authenticate to gain network access.'), - } - - -class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): - - """Simple HTTP request handler with GET and HEAD commands. - - This serves files from the current directory and any of its - subdirectories. The MIME type for files is determined by - calling the .guess_type() method. - - The GET and HEAD requests are identical except that the HEAD - request omits the actual contents of the file. - - """ - - server_version = "SimpleHTTP/" + __version__ - - def do_GET(self): - """Serve a GET request.""" - f = self.send_head() - if f: - self.copyfile(f, self.wfile) - f.close() - - def do_HEAD(self): - """Serve a HEAD request.""" - f = self.send_head() - if f: - f.close() - - def send_head(self): - """Common code for GET and HEAD commands. - - This sends the response code and MIME headers. - - Return value is either a file object (which has to be copied - to the outputfile by the caller unless the command was HEAD, - and must be closed by the caller under all circumstances), or - None, in which case the caller has nothing further to do. - - """ - path = self.translate_path(self.path) - f = None - if os.path.isdir(path): - if not self.path.endswith('/'): - # redirect browser - doing basically what apache does - self.send_response(301) - self.send_header("Location", self.path + "/") - self.end_headers() - return None - for index in "index.html", "index.htm": - index = os.path.join(path, index) - if os.path.exists(index): - path = index - break - else: - return self.list_directory(path) - ctype = self.guess_type(path) - try: - f = open(path, 'rb') - except IOError: - self.send_error(404, "File not found") - return None - self.send_response(200) - self.send_header("Content-type", ctype) - fs = os.fstat(f.fileno()) - self.send_header("Content-Length", str(fs[6])) - self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) - self.end_headers() - return f - - def list_directory(self, path): - """Helper to produce a directory listing (absent index.html). - - Return value is either a file object, or None (indicating an - error). In either case, the headers are sent, making the - interface the same as for send_head(). - - """ - try: - list = os.listdir(path) - except os.error: - self.send_error(404, "No permission to list directory") - return None - list.sort(key=lambda a: a.lower()) - r = [] - displaypath = html.escape(urllib_parse.unquote(self.path)) - enc = sys.getfilesystemencoding() - title = 'Directory listing for %s' % displaypath - r.append('') - r.append('\n') - r.append('' % enc) - r.append('%s\n' % title) - r.append('\n

%s

' % title) - r.append('
\n
\n
\n\n\n') - encoded = '\n'.join(r).encode(enc) - f = io.BytesIO() - f.write(encoded) - f.seek(0) - self.send_response(200) - self.send_header("Content-type", "text/html; charset=%s" % enc) - self.send_header("Content-Length", str(len(encoded))) - self.end_headers() - return f - - def translate_path(self, path): - """Translate a /-separated PATH to the local filename syntax. - - Components that mean special things to the local file system - (e.g. drive or directory names) are ignored. (XXX They should - probably be diagnosed.) - - """ - # abandon query parameters - path = path.split('?',1)[0] - path = path.split('#',1)[0] - path = posixpath.normpath(urllib_parse.unquote(path)) - words = path.split('/') - words = filter(None, words) - path = os.getcwd() - for word in words: - drive, word = os.path.splitdrive(word) - head, word = os.path.split(word) - if word in (os.curdir, os.pardir): continue - path = os.path.join(path, word) - return path - - def copyfile(self, source, outputfile): - """Copy all data between two file objects. - - The SOURCE argument is a file object open for reading - (or anything with a read() method) and the DESTINATION - argument is a file object open for writing (or - anything with a write() method). - - The only reason for overriding this would be to change - the block size or perhaps to replace newlines by CRLF - -- note however that this the default server uses this - to copy binary data as well. - - """ - shutil.copyfileobj(source, outputfile) - - def guess_type(self, path): - """Guess the type of a file. - - Argument is a PATH (a filename). - - Return value is a string of the form type/subtype, - usable for a MIME Content-type header. - - The default implementation looks the file's extension - up in the table self.extensions_map, using application/octet-stream - as a default; however it would be permissible (if - slow) to look inside the data to make a better guess. - - """ - - base, ext = posixpath.splitext(path) - if ext in self.extensions_map: - return self.extensions_map[ext] - ext = ext.lower() - if ext in self.extensions_map: - return self.extensions_map[ext] - else: - return self.extensions_map[''] - - if not mimetypes.inited: - mimetypes.init() # try to read system mime.types - extensions_map = mimetypes.types_map.copy() - extensions_map.update({ - '': 'application/octet-stream', # Default - '.py': 'text/plain', - '.c': 'text/plain', - '.h': 'text/plain', - }) - - -# Utilities for CGIHTTPRequestHandler - -def _url_collapse_path(path): - """ - Given a URL path, remove extra '/'s and '.' path elements and collapse - any '..' references and returns a colllapsed path. - - Implements something akin to RFC-2396 5.2 step 6 to parse relative paths. - The utility of this function is limited to is_cgi method and helps - preventing some security attacks. - - Returns: A tuple of (head, tail) where tail is everything after the final / - and head is everything before it. Head will always start with a '/' and, - if it contains anything else, never have a trailing '/'. - - Raises: IndexError if too many '..' occur within the path. - - """ - # Similar to os.path.split(os.path.normpath(path)) but specific to URL - # path semantics rather than local operating system semantics. - path_parts = path.split('/') - head_parts = [] - for part in path_parts[:-1]: - if part == '..': - head_parts.pop() # IndexError if more '..' than prior parts - elif part and part != '.': - head_parts.append( part ) - if path_parts: - tail_part = path_parts.pop() - if tail_part: - if tail_part == '..': - head_parts.pop() - tail_part = '' - elif tail_part == '.': - tail_part = '' - else: - tail_part = '' - - splitpath = ('/' + '/'.join(head_parts), tail_part) - collapsed_path = "/".join(splitpath) - - return collapsed_path - - - -nobody = None - -def nobody_uid(): - """Internal routine to get nobody's uid""" - global nobody - if nobody: - return nobody - try: - import pwd - except ImportError: - return -1 - try: - nobody = pwd.getpwnam('nobody')[2] - except KeyError: - nobody = 1 + max(x[2] for x in pwd.getpwall()) - return nobody - - -def executable(path): - """Test for executable file.""" - return os.access(path, os.X_OK) - - -class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): - - """Complete HTTP server with GET, HEAD and POST commands. - - GET and HEAD also support running CGI scripts. - - The POST command is *only* implemented for CGI scripts. - - """ - - # Determine platform specifics - have_fork = hasattr(os, 'fork') - - # Make rfile unbuffered -- we need to read one line and then pass - # the rest to a subprocess, so we can't use buffered input. - rbufsize = 0 - - def do_POST(self): - """Serve a POST request. - - This is only implemented for CGI scripts. - - """ - - if self.is_cgi(): - self.run_cgi() - else: - self.send_error(501, "Can only POST to CGI scripts") - - def send_head(self): - """Version of send_head that support CGI scripts""" - if self.is_cgi(): - return self.run_cgi() - else: - return SimpleHTTPRequestHandler.send_head(self) - - def is_cgi(self): - """Test whether self.path corresponds to a CGI script. - - Returns True and updates the cgi_info attribute to the tuple - (dir, rest) if self.path requires running a CGI script. - Returns False otherwise. - - If any exception is raised, the caller should assume that - self.path was rejected as invalid and act accordingly. - - The default implementation tests whether the normalized url - path begins with one of the strings in self.cgi_directories - (and the next character is a '/' or the end of the string). - - """ - collapsed_path = _url_collapse_path(self.path) - dir_sep = collapsed_path.find('/', 1) - head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:] - if head in self.cgi_directories: - self.cgi_info = head, tail - return True - return False - - - cgi_directories = ['/cgi-bin', '/htbin'] - - def is_executable(self, path): - """Test whether argument path is an executable file.""" - return executable(path) - - def is_python(self, path): - """Test whether argument path is a Python script.""" - head, tail = os.path.splitext(path) - return tail.lower() in (".py", ".pyw") - - def run_cgi(self): - """Execute a CGI script.""" - path = self.path - dir, rest = self.cgi_info - - i = path.find('/', len(dir) + 1) - while i >= 0: - nextdir = path[:i] - nextrest = path[i+1:] - - scriptdir = self.translate_path(nextdir) - if os.path.isdir(scriptdir): - dir, rest = nextdir, nextrest - i = path.find('/', len(dir) + 1) - else: - break - - # find an explicit query string, if present. - i = rest.rfind('?') - if i >= 0: - rest, query = rest[:i], rest[i+1:] - else: - query = '' - - # dissect the part after the directory name into a script name & - # a possible additional path, to be stored in PATH_INFO. - i = rest.find('/') - if i >= 0: - script, rest = rest[:i], rest[i:] - else: - script, rest = rest, '' - - scriptname = dir + '/' + script - scriptfile = self.translate_path(scriptname) - if not os.path.exists(scriptfile): - self.send_error(404, "No such CGI script (%r)" % scriptname) - return - if not os.path.isfile(scriptfile): - self.send_error(403, "CGI script is not a plain file (%r)" % - scriptname) - return - ispy = self.is_python(scriptname) - if self.have_fork or not ispy: - if not self.is_executable(scriptfile): - self.send_error(403, "CGI script is not executable (%r)" % - scriptname) - return - - # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html - # XXX Much of the following could be prepared ahead of time! - env = copy.deepcopy(os.environ) - env['SERVER_SOFTWARE'] = self.version_string() - env['SERVER_NAME'] = self.server.server_name - env['GATEWAY_INTERFACE'] = 'CGI/1.1' - env['SERVER_PROTOCOL'] = self.protocol_version - env['SERVER_PORT'] = str(self.server.server_port) - env['REQUEST_METHOD'] = self.command - uqrest = urllib_parse.unquote(rest) - env['PATH_INFO'] = uqrest - env['PATH_TRANSLATED'] = self.translate_path(uqrest) - env['SCRIPT_NAME'] = scriptname - if query: - env['QUERY_STRING'] = query - env['REMOTE_ADDR'] = self.client_address[0] - authorization = self.headers.get("authorization") - if authorization: - authorization = authorization.split() - if len(authorization) == 2: - import base64, binascii - env['AUTH_TYPE'] = authorization[0] - if authorization[0].lower() == "basic": - try: - authorization = authorization[1].encode('ascii') - if utils.PY3: - # In Py3.3, was: - authorization = base64.decodebytes(authorization).\ - decode('ascii') - else: - # Backport to Py2.7: - authorization = base64.decodestring(authorization).\ - decode('ascii') - except (binascii.Error, UnicodeError): - pass - else: - authorization = authorization.split(':') - if len(authorization) == 2: - env['REMOTE_USER'] = authorization[0] - # XXX REMOTE_IDENT - if self.headers.get('content-type') is None: - env['CONTENT_TYPE'] = self.headers.get_content_type() - else: - env['CONTENT_TYPE'] = self.headers['content-type'] - length = self.headers.get('content-length') - if length: - env['CONTENT_LENGTH'] = length - referer = self.headers.get('referer') - if referer: - env['HTTP_REFERER'] = referer - accept = [] - for line in self.headers.getallmatchingheaders('accept'): - if line[:1] in "\t\n\r ": - accept.append(line.strip()) - else: - accept = accept + line[7:].split(',') - env['HTTP_ACCEPT'] = ','.join(accept) - ua = self.headers.get('user-agent') - if ua: - env['HTTP_USER_AGENT'] = ua - co = filter(None, self.headers.get_all('cookie', [])) - cookie_str = ', '.join(co) - if cookie_str: - env['HTTP_COOKIE'] = cookie_str - # XXX Other HTTP_* headers - # Since we're setting the env in the parent, provide empty - # values to override previously set values - for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH', - 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'): - env.setdefault(k, "") - - self.send_response(200, "Script output follows") - self.flush_headers() - - decoded_query = query.replace('+', ' ') - - if self.have_fork: - # Unix -- fork as we should - args = [script] - if '=' not in decoded_query: - args.append(decoded_query) - nobody = nobody_uid() - self.wfile.flush() # Always flush before forking - pid = os.fork() - if pid != 0: - # Parent - pid, sts = os.waitpid(pid, 0) - # throw away additional data [see bug #427345] - while select.select([self.rfile], [], [], 0)[0]: - if not self.rfile.read(1): - break - if sts: - self.log_error("CGI script exit status %#x", sts) - return - # Child - try: - try: - os.setuid(nobody) - except os.error: - pass - os.dup2(self.rfile.fileno(), 0) - os.dup2(self.wfile.fileno(), 1) - os.execve(scriptfile, args, env) - except: - self.server.handle_error(self.request, self.client_address) - os._exit(127) - - else: - # Non-Unix -- use subprocess - import subprocess - cmdline = [scriptfile] - if self.is_python(scriptfile): - interp = sys.executable - if interp.lower().endswith("w.exe"): - # On Windows, use python.exe, not pythonw.exe - interp = interp[:-5] + interp[-4:] - cmdline = [interp, '-u'] + cmdline - if '=' not in query: - cmdline.append(query) - self.log_message("command: %s", subprocess.list2cmdline(cmdline)) - try: - nbytes = int(length) - except (TypeError, ValueError): - nbytes = 0 - p = subprocess.Popen(cmdline, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env = env - ) - if self.command.lower() == "post" and nbytes > 0: - data = self.rfile.read(nbytes) - else: - data = None - # throw away additional data [see bug #427345] - while select.select([self.rfile._sock], [], [], 0)[0]: - if not self.rfile._sock.recv(1): - break - stdout, stderr = p.communicate(data) - self.wfile.write(stdout) - if stderr: - self.log_error('%s', stderr) - p.stderr.close() - p.stdout.close() - status = p.returncode - if status: - self.log_error("CGI script exit status %#x", status) - else: - self.log_message("CGI script exited OK") - - -def test(HandlerClass = BaseHTTPRequestHandler, - ServerClass = HTTPServer, protocol="HTTP/1.0", port=8000): - """Test the HTTP request handler class. - - This runs an HTTP server on port 8000 (or the first command line - argument). - - """ - server_address = ('', port) - - HandlerClass.protocol_version = protocol - httpd = ServerClass(server_address, HandlerClass) - - sa = httpd.socket.getsockname() - print("Serving HTTP on", sa[0], "port", sa[1], "...") - try: - httpd.serve_forever() - except KeyboardInterrupt: - print("\nKeyboard interrupt received, exiting.") - httpd.server_close() - sys.exit(0) - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('--cgi', action='store_true', - help='Run as CGI Server') - parser.add_argument('port', action='store', - default=8000, type=int, - nargs='?', - help='Specify alternate port [default: 8000]') - args = parser.parse_args() - if args.cgi: - test(HandlerClass=CGIHTTPRequestHandler, port=args.port) - else: - test(HandlerClass=SimpleHTTPRequestHandler, port=args.port) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/misc.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/misc.py deleted file mode 100644 index 098a0667..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/misc.py +++ /dev/null @@ -1,944 +0,0 @@ -""" -Miscellaneous function (re)definitions from the Py3.4+ standard library -for Python 2.6/2.7. - -- math.ceil (for Python 2.7) -- collections.OrderedDict (for Python 2.6) -- collections.Counter (for Python 2.6) -- collections.ChainMap (for all versions prior to Python 3.3) -- itertools.count (for Python 2.6, with step parameter) -- subprocess.check_output (for Python 2.6) -- reprlib.recursive_repr (for Python 2.6+) -- functools.cmp_to_key (for Python 2.6) -""" - -from __future__ import absolute_import - -import subprocess -from math import ceil as oldceil - -from operator import itemgetter as _itemgetter, eq as _eq -import sys -import heapq as _heapq -from _weakref import proxy as _proxy -from itertools import repeat as _repeat, chain as _chain, starmap as _starmap -from socket import getaddrinfo, SOCK_STREAM, error, socket - -from future.utils import iteritems, itervalues, PY2, PY26, PY3 - -if PY2: - from collections import Mapping, MutableMapping -else: - from collections.abc import Mapping, MutableMapping - - -def ceil(x): - """ - Return the ceiling of x as an int. - This is the smallest integral value >= x. - """ - return int(oldceil(x)) - - -######################################################################## -### reprlib.recursive_repr decorator from Py3.4 -######################################################################## - -from itertools import islice - -if PY3: - try: - from _thread import get_ident - except ImportError: - from _dummy_thread import get_ident -else: - try: - from thread import get_ident - except ImportError: - from dummy_thread import get_ident - - -def recursive_repr(fillvalue='...'): - 'Decorator to make a repr function return fillvalue for a recursive call' - - def decorating_function(user_function): - repr_running = set() - - def wrapper(self): - key = id(self), get_ident() - if key in repr_running: - return fillvalue - repr_running.add(key) - try: - result = user_function(self) - finally: - repr_running.discard(key) - return result - - # Can't use functools.wraps() here because of bootstrap issues - wrapper.__module__ = getattr(user_function, '__module__') - wrapper.__doc__ = getattr(user_function, '__doc__') - wrapper.__name__ = getattr(user_function, '__name__') - wrapper.__annotations__ = getattr(user_function, '__annotations__', {}) - return wrapper - - return decorating_function - - -################################################################################ -### OrderedDict -################################################################################ - -class _Link(object): - __slots__ = 'prev', 'next', 'key', '__weakref__' - -class OrderedDict(dict): - 'Dictionary that remembers insertion order' - # An inherited dict maps keys to values. - # The inherited dict provides __getitem__, __len__, __contains__, and get. - # The remaining methods are order-aware. - # Big-O running times for all methods are the same as regular dictionaries. - - # The internal self.__map dict maps keys to links in a doubly linked list. - # The circular doubly linked list starts and ends with a sentinel element. - # The sentinel element never gets deleted (this simplifies the algorithm). - # The sentinel is in self.__hardroot with a weakref proxy in self.__root. - # The prev links are weakref proxies (to prevent circular references). - # Individual links are kept alive by the hard reference in self.__map. - # Those hard references disappear when a key is deleted from an OrderedDict. - - def __init__(*args, **kwds): - '''Initialize an ordered dictionary. The signature is the same as - regular dictionaries, but keyword arguments are not recommended because - their insertion order is arbitrary. - - ''' - if not args: - raise TypeError("descriptor '__init__' of 'OrderedDict' object " - "needs an argument") - self = args[0] - args = args[1:] - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__root - except AttributeError: - self.__hardroot = _Link() - self.__root = root = _proxy(self.__hardroot) - root.prev = root.next = root - self.__map = {} - self.__update(*args, **kwds) - - def __setitem__(self, key, value, - dict_setitem=dict.__setitem__, proxy=_proxy, Link=_Link): - 'od.__setitem__(i, y) <==> od[i]=y' - # Setting a new item creates a new link at the end of the linked list, - # and the inherited dictionary is updated with the new key/value pair. - if key not in self: - self.__map[key] = link = Link() - root = self.__root - last = root.prev - link.prev, link.next, link.key = last, root, key - last.next = link - root.prev = proxy(link) - dict_setitem(self, key, value) - - def __delitem__(self, key, dict_delitem=dict.__delitem__): - 'od.__delitem__(y) <==> del od[y]' - # Deleting an existing item uses self.__map to find the link which gets - # removed by updating the links in the predecessor and successor nodes. - dict_delitem(self, key) - link = self.__map.pop(key) - link_prev = link.prev - link_next = link.next - link_prev.next = link_next - link_next.prev = link_prev - - def __iter__(self): - 'od.__iter__() <==> iter(od)' - # Traverse the linked list in order. - root = self.__root - curr = root.next - while curr is not root: - yield curr.key - curr = curr.next - - def __reversed__(self): - 'od.__reversed__() <==> reversed(od)' - # Traverse the linked list in reverse order. - root = self.__root - curr = root.prev - while curr is not root: - yield curr.key - curr = curr.prev - - def clear(self): - 'od.clear() -> None. Remove all items from od.' - root = self.__root - root.prev = root.next = root - self.__map.clear() - dict.clear(self) - - def popitem(self, last=True): - '''od.popitem() -> (k, v), return and remove a (key, value) pair. - Pairs are returned in LIFO order if last is true or FIFO order if false. - - ''' - if not self: - raise KeyError('dictionary is empty') - root = self.__root - if last: - link = root.prev - link_prev = link.prev - link_prev.next = root - root.prev = link_prev - else: - link = root.next - link_next = link.next - root.next = link_next - link_next.prev = root - key = link.key - del self.__map[key] - value = dict.pop(self, key) - return key, value - - def move_to_end(self, key, last=True): - '''Move an existing element to the end (or beginning if last==False). - - Raises KeyError if the element does not exist. - When last=True, acts like a fast version of self[key]=self.pop(key). - - ''' - link = self.__map[key] - link_prev = link.prev - link_next = link.next - link_prev.next = link_next - link_next.prev = link_prev - root = self.__root - if last: - last = root.prev - link.prev = last - link.next = root - last.next = root.prev = link - else: - first = root.next - link.prev = root - link.next = first - root.next = first.prev = link - - def __sizeof__(self): - sizeof = sys.getsizeof - n = len(self) + 1 # number of links including root - size = sizeof(self.__dict__) # instance dictionary - size += sizeof(self.__map) * 2 # internal dict and inherited dict - size += sizeof(self.__hardroot) * n # link objects - size += sizeof(self.__root) * n # proxy objects - return size - - update = __update = MutableMapping.update - keys = MutableMapping.keys - values = MutableMapping.values - items = MutableMapping.items - __ne__ = MutableMapping.__ne__ - - __marker = object() - - def pop(self, key, default=__marker): - '''od.pop(k[,d]) -> v, remove specified key and return the corresponding - value. If key is not found, d is returned if given, otherwise KeyError - is raised. - - ''' - if key in self: - result = self[key] - del self[key] - return result - if default is self.__marker: - raise KeyError(key) - return default - - def setdefault(self, key, default=None): - 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' - if key in self: - return self[key] - self[key] = default - return default - - @recursive_repr() - def __repr__(self): - 'od.__repr__() <==> repr(od)' - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self.items())) - - def __reduce__(self): - 'Return state information for pickling' - inst_dict = vars(self).copy() - for k in vars(OrderedDict()): - inst_dict.pop(k, None) - return self.__class__, (), inst_dict or None, None, iter(self.items()) - - def copy(self): - 'od.copy() -> a shallow copy of od' - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S. - If not specified, the value defaults to None. - - ''' - self = cls() - for key in iterable: - self[key] = value - return self - - def __eq__(self, other): - '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive - while comparison to a regular mapping is order-insensitive. - - ''' - if isinstance(other, OrderedDict): - return dict.__eq__(self, other) and all(map(_eq, self, other)) - return dict.__eq__(self, other) - - -# {{{ http://code.activestate.com/recipes/576611/ (r11) - -try: - from operator import itemgetter - from heapq import nlargest -except ImportError: - pass - -######################################################################## -### Counter -######################################################################## - -def _count_elements(mapping, iterable): - 'Tally elements from the iterable.' - mapping_get = mapping.get - for elem in iterable: - mapping[elem] = mapping_get(elem, 0) + 1 - -class Counter(dict): - '''Dict subclass for counting hashable items. Sometimes called a bag - or multiset. Elements are stored as dictionary keys and their counts - are stored as dictionary values. - - >>> c = Counter('abcdeabcdabcaba') # count elements from a string - - >>> c.most_common(3) # three most common elements - [('a', 5), ('b', 4), ('c', 3)] - >>> sorted(c) # list all unique elements - ['a', 'b', 'c', 'd', 'e'] - >>> ''.join(sorted(c.elements())) # list elements with repetitions - 'aaaaabbbbcccdde' - >>> sum(c.values()) # total of all counts - 15 - - >>> c['a'] # count of letter 'a' - 5 - >>> for elem in 'shazam': # update counts from an iterable - ... c[elem] += 1 # by adding 1 to each element's count - >>> c['a'] # now there are seven 'a' - 7 - >>> del c['b'] # remove all 'b' - >>> c['b'] # now there are zero 'b' - 0 - - >>> d = Counter('simsalabim') # make another counter - >>> c.update(d) # add in the second counter - >>> c['a'] # now there are nine 'a' - 9 - - >>> c.clear() # empty the counter - >>> c - Counter() - - Note: If a count is set to zero or reduced to zero, it will remain - in the counter until the entry is deleted or the counter is cleared: - - >>> c = Counter('aaabbc') - >>> c['b'] -= 2 # reduce the count of 'b' by two - >>> c.most_common() # 'b' is still in, but its count is zero - [('a', 3), ('c', 1), ('b', 0)] - - ''' - # References: - # http://en.wikipedia.org/wiki/Multiset - # http://www.gnu.org/software/smalltalk/manual-base/html_node/Bag.html - # http://www.demo2s.com/Tutorial/Cpp/0380__set-multiset/Catalog0380__set-multiset.htm - # http://code.activestate.com/recipes/259174/ - # Knuth, TAOCP Vol. II section 4.6.3 - - def __init__(*args, **kwds): - '''Create a new, empty Counter object. And if given, count elements - from an input iterable. Or, initialize the count from another mapping - of elements to their counts. - - >>> c = Counter() # a new, empty counter - >>> c = Counter('gallahad') # a new counter from an iterable - >>> c = Counter({'a': 4, 'b': 2}) # a new counter from a mapping - >>> c = Counter(a=4, b=2) # a new counter from keyword args - - ''' - if not args: - raise TypeError("descriptor '__init__' of 'Counter' object " - "needs an argument") - self = args[0] - args = args[1:] - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - super(Counter, self).__init__() - self.update(*args, **kwds) - - def __missing__(self, key): - 'The count of elements not in the Counter is zero.' - # Needed so that self[missing_item] does not raise KeyError - return 0 - - def most_common(self, n=None): - '''List the n most common elements and their counts from the most - common to the least. If n is None, then list all element counts. - - >>> Counter('abcdeabcdabcaba').most_common(3) - [('a', 5), ('b', 4), ('c', 3)] - - ''' - # Emulate Bag.sortedByCount from Smalltalk - if n is None: - return sorted(self.items(), key=_itemgetter(1), reverse=True) - return _heapq.nlargest(n, self.items(), key=_itemgetter(1)) - - def elements(self): - '''Iterator over elements repeating each as many times as its count. - - >>> c = Counter('ABCABC') - >>> sorted(c.elements()) - ['A', 'A', 'B', 'B', 'C', 'C'] - - # Knuth's example for prime factors of 1836: 2**2 * 3**3 * 17**1 - >>> prime_factors = Counter({2: 2, 3: 3, 17: 1}) - >>> product = 1 - >>> for factor in prime_factors.elements(): # loop over factors - ... product *= factor # and multiply them - >>> product - 1836 - - Note, if an element's count has been set to zero or is a negative - number, elements() will ignore it. - - ''' - # Emulate Bag.do from Smalltalk and Multiset.begin from C++. - return _chain.from_iterable(_starmap(_repeat, self.items())) - - # Override dict methods where necessary - - @classmethod - def fromkeys(cls, iterable, v=None): - # There is no equivalent method for counters because setting v=1 - # means that no element can have a count greater than one. - raise NotImplementedError( - 'Counter.fromkeys() is undefined. Use Counter(iterable) instead.') - - def update(*args, **kwds): - '''Like dict.update() but add counts instead of replacing them. - - Source can be an iterable, a dictionary, or another Counter instance. - - >>> c = Counter('which') - >>> c.update('witch') # add elements from another iterable - >>> d = Counter('watch') - >>> c.update(d) # add elements from another counter - >>> c['h'] # four 'h' in which, witch, and watch - 4 - - ''' - # The regular dict.update() operation makes no sense here because the - # replace behavior results in the some of original untouched counts - # being mixed-in with all of the other counts for a mismash that - # doesn't have a straight-forward interpretation in most counting - # contexts. Instead, we implement straight-addition. Both the inputs - # and outputs are allowed to contain zero and negative counts. - - if not args: - raise TypeError("descriptor 'update' of 'Counter' object " - "needs an argument") - self = args[0] - args = args[1:] - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - iterable = args[0] if args else None - if iterable is not None: - if isinstance(iterable, Mapping): - if self: - self_get = self.get - for elem, count in iterable.items(): - self[elem] = count + self_get(elem, 0) - else: - super(Counter, self).update(iterable) # fast path when counter is empty - else: - _count_elements(self, iterable) - if kwds: - self.update(kwds) - - def subtract(*args, **kwds): - '''Like dict.update() but subtracts counts instead of replacing them. - Counts can be reduced below zero. Both the inputs and outputs are - allowed to contain zero and negative counts. - - Source can be an iterable, a dictionary, or another Counter instance. - - >>> c = Counter('which') - >>> c.subtract('witch') # subtract elements from another iterable - >>> c.subtract(Counter('watch')) # subtract elements from another counter - >>> c['h'] # 2 in which, minus 1 in witch, minus 1 in watch - 0 - >>> c['w'] # 1 in which, minus 1 in witch, minus 1 in watch - -1 - - ''' - if not args: - raise TypeError("descriptor 'subtract' of 'Counter' object " - "needs an argument") - self = args[0] - args = args[1:] - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - iterable = args[0] if args else None - if iterable is not None: - self_get = self.get - if isinstance(iterable, Mapping): - for elem, count in iterable.items(): - self[elem] = self_get(elem, 0) - count - else: - for elem in iterable: - self[elem] = self_get(elem, 0) - 1 - if kwds: - self.subtract(kwds) - - def copy(self): - 'Return a shallow copy.' - return self.__class__(self) - - def __reduce__(self): - return self.__class__, (dict(self),) - - def __delitem__(self, elem): - 'Like dict.__delitem__() but does not raise KeyError for missing values.' - if elem in self: - super(Counter, self).__delitem__(elem) - - def __repr__(self): - if not self: - return '%s()' % self.__class__.__name__ - try: - items = ', '.join(map('%r: %r'.__mod__, self.most_common())) - return '%s({%s})' % (self.__class__.__name__, items) - except TypeError: - # handle case where values are not orderable - return '{0}({1!r})'.format(self.__class__.__name__, dict(self)) - - # Multiset-style mathematical operations discussed in: - # Knuth TAOCP Volume II section 4.6.3 exercise 19 - # and at http://en.wikipedia.org/wiki/Multiset - # - # Outputs guaranteed to only include positive counts. - # - # To strip negative and zero counts, add-in an empty counter: - # c += Counter() - - def __add__(self, other): - '''Add counts from two counters. - - >>> Counter('abbb') + Counter('bcc') - Counter({'b': 4, 'c': 2, 'a': 1}) - - ''' - if not isinstance(other, Counter): - return NotImplemented - result = Counter() - for elem, count in self.items(): - newcount = count + other[elem] - if newcount > 0: - result[elem] = newcount - for elem, count in other.items(): - if elem not in self and count > 0: - result[elem] = count - return result - - def __sub__(self, other): - ''' Subtract count, but keep only results with positive counts. - - >>> Counter('abbbc') - Counter('bccd') - Counter({'b': 2, 'a': 1}) - - ''' - if not isinstance(other, Counter): - return NotImplemented - result = Counter() - for elem, count in self.items(): - newcount = count - other[elem] - if newcount > 0: - result[elem] = newcount - for elem, count in other.items(): - if elem not in self and count < 0: - result[elem] = 0 - count - return result - - def __or__(self, other): - '''Union is the maximum of value in either of the input counters. - - >>> Counter('abbb') | Counter('bcc') - Counter({'b': 3, 'c': 2, 'a': 1}) - - ''' - if not isinstance(other, Counter): - return NotImplemented - result = Counter() - for elem, count in self.items(): - other_count = other[elem] - newcount = other_count if count < other_count else count - if newcount > 0: - result[elem] = newcount - for elem, count in other.items(): - if elem not in self and count > 0: - result[elem] = count - return result - - def __and__(self, other): - ''' Intersection is the minimum of corresponding counts. - - >>> Counter('abbb') & Counter('bcc') - Counter({'b': 1}) - - ''' - if not isinstance(other, Counter): - return NotImplemented - result = Counter() - for elem, count in self.items(): - other_count = other[elem] - newcount = count if count < other_count else other_count - if newcount > 0: - result[elem] = newcount - return result - - def __pos__(self): - 'Adds an empty counter, effectively stripping negative and zero counts' - return self + Counter() - - def __neg__(self): - '''Subtracts from an empty counter. Strips positive and zero counts, - and flips the sign on negative counts. - - ''' - return Counter() - self - - def _keep_positive(self): - '''Internal method to strip elements with a negative or zero count''' - nonpositive = [elem for elem, count in self.items() if not count > 0] - for elem in nonpositive: - del self[elem] - return self - - def __iadd__(self, other): - '''Inplace add from another counter, keeping only positive counts. - - >>> c = Counter('abbb') - >>> c += Counter('bcc') - >>> c - Counter({'b': 4, 'c': 2, 'a': 1}) - - ''' - for elem, count in other.items(): - self[elem] += count - return self._keep_positive() - - def __isub__(self, other): - '''Inplace subtract counter, but keep only results with positive counts. - - >>> c = Counter('abbbc') - >>> c -= Counter('bccd') - >>> c - Counter({'b': 2, 'a': 1}) - - ''' - for elem, count in other.items(): - self[elem] -= count - return self._keep_positive() - - def __ior__(self, other): - '''Inplace union is the maximum of value from either counter. - - >>> c = Counter('abbb') - >>> c |= Counter('bcc') - >>> c - Counter({'b': 3, 'c': 2, 'a': 1}) - - ''' - for elem, other_count in other.items(): - count = self[elem] - if other_count > count: - self[elem] = other_count - return self._keep_positive() - - def __iand__(self, other): - '''Inplace intersection is the minimum of corresponding counts. - - >>> c = Counter('abbb') - >>> c &= Counter('bcc') - >>> c - Counter({'b': 1}) - - ''' - for elem, count in self.items(): - other_count = other[elem] - if other_count < count: - self[elem] = other_count - return self._keep_positive() - - -def check_output(*popenargs, **kwargs): - """ - For Python 2.6 compatibility: see - http://stackoverflow.com/questions/4814970/ - """ - - if 'stdout' in kwargs: - raise ValueError('stdout argument not allowed, it will be overridden.') - process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) - output, unused_err = process.communicate() - retcode = process.poll() - if retcode: - cmd = kwargs.get("args") - if cmd is None: - cmd = popenargs[0] - raise subprocess.CalledProcessError(retcode, cmd) - return output - - -def count(start=0, step=1): - """ - ``itertools.count`` in Py 2.6 doesn't accept a step - parameter. This is an enhanced version of ``itertools.count`` - for Py2.6 equivalent to ``itertools.count`` in Python 2.7+. - """ - while True: - yield start - start += step - - -######################################################################## -### ChainMap (helper for configparser and string.Template) -### From the Py3.4 source code. See also: -### https://github.com/kkxue/Py2ChainMap/blob/master/py2chainmap.py -######################################################################## - -class ChainMap(MutableMapping): - ''' A ChainMap groups multiple dicts (or other mappings) together - to create a single, updateable view. - - The underlying mappings are stored in a list. That list is public and can - accessed or updated using the *maps* attribute. There is no other state. - - Lookups search the underlying mappings successively until a key is found. - In contrast, writes, updates, and deletions only operate on the first - mapping. - - ''' - - def __init__(self, *maps): - '''Initialize a ChainMap by setting *maps* to the given mappings. - If no mappings are provided, a single empty dictionary is used. - - ''' - self.maps = list(maps) or [{}] # always at least one map - - def __missing__(self, key): - raise KeyError(key) - - def __getitem__(self, key): - for mapping in self.maps: - try: - return mapping[key] # can't use 'key in mapping' with defaultdict - except KeyError: - pass - return self.__missing__(key) # support subclasses that define __missing__ - - def get(self, key, default=None): - return self[key] if key in self else default - - def __len__(self): - return len(set().union(*self.maps)) # reuses stored hash values if possible - - def __iter__(self): - return iter(set().union(*self.maps)) - - def __contains__(self, key): - return any(key in m for m in self.maps) - - def __bool__(self): - return any(self.maps) - - # Py2 compatibility: - __nonzero__ = __bool__ - - @recursive_repr() - def __repr__(self): - return '{0.__class__.__name__}({1})'.format( - self, ', '.join(map(repr, self.maps))) - - @classmethod - def fromkeys(cls, iterable, *args): - 'Create a ChainMap with a single dict created from the iterable.' - return cls(dict.fromkeys(iterable, *args)) - - def copy(self): - 'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]' - return self.__class__(self.maps[0].copy(), *self.maps[1:]) - - __copy__ = copy - - def new_child(self, m=None): # like Django's Context.push() - ''' - New ChainMap with a new map followed by all previous maps. If no - map is provided, an empty dict is used. - ''' - if m is None: - m = {} - return self.__class__(m, *self.maps) - - @property - def parents(self): # like Django's Context.pop() - 'New ChainMap from maps[1:].' - return self.__class__(*self.maps[1:]) - - def __setitem__(self, key, value): - self.maps[0][key] = value - - def __delitem__(self, key): - try: - del self.maps[0][key] - except KeyError: - raise KeyError('Key not found in the first mapping: {0!r}'.format(key)) - - def popitem(self): - 'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.' - try: - return self.maps[0].popitem() - except KeyError: - raise KeyError('No keys found in the first mapping.') - - def pop(self, key, *args): - 'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].' - try: - return self.maps[0].pop(key, *args) - except KeyError: - raise KeyError('Key not found in the first mapping: {0!r}'.format(key)) - - def clear(self): - 'Clear maps[0], leaving maps[1:] intact.' - self.maps[0].clear() - - -# Re-use the same sentinel as in the Python stdlib socket module: -from socket import _GLOBAL_DEFAULT_TIMEOUT -# Was: _GLOBAL_DEFAULT_TIMEOUT = object() - - -def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, - source_address=None): - """Backport of 3-argument create_connection() for Py2.6. - - Connect to *address* and return the socket object. - - Convenience function. Connect to *address* (a 2-tuple ``(host, - port)``) and return the socket object. Passing the optional - *timeout* parameter will set the timeout on the socket instance - before attempting to connect. If no *timeout* is supplied, the - global default timeout setting returned by :func:`getdefaulttimeout` - is used. If *source_address* is set it must be a tuple of (host, port) - for the socket to bind as a source address before making the connection. - An host of '' or port 0 tells the OS to use the default. - """ - - host, port = address - err = None - for res in getaddrinfo(host, port, 0, SOCK_STREAM): - af, socktype, proto, canonname, sa = res - sock = None - try: - sock = socket(af, socktype, proto) - if timeout is not _GLOBAL_DEFAULT_TIMEOUT: - sock.settimeout(timeout) - if source_address: - sock.bind(source_address) - sock.connect(sa) - return sock - - except error as _: - err = _ - if sock is not None: - sock.close() - - if err is not None: - raise err - else: - raise error("getaddrinfo returns an empty list") - -# Backport from Py2.7 for Py2.6: -def cmp_to_key(mycmp): - """Convert a cmp= function into a key= function""" - class K(object): - __slots__ = ['obj'] - def __init__(self, obj, *args): - self.obj = obj - def __lt__(self, other): - return mycmp(self.obj, other.obj) < 0 - def __gt__(self, other): - return mycmp(self.obj, other.obj) > 0 - def __eq__(self, other): - return mycmp(self.obj, other.obj) == 0 - def __le__(self, other): - return mycmp(self.obj, other.obj) <= 0 - def __ge__(self, other): - return mycmp(self.obj, other.obj) >= 0 - def __ne__(self, other): - return mycmp(self.obj, other.obj) != 0 - def __hash__(self): - raise TypeError('hash not implemented') - return K - -# Back up our definitions above in case they're useful -_OrderedDict = OrderedDict -_Counter = Counter -_check_output = check_output -_count = count -_ceil = ceil -__count_elements = _count_elements -_recursive_repr = recursive_repr -_ChainMap = ChainMap -_create_connection = create_connection -_cmp_to_key = cmp_to_key - -# Overwrite the definitions above with the usual ones -# from the standard library: -if sys.version_info >= (2, 7): - from collections import OrderedDict, Counter - from itertools import count - from functools import cmp_to_key - try: - from subprocess import check_output - except ImportError: - # Not available. This happens with Google App Engine: see issue #231 - pass - from socket import create_connection - -if sys.version_info >= (3, 0): - from math import ceil - from collections import _count_elements - -if sys.version_info >= (3, 3): - from reprlib import recursive_repr - from collections import ChainMap diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/socket.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/socket.py deleted file mode 100644 index 930e1dae..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/socket.py +++ /dev/null @@ -1,454 +0,0 @@ -# Wrapper module for _socket, providing some additional facilities -# implemented in Python. - -"""\ -This module provides socket operations and some related functions. -On Unix, it supports IP (Internet Protocol) and Unix domain sockets. -On other systems, it only supports IP. Functions specific for a -socket are available as methods of the socket object. - -Functions: - -socket() -- create a new socket object -socketpair() -- create a pair of new socket objects [*] -fromfd() -- create a socket object from an open file descriptor [*] -fromshare() -- create a socket object from data received from socket.share() [*] -gethostname() -- return the current hostname -gethostbyname() -- map a hostname to its IP number -gethostbyaddr() -- map an IP number or hostname to DNS info -getservbyname() -- map a service name and a protocol name to a port number -getprotobyname() -- map a protocol name (e.g. 'tcp') to a number -ntohs(), ntohl() -- convert 16, 32 bit int from network to host byte order -htons(), htonl() -- convert 16, 32 bit int from host to network byte order -inet_aton() -- convert IP addr string (123.45.67.89) to 32-bit packed format -inet_ntoa() -- convert 32-bit packed format IP to string (123.45.67.89) -socket.getdefaulttimeout() -- get the default timeout value -socket.setdefaulttimeout() -- set the default timeout value -create_connection() -- connects to an address, with an optional timeout and - optional source address. - - [*] not available on all platforms! - -Special objects: - -SocketType -- type object for socket objects -error -- exception raised for I/O errors -has_ipv6 -- boolean value indicating if IPv6 is supported - -Integer constants: - -AF_INET, AF_UNIX -- socket domains (first argument to socket() call) -SOCK_STREAM, SOCK_DGRAM, SOCK_RAW -- socket types (second argument) - -Many other constants may be defined; these may be used in calls to -the setsockopt() and getsockopt() methods. -""" - -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import -from future.builtins import super - -import _socket -from _socket import * - -import os, sys, io - -try: - import errno -except ImportError: - errno = None -EBADF = getattr(errno, 'EBADF', 9) -EAGAIN = getattr(errno, 'EAGAIN', 11) -EWOULDBLOCK = getattr(errno, 'EWOULDBLOCK', 11) - -__all__ = ["getfqdn", "create_connection"] -__all__.extend(os._get_exports_list(_socket)) - - -_realsocket = socket - -# WSA error codes -if sys.platform.lower().startswith("win"): - errorTab = {} - errorTab[10004] = "The operation was interrupted." - errorTab[10009] = "A bad file handle was passed." - errorTab[10013] = "Permission denied." - errorTab[10014] = "A fault occurred on the network??" # WSAEFAULT - errorTab[10022] = "An invalid operation was attempted." - errorTab[10035] = "The socket operation would block" - errorTab[10036] = "A blocking operation is already in progress." - errorTab[10048] = "The network address is in use." - errorTab[10054] = "The connection has been reset." - errorTab[10058] = "The network has been shut down." - errorTab[10060] = "The operation timed out." - errorTab[10061] = "Connection refused." - errorTab[10063] = "The name is too long." - errorTab[10064] = "The host is down." - errorTab[10065] = "The host is unreachable." - __all__.append("errorTab") - - -class socket(_socket.socket): - - """A subclass of _socket.socket adding the makefile() method.""" - - __slots__ = ["__weakref__", "_io_refs", "_closed"] - - def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None): - if fileno is None: - _socket.socket.__init__(self, family, type, proto) - else: - _socket.socket.__init__(self, family, type, proto, fileno) - self._io_refs = 0 - self._closed = False - - def __enter__(self): - return self - - def __exit__(self, *args): - if not self._closed: - self.close() - - def __repr__(self): - """Wrap __repr__() to reveal the real class name.""" - s = _socket.socket.__repr__(self) - if s.startswith(" socket object - - Return a new socket object connected to the same system resource. - """ - fd = dup(self.fileno()) - sock = self.__class__(self.family, self.type, self.proto, fileno=fd) - sock.settimeout(self.gettimeout()) - return sock - - def accept(self): - """accept() -> (socket object, address info) - - Wait for an incoming connection. Return a new socket - representing the connection, and the address of the client. - For IP sockets, the address info is a pair (hostaddr, port). - """ - fd, addr = self._accept() - sock = socket(self.family, self.type, self.proto, fileno=fd) - # Issue #7995: if no default timeout is set and the listening - # socket had a (non-zero) timeout, force the new socket in blocking - # mode to override platform-specific socket flags inheritance. - if getdefaulttimeout() is None and self.gettimeout(): - sock.setblocking(True) - return sock, addr - - def makefile(self, mode="r", buffering=None, **_3to2kwargs): - """makefile(...) -> an I/O stream connected to the socket - - The arguments are as for io.open() after the filename, - except the only mode characters supported are 'r', 'w' and 'b'. - The semantics are similar too. (XXX refactor to share code?) - """ - if 'newline' in _3to2kwargs: newline = _3to2kwargs['newline']; del _3to2kwargs['newline'] - else: newline = None - if 'errors' in _3to2kwargs: errors = _3to2kwargs['errors']; del _3to2kwargs['errors'] - else: errors = None - if 'encoding' in _3to2kwargs: encoding = _3to2kwargs['encoding']; del _3to2kwargs['encoding'] - else: encoding = None - for c in mode: - if c not in ("r", "w", "b"): - raise ValueError("invalid mode %r (only r, w, b allowed)") - writing = "w" in mode - reading = "r" in mode or not writing - assert reading or writing - binary = "b" in mode - rawmode = "" - if reading: - rawmode += "r" - if writing: - rawmode += "w" - raw = SocketIO(self, rawmode) - self._io_refs += 1 - if buffering is None: - buffering = -1 - if buffering < 0: - buffering = io.DEFAULT_BUFFER_SIZE - if buffering == 0: - if not binary: - raise ValueError("unbuffered streams must be binary") - return raw - if reading and writing: - buffer = io.BufferedRWPair(raw, raw, buffering) - elif reading: - buffer = io.BufferedReader(raw, buffering) - else: - assert writing - buffer = io.BufferedWriter(raw, buffering) - if binary: - return buffer - text = io.TextIOWrapper(buffer, encoding, errors, newline) - text.mode = mode - return text - - def _decref_socketios(self): - if self._io_refs > 0: - self._io_refs -= 1 - if self._closed: - self.close() - - def _real_close(self, _ss=_socket.socket): - # This function should not reference any globals. See issue #808164. - _ss.close(self) - - def close(self): - # This function should not reference any globals. See issue #808164. - self._closed = True - if self._io_refs <= 0: - self._real_close() - - def detach(self): - """detach() -> file descriptor - - Close the socket object without closing the underlying file descriptor. - The object cannot be used after this call, but the file descriptor - can be reused for other purposes. The file descriptor is returned. - """ - self._closed = True - return super().detach() - -def fromfd(fd, family, type, proto=0): - """ fromfd(fd, family, type[, proto]) -> socket object - - Create a socket object from a duplicate of the given file - descriptor. The remaining arguments are the same as for socket(). - """ - nfd = dup(fd) - return socket(family, type, proto, nfd) - -if hasattr(_socket.socket, "share"): - def fromshare(info): - """ fromshare(info) -> socket object - - Create a socket object from a the bytes object returned by - socket.share(pid). - """ - return socket(0, 0, 0, info) - -if hasattr(_socket, "socketpair"): - - def socketpair(family=None, type=SOCK_STREAM, proto=0): - """socketpair([family[, type[, proto]]]) -> (socket object, socket object) - - Create a pair of socket objects from the sockets returned by the platform - socketpair() function. - The arguments are the same as for socket() except the default family is - AF_UNIX if defined on the platform; otherwise, the default is AF_INET. - """ - if family is None: - try: - family = AF_UNIX - except NameError: - family = AF_INET - a, b = _socket.socketpair(family, type, proto) - a = socket(family, type, proto, a.detach()) - b = socket(family, type, proto, b.detach()) - return a, b - - -_blocking_errnos = set([EAGAIN, EWOULDBLOCK]) - -class SocketIO(io.RawIOBase): - - """Raw I/O implementation for stream sockets. - - This class supports the makefile() method on sockets. It provides - the raw I/O interface on top of a socket object. - """ - - # One might wonder why not let FileIO do the job instead. There are two - # main reasons why FileIO is not adapted: - # - it wouldn't work under Windows (where you can't used read() and - # write() on a socket handle) - # - it wouldn't work with socket timeouts (FileIO would ignore the - # timeout and consider the socket non-blocking) - - # XXX More docs - - def __init__(self, sock, mode): - if mode not in ("r", "w", "rw", "rb", "wb", "rwb"): - raise ValueError("invalid mode: %r" % mode) - io.RawIOBase.__init__(self) - self._sock = sock - if "b" not in mode: - mode += "b" - self._mode = mode - self._reading = "r" in mode - self._writing = "w" in mode - self._timeout_occurred = False - - def readinto(self, b): - """Read up to len(b) bytes into the writable buffer *b* and return - the number of bytes read. If the socket is non-blocking and no bytes - are available, None is returned. - - If *b* is non-empty, a 0 return value indicates that the connection - was shutdown at the other end. - """ - self._checkClosed() - self._checkReadable() - if self._timeout_occurred: - raise IOError("cannot read from timed out object") - while True: - try: - return self._sock.recv_into(b) - except timeout: - self._timeout_occurred = True - raise - # except InterruptedError: - # continue - except error as e: - if e.args[0] in _blocking_errnos: - return None - raise - - def write(self, b): - """Write the given bytes or bytearray object *b* to the socket - and return the number of bytes written. This can be less than - len(b) if not all data could be written. If the socket is - non-blocking and no bytes could be written None is returned. - """ - self._checkClosed() - self._checkWritable() - try: - return self._sock.send(b) - except error as e: - # XXX what about EINTR? - if e.args[0] in _blocking_errnos: - return None - raise - - def readable(self): - """True if the SocketIO is open for reading. - """ - if self.closed: - raise ValueError("I/O operation on closed socket.") - return self._reading - - def writable(self): - """True if the SocketIO is open for writing. - """ - if self.closed: - raise ValueError("I/O operation on closed socket.") - return self._writing - - def seekable(self): - """True if the SocketIO is open for seeking. - """ - if self.closed: - raise ValueError("I/O operation on closed socket.") - return super().seekable() - - def fileno(self): - """Return the file descriptor of the underlying socket. - """ - self._checkClosed() - return self._sock.fileno() - - @property - def name(self): - if not self.closed: - return self.fileno() - else: - return -1 - - @property - def mode(self): - return self._mode - - def close(self): - """Close the SocketIO object. This doesn't close the underlying - socket, except if all references to it have disappeared. - """ - if self.closed: - return - io.RawIOBase.close(self) - self._sock._decref_socketios() - self._sock = None - - -def getfqdn(name=''): - """Get fully qualified domain name from name. - - An empty argument is interpreted as meaning the local host. - - First the hostname returned by gethostbyaddr() is checked, then - possibly existing aliases. In case no FQDN is available, hostname - from gethostname() is returned. - """ - name = name.strip() - if not name or name == '0.0.0.0': - name = gethostname() - try: - hostname, aliases, ipaddrs = gethostbyaddr(name) - except error: - pass - else: - aliases.insert(0, hostname) - for name in aliases: - if '.' in name: - break - else: - name = hostname - return name - - -# Re-use the same sentinel as in the Python stdlib socket module: -from socket import _GLOBAL_DEFAULT_TIMEOUT -# Was: _GLOBAL_DEFAULT_TIMEOUT = object() - - -def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, - source_address=None): - """Connect to *address* and return the socket object. - - Convenience function. Connect to *address* (a 2-tuple ``(host, - port)``) and return the socket object. Passing the optional - *timeout* parameter will set the timeout on the socket instance - before attempting to connect. If no *timeout* is supplied, the - global default timeout setting returned by :func:`getdefaulttimeout` - is used. If *source_address* is set it must be a tuple of (host, port) - for the socket to bind as a source address before making the connection. - An host of '' or port 0 tells the OS to use the default. - """ - - host, port = address - err = None - for res in getaddrinfo(host, port, 0, SOCK_STREAM): - af, socktype, proto, canonname, sa = res - sock = None - try: - sock = socket(af, socktype, proto) - if timeout is not _GLOBAL_DEFAULT_TIMEOUT: - sock.settimeout(timeout) - if source_address: - sock.bind(source_address) - sock.connect(sa) - return sock - - except error as _: - err = _ - if sock is not None: - sock.close() - - if err is not None: - raise err - else: - raise error("getaddrinfo returns an empty list") diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/socketserver.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/socketserver.py deleted file mode 100644 index d1e24a6d..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/socketserver.py +++ /dev/null @@ -1,747 +0,0 @@ -"""Generic socket server classes. - -This module tries to capture the various aspects of defining a server: - -For socket-based servers: - -- address family: - - AF_INET{,6}: IP (Internet Protocol) sockets (default) - - AF_UNIX: Unix domain sockets - - others, e.g. AF_DECNET are conceivable (see -- socket type: - - SOCK_STREAM (reliable stream, e.g. TCP) - - SOCK_DGRAM (datagrams, e.g. UDP) - -For request-based servers (including socket-based): - -- client address verification before further looking at the request - (This is actually a hook for any processing that needs to look - at the request before anything else, e.g. logging) -- how to handle multiple requests: - - synchronous (one request is handled at a time) - - forking (each request is handled by a new process) - - threading (each request is handled by a new thread) - -The classes in this module favor the server type that is simplest to -write: a synchronous TCP/IP server. This is bad class design, but -save some typing. (There's also the issue that a deep class hierarchy -slows down method lookups.) - -There are five classes in an inheritance diagram, four of which represent -synchronous servers of four types: - - +------------+ - | BaseServer | - +------------+ - | - v - +-----------+ +------------------+ - | TCPServer |------->| UnixStreamServer | - +-----------+ +------------------+ - | - v - +-----------+ +--------------------+ - | UDPServer |------->| UnixDatagramServer | - +-----------+ +--------------------+ - -Note that UnixDatagramServer derives from UDPServer, not from -UnixStreamServer -- the only difference between an IP and a Unix -stream server is the address family, which is simply repeated in both -unix server classes. - -Forking and threading versions of each type of server can be created -using the ForkingMixIn and ThreadingMixIn mix-in classes. For -instance, a threading UDP server class is created as follows: - - class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass - -The Mix-in class must come first, since it overrides a method defined -in UDPServer! Setting the various member variables also changes -the behavior of the underlying server mechanism. - -To implement a service, you must derive a class from -BaseRequestHandler and redefine its handle() method. You can then run -various versions of the service by combining one of the server classes -with your request handler class. - -The request handler class must be different for datagram or stream -services. This can be hidden by using the request handler -subclasses StreamRequestHandler or DatagramRequestHandler. - -Of course, you still have to use your head! - -For instance, it makes no sense to use a forking server if the service -contains state in memory that can be modified by requests (since the -modifications in the child process would never reach the initial state -kept in the parent process and passed to each child). In this case, -you can use a threading server, but you will probably have to use -locks to avoid two requests that come in nearly simultaneous to apply -conflicting changes to the server state. - -On the other hand, if you are building e.g. an HTTP server, where all -data is stored externally (e.g. in the file system), a synchronous -class will essentially render the service "deaf" while one request is -being handled -- which may be for a very long time if a client is slow -to read all the data it has requested. Here a threading or forking -server is appropriate. - -In some cases, it may be appropriate to process part of a request -synchronously, but to finish processing in a forked child depending on -the request data. This can be implemented by using a synchronous -server and doing an explicit fork in the request handler class -handle() method. - -Another approach to handling multiple simultaneous requests in an -environment that supports neither threads nor fork (or where these are -too expensive or inappropriate for the service) is to maintain an -explicit table of partially finished requests and to use select() to -decide which request to work on next (or whether to handle a new -incoming request). This is particularly important for stream services -where each client can potentially be connected for a long time (if -threads or subprocesses cannot be used). - -Future work: -- Standard classes for Sun RPC (which uses either UDP or TCP) -- Standard mix-in classes to implement various authentication - and encryption schemes -- Standard framework for select-based multiplexing - -XXX Open problems: -- What to do with out-of-band data? - -BaseServer: -- split generic "request" functionality out into BaseServer class. - Copyright (C) 2000 Luke Kenneth Casson Leighton - - example: read entries from a SQL database (requires overriding - get_request() to return a table entry from the database). - entry is processed by a RequestHandlerClass. - -""" - -# Author of the BaseServer patch: Luke Kenneth Casson Leighton - -# XXX Warning! -# There is a test suite for this module, but it cannot be run by the -# standard regression test. -# To run it manually, run Lib/test/test_socketserver.py. - -from __future__ import (absolute_import, print_function) - -__version__ = "0.4" - - -import socket -import select -import sys -import os -import errno -try: - import threading -except ImportError: - import dummy_threading as threading - -__all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer", - "ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler", - "StreamRequestHandler","DatagramRequestHandler", - "ThreadingMixIn", "ForkingMixIn"] -if hasattr(socket, "AF_UNIX"): - __all__.extend(["UnixStreamServer","UnixDatagramServer", - "ThreadingUnixStreamServer", - "ThreadingUnixDatagramServer"]) - -def _eintr_retry(func, *args): - """restart a system call interrupted by EINTR""" - while True: - try: - return func(*args) - except OSError as e: - if e.errno != errno.EINTR: - raise - -class BaseServer(object): - - """Base class for server classes. - - Methods for the caller: - - - __init__(server_address, RequestHandlerClass) - - serve_forever(poll_interval=0.5) - - shutdown() - - handle_request() # if you do not use serve_forever() - - fileno() -> int # for select() - - Methods that may be overridden: - - - server_bind() - - server_activate() - - get_request() -> request, client_address - - handle_timeout() - - verify_request(request, client_address) - - server_close() - - process_request(request, client_address) - - shutdown_request(request) - - close_request(request) - - service_actions() - - handle_error() - - Methods for derived classes: - - - finish_request(request, client_address) - - Class variables that may be overridden by derived classes or - instances: - - - timeout - - address_family - - socket_type - - allow_reuse_address - - Instance variables: - - - RequestHandlerClass - - socket - - """ - - timeout = None - - def __init__(self, server_address, RequestHandlerClass): - """Constructor. May be extended, do not override.""" - self.server_address = server_address - self.RequestHandlerClass = RequestHandlerClass - self.__is_shut_down = threading.Event() - self.__shutdown_request = False - - def server_activate(self): - """Called by constructor to activate the server. - - May be overridden. - - """ - pass - - def serve_forever(self, poll_interval=0.5): - """Handle one request at a time until shutdown. - - Polls for shutdown every poll_interval seconds. Ignores - self.timeout. If you need to do periodic tasks, do them in - another thread. - """ - self.__is_shut_down.clear() - try: - while not self.__shutdown_request: - # XXX: Consider using another file descriptor or - # connecting to the socket to wake this up instead of - # polling. Polling reduces our responsiveness to a - # shutdown request and wastes cpu at all other times. - r, w, e = _eintr_retry(select.select, [self], [], [], - poll_interval) - if self in r: - self._handle_request_noblock() - - self.service_actions() - finally: - self.__shutdown_request = False - self.__is_shut_down.set() - - def shutdown(self): - """Stops the serve_forever loop. - - Blocks until the loop has finished. This must be called while - serve_forever() is running in another thread, or it will - deadlock. - """ - self.__shutdown_request = True - self.__is_shut_down.wait() - - def service_actions(self): - """Called by the serve_forever() loop. - - May be overridden by a subclass / Mixin to implement any code that - needs to be run during the loop. - """ - pass - - # The distinction between handling, getting, processing and - # finishing a request is fairly arbitrary. Remember: - # - # - handle_request() is the top-level call. It calls - # select, get_request(), verify_request() and process_request() - # - get_request() is different for stream or datagram sockets - # - process_request() is the place that may fork a new process - # or create a new thread to finish the request - # - finish_request() instantiates the request handler class; - # this constructor will handle the request all by itself - - def handle_request(self): - """Handle one request, possibly blocking. - - Respects self.timeout. - """ - # Support people who used socket.settimeout() to escape - # handle_request before self.timeout was available. - timeout = self.socket.gettimeout() - if timeout is None: - timeout = self.timeout - elif self.timeout is not None: - timeout = min(timeout, self.timeout) - fd_sets = _eintr_retry(select.select, [self], [], [], timeout) - if not fd_sets[0]: - self.handle_timeout() - return - self._handle_request_noblock() - - def _handle_request_noblock(self): - """Handle one request, without blocking. - - I assume that select.select has returned that the socket is - readable before this function was called, so there should be - no risk of blocking in get_request(). - """ - try: - request, client_address = self.get_request() - except socket.error: - return - if self.verify_request(request, client_address): - try: - self.process_request(request, client_address) - except: - self.handle_error(request, client_address) - self.shutdown_request(request) - - def handle_timeout(self): - """Called if no new request arrives within self.timeout. - - Overridden by ForkingMixIn. - """ - pass - - def verify_request(self, request, client_address): - """Verify the request. May be overridden. - - Return True if we should proceed with this request. - - """ - return True - - def process_request(self, request, client_address): - """Call finish_request. - - Overridden by ForkingMixIn and ThreadingMixIn. - - """ - self.finish_request(request, client_address) - self.shutdown_request(request) - - def server_close(self): - """Called to clean-up the server. - - May be overridden. - - """ - pass - - def finish_request(self, request, client_address): - """Finish one request by instantiating RequestHandlerClass.""" - self.RequestHandlerClass(request, client_address, self) - - def shutdown_request(self, request): - """Called to shutdown and close an individual request.""" - self.close_request(request) - - def close_request(self, request): - """Called to clean up an individual request.""" - pass - - def handle_error(self, request, client_address): - """Handle an error gracefully. May be overridden. - - The default is to print a traceback and continue. - - """ - print('-'*40) - print('Exception happened during processing of request from', end=' ') - print(client_address) - import traceback - traceback.print_exc() # XXX But this goes to stderr! - print('-'*40) - - -class TCPServer(BaseServer): - - """Base class for various socket-based server classes. - - Defaults to synchronous IP stream (i.e., TCP). - - Methods for the caller: - - - __init__(server_address, RequestHandlerClass, bind_and_activate=True) - - serve_forever(poll_interval=0.5) - - shutdown() - - handle_request() # if you don't use serve_forever() - - fileno() -> int # for select() - - Methods that may be overridden: - - - server_bind() - - server_activate() - - get_request() -> request, client_address - - handle_timeout() - - verify_request(request, client_address) - - process_request(request, client_address) - - shutdown_request(request) - - close_request(request) - - handle_error() - - Methods for derived classes: - - - finish_request(request, client_address) - - Class variables that may be overridden by derived classes or - instances: - - - timeout - - address_family - - socket_type - - request_queue_size (only for stream sockets) - - allow_reuse_address - - Instance variables: - - - server_address - - RequestHandlerClass - - socket - - """ - - address_family = socket.AF_INET - - socket_type = socket.SOCK_STREAM - - request_queue_size = 5 - - allow_reuse_address = False - - def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): - """Constructor. May be extended, do not override.""" - BaseServer.__init__(self, server_address, RequestHandlerClass) - self.socket = socket.socket(self.address_family, - self.socket_type) - if bind_and_activate: - self.server_bind() - self.server_activate() - - def server_bind(self): - """Called by constructor to bind the socket. - - May be overridden. - - """ - if self.allow_reuse_address: - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.socket.bind(self.server_address) - self.server_address = self.socket.getsockname() - - def server_activate(self): - """Called by constructor to activate the server. - - May be overridden. - - """ - self.socket.listen(self.request_queue_size) - - def server_close(self): - """Called to clean-up the server. - - May be overridden. - - """ - self.socket.close() - - def fileno(self): - """Return socket file number. - - Interface required by select(). - - """ - return self.socket.fileno() - - def get_request(self): - """Get the request and client address from the socket. - - May be overridden. - - """ - return self.socket.accept() - - def shutdown_request(self, request): - """Called to shutdown and close an individual request.""" - try: - #explicitly shutdown. socket.close() merely releases - #the socket and waits for GC to perform the actual close. - request.shutdown(socket.SHUT_WR) - except socket.error: - pass #some platforms may raise ENOTCONN here - self.close_request(request) - - def close_request(self, request): - """Called to clean up an individual request.""" - request.close() - - -class UDPServer(TCPServer): - - """UDP server class.""" - - allow_reuse_address = False - - socket_type = socket.SOCK_DGRAM - - max_packet_size = 8192 - - def get_request(self): - data, client_addr = self.socket.recvfrom(self.max_packet_size) - return (data, self.socket), client_addr - - def server_activate(self): - # No need to call listen() for UDP. - pass - - def shutdown_request(self, request): - # No need to shutdown anything. - self.close_request(request) - - def close_request(self, request): - # No need to close anything. - pass - -class ForkingMixIn(object): - - """Mix-in class to handle each request in a new process.""" - - timeout = 300 - active_children = None - max_children = 40 - - def collect_children(self): - """Internal routine to wait for children that have exited.""" - if self.active_children is None: return - while len(self.active_children) >= self.max_children: - # XXX: This will wait for any child process, not just ones - # spawned by this library. This could confuse other - # libraries that expect to be able to wait for their own - # children. - try: - pid, status = os.waitpid(0, 0) - except os.error: - pid = None - if pid not in self.active_children: continue - self.active_children.remove(pid) - - # XXX: This loop runs more system calls than it ought - # to. There should be a way to put the active_children into a - # process group and then use os.waitpid(-pgid) to wait for any - # of that set, but I couldn't find a way to allocate pgids - # that couldn't collide. - for child in self.active_children: - try: - pid, status = os.waitpid(child, os.WNOHANG) - except os.error: - pid = None - if not pid: continue - try: - self.active_children.remove(pid) - except ValueError as e: - raise ValueError('%s. x=%d and list=%r' % (e.message, pid, - self.active_children)) - - def handle_timeout(self): - """Wait for zombies after self.timeout seconds of inactivity. - - May be extended, do not override. - """ - self.collect_children() - - def service_actions(self): - """Collect the zombie child processes regularly in the ForkingMixIn. - - service_actions is called in the BaseServer's serve_forver loop. - """ - self.collect_children() - - def process_request(self, request, client_address): - """Fork a new subprocess to process the request.""" - pid = os.fork() - if pid: - # Parent process - if self.active_children is None: - self.active_children = [] - self.active_children.append(pid) - self.close_request(request) - return - else: - # Child process. - # This must never return, hence os._exit()! - try: - self.finish_request(request, client_address) - self.shutdown_request(request) - os._exit(0) - except: - try: - self.handle_error(request, client_address) - self.shutdown_request(request) - finally: - os._exit(1) - - -class ThreadingMixIn(object): - """Mix-in class to handle each request in a new thread.""" - - # Decides how threads will act upon termination of the - # main process - daemon_threads = False - - def process_request_thread(self, request, client_address): - """Same as in BaseServer but as a thread. - - In addition, exception handling is done here. - - """ - try: - self.finish_request(request, client_address) - self.shutdown_request(request) - except: - self.handle_error(request, client_address) - self.shutdown_request(request) - - def process_request(self, request, client_address): - """Start a new thread to process the request.""" - t = threading.Thread(target = self.process_request_thread, - args = (request, client_address)) - t.daemon = self.daemon_threads - t.start() - - -class ForkingUDPServer(ForkingMixIn, UDPServer): pass -class ForkingTCPServer(ForkingMixIn, TCPServer): pass - -class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass -class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass - -if hasattr(socket, 'AF_UNIX'): - - class UnixStreamServer(TCPServer): - address_family = socket.AF_UNIX - - class UnixDatagramServer(UDPServer): - address_family = socket.AF_UNIX - - class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): pass - - class ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): pass - -class BaseRequestHandler(object): - - """Base class for request handler classes. - - This class is instantiated for each request to be handled. The - constructor sets the instance variables request, client_address - and server, and then calls the handle() method. To implement a - specific service, all you need to do is to derive a class which - defines a handle() method. - - The handle() method can find the request as self.request, the - client address as self.client_address, and the server (in case it - needs access to per-server information) as self.server. Since a - separate instance is created for each request, the handle() method - can define arbitrary other instance variariables. - - """ - - def __init__(self, request, client_address, server): - self.request = request - self.client_address = client_address - self.server = server - self.setup() - try: - self.handle() - finally: - self.finish() - - def setup(self): - pass - - def handle(self): - pass - - def finish(self): - pass - - -# The following two classes make it possible to use the same service -# class for stream or datagram servers. -# Each class sets up these instance variables: -# - rfile: a file object from which receives the request is read -# - wfile: a file object to which the reply is written -# When the handle() method returns, wfile is flushed properly - - -class StreamRequestHandler(BaseRequestHandler): - - """Define self.rfile and self.wfile for stream sockets.""" - - # Default buffer sizes for rfile, wfile. - # We default rfile to buffered because otherwise it could be - # really slow for large data (a getc() call per byte); we make - # wfile unbuffered because (a) often after a write() we want to - # read and we need to flush the line; (b) big writes to unbuffered - # files are typically optimized by stdio even when big reads - # aren't. - rbufsize = -1 - wbufsize = 0 - - # A timeout to apply to the request socket, if not None. - timeout = None - - # Disable nagle algorithm for this socket, if True. - # Use only when wbufsize != 0, to avoid small packets. - disable_nagle_algorithm = False - - def setup(self): - self.connection = self.request - if self.timeout is not None: - self.connection.settimeout(self.timeout) - if self.disable_nagle_algorithm: - self.connection.setsockopt(socket.IPPROTO_TCP, - socket.TCP_NODELAY, True) - self.rfile = self.connection.makefile('rb', self.rbufsize) - self.wfile = self.connection.makefile('wb', self.wbufsize) - - def finish(self): - if not self.wfile.closed: - try: - self.wfile.flush() - except socket.error: - # An final socket error may have occurred here, such as - # the local error ECONNABORTED. - pass - self.wfile.close() - self.rfile.close() - - -class DatagramRequestHandler(BaseRequestHandler): - - # XXX Regrettably, I cannot get this working on Linux; - # s.recvfrom() doesn't return a meaningful client address. - - """Define self.rfile and self.wfile for datagram sockets.""" - - def setup(self): - from io import BytesIO - self.packet, self.socket = self.request - self.rfile = BytesIO(self.packet) - self.wfile = BytesIO() - - def finish(self): - self.socket.sendto(self.wfile.getvalue(), self.client_address) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/__init__.py deleted file mode 100644 index 0bba5e69..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -""" -test package backported for python-future. - -Its primary purpose is to allow use of "import test.support" for running -the Python standard library unit tests using the new Python 3 stdlib -import location. - -Python 3 renamed test.test_support to test.support. -""" diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index fc2d693bddfedd750e4f8f8b1b3e7b306857c769..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 474 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/__pycache__/ssl_servers.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/__pycache__/ssl_servers.cpython-39.pyc deleted file mode 100644 index b6d26e4676ebe43944b3a42352abccc2904d278f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7110 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 100: - EnumParOut = Ident1 - else: - EnumParOut = Ident4 - elif EnumParIn == Ident3: - EnumParOut = Ident2 - elif EnumParIn == Ident4: - pass - elif EnumParIn == Ident5: - EnumParOut = Ident3 - return EnumParOut - -def Proc7(IntParI1, IntParI2): - IntLoc = IntParI1 + 2 - IntParOut = IntParI2 + IntLoc - return IntParOut - -def Proc8(Array1Par, Array2Par, IntParI1, IntParI2): - global IntGlob - - IntLoc = IntParI1 + 5 - Array1Par[IntLoc] = IntParI2 - Array1Par[IntLoc+1] = Array1Par[IntLoc] - Array1Par[IntLoc+30] = IntLoc - for IntIndex in range(IntLoc, IntLoc+2): - Array2Par[IntLoc][IntIndex] = IntLoc - Array2Par[IntLoc][IntLoc-1] = Array2Par[IntLoc][IntLoc-1] + 1 - Array2Par[IntLoc+20][IntLoc] = Array1Par[IntLoc] - IntGlob = 5 - -def Func1(CharPar1, CharPar2): - CharLoc1 = CharPar1 - CharLoc2 = CharLoc1 - if CharLoc2 != CharPar2: - return Ident1 - else: - return Ident2 - -def Func2(StrParI1, StrParI2): - IntLoc = 1 - while IntLoc <= 1: - if Func1(StrParI1[IntLoc], StrParI2[IntLoc+1]) == Ident1: - CharLoc = 'A' - IntLoc = IntLoc + 1 - if CharLoc >= 'W' and CharLoc <= 'Z': - IntLoc = 7 - if CharLoc == 'X': - return TRUE - else: - if StrParI1 > StrParI2: - IntLoc = IntLoc + 7 - return TRUE - else: - return FALSE - -def Func3(EnumParIn): - EnumLoc = EnumParIn - if EnumLoc == Ident3: return TRUE - return FALSE - -if __name__ == '__main__': - import sys - def error(msg): - print(msg, end=' ', file=sys.stderr) - print("usage: %s [number_of_loops]" % sys.argv[0], file=sys.stderr) - sys.exit(100) - nargs = len(sys.argv) - 1 - if nargs > 1: - error("%d arguments are too many;" % nargs) - elif nargs == 1: - try: loops = int(sys.argv[1]) - except ValueError: - error("Invalid argument %r;" % sys.argv[1]) - else: - loops = LOOPS - main(loops) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/sha256.pem b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/sha256.pem deleted file mode 100644 index d3db4b85..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/sha256.pem +++ /dev/null @@ -1,128 +0,0 @@ -# Certificate chain for https://sha256.tbs-internet.com - 0 s:/C=FR/postalCode=14000/ST=Calvados/L=CAEN/street=22 rue de Bretagne/O=TBS INTERNET/OU=0002 440443810/OU=sha-256 production/CN=sha256.tbs-internet.com - i:/C=FR/ST=Calvados/L=Caen/O=TBS INTERNET/OU=Terms and Conditions: http://www.tbs-internet.com/CA/repository/OU=TBS INTERNET CA/CN=TBS X509 CA SGC ------BEGIN CERTIFICATE----- -MIIGXDCCBUSgAwIBAgIRAKpVmHgg9nfCodAVwcP4siwwDQYJKoZIhvcNAQELBQAw -gcQxCzAJBgNVBAYTAkZSMREwDwYDVQQIEwhDYWx2YWRvczENMAsGA1UEBxMEQ2Fl -bjEVMBMGA1UEChMMVEJTIElOVEVSTkVUMUgwRgYDVQQLEz9UZXJtcyBhbmQgQ29u -ZGl0aW9uczogaHR0cDovL3d3dy50YnMtaW50ZXJuZXQuY29tL0NBL3JlcG9zaXRv -cnkxGDAWBgNVBAsTD1RCUyBJTlRFUk5FVCBDQTEYMBYGA1UEAxMPVEJTIFg1MDkg -Q0EgU0dDMB4XDTEyMDEwNDAwMDAwMFoXDTE0MDIxNzIzNTk1OVowgcsxCzAJBgNV -BAYTAkZSMQ4wDAYDVQQREwUxNDAwMDERMA8GA1UECBMIQ2FsdmFkb3MxDTALBgNV -BAcTBENBRU4xGzAZBgNVBAkTEjIyIHJ1ZSBkZSBCcmV0YWduZTEVMBMGA1UEChMM -VEJTIElOVEVSTkVUMRcwFQYDVQQLEw4wMDAyIDQ0MDQ0MzgxMDEbMBkGA1UECxMS -c2hhLTI1NiBwcm9kdWN0aW9uMSAwHgYDVQQDExdzaGEyNTYudGJzLWludGVybmV0 -LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKQIX/zdJcyxty0m -PM1XQSoSSifueS3AVcgqMsaIKS/u+rYzsv4hQ/qA6vLn5m5/ewUcZDj7zdi6rBVf -PaVNXJ6YinLX0tkaW8TEjeVuZG5yksGZlhCt1CJ1Ho9XLiLaP4uJ7MCoNUntpJ+E -LfrOdgsIj91kPmwjDJeztVcQCvKzhjVJA/KxdInc0JvOATn7rpaSmQI5bvIjufgo -qVsTPwVFzuUYULXBk7KxRT7MiEqnd5HvviNh0285QC478zl3v0I0Fb5El4yD3p49 -IthcRnxzMKc0UhU5ogi0SbONyBfm/mzONVfSxpM+MlyvZmJqrbuuLoEDzJD+t8PU -xSuzgbcCAwEAAaOCAj4wggI6MB8GA1UdIwQYMBaAFAdEdoWTKLx/bXjSCuv6TEvf -2YIfMB0GA1UdDgQWBBT/qTGYdaj+f61c2IRFL/B1eEsM8DAOBgNVHQ8BAf8EBAMC -BaAwDAYDVR0TAQH/BAIwADA0BgNVHSUELTArBggrBgEFBQcDAQYIKwYBBQUHAwIG -CisGAQQBgjcKAwMGCWCGSAGG+EIEATBLBgNVHSAERDBCMEAGCisGAQQB5TcCBAEw -MjAwBggrBgEFBQcCARYkaHR0cHM6Ly93d3cudGJzLWludGVybmV0LmNvbS9DQS9D -UFM0MG0GA1UdHwRmMGQwMqAwoC6GLGh0dHA6Ly9jcmwudGJzLWludGVybmV0LmNv -bS9UQlNYNTA5Q0FTR0MuY3JsMC6gLKAqhihodHRwOi8vY3JsLnRicy14NTA5LmNv -bS9UQlNYNTA5Q0FTR0MuY3JsMIGmBggrBgEFBQcBAQSBmTCBljA4BggrBgEFBQcw -AoYsaHR0cDovL2NydC50YnMtaW50ZXJuZXQuY29tL1RCU1g1MDlDQVNHQy5jcnQw -NAYIKwYBBQUHMAKGKGh0dHA6Ly9jcnQudGJzLXg1MDkuY29tL1RCU1g1MDlDQVNH -Qy5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnRicy14NTA5LmNvbTA/BgNV -HREEODA2ghdzaGEyNTYudGJzLWludGVybmV0LmNvbYIbd3d3LnNoYTI1Ni50YnMt -aW50ZXJuZXQuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQA0pOuL8QvAa5yksTbGShzX -ABApagunUGoEydv4YJT1MXy9tTp7DrWaozZSlsqBxrYAXP1d9r2fuKbEniYHxaQ0 -UYaf1VSIlDo1yuC8wE7wxbHDIpQ/E5KAyxiaJ8obtDhFstWAPAH+UoGXq0kj2teN -21sFQ5dXgA95nldvVFsFhrRUNB6xXAcaj0VZFhttI0ZfQZmQwEI/P+N9Jr40OGun -aa+Dn0TMeUH4U20YntfLbu2nDcJcYfyurm+8/0Tr4HznLnedXu9pCPYj0TaddrgT -XO0oFiyy7qGaY6+qKh71yD64Y3ycCJ/HR9Wm39mjZYc9ezYwT4noP6r7Lk8YO7/q ------END CERTIFICATE----- - 1 s:/C=FR/ST=Calvados/L=Caen/O=TBS INTERNET/OU=Terms and Conditions: http://www.tbs-internet.com/CA/repository/OU=TBS INTERNET CA/CN=TBS X509 CA SGC - i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root ------BEGIN CERTIFICATE----- -MIIFVjCCBD6gAwIBAgIQXpDZ0ETJMV02WTx3GTnhhTANBgkqhkiG9w0BAQUFADBv -MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk -ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF -eHRlcm5hbCBDQSBSb290MB4XDTA1MTIwMTAwMDAwMFoXDTE5MDYyNDE5MDYzMFow -gcQxCzAJBgNVBAYTAkZSMREwDwYDVQQIEwhDYWx2YWRvczENMAsGA1UEBxMEQ2Fl -bjEVMBMGA1UEChMMVEJTIElOVEVSTkVUMUgwRgYDVQQLEz9UZXJtcyBhbmQgQ29u -ZGl0aW9uczogaHR0cDovL3d3dy50YnMtaW50ZXJuZXQuY29tL0NBL3JlcG9zaXRv -cnkxGDAWBgNVBAsTD1RCUyBJTlRFUk5FVCBDQTEYMBYGA1UEAxMPVEJTIFg1MDkg -Q0EgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsgOkO3f7wzN6 -rOjg45tR5vjBfzK7qmV9IBxb/QW9EEXxG+E7FNhZqQLtwGBKoSsHTnQqV75wWMk0 -9tinWvftBkSpj5sTi/8cbzJfUvTSVYh3Qxv6AVVjMMH/ruLjE6y+4PoaPs8WoYAQ -ts5R4Z1g8c/WnTepLst2x0/Wv7GmuoQi+gXvHU6YrBiu7XkeYhzc95QdviWSJRDk -owhb5K43qhcvjRmBfO/paGlCliDGZp8mHwrI21mwobWpVjTxZRwYO3bd4+TGcI4G -Ie5wmHwE8F7SK1tgSqbBacKjDa93j7txKkfz/Yd2n7TGqOXiHPsJpG655vrKtnXk -9vs1zoDeJQIDAQABo4IBljCCAZIwHQYDVR0OBBYEFAdEdoWTKLx/bXjSCuv6TEvf -2YIfMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMCAGA1UdJQQZ -MBcGCisGAQQBgjcKAwMGCWCGSAGG+EIEATAYBgNVHSAEETAPMA0GCysGAQQBgOU3 -AgQBMHsGA1UdHwR0MHIwOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0Fk -ZFRydXN0RXh0ZXJuYWxDQVJvb3QuY3JsMDagNKAyhjBodHRwOi8vY3JsLmNvbW9k -by5uZXQvQWRkVHJ1c3RFeHRlcm5hbENBUm9vdC5jcmwwgYAGCCsGAQUFBwEBBHQw -cjA4BggrBgEFBQcwAoYsaHR0cDovL2NydC5jb21vZG9jYS5jb20vQWRkVHJ1c3RV -VE5TR0NDQS5jcnQwNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQuY29tb2RvLm5ldC9B -ZGRUcnVzdFVUTlNHQ0NBLmNydDARBglghkgBhvhCAQEEBAMCAgQwDQYJKoZIhvcN -AQEFBQADggEBAK2zEzs+jcIrVK9oDkdDZNvhuBYTdCfpxfFs+OAujW0bIfJAy232 -euVsnJm6u/+OrqKudD2tad2BbejLLXhMZViaCmK7D9nrXHx4te5EP8rL19SUVqLY -1pTnv5dhNgEgvA7n5lIzDSYs7yRLsr7HJsYPr6SeYSuZizyX1SNz7ooJ32/F3X98 -RB0Mlc/E0OyOrkQ9/y5IrnpnaSora8CnUrV5XNOg+kyCz9edCyx4D5wXYcwZPVWz -8aDqquESrezPyjtfi4WRO4s/VD3HLZvOxzMrWAVYCDG9FxaOhF0QGuuG1F7F3GKV -v6prNyCl016kRl2j1UT+a7gLd8fA25A4C9E= ------END CERTIFICATE----- - 2 s:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root - i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC ------BEGIN CERTIFICATE----- -MIIEZjCCA06gAwIBAgIQUSYKkxzif5zDpV954HKugjANBgkqhkiG9w0BAQUFADCB -kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug -Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw -IFNHQzAeFw0wNTA2MDcwODA5MTBaFw0xOTA2MjQxOTA2MzBaMG8xCzAJBgNVBAYT -AlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0 -ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB -IFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC39xoz5vIABC05 -4E5b7R+8bA/Ntfojts7emxEzl6QpTH2Tn71KvJPtAxrjj8/lbVBa1pcplFqAsEl6 -2y6V/bjKvzc4LR4+kUGtcFbH8E8/6DKedMrIkFTpxl8PeJ2aQDwOrGGqXhSPnoeh -alDc15pOrwWzpnGUnHGzUGAKxxOdOAeGAqjpqGkmGJCrTLBPI6s6T4TY386f4Wlv -u9dC12tE5Met7m1BX3JacQg3s3llpFmglDf3AC8NwpJy2tA4ctsUqEXEXSp9t7TW -xO6szRNEt8kr3UMAJfphuWlqWCMRt6czj1Z1WfXNKddGtworZbbTQm8Vsrh7++/p -XVPVNFonAgMBAAGjgdgwgdUwHwYDVR0jBBgwFoAUUzLRs89/+uDxoF2FTpLSnkUd -tE8wHQYDVR0OBBYEFK29mHo0tCb3+sQmVO8DveAky1QaMA4GA1UdDwEB/wQEAwIB -BjAPBgNVHRMBAf8EBTADAQH/MBEGCWCGSAGG+EIBAQQEAwIBAjAgBgNVHSUEGTAX -BgorBgEEAYI3CgMDBglghkgBhvhCBAEwPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDov -L2NybC51c2VydHJ1c3QuY29tL1VUTi1EQVRBQ29ycFNHQy5jcmwwDQYJKoZIhvcN -AQEFBQADggEBAMbuUxdoFLJRIh6QWA2U/b3xcOWGLcM2MY9USEbnLQg3vGwKYOEO -rVE04BKT6b64q7gmtOmWPSiPrmQH/uAB7MXjkesYoPF1ftsK5p+R26+udd8jkWjd -FwBaS/9kbHDrARrQkNnHptZt9hPk/7XJ0h4qy7ElQyZ42TCbTg0evmnv3+r+LbPM -+bDdtRTKkdSytaX7ARmjR3mfnYyVhzT4HziS2jamEfpr62vp3EV4FTkG101B5CHI -3C+H0be/SGB1pWLLJN47YaApIKa+xWycxOkKaSLvkTr6Jq/RW0GnOuL4OAdCq8Fb -+M5tug8EPzI0rNwEKNdwMBQmBsTkm5jVz3g= ------END CERTIFICATE----- - 3 s:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC - i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC ------BEGIN CERTIFICATE----- -MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB -kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug -Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw -IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG -EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD -VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu -dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 -E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ -D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK -4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq -lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW -bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB -o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT -MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js -LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr -BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB -AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft -Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj -j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH -KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv -2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 -mfnGV/TJVTl4uix5yaaIK/QI ------END CERTIFICATE----- diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_cert.pem b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_cert.pem deleted file mode 100644 index 47a7d7e3..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_cert.pem +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICVDCCAb2gAwIBAgIJANfHOBkZr8JOMA0GCSqGSIb3DQEBBQUAMF8xCzAJBgNV -BAYTAlhZMRcwFQYDVQQHEw5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9u -IFNvZnR3YXJlIEZvdW5kYXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMDEw -MDgyMzAxNTZaFw0yMDEwMDUyMzAxNTZaMF8xCzAJBgNVBAYTAlhZMRcwFQYDVQQH -Ew5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9uIFNvZnR3YXJlIEZvdW5k -YXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw -gYkCgYEA21vT5isq7F68amYuuNpSFlKDPrMUCa4YWYqZRt2OZ+/3NKaZ2xAiSwr7 -6MrQF70t5nLbSPpqE5+5VrS58SY+g/sXLiFd6AplH1wJZwh78DofbFYXUggktFMt -pTyiX8jtP66bkcPkDADA089RI1TQR6Ca+n7HFa7c1fabVV6i3zkCAwEAAaMYMBYw -FAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBBQUAA4GBAHPctQBEQ4wd -BJ6+JcpIraopLn8BGhbjNWj40mmRqWB/NAWF6M5ne7KpGAu7tLeG4hb1zLaldK8G -lxy2GPSRF6LFS48dpEj2HbMv2nvv6xxalDMJ9+DicWgAKTQ6bcX2j3GUkCR0g/T1 -CRlNBAAlvhKzO7Clpf9l0YKBEfraJByX ------END CERTIFICATE----- diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_key.passwd.pem b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_key.passwd.pem deleted file mode 100644 index 2524672e..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_key.passwd.pem +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,1A8D9D2A02EC698A - -kJYbfZ8L0sfe9Oty3gw0aloNnY5E8fegRfQLZlNoxTl6jNt0nIwI8kDJ36CZgR9c -u3FDJm/KqrfUoz8vW+qEnWhSG7QPX2wWGPHd4K94Yz/FgrRzZ0DoK7XxXq9gOtVA -AVGQhnz32p+6WhfGsCr9ArXEwRZrTk/FvzEPaU5fHcoSkrNVAGX8IpSVkSDwEDQr -Gv17+cfk99UV1OCza6yKHoFkTtrC+PZU71LomBabivS2Oc4B9hYuSR2hF01wTHP+ -YlWNagZOOVtNz4oKK9x9eNQpmfQXQvPPTfusexKIbKfZrMvJoxcm1gfcZ0H/wK6P -6wmXSG35qMOOztCZNtperjs1wzEBXznyK8QmLcAJBjkfarABJX9vBEzZV0OUKhy+ -noORFwHTllphbmydLhu6ehLUZMHPhzAS5UN7srtpSN81eerDMy0RMUAwA7/PofX1 -94Me85Q8jP0PC9ETdsJcPqLzAPETEYu0ELewKRcrdyWi+tlLFrpE5KT/s5ecbl9l -7B61U4Kfd1PIXc/siINhU3A3bYK+845YyUArUOnKf1kEox7p1RpD7yFqVT04lRTo -cibNKATBusXSuBrp2G6GNuhWEOSafWCKJQAzgCYIp6ZTV2khhMUGppc/2H3CF6cO -zX0KtlPVZC7hLkB6HT8SxYUwF1zqWY7+/XPPdc37MeEZ87Q3UuZwqORLY+Z0hpgt -L5JXBCoklZhCAaN2GqwFLXtGiRSRFGY7xXIhbDTlE65Wv1WGGgDLMKGE1gOz3yAo -2jjG1+yAHJUdE69XTFHSqSkvaloA1W03LdMXZ9VuQJ/ySXCie6ABAQ== ------END RSA PRIVATE KEY----- diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_key.pem b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_key.pem deleted file mode 100644 index 3fd3bbd5..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_key.pem +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANtb0+YrKuxevGpm -LrjaUhZSgz6zFAmuGFmKmUbdjmfv9zSmmdsQIksK++jK0Be9LeZy20j6ahOfuVa0 -ufEmPoP7Fy4hXegKZR9cCWcIe/A6H2xWF1IIJLRTLaU8ol/I7T+um5HD5AwAwNPP -USNU0Eegmvp+xxWu3NX2m1Veot85AgMBAAECgYA3ZdZ673X0oexFlq7AAmrutkHt -CL7LvwrpOiaBjhyTxTeSNWzvtQBkIU8DOI0bIazA4UreAFffwtvEuPmonDb3F+Iq -SMAu42XcGyVZEl+gHlTPU9XRX7nTOXVt+MlRRRxL6t9GkGfUAXI3XxJDXW3c0vBK -UL9xqD8cORXOfE06rQJBAP8mEX1ERkR64Ptsoe4281vjTlNfIbs7NMPkUnrn9N/Y -BLhjNIfQ3HFZG8BTMLfX7kCS9D593DW5tV4Z9BP/c6cCQQDcFzCcVArNh2JSywOQ -ZfTfRbJg/Z5Lt9Fkngv1meeGNPgIMLN8Sg679pAOOWmzdMO3V706rNPzSVMME7E5 -oPIfAkEA8pDddarP5tCvTTgUpmTFbakm0KoTZm2+FzHcnA4jRh+XNTjTOv98Y6Ik -eO5d1ZnKXseWvkZncQgxfdnMqqpj5wJAcNq/RVne1DbYlwWchT2Si65MYmmJ8t+F -0mcsULqjOnEMwf5e+ptq5LzwbyrHZYq5FNk7ocufPv/ZQrcSSC+cFwJBAKvOJByS -x56qyGeZLOQlWS2JS3KJo59XuLFGqcbgN9Om9xFa41Yb4N9NvplFivsvZdw3m1Q/ -SPIXQuT8RMPDVNQ= ------END PRIVATE KEY----- diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_servers.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_servers.py deleted file mode 100644 index 87a3fb85..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/ssl_servers.py +++ /dev/null @@ -1,207 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals -from future.builtins import filter, str -from future import utils -import os -import sys -import ssl -import pprint -import socket -from future.backports.urllib import parse as urllib_parse -from future.backports.http.server import (HTTPServer as _HTTPServer, - SimpleHTTPRequestHandler, BaseHTTPRequestHandler) -from future.backports.test import support -threading = support.import_module("threading") - -here = os.path.dirname(__file__) - -HOST = support.HOST -CERTFILE = os.path.join(here, 'keycert.pem') - -# This one's based on HTTPServer, which is based on SocketServer - -class HTTPSServer(_HTTPServer): - - def __init__(self, server_address, handler_class, context): - _HTTPServer.__init__(self, server_address, handler_class) - self.context = context - - def __str__(self): - return ('<%s %s:%s>' % - (self.__class__.__name__, - self.server_name, - self.server_port)) - - def get_request(self): - # override this to wrap socket with SSL - try: - sock, addr = self.socket.accept() - sslconn = self.context.wrap_socket(sock, server_side=True) - except socket.error as e: - # socket errors are silenced by the caller, print them here - if support.verbose: - sys.stderr.write("Got an error:\n%s\n" % e) - raise - return sslconn, addr - -class RootedHTTPRequestHandler(SimpleHTTPRequestHandler): - # need to override translate_path to get a known root, - # instead of using os.curdir, since the test could be - # run from anywhere - - server_version = "TestHTTPS/1.0" - root = here - # Avoid hanging when a request gets interrupted by the client - timeout = 5 - - def translate_path(self, path): - """Translate a /-separated PATH to the local filename syntax. - - Components that mean special things to the local file system - (e.g. drive or directory names) are ignored. (XXX They should - probably be diagnosed.) - - """ - # abandon query parameters - path = urllib.parse.urlparse(path)[2] - path = os.path.normpath(urllib.parse.unquote(path)) - words = path.split('/') - words = filter(None, words) - path = self.root - for word in words: - drive, word = os.path.splitdrive(word) - head, word = os.path.split(word) - path = os.path.join(path, word) - return path - - def log_message(self, format, *args): - # we override this to suppress logging unless "verbose" - if support.verbose: - sys.stdout.write(" server (%s:%d %s):\n [%s] %s\n" % - (self.server.server_address, - self.server.server_port, - self.request.cipher(), - self.log_date_time_string(), - format%args)) - - -class StatsRequestHandler(BaseHTTPRequestHandler): - """Example HTTP request handler which returns SSL statistics on GET - requests. - """ - - server_version = "StatsHTTPS/1.0" - - def do_GET(self, send_body=True): - """Serve a GET request.""" - sock = self.rfile.raw._sock - context = sock.context - stats = { - 'session_cache': context.session_stats(), - 'cipher': sock.cipher(), - 'compression': sock.compression(), - } - body = pprint.pformat(stats) - body = body.encode('utf-8') - self.send_response(200) - self.send_header("Content-type", "text/plain; charset=utf-8") - self.send_header("Content-Length", str(len(body))) - self.end_headers() - if send_body: - self.wfile.write(body) - - def do_HEAD(self): - """Serve a HEAD request.""" - self.do_GET(send_body=False) - - def log_request(self, format, *args): - if support.verbose: - BaseHTTPRequestHandler.log_request(self, format, *args) - - -class HTTPSServerThread(threading.Thread): - - def __init__(self, context, host=HOST, handler_class=None): - self.flag = None - self.server = HTTPSServer((host, 0), - handler_class or RootedHTTPRequestHandler, - context) - self.port = self.server.server_port - threading.Thread.__init__(self) - self.daemon = True - - def __str__(self): - return "<%s %s>" % (self.__class__.__name__, self.server) - - def start(self, flag=None): - self.flag = flag - threading.Thread.start(self) - - def run(self): - if self.flag: - self.flag.set() - try: - self.server.serve_forever(0.05) - finally: - self.server.server_close() - - def stop(self): - self.server.shutdown() - - -def make_https_server(case, certfile=CERTFILE, host=HOST, handler_class=None): - # we assume the certfile contains both private key and certificate - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - context.load_cert_chain(certfile) - server = HTTPSServerThread(context, host, handler_class) - flag = threading.Event() - server.start(flag) - flag.wait() - def cleanup(): - if support.verbose: - sys.stdout.write('stopping HTTPS server\n') - server.stop() - if support.verbose: - sys.stdout.write('joining HTTPS thread\n') - server.join() - case.addCleanup(cleanup) - return server - - -if __name__ == "__main__": - import argparse - parser = argparse.ArgumentParser( - description='Run a test HTTPS server. ' - 'By default, the current directory is served.') - parser.add_argument('-p', '--port', type=int, default=4433, - help='port to listen on (default: %(default)s)') - parser.add_argument('-q', '--quiet', dest='verbose', default=True, - action='store_false', help='be less verbose') - parser.add_argument('-s', '--stats', dest='use_stats_handler', default=False, - action='store_true', help='always return stats page') - parser.add_argument('--curve-name', dest='curve_name', type=str, - action='store', - help='curve name for EC-based Diffie-Hellman') - parser.add_argument('--dh', dest='dh_file', type=str, action='store', - help='PEM file containing DH parameters') - args = parser.parse_args() - - support.verbose = args.verbose - if args.use_stats_handler: - handler_class = StatsRequestHandler - else: - handler_class = RootedHTTPRequestHandler - if utils.PY2: - handler_class.root = os.getcwdu() - else: - handler_class.root = os.getcwd() - context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - context.load_cert_chain(CERTFILE) - if args.curve_name: - context.set_ecdh_curve(args.curve_name) - if args.dh_file: - context.load_dh_params(args.dh_file) - - server = HTTPSServer(("", args.port), handler_class, context) - if args.verbose: - print("Listening on https://localhost:{0.port}".format(args)) - server.serve_forever(0.1) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/support.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/support.py deleted file mode 100644 index 1999e208..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/test/support.py +++ /dev/null @@ -1,2048 +0,0 @@ -# -*- coding: utf-8 -*- -"""Supporting definitions for the Python regression tests. - -Backported for python-future from Python 3.3 test/support.py. -""" - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future import utils -from future.builtins import str, range, open, int, map, list - -import contextlib -import errno -import functools -import gc -import socket -import sys -import os -import platform -import shutil -import warnings -import unittest -# For Python 2.6 compatibility: -if not hasattr(unittest, 'skip'): - import unittest2 as unittest - -import importlib -# import collections.abc # not present on Py2.7 -import re -import subprocess -import imp -import time -try: - import sysconfig -except ImportError: - # sysconfig is not available on Python 2.6. Try using distutils.sysconfig instead: - from distutils import sysconfig -import fnmatch -import logging.handlers -import struct -import tempfile - -try: - if utils.PY3: - import _thread, threading - else: - import thread as _thread, threading -except ImportError: - _thread = None - threading = None -try: - import multiprocessing.process -except ImportError: - multiprocessing = None - -try: - import zlib -except ImportError: - zlib = None - -try: - import gzip -except ImportError: - gzip = None - -try: - import bz2 -except ImportError: - bz2 = None - -try: - import lzma -except ImportError: - lzma = None - -__all__ = [ - "Error", "TestFailed", "ResourceDenied", "import_module", "verbose", - "use_resources", "max_memuse", "record_original_stdout", - "get_original_stdout", "unload", "unlink", "rmtree", "forget", - "is_resource_enabled", "requires", "requires_freebsd_version", - "requires_linux_version", "requires_mac_ver", "find_unused_port", - "bind_port", "IPV6_ENABLED", "is_jython", "TESTFN", "HOST", "SAVEDCWD", - "temp_cwd", "findfile", "create_empty_file", "sortdict", - "check_syntax_error", "open_urlresource", "check_warnings", "CleanImport", - "EnvironmentVarGuard", "TransientResource", "captured_stdout", - "captured_stdin", "captured_stderr", "time_out", "socket_peer_reset", - "ioerror_peer_reset", "run_with_locale", 'temp_umask', - "transient_internet", "set_memlimit", "bigmemtest", "bigaddrspacetest", - "BasicTestRunner", "run_unittest", "run_doctest", "threading_setup", - "threading_cleanup", "reap_children", "cpython_only", "check_impl_detail", - "get_attribute", "swap_item", "swap_attr", "requires_IEEE_754", - "TestHandler", "Matcher", "can_symlink", "skip_unless_symlink", - "skip_unless_xattr", "import_fresh_module", "requires_zlib", - "PIPE_MAX_SIZE", "failfast", "anticipate_failure", "run_with_tz", - "requires_gzip", "requires_bz2", "requires_lzma", "suppress_crash_popup", - ] - -class Error(Exception): - """Base class for regression test exceptions.""" - -class TestFailed(Error): - """Test failed.""" - -class ResourceDenied(unittest.SkipTest): - """Test skipped because it requested a disallowed resource. - - This is raised when a test calls requires() for a resource that - has not be enabled. It is used to distinguish between expected - and unexpected skips. - """ - -@contextlib.contextmanager -def _ignore_deprecated_imports(ignore=True): - """Context manager to suppress package and module deprecation - warnings when importing them. - - If ignore is False, this context manager has no effect.""" - if ignore: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", ".+ (module|package)", - DeprecationWarning) - yield - else: - yield - - -def import_module(name, deprecated=False): - """Import and return the module to be tested, raising SkipTest if - it is not available. - - If deprecated is True, any module or package deprecation messages - will be suppressed.""" - with _ignore_deprecated_imports(deprecated): - try: - return importlib.import_module(name) - except ImportError as msg: - raise unittest.SkipTest(str(msg)) - - -def _save_and_remove_module(name, orig_modules): - """Helper function to save and remove a module from sys.modules - - Raise ImportError if the module can't be imported. - """ - # try to import the module and raise an error if it can't be imported - if name not in sys.modules: - __import__(name) - del sys.modules[name] - for modname in list(sys.modules): - if modname == name or modname.startswith(name + '.'): - orig_modules[modname] = sys.modules[modname] - del sys.modules[modname] - -def _save_and_block_module(name, orig_modules): - """Helper function to save and block a module in sys.modules - - Return True if the module was in sys.modules, False otherwise. - """ - saved = True - try: - orig_modules[name] = sys.modules[name] - except KeyError: - saved = False - sys.modules[name] = None - return saved - - -def anticipate_failure(condition): - """Decorator to mark a test that is known to be broken in some cases - - Any use of this decorator should have a comment identifying the - associated tracker issue. - """ - if condition: - return unittest.expectedFailure - return lambda f: f - - -def import_fresh_module(name, fresh=(), blocked=(), deprecated=False): - """Import and return a module, deliberately bypassing sys.modules. - This function imports and returns a fresh copy of the named Python module - by removing the named module from sys.modules before doing the import. - Note that unlike reload, the original module is not affected by - this operation. - - *fresh* is an iterable of additional module names that are also removed - from the sys.modules cache before doing the import. - - *blocked* is an iterable of module names that are replaced with None - in the module cache during the import to ensure that attempts to import - them raise ImportError. - - The named module and any modules named in the *fresh* and *blocked* - parameters are saved before starting the import and then reinserted into - sys.modules when the fresh import is complete. - - Module and package deprecation messages are suppressed during this import - if *deprecated* is True. - - This function will raise ImportError if the named module cannot be - imported. - - If deprecated is True, any module or package deprecation messages - will be suppressed. - """ - # NOTE: test_heapq, test_json and test_warnings include extra sanity checks - # to make sure that this utility function is working as expected - with _ignore_deprecated_imports(deprecated): - # Keep track of modules saved for later restoration as well - # as those which just need a blocking entry removed - orig_modules = {} - names_to_remove = [] - _save_and_remove_module(name, orig_modules) - try: - for fresh_name in fresh: - _save_and_remove_module(fresh_name, orig_modules) - for blocked_name in blocked: - if not _save_and_block_module(blocked_name, orig_modules): - names_to_remove.append(blocked_name) - fresh_module = importlib.import_module(name) - except ImportError: - fresh_module = None - finally: - for orig_name, module in orig_modules.items(): - sys.modules[orig_name] = module - for name_to_remove in names_to_remove: - del sys.modules[name_to_remove] - return fresh_module - - -def get_attribute(obj, name): - """Get an attribute, raising SkipTest if AttributeError is raised.""" - try: - attribute = getattr(obj, name) - except AttributeError: - raise unittest.SkipTest("object %r has no attribute %r" % (obj, name)) - else: - return attribute - -verbose = 1 # Flag set to 0 by regrtest.py -use_resources = None # Flag set to [] by regrtest.py -max_memuse = 0 # Disable bigmem tests (they will still be run with - # small sizes, to make sure they work.) -real_max_memuse = 0 -failfast = False -match_tests = None - -# _original_stdout is meant to hold stdout at the time regrtest began. -# This may be "the real" stdout, or IDLE's emulation of stdout, or whatever. -# The point is to have some flavor of stdout the user can actually see. -_original_stdout = None -def record_original_stdout(stdout): - global _original_stdout - _original_stdout = stdout - -def get_original_stdout(): - return _original_stdout or sys.stdout - -def unload(name): - try: - del sys.modules[name] - except KeyError: - pass - -if sys.platform.startswith("win"): - def _waitfor(func, pathname, waitall=False): - # Perform the operation - func(pathname) - # Now setup the wait loop - if waitall: - dirname = pathname - else: - dirname, name = os.path.split(pathname) - dirname = dirname or '.' - # Check for `pathname` to be removed from the filesystem. - # The exponential backoff of the timeout amounts to a total - # of ~1 second after which the deletion is probably an error - # anyway. - # Testing on a i7@4.3GHz shows that usually only 1 iteration is - # required when contention occurs. - timeout = 0.001 - while timeout < 1.0: - # Note we are only testing for the existence of the file(s) in - # the contents of the directory regardless of any security or - # access rights. If we have made it this far, we have sufficient - # permissions to do that much using Python's equivalent of the - # Windows API FindFirstFile. - # Other Windows APIs can fail or give incorrect results when - # dealing with files that are pending deletion. - L = os.listdir(dirname) - if not (L if waitall else name in L): - return - # Increase the timeout and try again - time.sleep(timeout) - timeout *= 2 - warnings.warn('tests may fail, delete still pending for ' + pathname, - RuntimeWarning, stacklevel=4) - - def _unlink(filename): - _waitfor(os.unlink, filename) - - def _rmdir(dirname): - _waitfor(os.rmdir, dirname) - - def _rmtree(path): - def _rmtree_inner(path): - for name in os.listdir(path): - fullname = os.path.join(path, name) - if os.path.isdir(fullname): - _waitfor(_rmtree_inner, fullname, waitall=True) - os.rmdir(fullname) - else: - os.unlink(fullname) - _waitfor(_rmtree_inner, path, waitall=True) - _waitfor(os.rmdir, path) -else: - _unlink = os.unlink - _rmdir = os.rmdir - _rmtree = shutil.rmtree - -def unlink(filename): - try: - _unlink(filename) - except OSError as error: - # The filename need not exist. - if error.errno not in (errno.ENOENT, errno.ENOTDIR): - raise - -def rmdir(dirname): - try: - _rmdir(dirname) - except OSError as error: - # The directory need not exist. - if error.errno != errno.ENOENT: - raise - -def rmtree(path): - try: - _rmtree(path) - except OSError as error: - if error.errno != errno.ENOENT: - raise - -def make_legacy_pyc(source): - """Move a PEP 3147 pyc/pyo file to its legacy pyc/pyo location. - - The choice of .pyc or .pyo extension is done based on the __debug__ flag - value. - - :param source: The file system path to the source file. The source file - does not need to exist, however the PEP 3147 pyc file must exist. - :return: The file system path to the legacy pyc file. - """ - pyc_file = imp.cache_from_source(source) - up_one = os.path.dirname(os.path.abspath(source)) - legacy_pyc = os.path.join(up_one, source + ('c' if __debug__ else 'o')) - os.rename(pyc_file, legacy_pyc) - return legacy_pyc - -def forget(modname): - """'Forget' a module was ever imported. - - This removes the module from sys.modules and deletes any PEP 3147 or - legacy .pyc and .pyo files. - """ - unload(modname) - for dirname in sys.path: - source = os.path.join(dirname, modname + '.py') - # It doesn't matter if they exist or not, unlink all possible - # combinations of PEP 3147 and legacy pyc and pyo files. - unlink(source + 'c') - unlink(source + 'o') - unlink(imp.cache_from_source(source, debug_override=True)) - unlink(imp.cache_from_source(source, debug_override=False)) - -# On some platforms, should not run gui test even if it is allowed -# in `use_resources'. -if sys.platform.startswith('win'): - import ctypes - import ctypes.wintypes - def _is_gui_available(): - UOI_FLAGS = 1 - WSF_VISIBLE = 0x0001 - class USEROBJECTFLAGS(ctypes.Structure): - _fields_ = [("fInherit", ctypes.wintypes.BOOL), - ("fReserved", ctypes.wintypes.BOOL), - ("dwFlags", ctypes.wintypes.DWORD)] - dll = ctypes.windll.user32 - h = dll.GetProcessWindowStation() - if not h: - raise ctypes.WinError() - uof = USEROBJECTFLAGS() - needed = ctypes.wintypes.DWORD() - res = dll.GetUserObjectInformationW(h, - UOI_FLAGS, - ctypes.byref(uof), - ctypes.sizeof(uof), - ctypes.byref(needed)) - if not res: - raise ctypes.WinError() - return bool(uof.dwFlags & WSF_VISIBLE) -else: - def _is_gui_available(): - return True - -def is_resource_enabled(resource): - """Test whether a resource is enabled. Known resources are set by - regrtest.py.""" - return use_resources is not None and resource in use_resources - -def requires(resource, msg=None): - """Raise ResourceDenied if the specified resource is not available. - - If the caller's module is __main__ then automatically return True. The - possibility of False being returned occurs when regrtest.py is - executing. - """ - if resource == 'gui' and not _is_gui_available(): - raise unittest.SkipTest("Cannot use the 'gui' resource") - # see if the caller's module is __main__ - if so, treat as if - # the resource was set - if sys._getframe(1).f_globals.get("__name__") == "__main__": - return - if not is_resource_enabled(resource): - if msg is None: - msg = "Use of the %r resource not enabled" % resource - raise ResourceDenied(msg) - -def _requires_unix_version(sysname, min_version): - """Decorator raising SkipTest if the OS is `sysname` and the version is less - than `min_version`. - - For example, @_requires_unix_version('FreeBSD', (7, 2)) raises SkipTest if - the FreeBSD version is less than 7.2. - """ - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kw): - if platform.system() == sysname: - version_txt = platform.release().split('-', 1)[0] - try: - version = tuple(map(int, version_txt.split('.'))) - except ValueError: - pass - else: - if version < min_version: - min_version_txt = '.'.join(map(str, min_version)) - raise unittest.SkipTest( - "%s version %s or higher required, not %s" - % (sysname, min_version_txt, version_txt)) - return func(*args, **kw) - wrapper.min_version = min_version - return wrapper - return decorator - -def requires_freebsd_version(*min_version): - """Decorator raising SkipTest if the OS is FreeBSD and the FreeBSD version is - less than `min_version`. - - For example, @requires_freebsd_version(7, 2) raises SkipTest if the FreeBSD - version is less than 7.2. - """ - return _requires_unix_version('FreeBSD', min_version) - -def requires_linux_version(*min_version): - """Decorator raising SkipTest if the OS is Linux and the Linux version is - less than `min_version`. - - For example, @requires_linux_version(2, 6, 32) raises SkipTest if the Linux - version is less than 2.6.32. - """ - return _requires_unix_version('Linux', min_version) - -def requires_mac_ver(*min_version): - """Decorator raising SkipTest if the OS is Mac OS X and the OS X - version if less than min_version. - - For example, @requires_mac_ver(10, 5) raises SkipTest if the OS X version - is lesser than 10.5. - """ - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kw): - if sys.platform == 'darwin': - version_txt = platform.mac_ver()[0] - try: - version = tuple(map(int, version_txt.split('.'))) - except ValueError: - pass - else: - if version < min_version: - min_version_txt = '.'.join(map(str, min_version)) - raise unittest.SkipTest( - "Mac OS X %s or higher required, not %s" - % (min_version_txt, version_txt)) - return func(*args, **kw) - wrapper.min_version = min_version - return wrapper - return decorator - -# Don't use "localhost", since resolving it uses the DNS under recent -# Windows versions (see issue #18792). -HOST = "127.0.0.1" -HOSTv6 = "::1" - - -def find_unused_port(family=socket.AF_INET, socktype=socket.SOCK_STREAM): - """Returns an unused port that should be suitable for binding. This is - achieved by creating a temporary socket with the same family and type as - the 'sock' parameter (default is AF_INET, SOCK_STREAM), and binding it to - the specified host address (defaults to 0.0.0.0) with the port set to 0, - eliciting an unused ephemeral port from the OS. The temporary socket is - then closed and deleted, and the ephemeral port is returned. - - Either this method or bind_port() should be used for any tests where a - server socket needs to be bound to a particular port for the duration of - the test. Which one to use depends on whether the calling code is creating - a python socket, or if an unused port needs to be provided in a constructor - or passed to an external program (i.e. the -accept argument to openssl's - s_server mode). Always prefer bind_port() over find_unused_port() where - possible. Hard coded ports should *NEVER* be used. As soon as a server - socket is bound to a hard coded port, the ability to run multiple instances - of the test simultaneously on the same host is compromised, which makes the - test a ticking time bomb in a buildbot environment. On Unix buildbots, this - may simply manifest as a failed test, which can be recovered from without - intervention in most cases, but on Windows, the entire python process can - completely and utterly wedge, requiring someone to log in to the buildbot - and manually kill the affected process. - - (This is easy to reproduce on Windows, unfortunately, and can be traced to - the SO_REUSEADDR socket option having different semantics on Windows versus - Unix/Linux. On Unix, you can't have two AF_INET SOCK_STREAM sockets bind, - listen and then accept connections on identical host/ports. An EADDRINUSE - socket.error will be raised at some point (depending on the platform and - the order bind and listen were called on each socket). - - However, on Windows, if SO_REUSEADDR is set on the sockets, no EADDRINUSE - will ever be raised when attempting to bind two identical host/ports. When - accept() is called on each socket, the second caller's process will steal - the port from the first caller, leaving them both in an awkwardly wedged - state where they'll no longer respond to any signals or graceful kills, and - must be forcibly killed via OpenProcess()/TerminateProcess(). - - The solution on Windows is to use the SO_EXCLUSIVEADDRUSE socket option - instead of SO_REUSEADDR, which effectively affords the same semantics as - SO_REUSEADDR on Unix. Given the propensity of Unix developers in the Open - Source world compared to Windows ones, this is a common mistake. A quick - look over OpenSSL's 0.9.8g source shows that they use SO_REUSEADDR when - openssl.exe is called with the 's_server' option, for example. See - http://bugs.python.org/issue2550 for more info. The following site also - has a very thorough description about the implications of both REUSEADDR - and EXCLUSIVEADDRUSE on Windows: - http://msdn2.microsoft.com/en-us/library/ms740621(VS.85).aspx) - - XXX: although this approach is a vast improvement on previous attempts to - elicit unused ports, it rests heavily on the assumption that the ephemeral - port returned to us by the OS won't immediately be dished back out to some - other process when we close and delete our temporary socket but before our - calling code has a chance to bind the returned port. We can deal with this - issue if/when we come across it. - """ - - tempsock = socket.socket(family, socktype) - port = bind_port(tempsock) - tempsock.close() - del tempsock - return port - -def bind_port(sock, host=HOST): - """Bind the socket to a free port and return the port number. Relies on - ephemeral ports in order to ensure we are using an unbound port. This is - important as many tests may be running simultaneously, especially in a - buildbot environment. This method raises an exception if the sock.family - is AF_INET and sock.type is SOCK_STREAM, *and* the socket has SO_REUSEADDR - or SO_REUSEPORT set on it. Tests should *never* set these socket options - for TCP/IP sockets. The only case for setting these options is testing - multicasting via multiple UDP sockets. - - Additionally, if the SO_EXCLUSIVEADDRUSE socket option is available (i.e. - on Windows), it will be set on the socket. This will prevent anyone else - from bind()'ing to our host/port for the duration of the test. - """ - - if sock.family == socket.AF_INET and sock.type == socket.SOCK_STREAM: - if hasattr(socket, 'SO_REUSEADDR'): - if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) == 1: - raise TestFailed("tests should never set the SO_REUSEADDR " \ - "socket option on TCP/IP sockets!") - if hasattr(socket, 'SO_REUSEPORT'): - try: - if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT) == 1: - raise TestFailed("tests should never set the SO_REUSEPORT " \ - "socket option on TCP/IP sockets!") - except socket.error: - # Python's socket module was compiled using modern headers - # thus defining SO_REUSEPORT but this process is running - # under an older kernel that does not support SO_REUSEPORT. - pass - if hasattr(socket, 'SO_EXCLUSIVEADDRUSE'): - sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) - - sock.bind((host, 0)) - port = sock.getsockname()[1] - return port - -def _is_ipv6_enabled(): - """Check whether IPv6 is enabled on this host.""" - if socket.has_ipv6: - sock = None - try: - sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - sock.bind(('::1', 0)) - return True - except (socket.error, socket.gaierror): - pass - finally: - if sock: - sock.close() - return False - -IPV6_ENABLED = _is_ipv6_enabled() - - -# A constant likely larger than the underlying OS pipe buffer size, to -# make writes blocking. -# Windows limit seems to be around 512 B, and many Unix kernels have a -# 64 KiB pipe buffer size or 16 * PAGE_SIZE: take a few megs to be sure. -# (see issue #17835 for a discussion of this number). -PIPE_MAX_SIZE = 4 * 1024 * 1024 + 1 - -# A constant likely larger than the underlying OS socket buffer size, to make -# writes blocking. -# The socket buffer sizes can usually be tuned system-wide (e.g. through sysctl -# on Linux), or on a per-socket basis (SO_SNDBUF/SO_RCVBUF). See issue #18643 -# for a discussion of this number). -SOCK_MAX_SIZE = 16 * 1024 * 1024 + 1 - -# # decorator for skipping tests on non-IEEE 754 platforms -# requires_IEEE_754 = unittest.skipUnless( -# float.__getformat__("double").startswith("IEEE"), -# "test requires IEEE 754 doubles") - -requires_zlib = unittest.skipUnless(zlib, 'requires zlib') - -requires_bz2 = unittest.skipUnless(bz2, 'requires bz2') - -requires_lzma = unittest.skipUnless(lzma, 'requires lzma') - -is_jython = sys.platform.startswith('java') - -# Filename used for testing -if os.name == 'java': - # Jython disallows @ in module names - TESTFN = '$test' -else: - TESTFN = '@test' - -# Disambiguate TESTFN for parallel testing, while letting it remain a valid -# module name. -TESTFN = "{0}_{1}_tmp".format(TESTFN, os.getpid()) - -# # FS_NONASCII: non-ASCII character encodable by os.fsencode(), -# # or None if there is no such character. -# FS_NONASCII = None -# for character in ( -# # First try printable and common characters to have a readable filename. -# # For each character, the encoding list are just example of encodings able -# # to encode the character (the list is not exhaustive). -# -# # U+00E6 (Latin Small Letter Ae): cp1252, iso-8859-1 -# '\u00E6', -# # U+0130 (Latin Capital Letter I With Dot Above): cp1254, iso8859_3 -# '\u0130', -# # U+0141 (Latin Capital Letter L With Stroke): cp1250, cp1257 -# '\u0141', -# # U+03C6 (Greek Small Letter Phi): cp1253 -# '\u03C6', -# # U+041A (Cyrillic Capital Letter Ka): cp1251 -# '\u041A', -# # U+05D0 (Hebrew Letter Alef): Encodable to cp424 -# '\u05D0', -# # U+060C (Arabic Comma): cp864, cp1006, iso8859_6, mac_arabic -# '\u060C', -# # U+062A (Arabic Letter Teh): cp720 -# '\u062A', -# # U+0E01 (Thai Character Ko Kai): cp874 -# '\u0E01', -# -# # Then try more "special" characters. "special" because they may be -# # interpreted or displayed differently depending on the exact locale -# # encoding and the font. -# -# # U+00A0 (No-Break Space) -# '\u00A0', -# # U+20AC (Euro Sign) -# '\u20AC', -# ): -# try: -# os.fsdecode(os.fsencode(character)) -# except UnicodeError: -# pass -# else: -# FS_NONASCII = character -# break -# -# # TESTFN_UNICODE is a non-ascii filename -# TESTFN_UNICODE = TESTFN + "-\xe0\xf2\u0258\u0141\u011f" -# if sys.platform == 'darwin': -# # In Mac OS X's VFS API file names are, by definition, canonically -# # decomposed Unicode, encoded using UTF-8. See QA1173: -# # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html -# import unicodedata -# TESTFN_UNICODE = unicodedata.normalize('NFD', TESTFN_UNICODE) -# TESTFN_ENCODING = sys.getfilesystemencoding() -# -# # TESTFN_UNENCODABLE is a filename (str type) that should *not* be able to be -# # encoded by the filesystem encoding (in strict mode). It can be None if we -# # cannot generate such filename. -# TESTFN_UNENCODABLE = None -# if os.name in ('nt', 'ce'): -# # skip win32s (0) or Windows 9x/ME (1) -# if sys.getwindowsversion().platform >= 2: -# # Different kinds of characters from various languages to minimize the -# # probability that the whole name is encodable to MBCS (issue #9819) -# TESTFN_UNENCODABLE = TESTFN + "-\u5171\u0141\u2661\u0363\uDC80" -# try: -# TESTFN_UNENCODABLE.encode(TESTFN_ENCODING) -# except UnicodeEncodeError: -# pass -# else: -# print('WARNING: The filename %r CAN be encoded by the filesystem encoding (%s). ' -# 'Unicode filename tests may not be effective' -# % (TESTFN_UNENCODABLE, TESTFN_ENCODING)) -# TESTFN_UNENCODABLE = None -# # Mac OS X denies unencodable filenames (invalid utf-8) -# elif sys.platform != 'darwin': -# try: -# # ascii and utf-8 cannot encode the byte 0xff -# b'\xff'.decode(TESTFN_ENCODING) -# except UnicodeDecodeError: -# # 0xff will be encoded using the surrogate character u+DCFF -# TESTFN_UNENCODABLE = TESTFN \ -# + b'-\xff'.decode(TESTFN_ENCODING, 'surrogateescape') -# else: -# # File system encoding (eg. ISO-8859-* encodings) can encode -# # the byte 0xff. Skip some unicode filename tests. -# pass -# -# # TESTFN_UNDECODABLE is a filename (bytes type) that should *not* be able to be -# # decoded from the filesystem encoding (in strict mode). It can be None if we -# # cannot generate such filename (ex: the latin1 encoding can decode any byte -# # sequence). On UNIX, TESTFN_UNDECODABLE can be decoded by os.fsdecode() thanks -# # to the surrogateescape error handler (PEP 383), but not from the filesystem -# # encoding in strict mode. -# TESTFN_UNDECODABLE = None -# for name in ( -# # b'\xff' is not decodable by os.fsdecode() with code page 932. Windows -# # accepts it to create a file or a directory, or don't accept to enter to -# # such directory (when the bytes name is used). So test b'\xe7' first: it is -# # not decodable from cp932. -# b'\xe7w\xf0', -# # undecodable from ASCII, UTF-8 -# b'\xff', -# # undecodable from iso8859-3, iso8859-6, iso8859-7, cp424, iso8859-8, cp856 -# # and cp857 -# b'\xae\xd5' -# # undecodable from UTF-8 (UNIX and Mac OS X) -# b'\xed\xb2\x80', b'\xed\xb4\x80', -# # undecodable from shift_jis, cp869, cp874, cp932, cp1250, cp1251, cp1252, -# # cp1253, cp1254, cp1255, cp1257, cp1258 -# b'\x81\x98', -# ): -# try: -# name.decode(TESTFN_ENCODING) -# except UnicodeDecodeError: -# TESTFN_UNDECODABLE = os.fsencode(TESTFN) + name -# break -# -# if FS_NONASCII: -# TESTFN_NONASCII = TESTFN + '-' + FS_NONASCII -# else: -# TESTFN_NONASCII = None - -# Save the initial cwd -SAVEDCWD = os.getcwd() - -@contextlib.contextmanager -def temp_cwd(name='tempcwd', quiet=False, path=None): - """ - Context manager that temporarily changes the CWD. - - An existing path may be provided as *path*, in which case this - function makes no changes to the file system. - - Otherwise, the new CWD is created in the current directory and it's - named *name*. If *quiet* is False (default) and it's not possible to - create or change the CWD, an error is raised. If it's True, only a - warning is raised and the original CWD is used. - """ - saved_dir = os.getcwd() - is_temporary = False - if path is None: - path = name - try: - os.mkdir(name) - is_temporary = True - except OSError: - if not quiet: - raise - warnings.warn('tests may fail, unable to create temp CWD ' + name, - RuntimeWarning, stacklevel=3) - try: - os.chdir(path) - except OSError: - if not quiet: - raise - warnings.warn('tests may fail, unable to change the CWD to ' + path, - RuntimeWarning, stacklevel=3) - try: - yield os.getcwd() - finally: - os.chdir(saved_dir) - if is_temporary: - rmtree(name) - - -if hasattr(os, "umask"): - @contextlib.contextmanager - def temp_umask(umask): - """Context manager that temporarily sets the process umask.""" - oldmask = os.umask(umask) - try: - yield - finally: - os.umask(oldmask) - - -def findfile(file, here=__file__, subdir=None): - """Try to find a file on sys.path and the working directory. If it is not - found the argument passed to the function is returned (this does not - necessarily signal failure; could still be the legitimate path).""" - if os.path.isabs(file): - return file - if subdir is not None: - file = os.path.join(subdir, file) - path = sys.path - path = [os.path.dirname(here)] + path - for dn in path: - fn = os.path.join(dn, file) - if os.path.exists(fn): return fn - return file - -def create_empty_file(filename): - """Create an empty file. If the file already exists, truncate it.""" - fd = os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_TRUNC) - os.close(fd) - -def sortdict(dict): - "Like repr(dict), but in sorted order." - items = sorted(dict.items()) - reprpairs = ["%r: %r" % pair for pair in items] - withcommas = ", ".join(reprpairs) - return "{%s}" % withcommas - -def make_bad_fd(): - """ - Create an invalid file descriptor by opening and closing a file and return - its fd. - """ - file = open(TESTFN, "wb") - try: - return file.fileno() - finally: - file.close() - unlink(TESTFN) - -def check_syntax_error(testcase, statement): - testcase.assertRaises(SyntaxError, compile, statement, - '', 'exec') - -def open_urlresource(url, *args, **kw): - from future.backports.urllib import (request as urllib_request, - parse as urllib_parse) - - check = kw.pop('check', None) - - filename = urllib_parse.urlparse(url)[2].split('/')[-1] # '/': it's URL! - - fn = os.path.join(os.path.dirname(__file__), "data", filename) - - def check_valid_file(fn): - f = open(fn, *args, **kw) - if check is None: - return f - elif check(f): - f.seek(0) - return f - f.close() - - if os.path.exists(fn): - f = check_valid_file(fn) - if f is not None: - return f - unlink(fn) - - # Verify the requirement before downloading the file - requires('urlfetch') - - print('\tfetching %s ...' % url, file=get_original_stdout()) - f = urllib_request.urlopen(url, timeout=15) - try: - with open(fn, "wb") as out: - s = f.read() - while s: - out.write(s) - s = f.read() - finally: - f.close() - - f = check_valid_file(fn) - if f is not None: - return f - raise TestFailed('invalid resource %r' % fn) - - -class WarningsRecorder(object): - """Convenience wrapper for the warnings list returned on - entry to the warnings.catch_warnings() context manager. - """ - def __init__(self, warnings_list): - self._warnings = warnings_list - self._last = 0 - - def __getattr__(self, attr): - if len(self._warnings) > self._last: - return getattr(self._warnings[-1], attr) - elif attr in warnings.WarningMessage._WARNING_DETAILS: - return None - raise AttributeError("%r has no attribute %r" % (self, attr)) - - @property - def warnings(self): - return self._warnings[self._last:] - - def reset(self): - self._last = len(self._warnings) - - -def _filterwarnings(filters, quiet=False): - """Catch the warnings, then check if all the expected - warnings have been raised and re-raise unexpected warnings. - If 'quiet' is True, only re-raise the unexpected warnings. - """ - # Clear the warning registry of the calling module - # in order to re-raise the warnings. - frame = sys._getframe(2) - registry = frame.f_globals.get('__warningregistry__') - if registry: - if utils.PY3: - registry.clear() - else: - # Py2-compatible: - for i in range(len(registry)): - registry.pop() - with warnings.catch_warnings(record=True) as w: - # Set filter "always" to record all warnings. Because - # test_warnings swap the module, we need to look up in - # the sys.modules dictionary. - sys.modules['warnings'].simplefilter("always") - yield WarningsRecorder(w) - # Filter the recorded warnings - reraise = list(w) - missing = [] - for msg, cat in filters: - seen = False - for w in reraise[:]: - warning = w.message - # Filter out the matching messages - if (re.match(msg, str(warning), re.I) and - issubclass(warning.__class__, cat)): - seen = True - reraise.remove(w) - if not seen and not quiet: - # This filter caught nothing - missing.append((msg, cat.__name__)) - if reraise: - raise AssertionError("unhandled warning %s" % reraise[0]) - if missing: - raise AssertionError("filter (%r, %s) did not catch any warning" % - missing[0]) - - -@contextlib.contextmanager -def check_warnings(*filters, **kwargs): - """Context manager to silence warnings. - - Accept 2-tuples as positional arguments: - ("message regexp", WarningCategory) - - Optional argument: - - if 'quiet' is True, it does not fail if a filter catches nothing - (default True without argument, - default False if some filters are defined) - - Without argument, it defaults to: - check_warnings(("", Warning), quiet=True) - """ - quiet = kwargs.get('quiet') - if not filters: - filters = (("", Warning),) - # Preserve backward compatibility - if quiet is None: - quiet = True - return _filterwarnings(filters, quiet) - - -class CleanImport(object): - """Context manager to force import to return a new module reference. - - This is useful for testing module-level behaviours, such as - the emission of a DeprecationWarning on import. - - Use like this: - - with CleanImport("foo"): - importlib.import_module("foo") # new reference - """ - - def __init__(self, *module_names): - self.original_modules = sys.modules.copy() - for module_name in module_names: - if module_name in sys.modules: - module = sys.modules[module_name] - # It is possible that module_name is just an alias for - # another module (e.g. stub for modules renamed in 3.x). - # In that case, we also need delete the real module to clear - # the import cache. - if module.__name__ != module_name: - del sys.modules[module.__name__] - del sys.modules[module_name] - - def __enter__(self): - return self - - def __exit__(self, *ignore_exc): - sys.modules.update(self.original_modules) - -### Added for python-future: -if utils.PY3: - import collections.abc - mybase = collections.abc.MutableMapping -else: - import UserDict - mybase = UserDict.DictMixin -### - -class EnvironmentVarGuard(mybase): - - """Class to help protect the environment variable properly. Can be used as - a context manager.""" - - def __init__(self): - self._environ = os.environ - self._changed = {} - - def __getitem__(self, envvar): - return self._environ[envvar] - - def __setitem__(self, envvar, value): - # Remember the initial value on the first access - if envvar not in self._changed: - self._changed[envvar] = self._environ.get(envvar) - self._environ[envvar] = value - - def __delitem__(self, envvar): - # Remember the initial value on the first access - if envvar not in self._changed: - self._changed[envvar] = self._environ.get(envvar) - if envvar in self._environ: - del self._environ[envvar] - - def keys(self): - return self._environ.keys() - - def __iter__(self): - return iter(self._environ) - - def __len__(self): - return len(self._environ) - - def set(self, envvar, value): - self[envvar] = value - - def unset(self, envvar): - del self[envvar] - - def __enter__(self): - return self - - def __exit__(self, *ignore_exc): - for (k, v) in self._changed.items(): - if v is None: - if k in self._environ: - del self._environ[k] - else: - self._environ[k] = v - os.environ = self._environ - - -class DirsOnSysPath(object): - """Context manager to temporarily add directories to sys.path. - - This makes a copy of sys.path, appends any directories given - as positional arguments, then reverts sys.path to the copied - settings when the context ends. - - Note that *all* sys.path modifications in the body of the - context manager, including replacement of the object, - will be reverted at the end of the block. - """ - - def __init__(self, *paths): - self.original_value = sys.path[:] - self.original_object = sys.path - sys.path.extend(paths) - - def __enter__(self): - return self - - def __exit__(self, *ignore_exc): - sys.path = self.original_object - sys.path[:] = self.original_value - - -class TransientResource(object): - - """Raise ResourceDenied if an exception is raised while the context manager - is in effect that matches the specified exception and attributes.""" - - def __init__(self, exc, **kwargs): - self.exc = exc - self.attrs = kwargs - - def __enter__(self): - return self - - def __exit__(self, type_=None, value=None, traceback=None): - """If type_ is a subclass of self.exc and value has attributes matching - self.attrs, raise ResourceDenied. Otherwise let the exception - propagate (if any).""" - if type_ is not None and issubclass(self.exc, type_): - for attr, attr_value in self.attrs.items(): - if not hasattr(value, attr): - break - if getattr(value, attr) != attr_value: - break - else: - raise ResourceDenied("an optional resource is not available") - -# Context managers that raise ResourceDenied when various issues -# with the Internet connection manifest themselves as exceptions. -# XXX deprecate these and use transient_internet() instead -time_out = TransientResource(IOError, errno=errno.ETIMEDOUT) -socket_peer_reset = TransientResource(socket.error, errno=errno.ECONNRESET) -ioerror_peer_reset = TransientResource(IOError, errno=errno.ECONNRESET) - - -@contextlib.contextmanager -def transient_internet(resource_name, timeout=30.0, errnos=()): - """Return a context manager that raises ResourceDenied when various issues - with the Internet connection manifest themselves as exceptions.""" - default_errnos = [ - ('ECONNREFUSED', 111), - ('ECONNRESET', 104), - ('EHOSTUNREACH', 113), - ('ENETUNREACH', 101), - ('ETIMEDOUT', 110), - ] - default_gai_errnos = [ - ('EAI_AGAIN', -3), - ('EAI_FAIL', -4), - ('EAI_NONAME', -2), - ('EAI_NODATA', -5), - # Encountered when trying to resolve IPv6-only hostnames - ('WSANO_DATA', 11004), - ] - - denied = ResourceDenied("Resource %r is not available" % resource_name) - captured_errnos = errnos - gai_errnos = [] - if not captured_errnos: - captured_errnos = [getattr(errno, name, num) - for (name, num) in default_errnos] - gai_errnos = [getattr(socket, name, num) - for (name, num) in default_gai_errnos] - - def filter_error(err): - n = getattr(err, 'errno', None) - if (isinstance(err, socket.timeout) or - (isinstance(err, socket.gaierror) and n in gai_errnos) or - n in captured_errnos): - if not verbose: - sys.stderr.write(denied.args[0] + "\n") - # Was: raise denied from err - # For Python-Future: - exc = denied - exc.__cause__ = err - raise exc - - old_timeout = socket.getdefaulttimeout() - try: - if timeout is not None: - socket.setdefaulttimeout(timeout) - yield - except IOError as err: - # urllib can wrap original socket errors multiple times (!), we must - # unwrap to get at the original error. - while True: - a = err.args - if len(a) >= 1 and isinstance(a[0], IOError): - err = a[0] - # The error can also be wrapped as args[1]: - # except socket.error as msg: - # raise IOError('socket error', msg).with_traceback(sys.exc_info()[2]) - elif len(a) >= 2 and isinstance(a[1], IOError): - err = a[1] - else: - break - filter_error(err) - raise - # XXX should we catch generic exceptions and look for their - # __cause__ or __context__? - finally: - socket.setdefaulttimeout(old_timeout) - - -@contextlib.contextmanager -def captured_output(stream_name): - """Return a context manager used by captured_stdout/stdin/stderr - that temporarily replaces the sys stream *stream_name* with a StringIO.""" - import io - orig_stdout = getattr(sys, stream_name) - setattr(sys, stream_name, io.StringIO()) - try: - yield getattr(sys, stream_name) - finally: - setattr(sys, stream_name, orig_stdout) - -def captured_stdout(): - """Capture the output of sys.stdout: - - with captured_stdout() as s: - print("hello") - self.assertEqual(s.getvalue(), "hello") - """ - return captured_output("stdout") - -def captured_stderr(): - return captured_output("stderr") - -def captured_stdin(): - return captured_output("stdin") - - -def gc_collect(): - """Force as many objects as possible to be collected. - - In non-CPython implementations of Python, this is needed because timely - deallocation is not guaranteed by the garbage collector. (Even in CPython - this can be the case in case of reference cycles.) This means that __del__ - methods may be called later than expected and weakrefs may remain alive for - longer than expected. This function tries its best to force all garbage - objects to disappear. - """ - gc.collect() - if is_jython: - time.sleep(0.1) - gc.collect() - gc.collect() - -@contextlib.contextmanager -def disable_gc(): - have_gc = gc.isenabled() - gc.disable() - try: - yield - finally: - if have_gc: - gc.enable() - - -def python_is_optimized(): - """Find if Python was built with optimizations.""" - # We don't have sysconfig on Py2.6: - import sysconfig - cflags = sysconfig.get_config_var('PY_CFLAGS') or '' - final_opt = "" - for opt in cflags.split(): - if opt.startswith('-O'): - final_opt = opt - return final_opt != '' and final_opt != '-O0' - - -_header = 'nP' -_align = '0n' -if hasattr(sys, "gettotalrefcount"): - _header = '2P' + _header - _align = '0P' -_vheader = _header + 'n' - -def calcobjsize(fmt): - return struct.calcsize(_header + fmt + _align) - -def calcvobjsize(fmt): - return struct.calcsize(_vheader + fmt + _align) - - -_TPFLAGS_HAVE_GC = 1<<14 -_TPFLAGS_HEAPTYPE = 1<<9 - -def check_sizeof(test, o, size): - result = sys.getsizeof(o) - # add GC header size - if ((type(o) == type) and (o.__flags__ & _TPFLAGS_HEAPTYPE) or\ - ((type(o) != type) and (type(o).__flags__ & _TPFLAGS_HAVE_GC))): - size += _testcapi.SIZEOF_PYGC_HEAD - msg = 'wrong size for %s: got %d, expected %d' \ - % (type(o), result, size) - test.assertEqual(result, size, msg) - -#======================================================================= -# Decorator for running a function in a different locale, correctly resetting -# it afterwards. - -def run_with_locale(catstr, *locales): - def decorator(func): - def inner(*args, **kwds): - try: - import locale - category = getattr(locale, catstr) - orig_locale = locale.setlocale(category) - except AttributeError: - # if the test author gives us an invalid category string - raise - except: - # cannot retrieve original locale, so do nothing - locale = orig_locale = None - else: - for loc in locales: - try: - locale.setlocale(category, loc) - break - except: - pass - - # now run the function, resetting the locale on exceptions - try: - return func(*args, **kwds) - finally: - if locale and orig_locale: - locale.setlocale(category, orig_locale) - inner.__name__ = func.__name__ - inner.__doc__ = func.__doc__ - return inner - return decorator - -#======================================================================= -# Decorator for running a function in a specific timezone, correctly -# resetting it afterwards. - -def run_with_tz(tz): - def decorator(func): - def inner(*args, **kwds): - try: - tzset = time.tzset - except AttributeError: - raise unittest.SkipTest("tzset required") - if 'TZ' in os.environ: - orig_tz = os.environ['TZ'] - else: - orig_tz = None - os.environ['TZ'] = tz - tzset() - - # now run the function, resetting the tz on exceptions - try: - return func(*args, **kwds) - finally: - if orig_tz is None: - del os.environ['TZ'] - else: - os.environ['TZ'] = orig_tz - time.tzset() - - inner.__name__ = func.__name__ - inner.__doc__ = func.__doc__ - return inner - return decorator - -#======================================================================= -# Big-memory-test support. Separate from 'resources' because memory use -# should be configurable. - -# Some handy shorthands. Note that these are used for byte-limits as well -# as size-limits, in the various bigmem tests -_1M = 1024*1024 -_1G = 1024 * _1M -_2G = 2 * _1G -_4G = 4 * _1G - -MAX_Py_ssize_t = sys.maxsize - -def set_memlimit(limit): - global max_memuse - global real_max_memuse - sizes = { - 'k': 1024, - 'm': _1M, - 'g': _1G, - 't': 1024*_1G, - } - m = re.match(r'(\d+(\.\d+)?) (K|M|G|T)b?$', limit, - re.IGNORECASE | re.VERBOSE) - if m is None: - raise ValueError('Invalid memory limit %r' % (limit,)) - memlimit = int(float(m.group(1)) * sizes[m.group(3).lower()]) - real_max_memuse = memlimit - if memlimit > MAX_Py_ssize_t: - memlimit = MAX_Py_ssize_t - if memlimit < _2G - 1: - raise ValueError('Memory limit %r too low to be useful' % (limit,)) - max_memuse = memlimit - -class _MemoryWatchdog(object): - """An object which periodically watches the process' memory consumption - and prints it out. - """ - - def __init__(self): - self.procfile = '/proc/{pid}/statm'.format(pid=os.getpid()) - self.started = False - - def start(self): - try: - f = open(self.procfile, 'r') - except OSError as e: - warnings.warn('/proc not available for stats: {0}'.format(e), - RuntimeWarning) - sys.stderr.flush() - return - - watchdog_script = findfile("memory_watchdog.py") - self.mem_watchdog = subprocess.Popen([sys.executable, watchdog_script], - stdin=f, stderr=subprocess.DEVNULL) - f.close() - self.started = True - - def stop(self): - if self.started: - self.mem_watchdog.terminate() - self.mem_watchdog.wait() - - -def bigmemtest(size, memuse, dry_run=True): - """Decorator for bigmem tests. - - 'minsize' is the minimum useful size for the test (in arbitrary, - test-interpreted units.) 'memuse' is the number of 'bytes per size' for - the test, or a good estimate of it. - - if 'dry_run' is False, it means the test doesn't support dummy runs - when -M is not specified. - """ - def decorator(f): - def wrapper(self): - size = wrapper.size - memuse = wrapper.memuse - if not real_max_memuse: - maxsize = 5147 - else: - maxsize = size - - if ((real_max_memuse or not dry_run) - and real_max_memuse < maxsize * memuse): - raise unittest.SkipTest( - "not enough memory: %.1fG minimum needed" - % (size * memuse / (1024 ** 3))) - - if real_max_memuse and verbose: - print() - print(" ... expected peak memory use: {peak:.1f}G" - .format(peak=size * memuse / (1024 ** 3))) - watchdog = _MemoryWatchdog() - watchdog.start() - else: - watchdog = None - - try: - return f(self, maxsize) - finally: - if watchdog: - watchdog.stop() - - wrapper.size = size - wrapper.memuse = memuse - return wrapper - return decorator - -def bigaddrspacetest(f): - """Decorator for tests that fill the address space.""" - def wrapper(self): - if max_memuse < MAX_Py_ssize_t: - if MAX_Py_ssize_t >= 2**63 - 1 and max_memuse >= 2**31: - raise unittest.SkipTest( - "not enough memory: try a 32-bit build instead") - else: - raise unittest.SkipTest( - "not enough memory: %.1fG minimum needed" - % (MAX_Py_ssize_t / (1024 ** 3))) - else: - return f(self) - return wrapper - -#======================================================================= -# unittest integration. - -class BasicTestRunner(object): - def run(self, test): - result = unittest.TestResult() - test(result) - return result - -def _id(obj): - return obj - -def requires_resource(resource): - if resource == 'gui' and not _is_gui_available(): - return unittest.skip("resource 'gui' is not available") - if is_resource_enabled(resource): - return _id - else: - return unittest.skip("resource {0!r} is not enabled".format(resource)) - -def cpython_only(test): - """ - Decorator for tests only applicable on CPython. - """ - return impl_detail(cpython=True)(test) - -def impl_detail(msg=None, **guards): - if check_impl_detail(**guards): - return _id - if msg is None: - guardnames, default = _parse_guards(guards) - if default: - msg = "implementation detail not available on {0}" - else: - msg = "implementation detail specific to {0}" - guardnames = sorted(guardnames.keys()) - msg = msg.format(' or '.join(guardnames)) - return unittest.skip(msg) - -def _parse_guards(guards): - # Returns a tuple ({platform_name: run_me}, default_value) - if not guards: - return ({'cpython': True}, False) - is_true = list(guards.values())[0] - assert list(guards.values()) == [is_true] * len(guards) # all True or all False - return (guards, not is_true) - -# Use the following check to guard CPython's implementation-specific tests -- -# or to run them only on the implementation(s) guarded by the arguments. -def check_impl_detail(**guards): - """This function returns True or False depending on the host platform. - Examples: - if check_impl_detail(): # only on CPython (default) - if check_impl_detail(jython=True): # only on Jython - if check_impl_detail(cpython=False): # everywhere except on CPython - """ - guards, default = _parse_guards(guards) - return guards.get(platform.python_implementation().lower(), default) - - -def no_tracing(func): - """Decorator to temporarily turn off tracing for the duration of a test.""" - if not hasattr(sys, 'gettrace'): - return func - else: - @functools.wraps(func) - def wrapper(*args, **kwargs): - original_trace = sys.gettrace() - try: - sys.settrace(None) - return func(*args, **kwargs) - finally: - sys.settrace(original_trace) - return wrapper - - -def refcount_test(test): - """Decorator for tests which involve reference counting. - - To start, the decorator does not run the test if is not run by CPython. - After that, any trace function is unset during the test to prevent - unexpected refcounts caused by the trace function. - - """ - return no_tracing(cpython_only(test)) - - -def _filter_suite(suite, pred): - """Recursively filter test cases in a suite based on a predicate.""" - newtests = [] - for test in suite._tests: - if isinstance(test, unittest.TestSuite): - _filter_suite(test, pred) - newtests.append(test) - else: - if pred(test): - newtests.append(test) - suite._tests = newtests - -def _run_suite(suite): - """Run tests from a unittest.TestSuite-derived class.""" - if verbose: - runner = unittest.TextTestRunner(sys.stdout, verbosity=2, - failfast=failfast) - else: - runner = BasicTestRunner() - - result = runner.run(suite) - if not result.wasSuccessful(): - if len(result.errors) == 1 and not result.failures: - err = result.errors[0][1] - elif len(result.failures) == 1 and not result.errors: - err = result.failures[0][1] - else: - err = "multiple errors occurred" - if not verbose: err += "; run in verbose mode for details" - raise TestFailed(err) - - -def run_unittest(*classes): - """Run tests from unittest.TestCase-derived classes.""" - valid_types = (unittest.TestSuite, unittest.TestCase) - suite = unittest.TestSuite() - for cls in classes: - if isinstance(cls, str): - if cls in sys.modules: - suite.addTest(unittest.findTestCases(sys.modules[cls])) - else: - raise ValueError("str arguments must be keys in sys.modules") - elif isinstance(cls, valid_types): - suite.addTest(cls) - else: - suite.addTest(unittest.makeSuite(cls)) - def case_pred(test): - if match_tests is None: - return True - for name in test.id().split("."): - if fnmatch.fnmatchcase(name, match_tests): - return True - return False - _filter_suite(suite, case_pred) - _run_suite(suite) - -# We don't have sysconfig on Py2.6: -# #======================================================================= -# # Check for the presence of docstrings. -# -# HAVE_DOCSTRINGS = (check_impl_detail(cpython=False) or -# sys.platform == 'win32' or -# sysconfig.get_config_var('WITH_DOC_STRINGS')) -# -# requires_docstrings = unittest.skipUnless(HAVE_DOCSTRINGS, -# "test requires docstrings") -# -# -# #======================================================================= -# doctest driver. - -def run_doctest(module, verbosity=None, optionflags=0): - """Run doctest on the given module. Return (#failures, #tests). - - If optional argument verbosity is not specified (or is None), pass - support's belief about verbosity on to doctest. Else doctest's - usual behavior is used (it searches sys.argv for -v). - """ - - import doctest - - if verbosity is None: - verbosity = verbose - else: - verbosity = None - - f, t = doctest.testmod(module, verbose=verbosity, optionflags=optionflags) - if f: - raise TestFailed("%d of %d doctests failed" % (f, t)) - if verbose: - print('doctest (%s) ... %d tests with zero failures' % - (module.__name__, t)) - return f, t - - -#======================================================================= -# Support for saving and restoring the imported modules. - -def modules_setup(): - return sys.modules.copy(), - -def modules_cleanup(oldmodules): - # Encoders/decoders are registered permanently within the internal - # codec cache. If we destroy the corresponding modules their - # globals will be set to None which will trip up the cached functions. - encodings = [(k, v) for k, v in sys.modules.items() - if k.startswith('encodings.')] - # Was: - # sys.modules.clear() - # Py2-compatible: - for i in range(len(sys.modules)): - sys.modules.pop() - - sys.modules.update(encodings) - # XXX: This kind of problem can affect more than just encodings. In particular - # extension modules (such as _ssl) don't cope with reloading properly. - # Really, test modules should be cleaning out the test specific modules they - # know they added (ala test_runpy) rather than relying on this function (as - # test_importhooks and test_pkg do currently). - # Implicitly imported *real* modules should be left alone (see issue 10556). - sys.modules.update(oldmodules) - -#======================================================================= -# Backported versions of threading_setup() and threading_cleanup() which don't refer -# to threading._dangling (not available on Py2.7). - -# Threading support to prevent reporting refleaks when running regrtest.py -R - -# NOTE: we use thread._count() rather than threading.enumerate() (or the -# moral equivalent thereof) because a threading.Thread object is still alive -# until its __bootstrap() method has returned, even after it has been -# unregistered from the threading module. -# thread._count(), on the other hand, only gets decremented *after* the -# __bootstrap() method has returned, which gives us reliable reference counts -# at the end of a test run. - -def threading_setup(): - if _thread: - return _thread._count(), - else: - return 1, - -def threading_cleanup(nb_threads): - if not _thread: - return - - _MAX_COUNT = 10 - for count in range(_MAX_COUNT): - n = _thread._count() - if n == nb_threads: - break - time.sleep(0.1) - # XXX print a warning in case of failure? - -def reap_threads(func): - """Use this function when threads are being used. This will - ensure that the threads are cleaned up even when the test fails. - If threading is unavailable this function does nothing. - """ - if not _thread: - return func - - @functools.wraps(func) - def decorator(*args): - key = threading_setup() - try: - return func(*args) - finally: - threading_cleanup(*key) - return decorator - -def reap_children(): - """Use this function at the end of test_main() whenever sub-processes - are started. This will help ensure that no extra children (zombies) - stick around to hog resources and create problems when looking - for refleaks. - """ - - # Reap all our dead child processes so we don't leave zombies around. - # These hog resources and might be causing some of the buildbots to die. - if hasattr(os, 'waitpid'): - any_process = -1 - while True: - try: - # This will raise an exception on Windows. That's ok. - pid, status = os.waitpid(any_process, os.WNOHANG) - if pid == 0: - break - except: - break - -@contextlib.contextmanager -def swap_attr(obj, attr, new_val): - """Temporary swap out an attribute with a new object. - - Usage: - with swap_attr(obj, "attr", 5): - ... - - This will set obj.attr to 5 for the duration of the with: block, - restoring the old value at the end of the block. If `attr` doesn't - exist on `obj`, it will be created and then deleted at the end of the - block. - """ - if hasattr(obj, attr): - real_val = getattr(obj, attr) - setattr(obj, attr, new_val) - try: - yield - finally: - setattr(obj, attr, real_val) - else: - setattr(obj, attr, new_val) - try: - yield - finally: - delattr(obj, attr) - -@contextlib.contextmanager -def swap_item(obj, item, new_val): - """Temporary swap out an item with a new object. - - Usage: - with swap_item(obj, "item", 5): - ... - - This will set obj["item"] to 5 for the duration of the with: block, - restoring the old value at the end of the block. If `item` doesn't - exist on `obj`, it will be created and then deleted at the end of the - block. - """ - if item in obj: - real_val = obj[item] - obj[item] = new_val - try: - yield - finally: - obj[item] = real_val - else: - obj[item] = new_val - try: - yield - finally: - del obj[item] - -def strip_python_stderr(stderr): - """Strip the stderr of a Python process from potential debug output - emitted by the interpreter. - - This will typically be run on the result of the communicate() method - of a subprocess.Popen object. - """ - stderr = re.sub(br"\[\d+ refs\]\r?\n?", b"", stderr).strip() - return stderr - -def args_from_interpreter_flags(): - """Return a list of command-line arguments reproducing the current - settings in sys.flags and sys.warnoptions.""" - return subprocess._args_from_interpreter_flags() - -#============================================================ -# Support for assertions about logging. -#============================================================ - -class TestHandler(logging.handlers.BufferingHandler): - def __init__(self, matcher): - # BufferingHandler takes a "capacity" argument - # so as to know when to flush. As we're overriding - # shouldFlush anyway, we can set a capacity of zero. - # You can call flush() manually to clear out the - # buffer. - logging.handlers.BufferingHandler.__init__(self, 0) - self.matcher = matcher - - def shouldFlush(self): - return False - - def emit(self, record): - self.format(record) - self.buffer.append(record.__dict__) - - def matches(self, **kwargs): - """ - Look for a saved dict whose keys/values match the supplied arguments. - """ - result = False - for d in self.buffer: - if self.matcher.matches(d, **kwargs): - result = True - break - return result - -class Matcher(object): - - _partial_matches = ('msg', 'message') - - def matches(self, d, **kwargs): - """ - Try to match a single dict with the supplied arguments. - - Keys whose values are strings and which are in self._partial_matches - will be checked for partial (i.e. substring) matches. You can extend - this scheme to (for example) do regular expression matching, etc. - """ - result = True - for k in kwargs: - v = kwargs[k] - dv = d.get(k) - if not self.match_value(k, dv, v): - result = False - break - return result - - def match_value(self, k, dv, v): - """ - Try to match a single stored value (dv) with a supplied value (v). - """ - if type(v) != type(dv): - result = False - elif type(dv) is not str or k not in self._partial_matches: - result = (v == dv) - else: - result = dv.find(v) >= 0 - return result - - -_can_symlink = None -def can_symlink(): - global _can_symlink - if _can_symlink is not None: - return _can_symlink - symlink_path = TESTFN + "can_symlink" - try: - os.symlink(TESTFN, symlink_path) - can = True - except (OSError, NotImplementedError, AttributeError): - can = False - else: - os.remove(symlink_path) - _can_symlink = can - return can - -def skip_unless_symlink(test): - """Skip decorator for tests that require functional symlink""" - ok = can_symlink() - msg = "Requires functional symlink implementation" - return test if ok else unittest.skip(msg)(test) - -_can_xattr = None -def can_xattr(): - global _can_xattr - if _can_xattr is not None: - return _can_xattr - if not hasattr(os, "setxattr"): - can = False - else: - tmp_fp, tmp_name = tempfile.mkstemp() - try: - with open(TESTFN, "wb") as fp: - try: - # TESTFN & tempfile may use different file systems with - # different capabilities - os.setxattr(tmp_fp, b"user.test", b"") - os.setxattr(fp.fileno(), b"user.test", b"") - # Kernels < 2.6.39 don't respect setxattr flags. - kernel_version = platform.release() - m = re.match("2.6.(\d{1,2})", kernel_version) - can = m is None or int(m.group(1)) >= 39 - except OSError: - can = False - finally: - unlink(TESTFN) - unlink(tmp_name) - _can_xattr = can - return can - -def skip_unless_xattr(test): - """Skip decorator for tests that require functional extended attributes""" - ok = can_xattr() - msg = "no non-broken extended attribute support" - return test if ok else unittest.skip(msg)(test) - - -if sys.platform.startswith('win'): - @contextlib.contextmanager - def suppress_crash_popup(): - """Disable Windows Error Reporting dialogs using SetErrorMode.""" - # see http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621%28v=vs.85%29.aspx - # GetErrorMode is not available on Windows XP and Windows Server 2003, - # but SetErrorMode returns the previous value, so we can use that - import ctypes - k32 = ctypes.windll.kernel32 - SEM_NOGPFAULTERRORBOX = 0x02 - old_error_mode = k32.SetErrorMode(SEM_NOGPFAULTERRORBOX) - k32.SetErrorMode(old_error_mode | SEM_NOGPFAULTERRORBOX) - try: - yield - finally: - k32.SetErrorMode(old_error_mode) -else: - # this is a no-op for other platforms - @contextlib.contextmanager - def suppress_crash_popup(): - yield - - -def patch(test_instance, object_to_patch, attr_name, new_value): - """Override 'object_to_patch'.'attr_name' with 'new_value'. - - Also, add a cleanup procedure to 'test_instance' to restore - 'object_to_patch' value for 'attr_name'. - The 'attr_name' should be a valid attribute for 'object_to_patch'. - - """ - # check that 'attr_name' is a real attribute for 'object_to_patch' - # will raise AttributeError if it does not exist - getattr(object_to_patch, attr_name) - - # keep a copy of the old value - attr_is_local = False - try: - old_value = object_to_patch.__dict__[attr_name] - except (AttributeError, KeyError): - old_value = getattr(object_to_patch, attr_name, None) - else: - attr_is_local = True - - # restore the value when the test is done - def cleanup(): - if attr_is_local: - setattr(object_to_patch, attr_name, old_value) - else: - delattr(object_to_patch, attr_name) - - test_instance.addCleanup(cleanup) - - # actually override the attribute - setattr(object_to_patch, attr_name, new_value) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/total_ordering.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/total_ordering.py deleted file mode 100644 index 760f06d6..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/total_ordering.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -For Python < 2.7.2. total_ordering in versions prior to 2.7.2 is buggy. -See http://bugs.python.org/issue10042 for details. For these versions use -code borrowed from Python 2.7.3. - -From django.utils. -""" - -import sys -if sys.version_info >= (2, 7, 2): - from functools import total_ordering -else: - def total_ordering(cls): - """Class decorator that fills in missing ordering methods""" - convert = { - '__lt__': [('__gt__', lambda self, other: not (self < other or self == other)), - ('__le__', lambda self, other: self < other or self == other), - ('__ge__', lambda self, other: not self < other)], - '__le__': [('__ge__', lambda self, other: not self <= other or self == other), - ('__lt__', lambda self, other: self <= other and not self == other), - ('__gt__', lambda self, other: not self <= other)], - '__gt__': [('__lt__', lambda self, other: not (self > other or self == other)), - ('__ge__', lambda self, other: self > other or self == other), - ('__le__', lambda self, other: not self > other)], - '__ge__': [('__le__', lambda self, other: (not self >= other) or self == other), - ('__gt__', lambda self, other: self >= other and not self == other), - ('__lt__', lambda self, other: not self >= other)] - } - roots = set(dir(cls)) & set(convert) - if not roots: - raise ValueError('must define at least one ordering operation: < > <= >=') - root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__ - for opname, opfunc in convert[root]: - if opname not in roots: - opfunc.__name__ = opname - opfunc.__doc__ = getattr(int, opname).__doc__ - setattr(cls, opname, opfunc) - return cls diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 49caaa4d3a19d9ddfd22b7bbb0173651b09f901f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 204 zcmYe~<>g`k0%o0Eu^{>}h(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o5j-KQ}ccGg03o zu_(o*D6=e8KcF(BBtK6-I5{IfKS$rw+to3?IJqdXAhk$euPimMOg|?xNxz`7BqKl1 zSkF?wII|>Gw;(Y&J25@ASU;__q_ikiKMBY$$S*1>)-Nr}0jZCV&&{2QW&i-7JT|@n diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__pycache__/error.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__pycache__/error.cpython-39.pyc deleted file mode 100644 index 9e54c199c4f432afd30c4342c3889749ac019aa3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2700 zcmb7GO>f&q5ap7TMALHYq;Ar$G>ZalA;6Ii1qw6>Qn#tm7%ki&b}s}9V#QsVbSP5Y zrR>PiE%_I{B)#P?^r!IJQ%}A3)Hh2@Q4;siHFmt;GxO%nP-0^vbkMw=KYsZQqd#e~ z+FF?GqMHeXbRtJG>E>+gM()^)ys;nojMluoHEu_3JNEO=IEdDmbL`00?<@+xcVt_3 zo;k9k)}MM&7xO@_VZNriYVBm>sUKazS}51CwywH3vnjiWPH#i}%^YX%NunlIRu(+T zg)z!-Ei$7dAI|tx=Xo~lhv8AG_)r+d)$6BuX86yKe%#Z#)I0nv&61R7MXGdG89vhG znD75&H~XCLSD`GGDL${bRHLj=Tof}tRH-=4O3lj=r%UBovBQVcie9c3TCHV#gi|8V z!=(-8;vos0IE)(;kvta13Kl;&I(ld|(?g& zg{ef5C~JFTDA2Kb&IDPWh$$ekmnOQb%B0M>A}F9ytLDvc@tgrZS(QQ;Uah8vC#6(7 zJXJy}jZINV>P>_^dg$^5!gbY}19wxi!=3@xuL=1`G zg3_+^p0UW2zHB{nB4094wxc^Zbdb=UMWeBcejnX%h&davxfA0*B%8a^nKJ#++{Fyz zL+2$sfW^Mh$EI#;B@BEHMp>>3F;=u=R6f#OyrefEUOIXc|NbckV)7hyS zJe;LfSqu)7v@G+%{ttWK#3s>VqV%ADs*2MAuo_HeRazEb^uHPyzQsdOBFhKZqv9d^F8VuIc5 z0o4{6O(@239mesvl+&EX-8lYrD)MGWU&RCZ9TJ4=iXde+z#m{>e2AbOw(UxS$!m?G zzKd?|LM&MWz9|2sb<&PHmOoC^zTi)w)|xW;6eY7MkkPsffylZhY|p=a)Reum6u{V= zZqO-{WJ>u-HK{2U3Cfhsuc9n&H;1%BFpH#(hZc>-)GaOVU|v~Ku{o~&RBBVZqlu+xeLY@g9bz>F11)!Y zZ5z+pL%{V7Jfhzt@jeN{_)Y97GPltUvA_Y>F8*5J+_j~K|0W#;#rjK;Y)A*+>zu!bu6HWCkI*(xE7Y>5m sx%l1GfWhei#mXvPaopN!Yjkk8skZls{5G6^cbj$Ht`~H+Hn(s63kDB)WdHyG diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__pycache__/parse.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__pycache__/parse.cpython-39.pyc deleted file mode 100644 index 01f737795b3fa10f7cc2559b23d6a8b4bc34e635..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28887 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__pycache__/request.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/__pycache__/request.cpython-39.pyc deleted file mode 100644 index 62410c7cabc39733ca425f5dbf819bbf60b4955e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69770 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 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/error.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/error.py deleted file mode 100644 index a473e445..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/error.py +++ /dev/null @@ -1,75 +0,0 @@ -"""Exception classes raised by urllib. - -The base exception class is URLError, which inherits from IOError. It -doesn't define any behavior of its own, but is the base class for all -exceptions defined in this package. - -HTTPError is an exception class that is also a valid HTTP response -instance. It behaves this way because HTTP protocol errors are valid -responses, with a status code, headers, and a body. In some contexts, -an application may want to handle an exception like a regular -response. -""" -from __future__ import absolute_import, division, unicode_literals -from future import standard_library - -from future.backports.urllib import response as urllib_response - - -__all__ = ['URLError', 'HTTPError', 'ContentTooShortError'] - - -# do these error classes make sense? -# make sure all of the IOError stuff is overridden. we just want to be -# subtypes. - -class URLError(IOError): - # URLError is a sub-type of IOError, but it doesn't share any of - # the implementation. need to override __init__ and __str__. - # It sets self.args for compatibility with other EnvironmentError - # subclasses, but args doesn't have the typical format with errno in - # slot 0 and strerror in slot 1. This may be better than nothing. - def __init__(self, reason, filename=None): - self.args = reason, - self.reason = reason - if filename is not None: - self.filename = filename - - def __str__(self): - return '' % self.reason - -class HTTPError(URLError, urllib_response.addinfourl): - """Raised when HTTP error occurs, but also acts like non-error return""" - __super_init = urllib_response.addinfourl.__init__ - - def __init__(self, url, code, msg, hdrs, fp): - self.code = code - self.msg = msg - self.hdrs = hdrs - self.fp = fp - self.filename = url - # The addinfourl classes depend on fp being a valid file - # object. In some cases, the HTTPError may not have a valid - # file object. If this happens, the simplest workaround is to - # not initialize the base classes. - if fp is not None: - self.__super_init(fp, hdrs, url, code) - - def __str__(self): - return 'HTTP Error %s: %s' % (self.code, self.msg) - - # since URLError specifies a .reason attribute, HTTPError should also - # provide this attribute. See issue13211 for discussion. - @property - def reason(self): - return self.msg - - def info(self): - return self.hdrs - - -# exception raised when downloaded size does not match content-length -class ContentTooShortError(URLError): - def __init__(self, message, content): - URLError.__init__(self, message) - self.content = content diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/parse.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/parse.py deleted file mode 100644 index 04e52d49..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/parse.py +++ /dev/null @@ -1,991 +0,0 @@ -""" -Ported using Python-Future from the Python 3.3 standard library. - -Parse (absolute and relative) URLs. - -urlparse module is based upon the following RFC specifications. - -RFC 3986 (STD66): "Uniform Resource Identifiers" by T. Berners-Lee, R. Fielding -and L. Masinter, January 2005. - -RFC 2732 : "Format for Literal IPv6 Addresses in URL's by R.Hinden, B.Carpenter -and L.Masinter, December 1999. - -RFC 2396: "Uniform Resource Identifiers (URI)": Generic Syntax by T. -Berners-Lee, R. Fielding, and L. Masinter, August 1998. - -RFC 2368: "The mailto URL scheme", by P.Hoffman , L Masinter, J. Zawinski, July 1998. - -RFC 1808: "Relative Uniform Resource Locators", by R. Fielding, UC Irvine, June -1995. - -RFC 1738: "Uniform Resource Locators (URL)" by T. Berners-Lee, L. Masinter, M. -McCahill, December 1994 - -RFC 3986 is considered the current standard and any future changes to -urlparse module should conform with it. The urlparse module is -currently not entirely compliant with this RFC due to defacto -scenarios for parsing, and for backward compatibility purposes, some -parsing quirks from older RFCs are retained. The testcases in -test_urlparse.py provides a good indicator of parsing behavior. -""" -from __future__ import absolute_import, division, unicode_literals -from future.builtins import bytes, chr, dict, int, range, str -from future.utils import raise_with_traceback - -import re -import sys -import collections - -__all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag", - "urlsplit", "urlunsplit", "urlencode", "parse_qs", - "parse_qsl", "quote", "quote_plus", "quote_from_bytes", - "unquote", "unquote_plus", "unquote_to_bytes"] - -# A classification of schemes ('' means apply by default) -uses_relative = ['ftp', 'http', 'gopher', 'nntp', 'imap', - 'wais', 'file', 'https', 'shttp', 'mms', - 'prospero', 'rtsp', 'rtspu', '', 'sftp', - 'svn', 'svn+ssh'] -uses_netloc = ['ftp', 'http', 'gopher', 'nntp', 'telnet', - 'imap', 'wais', 'file', 'mms', 'https', 'shttp', - 'snews', 'prospero', 'rtsp', 'rtspu', 'rsync', '', - 'svn', 'svn+ssh', 'sftp', 'nfs', 'git', 'git+ssh'] -uses_params = ['ftp', 'hdl', 'prospero', 'http', 'imap', - 'https', 'shttp', 'rtsp', 'rtspu', 'sip', 'sips', - 'mms', '', 'sftp', 'tel'] - -# These are not actually used anymore, but should stay for backwards -# compatibility. (They are undocumented, but have a public-looking name.) -non_hierarchical = ['gopher', 'hdl', 'mailto', 'news', - 'telnet', 'wais', 'imap', 'snews', 'sip', 'sips'] -uses_query = ['http', 'wais', 'imap', 'https', 'shttp', 'mms', - 'gopher', 'rtsp', 'rtspu', 'sip', 'sips', ''] -uses_fragment = ['ftp', 'hdl', 'http', 'gopher', 'news', - 'nntp', 'wais', 'https', 'shttp', 'snews', - 'file', 'prospero', ''] - -# Characters valid in scheme names -scheme_chars = ('abcdefghijklmnopqrstuvwxyz' - 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - '0123456789' - '+-.') - -# XXX: Consider replacing with functools.lru_cache -MAX_CACHE_SIZE = 20 -_parse_cache = {} - -def clear_cache(): - """Clear the parse cache and the quoters cache.""" - _parse_cache.clear() - _safe_quoters.clear() - - -# Helpers for bytes handling -# For 3.2, we deliberately require applications that -# handle improperly quoted URLs to do their own -# decoding and encoding. If valid use cases are -# presented, we may relax this by using latin-1 -# decoding internally for 3.3 -_implicit_encoding = 'ascii' -_implicit_errors = 'strict' - -def _noop(obj): - return obj - -def _encode_result(obj, encoding=_implicit_encoding, - errors=_implicit_errors): - return obj.encode(encoding, errors) - -def _decode_args(args, encoding=_implicit_encoding, - errors=_implicit_errors): - return tuple(x.decode(encoding, errors) if x else '' for x in args) - -def _coerce_args(*args): - # Invokes decode if necessary to create str args - # and returns the coerced inputs along with - # an appropriate result coercion function - # - noop for str inputs - # - encoding function otherwise - str_input = isinstance(args[0], str) - for arg in args[1:]: - # We special-case the empty string to support the - # "scheme=''" default argument to some functions - if arg and isinstance(arg, str) != str_input: - raise TypeError("Cannot mix str and non-str arguments") - if str_input: - return args + (_noop,) - return _decode_args(args) + (_encode_result,) - -# Result objects are more helpful than simple tuples -class _ResultMixinStr(object): - """Standard approach to encoding parsed results from str to bytes""" - __slots__ = () - - def encode(self, encoding='ascii', errors='strict'): - return self._encoded_counterpart(*(x.encode(encoding, errors) for x in self)) - - -class _ResultMixinBytes(object): - """Standard approach to decoding parsed results from bytes to str""" - __slots__ = () - - def decode(self, encoding='ascii', errors='strict'): - return self._decoded_counterpart(*(x.decode(encoding, errors) for x in self)) - - -class _NetlocResultMixinBase(object): - """Shared methods for the parsed result objects containing a netloc element""" - __slots__ = () - - @property - def username(self): - return self._userinfo[0] - - @property - def password(self): - return self._userinfo[1] - - @property - def hostname(self): - hostname = self._hostinfo[0] - if not hostname: - hostname = None - elif hostname is not None: - hostname = hostname.lower() - return hostname - - @property - def port(self): - port = self._hostinfo[1] - if port is not None: - port = int(port, 10) - # Return None on an illegal port - if not ( 0 <= port <= 65535): - return None - return port - - -class _NetlocResultMixinStr(_NetlocResultMixinBase, _ResultMixinStr): - __slots__ = () - - @property - def _userinfo(self): - netloc = self.netloc - userinfo, have_info, hostinfo = netloc.rpartition('@') - if have_info: - username, have_password, password = userinfo.partition(':') - if not have_password: - password = None - else: - username = password = None - return username, password - - @property - def _hostinfo(self): - netloc = self.netloc - _, _, hostinfo = netloc.rpartition('@') - _, have_open_br, bracketed = hostinfo.partition('[') - if have_open_br: - hostname, _, port = bracketed.partition(']') - _, have_port, port = port.partition(':') - else: - hostname, have_port, port = hostinfo.partition(':') - if not have_port: - port = None - return hostname, port - - -class _NetlocResultMixinBytes(_NetlocResultMixinBase, _ResultMixinBytes): - __slots__ = () - - @property - def _userinfo(self): - netloc = self.netloc - userinfo, have_info, hostinfo = netloc.rpartition(b'@') - if have_info: - username, have_password, password = userinfo.partition(b':') - if not have_password: - password = None - else: - username = password = None - return username, password - - @property - def _hostinfo(self): - netloc = self.netloc - _, _, hostinfo = netloc.rpartition(b'@') - _, have_open_br, bracketed = hostinfo.partition(b'[') - if have_open_br: - hostname, _, port = bracketed.partition(b']') - _, have_port, port = port.partition(b':') - else: - hostname, have_port, port = hostinfo.partition(b':') - if not have_port: - port = None - return hostname, port - - -from collections import namedtuple - -_DefragResultBase = namedtuple('DefragResult', 'url fragment') -_SplitResultBase = namedtuple('SplitResult', 'scheme netloc path query fragment') -_ParseResultBase = namedtuple('ParseResult', 'scheme netloc path params query fragment') - -# For backwards compatibility, alias _NetlocResultMixinStr -# ResultBase is no longer part of the documented API, but it is -# retained since deprecating it isn't worth the hassle -ResultBase = _NetlocResultMixinStr - -# Structured result objects for string data -class DefragResult(_DefragResultBase, _ResultMixinStr): - __slots__ = () - def geturl(self): - if self.fragment: - return self.url + '#' + self.fragment - else: - return self.url - -class SplitResult(_SplitResultBase, _NetlocResultMixinStr): - __slots__ = () - def geturl(self): - return urlunsplit(self) - -class ParseResult(_ParseResultBase, _NetlocResultMixinStr): - __slots__ = () - def geturl(self): - return urlunparse(self) - -# Structured result objects for bytes data -class DefragResultBytes(_DefragResultBase, _ResultMixinBytes): - __slots__ = () - def geturl(self): - if self.fragment: - return self.url + b'#' + self.fragment - else: - return self.url - -class SplitResultBytes(_SplitResultBase, _NetlocResultMixinBytes): - __slots__ = () - def geturl(self): - return urlunsplit(self) - -class ParseResultBytes(_ParseResultBase, _NetlocResultMixinBytes): - __slots__ = () - def geturl(self): - return urlunparse(self) - -# Set up the encode/decode result pairs -def _fix_result_transcoding(): - _result_pairs = ( - (DefragResult, DefragResultBytes), - (SplitResult, SplitResultBytes), - (ParseResult, ParseResultBytes), - ) - for _decoded, _encoded in _result_pairs: - _decoded._encoded_counterpart = _encoded - _encoded._decoded_counterpart = _decoded - -_fix_result_transcoding() -del _fix_result_transcoding - -def urlparse(url, scheme='', allow_fragments=True): - """Parse a URL into 6 components: - :///;?# - Return a 6-tuple: (scheme, netloc, path, params, query, fragment). - Note that we don't break the components up in smaller bits - (e.g. netloc is a single string) and we don't expand % escapes.""" - url, scheme, _coerce_result = _coerce_args(url, scheme) - splitresult = urlsplit(url, scheme, allow_fragments) - scheme, netloc, url, query, fragment = splitresult - if scheme in uses_params and ';' in url: - url, params = _splitparams(url) - else: - params = '' - result = ParseResult(scheme, netloc, url, params, query, fragment) - return _coerce_result(result) - -def _splitparams(url): - if '/' in url: - i = url.find(';', url.rfind('/')) - if i < 0: - return url, '' - else: - i = url.find(';') - return url[:i], url[i+1:] - -def _splitnetloc(url, start=0): - delim = len(url) # position of end of domain part of url, default is end - for c in '/?#': # look for delimiters; the order is NOT important - wdelim = url.find(c, start) # find first of this delim - if wdelim >= 0: # if found - delim = min(delim, wdelim) # use earliest delim position - return url[start:delim], url[delim:] # return (domain, rest) - -def urlsplit(url, scheme='', allow_fragments=True): - """Parse a URL into 5 components: - :///?# - Return a 5-tuple: (scheme, netloc, path, query, fragment). - Note that we don't break the components up in smaller bits - (e.g. netloc is a single string) and we don't expand % escapes.""" - url, scheme, _coerce_result = _coerce_args(url, scheme) - allow_fragments = bool(allow_fragments) - key = url, scheme, allow_fragments, type(url), type(scheme) - cached = _parse_cache.get(key, None) - if cached: - return _coerce_result(cached) - if len(_parse_cache) >= MAX_CACHE_SIZE: # avoid runaway growth - clear_cache() - netloc = query = fragment = '' - i = url.find(':') - if i > 0: - if url[:i] == 'http': # optimize the common case - scheme = url[:i].lower() - url = url[i+1:] - if url[:2] == '//': - netloc, url = _splitnetloc(url, 2) - if (('[' in netloc and ']' not in netloc) or - (']' in netloc and '[' not in netloc)): - raise ValueError("Invalid IPv6 URL") - if allow_fragments and '#' in url: - url, fragment = url.split('#', 1) - if '?' in url: - url, query = url.split('?', 1) - v = SplitResult(scheme, netloc, url, query, fragment) - _parse_cache[key] = v - return _coerce_result(v) - for c in url[:i]: - if c not in scheme_chars: - break - else: - # make sure "url" is not actually a port number (in which case - # "scheme" is really part of the path) - rest = url[i+1:] - if not rest or any(c not in '0123456789' for c in rest): - # not a port number - scheme, url = url[:i].lower(), rest - - if url[:2] == '//': - netloc, url = _splitnetloc(url, 2) - if (('[' in netloc and ']' not in netloc) or - (']' in netloc and '[' not in netloc)): - raise ValueError("Invalid IPv6 URL") - if allow_fragments and '#' in url: - url, fragment = url.split('#', 1) - if '?' in url: - url, query = url.split('?', 1) - v = SplitResult(scheme, netloc, url, query, fragment) - _parse_cache[key] = v - return _coerce_result(v) - -def urlunparse(components): - """Put a parsed URL back together again. This may result in a - slightly different, but equivalent URL, if the URL that was parsed - originally had redundant delimiters, e.g. a ? with an empty query - (the draft states that these are equivalent).""" - scheme, netloc, url, params, query, fragment, _coerce_result = ( - _coerce_args(*components)) - if params: - url = "%s;%s" % (url, params) - return _coerce_result(urlunsplit((scheme, netloc, url, query, fragment))) - -def urlunsplit(components): - """Combine the elements of a tuple as returned by urlsplit() into a - complete URL as a string. The data argument can be any five-item iterable. - This may result in a slightly different, but equivalent URL, if the URL that - was parsed originally had unnecessary delimiters (for example, a ? with an - empty query; the RFC states that these are equivalent).""" - scheme, netloc, url, query, fragment, _coerce_result = ( - _coerce_args(*components)) - if netloc or (scheme and scheme in uses_netloc and url[:2] != '//'): - if url and url[:1] != '/': url = '/' + url - url = '//' + (netloc or '') + url - if scheme: - url = scheme + ':' + url - if query: - url = url + '?' + query - if fragment: - url = url + '#' + fragment - return _coerce_result(url) - -def urljoin(base, url, allow_fragments=True): - """Join a base URL and a possibly relative URL to form an absolute - interpretation of the latter.""" - if not base: - return url - if not url: - return base - base, url, _coerce_result = _coerce_args(base, url) - bscheme, bnetloc, bpath, bparams, bquery, bfragment = \ - urlparse(base, '', allow_fragments) - scheme, netloc, path, params, query, fragment = \ - urlparse(url, bscheme, allow_fragments) - if scheme != bscheme or scheme not in uses_relative: - return _coerce_result(url) - if scheme in uses_netloc: - if netloc: - return _coerce_result(urlunparse((scheme, netloc, path, - params, query, fragment))) - netloc = bnetloc - if path[:1] == '/': - return _coerce_result(urlunparse((scheme, netloc, path, - params, query, fragment))) - if not path and not params: - path = bpath - params = bparams - if not query: - query = bquery - return _coerce_result(urlunparse((scheme, netloc, path, - params, query, fragment))) - segments = bpath.split('/')[:-1] + path.split('/') - # XXX The stuff below is bogus in various ways... - if segments[-1] == '.': - segments[-1] = '' - while '.' in segments: - segments.remove('.') - while 1: - i = 1 - n = len(segments) - 1 - while i < n: - if (segments[i] == '..' - and segments[i-1] not in ('', '..')): - del segments[i-1:i+1] - break - i = i+1 - else: - break - if segments == ['', '..']: - segments[-1] = '' - elif len(segments) >= 2 and segments[-1] == '..': - segments[-2:] = [''] - return _coerce_result(urlunparse((scheme, netloc, '/'.join(segments), - params, query, fragment))) - -def urldefrag(url): - """Removes any existing fragment from URL. - - Returns a tuple of the defragmented URL and the fragment. If - the URL contained no fragments, the second element is the - empty string. - """ - url, _coerce_result = _coerce_args(url) - if '#' in url: - s, n, p, a, q, frag = urlparse(url) - defrag = urlunparse((s, n, p, a, q, '')) - else: - frag = '' - defrag = url - return _coerce_result(DefragResult(defrag, frag)) - -_hexdig = '0123456789ABCDEFabcdef' -_hextobyte = dict(((a + b).encode(), bytes([int(a + b, 16)])) - for a in _hexdig for b in _hexdig) - -def unquote_to_bytes(string): - """unquote_to_bytes('abc%20def') -> b'abc def'.""" - # Note: strings are encoded as UTF-8. This is only an issue if it contains - # unescaped non-ASCII characters, which URIs should not. - if not string: - # Is it a string-like object? - string.split - return bytes(b'') - if isinstance(string, str): - string = string.encode('utf-8') - ### For Python-Future: - # It is already a byte-string object, but force it to be newbytes here on - # Py2: - string = bytes(string) - ### - bits = string.split(b'%') - if len(bits) == 1: - return string - res = [bits[0]] - append = res.append - for item in bits[1:]: - try: - append(_hextobyte[item[:2]]) - append(item[2:]) - except KeyError: - append(b'%') - append(item) - return bytes(b'').join(res) - -_asciire = re.compile('([\x00-\x7f]+)') - -def unquote(string, encoding='utf-8', errors='replace'): - """Replace %xx escapes by their single-character equivalent. The optional - encoding and errors parameters specify how to decode percent-encoded - sequences into Unicode characters, as accepted by the bytes.decode() - method. - By default, percent-encoded sequences are decoded with UTF-8, and invalid - sequences are replaced by a placeholder character. - - unquote('abc%20def') -> 'abc def'. - """ - if '%' not in string: - string.split - return string - if encoding is None: - encoding = 'utf-8' - if errors is None: - errors = 'replace' - bits = _asciire.split(string) - res = [bits[0]] - append = res.append - for i in range(1, len(bits), 2): - append(unquote_to_bytes(bits[i]).decode(encoding, errors)) - append(bits[i + 1]) - return ''.join(res) - -def parse_qs(qs, keep_blank_values=False, strict_parsing=False, - encoding='utf-8', errors='replace'): - """Parse a query given as a string argument. - - Arguments: - - qs: percent-encoded query string to be parsed - - keep_blank_values: flag indicating whether blank values in - percent-encoded queries should be treated as blank strings. - A true value indicates that blanks should be retained as - blank strings. The default false value indicates that - blank values are to be ignored and treated as if they were - not included. - - strict_parsing: flag indicating what to do with parsing errors. - If false (the default), errors are silently ignored. - If true, errors raise a ValueError exception. - - encoding and errors: specify how to decode percent-encoded sequences - into Unicode characters, as accepted by the bytes.decode() method. - """ - parsed_result = {} - pairs = parse_qsl(qs, keep_blank_values, strict_parsing, - encoding=encoding, errors=errors) - for name, value in pairs: - if name in parsed_result: - parsed_result[name].append(value) - else: - parsed_result[name] = [value] - return parsed_result - -def parse_qsl(qs, keep_blank_values=False, strict_parsing=False, - encoding='utf-8', errors='replace'): - """Parse a query given as a string argument. - - Arguments: - - qs: percent-encoded query string to be parsed - - keep_blank_values: flag indicating whether blank values in - percent-encoded queries should be treated as blank strings. A - true value indicates that blanks should be retained as blank - strings. The default false value indicates that blank values - are to be ignored and treated as if they were not included. - - strict_parsing: flag indicating what to do with parsing errors. If - false (the default), errors are silently ignored. If true, - errors raise a ValueError exception. - - encoding and errors: specify how to decode percent-encoded sequences - into Unicode characters, as accepted by the bytes.decode() method. - - Returns a list, as G-d intended. - """ - qs, _coerce_result = _coerce_args(qs) - pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')] - r = [] - for name_value in pairs: - if not name_value and not strict_parsing: - continue - nv = name_value.split('=', 1) - if len(nv) != 2: - if strict_parsing: - raise ValueError("bad query field: %r" % (name_value,)) - # Handle case of a control-name with no equal sign - if keep_blank_values: - nv.append('') - else: - continue - if len(nv[1]) or keep_blank_values: - name = nv[0].replace('+', ' ') - name = unquote(name, encoding=encoding, errors=errors) - name = _coerce_result(name) - value = nv[1].replace('+', ' ') - value = unquote(value, encoding=encoding, errors=errors) - value = _coerce_result(value) - r.append((name, value)) - return r - -def unquote_plus(string, encoding='utf-8', errors='replace'): - """Like unquote(), but also replace plus signs by spaces, as required for - unquoting HTML form values. - - unquote_plus('%7e/abc+def') -> '~/abc def' - """ - string = string.replace('+', ' ') - return unquote(string, encoding, errors) - -_ALWAYS_SAFE = frozenset(bytes(b'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - b'abcdefghijklmnopqrstuvwxyz' - b'0123456789' - b'_.-')) -_ALWAYS_SAFE_BYTES = bytes(_ALWAYS_SAFE) -_safe_quoters = {} - -class Quoter(collections.defaultdict): - """A mapping from bytes (in range(0,256)) to strings. - - String values are percent-encoded byte values, unless the key < 128, and - in the "safe" set (either the specified safe set, or default set). - """ - # Keeps a cache internally, using defaultdict, for efficiency (lookups - # of cached keys don't call Python code at all). - def __init__(self, safe): - """safe: bytes object.""" - self.safe = _ALWAYS_SAFE.union(bytes(safe)) - - def __repr__(self): - # Without this, will just display as a defaultdict - return "" % dict(self) - - def __missing__(self, b): - # Handle a cache miss. Store quoted string in cache and return. - res = chr(b) if b in self.safe else '%{0:02X}'.format(b) - self[b] = res - return res - -def quote(string, safe='/', encoding=None, errors=None): - """quote('abc def') -> 'abc%20def' - - Each part of a URL, e.g. the path info, the query, etc., has a - different set of reserved characters that must be quoted. - - RFC 2396 Uniform Resource Identifiers (URI): Generic Syntax lists - the following reserved characters. - - reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | - "$" | "," - - Each of these characters is reserved in some component of a URL, - but not necessarily in all of them. - - By default, the quote function is intended for quoting the path - section of a URL. Thus, it will not encode '/'. This character - is reserved, but in typical usage the quote function is being - called on a path where the existing slash characters are used as - reserved characters. - - string and safe may be either str or bytes objects. encoding must - not be specified if string is a str. - - The optional encoding and errors parameters specify how to deal with - non-ASCII characters, as accepted by the str.encode method. - By default, encoding='utf-8' (characters are encoded with UTF-8), and - errors='strict' (unsupported characters raise a UnicodeEncodeError). - """ - if isinstance(string, str): - if not string: - return string - if encoding is None: - encoding = 'utf-8' - if errors is None: - errors = 'strict' - string = string.encode(encoding, errors) - else: - if encoding is not None: - raise TypeError("quote() doesn't support 'encoding' for bytes") - if errors is not None: - raise TypeError("quote() doesn't support 'errors' for bytes") - return quote_from_bytes(string, safe) - -def quote_plus(string, safe='', encoding=None, errors=None): - """Like quote(), but also replace ' ' with '+', as required for quoting - HTML form values. Plus signs in the original string are escaped unless - they are included in safe. It also does not have safe default to '/'. - """ - # Check if ' ' in string, where string may either be a str or bytes. If - # there are no spaces, the regular quote will produce the right answer. - if ((isinstance(string, str) and ' ' not in string) or - (isinstance(string, bytes) and b' ' not in string)): - return quote(string, safe, encoding, errors) - if isinstance(safe, str): - space = str(' ') - else: - space = bytes(b' ') - string = quote(string, safe + space, encoding, errors) - return string.replace(' ', '+') - -def quote_from_bytes(bs, safe='/'): - """Like quote(), but accepts a bytes object rather than a str, and does - not perform string-to-bytes encoding. It always returns an ASCII string. - quote_from_bytes(b'abc def\x3f') -> 'abc%20def%3f' - """ - if not isinstance(bs, (bytes, bytearray)): - raise TypeError("quote_from_bytes() expected bytes") - if not bs: - return str('') - ### For Python-Future: - bs = bytes(bs) - ### - if isinstance(safe, str): - # Normalize 'safe' by converting to bytes and removing non-ASCII chars - safe = str(safe).encode('ascii', 'ignore') - else: - ### For Python-Future: - safe = bytes(safe) - ### - safe = bytes([c for c in safe if c < 128]) - if not bs.rstrip(_ALWAYS_SAFE_BYTES + safe): - return bs.decode() - try: - quoter = _safe_quoters[safe] - except KeyError: - _safe_quoters[safe] = quoter = Quoter(safe).__getitem__ - return str('').join([quoter(char) for char in bs]) - -def urlencode(query, doseq=False, safe='', encoding=None, errors=None): - """Encode a sequence of two-element tuples or dictionary into a URL query string. - - If any values in the query arg are sequences and doseq is true, each - sequence element is converted to a separate parameter. - - If the query arg is a sequence of two-element tuples, the order of the - parameters in the output will match the order of parameters in the - input. - - The query arg may be either a string or a bytes type. When query arg is a - string, the safe, encoding and error parameters are sent the quote_plus for - encoding. - """ - - if hasattr(query, "items"): - query = query.items() - else: - # It's a bother at times that strings and string-like objects are - # sequences. - try: - # non-sequence items should not work with len() - # non-empty strings will fail this - if len(query) and not isinstance(query[0], tuple): - raise TypeError - # Zero-length sequences of all types will get here and succeed, - # but that's a minor nit. Since the original implementation - # allowed empty dicts that type of behavior probably should be - # preserved for consistency - except TypeError: - ty, va, tb = sys.exc_info() - raise_with_traceback(TypeError("not a valid non-string sequence " - "or mapping object"), tb) - - l = [] - if not doseq: - for k, v in query: - if isinstance(k, bytes): - k = quote_plus(k, safe) - else: - k = quote_plus(str(k), safe, encoding, errors) - - if isinstance(v, bytes): - v = quote_plus(v, safe) - else: - v = quote_plus(str(v), safe, encoding, errors) - l.append(k + '=' + v) - else: - for k, v in query: - if isinstance(k, bytes): - k = quote_plus(k, safe) - else: - k = quote_plus(str(k), safe, encoding, errors) - - if isinstance(v, bytes): - v = quote_plus(v, safe) - l.append(k + '=' + v) - elif isinstance(v, str): - v = quote_plus(v, safe, encoding, errors) - l.append(k + '=' + v) - else: - try: - # Is this a sufficient test for sequence-ness? - x = len(v) - except TypeError: - # not a sequence - v = quote_plus(str(v), safe, encoding, errors) - l.append(k + '=' + v) - else: - # loop over the sequence - for elt in v: - if isinstance(elt, bytes): - elt = quote_plus(elt, safe) - else: - elt = quote_plus(str(elt), safe, encoding, errors) - l.append(k + '=' + elt) - return str('&').join(l) - -# Utilities to parse URLs (most of these return None for missing parts): -# unwrap('') --> 'type://host/path' -# splittype('type:opaquestring') --> 'type', 'opaquestring' -# splithost('//host[:port]/path') --> 'host[:port]', '/path' -# splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]' -# splitpasswd('user:passwd') -> 'user', 'passwd' -# splitport('host:port') --> 'host', 'port' -# splitquery('/path?query') --> '/path', 'query' -# splittag('/path#tag') --> '/path', 'tag' -# splitattr('/path;attr1=value1;attr2=value2;...') -> -# '/path', ['attr1=value1', 'attr2=value2', ...] -# splitvalue('attr=value') --> 'attr', 'value' -# urllib.parse.unquote('abc%20def') -> 'abc def' -# quote('abc def') -> 'abc%20def') - -def to_bytes(url): - """to_bytes(u"URL") --> 'URL'.""" - # Most URL schemes require ASCII. If that changes, the conversion - # can be relaxed. - # XXX get rid of to_bytes() - if isinstance(url, str): - try: - url = url.encode("ASCII").decode() - except UnicodeError: - raise UnicodeError("URL " + repr(url) + - " contains non-ASCII characters") - return url - -def unwrap(url): - """unwrap('') --> 'type://host/path'.""" - url = str(url).strip() - if url[:1] == '<' and url[-1:] == '>': - url = url[1:-1].strip() - if url[:4] == 'URL:': url = url[4:].strip() - return url - -_typeprog = None -def splittype(url): - """splittype('type:opaquestring') --> 'type', 'opaquestring'.""" - global _typeprog - if _typeprog is None: - import re - _typeprog = re.compile('^([^/:]+):') - - match = _typeprog.match(url) - if match: - scheme = match.group(1) - return scheme.lower(), url[len(scheme) + 1:] - return None, url - -_hostprog = None -def splithost(url): - """splithost('//host[:port]/path') --> 'host[:port]', '/path'.""" - global _hostprog - if _hostprog is None: - import re - _hostprog = re.compile('^//([^/?]*)(.*)$') - - match = _hostprog.match(url) - if match: - host_port = match.group(1) - path = match.group(2) - if path and not path.startswith('/'): - path = '/' + path - return host_port, path - return None, url - -_userprog = None -def splituser(host): - """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" - global _userprog - if _userprog is None: - import re - _userprog = re.compile('^(.*)@(.*)$') - - match = _userprog.match(host) - if match: return match.group(1, 2) - return None, host - -_passwdprog = None -def splitpasswd(user): - """splitpasswd('user:passwd') -> 'user', 'passwd'.""" - global _passwdprog - if _passwdprog is None: - import re - _passwdprog = re.compile('^([^:]*):(.*)$',re.S) - - match = _passwdprog.match(user) - if match: return match.group(1, 2) - return user, None - -# splittag('/path#tag') --> '/path', 'tag' -_portprog = None -def splitport(host): - """splitport('host:port') --> 'host', 'port'.""" - global _portprog - if _portprog is None: - import re - _portprog = re.compile('^(.*):([0-9]+)$') - - match = _portprog.match(host) - if match: return match.group(1, 2) - return host, None - -_nportprog = None -def splitnport(host, defport=-1): - """Split host and port, returning numeric port. - Return given default port if no ':' found; defaults to -1. - Return numerical port if a valid number are found after ':'. - Return None if ':' but not a valid number.""" - global _nportprog - if _nportprog is None: - import re - _nportprog = re.compile('^(.*):(.*)$') - - match = _nportprog.match(host) - if match: - host, port = match.group(1, 2) - try: - if not port: raise ValueError("no digits") - nport = int(port) - except ValueError: - nport = None - return host, nport - return host, defport - -_queryprog = None -def splitquery(url): - """splitquery('/path?query') --> '/path', 'query'.""" - global _queryprog - if _queryprog is None: - import re - _queryprog = re.compile('^(.*)\?([^?]*)$') - - match = _queryprog.match(url) - if match: return match.group(1, 2) - return url, None - -_tagprog = None -def splittag(url): - """splittag('/path#tag') --> '/path', 'tag'.""" - global _tagprog - if _tagprog is None: - import re - _tagprog = re.compile('^(.*)#([^#]*)$') - - match = _tagprog.match(url) - if match: return match.group(1, 2) - return url, None - -def splitattr(url): - """splitattr('/path;attr1=value1;attr2=value2;...') -> - '/path', ['attr1=value1', 'attr2=value2', ...].""" - words = url.split(';') - return words[0], words[1:] - -_valueprog = None -def splitvalue(attr): - """splitvalue('attr=value') --> 'attr', 'value'.""" - global _valueprog - if _valueprog is None: - import re - _valueprog = re.compile('^([^=]*)=(.*)$') - - match = _valueprog.match(attr) - if match: return match.group(1, 2) - return attr, None diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/request.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/request.py deleted file mode 100644 index baee5401..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/request.py +++ /dev/null @@ -1,2647 +0,0 @@ -""" -Ported using Python-Future from the Python 3.3 standard library. - -An extensible library for opening URLs using a variety of protocols - -The simplest way to use this module is to call the urlopen function, -which accepts a string containing a URL or a Request object (described -below). It opens the URL and returns the results as file-like -object; the returned object has some extra methods described below. - -The OpenerDirector manages a collection of Handler objects that do -all the actual work. Each Handler implements a particular protocol or -option. The OpenerDirector is a composite object that invokes the -Handlers needed to open the requested URL. For example, the -HTTPHandler performs HTTP GET and POST requests and deals with -non-error returns. The HTTPRedirectHandler automatically deals with -HTTP 301, 302, 303 and 307 redirect errors, and the HTTPDigestAuthHandler -deals with digest authentication. - -urlopen(url, data=None) -- Basic usage is the same as original -urllib. pass the url and optionally data to post to an HTTP URL, and -get a file-like object back. One difference is that you can also pass -a Request instance instead of URL. Raises a URLError (subclass of -IOError); for HTTP errors, raises an HTTPError, which can also be -treated as a valid response. - -build_opener -- Function that creates a new OpenerDirector instance. -Will install the default handlers. Accepts one or more Handlers as -arguments, either instances or Handler classes that it will -instantiate. If one of the argument is a subclass of the default -handler, the argument will be installed instead of the default. - -install_opener -- Installs a new opener as the default opener. - -objects of interest: - -OpenerDirector -- Sets up the User Agent as the Python-urllib client and manages -the Handler classes, while dealing with requests and responses. - -Request -- An object that encapsulates the state of a request. The -state can be as simple as the URL. It can also include extra HTTP -headers, e.g. a User-Agent. - -BaseHandler -- - -internals: -BaseHandler and parent -_call_chain conventions - -Example usage: - -import urllib.request - -# set up authentication info -authinfo = urllib.request.HTTPBasicAuthHandler() -authinfo.add_password(realm='PDQ Application', - uri='https://mahler:8092/site-updates.py', - user='klem', - passwd='geheim$parole') - -proxy_support = urllib.request.ProxyHandler({"http" : "http://ahad-haam:3128"}) - -# build a new opener that adds authentication and caching FTP handlers -opener = urllib.request.build_opener(proxy_support, authinfo, - urllib.request.CacheFTPHandler) - -# install it -urllib.request.install_opener(opener) - -f = urllib.request.urlopen('http://www.python.org/') -""" - -# XXX issues: -# If an authentication error handler that tries to perform -# authentication for some reason but fails, how should the error be -# signalled? The client needs to know the HTTP error code. But if -# the handler knows that the problem was, e.g., that it didn't know -# that hash algo that requested in the challenge, it would be good to -# pass that information along to the client, too. -# ftp errors aren't handled cleanly -# check digest against correct (i.e. non-apache) implementation - -# Possible extensions: -# complex proxies XXX not sure what exactly was meant by this -# abstract factory for opener - -from __future__ import absolute_import, division, print_function, unicode_literals -from future.builtins import bytes, dict, filter, input, int, map, open, str -from future.utils import PY2, PY3, raise_with_traceback - -import base64 -import bisect -import hashlib -import array - -from future.backports import email -from future.backports.http import client as http_client -from .error import URLError, HTTPError, ContentTooShortError -from .parse import ( - urlparse, urlsplit, urljoin, unwrap, quote, unquote, - splittype, splithost, splitport, splituser, splitpasswd, - splitattr, splitquery, splitvalue, splittag, to_bytes, urlunparse) -from .response import addinfourl, addclosehook - -import io -import os -import posixpath -import re -import socket -import sys -import time -import tempfile -import contextlib -import warnings - -from future.utils import PY2 - -if PY2: - from collections import Iterable -else: - from collections.abc import Iterable - -# check for SSL -try: - import ssl - # Not available in the SSL module in Py2: - from ssl import SSLContext -except ImportError: - _have_ssl = False -else: - _have_ssl = True - -__all__ = [ - # Classes - 'Request', 'OpenerDirector', 'BaseHandler', 'HTTPDefaultErrorHandler', - 'HTTPRedirectHandler', 'HTTPCookieProcessor', 'ProxyHandler', - 'HTTPPasswordMgr', 'HTTPPasswordMgrWithDefaultRealm', - 'AbstractBasicAuthHandler', 'HTTPBasicAuthHandler', 'ProxyBasicAuthHandler', - 'AbstractDigestAuthHandler', 'HTTPDigestAuthHandler', 'ProxyDigestAuthHandler', - 'HTTPHandler', 'FileHandler', 'FTPHandler', 'CacheFTPHandler', - 'UnknownHandler', 'HTTPErrorProcessor', - # Functions - 'urlopen', 'install_opener', 'build_opener', - 'pathname2url', 'url2pathname', 'getproxies', - # Legacy interface - 'urlretrieve', 'urlcleanup', 'URLopener', 'FancyURLopener', -] - -# used in User-Agent header sent -__version__ = sys.version[:3] - -_opener = None -def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, **_3to2kwargs): - if 'cadefault' in _3to2kwargs: cadefault = _3to2kwargs['cadefault']; del _3to2kwargs['cadefault'] - else: cadefault = False - if 'capath' in _3to2kwargs: capath = _3to2kwargs['capath']; del _3to2kwargs['capath'] - else: capath = None - if 'cafile' in _3to2kwargs: cafile = _3to2kwargs['cafile']; del _3to2kwargs['cafile'] - else: cafile = None - global _opener - if cafile or capath or cadefault: - if not _have_ssl: - raise ValueError('SSL support not available') - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - context.options |= ssl.OP_NO_SSLv2 - context.verify_mode = ssl.CERT_REQUIRED - if cafile or capath: - context.load_verify_locations(cafile, capath) - else: - context.set_default_verify_paths() - https_handler = HTTPSHandler(context=context, check_hostname=True) - opener = build_opener(https_handler) - elif _opener is None: - _opener = opener = build_opener() - else: - opener = _opener - return opener.open(url, data, timeout) - -def install_opener(opener): - global _opener - _opener = opener - -_url_tempfiles = [] -def urlretrieve(url, filename=None, reporthook=None, data=None): - """ - Retrieve a URL into a temporary location on disk. - - Requires a URL argument. If a filename is passed, it is used as - the temporary file location. The reporthook argument should be - a callable that accepts a block number, a read size, and the - total file size of the URL target. The data argument should be - valid URL encoded data. - - If a filename is passed and the URL points to a local resource, - the result is a copy from local file to new file. - - Returns a tuple containing the path to the newly created - data file as well as the resulting HTTPMessage object. - """ - url_type, path = splittype(url) - - with contextlib.closing(urlopen(url, data)) as fp: - headers = fp.info() - - # Just return the local path and the "headers" for file:// - # URLs. No sense in performing a copy unless requested. - if url_type == "file" and not filename: - return os.path.normpath(path), headers - - # Handle temporary file setup. - if filename: - tfp = open(filename, 'wb') - else: - tfp = tempfile.NamedTemporaryFile(delete=False) - filename = tfp.name - _url_tempfiles.append(filename) - - with tfp: - result = filename, headers - bs = 1024*8 - size = -1 - read = 0 - blocknum = 0 - if "content-length" in headers: - size = int(headers["Content-Length"]) - - if reporthook: - reporthook(blocknum, bs, size) - - while True: - block = fp.read(bs) - if not block: - break - read += len(block) - tfp.write(block) - blocknum += 1 - if reporthook: - reporthook(blocknum, bs, size) - - if size >= 0 and read < size: - raise ContentTooShortError( - "retrieval incomplete: got only %i out of %i bytes" - % (read, size), result) - - return result - -def urlcleanup(): - for temp_file in _url_tempfiles: - try: - os.unlink(temp_file) - except EnvironmentError: - pass - - del _url_tempfiles[:] - global _opener - if _opener: - _opener = None - -if PY3: - _cut_port_re = re.compile(r":\d+$", re.ASCII) -else: - _cut_port_re = re.compile(r":\d+$") - -def request_host(request): - - """Return request-host, as defined by RFC 2965. - - Variation from RFC: returned value is lowercased, for convenient - comparison. - - """ - url = request.full_url - host = urlparse(url)[1] - if host == "": - host = request.get_header("Host", "") - - # remove port, if present - host = _cut_port_re.sub("", host, 1) - return host.lower() - -class Request(object): - - def __init__(self, url, data=None, headers={}, - origin_req_host=None, unverifiable=False, - method=None): - # unwrap('') --> 'type://host/path' - self.full_url = unwrap(url) - self.full_url, self.fragment = splittag(self.full_url) - self.data = data - self.headers = {} - self._tunnel_host = None - for key, value in headers.items(): - self.add_header(key, value) - self.unredirected_hdrs = {} - if origin_req_host is None: - origin_req_host = request_host(self) - self.origin_req_host = origin_req_host - self.unverifiable = unverifiable - self.method = method - self._parse() - - def _parse(self): - self.type, rest = splittype(self.full_url) - if self.type is None: - raise ValueError("unknown url type: %r" % self.full_url) - self.host, self.selector = splithost(rest) - if self.host: - self.host = unquote(self.host) - - def get_method(self): - """Return a string indicating the HTTP request method.""" - if self.method is not None: - return self.method - elif self.data is not None: - return "POST" - else: - return "GET" - - def get_full_url(self): - if self.fragment: - return '%s#%s' % (self.full_url, self.fragment) - else: - return self.full_url - - # Begin deprecated methods - - def add_data(self, data): - msg = "Request.add_data method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - self.data = data - - def has_data(self): - msg = "Request.has_data method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.data is not None - - def get_data(self): - msg = "Request.get_data method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.data - - def get_type(self): - msg = "Request.get_type method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.type - - def get_host(self): - msg = "Request.get_host method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.host - - def get_selector(self): - msg = "Request.get_selector method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.selector - - def is_unverifiable(self): - msg = "Request.is_unverifiable method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.unverifiable - - def get_origin_req_host(self): - msg = "Request.get_origin_req_host method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.origin_req_host - - # End deprecated methods - - def set_proxy(self, host, type): - if self.type == 'https' and not self._tunnel_host: - self._tunnel_host = self.host - else: - self.type= type - self.selector = self.full_url - self.host = host - - def has_proxy(self): - return self.selector == self.full_url - - def add_header(self, key, val): - # useful for something like authentication - self.headers[key.capitalize()] = val - - def add_unredirected_header(self, key, val): - # will not be added to a redirected request - self.unredirected_hdrs[key.capitalize()] = val - - def has_header(self, header_name): - return (header_name in self.headers or - header_name in self.unredirected_hdrs) - - def get_header(self, header_name, default=None): - return self.headers.get( - header_name, - self.unredirected_hdrs.get(header_name, default)) - - def header_items(self): - hdrs = self.unredirected_hdrs.copy() - hdrs.update(self.headers) - return list(hdrs.items()) - -class OpenerDirector(object): - def __init__(self): - client_version = "Python-urllib/%s" % __version__ - self.addheaders = [('User-agent', client_version)] - # self.handlers is retained only for backward compatibility - self.handlers = [] - # manage the individual handlers - self.handle_open = {} - self.handle_error = {} - self.process_response = {} - self.process_request = {} - - def add_handler(self, handler): - if not hasattr(handler, "add_parent"): - raise TypeError("expected BaseHandler instance, got %r" % - type(handler)) - - added = False - for meth in dir(handler): - if meth in ["redirect_request", "do_open", "proxy_open"]: - # oops, coincidental match - continue - - i = meth.find("_") - protocol = meth[:i] - condition = meth[i+1:] - - if condition.startswith("error"): - j = condition.find("_") + i + 1 - kind = meth[j+1:] - try: - kind = int(kind) - except ValueError: - pass - lookup = self.handle_error.get(protocol, {}) - self.handle_error[protocol] = lookup - elif condition == "open": - kind = protocol - lookup = self.handle_open - elif condition == "response": - kind = protocol - lookup = self.process_response - elif condition == "request": - kind = protocol - lookup = self.process_request - else: - continue - - handlers = lookup.setdefault(kind, []) - if handlers: - bisect.insort(handlers, handler) - else: - handlers.append(handler) - added = True - - if added: - bisect.insort(self.handlers, handler) - handler.add_parent(self) - - def close(self): - # Only exists for backwards compatibility. - pass - - def _call_chain(self, chain, kind, meth_name, *args): - # Handlers raise an exception if no one else should try to handle - # the request, or return None if they can't but another handler - # could. Otherwise, they return the response. - handlers = chain.get(kind, ()) - for handler in handlers: - func = getattr(handler, meth_name) - result = func(*args) - if result is not None: - return result - - def open(self, fullurl, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): - """ - Accept a URL or a Request object - - Python-Future: if the URL is passed as a byte-string, decode it first. - """ - if isinstance(fullurl, bytes): - fullurl = fullurl.decode() - if isinstance(fullurl, str): - req = Request(fullurl, data) - else: - req = fullurl - if data is not None: - req.data = data - - req.timeout = timeout - protocol = req.type - - # pre-process request - meth_name = protocol+"_request" - for processor in self.process_request.get(protocol, []): - meth = getattr(processor, meth_name) - req = meth(req) - - response = self._open(req, data) - - # post-process response - meth_name = protocol+"_response" - for processor in self.process_response.get(protocol, []): - meth = getattr(processor, meth_name) - response = meth(req, response) - - return response - - def _open(self, req, data=None): - result = self._call_chain(self.handle_open, 'default', - 'default_open', req) - if result: - return result - - protocol = req.type - result = self._call_chain(self.handle_open, protocol, protocol + - '_open', req) - if result: - return result - - return self._call_chain(self.handle_open, 'unknown', - 'unknown_open', req) - - def error(self, proto, *args): - if proto in ('http', 'https'): - # XXX http[s] protocols are special-cased - dict = self.handle_error['http'] # https is not different than http - proto = args[2] # YUCK! - meth_name = 'http_error_%s' % proto - http_err = 1 - orig_args = args - else: - dict = self.handle_error - meth_name = proto + '_error' - http_err = 0 - args = (dict, proto, meth_name) + args - result = self._call_chain(*args) - if result: - return result - - if http_err: - args = (dict, 'default', 'http_error_default') + orig_args - return self._call_chain(*args) - -# XXX probably also want an abstract factory that knows when it makes -# sense to skip a superclass in favor of a subclass and when it might -# make sense to include both - -def build_opener(*handlers): - """Create an opener object from a list of handlers. - - The opener will use several default handlers, including support - for HTTP, FTP and when applicable HTTPS. - - If any of the handlers passed as arguments are subclasses of the - default handlers, the default handlers will not be used. - """ - def isclass(obj): - return isinstance(obj, type) or hasattr(obj, "__bases__") - - opener = OpenerDirector() - default_classes = [ProxyHandler, UnknownHandler, HTTPHandler, - HTTPDefaultErrorHandler, HTTPRedirectHandler, - FTPHandler, FileHandler, HTTPErrorProcessor] - if hasattr(http_client, "HTTPSConnection"): - default_classes.append(HTTPSHandler) - skip = set() - for klass in default_classes: - for check in handlers: - if isclass(check): - if issubclass(check, klass): - skip.add(klass) - elif isinstance(check, klass): - skip.add(klass) - for klass in skip: - default_classes.remove(klass) - - for klass in default_classes: - opener.add_handler(klass()) - - for h in handlers: - if isclass(h): - h = h() - opener.add_handler(h) - return opener - -class BaseHandler(object): - handler_order = 500 - - def add_parent(self, parent): - self.parent = parent - - def close(self): - # Only exists for backwards compatibility - pass - - def __lt__(self, other): - if not hasattr(other, "handler_order"): - # Try to preserve the old behavior of having custom classes - # inserted after default ones (works only for custom user - # classes which are not aware of handler_order). - return True - return self.handler_order < other.handler_order - - -class HTTPErrorProcessor(BaseHandler): - """Process HTTP error responses.""" - handler_order = 1000 # after all other processing - - def http_response(self, request, response): - code, msg, hdrs = response.code, response.msg, response.info() - - # According to RFC 2616, "2xx" code indicates that the client's - # request was successfully received, understood, and accepted. - if not (200 <= code < 300): - response = self.parent.error( - 'http', request, response, code, msg, hdrs) - - return response - - https_response = http_response - -class HTTPDefaultErrorHandler(BaseHandler): - def http_error_default(self, req, fp, code, msg, hdrs): - raise HTTPError(req.full_url, code, msg, hdrs, fp) - -class HTTPRedirectHandler(BaseHandler): - # maximum number of redirections to any single URL - # this is needed because of the state that cookies introduce - max_repeats = 4 - # maximum total number of redirections (regardless of URL) before - # assuming we're in a loop - max_redirections = 10 - - def redirect_request(self, req, fp, code, msg, headers, newurl): - """Return a Request or None in response to a redirect. - - This is called by the http_error_30x methods when a - redirection response is received. If a redirection should - take place, return a new Request to allow http_error_30x to - perform the redirect. Otherwise, raise HTTPError if no-one - else should try to handle this url. Return None if you can't - but another Handler might. - """ - m = req.get_method() - if (not (code in (301, 302, 303, 307) and m in ("GET", "HEAD") - or code in (301, 302, 303) and m == "POST")): - raise HTTPError(req.full_url, code, msg, headers, fp) - - # Strictly (according to RFC 2616), 301 or 302 in response to - # a POST MUST NOT cause a redirection without confirmation - # from the user (of urllib.request, in this case). In practice, - # essentially all clients do redirect in this case, so we do - # the same. - # be conciliant with URIs containing a space - newurl = newurl.replace(' ', '%20') - CONTENT_HEADERS = ("content-length", "content-type") - newheaders = dict((k, v) for k, v in req.headers.items() - if k.lower() not in CONTENT_HEADERS) - return Request(newurl, - headers=newheaders, - origin_req_host=req.origin_req_host, - unverifiable=True) - - # Implementation note: To avoid the server sending us into an - # infinite loop, the request object needs to track what URLs we - # have already seen. Do this by adding a handler-specific - # attribute to the Request object. - def http_error_302(self, req, fp, code, msg, headers): - # Some servers (incorrectly) return multiple Location headers - # (so probably same goes for URI). Use first header. - if "location" in headers: - newurl = headers["location"] - elif "uri" in headers: - newurl = headers["uri"] - else: - return - - # fix a possible malformed URL - urlparts = urlparse(newurl) - - # For security reasons we don't allow redirection to anything other - # than http, https or ftp. - - if urlparts.scheme not in ('http', 'https', 'ftp', ''): - raise HTTPError( - newurl, code, - "%s - Redirection to url '%s' is not allowed" % (msg, newurl), - headers, fp) - - if not urlparts.path: - urlparts = list(urlparts) - urlparts[2] = "/" - newurl = urlunparse(urlparts) - - newurl = urljoin(req.full_url, newurl) - - # XXX Probably want to forget about the state of the current - # request, although that might interact poorly with other - # handlers that also use handler-specific request attributes - new = self.redirect_request(req, fp, code, msg, headers, newurl) - if new is None: - return - - # loop detection - # .redirect_dict has a key url if url was previously visited. - if hasattr(req, 'redirect_dict'): - visited = new.redirect_dict = req.redirect_dict - if (visited.get(newurl, 0) >= self.max_repeats or - len(visited) >= self.max_redirections): - raise HTTPError(req.full_url, code, - self.inf_msg + msg, headers, fp) - else: - visited = new.redirect_dict = req.redirect_dict = {} - visited[newurl] = visited.get(newurl, 0) + 1 - - # Don't close the fp until we are sure that we won't use it - # with HTTPError. - fp.read() - fp.close() - - return self.parent.open(new, timeout=req.timeout) - - http_error_301 = http_error_303 = http_error_307 = http_error_302 - - inf_msg = "The HTTP server returned a redirect error that would " \ - "lead to an infinite loop.\n" \ - "The last 30x error message was:\n" - - -def _parse_proxy(proxy): - """Return (scheme, user, password, host/port) given a URL or an authority. - - If a URL is supplied, it must have an authority (host:port) component. - According to RFC 3986, having an authority component means the URL must - have two slashes after the scheme: - - >>> _parse_proxy('file:/ftp.example.com/') - Traceback (most recent call last): - ValueError: proxy URL with no authority: 'file:/ftp.example.com/' - - The first three items of the returned tuple may be None. - - Examples of authority parsing: - - >>> _parse_proxy('proxy.example.com') - (None, None, None, 'proxy.example.com') - >>> _parse_proxy('proxy.example.com:3128') - (None, None, None, 'proxy.example.com:3128') - - The authority component may optionally include userinfo (assumed to be - username:password): - - >>> _parse_proxy('joe:password@proxy.example.com') - (None, 'joe', 'password', 'proxy.example.com') - >>> _parse_proxy('joe:password@proxy.example.com:3128') - (None, 'joe', 'password', 'proxy.example.com:3128') - - Same examples, but with URLs instead: - - >>> _parse_proxy('http://proxy.example.com/') - ('http', None, None, 'proxy.example.com') - >>> _parse_proxy('http://proxy.example.com:3128/') - ('http', None, None, 'proxy.example.com:3128') - >>> _parse_proxy('http://joe:password@proxy.example.com/') - ('http', 'joe', 'password', 'proxy.example.com') - >>> _parse_proxy('http://joe:password@proxy.example.com:3128') - ('http', 'joe', 'password', 'proxy.example.com:3128') - - Everything after the authority is ignored: - - >>> _parse_proxy('ftp://joe:password@proxy.example.com/rubbish:3128') - ('ftp', 'joe', 'password', 'proxy.example.com') - - Test for no trailing '/' case: - - >>> _parse_proxy('http://joe:password@proxy.example.com') - ('http', 'joe', 'password', 'proxy.example.com') - - """ - scheme, r_scheme = splittype(proxy) - if not r_scheme.startswith("/"): - # authority - scheme = None - authority = proxy - else: - # URL - if not r_scheme.startswith("//"): - raise ValueError("proxy URL with no authority: %r" % proxy) - # We have an authority, so for RFC 3986-compliant URLs (by ss 3. - # and 3.3.), path is empty or starts with '/' - end = r_scheme.find("/", 2) - if end == -1: - end = None - authority = r_scheme[2:end] - userinfo, hostport = splituser(authority) - if userinfo is not None: - user, password = splitpasswd(userinfo) - else: - user = password = None - return scheme, user, password, hostport - -class ProxyHandler(BaseHandler): - # Proxies must be in front - handler_order = 100 - - def __init__(self, proxies=None): - if proxies is None: - proxies = getproxies() - assert hasattr(proxies, 'keys'), "proxies must be a mapping" - self.proxies = proxies - for type, url in proxies.items(): - setattr(self, '%s_open' % type, - lambda r, proxy=url, type=type, meth=self.proxy_open: - meth(r, proxy, type)) - - def proxy_open(self, req, proxy, type): - orig_type = req.type - proxy_type, user, password, hostport = _parse_proxy(proxy) - if proxy_type is None: - proxy_type = orig_type - - if req.host and proxy_bypass(req.host): - return None - - if user and password: - user_pass = '%s:%s' % (unquote(user), - unquote(password)) - creds = base64.b64encode(user_pass.encode()).decode("ascii") - req.add_header('Proxy-authorization', 'Basic ' + creds) - hostport = unquote(hostport) - req.set_proxy(hostport, proxy_type) - if orig_type == proxy_type or orig_type == 'https': - # let other handlers take care of it - return None - else: - # need to start over, because the other handlers don't - # grok the proxy's URL type - # e.g. if we have a constructor arg proxies like so: - # {'http': 'ftp://proxy.example.com'}, we may end up turning - # a request for http://acme.example.com/a into one for - # ftp://proxy.example.com/a - return self.parent.open(req, timeout=req.timeout) - -class HTTPPasswordMgr(object): - - def __init__(self): - self.passwd = {} - - def add_password(self, realm, uri, user, passwd): - # uri could be a single URI or a sequence - if isinstance(uri, str): - uri = [uri] - if realm not in self.passwd: - self.passwd[realm] = {} - for default_port in True, False: - reduced_uri = tuple( - [self.reduce_uri(u, default_port) for u in uri]) - self.passwd[realm][reduced_uri] = (user, passwd) - - def find_user_password(self, realm, authuri): - domains = self.passwd.get(realm, {}) - for default_port in True, False: - reduced_authuri = self.reduce_uri(authuri, default_port) - for uris, authinfo in domains.items(): - for uri in uris: - if self.is_suburi(uri, reduced_authuri): - return authinfo - return None, None - - def reduce_uri(self, uri, default_port=True): - """Accept authority or URI and extract only the authority and path.""" - # note HTTP URLs do not have a userinfo component - parts = urlsplit(uri) - if parts[1]: - # URI - scheme = parts[0] - authority = parts[1] - path = parts[2] or '/' - else: - # host or host:port - scheme = None - authority = uri - path = '/' - host, port = splitport(authority) - if default_port and port is None and scheme is not None: - dport = {"http": 80, - "https": 443, - }.get(scheme) - if dport is not None: - authority = "%s:%d" % (host, dport) - return authority, path - - def is_suburi(self, base, test): - """Check if test is below base in a URI tree - - Both args must be URIs in reduced form. - """ - if base == test: - return True - if base[0] != test[0]: - return False - common = posixpath.commonprefix((base[1], test[1])) - if len(common) == len(base[1]): - return True - return False - - -class HTTPPasswordMgrWithDefaultRealm(HTTPPasswordMgr): - - def find_user_password(self, realm, authuri): - user, password = HTTPPasswordMgr.find_user_password(self, realm, - authuri) - if user is not None: - return user, password - return HTTPPasswordMgr.find_user_password(self, None, authuri) - - -class AbstractBasicAuthHandler(object): - - # XXX this allows for multiple auth-schemes, but will stupidly pick - # the last one with a realm specified. - - # allow for double- and single-quoted realm values - # (single quotes are a violation of the RFC, but appear in the wild) - rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+' - 'realm=(["\']?)([^"\']*)\\2', re.I) - - # XXX could pre-emptively send auth info already accepted (RFC 2617, - # end of section 2, and section 1.2 immediately after "credentials" - # production). - - def __init__(self, password_mgr=None): - if password_mgr is None: - password_mgr = HTTPPasswordMgr() - self.passwd = password_mgr - self.add_password = self.passwd.add_password - self.retried = 0 - - def reset_retry_count(self): - self.retried = 0 - - def http_error_auth_reqed(self, authreq, host, req, headers): - # host may be an authority (without userinfo) or a URL with an - # authority - # XXX could be multiple headers - authreq = headers.get(authreq, None) - - if self.retried > 5: - # retry sending the username:password 5 times before failing. - raise HTTPError(req.get_full_url(), 401, "basic auth failed", - headers, None) - else: - self.retried += 1 - - if authreq: - scheme = authreq.split()[0] - if scheme.lower() != 'basic': - raise ValueError("AbstractBasicAuthHandler does not" - " support the following scheme: '%s'" % - scheme) - else: - mo = AbstractBasicAuthHandler.rx.search(authreq) - if mo: - scheme, quote, realm = mo.groups() - if quote not in ['"',"'"]: - warnings.warn("Basic Auth Realm was unquoted", - UserWarning, 2) - if scheme.lower() == 'basic': - response = self.retry_http_basic_auth(host, req, realm) - if response and response.code != 401: - self.retried = 0 - return response - - def retry_http_basic_auth(self, host, req, realm): - user, pw = self.passwd.find_user_password(realm, host) - if pw is not None: - raw = "%s:%s" % (user, pw) - auth = "Basic " + base64.b64encode(raw.encode()).decode("ascii") - if req.headers.get(self.auth_header, None) == auth: - return None - req.add_unredirected_header(self.auth_header, auth) - return self.parent.open(req, timeout=req.timeout) - else: - return None - - -class HTTPBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler): - - auth_header = 'Authorization' - - def http_error_401(self, req, fp, code, msg, headers): - url = req.full_url - response = self.http_error_auth_reqed('www-authenticate', - url, req, headers) - self.reset_retry_count() - return response - - -class ProxyBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler): - - auth_header = 'Proxy-authorization' - - def http_error_407(self, req, fp, code, msg, headers): - # http_error_auth_reqed requires that there is no userinfo component in - # authority. Assume there isn't one, since urllib.request does not (and - # should not, RFC 3986 s. 3.2.1) support requests for URLs containing - # userinfo. - authority = req.host - response = self.http_error_auth_reqed('proxy-authenticate', - authority, req, headers) - self.reset_retry_count() - return response - - -# Return n random bytes. -_randombytes = os.urandom - - -class AbstractDigestAuthHandler(object): - # Digest authentication is specified in RFC 2617. - - # XXX The client does not inspect the Authentication-Info header - # in a successful response. - - # XXX It should be possible to test this implementation against - # a mock server that just generates a static set of challenges. - - # XXX qop="auth-int" supports is shaky - - def __init__(self, passwd=None): - if passwd is None: - passwd = HTTPPasswordMgr() - self.passwd = passwd - self.add_password = self.passwd.add_password - self.retried = 0 - self.nonce_count = 0 - self.last_nonce = None - - def reset_retry_count(self): - self.retried = 0 - - def http_error_auth_reqed(self, auth_header, host, req, headers): - authreq = headers.get(auth_header, None) - if self.retried > 5: - # Don't fail endlessly - if we failed once, we'll probably - # fail a second time. Hm. Unless the Password Manager is - # prompting for the information. Crap. This isn't great - # but it's better than the current 'repeat until recursion - # depth exceeded' approach - raise HTTPError(req.full_url, 401, "digest auth failed", - headers, None) - else: - self.retried += 1 - if authreq: - scheme = authreq.split()[0] - if scheme.lower() == 'digest': - return self.retry_http_digest_auth(req, authreq) - elif scheme.lower() != 'basic': - raise ValueError("AbstractDigestAuthHandler does not support" - " the following scheme: '%s'" % scheme) - - def retry_http_digest_auth(self, req, auth): - token, challenge = auth.split(' ', 1) - chal = parse_keqv_list(filter(None, parse_http_list(challenge))) - auth = self.get_authorization(req, chal) - if auth: - auth_val = 'Digest %s' % auth - if req.headers.get(self.auth_header, None) == auth_val: - return None - req.add_unredirected_header(self.auth_header, auth_val) - resp = self.parent.open(req, timeout=req.timeout) - return resp - - def get_cnonce(self, nonce): - # The cnonce-value is an opaque - # quoted string value provided by the client and used by both client - # and server to avoid chosen plaintext attacks, to provide mutual - # authentication, and to provide some message integrity protection. - # This isn't a fabulous effort, but it's probably Good Enough. - s = "%s:%s:%s:" % (self.nonce_count, nonce, time.ctime()) - b = s.encode("ascii") + _randombytes(8) - dig = hashlib.sha1(b).hexdigest() - return dig[:16] - - def get_authorization(self, req, chal): - try: - realm = chal['realm'] - nonce = chal['nonce'] - qop = chal.get('qop') - algorithm = chal.get('algorithm', 'MD5') - # mod_digest doesn't send an opaque, even though it isn't - # supposed to be optional - opaque = chal.get('opaque', None) - except KeyError: - return None - - H, KD = self.get_algorithm_impls(algorithm) - if H is None: - return None - - user, pw = self.passwd.find_user_password(realm, req.full_url) - if user is None: - return None - - # XXX not implemented yet - if req.data is not None: - entdig = self.get_entity_digest(req.data, chal) - else: - entdig = None - - A1 = "%s:%s:%s" % (user, realm, pw) - A2 = "%s:%s" % (req.get_method(), - # XXX selector: what about proxies and full urls - req.selector) - if qop == 'auth': - if nonce == self.last_nonce: - self.nonce_count += 1 - else: - self.nonce_count = 1 - self.last_nonce = nonce - ncvalue = '%08x' % self.nonce_count - cnonce = self.get_cnonce(nonce) - noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, H(A2)) - respdig = KD(H(A1), noncebit) - elif qop is None: - respdig = KD(H(A1), "%s:%s" % (nonce, H(A2))) - else: - # XXX handle auth-int. - raise URLError("qop '%s' is not supported." % qop) - - # XXX should the partial digests be encoded too? - - base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \ - 'response="%s"' % (user, realm, nonce, req.selector, - respdig) - if opaque: - base += ', opaque="%s"' % opaque - if entdig: - base += ', digest="%s"' % entdig - base += ', algorithm="%s"' % algorithm - if qop: - base += ', qop=auth, nc=%s, cnonce="%s"' % (ncvalue, cnonce) - return base - - def get_algorithm_impls(self, algorithm): - # lambdas assume digest modules are imported at the top level - if algorithm == 'MD5': - H = lambda x: hashlib.md5(x.encode("ascii")).hexdigest() - elif algorithm == 'SHA': - H = lambda x: hashlib.sha1(x.encode("ascii")).hexdigest() - # XXX MD5-sess - KD = lambda s, d: H("%s:%s" % (s, d)) - return H, KD - - def get_entity_digest(self, data, chal): - # XXX not implemented yet - return None - - -class HTTPDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler): - """An authentication protocol defined by RFC 2069 - - Digest authentication improves on basic authentication because it - does not transmit passwords in the clear. - """ - - auth_header = 'Authorization' - handler_order = 490 # before Basic auth - - def http_error_401(self, req, fp, code, msg, headers): - host = urlparse(req.full_url)[1] - retry = self.http_error_auth_reqed('www-authenticate', - host, req, headers) - self.reset_retry_count() - return retry - - -class ProxyDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler): - - auth_header = 'Proxy-Authorization' - handler_order = 490 # before Basic auth - - def http_error_407(self, req, fp, code, msg, headers): - host = req.host - retry = self.http_error_auth_reqed('proxy-authenticate', - host, req, headers) - self.reset_retry_count() - return retry - -class AbstractHTTPHandler(BaseHandler): - - def __init__(self, debuglevel=0): - self._debuglevel = debuglevel - - def set_http_debuglevel(self, level): - self._debuglevel = level - - def do_request_(self, request): - host = request.host - if not host: - raise URLError('no host given') - - if request.data is not None: # POST - data = request.data - if isinstance(data, str): - msg = "POST data should be bytes or an iterable of bytes. " \ - "It cannot be of type str." - raise TypeError(msg) - if not request.has_header('Content-type'): - request.add_unredirected_header( - 'Content-type', - 'application/x-www-form-urlencoded') - if not request.has_header('Content-length'): - size = None - try: - ### For Python-Future: - if PY2 and isinstance(data, array.array): - # memoryviews of arrays aren't supported - # in Py2.7. (e.g. memoryview(array.array('I', - # [1, 2, 3, 4])) raises a TypeError.) - # So we calculate the size manually instead: - size = len(data) * data.itemsize - ### - else: - mv = memoryview(data) - size = len(mv) * mv.itemsize - except TypeError: - if isinstance(data, Iterable): - raise ValueError("Content-Length should be specified " - "for iterable data of type %r %r" % (type(data), - data)) - else: - request.add_unredirected_header( - 'Content-length', '%d' % size) - - sel_host = host - if request.has_proxy(): - scheme, sel = splittype(request.selector) - sel_host, sel_path = splithost(sel) - if not request.has_header('Host'): - request.add_unredirected_header('Host', sel_host) - for name, value in self.parent.addheaders: - name = name.capitalize() - if not request.has_header(name): - request.add_unredirected_header(name, value) - - return request - - def do_open(self, http_class, req, **http_conn_args): - """Return an HTTPResponse object for the request, using http_class. - - http_class must implement the HTTPConnection API from http.client. - """ - host = req.host - if not host: - raise URLError('no host given') - - # will parse host:port - h = http_class(host, timeout=req.timeout, **http_conn_args) - - headers = dict(req.unredirected_hdrs) - headers.update(dict((k, v) for k, v in req.headers.items() - if k not in headers)) - - # TODO(jhylton): Should this be redesigned to handle - # persistent connections? - - # We want to make an HTTP/1.1 request, but the addinfourl - # class isn't prepared to deal with a persistent connection. - # It will try to read all remaining data from the socket, - # which will block while the server waits for the next request. - # So make sure the connection gets closed after the (only) - # request. - headers["Connection"] = "close" - headers = dict((name.title(), val) for name, val in headers.items()) - - if req._tunnel_host: - tunnel_headers = {} - proxy_auth_hdr = "Proxy-Authorization" - if proxy_auth_hdr in headers: - tunnel_headers[proxy_auth_hdr] = headers[proxy_auth_hdr] - # Proxy-Authorization should not be sent to origin - # server. - del headers[proxy_auth_hdr] - h.set_tunnel(req._tunnel_host, headers=tunnel_headers) - - try: - h.request(req.get_method(), req.selector, req.data, headers) - except socket.error as err: # timeout error - h.close() - raise URLError(err) - else: - r = h.getresponse() - # If the server does not send us a 'Connection: close' header, - # HTTPConnection assumes the socket should be left open. Manually - # mark the socket to be closed when this response object goes away. - if h.sock: - h.sock.close() - h.sock = None - - - r.url = req.get_full_url() - # This line replaces the .msg attribute of the HTTPResponse - # with .headers, because urllib clients expect the response to - # have the reason in .msg. It would be good to mark this - # attribute is deprecated and get then to use info() or - # .headers. - r.msg = r.reason - return r - - -class HTTPHandler(AbstractHTTPHandler): - - def http_open(self, req): - return self.do_open(http_client.HTTPConnection, req) - - http_request = AbstractHTTPHandler.do_request_ - -if hasattr(http_client, 'HTTPSConnection'): - - class HTTPSHandler(AbstractHTTPHandler): - - def __init__(self, debuglevel=0, context=None, check_hostname=None): - AbstractHTTPHandler.__init__(self, debuglevel) - self._context = context - self._check_hostname = check_hostname - - def https_open(self, req): - return self.do_open(http_client.HTTPSConnection, req, - context=self._context, check_hostname=self._check_hostname) - - https_request = AbstractHTTPHandler.do_request_ - - __all__.append('HTTPSHandler') - -class HTTPCookieProcessor(BaseHandler): - def __init__(self, cookiejar=None): - import future.backports.http.cookiejar as http_cookiejar - if cookiejar is None: - cookiejar = http_cookiejar.CookieJar() - self.cookiejar = cookiejar - - def http_request(self, request): - self.cookiejar.add_cookie_header(request) - return request - - def http_response(self, request, response): - self.cookiejar.extract_cookies(response, request) - return response - - https_request = http_request - https_response = http_response - -class UnknownHandler(BaseHandler): - def unknown_open(self, req): - type = req.type - raise URLError('unknown url type: %s' % type) - -def parse_keqv_list(l): - """Parse list of key=value strings where keys are not duplicated.""" - parsed = {} - for elt in l: - k, v = elt.split('=', 1) - if v[0] == '"' and v[-1] == '"': - v = v[1:-1] - parsed[k] = v - return parsed - -def parse_http_list(s): - """Parse lists as described by RFC 2068 Section 2. - - In particular, parse comma-separated lists where the elements of - the list may include quoted-strings. A quoted-string could - contain a comma. A non-quoted string could have quotes in the - middle. Neither commas nor quotes count if they are escaped. - Only double-quotes count, not single-quotes. - """ - res = [] - part = '' - - escape = quote = False - for cur in s: - if escape: - part += cur - escape = False - continue - if quote: - if cur == '\\': - escape = True - continue - elif cur == '"': - quote = False - part += cur - continue - - if cur == ',': - res.append(part) - part = '' - continue - - if cur == '"': - quote = True - - part += cur - - # append last part - if part: - res.append(part) - - return [part.strip() for part in res] - -class FileHandler(BaseHandler): - # Use local file or FTP depending on form of URL - def file_open(self, req): - url = req.selector - if url[:2] == '//' and url[2:3] != '/' and (req.host and - req.host != 'localhost'): - if not req.host is self.get_names(): - raise URLError("file:// scheme is supported only on localhost") - else: - return self.open_local_file(req) - - # names for the localhost - names = None - def get_names(self): - if FileHandler.names is None: - try: - FileHandler.names = tuple( - socket.gethostbyname_ex('localhost')[2] + - socket.gethostbyname_ex(socket.gethostname())[2]) - except socket.gaierror: - FileHandler.names = (socket.gethostbyname('localhost'),) - return FileHandler.names - - # not entirely sure what the rules are here - def open_local_file(self, req): - import future.backports.email.utils as email_utils - import mimetypes - host = req.host - filename = req.selector - localfile = url2pathname(filename) - try: - stats = os.stat(localfile) - size = stats.st_size - modified = email_utils.formatdate(stats.st_mtime, usegmt=True) - mtype = mimetypes.guess_type(filename)[0] - headers = email.message_from_string( - 'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' % - (mtype or 'text/plain', size, modified)) - if host: - host, port = splitport(host) - if not host or \ - (not port and _safe_gethostbyname(host) in self.get_names()): - if host: - origurl = 'file://' + host + filename - else: - origurl = 'file://' + filename - return addinfourl(open(localfile, 'rb'), headers, origurl) - except OSError as exp: - # users shouldn't expect OSErrors coming from urlopen() - raise URLError(exp) - raise URLError('file not on local host') - -def _safe_gethostbyname(host): - try: - return socket.gethostbyname(host) - except socket.gaierror: - return None - -class FTPHandler(BaseHandler): - def ftp_open(self, req): - import ftplib - import mimetypes - host = req.host - if not host: - raise URLError('ftp error: no host given') - host, port = splitport(host) - if port is None: - port = ftplib.FTP_PORT - else: - port = int(port) - - # username/password handling - user, host = splituser(host) - if user: - user, passwd = splitpasswd(user) - else: - passwd = None - host = unquote(host) - user = user or '' - passwd = passwd or '' - - try: - host = socket.gethostbyname(host) - except socket.error as msg: - raise URLError(msg) - path, attrs = splitattr(req.selector) - dirs = path.split('/') - dirs = list(map(unquote, dirs)) - dirs, file = dirs[:-1], dirs[-1] - if dirs and not dirs[0]: - dirs = dirs[1:] - try: - fw = self.connect_ftp(user, passwd, host, port, dirs, req.timeout) - type = file and 'I' or 'D' - for attr in attrs: - attr, value = splitvalue(attr) - if attr.lower() == 'type' and \ - value in ('a', 'A', 'i', 'I', 'd', 'D'): - type = value.upper() - fp, retrlen = fw.retrfile(file, type) - headers = "" - mtype = mimetypes.guess_type(req.full_url)[0] - if mtype: - headers += "Content-type: %s\n" % mtype - if retrlen is not None and retrlen >= 0: - headers += "Content-length: %d\n" % retrlen - headers = email.message_from_string(headers) - return addinfourl(fp, headers, req.full_url) - except ftplib.all_errors as exp: - exc = URLError('ftp error: %r' % exp) - raise_with_traceback(exc) - - def connect_ftp(self, user, passwd, host, port, dirs, timeout): - return ftpwrapper(user, passwd, host, port, dirs, timeout, - persistent=False) - -class CacheFTPHandler(FTPHandler): - # XXX would be nice to have pluggable cache strategies - # XXX this stuff is definitely not thread safe - def __init__(self): - self.cache = {} - self.timeout = {} - self.soonest = 0 - self.delay = 60 - self.max_conns = 16 - - def setTimeout(self, t): - self.delay = t - - def setMaxConns(self, m): - self.max_conns = m - - def connect_ftp(self, user, passwd, host, port, dirs, timeout): - key = user, host, port, '/'.join(dirs), timeout - if key in self.cache: - self.timeout[key] = time.time() + self.delay - else: - self.cache[key] = ftpwrapper(user, passwd, host, port, - dirs, timeout) - self.timeout[key] = time.time() + self.delay - self.check_cache() - return self.cache[key] - - def check_cache(self): - # first check for old ones - t = time.time() - if self.soonest <= t: - for k, v in list(self.timeout.items()): - if v < t: - self.cache[k].close() - del self.cache[k] - del self.timeout[k] - self.soonest = min(list(self.timeout.values())) - - # then check the size - if len(self.cache) == self.max_conns: - for k, v in list(self.timeout.items()): - if v == self.soonest: - del self.cache[k] - del self.timeout[k] - break - self.soonest = min(list(self.timeout.values())) - - def clear_cache(self): - for conn in self.cache.values(): - conn.close() - self.cache.clear() - self.timeout.clear() - - -# Code move from the old urllib module - -MAXFTPCACHE = 10 # Trim the ftp cache beyond this size - -# Helper for non-unix systems -if os.name == 'nt': - from nturl2path import url2pathname, pathname2url -else: - def url2pathname(pathname): - """OS-specific conversion from a relative URL of the 'file' scheme - to a file system path; not recommended for general use.""" - return unquote(pathname) - - def pathname2url(pathname): - """OS-specific conversion from a file system path to a relative URL - of the 'file' scheme; not recommended for general use.""" - return quote(pathname) - -# This really consists of two pieces: -# (1) a class which handles opening of all sorts of URLs -# (plus assorted utilities etc.) -# (2) a set of functions for parsing URLs -# XXX Should these be separated out into different modules? - - -ftpcache = {} -class URLopener(object): - """Class to open URLs. - This is a class rather than just a subroutine because we may need - more than one set of global protocol-specific options. - Note -- this is a base class for those who don't want the - automatic handling of errors type 302 (relocated) and 401 - (authorization needed).""" - - __tempfiles = None - - version = "Python-urllib/%s" % __version__ - - # Constructor - def __init__(self, proxies=None, **x509): - msg = "%(class)s style of invoking requests is deprecated. " \ - "Use newer urlopen functions/methods" % {'class': self.__class__.__name__} - warnings.warn(msg, DeprecationWarning, stacklevel=3) - if proxies is None: - proxies = getproxies() - assert hasattr(proxies, 'keys'), "proxies must be a mapping" - self.proxies = proxies - self.key_file = x509.get('key_file') - self.cert_file = x509.get('cert_file') - self.addheaders = [('User-Agent', self.version)] - self.__tempfiles = [] - self.__unlink = os.unlink # See cleanup() - self.tempcache = None - # Undocumented feature: if you assign {} to tempcache, - # it is used to cache files retrieved with - # self.retrieve(). This is not enabled by default - # since it does not work for changing documents (and I - # haven't got the logic to check expiration headers - # yet). - self.ftpcache = ftpcache - # Undocumented feature: you can use a different - # ftp cache by assigning to the .ftpcache member; - # in case you want logically independent URL openers - # XXX This is not threadsafe. Bah. - - def __del__(self): - self.close() - - def close(self): - self.cleanup() - - def cleanup(self): - # This code sometimes runs when the rest of this module - # has already been deleted, so it can't use any globals - # or import anything. - if self.__tempfiles: - for file in self.__tempfiles: - try: - self.__unlink(file) - except OSError: - pass - del self.__tempfiles[:] - if self.tempcache: - self.tempcache.clear() - - def addheader(self, *args): - """Add a header to be used by the HTTP interface only - e.g. u.addheader('Accept', 'sound/basic')""" - self.addheaders.append(args) - - # External interface - def open(self, fullurl, data=None): - """Use URLopener().open(file) instead of open(file, 'r').""" - fullurl = unwrap(to_bytes(fullurl)) - fullurl = quote(fullurl, safe="%/:=&?~#+!$,;'@()*[]|") - if self.tempcache and fullurl in self.tempcache: - filename, headers = self.tempcache[fullurl] - fp = open(filename, 'rb') - return addinfourl(fp, headers, fullurl) - urltype, url = splittype(fullurl) - if not urltype: - urltype = 'file' - if urltype in self.proxies: - proxy = self.proxies[urltype] - urltype, proxyhost = splittype(proxy) - host, selector = splithost(proxyhost) - url = (host, fullurl) # Signal special case to open_*() - else: - proxy = None - name = 'open_' + urltype - self.type = urltype - name = name.replace('-', '_') - if not hasattr(self, name): - if proxy: - return self.open_unknown_proxy(proxy, fullurl, data) - else: - return self.open_unknown(fullurl, data) - try: - if data is None: - return getattr(self, name)(url) - else: - return getattr(self, name)(url, data) - except HTTPError: - raise - except socket.error as msg: - raise_with_traceback(IOError('socket error', msg)) - - def open_unknown(self, fullurl, data=None): - """Overridable interface to open unknown URL type.""" - type, url = splittype(fullurl) - raise IOError('url error', 'unknown url type', type) - - def open_unknown_proxy(self, proxy, fullurl, data=None): - """Overridable interface to open unknown URL type.""" - type, url = splittype(fullurl) - raise IOError('url error', 'invalid proxy for %s' % type, proxy) - - # External interface - def retrieve(self, url, filename=None, reporthook=None, data=None): - """retrieve(url) returns (filename, headers) for a local object - or (tempfilename, headers) for a remote object.""" - url = unwrap(to_bytes(url)) - if self.tempcache and url in self.tempcache: - return self.tempcache[url] - type, url1 = splittype(url) - if filename is None and (not type or type == 'file'): - try: - fp = self.open_local_file(url1) - hdrs = fp.info() - fp.close() - return url2pathname(splithost(url1)[1]), hdrs - except IOError as msg: - pass - fp = self.open(url, data) - try: - headers = fp.info() - if filename: - tfp = open(filename, 'wb') - else: - import tempfile - garbage, path = splittype(url) - garbage, path = splithost(path or "") - path, garbage = splitquery(path or "") - path, garbage = splitattr(path or "") - suffix = os.path.splitext(path)[1] - (fd, filename) = tempfile.mkstemp(suffix) - self.__tempfiles.append(filename) - tfp = os.fdopen(fd, 'wb') - try: - result = filename, headers - if self.tempcache is not None: - self.tempcache[url] = result - bs = 1024*8 - size = -1 - read = 0 - blocknum = 0 - if "content-length" in headers: - size = int(headers["Content-Length"]) - if reporthook: - reporthook(blocknum, bs, size) - while 1: - block = fp.read(bs) - if not block: - break - read += len(block) - tfp.write(block) - blocknum += 1 - if reporthook: - reporthook(blocknum, bs, size) - finally: - tfp.close() - finally: - fp.close() - - # raise exception if actual size does not match content-length header - if size >= 0 and read < size: - raise ContentTooShortError( - "retrieval incomplete: got only %i out of %i bytes" - % (read, size), result) - - return result - - # Each method named open_ knows how to open that type of URL - - def _open_generic_http(self, connection_factory, url, data): - """Make an HTTP connection using connection_class. - - This is an internal method that should be called from - open_http() or open_https(). - - Arguments: - - connection_factory should take a host name and return an - HTTPConnection instance. - - url is the url to retrieval or a host, relative-path pair. - - data is payload for a POST request or None. - """ - - user_passwd = None - proxy_passwd= None - if isinstance(url, str): - host, selector = splithost(url) - if host: - user_passwd, host = splituser(host) - host = unquote(host) - realhost = host - else: - host, selector = url - # check whether the proxy contains authorization information - proxy_passwd, host = splituser(host) - # now we proceed with the url we want to obtain - urltype, rest = splittype(selector) - url = rest - user_passwd = None - if urltype.lower() != 'http': - realhost = None - else: - realhost, rest = splithost(rest) - if realhost: - user_passwd, realhost = splituser(realhost) - if user_passwd: - selector = "%s://%s%s" % (urltype, realhost, rest) - if proxy_bypass(realhost): - host = realhost - - if not host: raise IOError('http error', 'no host given') - - if proxy_passwd: - proxy_passwd = unquote(proxy_passwd) - proxy_auth = base64.b64encode(proxy_passwd.encode()).decode('ascii') - else: - proxy_auth = None - - if user_passwd: - user_passwd = unquote(user_passwd) - auth = base64.b64encode(user_passwd.encode()).decode('ascii') - else: - auth = None - http_conn = connection_factory(host) - headers = {} - if proxy_auth: - headers["Proxy-Authorization"] = "Basic %s" % proxy_auth - if auth: - headers["Authorization"] = "Basic %s" % auth - if realhost: - headers["Host"] = realhost - - # Add Connection:close as we don't support persistent connections yet. - # This helps in closing the socket and avoiding ResourceWarning - - headers["Connection"] = "close" - - for header, value in self.addheaders: - headers[header] = value - - if data is not None: - headers["Content-Type"] = "application/x-www-form-urlencoded" - http_conn.request("POST", selector, data, headers) - else: - http_conn.request("GET", selector, headers=headers) - - try: - response = http_conn.getresponse() - except http_client.BadStatusLine: - # something went wrong with the HTTP status line - raise URLError("http protocol error: bad status line") - - # According to RFC 2616, "2xx" code indicates that the client's - # request was successfully received, understood, and accepted. - if 200 <= response.status < 300: - return addinfourl(response, response.msg, "http:" + url, - response.status) - else: - return self.http_error( - url, response.fp, - response.status, response.reason, response.msg, data) - - def open_http(self, url, data=None): - """Use HTTP protocol.""" - return self._open_generic_http(http_client.HTTPConnection, url, data) - - def http_error(self, url, fp, errcode, errmsg, headers, data=None): - """Handle http errors. - - Derived class can override this, or provide specific handlers - named http_error_DDD where DDD is the 3-digit error code.""" - # First check if there's a specific handler for this error - name = 'http_error_%d' % errcode - if hasattr(self, name): - method = getattr(self, name) - if data is None: - result = method(url, fp, errcode, errmsg, headers) - else: - result = method(url, fp, errcode, errmsg, headers, data) - if result: return result - return self.http_error_default(url, fp, errcode, errmsg, headers) - - def http_error_default(self, url, fp, errcode, errmsg, headers): - """Default error handler: close the connection and raise IOError.""" - fp.close() - raise HTTPError(url, errcode, errmsg, headers, None) - - if _have_ssl: - def _https_connection(self, host): - return http_client.HTTPSConnection(host, - key_file=self.key_file, - cert_file=self.cert_file) - - def open_https(self, url, data=None): - """Use HTTPS protocol.""" - return self._open_generic_http(self._https_connection, url, data) - - def open_file(self, url): - """Use local file or FTP depending on form of URL.""" - if not isinstance(url, str): - raise URLError('file error: proxy support for file protocol currently not implemented') - if url[:2] == '//' and url[2:3] != '/' and url[2:12].lower() != 'localhost/': - raise ValueError("file:// scheme is supported only on localhost") - else: - return self.open_local_file(url) - - def open_local_file(self, url): - """Use local file.""" - import future.backports.email.utils as email_utils - import mimetypes - host, file = splithost(url) - localname = url2pathname(file) - try: - stats = os.stat(localname) - except OSError as e: - raise URLError(e.strerror, e.filename) - size = stats.st_size - modified = email_utils.formatdate(stats.st_mtime, usegmt=True) - mtype = mimetypes.guess_type(url)[0] - headers = email.message_from_string( - 'Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' % - (mtype or 'text/plain', size, modified)) - if not host: - urlfile = file - if file[:1] == '/': - urlfile = 'file://' + file - return addinfourl(open(localname, 'rb'), headers, urlfile) - host, port = splitport(host) - if (not port - and socket.gethostbyname(host) in ((localhost(),) + thishost())): - urlfile = file - if file[:1] == '/': - urlfile = 'file://' + file - elif file[:2] == './': - raise ValueError("local file url may start with / or file:. Unknown url of type: %s" % url) - return addinfourl(open(localname, 'rb'), headers, urlfile) - raise URLError('local file error: not on local host') - - def open_ftp(self, url): - """Use FTP protocol.""" - if not isinstance(url, str): - raise URLError('ftp error: proxy support for ftp protocol currently not implemented') - import mimetypes - host, path = splithost(url) - if not host: raise URLError('ftp error: no host given') - host, port = splitport(host) - user, host = splituser(host) - if user: user, passwd = splitpasswd(user) - else: passwd = None - host = unquote(host) - user = unquote(user or '') - passwd = unquote(passwd or '') - host = socket.gethostbyname(host) - if not port: - import ftplib - port = ftplib.FTP_PORT - else: - port = int(port) - path, attrs = splitattr(path) - path = unquote(path) - dirs = path.split('/') - dirs, file = dirs[:-1], dirs[-1] - if dirs and not dirs[0]: dirs = dirs[1:] - if dirs and not dirs[0]: dirs[0] = '/' - key = user, host, port, '/'.join(dirs) - # XXX thread unsafe! - if len(self.ftpcache) > MAXFTPCACHE: - # Prune the cache, rather arbitrarily - for k in self.ftpcache.keys(): - if k != key: - v = self.ftpcache[k] - del self.ftpcache[k] - v.close() - try: - if key not in self.ftpcache: - self.ftpcache[key] = \ - ftpwrapper(user, passwd, host, port, dirs) - if not file: type = 'D' - else: type = 'I' - for attr in attrs: - attr, value = splitvalue(attr) - if attr.lower() == 'type' and \ - value in ('a', 'A', 'i', 'I', 'd', 'D'): - type = value.upper() - (fp, retrlen) = self.ftpcache[key].retrfile(file, type) - mtype = mimetypes.guess_type("ftp:" + url)[0] - headers = "" - if mtype: - headers += "Content-Type: %s\n" % mtype - if retrlen is not None and retrlen >= 0: - headers += "Content-Length: %d\n" % retrlen - headers = email.message_from_string(headers) - return addinfourl(fp, headers, "ftp:" + url) - except ftperrors() as exp: - raise_with_traceback(URLError('ftp error %r' % exp)) - - def open_data(self, url, data=None): - """Use "data" URL.""" - if not isinstance(url, str): - raise URLError('data error: proxy support for data protocol currently not implemented') - # ignore POSTed data - # - # syntax of data URLs: - # dataurl := "data:" [ mediatype ] [ ";base64" ] "," data - # mediatype := [ type "/" subtype ] *( ";" parameter ) - # data := *urlchar - # parameter := attribute "=" value - try: - [type, data] = url.split(',', 1) - except ValueError: - raise IOError('data error', 'bad data URL') - if not type: - type = 'text/plain;charset=US-ASCII' - semi = type.rfind(';') - if semi >= 0 and '=' not in type[semi:]: - encoding = type[semi+1:] - type = type[:semi] - else: - encoding = '' - msg = [] - msg.append('Date: %s'%time.strftime('%a, %d %b %Y %H:%M:%S GMT', - time.gmtime(time.time()))) - msg.append('Content-type: %s' % type) - if encoding == 'base64': - # XXX is this encoding/decoding ok? - data = base64.decodebytes(data.encode('ascii')).decode('latin-1') - else: - data = unquote(data) - msg.append('Content-Length: %d' % len(data)) - msg.append('') - msg.append(data) - msg = '\n'.join(msg) - headers = email.message_from_string(msg) - f = io.StringIO(msg) - #f.fileno = None # needed for addinfourl - return addinfourl(f, headers, url) - - -class FancyURLopener(URLopener): - """Derived class with handlers for errors we can handle (perhaps).""" - - def __init__(self, *args, **kwargs): - URLopener.__init__(self, *args, **kwargs) - self.auth_cache = {} - self.tries = 0 - self.maxtries = 10 - - def http_error_default(self, url, fp, errcode, errmsg, headers): - """Default error handling -- don't raise an exception.""" - return addinfourl(fp, headers, "http:" + url, errcode) - - def http_error_302(self, url, fp, errcode, errmsg, headers, data=None): - """Error 302 -- relocated (temporarily).""" - self.tries += 1 - if self.maxtries and self.tries >= self.maxtries: - if hasattr(self, "http_error_500"): - meth = self.http_error_500 - else: - meth = self.http_error_default - self.tries = 0 - return meth(url, fp, 500, - "Internal Server Error: Redirect Recursion", headers) - result = self.redirect_internal(url, fp, errcode, errmsg, headers, - data) - self.tries = 0 - return result - - def redirect_internal(self, url, fp, errcode, errmsg, headers, data): - if 'location' in headers: - newurl = headers['location'] - elif 'uri' in headers: - newurl = headers['uri'] - else: - return - fp.close() - - # In case the server sent a relative URL, join with original: - newurl = urljoin(self.type + ":" + url, newurl) - - urlparts = urlparse(newurl) - - # For security reasons, we don't allow redirection to anything other - # than http, https and ftp. - - # We are using newer HTTPError with older redirect_internal method - # This older method will get deprecated in 3.3 - - if urlparts.scheme not in ('http', 'https', 'ftp', ''): - raise HTTPError(newurl, errcode, - errmsg + - " Redirection to url '%s' is not allowed." % newurl, - headers, fp) - - return self.open(newurl) - - def http_error_301(self, url, fp, errcode, errmsg, headers, data=None): - """Error 301 -- also relocated (permanently).""" - return self.http_error_302(url, fp, errcode, errmsg, headers, data) - - def http_error_303(self, url, fp, errcode, errmsg, headers, data=None): - """Error 303 -- also relocated (essentially identical to 302).""" - return self.http_error_302(url, fp, errcode, errmsg, headers, data) - - def http_error_307(self, url, fp, errcode, errmsg, headers, data=None): - """Error 307 -- relocated, but turn POST into error.""" - if data is None: - return self.http_error_302(url, fp, errcode, errmsg, headers, data) - else: - return self.http_error_default(url, fp, errcode, errmsg, headers) - - def http_error_401(self, url, fp, errcode, errmsg, headers, data=None, - retry=False): - """Error 401 -- authentication required. - This function supports Basic authentication only.""" - if 'www-authenticate' not in headers: - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - stuff = headers['www-authenticate'] - match = re.match('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', stuff) - if not match: - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - scheme, realm = match.groups() - if scheme.lower() != 'basic': - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - if not retry: - URLopener.http_error_default(self, url, fp, errcode, errmsg, - headers) - name = 'retry_' + self.type + '_basic_auth' - if data is None: - return getattr(self,name)(url, realm) - else: - return getattr(self,name)(url, realm, data) - - def http_error_407(self, url, fp, errcode, errmsg, headers, data=None, - retry=False): - """Error 407 -- proxy authentication required. - This function supports Basic authentication only.""" - if 'proxy-authenticate' not in headers: - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - stuff = headers['proxy-authenticate'] - match = re.match('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', stuff) - if not match: - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - scheme, realm = match.groups() - if scheme.lower() != 'basic': - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - if not retry: - URLopener.http_error_default(self, url, fp, errcode, errmsg, - headers) - name = 'retry_proxy_' + self.type + '_basic_auth' - if data is None: - return getattr(self,name)(url, realm) - else: - return getattr(self,name)(url, realm, data) - - def retry_proxy_http_basic_auth(self, url, realm, data=None): - host, selector = splithost(url) - newurl = 'http://' + host + selector - proxy = self.proxies['http'] - urltype, proxyhost = splittype(proxy) - proxyhost, proxyselector = splithost(proxyhost) - i = proxyhost.find('@') + 1 - proxyhost = proxyhost[i:] - user, passwd = self.get_user_passwd(proxyhost, realm, i) - if not (user or passwd): return None - proxyhost = "%s:%s@%s" % (quote(user, safe=''), - quote(passwd, safe=''), proxyhost) - self.proxies['http'] = 'http://' + proxyhost + proxyselector - if data is None: - return self.open(newurl) - else: - return self.open(newurl, data) - - def retry_proxy_https_basic_auth(self, url, realm, data=None): - host, selector = splithost(url) - newurl = 'https://' + host + selector - proxy = self.proxies['https'] - urltype, proxyhost = splittype(proxy) - proxyhost, proxyselector = splithost(proxyhost) - i = proxyhost.find('@') + 1 - proxyhost = proxyhost[i:] - user, passwd = self.get_user_passwd(proxyhost, realm, i) - if not (user or passwd): return None - proxyhost = "%s:%s@%s" % (quote(user, safe=''), - quote(passwd, safe=''), proxyhost) - self.proxies['https'] = 'https://' + proxyhost + proxyselector - if data is None: - return self.open(newurl) - else: - return self.open(newurl, data) - - def retry_http_basic_auth(self, url, realm, data=None): - host, selector = splithost(url) - i = host.find('@') + 1 - host = host[i:] - user, passwd = self.get_user_passwd(host, realm, i) - if not (user or passwd): return None - host = "%s:%s@%s" % (quote(user, safe=''), - quote(passwd, safe=''), host) - newurl = 'http://' + host + selector - if data is None: - return self.open(newurl) - else: - return self.open(newurl, data) - - def retry_https_basic_auth(self, url, realm, data=None): - host, selector = splithost(url) - i = host.find('@') + 1 - host = host[i:] - user, passwd = self.get_user_passwd(host, realm, i) - if not (user or passwd): return None - host = "%s:%s@%s" % (quote(user, safe=''), - quote(passwd, safe=''), host) - newurl = 'https://' + host + selector - if data is None: - return self.open(newurl) - else: - return self.open(newurl, data) - - def get_user_passwd(self, host, realm, clear_cache=0): - key = realm + '@' + host.lower() - if key in self.auth_cache: - if clear_cache: - del self.auth_cache[key] - else: - return self.auth_cache[key] - user, passwd = self.prompt_user_passwd(host, realm) - if user or passwd: self.auth_cache[key] = (user, passwd) - return user, passwd - - def prompt_user_passwd(self, host, realm): - """Override this in a GUI environment!""" - import getpass - try: - user = input("Enter username for %s at %s: " % (realm, host)) - passwd = getpass.getpass("Enter password for %s in %s at %s: " % - (user, realm, host)) - return user, passwd - except KeyboardInterrupt: - print() - return None, None - - -# Utility functions - -_localhost = None -def localhost(): - """Return the IP address of the magic hostname 'localhost'.""" - global _localhost - if _localhost is None: - _localhost = socket.gethostbyname('localhost') - return _localhost - -_thishost = None -def thishost(): - """Return the IP addresses of the current host.""" - global _thishost - if _thishost is None: - try: - _thishost = tuple(socket.gethostbyname_ex(socket.gethostname())[2]) - except socket.gaierror: - _thishost = tuple(socket.gethostbyname_ex('localhost')[2]) - return _thishost - -_ftperrors = None -def ftperrors(): - """Return the set of errors raised by the FTP class.""" - global _ftperrors - if _ftperrors is None: - import ftplib - _ftperrors = ftplib.all_errors - return _ftperrors - -_noheaders = None -def noheaders(): - """Return an empty email Message object.""" - global _noheaders - if _noheaders is None: - _noheaders = email.message_from_string("") - return _noheaders - - -# Utility classes - -class ftpwrapper(object): - """Class used by open_ftp() for cache of open FTP connections.""" - - def __init__(self, user, passwd, host, port, dirs, timeout=None, - persistent=True): - self.user = user - self.passwd = passwd - self.host = host - self.port = port - self.dirs = dirs - self.timeout = timeout - self.refcount = 0 - self.keepalive = persistent - self.init() - - def init(self): - import ftplib - self.busy = 0 - self.ftp = ftplib.FTP() - self.ftp.connect(self.host, self.port, self.timeout) - self.ftp.login(self.user, self.passwd) - _target = '/'.join(self.dirs) - self.ftp.cwd(_target) - - def retrfile(self, file, type): - import ftplib - self.endtransfer() - if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1 - else: cmd = 'TYPE ' + type; isdir = 0 - try: - self.ftp.voidcmd(cmd) - except ftplib.all_errors: - self.init() - self.ftp.voidcmd(cmd) - conn = None - if file and not isdir: - # Try to retrieve as a file - try: - cmd = 'RETR ' + file - conn, retrlen = self.ftp.ntransfercmd(cmd) - except ftplib.error_perm as reason: - if str(reason)[:3] != '550': - raise_with_traceback(URLError('ftp error: %r' % reason)) - if not conn: - # Set transfer mode to ASCII! - self.ftp.voidcmd('TYPE A') - # Try a directory listing. Verify that directory exists. - if file: - pwd = self.ftp.pwd() - try: - try: - self.ftp.cwd(file) - except ftplib.error_perm as reason: - ### Was: - # raise URLError('ftp error: %r' % reason) from reason - exc = URLError('ftp error: %r' % reason) - exc.__cause__ = reason - raise exc - finally: - self.ftp.cwd(pwd) - cmd = 'LIST ' + file - else: - cmd = 'LIST' - conn, retrlen = self.ftp.ntransfercmd(cmd) - self.busy = 1 - - ftpobj = addclosehook(conn.makefile('rb'), self.file_close) - self.refcount += 1 - conn.close() - # Pass back both a suitably decorated object and a retrieval length - return (ftpobj, retrlen) - - def endtransfer(self): - self.busy = 0 - - def close(self): - self.keepalive = False - if self.refcount <= 0: - self.real_close() - - def file_close(self): - self.endtransfer() - self.refcount -= 1 - if self.refcount <= 0 and not self.keepalive: - self.real_close() - - def real_close(self): - self.endtransfer() - try: - self.ftp.close() - except ftperrors(): - pass - -# Proxy handling -def getproxies_environment(): - """Return a dictionary of scheme -> proxy server URL mappings. - - Scan the environment for variables named _proxy; - this seems to be the standard convention. If you need a - different way, you can pass a proxies dictionary to the - [Fancy]URLopener constructor. - - """ - proxies = {} - for name, value in os.environ.items(): - name = name.lower() - if value and name[-6:] == '_proxy': - proxies[name[:-6]] = value - return proxies - -def proxy_bypass_environment(host): - """Test if proxies should not be used for a particular host. - - Checks the environment for a variable named no_proxy, which should - be a list of DNS suffixes separated by commas, or '*' for all hosts. - """ - no_proxy = os.environ.get('no_proxy', '') or os.environ.get('NO_PROXY', '') - # '*' is special case for always bypass - if no_proxy == '*': - return 1 - # strip port off host - hostonly, port = splitport(host) - # check if the host ends with any of the DNS suffixes - no_proxy_list = [proxy.strip() for proxy in no_proxy.split(',')] - for name in no_proxy_list: - if name and (hostonly.endswith(name) or host.endswith(name)): - return 1 - # otherwise, don't bypass - return 0 - - -# This code tests an OSX specific data structure but is testable on all -# platforms -def _proxy_bypass_macosx_sysconf(host, proxy_settings): - """ - Return True iff this host shouldn't be accessed using a proxy - - This function uses the MacOSX framework SystemConfiguration - to fetch the proxy information. - - proxy_settings come from _scproxy._get_proxy_settings or get mocked ie: - { 'exclude_simple': bool, - 'exceptions': ['foo.bar', '*.bar.com', '127.0.0.1', '10.1', '10.0/16'] - } - """ - from fnmatch import fnmatch - - hostonly, port = splitport(host) - - def ip2num(ipAddr): - parts = ipAddr.split('.') - parts = list(map(int, parts)) - if len(parts) != 4: - parts = (parts + [0, 0, 0, 0])[:4] - return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3] - - # Check for simple host names: - if '.' not in host: - if proxy_settings['exclude_simple']: - return True - - hostIP = None - - for value in proxy_settings.get('exceptions', ()): - # Items in the list are strings like these: *.local, 169.254/16 - if not value: continue - - m = re.match(r"(\d+(?:\.\d+)*)(/\d+)?", value) - if m is not None: - if hostIP is None: - try: - hostIP = socket.gethostbyname(hostonly) - hostIP = ip2num(hostIP) - except socket.error: - continue - - base = ip2num(m.group(1)) - mask = m.group(2) - if mask is None: - mask = 8 * (m.group(1).count('.') + 1) - else: - mask = int(mask[1:]) - mask = 32 - mask - - if (hostIP >> mask) == (base >> mask): - return True - - elif fnmatch(host, value): - return True - - return False - - -if sys.platform == 'darwin': - from _scproxy import _get_proxy_settings, _get_proxies - - def proxy_bypass_macosx_sysconf(host): - proxy_settings = _get_proxy_settings() - return _proxy_bypass_macosx_sysconf(host, proxy_settings) - - def getproxies_macosx_sysconf(): - """Return a dictionary of scheme -> proxy server URL mappings. - - This function uses the MacOSX framework SystemConfiguration - to fetch the proxy information. - """ - return _get_proxies() - - - - def proxy_bypass(host): - if getproxies_environment(): - return proxy_bypass_environment(host) - else: - return proxy_bypass_macosx_sysconf(host) - - def getproxies(): - return getproxies_environment() or getproxies_macosx_sysconf() - - -elif os.name == 'nt': - def getproxies_registry(): - """Return a dictionary of scheme -> proxy server URL mappings. - - Win32 uses the registry to store proxies. - - """ - proxies = {} - try: - import winreg - except ImportError: - # Std module, so should be around - but you never know! - return proxies - try: - internetSettings = winreg.OpenKey(winreg.HKEY_CURRENT_USER, - r'Software\Microsoft\Windows\CurrentVersion\Internet Settings') - proxyEnable = winreg.QueryValueEx(internetSettings, - 'ProxyEnable')[0] - if proxyEnable: - # Returned as Unicode but problems if not converted to ASCII - proxyServer = str(winreg.QueryValueEx(internetSettings, - 'ProxyServer')[0]) - if '=' in proxyServer: - # Per-protocol settings - for p in proxyServer.split(';'): - protocol, address = p.split('=', 1) - # See if address has a type:// prefix - if not re.match('^([^/:]+)://', address): - address = '%s://%s' % (protocol, address) - proxies[protocol] = address - else: - # Use one setting for all protocols - if proxyServer[:5] == 'http:': - proxies['http'] = proxyServer - else: - proxies['http'] = 'http://%s' % proxyServer - proxies['https'] = 'https://%s' % proxyServer - proxies['ftp'] = 'ftp://%s' % proxyServer - internetSettings.Close() - except (WindowsError, ValueError, TypeError): - # Either registry key not found etc, or the value in an - # unexpected format. - # proxies already set up to be empty so nothing to do - pass - return proxies - - def getproxies(): - """Return a dictionary of scheme -> proxy server URL mappings. - - Returns settings gathered from the environment, if specified, - or the registry. - - """ - return getproxies_environment() or getproxies_registry() - - def proxy_bypass_registry(host): - try: - import winreg - except ImportError: - # Std modules, so should be around - but you never know! - return 0 - try: - internetSettings = winreg.OpenKey(winreg.HKEY_CURRENT_USER, - r'Software\Microsoft\Windows\CurrentVersion\Internet Settings') - proxyEnable = winreg.QueryValueEx(internetSettings, - 'ProxyEnable')[0] - proxyOverride = str(winreg.QueryValueEx(internetSettings, - 'ProxyOverride')[0]) - # ^^^^ Returned as Unicode but problems if not converted to ASCII - except WindowsError: - return 0 - if not proxyEnable or not proxyOverride: - return 0 - # try to make a host list from name and IP address. - rawHost, port = splitport(host) - host = [rawHost] - try: - addr = socket.gethostbyname(rawHost) - if addr != rawHost: - host.append(addr) - except socket.error: - pass - try: - fqdn = socket.getfqdn(rawHost) - if fqdn != rawHost: - host.append(fqdn) - except socket.error: - pass - # make a check value list from the registry entry: replace the - # '' string by the localhost entry and the corresponding - # canonical entry. - proxyOverride = proxyOverride.split(';') - # now check if we match one of the registry values. - for test in proxyOverride: - if test == '': - if '.' not in rawHost: - return 1 - test = test.replace(".", r"\.") # mask dots - test = test.replace("*", r".*") # change glob sequence - test = test.replace("?", r".") # change glob char - for val in host: - if re.match(test, val, re.I): - return 1 - return 0 - - def proxy_bypass(host): - """Return a dictionary of scheme -> proxy server URL mappings. - - Returns settings gathered from the environment, if specified, - or the registry. - - """ - if getproxies_environment(): - return proxy_bypass_environment(host) - else: - return proxy_bypass_registry(host) - -else: - # By default use environment variables - getproxies = getproxies_environment - proxy_bypass = proxy_bypass_environment diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/response.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/response.py deleted file mode 100644 index adbf6e5a..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/response.py +++ /dev/null @@ -1,103 +0,0 @@ -"""Response classes used by urllib. - -The base class, addbase, defines a minimal file-like interface, -including read() and readline(). The typical response object is an -addinfourl instance, which defines an info() method that returns -headers and a geturl() method that returns the url. -""" -from __future__ import absolute_import, division, unicode_literals -from future.builtins import object - -class addbase(object): - """Base class for addinfo and addclosehook.""" - - # XXX Add a method to expose the timeout on the underlying socket? - - def __init__(self, fp): - # TODO(jhylton): Is there a better way to delegate using io? - self.fp = fp - self.read = self.fp.read - self.readline = self.fp.readline - # TODO(jhylton): Make sure an object with readlines() is also iterable - if hasattr(self.fp, "readlines"): - self.readlines = self.fp.readlines - if hasattr(self.fp, "fileno"): - self.fileno = self.fp.fileno - else: - self.fileno = lambda: None - - def __iter__(self): - # Assigning `__iter__` to the instance doesn't work as intended - # because the iter builtin does something like `cls.__iter__(obj)` - # and thus fails to find the _bound_ method `obj.__iter__`. - # Returning just `self.fp` works for built-in file objects but - # might not work for general file-like objects. - return iter(self.fp) - - def __repr__(self): - return '<%s at %r whose fp = %r>' % (self.__class__.__name__, - id(self), self.fp) - - def close(self): - if self.fp: - self.fp.close() - self.fp = None - self.read = None - self.readline = None - self.readlines = None - self.fileno = None - self.__iter__ = None - self.__next__ = None - - def __enter__(self): - if self.fp is None: - raise ValueError("I/O operation on closed file") - return self - - def __exit__(self, type, value, traceback): - self.close() - -class addclosehook(addbase): - """Class to add a close hook to an open file.""" - - def __init__(self, fp, closehook, *hookargs): - addbase.__init__(self, fp) - self.closehook = closehook - self.hookargs = hookargs - - def close(self): - if self.closehook: - self.closehook(*self.hookargs) - self.closehook = None - self.hookargs = None - addbase.close(self) - -class addinfo(addbase): - """class to add an info() method to an open file.""" - - def __init__(self, fp, headers): - addbase.__init__(self, fp) - self.headers = headers - - def info(self): - return self.headers - -class addinfourl(addbase): - """class to add info() and geturl() methods to an open file.""" - - def __init__(self, fp, headers, url, code=None): - addbase.__init__(self, fp) - self.headers = headers - self.url = url - self.code = code - - def info(self): - return self.headers - - def getcode(self): - return self.code - - def geturl(self): - return self.url - -del absolute_import, division, unicode_literals, object diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/robotparser.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/robotparser.py deleted file mode 100644 index a0f36511..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/urllib/robotparser.py +++ /dev/null @@ -1,211 +0,0 @@ -from __future__ import absolute_import, division, unicode_literals -from future.builtins import str -""" robotparser.py - - Copyright (C) 2000 Bastian Kleineidam - - You can choose between two licenses when using this package: - 1) GNU GPLv2 - 2) PSF license for Python 2.2 - - The robots.txt Exclusion Protocol is implemented as specified in - http://info.webcrawler.com/mak/projects/robots/norobots-rfc.html -""" - -# Was: import urllib.parse, urllib.request -from future.backports import urllib -from future.backports.urllib import parse as _parse, request as _request -urllib.parse = _parse -urllib.request = _request - - -__all__ = ["RobotFileParser"] - -class RobotFileParser(object): - """ This class provides a set of methods to read, parse and answer - questions about a single robots.txt file. - - """ - - def __init__(self, url=''): - self.entries = [] - self.default_entry = None - self.disallow_all = False - self.allow_all = False - self.set_url(url) - self.last_checked = 0 - - def mtime(self): - """Returns the time the robots.txt file was last fetched. - - This is useful for long-running web spiders that need to - check for new robots.txt files periodically. - - """ - return self.last_checked - - def modified(self): - """Sets the time the robots.txt file was last fetched to the - current time. - - """ - import time - self.last_checked = time.time() - - def set_url(self, url): - """Sets the URL referring to a robots.txt file.""" - self.url = url - self.host, self.path = urllib.parse.urlparse(url)[1:3] - - def read(self): - """Reads the robots.txt URL and feeds it to the parser.""" - try: - f = urllib.request.urlopen(self.url) - except urllib.error.HTTPError as err: - if err.code in (401, 403): - self.disallow_all = True - elif err.code >= 400: - self.allow_all = True - else: - raw = f.read() - self.parse(raw.decode("utf-8").splitlines()) - - def _add_entry(self, entry): - if "*" in entry.useragents: - # the default entry is considered last - if self.default_entry is None: - # the first default entry wins - self.default_entry = entry - else: - self.entries.append(entry) - - def parse(self, lines): - """Parse the input lines from a robots.txt file. - - We allow that a user-agent: line is not preceded by - one or more blank lines. - """ - # states: - # 0: start state - # 1: saw user-agent line - # 2: saw an allow or disallow line - state = 0 - entry = Entry() - - for line in lines: - if not line: - if state == 1: - entry = Entry() - state = 0 - elif state == 2: - self._add_entry(entry) - entry = Entry() - state = 0 - # remove optional comment and strip line - i = line.find('#') - if i >= 0: - line = line[:i] - line = line.strip() - if not line: - continue - line = line.split(':', 1) - if len(line) == 2: - line[0] = line[0].strip().lower() - line[1] = urllib.parse.unquote(line[1].strip()) - if line[0] == "user-agent": - if state == 2: - self._add_entry(entry) - entry = Entry() - entry.useragents.append(line[1]) - state = 1 - elif line[0] == "disallow": - if state != 0: - entry.rulelines.append(RuleLine(line[1], False)) - state = 2 - elif line[0] == "allow": - if state != 0: - entry.rulelines.append(RuleLine(line[1], True)) - state = 2 - if state == 2: - self._add_entry(entry) - - - def can_fetch(self, useragent, url): - """using the parsed robots.txt decide if useragent can fetch url""" - if self.disallow_all: - return False - if self.allow_all: - return True - # search for given user agent matches - # the first match counts - parsed_url = urllib.parse.urlparse(urllib.parse.unquote(url)) - url = urllib.parse.urlunparse(('','',parsed_url.path, - parsed_url.params,parsed_url.query, parsed_url.fragment)) - url = urllib.parse.quote(url) - if not url: - url = "/" - for entry in self.entries: - if entry.applies_to(useragent): - return entry.allowance(url) - # try the default entry last - if self.default_entry: - return self.default_entry.allowance(url) - # agent not found ==> access granted - return True - - def __str__(self): - return ''.join([str(entry) + "\n" for entry in self.entries]) - - -class RuleLine(object): - """A rule line is a single "Allow:" (allowance==True) or "Disallow:" - (allowance==False) followed by a path.""" - def __init__(self, path, allowance): - if path == '' and not allowance: - # an empty value means allow all - allowance = True - self.path = urllib.parse.quote(path) - self.allowance = allowance - - def applies_to(self, filename): - return self.path == "*" or filename.startswith(self.path) - - def __str__(self): - return (self.allowance and "Allow" or "Disallow") + ": " + self.path - - -class Entry(object): - """An entry has one or more user-agents and zero or more rulelines""" - def __init__(self): - self.useragents = [] - self.rulelines = [] - - def __str__(self): - ret = [] - for agent in self.useragents: - ret.extend(["User-agent: ", agent, "\n"]) - for line in self.rulelines: - ret.extend([str(line), "\n"]) - return ''.join(ret) - - def applies_to(self, useragent): - """check if this entry applies to the specified agent""" - # split the name token and make it lower case - useragent = useragent.split("/")[0].lower() - for agent in self.useragents: - if agent == '*': - # we have the catch-all agent - return True - agent = agent.lower() - if agent in useragent: - return True - return False - - def allowance(self, filename): - """Preconditions: - - our agent applies to this entry - - filename is URL decoded""" - for line in self.rulelines: - if line.applies_to(filename): - return line.allowance - return True diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/__init__.py deleted file mode 100644 index 196d3788..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# This directory is a Python package. diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 045ae1e98e21400877db1241a065ada5862b739f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 204 zcmYe~<>g`k0%o0Ev1&m2F^Gc<7=auIATDMB5-AM944RC7D;bJF!U*D5p?+>^N@k+I zM`BTmOHpQ7s(wIaMoE62esFR|etwR=r?;zPd~tG7VnJ$=zFt{sUYUMQW|DqEWl2VU zp0S>#esN|=s%}AIa&}^RYO#J=X-R2Os(uoXUyxr^QmkK*n^RPftREkrnU`4-AFo$X Vd5gm)H$SB`C)Ez*l+QrS006#9Ht+xd diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/__pycache__/client.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/__pycache__/client.cpython-39.pyc deleted file mode 100644 index 045827c6aa6235b88028a788a03e7cc16aa5a476..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33767 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 Rzzkciz(iOueIY$;{XdY1OS%96 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/__pycache__/server.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/__pycache__/server.cpython-39.pyc deleted file mode 100644 index 0d3325b920237e22b76bda4d7bcd0fd9db6f2b6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29902 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 G&HoM4<#vDo diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/client.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/client.py deleted file mode 100644 index b78e5bad..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/client.py +++ /dev/null @@ -1,1496 +0,0 @@ -# -# XML-RPC CLIENT LIBRARY -# $Id$ -# -# an XML-RPC client interface for Python. -# -# the marshalling and response parser code can also be used to -# implement XML-RPC servers. -# -# Notes: -# this version is designed to work with Python 2.1 or newer. -# -# History: -# 1999-01-14 fl Created -# 1999-01-15 fl Changed dateTime to use localtime -# 1999-01-16 fl Added Binary/base64 element, default to RPC2 service -# 1999-01-19 fl Fixed array data element (from Skip Montanaro) -# 1999-01-21 fl Fixed dateTime constructor, etc. -# 1999-02-02 fl Added fault handling, handle empty sequences, etc. -# 1999-02-10 fl Fixed problem with empty responses (from Skip Montanaro) -# 1999-06-20 fl Speed improvements, pluggable parsers/transports (0.9.8) -# 2000-11-28 fl Changed boolean to check the truth value of its argument -# 2001-02-24 fl Added encoding/Unicode/SafeTransport patches -# 2001-02-26 fl Added compare support to wrappers (0.9.9/1.0b1) -# 2001-03-28 fl Make sure response tuple is a singleton -# 2001-03-29 fl Don't require empty params element (from Nicholas Riley) -# 2001-06-10 fl Folded in _xmlrpclib accelerator support (1.0b2) -# 2001-08-20 fl Base xmlrpclib.Error on built-in Exception (from Paul Prescod) -# 2001-09-03 fl Allow Transport subclass to override getparser -# 2001-09-10 fl Lazy import of urllib, cgi, xmllib (20x import speedup) -# 2001-10-01 fl Remove containers from memo cache when done with them -# 2001-10-01 fl Use faster escape method (80% dumps speedup) -# 2001-10-02 fl More dumps microtuning -# 2001-10-04 fl Make sure import expat gets a parser (from Guido van Rossum) -# 2001-10-10 sm Allow long ints to be passed as ints if they don't overflow -# 2001-10-17 sm Test for int and long overflow (allows use on 64-bit systems) -# 2001-11-12 fl Use repr() to marshal doubles (from Paul Felix) -# 2002-03-17 fl Avoid buffered read when possible (from James Rucker) -# 2002-04-07 fl Added pythondoc comments -# 2002-04-16 fl Added __str__ methods to datetime/binary wrappers -# 2002-05-15 fl Added error constants (from Andrew Kuchling) -# 2002-06-27 fl Merged with Python CVS version -# 2002-10-22 fl Added basic authentication (based on code from Phillip Eby) -# 2003-01-22 sm Add support for the bool type -# 2003-02-27 gvr Remove apply calls -# 2003-04-24 sm Use cStringIO if available -# 2003-04-25 ak Add support for nil -# 2003-06-15 gn Add support for time.struct_time -# 2003-07-12 gp Correct marshalling of Faults -# 2003-10-31 mvl Add multicall support -# 2004-08-20 mvl Bump minimum supported Python version to 2.1 -# -# Copyright (c) 1999-2002 by Secret Labs AB. -# Copyright (c) 1999-2002 by Fredrik Lundh. -# -# info@pythonware.com -# http://www.pythonware.com -# -# -------------------------------------------------------------------- -# The XML-RPC client interface is -# -# Copyright (c) 1999-2002 by Secret Labs AB -# Copyright (c) 1999-2002 by Fredrik Lundh -# -# By obtaining, using, and/or copying this software and/or its -# associated documentation, you agree that you have read, understood, -# and will comply with the following terms and conditions: -# -# Permission to use, copy, modify, and distribute this software and -# its associated documentation for any purpose and without fee is -# hereby granted, provided that the above copyright notice appears in -# all copies, and that both that copyright notice and this permission -# notice appear in supporting documentation, and that the name of -# Secret Labs AB or the author not be used in advertising or publicity -# pertaining to distribution of the software without specific, written -# prior permission. -# -# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD -# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- -# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR -# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY -# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS -# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE -# OF THIS SOFTWARE. -# -------------------------------------------------------------------- - -""" -Ported using Python-Future from the Python 3.3 standard library. - -An XML-RPC client interface for Python. - -The marshalling and response parser code can also be used to -implement XML-RPC servers. - -Exported exceptions: - - Error Base class for client errors - ProtocolError Indicates an HTTP protocol error - ResponseError Indicates a broken response package - Fault Indicates an XML-RPC fault package - -Exported classes: - - ServerProxy Represents a logical connection to an XML-RPC server - - MultiCall Executor of boxcared xmlrpc requests - DateTime dateTime wrapper for an ISO 8601 string or time tuple or - localtime integer value to generate a "dateTime.iso8601" - XML-RPC value - Binary binary data wrapper - - Marshaller Generate an XML-RPC params chunk from a Python data structure - Unmarshaller Unmarshal an XML-RPC response from incoming XML event message - Transport Handles an HTTP transaction to an XML-RPC server - SafeTransport Handles an HTTPS transaction to an XML-RPC server - -Exported constants: - - (none) - -Exported functions: - - getparser Create instance of the fastest available parser & attach - to an unmarshalling object - dumps Convert an argument tuple or a Fault instance to an XML-RPC - request (or response, if the methodresponse option is used). - loads Convert an XML-RPC packet to unmarshalled data plus a method - name (None if not present). -""" - -from __future__ import (absolute_import, division, print_function, - unicode_literals) -from future.builtins import bytes, dict, int, range, str - -import base64 -# Py2.7 compatibility hack -base64.encodebytes = base64.encodestring -base64.decodebytes = base64.decodestring -import sys -import time -from datetime import datetime -from future.backports.http import client as http_client -from future.backports.urllib import parse as urllib_parse -from future.utils import ensure_new_type -from xml.parsers import expat -import socket -import errno -from io import BytesIO -try: - import gzip -except ImportError: - gzip = None #python can be built without zlib/gzip support - -# -------------------------------------------------------------------- -# Internal stuff - -def escape(s): - s = s.replace("&", "&") - s = s.replace("<", "<") - return s.replace(">", ">",) - -# used in User-Agent header sent -__version__ = sys.version[:3] - -# xmlrpc integer limits -MAXINT = 2**31-1 -MININT = -2**31 - -# -------------------------------------------------------------------- -# Error constants (from Dan Libby's specification at -# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php) - -# Ranges of errors -PARSE_ERROR = -32700 -SERVER_ERROR = -32600 -APPLICATION_ERROR = -32500 -SYSTEM_ERROR = -32400 -TRANSPORT_ERROR = -32300 - -# Specific errors -NOT_WELLFORMED_ERROR = -32700 -UNSUPPORTED_ENCODING = -32701 -INVALID_ENCODING_CHAR = -32702 -INVALID_XMLRPC = -32600 -METHOD_NOT_FOUND = -32601 -INVALID_METHOD_PARAMS = -32602 -INTERNAL_ERROR = -32603 - -# -------------------------------------------------------------------- -# Exceptions - -## -# Base class for all kinds of client-side errors. - -class Error(Exception): - """Base class for client errors.""" - def __str__(self): - return repr(self) - -## -# Indicates an HTTP-level protocol error. This is raised by the HTTP -# transport layer, if the server returns an error code other than 200 -# (OK). -# -# @param url The target URL. -# @param errcode The HTTP error code. -# @param errmsg The HTTP error message. -# @param headers The HTTP header dictionary. - -class ProtocolError(Error): - """Indicates an HTTP protocol error.""" - def __init__(self, url, errcode, errmsg, headers): - Error.__init__(self) - self.url = url - self.errcode = errcode - self.errmsg = errmsg - self.headers = headers - def __repr__(self): - return ( - "" % - (self.url, self.errcode, self.errmsg) - ) - -## -# Indicates a broken XML-RPC response package. This exception is -# raised by the unmarshalling layer, if the XML-RPC response is -# malformed. - -class ResponseError(Error): - """Indicates a broken response package.""" - pass - -## -# Indicates an XML-RPC fault response package. This exception is -# raised by the unmarshalling layer, if the XML-RPC response contains -# a fault string. This exception can also be used as a class, to -# generate a fault XML-RPC message. -# -# @param faultCode The XML-RPC fault code. -# @param faultString The XML-RPC fault string. - -class Fault(Error): - """Indicates an XML-RPC fault package.""" - def __init__(self, faultCode, faultString, **extra): - Error.__init__(self) - self.faultCode = faultCode - self.faultString = faultString - def __repr__(self): - return "" % (ensure_new_type(self.faultCode), - ensure_new_type(self.faultString)) - -# -------------------------------------------------------------------- -# Special values - -## -# Backwards compatibility - -boolean = Boolean = bool - -## -# Wrapper for XML-RPC DateTime values. This converts a time value to -# the format used by XML-RPC. -#

-# The value can be given as a datetime object, as a string in the -# format "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by -# time.localtime()), or an integer value (as returned by time.time()). -# The wrapper uses time.localtime() to convert an integer to a time -# tuple. -# -# @param value The time, given as a datetime object, an ISO 8601 string, -# a time tuple, or an integer time value. - - -### For Python-Future: -def _iso8601_format(value): - return "%04d%02d%02dT%02d:%02d:%02d" % ( - value.year, value.month, value.day, - value.hour, value.minute, value.second) -### -# Issue #13305: different format codes across platforms -# _day0 = datetime(1, 1, 1) -# if _day0.strftime('%Y') == '0001': # Mac OS X -# def _iso8601_format(value): -# return value.strftime("%Y%m%dT%H:%M:%S") -# elif _day0.strftime('%4Y') == '0001': # Linux -# def _iso8601_format(value): -# return value.strftime("%4Y%m%dT%H:%M:%S") -# else: -# def _iso8601_format(value): -# return value.strftime("%Y%m%dT%H:%M:%S").zfill(17) -# del _day0 - - -def _strftime(value): - if isinstance(value, datetime): - return _iso8601_format(value) - - if not isinstance(value, (tuple, time.struct_time)): - if value == 0: - value = time.time() - value = time.localtime(value) - - return "%04d%02d%02dT%02d:%02d:%02d" % value[:6] - -class DateTime(object): - """DateTime wrapper for an ISO 8601 string or time tuple or - localtime integer value to generate 'dateTime.iso8601' XML-RPC - value. - """ - - def __init__(self, value=0): - if isinstance(value, str): - self.value = value - else: - self.value = _strftime(value) - - def make_comparable(self, other): - if isinstance(other, DateTime): - s = self.value - o = other.value - elif isinstance(other, datetime): - s = self.value - o = _iso8601_format(other) - elif isinstance(other, str): - s = self.value - o = other - elif hasattr(other, "timetuple"): - s = self.timetuple() - o = other.timetuple() - else: - otype = (hasattr(other, "__class__") - and other.__class__.__name__ - or type(other)) - raise TypeError("Can't compare %s and %s" % - (self.__class__.__name__, otype)) - return s, o - - def __lt__(self, other): - s, o = self.make_comparable(other) - return s < o - - def __le__(self, other): - s, o = self.make_comparable(other) - return s <= o - - def __gt__(self, other): - s, o = self.make_comparable(other) - return s > o - - def __ge__(self, other): - s, o = self.make_comparable(other) - return s >= o - - def __eq__(self, other): - s, o = self.make_comparable(other) - return s == o - - def __ne__(self, other): - s, o = self.make_comparable(other) - return s != o - - def timetuple(self): - return time.strptime(self.value, "%Y%m%dT%H:%M:%S") - - ## - # Get date/time value. - # - # @return Date/time value, as an ISO 8601 string. - - def __str__(self): - return self.value - - def __repr__(self): - return "" % (ensure_new_type(self.value), id(self)) - - def decode(self, data): - self.value = str(data).strip() - - def encode(self, out): - out.write("") - out.write(self.value) - out.write("\n") - -def _datetime(data): - # decode xml element contents into a DateTime structure. - value = DateTime() - value.decode(data) - return value - -def _datetime_type(data): - return datetime.strptime(data, "%Y%m%dT%H:%M:%S") - -## -# Wrapper for binary data. This can be used to transport any kind -# of binary data over XML-RPC, using BASE64 encoding. -# -# @param data An 8-bit string containing arbitrary data. - -class Binary(object): - """Wrapper for binary data.""" - - def __init__(self, data=None): - if data is None: - data = b"" - else: - if not isinstance(data, (bytes, bytearray)): - raise TypeError("expected bytes or bytearray, not %s" % - data.__class__.__name__) - data = bytes(data) # Make a copy of the bytes! - self.data = data - - ## - # Get buffer contents. - # - # @return Buffer contents, as an 8-bit string. - - def __str__(self): - return str(self.data, "latin-1") # XXX encoding?! - - def __eq__(self, other): - if isinstance(other, Binary): - other = other.data - return self.data == other - - def __ne__(self, other): - if isinstance(other, Binary): - other = other.data - return self.data != other - - def decode(self, data): - self.data = base64.decodebytes(data) - - def encode(self, out): - out.write("\n") - encoded = base64.encodebytes(self.data) - out.write(encoded.decode('ascii')) - out.write("\n") - -def _binary(data): - # decode xml element contents into a Binary structure - value = Binary() - value.decode(data) - return value - -WRAPPERS = (DateTime, Binary) - -# -------------------------------------------------------------------- -# XML parsers - -class ExpatParser(object): - # fast expat parser for Python 2.0 and later. - def __init__(self, target): - self._parser = parser = expat.ParserCreate(None, None) - self._target = target - parser.StartElementHandler = target.start - parser.EndElementHandler = target.end - parser.CharacterDataHandler = target.data - encoding = None - target.xml(encoding, None) - - def feed(self, data): - self._parser.Parse(data, 0) - - def close(self): - self._parser.Parse("", 1) # end of data - del self._target, self._parser # get rid of circular references - -# -------------------------------------------------------------------- -# XML-RPC marshalling and unmarshalling code - -## -# XML-RPC marshaller. -# -# @param encoding Default encoding for 8-bit strings. The default -# value is None (interpreted as UTF-8). -# @see dumps - -class Marshaller(object): - """Generate an XML-RPC params chunk from a Python data structure. - - Create a Marshaller instance for each set of parameters, and use - the "dumps" method to convert your data (represented as a tuple) - to an XML-RPC params chunk. To write a fault response, pass a - Fault instance instead. You may prefer to use the "dumps" module - function for this purpose. - """ - - # by the way, if you don't understand what's going on in here, - # that's perfectly ok. - - def __init__(self, encoding=None, allow_none=False): - self.memo = {} - self.data = None - self.encoding = encoding - self.allow_none = allow_none - - dispatch = {} - - def dumps(self, values): - out = [] - write = out.append - dump = self.__dump - if isinstance(values, Fault): - # fault instance - write("\n") - dump({'faultCode': values.faultCode, - 'faultString': values.faultString}, - write) - write("\n") - else: - # parameter block - # FIXME: the xml-rpc specification allows us to leave out - # the entire block if there are no parameters. - # however, changing this may break older code (including - # old versions of xmlrpclib.py), so this is better left as - # is for now. See @XMLRPC3 for more information. /F - write("\n") - for v in values: - write("\n") - dump(v, write) - write("\n") - write("\n") - result = "".join(out) - return str(result) - - def __dump(self, value, write): - try: - f = self.dispatch[type(ensure_new_type(value))] - except KeyError: - # check if this object can be marshalled as a structure - if not hasattr(value, '__dict__'): - raise TypeError("cannot marshal %s objects" % type(value)) - # check if this class is a sub-class of a basic type, - # because we don't know how to marshal these types - # (e.g. a string sub-class) - for type_ in type(value).__mro__: - if type_ in self.dispatch.keys(): - raise TypeError("cannot marshal %s objects" % type(value)) - # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix - # for the p3yk merge, this should probably be fixed more neatly. - f = self.dispatch["_arbitrary_instance"] - f(self, value, write) - - def dump_nil (self, value, write): - if not self.allow_none: - raise TypeError("cannot marshal None unless allow_none is enabled") - write("") - dispatch[type(None)] = dump_nil - - def dump_bool(self, value, write): - write("") - write(value and "1" or "0") - write("\n") - dispatch[bool] = dump_bool - - def dump_long(self, value, write): - if value > MAXINT or value < MININT: - raise OverflowError("long int exceeds XML-RPC limits") - write("") - write(str(int(value))) - write("\n") - dispatch[int] = dump_long - - # backward compatible - dump_int = dump_long - - def dump_double(self, value, write): - write("") - write(repr(ensure_new_type(value))) - write("\n") - dispatch[float] = dump_double - - def dump_unicode(self, value, write, escape=escape): - write("") - write(escape(value)) - write("\n") - dispatch[str] = dump_unicode - - def dump_bytes(self, value, write): - write("\n") - encoded = base64.encodebytes(value) - write(encoded.decode('ascii')) - write("\n") - dispatch[bytes] = dump_bytes - dispatch[bytearray] = dump_bytes - - def dump_array(self, value, write): - i = id(value) - if i in self.memo: - raise TypeError("cannot marshal recursive sequences") - self.memo[i] = None - dump = self.__dump - write("\n") - for v in value: - dump(v, write) - write("\n") - del self.memo[i] - dispatch[tuple] = dump_array - dispatch[list] = dump_array - - def dump_struct(self, value, write, escape=escape): - i = id(value) - if i in self.memo: - raise TypeError("cannot marshal recursive dictionaries") - self.memo[i] = None - dump = self.__dump - write("\n") - for k, v in value.items(): - write("\n") - if not isinstance(k, str): - raise TypeError("dictionary key must be string") - write("%s\n" % escape(k)) - dump(v, write) - write("\n") - write("\n") - del self.memo[i] - dispatch[dict] = dump_struct - - def dump_datetime(self, value, write): - write("") - write(_strftime(value)) - write("\n") - dispatch[datetime] = dump_datetime - - def dump_instance(self, value, write): - # check for special wrappers - if value.__class__ in WRAPPERS: - self.write = write - value.encode(self) - del self.write - else: - # store instance attributes as a struct (really?) - self.dump_struct(value.__dict__, write) - dispatch[DateTime] = dump_instance - dispatch[Binary] = dump_instance - # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix - # for the p3yk merge, this should probably be fixed more neatly. - dispatch["_arbitrary_instance"] = dump_instance - -## -# XML-RPC unmarshaller. -# -# @see loads - -class Unmarshaller(object): - """Unmarshal an XML-RPC response, based on incoming XML event - messages (start, data, end). Call close() to get the resulting - data structure. - - Note that this reader is fairly tolerant, and gladly accepts bogus - XML-RPC data without complaining (but not bogus XML). - """ - - # and again, if you don't understand what's going on in here, - # that's perfectly ok. - - def __init__(self, use_datetime=False, use_builtin_types=False): - self._type = None - self._stack = [] - self._marks = [] - self._data = [] - self._methodname = None - self._encoding = "utf-8" - self.append = self._stack.append - self._use_datetime = use_builtin_types or use_datetime - self._use_bytes = use_builtin_types - - def close(self): - # return response tuple and target method - if self._type is None or self._marks: - raise ResponseError() - if self._type == "fault": - raise Fault(**self._stack[0]) - return tuple(self._stack) - - def getmethodname(self): - return self._methodname - - # - # event handlers - - def xml(self, encoding, standalone): - self._encoding = encoding - # FIXME: assert standalone == 1 ??? - - def start(self, tag, attrs): - # prepare to handle this element - if tag == "array" or tag == "struct": - self._marks.append(len(self._stack)) - self._data = [] - self._value = (tag == "value") - - def data(self, text): - self._data.append(text) - - def end(self, tag): - # call the appropriate end tag handler - try: - f = self.dispatch[tag] - except KeyError: - pass # unknown tag ? - else: - return f(self, "".join(self._data)) - - # - # accelerator support - - def end_dispatch(self, tag, data): - # dispatch data - try: - f = self.dispatch[tag] - except KeyError: - pass # unknown tag ? - else: - return f(self, data) - - # - # element decoders - - dispatch = {} - - def end_nil (self, data): - self.append(None) - self._value = 0 - dispatch["nil"] = end_nil - - def end_boolean(self, data): - if data == "0": - self.append(False) - elif data == "1": - self.append(True) - else: - raise TypeError("bad boolean value") - self._value = 0 - dispatch["boolean"] = end_boolean - - def end_int(self, data): - self.append(int(data)) - self._value = 0 - dispatch["i4"] = end_int - dispatch["i8"] = end_int - dispatch["int"] = end_int - - def end_double(self, data): - self.append(float(data)) - self._value = 0 - dispatch["double"] = end_double - - def end_string(self, data): - if self._encoding: - data = data.decode(self._encoding) - self.append(data) - self._value = 0 - dispatch["string"] = end_string - dispatch["name"] = end_string # struct keys are always strings - - def end_array(self, data): - mark = self._marks.pop() - # map arrays to Python lists - self._stack[mark:] = [self._stack[mark:]] - self._value = 0 - dispatch["array"] = end_array - - def end_struct(self, data): - mark = self._marks.pop() - # map structs to Python dictionaries - dict = {} - items = self._stack[mark:] - for i in range(0, len(items), 2): - dict[items[i]] = items[i+1] - self._stack[mark:] = [dict] - self._value = 0 - dispatch["struct"] = end_struct - - def end_base64(self, data): - value = Binary() - value.decode(data.encode("ascii")) - if self._use_bytes: - value = value.data - self.append(value) - self._value = 0 - dispatch["base64"] = end_base64 - - def end_dateTime(self, data): - value = DateTime() - value.decode(data) - if self._use_datetime: - value = _datetime_type(data) - self.append(value) - dispatch["dateTime.iso8601"] = end_dateTime - - def end_value(self, data): - # if we stumble upon a value element with no internal - # elements, treat it as a string element - if self._value: - self.end_string(data) - dispatch["value"] = end_value - - def end_params(self, data): - self._type = "params" - dispatch["params"] = end_params - - def end_fault(self, data): - self._type = "fault" - dispatch["fault"] = end_fault - - def end_methodName(self, data): - if self._encoding: - data = data.decode(self._encoding) - self._methodname = data - self._type = "methodName" # no params - dispatch["methodName"] = end_methodName - -## Multicall support -# - -class _MultiCallMethod(object): - # some lesser magic to store calls made to a MultiCall object - # for batch execution - def __init__(self, call_list, name): - self.__call_list = call_list - self.__name = name - def __getattr__(self, name): - return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name)) - def __call__(self, *args): - self.__call_list.append((self.__name, args)) - -class MultiCallIterator(object): - """Iterates over the results of a multicall. Exceptions are - raised in response to xmlrpc faults.""" - - def __init__(self, results): - self.results = results - - def __getitem__(self, i): - item = self.results[i] - if isinstance(type(item), dict): - raise Fault(item['faultCode'], item['faultString']) - elif type(item) == type([]): - return item[0] - else: - raise ValueError("unexpected type in multicall result") - -class MultiCall(object): - """server -> a object used to boxcar method calls - - server should be a ServerProxy object. - - Methods can be added to the MultiCall using normal - method call syntax e.g.: - - multicall = MultiCall(server_proxy) - multicall.add(2,3) - multicall.get_address("Guido") - - To execute the multicall, call the MultiCall object e.g.: - - add_result, address = multicall() - """ - - def __init__(self, server): - self.__server = server - self.__call_list = [] - - def __repr__(self): - return "" % id(self) - - __str__ = __repr__ - - def __getattr__(self, name): - return _MultiCallMethod(self.__call_list, name) - - def __call__(self): - marshalled_list = [] - for name, args in self.__call_list: - marshalled_list.append({'methodName' : name, 'params' : args}) - - return MultiCallIterator(self.__server.system.multicall(marshalled_list)) - -# -------------------------------------------------------------------- -# convenience functions - -FastMarshaller = FastParser = FastUnmarshaller = None - -## -# Create a parser object, and connect it to an unmarshalling instance. -# This function picks the fastest available XML parser. -# -# return A (parser, unmarshaller) tuple. - -def getparser(use_datetime=False, use_builtin_types=False): - """getparser() -> parser, unmarshaller - - Create an instance of the fastest available parser, and attach it - to an unmarshalling object. Return both objects. - """ - if FastParser and FastUnmarshaller: - if use_builtin_types: - mkdatetime = _datetime_type - mkbytes = base64.decodebytes - elif use_datetime: - mkdatetime = _datetime_type - mkbytes = _binary - else: - mkdatetime = _datetime - mkbytes = _binary - target = FastUnmarshaller(True, False, mkbytes, mkdatetime, Fault) - parser = FastParser(target) - else: - target = Unmarshaller(use_datetime=use_datetime, use_builtin_types=use_builtin_types) - if FastParser: - parser = FastParser(target) - else: - parser = ExpatParser(target) - return parser, target - -## -# Convert a Python tuple or a Fault instance to an XML-RPC packet. -# -# @def dumps(params, **options) -# @param params A tuple or Fault instance. -# @keyparam methodname If given, create a methodCall request for -# this method name. -# @keyparam methodresponse If given, create a methodResponse packet. -# If used with a tuple, the tuple must be a singleton (that is, -# it must contain exactly one element). -# @keyparam encoding The packet encoding. -# @return A string containing marshalled data. - -def dumps(params, methodname=None, methodresponse=None, encoding=None, - allow_none=False): - """data [,options] -> marshalled data - - Convert an argument tuple or a Fault instance to an XML-RPC - request (or response, if the methodresponse option is used). - - In addition to the data object, the following options can be given - as keyword arguments: - - methodname: the method name for a methodCall packet - - methodresponse: true to create a methodResponse packet. - If this option is used with a tuple, the tuple must be - a singleton (i.e. it can contain only one element). - - encoding: the packet encoding (default is UTF-8) - - All byte strings in the data structure are assumed to use the - packet encoding. Unicode strings are automatically converted, - where necessary. - """ - - assert isinstance(params, (tuple, Fault)), "argument must be tuple or Fault instance" - if isinstance(params, Fault): - methodresponse = 1 - elif methodresponse and isinstance(params, tuple): - assert len(params) == 1, "response tuple must be a singleton" - - if not encoding: - encoding = "utf-8" - - if FastMarshaller: - m = FastMarshaller(encoding) - else: - m = Marshaller(encoding, allow_none) - - data = m.dumps(params) - - if encoding != "utf-8": - xmlheader = "\n" % str(encoding) - else: - xmlheader = "\n" # utf-8 is default - - # standard XML-RPC wrappings - if methodname: - # a method call - if not isinstance(methodname, str): - methodname = methodname.encode(encoding) - data = ( - xmlheader, - "\n" - "", methodname, "\n", - data, - "\n" - ) - elif methodresponse: - # a method response, or a fault structure - data = ( - xmlheader, - "\n", - data, - "\n" - ) - else: - return data # return as is - return str("").join(data) - -## -# Convert an XML-RPC packet to a Python object. If the XML-RPC packet -# represents a fault condition, this function raises a Fault exception. -# -# @param data An XML-RPC packet, given as an 8-bit string. -# @return A tuple containing the unpacked data, and the method name -# (None if not present). -# @see Fault - -def loads(data, use_datetime=False, use_builtin_types=False): - """data -> unmarshalled data, method name - - Convert an XML-RPC packet to unmarshalled data plus a method - name (None if not present). - - If the XML-RPC packet represents a fault condition, this function - raises a Fault exception. - """ - p, u = getparser(use_datetime=use_datetime, use_builtin_types=use_builtin_types) - p.feed(data) - p.close() - return u.close(), u.getmethodname() - -## -# Encode a string using the gzip content encoding such as specified by the -# Content-Encoding: gzip -# in the HTTP header, as described in RFC 1952 -# -# @param data the unencoded data -# @return the encoded data - -def gzip_encode(data): - """data -> gzip encoded data - - Encode data using the gzip content encoding as described in RFC 1952 - """ - if not gzip: - raise NotImplementedError - f = BytesIO() - gzf = gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1) - gzf.write(data) - gzf.close() - encoded = f.getvalue() - f.close() - return encoded - -## -# Decode a string using the gzip content encoding such as specified by the -# Content-Encoding: gzip -# in the HTTP header, as described in RFC 1952 -# -# @param data The encoded data -# @return the unencoded data -# @raises ValueError if data is not correctly coded. - -def gzip_decode(data): - """gzip encoded data -> unencoded data - - Decode data using the gzip content encoding as described in RFC 1952 - """ - if not gzip: - raise NotImplementedError - f = BytesIO(data) - gzf = gzip.GzipFile(mode="rb", fileobj=f) - try: - decoded = gzf.read() - except IOError: - raise ValueError("invalid data") - f.close() - gzf.close() - return decoded - -## -# Return a decoded file-like object for the gzip encoding -# as described in RFC 1952. -# -# @param response A stream supporting a read() method -# @return a file-like object that the decoded data can be read() from - -class GzipDecodedResponse(gzip.GzipFile if gzip else object): - """a file-like object to decode a response encoded with the gzip - method, as described in RFC 1952. - """ - def __init__(self, response): - #response doesn't support tell() and read(), required by - #GzipFile - if not gzip: - raise NotImplementedError - self.io = BytesIO(response.read()) - gzip.GzipFile.__init__(self, mode="rb", fileobj=self.io) - - def close(self): - gzip.GzipFile.close(self) - self.io.close() - - -# -------------------------------------------------------------------- -# request dispatcher - -class _Method(object): - # some magic to bind an XML-RPC method to an RPC server. - # supports "nested" methods (e.g. examples.getStateName) - def __init__(self, send, name): - self.__send = send - self.__name = name - def __getattr__(self, name): - return _Method(self.__send, "%s.%s" % (self.__name, name)) - def __call__(self, *args): - return self.__send(self.__name, args) - -## -# Standard transport class for XML-RPC over HTTP. -#

-# You can create custom transports by subclassing this method, and -# overriding selected methods. - -class Transport(object): - """Handles an HTTP transaction to an XML-RPC server.""" - - # client identifier (may be overridden) - user_agent = "Python-xmlrpc/%s" % __version__ - - #if true, we'll request gzip encoding - accept_gzip_encoding = True - - # if positive, encode request using gzip if it exceeds this threshold - # note that many server will get confused, so only use it if you know - # that they can decode such a request - encode_threshold = None #None = don't encode - - def __init__(self, use_datetime=False, use_builtin_types=False): - self._use_datetime = use_datetime - self._use_builtin_types = use_builtin_types - self._connection = (None, None) - self._extra_headers = [] - - ## - # Send a complete request, and parse the response. - # Retry request if a cached connection has disconnected. - # - # @param host Target host. - # @param handler Target PRC handler. - # @param request_body XML-RPC request body. - # @param verbose Debugging flag. - # @return Parsed response. - - def request(self, host, handler, request_body, verbose=False): - #retry request once if cached connection has gone cold - for i in (0, 1): - try: - return self.single_request(host, handler, request_body, verbose) - except socket.error as e: - if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE): - raise - except http_client.BadStatusLine: #close after we sent request - if i: - raise - - def single_request(self, host, handler, request_body, verbose=False): - # issue XML-RPC request - try: - http_conn = self.send_request(host, handler, request_body, verbose) - resp = http_conn.getresponse() - if resp.status == 200: - self.verbose = verbose - return self.parse_response(resp) - - except Fault: - raise - except Exception: - #All unexpected errors leave connection in - # a strange state, so we clear it. - self.close() - raise - - #We got an error response. - #Discard any response data and raise exception - if resp.getheader("content-length", ""): - resp.read() - raise ProtocolError( - host + handler, - resp.status, resp.reason, - dict(resp.getheaders()) - ) - - - ## - # Create parser. - # - # @return A 2-tuple containing a parser and a unmarshaller. - - def getparser(self): - # get parser and unmarshaller - return getparser(use_datetime=self._use_datetime, - use_builtin_types=self._use_builtin_types) - - ## - # Get authorization info from host parameter - # Host may be a string, or a (host, x509-dict) tuple; if a string, - # it is checked for a "user:pw@host" format, and a "Basic - # Authentication" header is added if appropriate. - # - # @param host Host descriptor (URL or (URL, x509 info) tuple). - # @return A 3-tuple containing (actual host, extra headers, - # x509 info). The header and x509 fields may be None. - - def get_host_info(self, host): - - x509 = {} - if isinstance(host, tuple): - host, x509 = host - - auth, host = urllib_parse.splituser(host) - - if auth: - auth = urllib_parse.unquote_to_bytes(auth) - auth = base64.encodebytes(auth).decode("utf-8") - auth = "".join(auth.split()) # get rid of whitespace - extra_headers = [ - ("Authorization", "Basic " + auth) - ] - else: - extra_headers = [] - - return host, extra_headers, x509 - - ## - # Connect to server. - # - # @param host Target host. - # @return An HTTPConnection object - - def make_connection(self, host): - #return an existing connection if possible. This allows - #HTTP/1.1 keep-alive. - if self._connection and host == self._connection[0]: - return self._connection[1] - # create a HTTP connection object from a host descriptor - chost, self._extra_headers, x509 = self.get_host_info(host) - self._connection = host, http_client.HTTPConnection(chost) - return self._connection[1] - - ## - # Clear any cached connection object. - # Used in the event of socket errors. - # - def close(self): - if self._connection[1]: - self._connection[1].close() - self._connection = (None, None) - - ## - # Send HTTP request. - # - # @param host Host descriptor (URL or (URL, x509 info) tuple). - # @param handler Targer RPC handler (a path relative to host) - # @param request_body The XML-RPC request body - # @param debug Enable debugging if debug is true. - # @return An HTTPConnection. - - def send_request(self, host, handler, request_body, debug): - connection = self.make_connection(host) - headers = self._extra_headers[:] - if debug: - connection.set_debuglevel(1) - if self.accept_gzip_encoding and gzip: - connection.putrequest("POST", handler, skip_accept_encoding=True) - headers.append(("Accept-Encoding", "gzip")) - else: - connection.putrequest("POST", handler) - headers.append(("Content-Type", "text/xml")) - headers.append(("User-Agent", self.user_agent)) - self.send_headers(connection, headers) - self.send_content(connection, request_body) - return connection - - ## - # Send request headers. - # This function provides a useful hook for subclassing - # - # @param connection httpConnection. - # @param headers list of key,value pairs for HTTP headers - - def send_headers(self, connection, headers): - for key, val in headers: - connection.putheader(key, val) - - ## - # Send request body. - # This function provides a useful hook for subclassing - # - # @param connection httpConnection. - # @param request_body XML-RPC request body. - - def send_content(self, connection, request_body): - #optionally encode the request - if (self.encode_threshold is not None and - self.encode_threshold < len(request_body) and - gzip): - connection.putheader("Content-Encoding", "gzip") - request_body = gzip_encode(request_body) - - connection.putheader("Content-Length", str(len(request_body))) - connection.endheaders(request_body) - - ## - # Parse response. - # - # @param file Stream. - # @return Response tuple and target method. - - def parse_response(self, response): - # read response data from httpresponse, and parse it - # Check for new http response object, otherwise it is a file object. - if hasattr(response, 'getheader'): - if response.getheader("Content-Encoding", "") == "gzip": - stream = GzipDecodedResponse(response) - else: - stream = response - else: - stream = response - - p, u = self.getparser() - - while 1: - data = stream.read(1024) - if not data: - break - if self.verbose: - print("body:", repr(data)) - p.feed(data) - - if stream is not response: - stream.close() - p.close() - - return u.close() - -## -# Standard transport class for XML-RPC over HTTPS. - -class SafeTransport(Transport): - """Handles an HTTPS transaction to an XML-RPC server.""" - - # FIXME: mostly untested - - def make_connection(self, host): - if self._connection and host == self._connection[0]: - return self._connection[1] - - if not hasattr(http_client, "HTTPSConnection"): - raise NotImplementedError( - "your version of http.client doesn't support HTTPS") - # create a HTTPS connection object from a host descriptor - # host may be a string, or a (host, x509-dict) tuple - chost, self._extra_headers, x509 = self.get_host_info(host) - self._connection = host, http_client.HTTPSConnection(chost, - None, **(x509 or {})) - return self._connection[1] - -## -# Standard server proxy. This class establishes a virtual connection -# to an XML-RPC server. -#

%s
' % doc - return '
%s
%s
\n' % (decl, doc) - - def docserver(self, server_name, package_documentation, methods): - """Produce HTML documentation for an XML-RPC server.""" - - fdict = {} - for key, value in methods.items(): - fdict[key] = '#-' + key - fdict[value] = fdict[key] - - server_name = self.escape(server_name) - head = '%s' % server_name - result = self.heading(head, '#ffffff', '#7799ee') - - doc = self.markup(package_documentation, self.preformat, fdict) - doc = doc and '%s' % doc - result = result + '

%s

\n' % doc - - contents = [] - method_items = sorted(methods.items()) - for key, value in method_items: - contents.append(self.docroutine(value, key, funcs=fdict)) - result = result + self.bigsection( - 'Methods', '#ffffff', '#eeaa77', ''.join(contents)) - - return result - -class XMLRPCDocGenerator(object): - """Generates documentation for an XML-RPC server. - - This class is designed as mix-in and should not - be constructed directly. - """ - - def __init__(self): - # setup variables used for HTML documentation - self.server_name = 'XML-RPC Server Documentation' - self.server_documentation = \ - "This server exports the following methods through the XML-RPC "\ - "protocol." - self.server_title = 'XML-RPC Server Documentation' - - def set_server_title(self, server_title): - """Set the HTML title of the generated server documentation""" - - self.server_title = server_title - - def set_server_name(self, server_name): - """Set the name of the generated HTML server documentation""" - - self.server_name = server_name - - def set_server_documentation(self, server_documentation): - """Set the documentation string for the entire server.""" - - self.server_documentation = server_documentation - - def generate_html_documentation(self): - """generate_html_documentation() => html documentation for the server - - Generates HTML documentation for the server using introspection for - installed functions and instances that do not implement the - _dispatch method. Alternatively, instances can choose to implement - the _get_method_argstring(method_name) method to provide the - argument string used in the documentation and the - _methodHelp(method_name) method to provide the help text used - in the documentation.""" - - methods = {} - - for method_name in self.system_listMethods(): - if method_name in self.funcs: - method = self.funcs[method_name] - elif self.instance is not None: - method_info = [None, None] # argspec, documentation - if hasattr(self.instance, '_get_method_argstring'): - method_info[0] = self.instance._get_method_argstring(method_name) - if hasattr(self.instance, '_methodHelp'): - method_info[1] = self.instance._methodHelp(method_name) - - method_info = tuple(method_info) - if method_info != (None, None): - method = method_info - elif not hasattr(self.instance, '_dispatch'): - try: - method = resolve_dotted_attribute( - self.instance, - method_name - ) - except AttributeError: - method = method_info - else: - method = method_info - else: - assert 0, "Could not find method in self.functions and no "\ - "instance installed" - - methods[method_name] = method - - documenter = ServerHTMLDoc() - documentation = documenter.docserver( - self.server_name, - self.server_documentation, - methods - ) - - return documenter.page(self.server_title, documentation) - -class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): - """XML-RPC and documentation request handler class. - - Handles all HTTP POST requests and attempts to decode them as - XML-RPC requests. - - Handles all HTTP GET requests and interprets them as requests - for documentation. - """ - - def do_GET(self): - """Handles the HTTP GET request. - - Interpret all HTTP GET requests as requests for server - documentation. - """ - # Check that the path is legal - if not self.is_rpc_path_valid(): - self.report_404() - return - - response = self.server.generate_html_documentation().encode('utf-8') - self.send_response(200) - self.send_header("Content-type", "text/html") - self.send_header("Content-length", str(len(response))) - self.end_headers() - self.wfile.write(response) - -class DocXMLRPCServer( SimpleXMLRPCServer, - XMLRPCDocGenerator): - """XML-RPC and HTML documentation server. - - Adds the ability to serve server documentation to the capabilities - of SimpleXMLRPCServer. - """ - - def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler, - logRequests=True, allow_none=False, encoding=None, - bind_and_activate=True, use_builtin_types=False): - SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, - allow_none, encoding, bind_and_activate, - use_builtin_types) - XMLRPCDocGenerator.__init__(self) - -class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler, - XMLRPCDocGenerator): - """Handler for XML-RPC data and documentation requests passed through - CGI""" - - def handle_get(self): - """Handles the HTTP GET request. - - Interpret all HTTP GET requests as requests for server - documentation. - """ - - response = self.generate_html_documentation().encode('utf-8') - - print('Content-Type: text/html') - print('Content-Length: %d' % len(response)) - print() - sys.stdout.flush() - sys.stdout.buffer.write(response) - sys.stdout.buffer.flush() - - def __init__(self): - CGIXMLRPCRequestHandler.__init__(self) - XMLRPCDocGenerator.__init__(self) - - -if __name__ == '__main__': - import datetime - - class ExampleService: - def getData(self): - return '42' - - class currentTime: - @staticmethod - def getCurrentTime(): - return datetime.datetime.now() - - server = SimpleXMLRPCServer(("localhost", 8000)) - server.register_function(pow) - server.register_function(lambda x,y: x+y, 'add') - server.register_instance(ExampleService(), allow_dotted_names=True) - server.register_multicall_functions() - print('Serving XML-RPC on localhost port 8000') - print('It is advisable to run this example server within a secure, closed network.') - try: - server.serve_forever() - except KeyboardInterrupt: - print("\nKeyboard interrupt received, exiting.") - server.server_close() - sys.exit(0) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__init__.py deleted file mode 100644 index 8bc1649d..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__init__.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -A module that brings in equivalents of the new and modified Python 3 -builtins into Py2. Has no effect on Py3. - -See the docs `here `_ -(``docs/what-else.rst``) for more information. - -""" - -from future.builtins.iterators import (filter, map, zip) -# The isinstance import is no longer needed. We provide it only for -# backward-compatibility with future v0.8.2. It will be removed in future v1.0. -from future.builtins.misc import (ascii, chr, hex, input, isinstance, next, - oct, open, pow, round, super, max, min) -from future.utils import PY3 - -if PY3: - import builtins - bytes = builtins.bytes - dict = builtins.dict - int = builtins.int - list = builtins.list - object = builtins.object - range = builtins.range - str = builtins.str - __all__ = [] -else: - from future.types import (newbytes as bytes, - newdict as dict, - newint as int, - newlist as list, - newobject as object, - newrange as range, - newstr as str) -from future import utils - - -if not utils.PY3: - # We only import names that shadow the builtins on Py2. No other namespace - # pollution on Py2. - - # Only shadow builtins on Py2; no new names - __all__ = ['filter', 'map', 'zip', - 'ascii', 'chr', 'hex', 'input', 'next', 'oct', 'open', 'pow', - 'round', 'super', - 'bytes', 'dict', 'int', 'list', 'object', 'range', 'str', 'max', 'min' - ] - -else: - # No namespace pollution on Py3 - __all__ = [] diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index f5ae19133d91bca3007e84edac525aa4a7b1adaa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1264 zcmaKr&2A$_5XZ-!8GlUde68ceNp`mv!e%WIk3>$OXvIn(EkZ((a-yWwcS%g=TTBx~ULNWgRQrx6DSJtp%hP#|8e#=so zk%5>Ap0Uf7T@;F)N0X(@t7>%+0n+1$3r$NH0nZc zLg7%RRul@u{fSrU4Sm;`p+K&kFVoWbZ$&xmx-d0UA)G&5;QE5!xlpKbX0SI#esTNmnLsWKOuWx4iwhmM6B)|`L&?fEyurfXrXCM(O0YvL#q zXy3pAmZw_Cg|V)U3&rFb%TpcKshacZl(8CJg-a`PGwf^XOYIW^LP)^=^>mZaB2Z)N z4MK;|C2SJ55H4IR2`U*rX$UF-jr-`~I<${*^)^}UAcikoGfA-6NdklWJ9)U~k-%D} zw$SE1anO7Jq!ecQpOV`Y=zn$pqXjzmZmW{$T}rC;AVwdNr(?on!V|(%0wX*_P$p@f zVW=?-^x4pH{-l@l zR}Wu4$J@+sh#cRE+^CbdX~*xRUB8?5{GLl|-K3um{DJE%ov8P_>u*H;Xz<+ehcI}2 z$$SeN_nb%9d=G};J#*o;XAXF`obPYLhQBF$OUK`gHV&Q9kpF`Yy`xwd774@W2^cod zMPh|yi(H0QC~4R>ml0DE7@UTX(HaXAZp?_SViCrSYw$Fr>I5RT&?;pQ*EYsYyOZ6C zS6NS0lqbN*4;ZnOxYH^&2bLRrOvEuTi&5OX>3IyV(#j&YxrRv{kD1gFMogrc(w2Q# z+ioMbF|dV7615U?$<*Aa1T0`QJFh0yc;Z=>tj8=&GaMA?5XP7$gbR`2Q#A^i<#x;x zC70teJk3`@y{I9|=^qV=;?bDpQiLkPg;UMt5;p&BU%-f1_4wPwl9qZa=ZfeI5+r@2Heghtj<+% zC2Z^wJcg(E4F|(GTELYSrUvH=CETfl)LarE^ z%jU|)Y&I)#+8)hjj94_Awb)f_8W>Lz*fN%;!rnRVArpxib=IQ^yOSWf^5sR_a4L)~ zbK-5Sh4dOCj!X0P+s=ik8oHy4TpGT>=oPW@TphfSOO_zY^vT4KBuvQ1PjnO&+iXj-40`(nQUjn9w?rZL= zTw}{+2@fF!X)k0%f5jLC;SHx5#%bD)WCl_$tq2X)>JX?@K%u=9RVqw~6|1`z-80Ts zN*_~SAz2McfLeHUb#W&V?7y9CpD}_+LK?F@A|a5uRzy8PziIk~onIo*2~#TU1NSp+#J{j!GVT-e0J*&Zt`qBXJ@qui|x1!jW4D zDy)mknMR*M6XD1$`neT}8C~}KrF<0KFxGw-HN+oO8iG1pYe>-}4JrC%8x%d#5dTu! z1I3`y51L6oij7)7ib16x#ny#*Lzlv>uMW=L`Ziba;b&*z(s}WG_|*N;MSbr+*(vqv zD?rkrkn>g&5L;|fwF&Y6d%j?@X~S=RE3S_g~N;EDxn%}7#rKMNukk=`|rQBqD9Nj1WOn{)QE0&buhs5>!a4nOyU$r%$f0pMDSJ)Qs!H zqG07=24A_Z27vWv|4q5J~Jl|a)d?ih22n-i=~5as?vGQ zs6!4)9Bt_EU!B0Y+zqyf%;ycX(D7-z4>!Daepad6%;!fDor?2pMo&y`oDK{pE&$sx zk+X$UoOIhqSIG))O19*5QCW&=Lop61H0b1W-8Ez<7)eu>=V?l>-g2#AOi$h=)LL1g zWQg{>+jszI7_i)^_fd3$$i+f4y$9=7Urj^BSH(;e3a z_*_dJO-ItzBf?5kCinMf3a64Y4NkK9&YAcY=dnoJHihTWIk=pnZBS!;sK z+TETmdDGQ;d@T8Ip8m*Ly5a9_T<}b=_*)#?ueI3m_-?le;?)z_Eq-LpE z$#p#2ak-1p%eZU~_|5EjYyf-;g}r8LZsHbCd`uO0r(jm@>+u)ut@x#n%;sYZ{ZAhE T#{Q%?^xbEIAHlj^4gG%r(wgBA diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__pycache__/misc.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__pycache__/misc.cpython-39.pyc deleted file mode 100644 index 99904860d971162f94c79a2f825766254c8d2643..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3088 zcmb7G-EP}P7AC15%ZwDqZd#{FlT6dNl>&z<>|(JkilXh-MT!Q2QKt>UqL3(ZB#lFI zv@@h^8QGgA56}nbRqyr{77Og%J_K*~YF}Zm_B%sLak2pxrNE;zXU@!=^PQhz(CN4q zp4#r;e){L8W&Mi==bst|pX1eE*_IVqz7^S#6WfX7JGRB0$}`N-tag0C4UpE*5a+?4ga#4ZN+aUZ~5DX zUyrXOZ~Ipb@5a}Xcl_&yzX1M*f79^om?iJ}@4}v2&n*8v{@&P*I?=`Fw%?gp(Z+AK ze>;7SGn(;^zr%NqVI64OZ-c@@&`p06^s;{$lx*5LhV`KE3AE#PK)3yE(5wDcc>Vq_ zwqh7v_pgKA^lwfqoWXF$>utOgZB88jcGNtwdRxKI4a;)BV2O|$*sU*v^a)p3{~d{uFt^x< zPlGuUM6oJ%`=yylU_wO8B0diC7^lxD?khd-qe@d4fBuiAI~)p)WXpmyN>WiksgF%q%J7Ve42rfmM9_eXDU7aIW9d@@A^V7n=D zcf^5V#Ak|!0d0j7Lr(f1Ba4ye2zM7&mydg2JfZ60jBJN}9G0zqr zdq@xp?T&z4j@-rEglFa9va;k=tOzM8_~EcDc8n7-nP#p8sOm&uo0ta?IUg|15o&~x z&GR_rDkuZWx_Zjv7=7y)9S7;vD@(v2zDKP`0#%(!#Zg|nY$0dR zY>YvkopaI~0H0+gW4Et?XPzQ8;>GSu5!ak2nhYIrSQPboo81YibV#?amK zJZ$!Fy5@Rz(a19qYcO>x;a;t1n0{E)(71{kdR@;cnq~hfYBPEA0IF+9Icg4ITVPF@ zR=lZW@J+~3g-cGP6^Cve(xtk5h;QNj6JGrrNM=2?GW(gMwle3b_00YYNo*sz4T%kj z4T)nUe}Ke+#5sE(lG+nT-OuVzq5sTzWF1*04~-g)9$D$JNt*rst1U4PZ8N4ciGubJ$CmqmgdQbD|e;wh>AWt zK^9fLjvDMswRL*u4sw{4ic`h;In~uRFE8oRnjYI>XS=xDGPKkM1wWxo!~G{hK~1&G zy@_W!5@6RoZ?)M1K)>`n=rs%XMT=7xFqa1qinb_lF?P z`GHbWA#Lo_!d?{i**Ld|iAXdw@ zTXyw24*0gSWw-IamwOWy;jciXil0)5%OWVdR~R^4f1RQ;T$)4t)> QTXu8#b5Hq)$ diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__pycache__/new_min_max.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/__pycache__/new_min_max.cpython-39.pyc deleted file mode 100644 index 4fe2280b4f229d229ec957e3641dcc7df3914c5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1601 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 WEi~>=x=^ueZ#%Btwzog>+W!NE5?0Ir diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/disabled.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/disabled.py deleted file mode 100644 index f6d6ea9b..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/disabled.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -This disables builtin functions (and one exception class) which are -removed from Python 3.3. - -This module is designed to be used like this:: - - from future.builtins.disabled import * - -This disables the following obsolete Py2 builtin functions:: - - apply, cmp, coerce, execfile, file, input, long, - raw_input, reduce, reload, unicode, xrange - -We don't hack __builtin__, which is very fragile because it contaminates -imported modules too. Instead, we just create new functions with -the same names as the obsolete builtins from Python 2 which raise -NameError exceptions when called. - -Note that both ``input()`` and ``raw_input()`` are among the disabled -functions (in this module). Although ``input()`` exists as a builtin in -Python 3, the Python 2 ``input()`` builtin is unsafe to use because it -can lead to shell injection. Therefore we shadow it by default upon ``from -future.builtins.disabled import *``, in case someone forgets to import our -replacement ``input()`` somehow and expects Python 3 semantics. - -See the ``future.builtins.misc`` module for a working version of -``input`` with Python 3 semantics. - -(Note that callable() is not among the functions disabled; this was -reintroduced into Python 3.2.) - -This exception class is also disabled: - - StandardError - -""" - -from __future__ import division, absolute_import, print_function - -from future import utils - - -OBSOLETE_BUILTINS = ['apply', 'chr', 'cmp', 'coerce', 'execfile', 'file', - 'input', 'long', 'raw_input', 'reduce', 'reload', - 'unicode', 'xrange', 'StandardError'] - - -def disabled_function(name): - ''' - Returns a function that cannot be called - ''' - def disabled(*args, **kwargs): - ''' - A function disabled by the ``future`` module. This function is - no longer a builtin in Python 3. - ''' - raise NameError('obsolete Python 2 builtin {0} is disabled'.format(name)) - return disabled - - -if not utils.PY3: - for fname in OBSOLETE_BUILTINS: - locals()[fname] = disabled_function(fname) - __all__ = OBSOLETE_BUILTINS -else: - __all__ = [] diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/iterators.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/iterators.py deleted file mode 100644 index dff651e0..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/iterators.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -This module is designed to be used as follows:: - - from future.builtins.iterators import * - -And then, for example:: - - for i in range(10**15): - pass - - for (a, b) in zip(range(10**15), range(-10**15, 0)): - pass - -Note that this is standard Python 3 code, plus some imports that do -nothing on Python 3. - -The iterators this brings in are:: - -- ``range`` -- ``filter`` -- ``map`` -- ``zip`` - -On Python 2, ``range`` is a pure-Python backport of Python 3's ``range`` -iterator with slicing support. The other iterators (``filter``, ``map``, -``zip``) are from the ``itertools`` module on Python 2. On Python 3 these -are available in the module namespace but not exported for * imports via -__all__ (zero no namespace pollution). - -Note that these are also available in the standard library -``future_builtins`` module on Python 2 -- but not Python 3, so using -the standard library version is not portable, nor anywhere near complete. -""" - -from __future__ import division, absolute_import, print_function - -import itertools -from future import utils - -if not utils.PY3: - filter = itertools.ifilter - map = itertools.imap - from future.types import newrange as range - zip = itertools.izip - __all__ = ['filter', 'map', 'range', 'zip'] -else: - import builtins - filter = builtins.filter - map = builtins.map - range = builtins.range - zip = builtins.zip - __all__ = [] diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/misc.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/misc.py deleted file mode 100644 index f86ce5f3..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/misc.py +++ /dev/null @@ -1,135 +0,0 @@ -""" -A module that brings in equivalents of various modified Python 3 builtins -into Py2. Has no effect on Py3. - -The builtin functions are: - -- ``ascii`` (from Py2's future_builtins module) -- ``hex`` (from Py2's future_builtins module) -- ``oct`` (from Py2's future_builtins module) -- ``chr`` (equivalent to ``unichr`` on Py2) -- ``input`` (equivalent to ``raw_input`` on Py2) -- ``next`` (calls ``__next__`` if it exists, else ``next`` method) -- ``open`` (equivalent to io.open on Py2) -- ``super`` (backport of Py3's magic zero-argument super() function -- ``round`` (new "Banker's Rounding" behaviour from Py3) -- ``max`` (new default option from Py3.4) -- ``min`` (new default option from Py3.4) - -``isinstance`` is also currently exported for backwards compatibility -with v0.8.2, although this has been deprecated since v0.9. - - -input() -------- -Like the new ``input()`` function from Python 3 (without eval()), except -that it returns bytes. Equivalent to Python 2's ``raw_input()``. - -Warning: By default, importing this module *removes* the old Python 2 -input() function entirely from ``__builtin__`` for safety. This is -because forgetting to import the new ``input`` from ``future`` might -otherwise lead to a security vulnerability (shell injection) on Python 2. - -To restore it, you can retrieve it yourself from -``__builtin__._old_input``. - -Fortunately, ``input()`` seems to be seldom used in the wild in Python -2... - -""" - -from future import utils - - -if utils.PY2: - from io import open - from future_builtins import ascii, oct, hex - from __builtin__ import unichr as chr, pow as _builtin_pow - import __builtin__ - - # Only for backward compatibility with future v0.8.2: - isinstance = __builtin__.isinstance - - # Warning: Python 2's input() is unsafe and MUST not be able to be used - # accidentally by someone who expects Python 3 semantics but forgets - # to import it on Python 2. Versions of ``future`` prior to 0.11 - # deleted it from __builtin__. Now we keep in __builtin__ but shadow - # the name like all others. Just be sure to import ``input``. - - input = raw_input - - from future.builtins.newnext import newnext as next - from future.builtins.newround import newround as round - from future.builtins.newsuper import newsuper as super - from future.builtins.new_min_max import newmax as max - from future.builtins.new_min_max import newmin as min - from future.types.newint import newint - - _SENTINEL = object() - - def pow(x, y, z=_SENTINEL): - """ - pow(x, y[, z]) -> number - - With two arguments, equivalent to x**y. With three arguments, - equivalent to (x**y) % z, but may be more efficient (e.g. for ints). - """ - # Handle newints - if isinstance(x, newint): - x = long(x) - if isinstance(y, newint): - y = long(y) - if isinstance(z, newint): - z = long(z) - - try: - if z == _SENTINEL: - return _builtin_pow(x, y) - else: - return _builtin_pow(x, y, z) - except ValueError: - if z == _SENTINEL: - return _builtin_pow(x+0j, y) - else: - return _builtin_pow(x+0j, y, z) - - - # ``future`` doesn't support Py3.0/3.1. If we ever did, we'd add this: - # callable = __builtin__.callable - - __all__ = ['ascii', 'chr', 'hex', 'input', 'isinstance', 'next', 'oct', - 'open', 'pow', 'round', 'super', 'max', 'min'] - -else: - import builtins - ascii = builtins.ascii - chr = builtins.chr - hex = builtins.hex - input = builtins.input - next = builtins.next - # Only for backward compatibility with future v0.8.2: - isinstance = builtins.isinstance - oct = builtins.oct - open = builtins.open - pow = builtins.pow - round = builtins.round - super = builtins.super - if utils.PY34_PLUS: - max = builtins.max - min = builtins.min - __all__ = [] - else: - from future.builtins.new_min_max import newmax as max - from future.builtins.new_min_max import newmin as min - __all__ = ['min', 'max'] - - # The callable() function was removed from Py3.0 and 3.1 and - # reintroduced into Py3.2+. ``future`` doesn't support Py3.0/3.1. If we ever - # did, we'd add this: - # try: - # callable = builtins.callable - # except AttributeError: - # # Definition from Pandas - # def callable(obj): - # return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - # __all__.append('callable') diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/new_min_max.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/new_min_max.py deleted file mode 100644 index 6f0c2a86..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/new_min_max.py +++ /dev/null @@ -1,59 +0,0 @@ -import itertools - -from future import utils -if utils.PY2: - from __builtin__ import max as _builtin_max, min as _builtin_min -else: - from builtins import max as _builtin_max, min as _builtin_min - -_SENTINEL = object() - - -def newmin(*args, **kwargs): - return new_min_max(_builtin_min, *args, **kwargs) - - -def newmax(*args, **kwargs): - return new_min_max(_builtin_max, *args, **kwargs) - - -def new_min_max(_builtin_func, *args, **kwargs): - """ - To support the argument "default" introduced in python 3.4 for min and max - :param _builtin_func: builtin min or builtin max - :param args: - :param kwargs: - :return: returns the min or max based on the arguments passed - """ - - for key, _ in kwargs.items(): - if key not in set(['key', 'default']): - raise TypeError('Illegal argument %s', key) - - if len(args) == 0: - raise TypeError - - if len(args) != 1 and kwargs.get('default', _SENTINEL) is not _SENTINEL: - raise TypeError - - if len(args) == 1: - iterator = iter(args[0]) - try: - first = next(iterator) - except StopIteration: - if kwargs.get('default', _SENTINEL) is not _SENTINEL: - return kwargs.get('default') - else: - raise ValueError('{}() arg is an empty sequence'.format(_builtin_func.__name__)) - else: - iterator = itertools.chain([first], iterator) - if kwargs.get('key') is not None: - return _builtin_func(iterator, key=kwargs.get('key')) - else: - return _builtin_func(iterator) - - if len(args) > 1: - if kwargs.get('key') is not None: - return _builtin_func(args, key=kwargs.get('key')) - else: - return _builtin_func(args) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/newnext.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/newnext.py deleted file mode 100644 index 097638ac..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/newnext.py +++ /dev/null @@ -1,70 +0,0 @@ -''' -This module provides a newnext() function in Python 2 that mimics the -behaviour of ``next()`` in Python 3, falling back to Python 2's behaviour for -compatibility if this fails. - -``newnext(iterator)`` calls the iterator's ``__next__()`` method if it exists. If this -doesn't exist, it falls back to calling a ``next()`` method. - -For example: - - >>> class Odds(object): - ... def __init__(self, start=1): - ... self.value = start - 2 - ... def __next__(self): # note the Py3 interface - ... self.value += 2 - ... return self.value - ... def __iter__(self): - ... return self - ... - >>> iterator = Odds() - >>> next(iterator) - 1 - >>> next(iterator) - 3 - -If you are defining your own custom iterator class as above, it is preferable -to explicitly decorate the class with the @implements_iterator decorator from -``future.utils`` as follows: - - >>> @implements_iterator - ... class Odds(object): - ... # etc - ... pass - -This next() function is primarily for consuming iterators defined in Python 3 -code elsewhere that we would like to run on Python 2 or 3. -''' - -_builtin_next = next - -_SENTINEL = object() - -def newnext(iterator, default=_SENTINEL): - """ - next(iterator[, default]) - - Return the next item from the iterator. If default is given and the iterator - is exhausted, it is returned instead of raising StopIteration. - """ - - # args = [] - # if default is not _SENTINEL: - # args.append(default) - try: - try: - return iterator.__next__() - except AttributeError: - try: - return iterator.next() - except AttributeError: - raise TypeError("'{0}' object is not an iterator".format( - iterator.__class__.__name__)) - except StopIteration as e: - if default is _SENTINEL: - raise e - else: - return default - - -__all__ = ['newnext'] diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/newround.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/newround.py deleted file mode 100644 index 394a2c63..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/newround.py +++ /dev/null @@ -1,102 +0,0 @@ -""" -``python-future``: pure Python implementation of Python 3 round(). -""" - -from future.utils import PYPY, PY26, bind_method - -# Use the decimal module for simplicity of implementation (and -# hopefully correctness). -from decimal import Decimal, ROUND_HALF_EVEN - - -def newround(number, ndigits=None): - """ - See Python 3 documentation: uses Banker's Rounding. - - Delegates to the __round__ method if for some reason this exists. - - If not, rounds a number to a given precision in decimal digits (default - 0 digits). This returns an int when called with one argument, - otherwise the same type as the number. ndigits may be negative. - - See the test_round method in future/tests/test_builtins.py for - examples. - """ - return_int = False - if ndigits is None: - return_int = True - ndigits = 0 - if hasattr(number, '__round__'): - return number.__round__(ndigits) - - if ndigits < 0: - raise NotImplementedError('negative ndigits not supported yet') - exponent = Decimal('10') ** (-ndigits) - - if PYPY: - # Work around issue #24: round() breaks on PyPy with NumPy's types - if 'numpy' in repr(type(number)): - number = float(number) - - if isinstance(number, Decimal): - d = number - else: - if not PY26: - d = Decimal.from_float(number).quantize(exponent, - rounding=ROUND_HALF_EVEN) - else: - d = from_float_26(number).quantize(exponent, rounding=ROUND_HALF_EVEN) - - if return_int: - return int(d) - else: - return float(d) - - -### From Python 2.7's decimal.py. Only needed to support Py2.6: - -def from_float_26(f): - """Converts a float to a decimal number, exactly. - - Note that Decimal.from_float(0.1) is not the same as Decimal('0.1'). - Since 0.1 is not exactly representable in binary floating point, the - value is stored as the nearest representable value which is - 0x1.999999999999ap-4. The exact equivalent of the value in decimal - is 0.1000000000000000055511151231257827021181583404541015625. - - >>> Decimal.from_float(0.1) - Decimal('0.1000000000000000055511151231257827021181583404541015625') - >>> Decimal.from_float(float('nan')) - Decimal('NaN') - >>> Decimal.from_float(float('inf')) - Decimal('Infinity') - >>> Decimal.from_float(-float('inf')) - Decimal('-Infinity') - >>> Decimal.from_float(-0.0) - Decimal('-0') - - """ - import math as _math - from decimal import _dec_from_triple # only available on Py2.6 and Py2.7 (not 3.3) - - if isinstance(f, (int, long)): # handle integer inputs - return Decimal(f) - if _math.isinf(f) or _math.isnan(f): # raises TypeError if not a float - return Decimal(repr(f)) - if _math.copysign(1.0, f) == 1.0: - sign = 0 - else: - sign = 1 - n, d = abs(f).as_integer_ratio() - # int.bit_length() method doesn't exist on Py2.6: - def bit_length(d): - if d != 0: - return len(bin(abs(d))) - 2 - else: - return 0 - k = bit_length(d) - 1 - result = _dec_from_triple(sign, str(n*5**k), -k) - return result - - -__all__ = ['newround'] diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/newsuper.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/newsuper.py deleted file mode 100644 index 5d3402bd..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/builtins/newsuper.py +++ /dev/null @@ -1,114 +0,0 @@ -''' -This module provides a newsuper() function in Python 2 that mimics the -behaviour of super() in Python 3. It is designed to be used as follows: - - from __future__ import division, absolute_import, print_function - from future.builtins import super - -And then, for example: - - class VerboseList(list): - def append(self, item): - print('Adding an item') - super().append(item) # new simpler super() function - -Importing this module on Python 3 has no effect. - -This is based on (i.e. almost identical to) Ryan Kelly's magicsuper -module here: - - https://github.com/rfk/magicsuper.git - -Excerpts from Ryan's docstring: - - "Of course, you can still explicitly pass in the arguments if you want - to do something strange. Sometimes you really do want that, e.g. to - skip over some classes in the method resolution order. - - "How does it work? By inspecting the calling frame to determine the - function object being executed and the object on which it's being - called, and then walking the object's __mro__ chain to find out where - that function was defined. Yuck, but it seems to work..." -''' - -from __future__ import absolute_import -import sys -from types import FunctionType - -from future.utils import PY3, PY26 - - -_builtin_super = super - -_SENTINEL = object() - -def newsuper(typ=_SENTINEL, type_or_obj=_SENTINEL, framedepth=1): - '''Like builtin super(), but capable of magic. - - This acts just like the builtin super() function, but if called - without any arguments it attempts to infer them at runtime. - ''' - # Infer the correct call if used without arguments. - if typ is _SENTINEL: - # We'll need to do some frame hacking. - f = sys._getframe(framedepth) - - try: - # Get the function's first positional argument. - type_or_obj = f.f_locals[f.f_code.co_varnames[0]] - except (IndexError, KeyError,): - raise RuntimeError('super() used in a function with no args') - - try: - # Get the MRO so we can crawl it. - mro = type_or_obj.__mro__ - except (AttributeError, RuntimeError): # see issue #160 - try: - mro = type_or_obj.__class__.__mro__ - except AttributeError: - raise RuntimeError('super() used with a non-newstyle class') - - # A ``for...else`` block? Yes! It's odd, but useful. - # If unfamiliar with for...else, see: - # - # http://psung.blogspot.com/2007/12/for-else-in-python.html - for typ in mro: - # Find the class that owns the currently-executing method. - for meth in typ.__dict__.values(): - # Drill down through any wrappers to the underlying func. - # This handles e.g. classmethod() and staticmethod(). - try: - while not isinstance(meth,FunctionType): - if isinstance(meth, property): - # Calling __get__ on the property will invoke - # user code which might throw exceptions or have - # side effects - meth = meth.fget - else: - try: - meth = meth.__func__ - except AttributeError: - meth = meth.__get__(type_or_obj, typ) - except (AttributeError, TypeError): - continue - if meth.func_code is f.f_code: - break # Aha! Found you. - else: - continue # Not found! Move onto the next class in MRO. - break # Found! Break out of the search loop. - else: - raise RuntimeError('super() called outside a method') - - # Dispatch to builtin super(). - if type_or_obj is not _SENTINEL: - return _builtin_super(typ, type_or_obj) - return _builtin_super(typ) - - -def superm(*args, **kwds): - f = sys._getframe(1) - nm = f.f_code.co_name - return getattr(newsuper(framedepth=2),nm)(*args, **kwds) - - -__all__ = ['newsuper'] diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__init__.py deleted file mode 100644 index 0cd60d3d..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# future.moves package -from __future__ import absolute_import -import sys -__future_module__ = True -from future.standard_library import import_top_level_modules - -if sys.version_info[0] >= 3: - import_top_level_modules() diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index bf2d8822042b6567f2c79eaa1fe80f2ad6c85674..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 399 zcmYjN%}N6?5Ki`|meMMo)JG`j=HNx4h=PJ5h)_=pGNjGa#%4E5l3m!dPva~2D2q2w zeFaY@E{Fq}`M%+sFO%@eWJG+##~;t1zJ3MrKO#>q{joEj1QAq`g65P49H}UeM4ZPS zCn_n@Jf#Gq*MT5MM5LS4oMHM#yf&JX>0s*}$dq<_d||b!9fB+>ZQR3?Zyxp-oUTA& zg9-{QYK3+ihpC-z1hA@IZ4khOrMlR*HsAm2_HuyypqFLV@ltRjK*@#SrZvH+8#idI z)FsGrrQeAe`sQ!?UaZ4L{9Aflk42(qxfSdft2L>o3~N|F}(NS?g=D7|{} z6+AiJf~*5GlW&-x`3axTX9$9ve!hG_|LS2sB7kd1e-1?;fi*HX$GCq-Ix-|D7)j!e zfr%bvsEoxZAMa?AAu(Q|WbzGKl;G}=uf5fEkW87%x&R}xd|p7qC)rbS(9IZotwZf3 zW3IoP#{;HyP}(~Jqi)VbZA{BT;UpLN(VxUvQEO0k@&3X)a#N7)%FmV>DHNyo+=*MK z8cCOJ5o}3Uo5EU6A0F>+nBO>FNk`L0mJQWvO{+E(wp^qa)T-E$M8zvR2c_r~6yxmyTD`(Y%pmL$zAaY71l6_8ruNu ZS<0(TJG$!KTw}QIyAMur8pSw)`~_x3W^e!i diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/_thread.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/_thread.cpython-39.pyc deleted file mode 100644 index d086b018005b18c23020d9fac9da52d777455939..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 358 zcmYk2%}T^D5XY0I+g(Z*@g;Xms$!|WGKjF(|h``9n$I~0+&k_Ec2XF;$&magSutF8iFdpBY@+y)Mj3hBf zz(n~esw3fNvpoeV60<$;ztH#zBc}9#@d?arzL?hmf)7*0n}5 zX2#1|G-1-ZQdvWwG|ZVRjN~Fa{2Q*cXcg=X!PI(3W&xx7@GWU2Mak(MH{!;Wour$- zaJr`3ywFhUc&RMOr!uqLuCdex)Q$ WKP)aL6_*$;+woH)9Cie?Wz2~rdk6hR8j#egiEW@{HW*|56_^ppAveoC&M z`~^==npPZ`H~VJh%?!&&qY#0S%g?tnjL=s{{+kEn7TnGNa+G6*3Y=oxy*=d>BqbOL zV$OjR$ZPtOl`%q|VDgrRXG%7!YL(Q4z?x}3(>)Ji9= nD!%;S@50i46)kiFHft|0gd6>UI45JZ$+LGf>)D`-H5=}dN;-DEPHWD0!&FMJCx zeFYySw_fclc;!i}vKi*fIp6utPcl1@xy`AaY>tRIl*98=O`jQ?EaanFU-&s+Xb~G)L zJ!YMk%2@)VA-U7-Ms9Ovov=evO=KqeLNPVmAsy6ci?c47M8s60+uVtENp!+CqIJU%(3=rMF)B z3SOD6L;N5oIp_P~XSxO(C%R}1td}m0PsaQ0kn=vzx$)d27VXNbGF8@errU7W)Dqj+Hq5lF z6j*lj*V-$bw}Y;dyZP_Pblcd%56}!`D(FlYd1KNV=)BpKI;V?dqqU-U_qW&FCPoxs zXk0^HQ9&t-Os&kbxxLU2vjos(oN1I==n0&M<~jvm=M#nv>& zo$2lqD-L3Yd(A1Z&3R@;bi%UMg!SVu>Slci`*P-tt~?5rm&STZ!Qr@{gpLr4%2yVJ zuo2aUw(@AbG1`SxtyL+sDVFGb=x#%*7D`j$LTZPhyAAEJ%yDF`vG2@wY2>ebFL{oc zlKfWMY-m-D{GnNSQ}D@rMceq@{pd!xxs@eaKB%#%xmGh?HhyJ_v%v*-WbC+<^H)iB j_@8N>o0{O?@}O)&a+#Z~(s%{5+odFFlt2VAB#^=nBZF%p diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/pickle.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/pickle.cpython-39.pyc deleted file mode 100644 index 445bc69381edc8417603b8f50a27b187c7805041..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 408 zcmYjL%}T>S5Z=wEKh_lW>H`RsniYByL@Zda2oh*f8jz4?SG#nR4ZE8_d?R1MN6A$@ z_zIq!CMeD_-^@2N-|WQ0;Q)b=@%z&!#E%{MZ+#%w;N}5V(H7ID=A+QJ@#-u1D7|{} z6+AiZf;ccU`OOFOCv33@5g0lCe0qocHNt=M04~AJDFlula8eA Vhxz$V!UcxAX8g|xN1l&E@L%kXUt|CP diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/reprlib.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/reprlib.cpython-39.pyc deleted file mode 100644 index 96369f02ce4d73e76b03fb2995a37b929c67f298..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 356 zcmYk2%}T^D5XY0I`$6dV(H4^?Bx#|K(r@@|(OGW~*Cl;Hk=?VOg47kQBHgf9oacF?!&jBO7M!&J7)Qfty)3X zUFmgAH$|znq^tYe>)aKV8DVMKin^unHZ`3ub-hf_sjIv=HLQ4LFT&AL4pq7Z_!W|x WuAg6w8ZI$xcEgv(I1WM_f&TzGP++nE diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/socketserver.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/socketserver.cpython-39.pyc deleted file mode 100644 index 49a2bbe0e6af5352ef91b1c79b7fa49a433b8f26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 374 zcmYk2!Ait15QdYcyDp`R_!2@zo5O-2h$tv1f-JNb3sSO8v~km>BxzwE$%_x*qx9;@ zSMcPt1=)d_$v4c;{Ddu+0fHc>UoW4~zX#Y458xWooMtx{?u@%k=(+QIu{VOWXTLr>COvZ&|T=+m{0jjb5zSBx@$>=>Z z{MN`$(Dkl#x~54{YOUzQ=Xxxdqqe^aQvvZ}cSMde4(uuQ%6S5Y8ssHf>X@2OpxMSx^v!A_@wMB8B$if`o0dHle%Sus?x5lCR*ScD<8-^_e7vurdPcwoWd>(eLrw}$*TAIJqfGXu!H3<*ynick~#!j}OJB&PVA z2com^c%Y{lrd_CaL{Ek^lzrMq9>@9~a$PZyLpp^1-rSpn?=aPypnA-fMhRzmQpi$i zJMrsa_IO-lfK#ZWg9W7eD{hsurL*-ILSvbArcFRkl;%8LCoO-NMD-v^o}G1?Cy8$U zL8F$&&KAPx08$zQ)C1#|rB*37hVK1}q{^HCZ5^gcO18<2?lzKMGiv{5)RLv^=3o=c z!lXKc+KZ>7rmumIoJgJ(jNCAtUFo9YWVX$%+K_pgD<#P7-SuSxLRNB3;)-u7B8nv` yw>DRsllY97!t$dMwy+g9q}@uSs(}1m9hciWoVEv_BiL(p03x*OhdxFaJVsxk^@;@m diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/sys.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/__pycache__/sys.cpython-39.pyc deleted file mode 100644 index b1d3ab9a1f9c889f97c16fd4b879f63890250a04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 346 zcmYk0!Ait15QdYct#$36;S&_mq~jt69<5Op|G-a_;c1_j&ZyoCCUs}52SirSCt&yhB$llgwtdWYrUhfKz3;7H z%+3cC={DxQm&#cJXd-TO(;j1GFO`=qOh$k|k=+H`$S2SHb3<4sG~ zRbz~z_Yb$%%vF|m!qR*Y?Ld_*X*c@Dw5Rzwb<&HYj#uyeO*p!}fod~AfbDrVPRWHf Tb+5!FhOI6ENrN; z0wy|4Q5B0Yo9rn{k(g}IGWv#Hw8Z@Z-#VikFPSQ9V?C6}`e_9TAE%GYX+LM|rSXlG zjM+gsi$_cvue7rSN`3UEDl2!{;ood&M5AG)pR%#{f$R)M_u)s-QVPZC9k=4fs#el< zS9nv=OHWj)HFLS;wX`&8Wz|xxwzTeiVX9SfPMz}dwC4FM-$_TugJ@}5fKe`~ WyMA^tUU7***bRRg<2VTM9Q+rtNMRoU diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/_dummy_thread.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/_dummy_thread.py deleted file mode 100644 index 688d249b..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/_dummy_thread.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from _dummy_thread import * -else: - __future_module__ = True - from dummy_thread import * diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/_markupbase.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/_markupbase.py deleted file mode 100644 index f9fb4bbf..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/_markupbase.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from _markupbase import * -else: - __future_module__ = True - from markupbase import * diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/_thread.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/_thread.py deleted file mode 100644 index c68018bb..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/_thread.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from _thread import * -else: - __future_module__ = True - from thread import * diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/builtins.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/builtins.py deleted file mode 100644 index e4b6221d..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/builtins.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from builtins import * -else: - __future_module__ = True - from __builtin__ import * - # Overwrite any old definitions with the equivalent future.builtins ones: - from future.builtins import * diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/collections.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/collections.py deleted file mode 100644 index 664ee6a3..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/collections.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import absolute_import -import sys - -from future.utils import PY2, PY26 -__future_module__ = True - -from collections import * - -if PY2: - from UserDict import UserDict - from UserList import UserList - from UserString import UserString - -if PY26: - from future.backports.misc import OrderedDict, Counter - -if sys.version_info < (3, 3): - from future.backports.misc import ChainMap, _count_elements diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/configparser.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/configparser.py deleted file mode 100644 index 33d9cf95..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/configparser.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY2 - -if PY2: - from ConfigParser import * -else: - from configparser import * diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/copyreg.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/copyreg.py deleted file mode 100644 index 9d08cdc5..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/copyreg.py +++ /dev/null @@ -1,12 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - import copyreg, sys - # A "*" import uses Python 3's copyreg.__all__ which does not include - # all public names in the API surface for copyreg, this avoids that - # problem by just making our module _be_ a reference to the actual module. - sys.modules['future.moves.copyreg'] = copyreg -else: - __future_module__ = True - from copy_reg import * diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__init__.py deleted file mode 100644 index 626b406f..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from dbm import * -else: - __future_module__ = True - from whichdb import * - from anydbm import * - -# Py3.3's dbm/__init__.py imports ndbm but doesn't expose it via __all__. -# In case some (badly written) code depends on dbm.ndbm after import dbm, -# we simulate this: -if PY3: - from dbm import ndbm -else: - try: - from future.moves.dbm import ndbm - except ImportError: - ndbm = None diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index fd7905bff5719f2ea7a1386d0096cb7833f71768..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 492 zcmYjO+iKh}5Y<@TUB|oIY#@|;LCJ>JD&(n9LJA2aw1kBiLJUE8S8AM!whl;t z#5f6ViSgcJbxoF&7j!2}=yua}X?0ymuJXdV3R`q_dyE$x#fZKU>-=gt?LyAK)>Z8! z=gz~o$!trbx>CAy6k8odZL;@Im|Ib6)OVBnOl>l;?#8KUQR7DafW_Ydxou<_VY{1N z4m+GXXWb)lQ&bJ``#Z~}P$|1eoj7x&KHyxh37kd8%-G!4^g z&5E|ltT~Q8uu@g>U6F2nq~B!8w#IBQW@yYfS4LHwM@8EOAGdp-;O~+00AU6*&xa5* OfMDdqE6;-k%-|37je%qU diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__pycache__/dumb.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__pycache__/dumb.cpython-39.pyc deleted file mode 100644 index 6285ff94fc2508af100d3bacad78db1416afc26d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 361 zcmYk2T}s3-5XY0I`$6dtdPb9#^D{QNRxtKB#1o$ zCMsT|CgpK4+S7QA_-KW)@i*+E3=c=Faz=Gtlv20GdMJ_Q^BfXBT|Z@$At}pE=Q}IP z(gx)sJz(B>shlODH071fd8e!5_-|Sp-YHlbCZPcXAIMH%cprW?R0}Q{ePEW~S=kG^ z+-<#S=&Iftqv+!C{H})zVJ)A_f=>b5C Z2F<+P4b!W`9oHD7UHI2DN~0Jj;J?1DVUPd- diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__pycache__/gnu.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__pycache__/gnu.cpython-39.pyc deleted file mode 100644 index f6917db7ebb65f8481d4e9c70f6d3714cf7c213e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 356 zcmYk2%}T^D5XY0IyOq*Kd}Z)HzSyF8`3<`$#?v7ytd_kKd80aQ9F)lBc?}7lq)%}$g?awkyWWUA zH}mBz+GEnYMp{GWQosw9RBfLf{S7P4dkH&}UqSN&nE(cN;YUy`p-K+`aEpSF Yq#LJ$%e{sx443WvrI8nTJ`TZu0hPL6iU0rr diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__pycache__/ndbm.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/dbm/__pycache__/ndbm.cpython-39.pyc deleted file mode 100644 index 021be2f50ec6f09323d135f6ef854f7f384d9c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 357 zcmYjN%SyyB6iu4WgVI6#38A9RVn7f?6cii;8JKP?NJ*QFEhbG#(n5d7oxk9xbnD7r zaOLe3#0&S_oWs4GL)dB+ASiP7`SK3)Yb5{619AgxF90~=SR#c}jK{YpJw;N2ks#&- zIFWvW>X7^C?7-#;;ILhK>t8y0gDIt*;6HW#pnYw{LWOJpxeH5 zx~99L)LPQb=Xxxdqqq53r)4NjF>-dt|%8B!a6>scSSUSa_N_RlOi;h9! WrXQA9Q-*5{)^_~U&" to HTML-safe sequences. - If the optional flag quote is true (the default), the quotation mark - characters, both double quote (") and single quote (') characters are also - translated. - """ - s = s.replace("&", "&") # Must be done first! - s = s.replace("<", "<") - s = s.replace(">", ">") - if quote: - s = s.replace('"', """) - s = s.replace('\'', "'") - return s - - __all__ = ['escape'] diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 1369020fd0ea4744b531a36587081ce3ce0904ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 874 zcmYjP&2H2%5Vn)-PeV(~8(2ZwHr29`=mDWtt0IcjiXT;UFRX+tZ|vPfb>h@^R zn7&+--3u9#nX(z-s-8)4$F~>7tt^w5i~Hbi{7^sBf-q^+n$l`k8p*8S_nL3<;HEQR zMRjaWFKgH6+EkN6AeEx4oG%cKxI zXUQqk{6yzVk(^&=R+Y&`nkgldv$tV_tpCCM)ABlYOjN7<_< zU%``;7R7;?$v4c;KZH#tV+29YK3?CUe+Kv;5x@kZ= z^4tdJEZJk)c&VHvVKgbdR;j4G^isI&_)nq@Zxkr|!?3Yi=(t$BEwUpJ?#lOuYQZI= z56tpAD_cS5UFl6l7e#4|qK{Aax4A1UtA(X$E2@?%xuSLFOH<9#OX{Q-=QS%f>`gej hKaXnD0t8$7dstd`{lV4#rfUrTE_`%?lPJbx$RD}}X+i)1 diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/__pycache__/parser.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/__pycache__/parser.cpython-39.pyc deleted file mode 100644 index 992dec8b617d2aca504b041fc0674f4a845850ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 370 zcmYjN+e*YR5KWryg}RIQ8C|q_SP%pe1%(waENovaNXc%Z4JJ)V(n7yT`~^RyuRi$; zJ~`cjI50Ek9A-Gfu=zYf5ajst`5pRKPyR;$(QWT7B- z0a~a=DJrLXG)@s8ZBRV^hFuin?vQPrQMDJj)RnOwB(i>5rqJ=?F`jf$p1;(-wj$4M z@0`sBn$%t@XGt#|{mp2@YXzg(LE*JZDrTLqU9?Ic?$>+#f$RiU_T@)XEx2TK#Vo(J zvJrIM7T%O}vnz~I^!D!NDt9}}Dq(5Th_azdZfVu}!j#M8j5_JXNyT<=>{U2A97eTi efb{-!@K36?o1PD+U0`r`{ex$ChQlDlQSb|yhGj1R diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/entities.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/entities.py deleted file mode 100644 index 56a88609..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/entities.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from html.entities import * -else: - __future_module__ = True - from htmlentitydefs import * diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/parser.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/parser.py deleted file mode 100644 index a6115b59..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/html/parser.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 -__future_module__ = True - -if PY3: - from html.parser import * -else: - from HTMLParser import * diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/__init__.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/__init__.py deleted file mode 100644 index 917b3d71..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from future.utils import PY3 - -if not PY3: - __future_module__ = True diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/moves/http/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index fd5701ecd88a2c2879ce00393a481ce9b58ef611..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 270 zcmYjL!Ait15KWpMM0O8?2Y;cUox`345d}p>kcIVPVHvVbv|-aGBx#|a|&4c$w&gaLBAkM$m5467z;y*GHH#G5#NHN7NYk9>v(>$K4tjd&Fu_FH= zmlb>`mT!yKmqJvt_dfMOmwhrehC@nqf;b(qb*i@-@skBX&L915q1DPrc$7ii1v6+^ zj!kkMtm?)&3s2AYw;1any$(}L>o+-lB9)x&@B7~Kc!n& z{(>uSrW-Hh+

-# This class is available as ServerProxy and Server. New code should -# use ServerProxy, to avoid confusion. -# -# @def ServerProxy(uri, **options) -# @param uri The connection point on the server. -# @keyparam transport A transport factory, compatible with the -# standard transport class. -# @keyparam encoding The default encoding used for 8-bit strings -# (default is UTF-8). -# @keyparam verbose Use a true value to enable debugging output. -# (printed to standard output). -# @see Transport - -class ServerProxy(object): - """uri [,options] -> a logical connection to an XML-RPC server - - uri is the connection point on the server, given as - scheme://host/target. - - The standard implementation always supports the "http" scheme. If - SSL socket support is available (Python 2.0), it also supports - "https". - - If the target part and the slash preceding it are both omitted, - "/RPC2" is assumed. - - The following options can be given as keyword arguments: - - transport: a transport factory - encoding: the request encoding (default is UTF-8) - - All 8-bit strings passed to the server proxy are assumed to use - the given encoding. - """ - - def __init__(self, uri, transport=None, encoding=None, verbose=False, - allow_none=False, use_datetime=False, use_builtin_types=False): - # establish a "logical" server connection - - # get the url - type, uri = urllib_parse.splittype(uri) - if type not in ("http", "https"): - raise IOError("unsupported XML-RPC protocol") - self.__host, self.__handler = urllib_parse.splithost(uri) - if not self.__handler: - self.__handler = "/RPC2" - - if transport is None: - if type == "https": - handler = SafeTransport - else: - handler = Transport - transport = handler(use_datetime=use_datetime, - use_builtin_types=use_builtin_types) - self.__transport = transport - - self.__encoding = encoding or 'utf-8' - self.__verbose = verbose - self.__allow_none = allow_none - - def __close(self): - self.__transport.close() - - def __request(self, methodname, params): - # call a method on the remote server - - request = dumps(params, methodname, encoding=self.__encoding, - allow_none=self.__allow_none).encode(self.__encoding) - - response = self.__transport.request( - self.__host, - self.__handler, - request, - verbose=self.__verbose - ) - - if len(response) == 1: - response = response[0] - - return response - - def __repr__(self): - return ( - "" % - (self.__host, self.__handler) - ) - - __str__ = __repr__ - - def __getattr__(self, name): - # magic method dispatcher - return _Method(self.__request, name) - - # note: to call a remote object with an non-standard name, use - # result getattr(server, "strange-python-name")(args) - - def __call__(self, attr): - """A workaround to get special attributes on the ServerProxy - without interfering with the magic __getattr__ - """ - if attr == "close": - return self.__close - elif attr == "transport": - return self.__transport - raise AttributeError("Attribute %r not found" % (attr,)) - -# compatibility - -Server = ServerProxy - -# -------------------------------------------------------------------- -# test code - -if __name__ == "__main__": - - # simple test program (from the XML-RPC specification) - - # local server, available from Lib/xmlrpc/server.py - server = ServerProxy("http://localhost:8000") - - try: - print(server.currentTime.getCurrentTime()) - except Error as v: - print("ERROR", v) - - multi = MultiCall(server) - multi.getData() - multi.pow(2,9) - multi.add(1,2) - try: - for response in multi(): - print(response) - except Error as v: - print("ERROR", v) diff --git a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/server.py b/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/server.py deleted file mode 100644 index 28072bfe..00000000 --- a/IKEA_scraper/.venv/lib/python3.9/site-packages/future/backports/xmlrpc/server.py +++ /dev/null @@ -1,999 +0,0 @@ -r""" -Ported using Python-Future from the Python 3.3 standard library. - -XML-RPC Servers. - -This module can be used to create simple XML-RPC servers -by creating a server and either installing functions, a -class instance, or by extending the SimpleXMLRPCServer -class. - -It can also be used to handle XML-RPC requests in a CGI -environment using CGIXMLRPCRequestHandler. - -The Doc* classes can be used to create XML-RPC servers that -serve pydoc-style documentation in response to HTTP -GET requests. This documentation is dynamically generated -based on the functions and methods registered with the -server. - -A list of possible usage patterns follows: - -1. Install functions: - -server = SimpleXMLRPCServer(("localhost", 8000)) -server.register_function(pow) -server.register_function(lambda x,y: x+y, 'add') -server.serve_forever() - -2. Install an instance: - -class MyFuncs: - def __init__(self): - # make all of the sys functions available through sys.func_name - import sys - self.sys = sys - def _listMethods(self): - # implement this method so that system.listMethods - # knows to advertise the sys methods - return list_public_methods(self) + \ - ['sys.' + method for method in list_public_methods(self.sys)] - def pow(self, x, y): return pow(x, y) - def add(self, x, y) : return x + y - -server = SimpleXMLRPCServer(("localhost", 8000)) -server.register_introspection_functions() -server.register_instance(MyFuncs()) -server.serve_forever() - -3. Install an instance with custom dispatch method: - -class Math: - def _listMethods(self): - # this method must be present for system.listMethods - # to work - return ['add', 'pow'] - def _methodHelp(self, method): - # this method must be present for system.methodHelp - # to work - if method == 'add': - return "add(2,3) => 5" - elif method == 'pow': - return "pow(x, y[, z]) => number" - else: - # By convention, return empty - # string if no help is available - return "" - def _dispatch(self, method, params): - if method == 'pow': - return pow(*params) - elif method == 'add': - return params[0] + params[1] - else: - raise ValueError('bad method') - -server = SimpleXMLRPCServer(("localhost", 8000)) -server.register_introspection_functions() -server.register_instance(Math()) -server.serve_forever() - -4. Subclass SimpleXMLRPCServer: - -class MathServer(SimpleXMLRPCServer): - def _dispatch(self, method, params): - try: - # We are forcing the 'export_' prefix on methods that are - # callable through XML-RPC to prevent potential security - # problems - func = getattr(self, 'export_' + method) - except AttributeError: - raise Exception('method "%s" is not supported' % method) - else: - return func(*params) - - def export_add(self, x, y): - return x + y - -server = MathServer(("localhost", 8000)) -server.serve_forever() - -5. CGI script: - -server = CGIXMLRPCRequestHandler() -server.register_function(pow) -server.handle_request() -""" - -from __future__ import absolute_import, division, print_function, unicode_literals -from future.builtins import int, str - -# Written by Brian Quinlan (brian@sweetapp.com). -# Based on code written by Fredrik Lundh. - -from future.backports.xmlrpc.client import Fault, dumps, loads, gzip_encode, gzip_decode -from future.backports.http.server import BaseHTTPRequestHandler -import future.backports.http.server as http_server -from future.backports import socketserver -import sys -import os -import re -import pydoc -import inspect -import traceback -try: - import fcntl -except ImportError: - fcntl = None - -def resolve_dotted_attribute(obj, attr, allow_dotted_names=True): - """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d - - Resolves a dotted attribute name to an object. Raises - an AttributeError if any attribute in the chain starts with a '_'. - - If the optional allow_dotted_names argument is false, dots are not - supported and this function operates similar to getattr(obj, attr). - """ - - if allow_dotted_names: - attrs = attr.split('.') - else: - attrs = [attr] - - for i in attrs: - if i.startswith('_'): - raise AttributeError( - 'attempt to access private attribute "%s"' % i - ) - else: - obj = getattr(obj,i) - return obj - -def list_public_methods(obj): - """Returns a list of attribute strings, found in the specified - object, which represent callable attributes""" - - return [member for member in dir(obj) - if not member.startswith('_') and - callable(getattr(obj, member))] - -class SimpleXMLRPCDispatcher(object): - """Mix-in class that dispatches XML-RPC requests. - - This class is used to register XML-RPC method handlers - and then to dispatch them. This class doesn't need to be - instanced directly when used by SimpleXMLRPCServer but it - can be instanced when used by the MultiPathXMLRPCServer - """ - - def __init__(self, allow_none=False, encoding=None, - use_builtin_types=False): - self.funcs = {} - self.instance = None - self.allow_none = allow_none - self.encoding = encoding or 'utf-8' - self.use_builtin_types = use_builtin_types - - def register_instance(self, instance, allow_dotted_names=False): - """Registers an instance to respond to XML-RPC requests. - - Only one instance can be installed at a time. - - If the registered instance has a _dispatch method then that - method will be called with the name of the XML-RPC method and - its parameters as a tuple - e.g. instance._dispatch('add',(2,3)) - - If the registered instance does not have a _dispatch method - then the instance will be searched to find a matching method - and, if found, will be called. Methods beginning with an '_' - are considered private and will not be called by - SimpleXMLRPCServer. - - If a registered function matches a XML-RPC request, then it - will be called instead of the registered instance. - - If the optional allow_dotted_names argument is true and the - instance does not have a _dispatch method, method names - containing dots are supported and resolved, as long as none of - the name segments start with an '_'. - - *** SECURITY WARNING: *** - - Enabling the allow_dotted_names options allows intruders - to access your module's global variables and may allow - intruders to execute arbitrary code on your machine. Only - use this option on a secure, closed network. - - """ - - self.instance = instance - self.allow_dotted_names = allow_dotted_names - - def register_function(self, function, name=None): - """Registers a function to respond to XML-RPC requests. - - The optional name argument can be used to set a Unicode name - for the function. - """ - - if name is None: - name = function.__name__ - self.funcs[name] = function - - def register_introspection_functions(self): - """Registers the XML-RPC introspection methods in the system - namespace. - - see http://xmlrpc.usefulinc.com/doc/reserved.html - """ - - self.funcs.update({'system.listMethods' : self.system_listMethods, - 'system.methodSignature' : self.system_methodSignature, - 'system.methodHelp' : self.system_methodHelp}) - - def register_multicall_functions(self): - """Registers the XML-RPC multicall method in the system - namespace. - - see http://www.xmlrpc.com/discuss/msgReader$1208""" - - self.funcs.update({'system.multicall' : self.system_multicall}) - - def _marshaled_dispatch(self, data, dispatch_method = None, path = None): - """Dispatches an XML-RPC method from marshalled (XML) data. - - XML-RPC methods are dispatched from the marshalled (XML) data - using the _dispatch method and the result is returned as - marshalled data. For backwards compatibility, a dispatch - function can be provided as an argument (see comment in - SimpleXMLRPCRequestHandler.do_POST) but overriding the - existing method through subclassing is the preferred means - of changing method dispatch behavior. - """ - - try: - params, method = loads(data, use_builtin_types=self.use_builtin_types) - - # generate response - if dispatch_method is not None: - response = dispatch_method(method, params) - else: - response = self._dispatch(method, params) - # wrap response in a singleton tuple - response = (response,) - response = dumps(response, methodresponse=1, - allow_none=self.allow_none, encoding=self.encoding) - except Fault as fault: - response = dumps(fault, allow_none=self.allow_none, - encoding=self.encoding) - except: - # report exception back to server - exc_type, exc_value, exc_tb = sys.exc_info() - response = dumps( - Fault(1, "%s:%s" % (exc_type, exc_value)), - encoding=self.encoding, allow_none=self.allow_none, - ) - - return response.encode(self.encoding) - - def system_listMethods(self): - """system.listMethods() => ['add', 'subtract', 'multiple'] - - Returns a list of the methods supported by the server.""" - - methods = set(self.funcs.keys()) - if self.instance is not None: - # Instance can implement _listMethod to return a list of - # methods - if hasattr(self.instance, '_listMethods'): - methods |= set(self.instance._listMethods()) - # if the instance has a _dispatch method then we - # don't have enough information to provide a list - # of methods - elif not hasattr(self.instance, '_dispatch'): - methods |= set(list_public_methods(self.instance)) - return sorted(methods) - - def system_methodSignature(self, method_name): - """system.methodSignature('add') => [double, int, int] - - Returns a list describing the signature of the method. In the - above example, the add method takes two integers as arguments - and returns a double result. - - This server does NOT support system.methodSignature.""" - - # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html - - return 'signatures not supported' - - def system_methodHelp(self, method_name): - """system.methodHelp('add') => "Adds two integers together" - - Returns a string containing documentation for the specified method.""" - - method = None - if method_name in self.funcs: - method = self.funcs[method_name] - elif self.instance is not None: - # Instance can implement _methodHelp to return help for a method - if hasattr(self.instance, '_methodHelp'): - return self.instance._methodHelp(method_name) - # if the instance has a _dispatch method then we - # don't have enough information to provide help - elif not hasattr(self.instance, '_dispatch'): - try: - method = resolve_dotted_attribute( - self.instance, - method_name, - self.allow_dotted_names - ) - except AttributeError: - pass - - # Note that we aren't checking that the method actually - # be a callable object of some kind - if method is None: - return "" - else: - return pydoc.getdoc(method) - - def system_multicall(self, call_list): - """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \ -[[4], ...] - - Allows the caller to package multiple XML-RPC calls into a single - request. - - See http://www.xmlrpc.com/discuss/msgReader$1208 - """ - - results = [] - for call in call_list: - method_name = call['methodName'] - params = call['params'] - - try: - # XXX A marshalling error in any response will fail the entire - # multicall. If someone cares they should fix this. - results.append([self._dispatch(method_name, params)]) - except Fault as fault: - results.append( - {'faultCode' : fault.faultCode, - 'faultString' : fault.faultString} - ) - except: - exc_type, exc_value, exc_tb = sys.exc_info() - results.append( - {'faultCode' : 1, - 'faultString' : "%s:%s" % (exc_type, exc_value)} - ) - return results - - def _dispatch(self, method, params): - """Dispatches the XML-RPC method. - - XML-RPC calls are forwarded to a registered function that - matches the called XML-RPC method name. If no such function - exists then the call is forwarded to the registered instance, - if available. - - If the registered instance has a _dispatch method then that - method will be called with the name of the XML-RPC method and - its parameters as a tuple - e.g. instance._dispatch('add',(2,3)) - - If the registered instance does not have a _dispatch method - then the instance will be searched to find a matching method - and, if found, will be called. - - Methods beginning with an '_' are considered private and will - not be called. - """ - - func = None - try: - # check to see if a matching function has been registered - func = self.funcs[method] - except KeyError: - if self.instance is not None: - # check for a _dispatch method - if hasattr(self.instance, '_dispatch'): - return self.instance._dispatch(method, params) - else: - # call instance method directly - try: - func = resolve_dotted_attribute( - self.instance, - method, - self.allow_dotted_names - ) - except AttributeError: - pass - - if func is not None: - return func(*params) - else: - raise Exception('method "%s" is not supported' % method) - -class SimpleXMLRPCRequestHandler(BaseHTTPRequestHandler): - """Simple XML-RPC request handler class. - - Handles all HTTP POST requests and attempts to decode them as - XML-RPC requests. - """ - - # Class attribute listing the accessible path components; - # paths not on this list will result in a 404 error. - rpc_paths = ('/', '/RPC2') - - #if not None, encode responses larger than this, if possible - encode_threshold = 1400 #a common MTU - - #Override form StreamRequestHandler: full buffering of output - #and no Nagle. - wbufsize = -1 - disable_nagle_algorithm = True - - # a re to match a gzip Accept-Encoding - aepattern = re.compile(r""" - \s* ([^\s;]+) \s* #content-coding - (;\s* q \s*=\s* ([0-9\.]+))? #q - """, re.VERBOSE | re.IGNORECASE) - - def accept_encodings(self): - r = {} - ae = self.headers.get("Accept-Encoding", "") - for e in ae.split(","): - match = self.aepattern.match(e) - if match: - v = match.group(3) - v = float(v) if v else 1.0 - r[match.group(1)] = v - return r - - def is_rpc_path_valid(self): - if self.rpc_paths: - return self.path in self.rpc_paths - else: - # If .rpc_paths is empty, just assume all paths are legal - return True - - def do_POST(self): - """Handles the HTTP POST request. - - Attempts to interpret all HTTP POST requests as XML-RPC calls, - which are forwarded to the server's _dispatch method for handling. - """ - - # Check that the path is legal - if not self.is_rpc_path_valid(): - self.report_404() - return - - try: - # Get arguments by reading body of request. - # We read this in chunks to avoid straining - # socket.read(); around the 10 or 15Mb mark, some platforms - # begin to have problems (bug #792570). - max_chunk_size = 10*1024*1024 - size_remaining = int(self.headers["content-length"]) - L = [] - while size_remaining: - chunk_size = min(size_remaining, max_chunk_size) - chunk = self.rfile.read(chunk_size) - if not chunk: - break - L.append(chunk) - size_remaining -= len(L[-1]) - data = b''.join(L) - - data = self.decode_request_content(data) - if data is None: - return #response has been sent - - # In previous versions of SimpleXMLRPCServer, _dispatch - # could be overridden in this class, instead of in - # SimpleXMLRPCDispatcher. To maintain backwards compatibility, - # check to see if a subclass implements _dispatch and dispatch - # using that method if present. - response = self.server._marshaled_dispatch( - data, getattr(self, '_dispatch', None), self.path - ) - except Exception as e: # This should only happen if the module is buggy - # internal error, report as HTTP server error - self.send_response(500) - - # Send information about the exception if requested - if hasattr(self.server, '_send_traceback_header') and \ - self.server._send_traceback_header: - self.send_header("X-exception", str(e)) - trace = traceback.format_exc() - trace = str(trace.encode('ASCII', 'backslashreplace'), 'ASCII') - self.send_header("X-traceback", trace) - - self.send_header("Content-length", "0") - self.end_headers() - else: - self.send_response(200) - self.send_header("Content-type", "text/xml") - if self.encode_threshold is not None: - if len(response) > self.encode_threshold: - q = self.accept_encodings().get("gzip", 0) - if q: - try: - response = gzip_encode(response) - self.send_header("Content-Encoding", "gzip") - except NotImplementedError: - pass - self.send_header("Content-length", str(len(response))) - self.end_headers() - self.wfile.write(response) - - def decode_request_content(self, data): - #support gzip encoding of request - encoding = self.headers.get("content-encoding", "identity").lower() - if encoding == "identity": - return data - if encoding == "gzip": - try: - return gzip_decode(data) - except NotImplementedError: - self.send_response(501, "encoding %r not supported" % encoding) - except ValueError: - self.send_response(400, "error decoding gzip content") - else: - self.send_response(501, "encoding %r not supported" % encoding) - self.send_header("Content-length", "0") - self.end_headers() - - def report_404 (self): - # Report a 404 error - self.send_response(404) - response = b'No such page' - self.send_header("Content-type", "text/plain") - self.send_header("Content-length", str(len(response))) - self.end_headers() - self.wfile.write(response) - - def log_request(self, code='-', size='-'): - """Selectively log an accepted request.""" - - if self.server.logRequests: - BaseHTTPRequestHandler.log_request(self, code, size) - -class SimpleXMLRPCServer(socketserver.TCPServer, - SimpleXMLRPCDispatcher): - """Simple XML-RPC server. - - Simple XML-RPC server that allows functions and a single instance - to be installed to handle requests. The default implementation - attempts to dispatch XML-RPC calls to the functions or instance - installed in the server. Override the _dispatch method inherited - from SimpleXMLRPCDispatcher to change this behavior. - """ - - allow_reuse_address = True - - # Warning: this is for debugging purposes only! Never set this to True in - # production code, as will be sending out sensitive information (exception - # and stack trace details) when exceptions are raised inside - # SimpleXMLRPCRequestHandler.do_POST - _send_traceback_header = False - - def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, - logRequests=True, allow_none=False, encoding=None, - bind_and_activate=True, use_builtin_types=False): - self.logRequests = logRequests - - SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types) - socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate) - - # [Bug #1222790] If possible, set close-on-exec flag; if a - # method spawns a subprocess, the subprocess shouldn't have - # the listening socket open. - if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'): - flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD) - flags |= fcntl.FD_CLOEXEC - fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags) - -class MultiPathXMLRPCServer(SimpleXMLRPCServer): - """Multipath XML-RPC Server - This specialization of SimpleXMLRPCServer allows the user to create - multiple Dispatcher instances and assign them to different - HTTP request paths. This makes it possible to run two or more - 'virtual XML-RPC servers' at the same port. - Make sure that the requestHandler accepts the paths in question. - """ - def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, - logRequests=True, allow_none=False, encoding=None, - bind_and_activate=True, use_builtin_types=False): - - SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none, - encoding, bind_and_activate, use_builtin_types) - self.dispatchers = {} - self.allow_none = allow_none - self.encoding = encoding or 'utf-8' - - def add_dispatcher(self, path, dispatcher): - self.dispatchers[path] = dispatcher - return dispatcher - - def get_dispatcher(self, path): - return self.dispatchers[path] - - def _marshaled_dispatch(self, data, dispatch_method = None, path = None): - try: - response = self.dispatchers[path]._marshaled_dispatch( - data, dispatch_method, path) - except: - # report low level exception back to server - # (each dispatcher should have handled their own - # exceptions) - exc_type, exc_value = sys.exc_info()[:2] - response = dumps( - Fault(1, "%s:%s" % (exc_type, exc_value)), - encoding=self.encoding, allow_none=self.allow_none) - response = response.encode(self.encoding) - return response - -class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher): - """Simple handler for XML-RPC data passed through CGI.""" - - def __init__(self, allow_none=False, encoding=None, use_builtin_types=False): - SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types) - - def handle_xmlrpc(self, request_text): - """Handle a single XML-RPC request""" - - response = self._marshaled_dispatch(request_text) - - print('Content-Type: text/xml') - print('Content-Length: %d' % len(response)) - print() - sys.stdout.flush() - sys.stdout.buffer.write(response) - sys.stdout.buffer.flush() - - def handle_get(self): - """Handle a single HTTP GET request. - - Default implementation indicates an error because - XML-RPC uses the POST method. - """ - - code = 400 - message, explain = BaseHTTPRequestHandler.responses[code] - - response = http_server.DEFAULT_ERROR_MESSAGE % \ - { - 'code' : code, - 'message' : message, - 'explain' : explain - } - response = response.encode('utf-8') - print('Status: %d %s' % (code, message)) - print('Content-Type: %s' % http_server.DEFAULT_ERROR_CONTENT_TYPE) - print('Content-Length: %d' % len(response)) - print() - sys.stdout.flush() - sys.stdout.buffer.write(response) - sys.stdout.buffer.flush() - - def handle_request(self, request_text=None): - """Handle a single XML-RPC request passed through a CGI post method. - - If no XML data is given then it is read from stdin. The resulting - XML-RPC response is printed to stdout along with the correct HTTP - headers. - """ - - if request_text is None and \ - os.environ.get('REQUEST_METHOD', None) == 'GET': - self.handle_get() - else: - # POST data is normally available through stdin - try: - length = int(os.environ.get('CONTENT_LENGTH', None)) - except (ValueError, TypeError): - length = -1 - if request_text is None: - request_text = sys.stdin.read(length) - - self.handle_xmlrpc(request_text) - - -# ----------------------------------------------------------------------------- -# Self documenting XML-RPC Server. - -class ServerHTMLDoc(pydoc.HTMLDoc): - """Class used to generate pydoc HTML document for a server""" - - def markup(self, text, escape=None, funcs={}, classes={}, methods={}): - """Mark up some plain text, given a context of symbols to look for. - Each context dictionary maps object names to anchor names.""" - escape = escape or self.escape - results = [] - here = 0 - - # XXX Note that this regular expression does not allow for the - # hyperlinking of arbitrary strings being used as method - # names. Only methods with names consisting of word characters - # and '.'s are hyperlinked. - pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|' - r'RFC[- ]?(\d+)|' - r'PEP[- ]?(\d+)|' - r'(self\.)?((?:\w|\.)+))\b') - while 1: - match = pattern.search(text, here) - if not match: break - start, end = match.span() - results.append(escape(text[here:start])) - - all, scheme, rfc, pep, selfdot, name = match.groups() - if scheme: - url = escape(all).replace('"', '"') - results.append('%s' % (url, url)) - elif rfc: - url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc) - results.append('%s' % (url, escape(all))) - elif pep: - url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep) - results.append('%s' % (url, escape(all))) - elif text[end:end+1] == '(': - results.append(self.namelink(name, methods, funcs, classes)) - elif selfdot: - results.append('self.%s' % name) - else: - results.append(self.namelink(name, classes)) - here = end - results.append(escape(text[here:])) - return ''.join(results) - - def docroutine(self, object, name, mod=None, - funcs={}, classes={}, methods={}, cl=None): - """Produce HTML documentation for a function or method object.""" - - anchor = (cl and cl.__name__ or '') + '-' + name - note = '' - - title = '%s' % ( - self.escape(anchor), self.escape(name)) - - if inspect.ismethod(object): - args = inspect.getfullargspec(object) - # exclude the argument bound to the instance, it will be - # confusing to the non-Python user - argspec = inspect.formatargspec ( - args.args[1:], - args.varargs, - args.varkw, - args.defaults, - annotations=args.annotations, - formatvalue=self.formatvalue - ) - elif inspect.isfunction(object): - args = inspect.getfullargspec(object) - argspec = inspect.formatargspec( - args.args, args.varargs, args.varkw, args.defaults, - annotations=args.annotations, - formatvalue=self.formatvalue) - else: - argspec = '(...)' - - if isinstance(object, tuple): - argspec = object[0] or argspec - docstring = object[1] or "" - else: - docstring = pydoc.getdoc(object) - - decl = title + argspec + (note and self.grey( - '%s' % note)) - - doc = self.markup( - docstring, self.preformat, funcs, classes, methods) - doc = doc and '