From b203a417ae6fef516b97f1c9fa398e3468efb9f1 Mon Sep 17 00:00:00 2001 From: Angelos Chatzimparmpas Date: Fri, 19 Jul 2019 13:38:22 +0200 Subject: [PATCH] stable version --- __pycache__/run.cpython-37.pyc | Bin 940 -> 7293 bytes backend/requirements.txt | 3 +- .../INSTALLER | 0 .../Flask_PyMongo-2.3.0.dist-info/LICENSE | 24 + .../Flask_PyMongo-2.3.0.dist-info/METADATA | 43 + .../Flask_PyMongo-2.3.0.dist-info/RECORD | 25 + .../WHEEL | 2 +- .../Flask_PyMongo-2.3.0.dist-info/pbr.json | 1 + .../top_level.txt | 1 + .../Werkzeug-0.12.2.dist-info/DESCRIPTION.rst | 54 - .../Werkzeug-0.12.2.dist-info/LICENSE.txt | 29 - .../Werkzeug-0.12.2.dist-info/METADATA | 83 - .../Werkzeug-0.12.2.dist-info/RECORD | 95 - .../Werkzeug-0.12.2.dist-info/metadata.json | 1 - .../Werkzeug-0.15.4.dist-info/INSTALLER | 1 + .../Werkzeug-0.15.4.dist-info/LICENSE.rst | 28 + .../Werkzeug-0.15.4.dist-info/METADATA | 133 + .../Werkzeug-0.15.4.dist-info/RECORD | 119 + .../Werkzeug-0.15.4.dist-info/WHEEL | 6 + .../top_level.txt | 0 .../python3.7/site-packages/bson/__init__.py | 1170 + .../bson/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 32798 bytes .../bson/__pycache__/binary.cpython-37.pyc | Bin 0 -> 5272 bytes .../bson/__pycache__/code.cpython-37.pyc | Bin 0 -> 2918 bytes .../__pycache__/codec_options.cpython-37.pyc | Bin 0 -> 11935 bytes .../bson/__pycache__/dbref.cpython-37.pyc | Bin 0 -> 4368 bytes .../__pycache__/decimal128.cpython-37.pyc | Bin 0 -> 9684 bytes .../bson/__pycache__/errors.cpython-37.pyc | Bin 0 -> 1316 bytes .../bson/__pycache__/int64.cpython-37.pyc | Bin 0 -> 816 bytes .../bson/__pycache__/json_util.cpython-37.pyc | Bin 0 -> 22881 bytes .../bson/__pycache__/max_key.cpython-37.pyc | Bin 0 -> 1561 bytes .../bson/__pycache__/min_key.cpython-37.pyc | Bin 0 -> 1561 bytes .../bson/__pycache__/objectid.cpython-37.pyc | Bin 0 -> 9116 bytes .../bson/__pycache__/py3compat.cpython-37.pyc | Bin 0 -> 2621 bytes .../bson/__pycache__/raw_bson.cpython-37.pyc | Bin 0 -> 4211 bytes .../bson/__pycache__/regex.cpython-37.pyc | Bin 0 -> 4074 bytes .../bson/__pycache__/son.cpython-37.pyc | Bin 0 -> 6377 bytes .../bson/__pycache__/timestamp.cpython-37.pyc | Bin 0 -> 3959 bytes .../bson/__pycache__/tz_util.cpython-37.pyc | Bin 0 -> 1515 bytes .../bson/_cbson.cpython-37m-darwin.so | Bin 0 -> 55768 bytes .../python3.7/site-packages/bson/binary.py | 242 + .../lib/python3.7/site-packages/bson/code.py | 99 + .../site-packages/bson/codec_options.py | 334 + .../lib/python3.7/site-packages/bson/dbref.py | 135 + .../site-packages/bson/decimal128.py | 335 + .../python3.7/site-packages/bson/errors.py | 40 + .../lib/python3.7/site-packages/bson/int64.py | 34 + .../python3.7/site-packages/bson/json_util.py | 829 + .../python3.7/site-packages/bson/max_key.py | 50 + .../python3.7/site-packages/bson/min_key.py | 50 + .../python3.7/site-packages/bson/objectid.py | 299 + .../python3.7/site-packages/bson/py3compat.py | 107 + .../python3.7/site-packages/bson/raw_bson.py | 124 + .../lib/python3.7/site-packages/bson/regex.py | 128 + .../lib/python3.7/site-packages/bson/son.py | 200 + .../python3.7/site-packages/bson/timestamp.py | 120 + .../python3.7/site-packages/bson/tz_util.py | 52 + .../site-packages/flask_pymongo/__init__.py | 241 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 7363 bytes .../__pycache__/_version.cpython-37.pyc | Bin 0 -> 270 bytes .../__pycache__/wrappers.cpython-37.pyc | Bin 0 -> 3428 bytes .../site-packages/flask_pymongo/_version.py | 5 + .../flask_pymongo/tests/__init__.py | 0 .../tests/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 210 bytes .../__pycache__/test_config.cpython-37.pyc | Bin 0 -> 4544 bytes .../__pycache__/test_gridfs.cpython-37.pyc | Bin 0 -> 4930 bytes .../test_url_converter.cpython-37.pyc | Bin 0 -> 942 bytes .../__pycache__/test_wrappers.cpython-37.pyc | Bin 0 -> 1184 bytes .../tests/__pycache__/util.cpython-37.pyc | Bin 0 -> 2016 bytes .../flask_pymongo/tests/test_config.py | 108 + .../flask_pymongo/tests/test_gridfs.py | 100 + .../flask_pymongo/tests/test_url_converter.py | 17 + .../flask_pymongo/tests/test_wrappers.py | 33 + .../site-packages/flask_pymongo/tests/util.py | 48 + .../site-packages/flask_pymongo/wrappers.py | 117 + .../site-packages/gridfs/__init__.py | 930 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 34102 bytes .../gridfs/__pycache__/errors.cpython-37.pyc | Bin 0 -> 1110 bytes .../__pycache__/grid_file.cpython-37.pyc | Bin 0 -> 26461 bytes .../python3.7/site-packages/gridfs/errors.py | 33 + .../site-packages/gridfs/grid_file.py | 840 + .../pymongo-3.8.0.dist-info/INSTALLER | 1 + .../pymongo-3.8.0.dist-info/METADATA | 243 + .../pymongo-3.8.0.dist-info/RECORD | 145 + .../pymongo-3.8.0.dist-info/WHEEL | 5 + .../pymongo-3.8.0.dist-info/top_level.txt | 3 + .../site-packages/pymongo/__init__.py | 99 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 1579 bytes .../pymongo/__pycache__/auth.cpython-37.pyc | Bin 0 -> 13164 bytes .../pymongo/__pycache__/bulk.cpython-37.pyc | Bin 0 -> 19199 bytes .../__pycache__/change_stream.cpython-37.pyc | Bin 0 -> 10520 bytes .../__pycache__/client_options.cpython-37.pyc | Bin 0 -> 7951 bytes .../__pycache__/client_session.cpython-37.pyc | Bin 0 -> 20005 bytes .../__pycache__/collation.cpython-37.pyc | Bin 0 -> 6501 bytes .../__pycache__/collection.cpython-37.pyc | Bin 0 -> 117932 bytes .../__pycache__/command_cursor.cpython-37.pyc | Bin 0 -> 9927 bytes .../pymongo/__pycache__/common.cpython-37.pyc | Bin 0 -> 19152 bytes .../compression_support.cpython-37.pyc | Bin 0 -> 3349 bytes .../pymongo/__pycache__/cursor.cpython-37.pyc | Bin 0 -> 37047 bytes .../__pycache__/cursor_manager.cpython-37.pyc | Bin 0 -> 1954 bytes .../__pycache__/database.cpython-37.pyc | Bin 0 -> 54085 bytes .../__pycache__/driver_info.cpython-37.pyc | Bin 0 -> 1380 bytes .../pymongo/__pycache__/errors.cpython-37.pyc | Bin 0 -> 10105 bytes .../__pycache__/helpers.cpython-37.pyc | Bin 0 -> 6138 bytes .../__pycache__/ismaster.cpython-37.pyc | Bin 0 -> 5102 bytes .../max_staleness_selectors.cpython-37.pyc | Bin 0 -> 2466 bytes .../__pycache__/message.cpython-37.pyc | Bin 0 -> 36820 bytes .../__pycache__/mongo_client.cpython-37.pyc | Bin 0 -> 72783 bytes .../mongo_replica_set_client.cpython-37.pyc | Bin 0 -> 1876 bytes .../__pycache__/monitor.cpython-37.pyc | Bin 0 -> 4764 bytes .../__pycache__/monitoring.cpython-37.pyc | Bin 0 -> 34278 bytes .../__pycache__/monotonic.cpython-37.pyc | Bin 0 -> 509 bytes .../__pycache__/network.cpython-37.pyc | Bin 0 -> 7363 bytes .../__pycache__/operations.cpython-37.pyc | Bin 0 -> 13893 bytes .../periodic_executor.cpython-37.pyc | Bin 0 -> 3719 bytes .../pymongo/__pycache__/pool.cpython-37.pyc | Bin 0 -> 29191 bytes .../__pycache__/read_concern.cpython-37.pyc | Bin 0 -> 2303 bytes .../read_preferences.cpython-37.pyc | Bin 0 -> 16361 bytes .../__pycache__/response.cpython-37.pyc | Bin 0 -> 3330 bytes .../__pycache__/results.cpython-37.pyc | Bin 0 -> 8754 bytes .../__pycache__/saslprep.cpython-37.pyc | Bin 0 -> 2444 bytes .../pymongo/__pycache__/server.cpython-37.pyc | Bin 0 -> 4643 bytes .../server_description.cpython-37.pyc | Bin 0 -> 6261 bytes .../server_selectors.cpython-37.pyc | Bin 0 -> 5714 bytes .../__pycache__/server_type.cpython-37.pyc | Bin 0 -> 450 bytes .../__pycache__/settings.cpython-37.pyc | Bin 0 -> 4187 bytes .../son_manipulator.cpython-37.pyc | Bin 0 -> 7510 bytes .../__pycache__/ssl_context.cpython-37.pyc | Bin 0 -> 3381 bytes .../ssl_match_hostname.cpython-37.pyc | Bin 0 -> 3019 bytes .../__pycache__/ssl_support.cpython-37.pyc | Bin 0 -> 3818 bytes .../__pycache__/thread_util.cpython-37.pyc | Bin 0 -> 4192 bytes .../__pycache__/topology.cpython-37.pyc | Bin 0 -> 17465 bytes .../topology_description.cpython-37.pyc | Bin 0 -> 13513 bytes .../__pycache__/uri_parser.cpython-37.pyc | Bin 0 -> 11207 bytes .../__pycache__/write_concern.cpython-37.pyc | Bin 0 -> 4584 bytes .../pymongo/_cmessage.cpython-37m-darwin.so | Bin 0 -> 31272 bytes .../python3.7/site-packages/pymongo/auth.py | 570 + .../python3.7/site-packages/pymongo/bulk.py | 702 + .../site-packages/pymongo/change_stream.py | 334 + .../site-packages/pymongo/client_options.py | 237 + .../site-packages/pymongo/client_session.py | 655 + .../site-packages/pymongo/collation.py | 225 + .../site-packages/pymongo/collection.py | 3356 +++ .../site-packages/pymongo/command_cursor.py | 345 + .../python3.7/site-packages/pymongo/common.py | 733 + .../pymongo/compression_support.py | 124 + .../python3.7/site-packages/pymongo/cursor.py | 1327 + .../site-packages/pymongo/cursor_manager.py | 65 + .../site-packages/pymongo/database.py | 1465 + .../site-packages/pymongo/driver_info.py | 39 + .../python3.7/site-packages/pymongo/errors.py | 249 + .../site-packages/pymongo/helpers.py | 272 + .../site-packages/pymongo/ismaster.py | 158 + .../pymongo/max_staleness_selectors.py | 116 + .../site-packages/pymongo/message.py | 1555 ++ .../site-packages/pymongo/mongo_client.py | 1950 ++ .../pymongo/mongo_replica_set_client.py | 48 + .../site-packages/pymongo/monitor.py | 184 + .../site-packages/pymongo/monitoring.py | 931 + .../site-packages/pymongo/monotonic.py | 38 + .../site-packages/pymongo/network.py | 305 + .../site-packages/pymongo/operations.py | 370 + .../pymongo/periodic_executor.py | 174 + .../python3.7/site-packages/pymongo/pool.py | 1113 + .../site-packages/pymongo/read_concern.py | 76 + .../site-packages/pymongo/read_preferences.py | 471 + .../site-packages/pymongo/response.py | 101 + .../site-packages/pymongo/results.py | 226 + .../site-packages/pymongo/saslprep.py | 108 + .../python3.7/site-packages/pymongo/server.py | 169 + .../pymongo/server_description.py | 202 + .../site-packages/pymongo/server_selectors.py | 156 + .../site-packages/pymongo/server_type.py | 23 + .../site-packages/pymongo/settings.py | 123 + .../site-packages/pymongo/son_manipulator.py | 191 + .../site-packages/pymongo/ssl_context.py | 96 + .../pymongo/ssl_match_hostname.py | 135 + .../site-packages/pymongo/ssl_support.py | 191 + .../site-packages/pymongo/thread_util.py | 131 + .../site-packages/pymongo/topology.py | 633 + .../pymongo/topology_description.py | 545 + .../site-packages/pymongo/uri_parser.py | 429 + .../site-packages/pymongo/write_concern.py | 126 + .../site-packages/werkzeug/__init__.py | 250 +- .../__pycache__/__init__.cpython-37.pyc | Bin 4725 -> 4787 bytes .../__pycache__/_compat.cpython-37.pyc | Bin 7157 -> 7199 bytes .../__pycache__/_internal.cpython-37.pyc | Bin 12539 -> 13284 bytes .../__pycache__/_reloader.cpython-37.pyc | Bin 8682 -> 9800 bytes .../__pycache__/datastructures.cpython-37.pyc | Bin 99066 -> 100837 bytes .../__pycache__/exceptions.cpython-37.pyc | Bin 22970 -> 25812 bytes .../__pycache__/filesystem.cpython-37.pyc | Bin 2263 -> 2181 bytes .../__pycache__/formparser.cpython-37.pyc | Bin 15939 -> 16128 bytes .../werkzeug/__pycache__/http.cpython-37.pyc | Bin 30820 -> 36004 bytes .../werkzeug/__pycache__/local.cpython-37.pyc | Bin 18697 -> 18622 bytes .../__pycache__/posixemulation.cpython-37.pyc | Bin 2822 -> 2753 bytes .../__pycache__/routing.cpython-37.pyc | Bin 59841 -> 64437 bytes .../__pycache__/script.cpython-37.pyc | Bin 10438 -> 0 bytes .../__pycache__/security.cpython-37.pyc | Bin 8527 -> 7766 bytes .../__pycache__/serving.cpython-37.pyc | Bin 24496 -> 28580 bytes .../werkzeug/__pycache__/test.cpython-37.pyc | Bin 29637 -> 33590 bytes .../__pycache__/testapp.cpython-37.pyc | Bin 9488 -> 9369 bytes .../werkzeug/__pycache__/urls.cpython-37.pyc | Bin 33149 -> 35805 bytes .../__pycache__/useragents.cpython-37.pyc | Bin 5259 -> 5992 bytes .../werkzeug/__pycache__/utils.cpython-37.pyc | Bin 21128 -> 25256 bytes .../__pycache__/wrappers.cpython-37.pyc | Bin 75013 -> 0 bytes .../werkzeug/__pycache__/wsgi.cpython-37.pyc | Bin 38796 -> 33356 bytes .../site-packages/werkzeug/_compat.py | 77 +- .../site-packages/werkzeug/_internal.py | 254 +- .../site-packages/werkzeug/_reloader.py | 201 +- .../werkzeug/contrib/__init__.py | 4 +- .../__pycache__/__init__.cpython-37.pyc | Bin 809 -> 739 bytes .../contrib/__pycache__/atom.cpython-37.pyc | Bin 14113 -> 14199 bytes .../contrib/__pycache__/cache.cpython-37.pyc | Bin 31000 -> 32599 bytes .../contrib/__pycache__/fixers.cpython-37.pyc | Bin 10101 -> 9540 bytes .../contrib/__pycache__/iterio.cpython-37.pyc | Bin 10994 -> 11106 bytes .../__pycache__/jsrouting.cpython-37.pyc | Bin 8317 -> 0 bytes .../__pycache__/limiter.cpython-37.pyc | Bin 1765 -> 0 bytes .../contrib/__pycache__/lint.cpython-37.pyc | Bin 11885 -> 466 bytes .../__pycache__/profiler.cpython-37.pyc | Bin 5459 -> 1583 bytes .../__pycache__/securecookie.cpython-37.pyc | Bin 10337 -> 10807 bytes .../__pycache__/sessions.cpython-37.pyc | Bin 12933 -> 13187 bytes .../__pycache__/testtools.cpython-37.pyc | Bin 2675 -> 0 bytes .../__pycache__/wrappers.cpython-37.pyc | Bin 10440 -> 12424 bytes .../site-packages/werkzeug/contrib/atom.py | 239 +- .../site-packages/werkzeug/contrib/cache.py | 265 +- .../site-packages/werkzeug/contrib/fixers.py | 244 +- .../site-packages/werkzeug/contrib/iterio.py | 98 +- .../werkzeug/contrib/jsrouting.py | 264 - .../site-packages/werkzeug/contrib/limiter.py | 41 - .../site-packages/werkzeug/contrib/lint.py | 350 +- .../werkzeug/contrib/profiler.py | 153 +- .../werkzeug/contrib/securecookie.py | 133 +- .../werkzeug/contrib/sessions.py | 147 +- .../werkzeug/contrib/testtools.py | 73 - .../werkzeug/contrib/wrappers.py | 233 +- .../site-packages/werkzeug/datastructures.py | 868 +- .../site-packages/werkzeug/debug/__init__.py | 330 +- .../debug/__pycache__/__init__.cpython-37.pyc | Bin 12618 -> 13199 bytes .../debug/__pycache__/console.cpython-37.pyc | Bin 7383 -> 7305 bytes .../debug/__pycache__/repr.cpython-37.pyc | Bin 8677 -> 8617 bytes .../debug/__pycache__/tbtools.cpython-37.pyc | Bin 15631 -> 18460 bytes .../site-packages/werkzeug/debug/console.py | 65 +- .../site-packages/werkzeug/debug/repr.py | 179 +- .../werkzeug/debug/shared/debugger.js | 7 +- .../werkzeug/debug/shared/jquery.js | 7 +- .../werkzeug/debug/shared/style.css | 11 + .../site-packages/werkzeug/debug/tbtools.py | 489 +- .../site-packages/werkzeug/exceptions.py | 373 +- .../site-packages/werkzeug/filesystem.py | 34 +- .../site-packages/werkzeug/formparser.py | 304 +- .../python3.7/site-packages/werkzeug/http.py | 683 +- .../python3.7/site-packages/werkzeug/local.py | 59 +- .../werkzeug/middleware/__init__.py | 25 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 762 bytes .../__pycache__/dispatcher.cpython-37.pyc | Bin 0 -> 2451 bytes .../__pycache__/http_proxy.cpython-37.pyc | Bin 0 -> 6366 bytes .../__pycache__/lint.cpython-37.pyc | Bin 0 -> 11674 bytes .../__pycache__/profiler.cpython-37.pyc | Bin 0 -> 4628 bytes .../__pycache__/proxy_fix.cpython-37.pyc | Bin 0 -> 7679 bytes .../__pycache__/shared_data.cpython-37.pyc | Bin 0 -> 8338 bytes .../werkzeug/middleware/dispatcher.py | 66 + .../werkzeug/middleware/http_proxy.py | 219 + .../site-packages/werkzeug/middleware/lint.py | 408 + .../werkzeug/middleware/profiler.py | 132 + .../werkzeug/middleware/proxy_fix.py | 228 + .../werkzeug/middleware/shared_data.py | 260 + .../site-packages/werkzeug/posixemulation.py | 45 +- .../site-packages/werkzeug/routing.py | 896 +- .../site-packages/werkzeug/script.py | 332 - .../site-packages/werkzeug/security.py | 171 +- .../site-packages/werkzeug/serving.py | 695 +- .../python3.7/site-packages/werkzeug/test.py | 693 +- .../site-packages/werkzeug/testapp.py | 117 +- .../python3.7/site-packages/werkzeug/urls.py | 648 +- .../site-packages/werkzeug/useragents.py | 158 +- .../python3.7/site-packages/werkzeug/utils.py | 514 +- .../site-packages/werkzeug/wrappers.py | 1970 -- .../werkzeug/wrappers/__init__.py | 36 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 1690 bytes .../__pycache__/accept.cpython-37.pyc | Bin 0 -> 2219 bytes .../wrappers/__pycache__/auth.cpython-37.pyc | Bin 0 -> 1706 bytes .../__pycache__/base_request.cpython-37.pyc | Bin 0 -> 22675 bytes .../__pycache__/base_response.cpython-37.pyc | Bin 0 -> 23323 bytes .../common_descriptors.cpython-37.pyc | Bin 0 -> 12205 bytes .../wrappers/__pycache__/etag.cpython-37.pyc | Bin 0 -> 11731 bytes .../wrappers/__pycache__/json.cpython-37.pyc | Bin 0 -> 4321 bytes .../__pycache__/request.cpython-37.pyc | Bin 0 -> 1825 bytes .../__pycache__/response.cpython-37.pyc | Bin 0 -> 3336 bytes .../__pycache__/user_agent.cpython-37.pyc | Bin 0 -> 853 bytes .../site-packages/werkzeug/wrappers/accept.py | 50 + .../site-packages/werkzeug/wrappers/auth.py | 33 + .../werkzeug/wrappers/base_request.py | 693 + .../werkzeug/wrappers/base_response.py | 702 + .../werkzeug/wrappers/common_descriptors.py | 322 + .../site-packages/werkzeug/wrappers/etag.py | 304 + .../site-packages/werkzeug/wrappers/json.py | 145 + .../werkzeug/wrappers/request.py | 44 + .../werkzeug/wrappers/response.py | 78 + .../werkzeug/wrappers/user_agent.py | 15 + .../python3.7/site-packages/werkzeug/wsgi.py | 608 +- backend/venv/venv/.Python | 1 + backend/venv/venv/bin/activate | 78 + backend/venv/venv/bin/activate.csh | 55 + backend/venv/venv/bin/activate.fish | 101 + backend/venv/venv/bin/activate.ps1 | 72 + backend/venv/venv/bin/activate.xsh | 46 + backend/venv/venv/bin/activate_this.py | 46 + backend/venv/venv/bin/easy_install | 10 + backend/venv/venv/bin/easy_install-3.7 | 10 + backend/venv/venv/bin/pip | 10 + backend/venv/venv/bin/pip3 | 10 + backend/venv/venv/bin/pip3.7 | 10 + backend/venv/venv/bin/python | 1 + backend/venv/venv/bin/python-config | 78 + backend/venv/venv/bin/python3 | Bin 0 -> 8632 bytes backend/venv/venv/bin/python3.7 | 1 + backend/venv/venv/bin/wheel | 10 + backend/venv/venv/include/python3.7m | 1 + backend/venv/venv/lib/python3.7/LICENSE.txt | 1 + backend/venv/venv/lib/python3.7/__future__.py | 1 + .../__pycache__/__future__.cpython-37.pyc | Bin 0 -> 4185 bytes .../__pycache__/_bootlocale.cpython-37.pyc | Bin 0 -> 1309 bytes .../_collections_abc.cpython-37.pyc | Bin 0 -> 29002 bytes .../__pycache__/_weakrefset.cpython-37.pyc | Bin 0 -> 7515 bytes .../python3.7/__pycache__/abc.cpython-37.pyc | Bin 0 -> 6511 bytes .../__pycache__/base64.cpython-37.pyc | Bin 0 -> 17112 bytes .../__pycache__/bisect.cpython-37.pyc | Bin 0 -> 2751 bytes .../__pycache__/codecs.cpython-37.pyc | Bin 0 -> 33958 bytes .../python3.7/__pycache__/copy.cpython-37.pyc | Bin 0 -> 7154 bytes .../__pycache__/copyreg.cpython-37.pyc | Bin 0 -> 4297 bytes .../python3.7/__pycache__/enum.cpython-37.pyc | Bin 0 -> 23800 bytes .../__pycache__/fnmatch.cpython-37.pyc | Bin 0 -> 3390 bytes .../__pycache__/functools.cpython-37.pyc | Bin 0 -> 23867 bytes .../__pycache__/genericpath.cpython-37.pyc | Bin 0 -> 3808 bytes .../__pycache__/hashlib.cpython-37.pyc | Bin 0 -> 6588 bytes .../__pycache__/heapq.cpython-37.pyc | Bin 0 -> 14415 bytes .../python3.7/__pycache__/hmac.cpython-37.pyc | Bin 0 -> 6166 bytes .../python3.7/__pycache__/io.cpython-37.pyc | Bin 0 -> 3469 bytes .../__pycache__/keyword.cpython-37.pyc | Bin 0 -> 1862 bytes .../__pycache__/linecache.cpython-37.pyc | Bin 0 -> 3842 bytes .../__pycache__/locale.cpython-37.pyc | Bin 0 -> 34608 bytes .../__pycache__/ntpath.cpython-37.pyc | Bin 0 -> 12972 bytes .../__pycache__/operator.cpython-37.pyc | Bin 0 -> 13953 bytes .../python3.7/__pycache__/os.cpython-37.pyc | Bin 0 -> 29753 bytes .../__pycache__/posixpath.cpython-37.pyc | Bin 0 -> 10456 bytes .../__pycache__/random.cpython-37.pyc | Bin 0 -> 19409 bytes .../python3.7/__pycache__/re.cpython-37.pyc | Bin 0 -> 13857 bytes .../__pycache__/reprlib.cpython-37.pyc | Bin 0 -> 5403 bytes .../__pycache__/shutil.cpython-37.pyc | Bin 0 -> 30598 bytes .../python3.7/__pycache__/site.cpython-37.pyc | Bin 0 -> 21834 bytes .../__pycache__/sre_compile.cpython-37.pyc | Bin 0 -> 15256 bytes .../__pycache__/sre_constants.cpython-37.pyc | Bin 0 -> 6344 bytes .../__pycache__/sre_parse.cpython-37.pyc | Bin 0 -> 21410 bytes .../python3.7/__pycache__/stat.cpython-37.pyc | Bin 0 -> 3933 bytes .../__pycache__/struct.cpython-37.pyc | Bin 0 -> 387 bytes .../__pycache__/tarfile.cpython-37.pyc | Bin 0 -> 61838 bytes .../__pycache__/tempfile.cpython-37.pyc | Bin 0 -> 22198 bytes .../__pycache__/token.cpython-37.pyc | Bin 0 -> 3652 bytes .../__pycache__/tokenize.cpython-37.pyc | Bin 0 -> 17783 bytes .../__pycache__/types.cpython-37.pyc | Bin 0 -> 9027 bytes .../__pycache__/warnings.cpython-37.pyc | Bin 0 -> 13819 bytes .../__pycache__/weakref.cpython-37.pyc | Bin 0 -> 19163 bytes .../venv/venv/lib/python3.7/_bootlocale.py | 1 + .../venv/lib/python3.7/_collections_abc.py | 1 + .../venv/venv/lib/python3.7/_dummy_thread.py | 1 + .../venv/venv/lib/python3.7/_weakrefset.py | 1 + backend/venv/venv/lib/python3.7/abc.py | 1 + backend/venv/venv/lib/python3.7/base64.py | 1 + backend/venv/venv/lib/python3.7/bisect.py | 1 + backend/venv/venv/lib/python3.7/codecs.py | 1 + backend/venv/venv/lib/python3.7/collections | 1 + .../venv/lib/python3.7/config-3.7m-darwin | 1 + backend/venv/venv/lib/python3.7/copy.py | 1 + backend/venv/venv/lib/python3.7/copyreg.py | 1 + .../venv/lib/python3.7/distutils/__init__.py | 134 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 3201 bytes .../lib/python3.7/distutils/distutils.cfg | 6 + backend/venv/venv/lib/python3.7/encodings | 1 + backend/venv/venv/lib/python3.7/enum.py | 1 + backend/venv/venv/lib/python3.7/fnmatch.py | 1 + backend/venv/venv/lib/python3.7/functools.py | 1 + .../venv/venv/lib/python3.7/genericpath.py | 1 + backend/venv/venv/lib/python3.7/hashlib.py | 1 + backend/venv/venv/lib/python3.7/heapq.py | 1 + backend/venv/venv/lib/python3.7/hmac.py | 1 + backend/venv/venv/lib/python3.7/imp.py | 1 + backend/venv/venv/lib/python3.7/importlib | 1 + backend/venv/venv/lib/python3.7/io.py | 1 + backend/venv/venv/lib/python3.7/keyword.py | 1 + backend/venv/venv/lib/python3.7/lib-dynload | 1 + backend/venv/venv/lib/python3.7/linecache.py | 1 + backend/venv/venv/lib/python3.7/locale.py | 1 + .../lib/python3.7/no-global-site-packages.txt | 0 backend/venv/venv/lib/python3.7/ntpath.py | 1 + backend/venv/venv/lib/python3.7/operator.py | 1 + .../venv/venv/lib/python3.7/orig-prefix.txt | 1 + backend/venv/venv/lib/python3.7/os.py | 1 + backend/venv/venv/lib/python3.7/posixpath.py | 1 + backend/venv/venv/lib/python3.7/random.py | 1 + backend/venv/venv/lib/python3.7/re.py | 1 + backend/venv/venv/lib/python3.7/readline.so | 1 + backend/venv/venv/lib/python3.7/reprlib.py | 1 + .../venv/venv/lib/python3.7/rlcompleter.py | 1 + backend/venv/venv/lib/python3.7/shutil.py | 1 + .../__pycache__/easy_install.cpython-37.pyc | Bin 0 -> 333 bytes .../python3.7/site-packages/easy_install.py | 5 + .../pip-19.1.1.dist-info/INSTALLER | 1 + .../pip-19.1.1.dist-info/LICENSE.txt | 20 + .../pip-19.1.1.dist-info/METADATA | 75 + .../site-packages/pip-19.1.1.dist-info/RECORD | 618 + .../site-packages/pip-19.1.1.dist-info/WHEEL | 6 + .../pip-19.1.1.dist-info/entry_points.txt | 5 + .../pip-19.1.1.dist-info/top_level.txt | 1 + .../python3.7/site-packages/pip/__init__.py | 1 + .../python3.7/site-packages/pip/__main__.py | 19 + .../pip/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 206 bytes .../pip/__pycache__/__main__.cpython-37.pyc | Bin 0 -> 458 bytes .../site-packages/pip/_internal/__init__.py | 78 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 1850 bytes .../__pycache__/build_env.cpython-37.pyc | Bin 0 -> 7447 bytes .../__pycache__/cache.cpython-37.pyc | Bin 0 -> 7058 bytes .../__pycache__/configuration.cpython-37.pyc | Bin 0 -> 9752 bytes .../__pycache__/download.cpython-37.pyc | Bin 0 -> 20920 bytes .../__pycache__/exceptions.cpython-37.pyc | Bin 0 -> 11750 bytes .../__pycache__/index.cpython-37.pyc | Bin 0 -> 28780 bytes .../__pycache__/locations.cpython-37.pyc | Bin 0 -> 4439 bytes .../__pycache__/pep425tags.cpython-37.pyc | Bin 0 -> 8169 bytes .../__pycache__/pyproject.cpython-37.pyc | Bin 0 -> 3186 bytes .../__pycache__/resolve.cpython-37.pyc | Bin 0 -> 9104 bytes .../__pycache__/wheel.cpython-37.pyc | Bin 0 -> 26080 bytes .../site-packages/pip/_internal/build_env.py | 215 + .../site-packages/pip/_internal/cache.py | 224 + .../pip/_internal/cli/__init__.py | 4 + .../cli/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 281 bytes .../__pycache__/autocompletion.cpython-37.pyc | Bin 0 -> 5094 bytes .../__pycache__/base_command.cpython-37.pyc | Bin 0 -> 7801 bytes .../cli/__pycache__/cmdoptions.cpython-37.pyc | Bin 0 -> 16865 bytes .../__pycache__/main_parser.cpython-37.pyc | Bin 0 -> 2367 bytes .../cli/__pycache__/parser.cpython-37.pyc | Bin 0 -> 8945 bytes .../__pycache__/status_codes.cpython-37.pyc | Bin 0 -> 410 bytes .../pip/_internal/cli/autocompletion.py | 152 + .../pip/_internal/cli/base_command.py | 340 + .../pip/_internal/cli/cmdoptions.py | 809 + .../pip/_internal/cli/main_parser.py | 104 + .../site-packages/pip/_internal/cli/parser.py | 261 + .../pip/_internal/cli/status_codes.py | 8 + .../pip/_internal/commands/__init__.py | 79 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 2509 bytes .../commands/__pycache__/check.cpython-37.pyc | Bin 0 -> 1334 bytes .../__pycache__/completion.cpython-37.pyc | Bin 0 -> 3083 bytes .../__pycache__/configuration.cpython-37.pyc | Bin 0 -> 6911 bytes .../__pycache__/download.cpython-37.pyc | Bin 0 -> 4722 bytes .../__pycache__/freeze.cpython-37.pyc | Bin 0 -> 2879 bytes .../commands/__pycache__/hash.cpython-37.pyc | Bin 0 -> 2073 bytes .../commands/__pycache__/help.cpython-37.pyc | Bin 0 -> 1249 bytes .../__pycache__/install.cpython-37.pyc | Bin 0 -> 12896 bytes .../commands/__pycache__/list.cpython-37.pyc | Bin 0 -> 8740 bytes .../__pycache__/search.cpython-37.pyc | Bin 0 -> 4315 bytes .../commands/__pycache__/show.cpython-37.pyc | Bin 0 -> 5896 bytes .../__pycache__/uninstall.cpython-37.pyc | Bin 0 -> 2705 bytes .../commands/__pycache__/wheel.cpython-37.pyc | Bin 0 -> 5008 bytes .../pip/_internal/commands/check.py | 41 + .../pip/_internal/commands/completion.py | 94 + .../pip/_internal/commands/configuration.py | 253 + .../pip/_internal/commands/download.py | 176 + .../pip/_internal/commands/freeze.py | 96 + .../pip/_internal/commands/hash.py | 57 + .../pip/_internal/commands/help.py | 37 + .../pip/_internal/commands/install.py | 587 + .../pip/_internal/commands/list.py | 302 + .../pip/_internal/commands/search.py | 135 + .../pip/_internal/commands/show.py | 168 + .../pip/_internal/commands/uninstall.py | 78 + .../pip/_internal/commands/wheel.py | 186 + .../pip/_internal/configuration.py | 384 + .../site-packages/pip/_internal/download.py | 979 + .../site-packages/pip/_internal/exceptions.py | 274 + .../site-packages/pip/_internal/index.py | 1108 + .../site-packages/pip/_internal/locations.py | 211 + .../pip/_internal/models/__init__.py | 2 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 269 bytes .../__pycache__/candidate.cpython-37.pyc | Bin 0 -> 1302 bytes .../__pycache__/format_control.cpython-37.pyc | Bin 0 -> 2272 bytes .../models/__pycache__/index.cpython-37.pyc | Bin 0 -> 1173 bytes .../models/__pycache__/link.cpython-37.pyc | Bin 0 -> 4992 bytes .../pip/_internal/models/candidate.py | 31 + .../pip/_internal/models/format_control.py | 73 + .../pip/_internal/models/index.py | 31 + .../pip/_internal/models/link.py | 163 + .../pip/_internal/operations/__init__.py | 0 .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 205 bytes .../__pycache__/check.cpython-37.pyc | Bin 0 -> 3635 bytes .../__pycache__/freeze.cpython-37.pyc | Bin 0 -> 5620 bytes .../__pycache__/prepare.cpython-37.pyc | Bin 0 -> 10549 bytes .../pip/_internal/operations/check.py | 155 + .../pip/_internal/operations/freeze.py | 247 + .../pip/_internal/operations/prepare.py | 426 + .../site-packages/pip/_internal/pep425tags.py | 381 + .../site-packages/pip/_internal/pyproject.py | 171 + .../pip/_internal/req/__init__.py | 78 + .../req/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 1716 bytes .../__pycache__/constructors.cpython-37.pyc | Bin 0 -> 7619 bytes .../req/__pycache__/req_file.cpython-37.pyc | Bin 0 -> 9232 bytes .../__pycache__/req_install.cpython-37.pyc | Bin 0 -> 24994 bytes .../req/__pycache__/req_set.cpython-37.pyc | Bin 0 -> 5677 bytes .../__pycache__/req_tracker.cpython-37.pyc | Bin 0 -> 3228 bytes .../__pycache__/req_uninstall.cpython-37.pyc | Bin 0 -> 17291 bytes .../pip/_internal/req/constructors.py | 340 + .../pip/_internal/req/req_file.py | 383 + .../pip/_internal/req/req_install.py | 1028 + .../pip/_internal/req/req_set.py | 193 + .../pip/_internal/req/req_tracker.py | 96 + .../pip/_internal/req/req_uninstall.py | 633 + .../site-packages/pip/_internal/resolve.py | 393 + .../pip/_internal/utils/__init__.py | 0 .../utils/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 200 bytes .../utils/__pycache__/appdirs.cpython-37.pyc | Bin 0 -> 8031 bytes .../utils/__pycache__/compat.cpython-37.pyc | Bin 0 -> 6148 bytes .../__pycache__/deprecation.cpython-37.pyc | Bin 0 -> 2609 bytes .../utils/__pycache__/encoding.cpython-37.pyc | Bin 0 -> 1284 bytes .../__pycache__/filesystem.cpython-37.pyc | Bin 0 -> 669 bytes .../utils/__pycache__/glibc.cpython-37.pyc | Bin 0 -> 1688 bytes .../utils/__pycache__/hashes.cpython-37.pyc | Bin 0 -> 3607 bytes .../utils/__pycache__/logging.cpython-37.pyc | Bin 0 -> 9031 bytes .../utils/__pycache__/misc.cpython-37.pyc | Bin 0 -> 25027 bytes .../utils/__pycache__/models.cpython-37.pyc | Bin 0 -> 1949 bytes .../utils/__pycache__/outdated.cpython-37.pyc | Bin 0 -> 3950 bytes .../__pycache__/packaging.cpython-37.pyc | Bin 0 -> 2621 bytes .../setuptools_build.cpython-37.pyc | Bin 0 -> 395 bytes .../utils/__pycache__/temp_dir.cpython-37.pyc | Bin 0 -> 4922 bytes .../utils/__pycache__/typing.cpython-37.pyc | Bin 0 -> 1330 bytes .../utils/__pycache__/ui.cpython-37.pyc | Bin 0 -> 11760 bytes .../pip/_internal/utils/appdirs.py | 268 + .../pip/_internal/utils/compat.py | 264 + .../pip/_internal/utils/deprecation.py | 93 + .../pip/_internal/utils/encoding.py | 39 + .../pip/_internal/utils/filesystem.py | 30 + .../pip/_internal/utils/glibc.py | 93 + .../pip/_internal/utils/hashes.py | 115 + .../pip/_internal/utils/logging.py | 371 + .../site-packages/pip/_internal/utils/misc.py | 1011 + .../pip/_internal/utils/models.py | 40 + .../pip/_internal/utils/outdated.py | 162 + .../pip/_internal/utils/packaging.py | 85 + .../pip/_internal/utils/setuptools_build.py | 8 + .../pip/_internal/utils/temp_dir.py | 155 + .../pip/_internal/utils/typing.py | 29 + .../site-packages/pip/_internal/utils/ui.py | 424 + .../pip/_internal/vcs/__init__.py | 604 + .../vcs/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 17040 bytes .../vcs/__pycache__/bazaar.cpython-37.pyc | Bin 0 -> 3441 bytes .../vcs/__pycache__/git.cpython-37.pyc | Bin 0 -> 9085 bytes .../vcs/__pycache__/mercurial.cpython-37.pyc | Bin 0 -> 3703 bytes .../vcs/__pycache__/subversion.cpython-37.pyc | Bin 0 -> 6732 bytes .../site-packages/pip/_internal/vcs/bazaar.py | 103 + .../site-packages/pip/_internal/vcs/git.py | 362 + .../pip/_internal/vcs/mercurial.py | 105 + .../pip/_internal/vcs/subversion.py | 234 + .../site-packages/pip/_internal/wheel.py | 1098 + .../site-packages/pip/_vendor/__init__.py | 109 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 2867 bytes .../__pycache__/appdirs.cpython-37.pyc | Bin 0 -> 20617 bytes .../_vendor/__pycache__/distro.cpython-37.pyc | Bin 0 -> 36330 bytes .../__pycache__/ipaddress.cpython-37.pyc | Bin 0 -> 66460 bytes .../__pycache__/pyparsing.cpython-37.pyc | Bin 0 -> 221818 bytes .../__pycache__/retrying.cpython-37.pyc | Bin 0 -> 8098 bytes .../_vendor/__pycache__/six.cpython-37.pyc | Bin 0 -> 26422 bytes .../site-packages/pip/_vendor/appdirs.py | 604 + .../pip/_vendor/cachecontrol/__init__.py | 11 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 558 bytes .../__pycache__/_cmd.cpython-37.pyc | Bin 0 -> 1561 bytes .../__pycache__/adapter.cpython-37.pyc | Bin 0 -> 3044 bytes .../__pycache__/cache.cpython-37.pyc | Bin 0 -> 1774 bytes .../__pycache__/compat.cpython-37.pyc | Bin 0 -> 765 bytes .../__pycache__/controller.cpython-37.pyc | Bin 0 -> 7644 bytes .../__pycache__/filewrapper.cpython-37.pyc | Bin 0 -> 2162 bytes .../__pycache__/heuristics.cpython-37.pyc | Bin 0 -> 4682 bytes .../__pycache__/serialize.cpython-37.pyc | Bin 0 -> 4246 bytes .../__pycache__/wrapper.cpython-37.pyc | Bin 0 -> 666 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-37.pyc | Bin 0 -> 302 bytes .../__pycache__/file_cache.cpython-37.pyc | Bin 0 -> 3236 bytes .../__pycache__/redis_cache.cpython-37.pyc | Bin 0 -> 1558 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 | 367 + .../pip/_vendor/cachecontrol/filewrapper.py | 80 + .../pip/_vendor/cachecontrol/heuristics.py | 135 + .../pip/_vendor/cachecontrol/serialize.py | 186 + .../pip/_vendor/cachecontrol/wrapper.py | 29 + .../pip/_vendor/certifi/__init__.py | 3 + .../pip/_vendor/certifi/__main__.py | 2 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 265 bytes .../__pycache__/__main__.cpython-37.pyc | Bin 0 -> 268 bytes .../certifi/__pycache__/core.cpython-37.pyc | Bin 0 -> 477 bytes .../pip/_vendor/certifi/cacert.pem | 4658 ++++ .../site-packages/pip/_vendor/certifi/core.py | 15 + .../pip/_vendor/chardet/__init__.py | 39 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 852 bytes .../__pycache__/big5freq.cpython-37.pyc | Bin 0 -> 27187 bytes .../__pycache__/big5prober.cpython-37.pyc | Bin 0 -> 1128 bytes .../chardistribution.cpython-37.pyc | Bin 0 -> 6314 bytes .../charsetgroupprober.cpython-37.pyc | Bin 0 -> 2235 bytes .../__pycache__/charsetprober.cpython-37.pyc | Bin 0 -> 3445 bytes .../codingstatemachine.cpython-37.pyc | Bin 0 -> 2892 bytes .../chardet/__pycache__/compat.cpython-37.pyc | Bin 0 -> 363 bytes .../__pycache__/cp949prober.cpython-37.pyc | Bin 0 -> 1135 bytes .../chardet/__pycache__/enums.cpython-37.pyc | Bin 0 -> 2626 bytes .../__pycache__/escprober.cpython-37.pyc | Bin 0 -> 2613 bytes .../chardet/__pycache__/escsm.cpython-37.pyc | Bin 0 -> 7074 bytes .../__pycache__/eucjpprober.cpython-37.pyc | Bin 0 -> 2421 bytes .../__pycache__/euckrfreq.cpython-37.pyc | Bin 0 -> 12071 bytes .../__pycache__/euckrprober.cpython-37.pyc | Bin 0 -> 1136 bytes .../__pycache__/euctwfreq.cpython-37.pyc | Bin 0 -> 27191 bytes .../__pycache__/euctwprober.cpython-37.pyc | Bin 0 -> 1136 bytes .../__pycache__/gb2312freq.cpython-37.pyc | Bin 0 -> 19115 bytes .../__pycache__/gb2312prober.cpython-37.pyc | Bin 0 -> 1144 bytes .../__pycache__/hebrewprober.cpython-37.pyc | Bin 0 -> 2978 bytes .../__pycache__/jisfreq.cpython-37.pyc | Bin 0 -> 22143 bytes .../chardet/__pycache__/jpcntx.cpython-37.pyc | Bin 0 -> 38022 bytes .../langbulgarianmodel.cpython-37.pyc | Bin 0 -> 23636 bytes .../langcyrillicmodel.cpython-37.pyc | Bin 0 -> 29092 bytes .../__pycache__/langgreekmodel.cpython-37.pyc | Bin 0 -> 23594 bytes .../langhebrewmodel.cpython-37.pyc | Bin 0 -> 22223 bytes .../langhungarianmodel.cpython-37.pyc | Bin 0 -> 23625 bytes .../__pycache__/langthaimodel.cpython-37.pyc | Bin 0 -> 22202 bytes .../langturkishmodel.cpython-37.pyc | Bin 0 -> 22225 bytes .../__pycache__/latin1prober.cpython-37.pyc | Bin 0 -> 2935 bytes .../mbcharsetprober.cpython-37.pyc | Bin 0 -> 2240 bytes .../mbcsgroupprober.cpython-37.pyc | Bin 0 -> 1131 bytes .../chardet/__pycache__/mbcssm.cpython-37.pyc | Bin 0 -> 15686 bytes .../sbcharsetprober.cpython-37.pyc | Bin 0 -> 2993 bytes .../sbcsgroupprober.cpython-37.pyc | Bin 0 -> 1621 bytes .../__pycache__/sjisprober.cpython-37.pyc | Bin 0 -> 2447 bytes .../universaldetector.cpython-37.pyc | Bin 0 -> 5837 bytes .../__pycache__/utf8prober.cpython-37.pyc | Bin 0 -> 1978 bytes .../__pycache__/version.cpython-37.pyc | Bin 0 -> 447 bytes .../pip/_vendor/chardet/big5freq.py | 386 + .../pip/_vendor/chardet/big5prober.py | 47 + .../pip/_vendor/chardet/chardistribution.py | 233 + .../pip/_vendor/chardet/charsetgroupprober.py | 106 + .../pip/_vendor/chardet/charsetprober.py | 145 + .../pip/_vendor/chardet/cli/__init__.py | 1 + .../cli/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 204 bytes .../cli/__pycache__/chardetect.cpython-37.pyc | Bin 0 -> 2693 bytes .../pip/_vendor/chardet/cli/chardetect.py | 85 + .../pip/_vendor/chardet/codingstatemachine.py | 88 + .../pip/_vendor/chardet/compat.py | 34 + .../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 | 228 + .../pip/_vendor/chardet/langcyrillicmodel.py | 333 + .../pip/_vendor/chardet/langgreekmodel.py | 225 + .../pip/_vendor/chardet/langhebrewmodel.py | 200 + .../pip/_vendor/chardet/langhungarianmodel.py | 225 + .../pip/_vendor/chardet/langthaimodel.py | 199 + .../pip/_vendor/chardet/langturkishmodel.py | 193 + .../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/sbcharsetprober.py | 132 + .../pip/_vendor/chardet/sbcsgroupprober.py | 73 + .../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-37.pyc | Bin 0 -> 452 bytes .../colorama/__pycache__/ansi.cpython-37.pyc | Bin 0 -> 3350 bytes .../__pycache__/ansitowin32.cpython-37.pyc | Bin 0 -> 7606 bytes .../__pycache__/initialise.cpython-37.pyc | Bin 0 -> 1671 bytes .../colorama/__pycache__/win32.cpython-37.pyc | Bin 0 -> 3865 bytes .../__pycache__/winterm.cpython-37.pyc | Bin 0 -> 4613 bytes .../pip/_vendor/colorama/ansi.py | 102 + .../pip/_vendor/colorama/ansitowin32.py | 257 + .../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-37.pyc | Bin 0 -> 1050 bytes .../distlib/__pycache__/compat.cpython-37.pyc | Bin 0 -> 32060 bytes .../__pycache__/database.cpython-37.pyc | Bin 0 -> 42581 bytes .../distlib/__pycache__/index.cpython-37.pyc | Bin 0 -> 17346 bytes .../__pycache__/locators.cpython-37.pyc | Bin 0 -> 38732 bytes .../__pycache__/manifest.cpython-37.pyc | Bin 0 -> 10298 bytes .../__pycache__/markers.cpython-37.pyc | Bin 0 -> 4484 bytes .../__pycache__/metadata.cpython-37.pyc | Bin 0 -> 27694 bytes .../__pycache__/resources.cpython-37.pyc | Bin 0 -> 10894 bytes .../__pycache__/scripts.cpython-37.pyc | Bin 0 -> 11084 bytes .../distlib/__pycache__/util.cpython-37.pyc | Bin 0 -> 47962 bytes .../__pycache__/version.cpython-37.pyc | Bin 0 -> 20434 bytes .../distlib/__pycache__/wheel.cpython-37.pyc | Bin 0 -> 25370 bytes .../pip/_vendor/distlib/_backport/__init__.py | 6 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 492 bytes .../_backport/__pycache__/misc.cpython-37.pyc | Bin 0 -> 1089 bytes .../__pycache__/shutil.cpython-37.pyc | Bin 0 -> 21405 bytes .../__pycache__/sysconfig.cpython-37.pyc | Bin 0 -> 15870 bytes .../__pycache__/tarfile.cpython-37.pyc | Bin 0 -> 62735 bytes .../pip/_vendor/distlib/_backport/misc.py | 41 + .../pip/_vendor/distlib/_backport/shutil.py | 761 + .../_vendor/distlib/_backport/sysconfig.cfg | 84 + .../_vendor/distlib/_backport/sysconfig.py | 788 + .../pip/_vendor/distlib/_backport/tarfile.py | 2607 ++ .../pip/_vendor/distlib/compat.py | 1120 + .../pip/_vendor/distlib/database.py | 1339 + .../pip/_vendor/distlib/index.py | 516 + .../pip/_vendor/distlib/locators.py | 1295 + .../pip/_vendor/distlib/manifest.py | 393 + .../pip/_vendor/distlib/markers.py | 131 + .../pip/_vendor/distlib/metadata.py | 1094 + .../pip/_vendor/distlib/resources.py | 355 + .../pip/_vendor/distlib/scripts.py | 417 + .../site-packages/pip/_vendor/distlib/t32.exe | Bin 0 -> 92672 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 0 -> 102400 bytes .../site-packages/pip/_vendor/distlib/util.py | 1756 ++ .../pip/_vendor/distlib/version.py | 736 + .../site-packages/pip/_vendor/distlib/w32.exe | Bin 0 -> 89088 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 0 -> 99328 bytes .../pip/_vendor/distlib/wheel.py | 988 + .../site-packages/pip/_vendor/distro.py | 1216 + .../pip/_vendor/html5lib/__init__.py | 35 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 1321 bytes .../__pycache__/_ihatexml.cpython-37.pyc | Bin 0 -> 13768 bytes .../__pycache__/_inputstream.cpython-37.pyc | Bin 0 -> 22659 bytes .../__pycache__/_tokenizer.cpython-37.pyc | Bin 0 -> 41560 bytes .../__pycache__/_utils.cpython-37.pyc | Bin 0 -> 3313 bytes .../__pycache__/constants.cpython-37.pyc | Bin 0 -> 66225 bytes .../__pycache__/html5parser.cpython-37.pyc | Bin 0 -> 97822 bytes .../__pycache__/serializer.cpython-37.pyc | Bin 0 -> 10838 bytes .../pip/_vendor/html5lib/_ihatexml.py | 288 + .../pip/_vendor/html5lib/_inputstream.py | 923 + .../pip/_vendor/html5lib/_tokenizer.py | 1721 ++ .../pip/_vendor/html5lib/_trie/__init__.py | 14 + .../_trie/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 434 bytes .../_trie/__pycache__/_base.cpython-37.pyc | Bin 0 -> 1517 bytes .../_trie/__pycache__/datrie.cpython-37.pyc | Bin 0 -> 2036 bytes .../_trie/__pycache__/py.cpython-37.pyc | Bin 0 -> 2239 bytes .../pip/_vendor/html5lib/_trie/_base.py | 37 + .../pip/_vendor/html5lib/_trie/datrie.py | 44 + .../pip/_vendor/html5lib/_trie/py.py | 67 + .../pip/_vendor/html5lib/_utils.py | 124 + .../pip/_vendor/html5lib/constants.py | 2947 ++ .../pip/_vendor/html5lib/filters/__init__.py | 0 .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 209 bytes .../alphabeticalattributes.cpython-37.pyc | Bin 0 -> 1325 bytes .../filters/__pycache__/base.cpython-37.pyc | Bin 0 -> 859 bytes .../inject_meta_charset.cpython-37.pyc | Bin 0 -> 1879 bytes .../filters/__pycache__/lint.cpython-37.pyc | Bin 0 -> 2643 bytes .../__pycache__/optionaltags.cpython-37.pyc | Bin 0 -> 2770 bytes .../__pycache__/sanitizer.cpython-37.pyc | Bin 0 -> 16445 bytes .../__pycache__/whitespace.cpython-37.pyc | Bin 0 -> 1363 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 | 896 + .../_vendor/html5lib/filters/whitespace.py | 38 + .../pip/_vendor/html5lib/html5parser.py | 2791 ++ .../pip/_vendor/html5lib/serializer.py | 409 + .../_vendor/html5lib/treeadapters/__init__.py | 30 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 948 bytes .../__pycache__/genshi.cpython-37.pyc | Bin 0 -> 1545 bytes .../__pycache__/sax.cpython-37.pyc | Bin 0 -> 1495 bytes .../_vendor/html5lib/treeadapters/genshi.py | 54 + .../pip/_vendor/html5lib/treeadapters/sax.py | 50 + .../_vendor/html5lib/treebuilders/__init__.py | 88 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 3329 bytes .../__pycache__/base.cpython-37.pyc | Bin 0 -> 11252 bytes .../__pycache__/dom.cpython-37.pyc | Bin 0 -> 9282 bytes .../__pycache__/etree.cpython-37.pyc | Bin 0 -> 11861 bytes .../__pycache__/etree_lxml.cpython-37.pyc | Bin 0 -> 11801 bytes .../pip/_vendor/html5lib/treebuilders/base.py | 417 + .../pip/_vendor/html5lib/treebuilders/dom.py | 236 + .../_vendor/html5lib/treebuilders/etree.py | 340 + .../html5lib/treebuilders/etree_lxml.py | 366 + .../_vendor/html5lib/treewalkers/__init__.py | 154 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 4006 bytes .../__pycache__/base.cpython-37.pyc | Bin 0 -> 7002 bytes .../__pycache__/dom.cpython-37.pyc | Bin 0 -> 1731 bytes .../__pycache__/etree.cpython-37.pyc | Bin 0 -> 3538 bytes .../__pycache__/etree_lxml.cpython-37.pyc | Bin 0 -> 6647 bytes .../__pycache__/genshi.cpython-37.pyc | Bin 0 -> 1905 bytes .../pip/_vendor/html5lib/treewalkers/base.py | 252 + .../pip/_vendor/html5lib/treewalkers/dom.py | 43 + .../pip/_vendor/html5lib/treewalkers/etree.py | 130 + .../html5lib/treewalkers/etree_lxml.py | 213 + .../_vendor/html5lib/treewalkers/genshi.py | 69 + .../pip/_vendor/idna/__init__.py | 2 + .../idna/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 266 bytes .../idna/__pycache__/codec.cpython-37.pyc | Bin 0 -> 3073 bytes .../idna/__pycache__/compat.cpython-37.pyc | Bin 0 -> 626 bytes .../idna/__pycache__/core.cpython-37.pyc | Bin 0 -> 9069 bytes .../idna/__pycache__/idnadata.cpython-37.pyc | Bin 0 -> 21440 bytes .../idna/__pycache__/intranges.cpython-37.pyc | Bin 0 -> 1806 bytes .../__pycache__/package_data.cpython-37.pyc | Bin 0 -> 220 bytes .../idna/__pycache__/uts46data.cpython-37.pyc | Bin 0 -> 176100 bytes .../site-packages/pip/_vendor/idna/codec.py | 118 + .../site-packages/pip/_vendor/idna/compat.py | 12 + .../site-packages/pip/_vendor/idna/core.py | 396 + .../pip/_vendor/idna/idnadata.py | 1979 ++ .../pip/_vendor/idna/intranges.py | 53 + .../pip/_vendor/idna/package_data.py | 2 + .../pip/_vendor/idna/uts46data.py | 8205 ++++++ .../site-packages/pip/_vendor/ipaddress.py | 2419 ++ .../pip/_vendor/lockfile/__init__.py | 347 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 9916 bytes .../__pycache__/linklockfile.cpython-37.pyc | Bin 0 -> 2297 bytes .../__pycache__/mkdirlockfile.cpython-37.pyc | Bin 0 -> 2659 bytes .../__pycache__/pidlockfile.cpython-37.pyc | Bin 0 -> 4859 bytes .../__pycache__/sqlitelockfile.cpython-37.pyc | Bin 0 -> 3758 bytes .../symlinklockfile.cpython-37.pyc | Bin 0 -> 2182 bytes .../pip/_vendor/lockfile/linklockfile.py | 73 + .../pip/_vendor/lockfile/mkdirlockfile.py | 84 + .../pip/_vendor/lockfile/pidlockfile.py | 190 + .../pip/_vendor/lockfile/sqlitelockfile.py | 156 + .../pip/_vendor/lockfile/symlinklockfile.py | 70 + .../pip/_vendor/msgpack/__init__.py | 66 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 2078 bytes .../__pycache__/_version.cpython-37.pyc | Bin 0 -> 227 bytes .../__pycache__/exceptions.cpython-37.pyc | Bin 0 -> 2183 bytes .../__pycache__/fallback.cpython-37.pyc | Bin 0 -> 24555 bytes .../pip/_vendor/msgpack/_version.py | 1 + .../pip/_vendor/msgpack/exceptions.py | 41 + .../pip/_vendor/msgpack/fallback.py | 977 + .../pip/_vendor/packaging/__about__.py | 27 + .../pip/_vendor/packaging/__init__.py | 26 + .../__pycache__/__about__.cpython-37.pyc | Bin 0 -> 740 bytes .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 578 bytes .../__pycache__/_compat.cpython-37.pyc | Bin 0 -> 1018 bytes .../__pycache__/_structures.cpython-37.pyc | Bin 0 -> 2870 bytes .../__pycache__/markers.cpython-37.pyc | Bin 0 -> 8873 bytes .../__pycache__/requirements.cpython-37.pyc | Bin 0 -> 3991 bytes .../__pycache__/specifiers.cpython-37.pyc | Bin 0 -> 19768 bytes .../__pycache__/utils.cpython-37.pyc | Bin 0 -> 1456 bytes .../__pycache__/version.cpython-37.pyc | Bin 0 -> 11960 bytes .../pip/_vendor/packaging/_compat.py | 31 + .../pip/_vendor/packaging/_structures.py | 68 + .../pip/_vendor/packaging/markers.py | 296 + .../pip/_vendor/packaging/requirements.py | 138 + .../pip/_vendor/packaging/specifiers.py | 749 + .../pip/_vendor/packaging/utils.py | 57 + .../pip/_vendor/packaging/version.py | 420 + .../pip/_vendor/pep517/__init__.py | 4 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 291 bytes .../__pycache__/_in_process.cpython-37.pyc | Bin 0 -> 5634 bytes .../pep517/__pycache__/build.cpython-37.pyc | Bin 0 -> 2769 bytes .../pep517/__pycache__/check.cpython-37.pyc | Bin 0 -> 4817 bytes .../__pycache__/colorlog.cpython-37.pyc | Bin 0 -> 2923 bytes .../pep517/__pycache__/compat.cpython-37.pyc | Bin 0 -> 1027 bytes .../__pycache__/envbuild.cpython-37.pyc | Bin 0 -> 4211 bytes .../__pycache__/wrappers.cpython-37.pyc | Bin 0 -> 5492 bytes .../pip/_vendor/pep517/_in_process.py | 207 + .../site-packages/pip/_vendor/pep517/build.py | 108 + .../site-packages/pip/_vendor/pep517/check.py | 202 + .../pip/_vendor/pep517/colorlog.py | 115 + .../pip/_vendor/pep517/compat.py | 23 + .../pip/_vendor/pep517/envbuild.py | 158 + .../pip/_vendor/pep517/wrappers.py | 163 + .../pip/_vendor/pkg_resources/__init__.py | 3286 +++ .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 99587 bytes .../__pycache__/py31compat.cpython-37.pyc | Bin 0 -> 651 bytes .../pip/_vendor/pkg_resources/py31compat.py | 23 + .../pip/_vendor/progress/__init__.py | 177 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 5567 bytes .../progress/__pycache__/bar.cpython-37.pyc | Bin 0 -> 2659 bytes .../__pycache__/counter.cpython-37.pyc | Bin 0 -> 1471 bytes .../__pycache__/spinner.cpython-37.pyc | Bin 0 -> 1436 bytes .../site-packages/pip/_vendor/progress/bar.py | 91 + .../pip/_vendor/progress/counter.py | 41 + .../pip/_vendor/progress/spinner.py | 43 + .../site-packages/pip/_vendor/pyparsing.py | 6493 +++++ .../pip/_vendor/pytoml/__init__.py | 4 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 385 bytes .../pytoml/__pycache__/core.cpython-37.pyc | Bin 0 -> 948 bytes .../pytoml/__pycache__/parser.cpython-37.pyc | Bin 0 -> 10065 bytes .../pytoml/__pycache__/test.cpython-37.pyc | Bin 0 -> 1248 bytes .../pytoml/__pycache__/utils.cpython-37.pyc | Bin 0 -> 2147 bytes .../pytoml/__pycache__/writer.cpython-37.pyc | Bin 0 -> 3582 bytes .../site-packages/pip/_vendor/pytoml/core.py | 13 + .../pip/_vendor/pytoml/parser.py | 341 + .../site-packages/pip/_vendor/pytoml/test.py | 30 + .../site-packages/pip/_vendor/pytoml/utils.py | 67 + .../pip/_vendor/pytoml/writer.py | 106 + .../pip/_vendor/requests/__init__.py | 133 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 3496 bytes .../__pycache__/__version__.cpython-37.pyc | Bin 0 -> 559 bytes .../_internal_utils.cpython-37.pyc | Bin 0 -> 1317 bytes .../__pycache__/adapters.cpython-37.pyc | Bin 0 -> 16894 bytes .../requests/__pycache__/api.cpython-37.pyc | Bin 0 -> 6500 bytes .../requests/__pycache__/auth.cpython-37.pyc | Bin 0 -> 8351 bytes .../requests/__pycache__/certs.cpython-37.pyc | Bin 0 -> 642 bytes .../__pycache__/compat.cpython-37.pyc | Bin 0 -> 1621 bytes .../__pycache__/cookies.cpython-37.pyc | Bin 0 -> 18796 bytes .../__pycache__/exceptions.cpython-37.pyc | Bin 0 -> 5514 bytes .../requests/__pycache__/help.cpython-37.pyc | Bin 0 -> 2695 bytes .../requests/__pycache__/hooks.cpython-37.pyc | Bin 0 -> 989 bytes .../__pycache__/models.cpython-37.pyc | Bin 0 -> 24117 bytes .../__pycache__/packages.cpython-37.pyc | Bin 0 -> 519 bytes .../__pycache__/sessions.cpython-37.pyc | Bin 0 -> 19437 bytes .../__pycache__/status_codes.cpython-37.pyc | Bin 0 -> 4175 bytes .../__pycache__/structures.cpython-37.pyc | Bin 0 -> 4388 bytes .../requests/__pycache__/utils.cpython-37.pyc | Bin 0 -> 22048 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 | 158 + .../pip/_vendor/requests/auth.py | 305 + .../pip/_vendor/requests/certs.py | 18 + .../pip/_vendor/requests/compat.py | 74 + .../pip/_vendor/requests/cookies.py | 549 + .../pip/_vendor/requests/exceptions.py | 126 + .../pip/_vendor/requests/help.py | 119 + .../pip/_vendor/requests/hooks.py | 34 + .../pip/_vendor/requests/models.py | 953 + .../pip/_vendor/requests/packages.py | 16 + .../pip/_vendor/requests/sessions.py | 770 + .../pip/_vendor/requests/status_codes.py | 120 + .../pip/_vendor/requests/structures.py | 103 + .../pip/_vendor/requests/utils.py | 977 + .../site-packages/pip/_vendor/retrying.py | 267 + .../site-packages/pip/_vendor/six.py | 952 + .../pip/_vendor/urllib3/__init__.py | 92 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 2124 bytes .../__pycache__/_collections.cpython-37.pyc | Bin 0 -> 10688 bytes .../__pycache__/connection.cpython-37.pyc | Bin 0 -> 10157 bytes .../__pycache__/connectionpool.cpython-37.pyc | Bin 0 -> 23662 bytes .../__pycache__/exceptions.cpython-37.pyc | Bin 0 -> 10409 bytes .../urllib3/__pycache__/fields.cpython-37.pyc | Bin 0 -> 5877 bytes .../__pycache__/filepost.cpython-37.pyc | Bin 0 -> 2769 bytes .../__pycache__/poolmanager.cpython-37.pyc | Bin 0 -> 12729 bytes .../__pycache__/request.cpython-37.pyc | Bin 0 -> 5591 bytes .../__pycache__/response.cpython-37.pyc | Bin 0 -> 18756 bytes .../pip/_vendor/urllib3/_collections.py | 329 + .../pip/_vendor/urllib3/connection.py | 391 + .../pip/_vendor/urllib3/connectionpool.py | 896 + .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 208 bytes .../_appengine_environ.cpython-37.pyc | Bin 0 -> 1104 bytes .../__pycache__/appengine.cpython-37.pyc | Bin 0 -> 8323 bytes .../__pycache__/ntlmpool.cpython-37.pyc | Bin 0 -> 3250 bytes .../__pycache__/pyopenssl.cpython-37.pyc | Bin 0 -> 14486 bytes .../securetransport.cpython-37.pyc | Bin 0 -> 17905 bytes .../contrib/__pycache__/socks.cpython-37.pyc | Bin 0 -> 4914 bytes .../urllib3/contrib/_appengine_environ.py | 30 + .../contrib/_securetransport/__init__.py | 0 .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 225 bytes .../__pycache__/bindings.cpython-37.pyc | Bin 0 -> 10320 bytes .../__pycache__/low_level.cpython-37.pyc | Bin 0 -> 7501 bytes .../contrib/_securetransport/bindings.py | 593 + .../contrib/_securetransport/low_level.py | 346 + .../pip/_vendor/urllib3/contrib/appengine.py | 289 + .../pip/_vendor/urllib3/contrib/ntlmpool.py | 111 + .../pip/_vendor/urllib3/contrib/pyopenssl.py | 466 + .../urllib3/contrib/securetransport.py | 804 + .../pip/_vendor/urllib3/contrib/socks.py | 192 + .../pip/_vendor/urllib3/exceptions.py | 246 + .../pip/_vendor/urllib3/fields.py | 178 + .../pip/_vendor/urllib3/filepost.py | 98 + .../pip/_vendor/urllib3/packages/__init__.py | 5 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 322 bytes .../packages/__pycache__/six.cpython-37.pyc | Bin 0 -> 24410 bytes .../urllib3/packages/backports/__init__.py | 0 .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 219 bytes .../__pycache__/makefile.cpython-37.pyc | Bin 0 -> 1313 bytes .../urllib3/packages/backports/makefile.py | 53 + .../pip/_vendor/urllib3/packages/six.py | 868 + .../packages/ssl_match_hostname/__init__.py | 19 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 563 bytes .../_implementation.cpython-37.pyc | Bin 0 -> 3322 bytes .../ssl_match_hostname/_implementation.py | 156 + .../pip/_vendor/urllib3/poolmanager.py | 450 + .../pip/_vendor/urllib3/request.py | 150 + .../pip/_vendor/urllib3/response.py | 705 + .../pip/_vendor/urllib3/util/__init__.py | 54 + .../util/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 1000 bytes .../__pycache__/connection.cpython-37.pyc | Bin 0 -> 3175 bytes .../util/__pycache__/queue.cpython-37.pyc | Bin 0 -> 1049 bytes .../util/__pycache__/request.cpython-37.pyc | Bin 0 -> 3230 bytes .../util/__pycache__/response.cpython-37.pyc | Bin 0 -> 1978 bytes .../util/__pycache__/retry.cpython-37.pyc | Bin 0 -> 12664 bytes .../util/__pycache__/ssl_.cpython-37.pyc | Bin 0 -> 9556 bytes .../util/__pycache__/timeout.cpython-37.pyc | Bin 0 -> 8779 bytes .../util/__pycache__/url.cpython-37.pyc | Bin 0 -> 5189 bytes .../util/__pycache__/wait.cpython-37.pyc | Bin 0 -> 3141 bytes .../pip/_vendor/urllib3/util/connection.py | 134 + .../pip/_vendor/urllib3/util/queue.py | 21 + .../pip/_vendor/urllib3/util/request.py | 118 + .../pip/_vendor/urllib3/util/response.py | 87 + .../pip/_vendor/urllib3/util/retry.py | 411 + .../pip/_vendor/urllib3/util/ssl_.py | 381 + .../pip/_vendor/urllib3/util/timeout.py | 242 + .../pip/_vendor/urllib3/util/url.py | 230 + .../pip/_vendor/urllib3/util/wait.py | 150 + .../pip/_vendor/webencodings/__init__.py | 342 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 9684 bytes .../__pycache__/labels.cpython-37.pyc | Bin 0 -> 4098 bytes .../__pycache__/mklabels.cpython-37.pyc | Bin 0 -> 1920 bytes .../__pycache__/tests.cpython-37.pyc | Bin 0 -> 5061 bytes .../__pycache__/x_user_defined.cpython-37.pyc | Bin 0 -> 2673 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 + .../site-packages/pkg_resources/__init__.py | 3286 +++ .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 99636 bytes .../__pycache__/py31compat.cpython-37.pyc | Bin 0 -> 646 bytes .../pkg_resources/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 209 bytes .../__pycache__/appdirs.cpython-37.pyc | Bin 0 -> 20697 bytes .../__pycache__/pyparsing.cpython-37.pyc | Bin 0 -> 203052 bytes .../_vendor/__pycache__/six.cpython-37.pyc | Bin 0 -> 24410 bytes .../pkg_resources/_vendor/appdirs.py | 608 + .../_vendor/packaging/__about__.py | 21 + .../_vendor/packaging/__init__.py | 14 + .../__pycache__/__about__.cpython-37.pyc | Bin 0 -> 745 bytes .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 583 bytes .../__pycache__/_compat.cpython-37.pyc | Bin 0 -> 1035 bytes .../__pycache__/_structures.cpython-37.pyc | Bin 0 -> 2887 bytes .../__pycache__/markers.cpython-37.pyc | Bin 0 -> 8895 bytes .../__pycache__/requirements.cpython-37.pyc | Bin 0 -> 3900 bytes .../__pycache__/specifiers.cpython-37.pyc | Bin 0 -> 19813 bytes .../__pycache__/utils.cpython-37.pyc | Bin 0 -> 514 bytes .../__pycache__/version.cpython-37.pyc | Bin 0 -> 10580 bytes .../_vendor/packaging/_compat.py | 30 + .../_vendor/packaging/_structures.py | 68 + .../_vendor/packaging/markers.py | 301 + .../_vendor/packaging/requirements.py | 127 + .../_vendor/packaging/specifiers.py | 774 + .../pkg_resources/_vendor/packaging/utils.py | 14 + .../_vendor/packaging/version.py | 393 + .../pkg_resources/_vendor/pyparsing.py | 5742 ++++ .../pkg_resources/_vendor/six.py | 868 + .../pkg_resources/extern/__init__.py | 73 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 2428 bytes .../site-packages/pkg_resources/py31compat.py | 23 + .../setuptools-41.0.1.dist-info/INSTALLER | 1 + .../setuptools-41.0.1.dist-info/LICENSE | 19 + .../setuptools-41.0.1.dist-info/METADATA | 77 + .../setuptools-41.0.1.dist-info/RECORD | 186 + .../setuptools-41.0.1.dist-info/WHEEL | 6 + .../dependency_links.txt | 2 + .../entry_points.txt | 65 + .../setuptools-41.0.1.dist-info/top_level.txt | 3 + .../setuptools-41.0.1.dist-info/zip-safe | 1 + .../site-packages/setuptools/__init__.py | 228 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 7710 bytes .../_deprecation_warning.cpython-37.pyc | Bin 0 -> 564 bytes .../__pycache__/archive_util.cpython-37.pyc | Bin 0 -> 5145 bytes .../__pycache__/build_meta.cpython-37.pyc | Bin 0 -> 8303 bytes .../__pycache__/config.cpython-37.pyc | Bin 0 -> 17707 bytes .../__pycache__/dep_util.cpython-37.pyc | Bin 0 -> 871 bytes .../__pycache__/depends.cpython-37.pyc | Bin 0 -> 5280 bytes .../__pycache__/dist.cpython-37.pyc | Bin 0 -> 42103 bytes .../__pycache__/extension.cpython-37.pyc | Bin 0 -> 1991 bytes .../__pycache__/glibc.cpython-37.pyc | Bin 0 -> 1556 bytes .../__pycache__/glob.cpython-37.pyc | Bin 0 -> 3766 bytes .../__pycache__/launch.cpython-37.pyc | Bin 0 -> 870 bytes .../__pycache__/lib2to3_ex.cpython-37.pyc | Bin 0 -> 2449 bytes .../__pycache__/monkey.cpython-37.pyc | Bin 0 -> 4650 bytes .../__pycache__/msvc.cpython-37.pyc | Bin 0 -> 34447 bytes .../__pycache__/namespaces.cpython-37.pyc | Bin 0 -> 3628 bytes .../__pycache__/package_index.cpython-37.pyc | Bin 0 -> 32714 bytes .../__pycache__/pep425tags.cpython-37.pyc | Bin 0 -> 7218 bytes .../__pycache__/py27compat.cpython-37.pyc | Bin 0 -> 827 bytes .../__pycache__/py31compat.cpython-37.pyc | Bin 0 -> 1225 bytes .../__pycache__/py33compat.cpython-37.pyc | Bin 0 -> 1436 bytes .../__pycache__/sandbox.cpython-37.pyc | Bin 0 -> 15552 bytes .../__pycache__/site-patch.cpython-37.pyc | Bin 0 -> 1518 bytes .../__pycache__/ssl_support.cpython-37.pyc | Bin 0 -> 6808 bytes .../__pycache__/unicode_utils.cpython-37.pyc | Bin 0 -> 1185 bytes .../__pycache__/version.cpython-37.pyc | Bin 0 -> 344 bytes .../__pycache__/wheel.cpython-37.pyc | Bin 0 -> 6997 bytes .../windows_support.cpython-37.pyc | Bin 0 -> 1027 bytes .../setuptools/_deprecation_warning.py | 7 + .../setuptools/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 206 bytes .../__pycache__/pyparsing.cpython-37.pyc | Bin 0 -> 203049 bytes .../_vendor/__pycache__/six.cpython-37.pyc | Bin 0 -> 24407 bytes .../setuptools/_vendor/packaging/__about__.py | 21 + .../setuptools/_vendor/packaging/__init__.py | 14 + .../__pycache__/__about__.cpython-37.pyc | Bin 0 -> 742 bytes .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 580 bytes .../__pycache__/_compat.cpython-37.pyc | Bin 0 -> 1032 bytes .../__pycache__/_structures.cpython-37.pyc | Bin 0 -> 2884 bytes .../__pycache__/markers.cpython-37.pyc | Bin 0 -> 8889 bytes .../__pycache__/requirements.cpython-37.pyc | Bin 0 -> 3891 bytes .../__pycache__/specifiers.cpython-37.pyc | Bin 0 -> 19810 bytes .../__pycache__/utils.cpython-37.pyc | Bin 0 -> 511 bytes .../__pycache__/version.cpython-37.pyc | Bin 0 -> 10577 bytes .../setuptools/_vendor/packaging/_compat.py | 30 + .../_vendor/packaging/_structures.py | 68 + .../setuptools/_vendor/packaging/markers.py | 301 + .../_vendor/packaging/requirements.py | 127 + .../_vendor/packaging/specifiers.py | 774 + .../setuptools/_vendor/packaging/utils.py | 14 + .../setuptools/_vendor/packaging/version.py | 393 + .../setuptools/_vendor/pyparsing.py | 5742 ++++ .../site-packages/setuptools/_vendor/six.py | 868 + .../site-packages/setuptools/archive_util.py | 173 + .../site-packages/setuptools/build_meta.py | 254 + .../site-packages/setuptools/cli-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/cli-64.exe | Bin 0 -> 74752 bytes .../site-packages/setuptools/cli.exe | Bin 0 -> 65536 bytes .../setuptools/command/__init__.py | 18 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 752 bytes .../command/__pycache__/alias.cpython-37.pyc | Bin 0 -> 2420 bytes .../__pycache__/bdist_egg.cpython-37.pyc | Bin 0 -> 14208 bytes .../__pycache__/bdist_rpm.cpython-37.pyc | Bin 0 -> 1799 bytes .../__pycache__/bdist_wininst.cpython-37.pyc | Bin 0 -> 990 bytes .../__pycache__/build_clib.cpython-37.pyc | Bin 0 -> 2469 bytes .../__pycache__/build_ext.cpython-37.pyc | Bin 0 -> 9723 bytes .../__pycache__/build_py.cpython-37.pyc | Bin 0 -> 8599 bytes .../__pycache__/develop.cpython-37.pyc | Bin 0 -> 6480 bytes .../__pycache__/dist_info.cpython-37.pyc | Bin 0 -> 1395 bytes .../__pycache__/easy_install.cpython-37.pyc | Bin 0 -> 64851 bytes .../__pycache__/egg_info.cpython-37.pyc | Bin 0 -> 21697 bytes .../__pycache__/install.cpython-37.pyc | Bin 0 -> 4027 bytes .../install_egg_info.cpython-37.pyc | Bin 0 -> 2428 bytes .../__pycache__/install_lib.cpython-37.pyc | Bin 0 -> 4104 bytes .../install_scripts.cpython-37.pyc | Bin 0 -> 2307 bytes .../__pycache__/py36compat.cpython-37.pyc | Bin 0 -> 4640 bytes .../__pycache__/register.cpython-37.pyc | Bin 0 -> 797 bytes .../command/__pycache__/rotate.cpython-37.pyc | Bin 0 -> 2546 bytes .../__pycache__/saveopts.cpython-37.pyc | Bin 0 -> 941 bytes .../command/__pycache__/sdist.cpython-37.pyc | Bin 0 -> 6799 bytes .../command/__pycache__/setopt.cpython-37.pyc | Bin 0 -> 4539 bytes .../command/__pycache__/test.cpython-37.pyc | Bin 0 -> 8192 bytes .../command/__pycache__/upload.cpython-37.pyc | Bin 0 -> 5205 bytes .../__pycache__/upload_docs.cpython-37.pyc | Bin 0 -> 6150 bytes .../site-packages/setuptools/command/alias.py | 80 + .../setuptools/command/bdist_egg.py | 502 + .../setuptools/command/bdist_rpm.py | 43 + .../setuptools/command/bdist_wininst.py | 21 + .../setuptools/command/build_clib.py | 98 + .../setuptools/command/build_ext.py | 321 + .../setuptools/command/build_py.py | 270 + .../setuptools/command/develop.py | 221 + .../setuptools/command/dist_info.py | 36 + .../setuptools/command/easy_install.py | 2342 ++ .../setuptools/command/egg_info.py | 717 + .../setuptools/command/install.py | 125 + .../setuptools/command/install_egg_info.py | 62 + .../setuptools/command/install_lib.py | 121 + .../setuptools/command/install_scripts.py | 65 + .../setuptools/command/launcher manifest.xml | 15 + .../setuptools/command/py36compat.py | 136 + .../setuptools/command/register.py | 18 + .../setuptools/command/rotate.py | 66 + .../setuptools/command/saveopts.py | 22 + .../site-packages/setuptools/command/sdist.py | 221 + .../setuptools/command/setopt.py | 149 + .../site-packages/setuptools/command/test.py | 271 + .../setuptools/command/upload.py | 196 + .../setuptools/command/upload_docs.py | 206 + .../site-packages/setuptools/config.py | 656 + .../site-packages/setuptools/dep_util.py | 23 + .../site-packages/setuptools/depends.py | 186 + .../site-packages/setuptools/dist.py | 1278 + .../site-packages/setuptools/extension.py | 57 + .../setuptools/extern/__init__.py | 73 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 2429 bytes .../site-packages/setuptools/glibc.py | 86 + .../site-packages/setuptools/glob.py | 174 + .../site-packages/setuptools/gui-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/gui-64.exe | Bin 0 -> 75264 bytes .../site-packages/setuptools/gui.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/launch.py | 35 + .../site-packages/setuptools/lib2to3_ex.py | 62 + .../site-packages/setuptools/monkey.py | 179 + .../site-packages/setuptools/msvc.py | 1301 + .../site-packages/setuptools/namespaces.py | 107 + .../site-packages/setuptools/package_index.py | 1136 + .../site-packages/setuptools/pep425tags.py | 319 + .../site-packages/setuptools/py27compat.py | 28 + .../site-packages/setuptools/py31compat.py | 32 + .../site-packages/setuptools/py33compat.py | 55 + .../site-packages/setuptools/sandbox.py | 491 + .../setuptools/script (dev).tmpl | 6 + .../site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/site-patch.py | 74 + .../site-packages/setuptools/ssl_support.py | 260 + .../site-packages/setuptools/unicode_utils.py | 44 + .../site-packages/setuptools/version.py | 6 + .../site-packages/setuptools/wheel.py | 211 + .../setuptools/windows_support.py | 29 + .../wheel-0.33.4.dist-info/INSTALLER | 1 + .../wheel-0.33.4.dist-info/LICENSE.txt | 22 + .../wheel-0.33.4.dist-info/METADATA | 60 + .../wheel-0.33.4.dist-info/RECORD | 32 + .../wheel-0.33.4.dist-info/WHEEL | 6 + .../wheel-0.33.4.dist-info/entry_points.txt | 6 + .../wheel-0.33.4.dist-info/top_level.txt | 1 + .../python3.7/site-packages/wheel/__init__.py | 2 + .../python3.7/site-packages/wheel/__main__.py | 19 + .../wheel/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 210 bytes .../wheel/__pycache__/__main__.cpython-37.pyc | Bin 0 -> 603 bytes .../__pycache__/bdist_wheel.cpython-37.pyc | Bin 0 -> 10095 bytes .../wheel/__pycache__/metadata.cpython-37.pyc | Bin 0 -> 3786 bytes .../__pycache__/pep425tags.cpython-37.pyc | Bin 0 -> 4721 bytes .../wheel/__pycache__/pkginfo.cpython-37.pyc | Bin 0 -> 1581 bytes .../wheel/__pycache__/util.cpython-37.pyc | Bin 0 -> 1326 bytes .../__pycache__/wheelfile.cpython-37.pyc | Bin 0 -> 5481 bytes .../site-packages/wheel/bdist_wheel.py | 372 + .../site-packages/wheel/cli/__init__.py | 88 + .../cli/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 3052 bytes .../cli/__pycache__/convert.cpython-37.pyc | Bin 0 -> 6221 bytes .../wheel/cli/__pycache__/pack.cpython-37.pyc | Bin 0 -> 2510 bytes .../cli/__pycache__/unpack.cpython-37.pyc | Bin 0 -> 955 bytes .../site-packages/wheel/cli/convert.py | 269 + .../python3.7/site-packages/wheel/cli/pack.py | 58 + .../site-packages/wheel/cli/unpack.py | 25 + .../python3.7/site-packages/wheel/metadata.py | 141 + .../site-packages/wheel/pep425tags.py | 185 + .../python3.7/site-packages/wheel/pkginfo.py | 43 + .../lib/python3.7/site-packages/wheel/util.py | 46 + .../site-packages/wheel/wheelfile.py | 169 + backend/venv/venv/lib/python3.7/site.py | 821 + .../venv/venv/lib/python3.7/sre_compile.py | 1 + .../venv/venv/lib/python3.7/sre_constants.py | 1 + backend/venv/venv/lib/python3.7/sre_parse.py | 1 + backend/venv/venv/lib/python3.7/stat.py | 1 + backend/venv/venv/lib/python3.7/struct.py | 1 + backend/venv/venv/lib/python3.7/tarfile.py | 1 + backend/venv/venv/lib/python3.7/tempfile.py | 1 + backend/venv/venv/lib/python3.7/token.py | 1 + backend/venv/venv/lib/python3.7/tokenize.py | 1 + backend/venv/venv/lib/python3.7/types.py | 1 + backend/venv/venv/lib/python3.7/warnings.py | 1 + backend/venv/venv/lib/python3.7/weakref.py | 1 + .../metadata.json | 1 + .../output.pkl | 1 + .../joblib/run/EnsembleModel/func_code.py | 54 + .../metadata.json | 1 + .../output.pkl | Bin 0 -> 8094 bytes .../metadata.json | 1 + .../output.pkl | Bin 0 -> 7482 bytes cache_dir/joblib/run/GridSearch/func_code.py | 47 + .../metadata.json | 1 + .../output.pkl | Bin 0 -> 473 bytes .../run/InitializeEnsemble/func_code.py | 105 + dist/datasets/breast-cancer-wisconsin.csv | 700 + dist/datasets/diabetes.csv | 769 + dist/datasets/iris.csv | 152 + .../app.dfca11727ccf9e74f393b0c223db19ce.css | 1 - dist/static/js/0.a13b6e380cf2b0351445.js | 8 - dist/static/js/0.a13b6e380cf2b0351445.js.map | 1 - dist/static/js/1.5b6be78b138f4f82e219.js | 2 - dist/static/js/1.5b6be78b138f4f82e219.js.map | 1 - dist/static/js/2.f51279a339a43b82ef61.js | 2 - dist/static/js/2.f51279a339a43b82ef61.js.map | 1 - dist/static/js/3.b0ba1fd256aee2a5d235.js | 2 - dist/static/js/3.b0ba1fd256aee2a5d235.js.map | 1 - dist/static/js/app.c1b5e870a4e821397b86.js | 2 - .../static/js/app.c1b5e870a4e821397b86.js.map | 1 - .../js/manifest.d4a6057c666542329f1b.js | 2 - .../js/manifest.d4a6057c666542329f1b.js.map | 1 - dist/static/js/vendor.1e5687ded8537373b3d4.js | 7 - .../js/vendor.1e5687ded8537373b3d4.js.map | 1 - frontend/.babelrc | 38 +- frontend/build/vue-loader.conf.js | 1 - frontend/build/webpack.base.conf.js | 6 +- frontend/package-lock.json | 22148 ++++++++++------ frontend/package.json | 70 +- frontend/shims.d.ts | 4 - frontend/src/App.vue | 13 +- frontend/src/assets/logo.png | Bin 6849 -> 0 bytes frontend/src/components/About.vue | 2 + frontend/src/components/Home.vue | 42 - frontend/src/components/LoadFile.vue | 245 + frontend/src/components/Main.vue | 32 + frontend/src/components/NotFound.vue | 2 + frontend/src/main.js | 34 +- frontend/src/main.js.map | 1 - frontend/src/main.ts | 16 - frontend/src/router/index.js | 44 +- frontend/src/router/index.js.map | 1 - frontend/src/router/index.ts | 28 - frontend/tsconfig.json | 13 - run.py | 313 +- 1297 files changed, 200363 insertions(+), 16455 deletions(-) rename backend/venv/lib/python3.7/site-packages/{Werkzeug-0.12.2.dist-info => Flask_PyMongo-2.3.0.dist-info}/INSTALLER (100%) create mode 100644 backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/LICENSE create mode 100644 backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/METADATA create mode 100644 backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/RECORD rename backend/venv/lib/python3.7/site-packages/{Werkzeug-0.12.2.dist-info => Flask_PyMongo-2.3.0.dist-info}/WHEEL (70%) create mode 100644 backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/pbr.json create mode 100644 backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/top_level.txt delete mode 100644 backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/DESCRIPTION.rst delete mode 100644 backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/LICENSE.txt delete mode 100644 backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/METADATA delete mode 100644 backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/RECORD delete mode 100644 backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/metadata.json create mode 100644 backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/INSTALLER create mode 100644 backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/LICENSE.rst create mode 100644 backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/METADATA create mode 100644 backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/RECORD create mode 100644 backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/WHEEL rename backend/venv/lib/python3.7/site-packages/{Werkzeug-0.12.2.dist-info => Werkzeug-0.15.4.dist-info}/top_level.txt (100%) create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__init__.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/binary.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/code.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/codec_options.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/dbref.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/decimal128.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/errors.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/int64.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/json_util.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/max_key.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/min_key.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/objectid.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/py3compat.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/raw_bson.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/regex.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/son.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/timestamp.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/bson/__pycache__/tz_util.cpython-37.pyc create mode 100755 backend/venv/lib/python3.7/site-packages/bson/_cbson.cpython-37m-darwin.so create mode 100644 backend/venv/lib/python3.7/site-packages/bson/binary.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/code.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/codec_options.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/dbref.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/decimal128.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/errors.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/int64.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/json_util.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/max_key.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/min_key.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/objectid.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/py3compat.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/raw_bson.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/regex.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/son.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/timestamp.py create mode 100644 backend/venv/lib/python3.7/site-packages/bson/tz_util.py create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/__init__.py create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/__pycache__/_version.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/__pycache__/wrappers.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/_version.py create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__init__.py create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/test_config.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/test_gridfs.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/test_url_converter.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/test_wrappers.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/util.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_config.py create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_gridfs.py create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_url_converter.py create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_wrappers.py create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/util.py create mode 100644 backend/venv/lib/python3.7/site-packages/flask_pymongo/wrappers.py create mode 100644 backend/venv/lib/python3.7/site-packages/gridfs/__init__.py create mode 100644 backend/venv/lib/python3.7/site-packages/gridfs/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/gridfs/__pycache__/errors.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/gridfs/__pycache__/grid_file.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/gridfs/errors.py create mode 100644 backend/venv/lib/python3.7/site-packages/gridfs/grid_file.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/INSTALLER create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/METADATA create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/RECORD create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/WHEEL create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/top_level.txt create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__init__.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/auth.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/bulk.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/change_stream.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/client_options.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/client_session.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/collation.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/collection.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/command_cursor.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/common.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/compression_support.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/cursor.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/cursor_manager.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/database.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/driver_info.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/errors.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/helpers.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/ismaster.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/max_staleness_selectors.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/message.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/mongo_client.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/mongo_replica_set_client.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/monitor.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/monitoring.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/monotonic.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/network.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/operations.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/periodic_executor.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/pool.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/read_concern.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/read_preferences.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/response.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/results.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/saslprep.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/server.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/server_description.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/server_selectors.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/server_type.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/settings.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/son_manipulator.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/ssl_context.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/ssl_match_hostname.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/ssl_support.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/thread_util.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/topology.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/topology_description.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/uri_parser.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/write_concern.cpython-37.pyc create mode 100755 backend/venv/lib/python3.7/site-packages/pymongo/_cmessage.cpython-37m-darwin.so create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/auth.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/bulk.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/change_stream.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/client_options.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/client_session.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/collation.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/collection.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/command_cursor.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/common.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/compression_support.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/cursor.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/cursor_manager.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/database.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/driver_info.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/errors.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/helpers.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/ismaster.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/max_staleness_selectors.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/message.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/mongo_client.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/mongo_replica_set_client.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/monitor.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/monitoring.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/monotonic.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/network.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/operations.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/periodic_executor.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/pool.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/read_concern.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/read_preferences.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/response.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/results.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/saslprep.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/server.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/server_description.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/server_selectors.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/server_type.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/settings.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/son_manipulator.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/ssl_context.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/ssl_match_hostname.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/ssl_support.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/thread_util.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/topology.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/topology_description.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/uri_parser.py create mode 100644 backend/venv/lib/python3.7/site-packages/pymongo/write_concern.py delete mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/script.cpython-37.pyc delete mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/wrappers.cpython-37.pyc delete mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/jsrouting.cpython-37.pyc delete mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/limiter.cpython-37.pyc delete mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/testtools.cpython-37.pyc delete mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/contrib/jsrouting.py delete mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/contrib/limiter.py delete mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/contrib/testtools.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/middleware/__init__.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/middleware/__pycache__/dispatcher.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/middleware/__pycache__/lint.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/middleware/dispatcher.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/middleware/http_proxy.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/middleware/lint.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/middleware/profiler.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/middleware/proxy_fix.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/middleware/shared_data.py delete mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/script.py delete mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/__init__.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/__pycache__/accept.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/__pycache__/auth.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/__pycache__/base_request.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/__pycache__/base_response.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/__pycache__/common_descriptors.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/__pycache__/etag.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/__pycache__/json.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/__pycache__/request.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/__pycache__/response.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/__pycache__/user_agent.cpython-37.pyc create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/accept.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/auth.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/base_request.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/base_response.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/common_descriptors.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/etag.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/json.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/request.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/response.py create mode 100644 backend/venv/lib/python3.7/site-packages/werkzeug/wrappers/user_agent.py create mode 120000 backend/venv/venv/.Python create mode 100644 backend/venv/venv/bin/activate create mode 100644 backend/venv/venv/bin/activate.csh create mode 100644 backend/venv/venv/bin/activate.fish create mode 100644 backend/venv/venv/bin/activate.ps1 create mode 100644 backend/venv/venv/bin/activate.xsh create mode 100644 backend/venv/venv/bin/activate_this.py create mode 100755 backend/venv/venv/bin/easy_install create mode 100755 backend/venv/venv/bin/easy_install-3.7 create mode 100755 backend/venv/venv/bin/pip create mode 100755 backend/venv/venv/bin/pip3 create mode 100755 backend/venv/venv/bin/pip3.7 create mode 120000 backend/venv/venv/bin/python create mode 100755 backend/venv/venv/bin/python-config create mode 100755 backend/venv/venv/bin/python3 create mode 120000 backend/venv/venv/bin/python3.7 create mode 100755 backend/venv/venv/bin/wheel create mode 120000 backend/venv/venv/include/python3.7m create mode 120000 backend/venv/venv/lib/python3.7/LICENSE.txt create mode 120000 backend/venv/venv/lib/python3.7/__future__.py create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/__future__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/_bootlocale.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/_collections_abc.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/_weakrefset.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/abc.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/base64.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/bisect.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/codecs.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/copy.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/copyreg.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/enum.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/fnmatch.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/functools.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/genericpath.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/hashlib.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/heapq.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/hmac.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/io.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/keyword.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/linecache.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/locale.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/ntpath.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/operator.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/os.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/posixpath.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/random.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/re.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/reprlib.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/shutil.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/site.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/sre_compile.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/sre_constants.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/sre_parse.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/stat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/struct.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/tarfile.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/tempfile.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/token.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/tokenize.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/types.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/warnings.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/__pycache__/weakref.cpython-37.pyc create mode 120000 backend/venv/venv/lib/python3.7/_bootlocale.py create mode 120000 backend/venv/venv/lib/python3.7/_collections_abc.py create mode 120000 backend/venv/venv/lib/python3.7/_dummy_thread.py create mode 120000 backend/venv/venv/lib/python3.7/_weakrefset.py create mode 120000 backend/venv/venv/lib/python3.7/abc.py create mode 120000 backend/venv/venv/lib/python3.7/base64.py create mode 120000 backend/venv/venv/lib/python3.7/bisect.py create mode 120000 backend/venv/venv/lib/python3.7/codecs.py create mode 120000 backend/venv/venv/lib/python3.7/collections create mode 120000 backend/venv/venv/lib/python3.7/config-3.7m-darwin create mode 120000 backend/venv/venv/lib/python3.7/copy.py create mode 120000 backend/venv/venv/lib/python3.7/copyreg.py create mode 100644 backend/venv/venv/lib/python3.7/distutils/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/distutils/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/distutils/distutils.cfg create mode 120000 backend/venv/venv/lib/python3.7/encodings create mode 120000 backend/venv/venv/lib/python3.7/enum.py create mode 120000 backend/venv/venv/lib/python3.7/fnmatch.py create mode 120000 backend/venv/venv/lib/python3.7/functools.py create mode 120000 backend/venv/venv/lib/python3.7/genericpath.py create mode 120000 backend/venv/venv/lib/python3.7/hashlib.py create mode 120000 backend/venv/venv/lib/python3.7/heapq.py create mode 120000 backend/venv/venv/lib/python3.7/hmac.py create mode 120000 backend/venv/venv/lib/python3.7/imp.py create mode 120000 backend/venv/venv/lib/python3.7/importlib create mode 120000 backend/venv/venv/lib/python3.7/io.py create mode 120000 backend/venv/venv/lib/python3.7/keyword.py create mode 120000 backend/venv/venv/lib/python3.7/lib-dynload create mode 120000 backend/venv/venv/lib/python3.7/linecache.py create mode 120000 backend/venv/venv/lib/python3.7/locale.py create mode 100644 backend/venv/venv/lib/python3.7/no-global-site-packages.txt create mode 120000 backend/venv/venv/lib/python3.7/ntpath.py create mode 120000 backend/venv/venv/lib/python3.7/operator.py create mode 100644 backend/venv/venv/lib/python3.7/orig-prefix.txt create mode 120000 backend/venv/venv/lib/python3.7/os.py create mode 120000 backend/venv/venv/lib/python3.7/posixpath.py create mode 120000 backend/venv/venv/lib/python3.7/random.py create mode 120000 backend/venv/venv/lib/python3.7/re.py create mode 120000 backend/venv/venv/lib/python3.7/readline.so create mode 120000 backend/venv/venv/lib/python3.7/reprlib.py create mode 120000 backend/venv/venv/lib/python3.7/rlcompleter.py create mode 120000 backend/venv/venv/lib/python3.7/shutil.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/__pycache__/easy_install.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/easy_install.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip-19.1.1.dist-info/INSTALLER create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip-19.1.1.dist-info/LICENSE.txt create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip-19.1.1.dist-info/METADATA create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip-19.1.1.dist-info/RECORD create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip-19.1.1.dist-info/WHEEL create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip-19.1.1.dist-info/entry_points.txt create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip-19.1.1.dist-info/top_level.txt create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/__main__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/__pycache__/__main__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/__pycache__/build_env.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/__pycache__/cache.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/__pycache__/configuration.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/__pycache__/download.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/__pycache__/exceptions.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/__pycache__/index.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/__pycache__/locations.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/__pycache__/pep425tags.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/__pycache__/pyproject.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/__pycache__/resolve.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/__pycache__/wheel.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/build_env.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cache.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cli/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cli/__pycache__/parser.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cli/base_command.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cli/main_parser.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cli/parser.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/cli/status_codes.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__pycache__/check.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__pycache__/completion.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__pycache__/download.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__pycache__/hash.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__pycache__/help.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__pycache__/install.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__pycache__/list.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__pycache__/search.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__pycache__/show.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/check.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/completion.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/configuration.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/download.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/freeze.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/hash.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/help.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/install.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/list.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/search.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/show.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/uninstall.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/commands/wheel.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/configuration.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/download.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/exceptions.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/index.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/locations.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/models/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/models/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/models/__pycache__/candidate.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/models/__pycache__/format_control.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/models/__pycache__/index.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/models/__pycache__/link.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/models/candidate.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/models/format_control.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/models/index.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/models/link.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/operations/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/operations/__pycache__/check.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/operations/check.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/operations/freeze.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/operations/prepare.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/pep425tags.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/pyproject.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/req/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/req/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/req/__pycache__/constructors.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/req/__pycache__/req_file.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/req/__pycache__/req_install.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/req/__pycache__/req_set.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/req/__pycache__/req_tracker.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/req/constructors.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/req/req_file.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/req/req_install.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/req/req_set.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/req/req_tracker.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/resolve.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/logging.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/misc.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/models.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/outdated.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/typing.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/__pycache__/ui.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/appdirs.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/deprecation.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/encoding.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/filesystem.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/glibc.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/hashes.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/logging.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/misc.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/models.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/outdated.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/packaging.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/typing.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/utils/ui.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/vcs/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/vcs/__pycache__/git.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/vcs/git.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/vcs/subversion.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_internal/wheel.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/__pycache__/appdirs.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/__pycache__/distro.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/__pycache__/ipaddress.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/__pycache__/pyparsing.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/__pycache__/retrying.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/__pycache__/six.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/appdirs.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/__pycache__/cache.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/__pycache__/compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/_cmd.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/adapter.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/cache.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/heuristics.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/certifi/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/certifi/__main__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/certifi/cacert.pem create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/certifi/core.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/big5prober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/enums.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/langcyrillicmodel.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/sjisprober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/utf8prober.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/big5freq.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/big5prober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/chardistribution.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/charsetgroupprober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/charsetprober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/cli/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/cli/chardetect.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/codingstatemachine.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/cp949prober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/enums.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/escprober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/escsm.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/eucjpprober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/euckrfreq.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/euckrprober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/euctwfreq.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/euctwprober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/gb2312freq.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/gb2312prober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/hebrewprober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/jisfreq.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/jpcntx.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/langbulgarianmodel.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/langcyrillicmodel.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/langgreekmodel.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/langhebrewmodel.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/langhungarianmodel.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/langthaimodel.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/langturkishmodel.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/latin1prober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/mbcharsetprober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/mbcsgroupprober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/mbcssm.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/sbcharsetprober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/sbcsgroupprober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/sjisprober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/universaldetector.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/utf8prober.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/chardet/version.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/colorama/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/colorama/__pycache__/ansi.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/colorama/__pycache__/win32.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/colorama/ansi.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/colorama/ansitowin32.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/colorama/initialise.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/colorama/win32.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/colorama/winterm.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/_backport/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/_backport/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/_backport/__pycache__/misc.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/_backport/__pycache__/shutil.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/_backport/__pycache__/sysconfig.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/_backport/__pycache__/tarfile.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/_backport/misc.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/_backport/shutil.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/_backport/tarfile.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/database.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/index.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/locators.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/manifest.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/markers.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/resources.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/t32.exe create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/t64.exe create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/util.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/version.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/w32.exe create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/w64.exe create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/distro.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/__pycache__/_ihatexml.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/__pycache__/_inputstream.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/__pycache__/_tokenizer.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/__pycache__/_utils.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/__pycache__/constants.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/__pycache__/html5parser.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/__pycache__/serializer.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/_ihatexml.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/_inputstream.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/_tokenizer.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/_trie/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/_trie/__pycache__/_base.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/_trie/__pycache__/datrie.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/_trie/__pycache__/py.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/_trie/_base.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/_trie/datrie.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/_trie/py.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/_utils.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/constants.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/__pycache__/alphabeticalattributes.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/__pycache__/base.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/__pycache__/inject_meta_charset.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/__pycache__/lint.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/__pycache__/optionaltags.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/__pycache__/sanitizer.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/__pycache__/whitespace.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/base.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/lint.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/optionaltags.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/sanitizer.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/filters/whitespace.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/html5parser.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/serializer.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/genshi.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/sax.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treeadapters/sax.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/base.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/dom.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/etree_lxml.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treebuilders/base.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treebuilders/dom.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treebuilders/etree.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/base.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/dom.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/etree.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/etree_lxml.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/genshi.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treewalkers/base.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treewalkers/dom.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treewalkers/etree.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/__pycache__/compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/__pycache__/core.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/__pycache__/intranges.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/__pycache__/package_data.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/codec.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/core.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/idnadata.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/intranges.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/package_data.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/idna/uts46data.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/ipaddress.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/lockfile/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/lockfile/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/lockfile/__pycache__/linklockfile.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/lockfile/__pycache__/mkdirlockfile.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/lockfile/__pycache__/pidlockfile.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/lockfile/__pycache__/sqlitelockfile.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/lockfile/__pycache__/symlinklockfile.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/lockfile/linklockfile.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/lockfile/mkdirlockfile.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/lockfile/pidlockfile.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/lockfile/sqlitelockfile.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/lockfile/symlinklockfile.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/msgpack/__pycache__/_version.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/msgpack/_version.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/__about__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/__pycache__/__about__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/__pycache__/_compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/_compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/markers.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/utils.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/packaging/version.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/__pycache__/_in_process.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/__pycache__/build.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/__pycache__/check.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/__pycache__/colorlog.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/__pycache__/compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/__pycache__/envbuild.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/__pycache__/wrappers.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/_in_process.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/build.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/check.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/colorlog.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/envbuild.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pep517/wrappers.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__pycache__/py31compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pkg_resources/py31compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/progress/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/progress/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/progress/__pycache__/bar.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/progress/__pycache__/counter.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/progress/__pycache__/spinner.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/progress/bar.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/progress/counter.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/progress/spinner.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pyparsing.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pytoml/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pytoml/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pytoml/__pycache__/core.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pytoml/__pycache__/parser.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pytoml/__pycache__/test.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pytoml/__pycache__/utils.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pytoml/__pycache__/writer.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pytoml/core.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pytoml/parser.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pytoml/test.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pytoml/utils.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/pytoml/writer.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/_internal_utils.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/adapters.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/api.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/cookies.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/help.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/models.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/__version__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/_internal_utils.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/adapters.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/api.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/auth.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/certs.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/cookies.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/exceptions.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/help.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/hooks.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/models.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/packages.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/sessions.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/status_codes.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/structures.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/requests/utils.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/retrying.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/six.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/__pycache__/_collections.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/__pycache__/exceptions.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/__pycache__/fields.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/__pycache__/filepost.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/__pycache__/response.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/_collections.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/connection.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/connectionpool.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/appengine.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/securetransport.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/contrib/socks.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/exceptions.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/fields.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/filepost.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/packages/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/packages/__pycache__/six.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/packages/six.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/poolmanager.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/request.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/response.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/__pycache__/connection.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/__pycache__/queue.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/__pycache__/request.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/__pycache__/response.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/__pycache__/retry.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/__pycache__/url.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/__pycache__/wait.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/connection.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/queue.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/request.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/response.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/retry.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/ssl_.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/timeout.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/url.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/urllib3/util/wait.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/webencodings/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/webencodings/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/webencodings/__pycache__/labels.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/webencodings/__pycache__/mklabels.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/webencodings/__pycache__/tests.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/webencodings/labels.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/webencodings/mklabels.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/webencodings/tests.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pip/_vendor/webencodings/x_user_defined.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/__pycache__/py31compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/__pycache__/appdirs.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/__pycache__/pyparsing.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/__pycache__/six.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/appdirs.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__pycache__/markers.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__pycache__/utils.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/_compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/pyparsing.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/_vendor/six.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/extern/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/pkg_resources/py31compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools-41.0.1.dist-info/INSTALLER create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools-41.0.1.dist-info/LICENSE create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools-41.0.1.dist-info/METADATA create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools-41.0.1.dist-info/RECORD create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools-41.0.1.dist-info/WHEEL create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools-41.0.1.dist-info/dependency_links.txt create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools-41.0.1.dist-info/entry_points.txt create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools-41.0.1.dist-info/top_level.txt create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools-41.0.1.dist-info/zip-safe create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/_deprecation_warning.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/archive_util.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/build_meta.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/config.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/dep_util.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/depends.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/dist.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/extension.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/glibc.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/glob.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/launch.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/lib2to3_ex.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/monkey.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/msvc.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/namespaces.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/package_index.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/pep425tags.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/py27compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/py31compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/py33compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/sandbox.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/site-patch.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/ssl_support.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/unicode_utils.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/version.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/wheel.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/__pycache__/windows_support.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_deprecation_warning.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/__pycache__/pyparsing.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/__pycache__/six.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/__about__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/__pycache__/__about__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/__pycache__/_compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/__pycache__/_structures.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/__pycache__/markers.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/__pycache__/requirements.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/__pycache__/specifiers.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/__pycache__/utils.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/__pycache__/version.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/_compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/pyparsing.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/_vendor/six.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/archive_util.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/build_meta.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/cli-32.exe create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/cli-64.exe create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/cli.exe create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/alias.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/bdist_wininst.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/build_clib.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/build_ext.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/build_py.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/develop.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/dist_info.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/easy_install.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/egg_info.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/install.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/install_lib.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/install_scripts.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/py36compat.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/register.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/rotate.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/saveopts.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/sdist.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/setopt.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/test.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/upload.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/__pycache__/upload_docs.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/alias.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/bdist_egg.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/bdist_rpm.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/bdist_wininst.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/build_clib.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/build_ext.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/build_py.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/develop.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/dist_info.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/easy_install.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/egg_info.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/install.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/install_egg_info.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/install_lib.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/install_scripts.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/launcher manifest.xml create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/py36compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/register.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/rotate.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/saveopts.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/sdist.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/setopt.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/test.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/upload.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/command/upload_docs.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/config.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/dep_util.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/depends.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/dist.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/extension.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/extern/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/extern/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/glibc.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/glob.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/gui-32.exe create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/gui-64.exe create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/gui.exe create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/launch.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/lib2to3_ex.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/monkey.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/msvc.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/namespaces.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/package_index.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/pep425tags.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/py27compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/py31compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/py33compat.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/sandbox.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/script (dev).tmpl create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/script.tmpl create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/site-patch.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/ssl_support.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/unicode_utils.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/version.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/wheel.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/setuptools/windows_support.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel-0.33.4.dist-info/INSTALLER create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel-0.33.4.dist-info/LICENSE.txt create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel-0.33.4.dist-info/METADATA create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel-0.33.4.dist-info/RECORD create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel-0.33.4.dist-info/WHEEL create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel-0.33.4.dist-info/entry_points.txt create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel-0.33.4.dist-info/top_level.txt create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/__main__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/__pycache__/__main__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/__pycache__/bdist_wheel.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/__pycache__/metadata.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/__pycache__/pep425tags.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/__pycache__/pkginfo.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/__pycache__/util.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/__pycache__/wheelfile.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/bdist_wheel.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/cli/__init__.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/cli/__pycache__/__init__.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/cli/__pycache__/convert.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/cli/__pycache__/pack.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/cli/__pycache__/unpack.cpython-37.pyc create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/cli/convert.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/cli/pack.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/cli/unpack.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/metadata.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/pep425tags.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/pkginfo.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/util.py create mode 100644 backend/venv/venv/lib/python3.7/site-packages/wheel/wheelfile.py create mode 100644 backend/venv/venv/lib/python3.7/site.py create mode 120000 backend/venv/venv/lib/python3.7/sre_compile.py create mode 120000 backend/venv/venv/lib/python3.7/sre_constants.py create mode 120000 backend/venv/venv/lib/python3.7/sre_parse.py create mode 120000 backend/venv/venv/lib/python3.7/stat.py create mode 120000 backend/venv/venv/lib/python3.7/struct.py create mode 120000 backend/venv/venv/lib/python3.7/tarfile.py create mode 120000 backend/venv/venv/lib/python3.7/tempfile.py create mode 120000 backend/venv/venv/lib/python3.7/token.py create mode 120000 backend/venv/venv/lib/python3.7/tokenize.py create mode 120000 backend/venv/venv/lib/python3.7/types.py create mode 120000 backend/venv/venv/lib/python3.7/warnings.py create mode 120000 backend/venv/venv/lib/python3.7/weakref.py create mode 100644 cache_dir/joblib/run/EnsembleModel/6cfa19bb450bfb5dfa3fc39dbc8aa77e/metadata.json create mode 100644 cache_dir/joblib/run/EnsembleModel/6cfa19bb450bfb5dfa3fc39dbc8aa77e/output.pkl create mode 100644 cache_dir/joblib/run/EnsembleModel/func_code.py create mode 100644 cache_dir/joblib/run/GridSearch/4b9f779431c047e8a87f8756bac85d64/metadata.json create mode 100644 cache_dir/joblib/run/GridSearch/4b9f779431c047e8a87f8756bac85d64/output.pkl create mode 100644 cache_dir/joblib/run/GridSearch/a32924a20c3a8483b7f841d3c9c7508f/metadata.json create mode 100644 cache_dir/joblib/run/GridSearch/a32924a20c3a8483b7f841d3c9c7508f/output.pkl create mode 100644 cache_dir/joblib/run/GridSearch/func_code.py create mode 100644 cache_dir/joblib/run/InitializeEnsemble/72a755383fba437e4dead6ff3e3d81e3/metadata.json create mode 100644 cache_dir/joblib/run/InitializeEnsemble/72a755383fba437e4dead6ff3e3d81e3/output.pkl create mode 100644 cache_dir/joblib/run/InitializeEnsemble/func_code.py create mode 100644 dist/datasets/breast-cancer-wisconsin.csv create mode 100755 dist/datasets/diabetes.csv create mode 100755 dist/datasets/iris.csv delete mode 100755 dist/static/css/app.dfca11727ccf9e74f393b0c223db19ce.css delete mode 100755 dist/static/js/0.a13b6e380cf2b0351445.js delete mode 100755 dist/static/js/0.a13b6e380cf2b0351445.js.map delete mode 100755 dist/static/js/1.5b6be78b138f4f82e219.js delete mode 100755 dist/static/js/1.5b6be78b138f4f82e219.js.map delete mode 100755 dist/static/js/2.f51279a339a43b82ef61.js delete mode 100755 dist/static/js/2.f51279a339a43b82ef61.js.map delete mode 100755 dist/static/js/3.b0ba1fd256aee2a5d235.js delete mode 100755 dist/static/js/3.b0ba1fd256aee2a5d235.js.map delete mode 100755 dist/static/js/app.c1b5e870a4e821397b86.js delete mode 100755 dist/static/js/app.c1b5e870a4e821397b86.js.map delete mode 100755 dist/static/js/manifest.d4a6057c666542329f1b.js delete mode 100755 dist/static/js/manifest.d4a6057c666542329f1b.js.map delete mode 100755 dist/static/js/vendor.1e5687ded8537373b3d4.js delete mode 100755 dist/static/js/vendor.1e5687ded8537373b3d4.js.map delete mode 100644 frontend/shims.d.ts delete mode 100755 frontend/src/assets/logo.png delete mode 100755 frontend/src/components/Home.vue create mode 100644 frontend/src/components/LoadFile.vue create mode 100755 frontend/src/components/Main.vue mode change 100644 => 100755 frontend/src/main.js delete mode 100644 frontend/src/main.js.map delete mode 100755 frontend/src/main.ts mode change 100644 => 100755 frontend/src/router/index.js delete mode 100644 frontend/src/router/index.js.map delete mode 100755 frontend/src/router/index.ts delete mode 100755 frontend/tsconfig.json diff --git a/__pycache__/run.cpython-37.pyc b/__pycache__/run.cpython-37.pyc index cd15aaedb42aa303fb246d2494941066e17a8d16..2893d0758fa27c4ae8483dc8ae0ce9f18f0cf238 100644 GIT binary patch literal 7293 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 hC)#m($3?}OoSzurq{*J)9v=2.10.1 MarkupSafe==1.0 six==1.11.0 -Werkzeug==0.12.2 \ No newline at end of file +Werkzeug==0.12.2 +flask_pymongo==2.3.0 \ No newline at end of file diff --git a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/INSTALLER b/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/INSTALLER similarity index 100% rename from backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/INSTALLER rename to backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/INSTALLER diff --git a/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/LICENSE b/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/LICENSE new file mode 100644 index 000000000..344b3c19d --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2011-2017, Dan Crosta +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/METADATA b/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/METADATA new file mode 100644 index 000000000..60703fae4 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/METADATA @@ -0,0 +1,43 @@ +Metadata-Version: 2.1 +Name: Flask-PyMongo +Version: 2.3.0 +Summary: PyMongo support for Flask applications +Home-page: http://flask-pymongo.readthedocs.org/ +Author: Dan Crosta +Author-email: dcrosta@late.am +License: BSD +Download-URL: https://github.com/dcrosta/flask-pymongo/tags +Platform: any +Classifier: Environment :: Web Environment +Classifier: Framework :: Flask +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 2.7 +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 +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Dist: Flask (>=0.11) +Requires-Dist: PyMongo (>=3.3) + + +Flask-PyMongo +------------- + +MongoDB support for Flask applications. + +Flask-PyMongo is pip-installable: + + $ pip install Flask-PyMongo + +Documentation for Flask-PyMongo is available on `ReadTheDocs +`_. + +Source code is hosted on `GitHub `_. +Contributions are welcome! + + diff --git a/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/RECORD b/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/RECORD new file mode 100644 index 000000000..5defbc42e --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/RECORD @@ -0,0 +1,25 @@ +Flask_PyMongo-2.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Flask_PyMongo-2.3.0.dist-info/LICENSE,sha256=Ap8d3yrzplSqXFfool6NTo87qPxXRHk_ikV97lurKvY,1298 +Flask_PyMongo-2.3.0.dist-info/METADATA,sha256=YAaRh_44jnKkfE9PdipsdhwJeAFMSYdvHBcC0_Uz0Ok,1356 +Flask_PyMongo-2.3.0.dist-info/RECORD,, +Flask_PyMongo-2.3.0.dist-info/WHEEL,sha256=HX-v9-noUkyUoxyZ1PMSuS7auUxDAR4VBdoYLqD0xws,110 +Flask_PyMongo-2.3.0.dist-info/pbr.json,sha256=Uog-jmFMzzyMUmvHpecWUp8hrfTHUIL8FHdBBkk0P6k,47 +Flask_PyMongo-2.3.0.dist-info/top_level.txt,sha256=D0YaRrox4mkWzPhTMnqwIP_A_L1SRd9krRPQCU3dDQU,14 +flask_pymongo/__init__.py,sha256=iiavXfDPhz37a1rM1XsyFn0XBdG4ykgdTsYUKLl9lCY,8968 +flask_pymongo/__pycache__/__init__.cpython-37.pyc,, +flask_pymongo/__pycache__/_version.cpython-37.pyc,, +flask_pymongo/__pycache__/wrappers.cpython-37.pyc,, +flask_pymongo/_version.py,sha256=wB_e6iDNGYA2lGSf9Do9xoBiacRXeGfjda4PNVbM_jk,122 +flask_pymongo/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask_pymongo/tests/__pycache__/__init__.cpython-37.pyc,, +flask_pymongo/tests/__pycache__/test_config.cpython-37.pyc,, +flask_pymongo/tests/__pycache__/test_gridfs.cpython-37.pyc,, +flask_pymongo/tests/__pycache__/test_url_converter.cpython-37.pyc,, +flask_pymongo/tests/__pycache__/test_wrappers.cpython-37.pyc,, +flask_pymongo/tests/__pycache__/util.cpython-37.pyc,, +flask_pymongo/tests/test_config.py,sha256=P6Fw10liyMYUz78e9U4I1ir0Wb-ltxYtdTWYr8WPrSM,3363 +flask_pymongo/tests/test_gridfs.py,sha256=sc70aukyMW9erW0pZZaZoEBKi3dfp1w-AW8OnCR5EDw,3039 +flask_pymongo/tests/test_url_converter.py,sha256=u1Avnps0Cgr6UG0akZnD8mPScJEVfJSvjLVziIouUfY,605 +flask_pymongo/tests/test_wrappers.py,sha256=c-NCD3xuuM5hWyCBlbMIEW9bkOVTTITHegf7AO-UOig,1212 +flask_pymongo/tests/util.py,sha256=XB7xxpDDPRkxYH4gA6v-FtAOo7IGnE8NubAYoDtlZWA,1087 +flask_pymongo/wrappers.py,sha256=A24URUPDchBNiY7qfRO9PKc9UGi6eMzB-FqbHVeChNY,4269 diff --git a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/WHEEL b/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/WHEEL similarity index 70% rename from backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/WHEEL rename to backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/WHEEL index 9dff69d86..c8240f03e 100644 --- a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/WHEEL +++ b/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/WHEEL @@ -1,5 +1,5 @@ Wheel-Version: 1.0 -Generator: bdist_wheel (0.24.0) +Generator: bdist_wheel (0.33.1) Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any diff --git a/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/pbr.json b/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/pbr.json new file mode 100644 index 000000000..e83a7d263 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/pbr.json @@ -0,0 +1 @@ +{"is_release": false, "git_version": "775c8c3"} \ No newline at end of file diff --git a/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/top_level.txt b/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/top_level.txt new file mode 100644 index 000000000..1748c7487 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/Flask_PyMongo-2.3.0.dist-info/top_level.txt @@ -0,0 +1 @@ +flask_pymongo diff --git a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/DESCRIPTION.rst b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/DESCRIPTION.rst deleted file mode 100644 index 3e3abb7f2..000000000 --- a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/DESCRIPTION.rst +++ /dev/null @@ -1,54 +0,0 @@ -Werkzeug -======== - -Werkzeug started as simple collection of various utilities for WSGI -applications and has become one of the most advanced WSGI utility -modules. It includes a powerful debugger, full featured request and -response objects, HTTP utilities to handle entity tags, cache control -headers, HTTP dates, cookie handling, file uploads, a powerful URL -routing system and a bunch of community contributed addon modules. - -Werkzeug is unicode aware and doesn't enforce a specific template -engine, database adapter or anything else. It doesn't even enforce -a specific way of handling requests and leaves all that up to the -developer. It's most useful for end user applications which should work -on as many server environments as possible (such as blogs, wikis, -bulletin boards, etc.). - -Details and example applications are available on the -`Werkzeug website `_. - - -Features --------- - -- unicode awareness - -- request and response objects - -- various utility functions for dealing with HTTP headers such as - `Accept` and `Cache-Control` headers. - -- thread local objects with proper cleanup at request end - -- an interactive debugger - -- A simple WSGI server with support for threading and forking - with an automatic reloader. - -- a flexible URL routing system with REST support. - -- fully WSGI compatible - - -Development Version -------------------- - -The Werkzeug development version can be installed by cloning the git -repository from `github`_:: - - git clone git@github.com:pallets/werkzeug.git - -.. _github: http://github.com/pallets/werkzeug - - diff --git a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/LICENSE.txt b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/LICENSE.txt deleted file mode 100644 index 1c2e0b7dc..000000000 --- a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/LICENSE.txt +++ /dev/null @@ -1,29 +0,0 @@ -Copyright (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * The names of the contributors may not be used to endorse or - promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/METADATA b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/METADATA deleted file mode 100644 index 52e12a4f0..000000000 --- a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/METADATA +++ /dev/null @@ -1,83 +0,0 @@ -Metadata-Version: 2.0 -Name: Werkzeug -Version: 0.12.2 -Summary: The Swiss Army knife of Python web development -Home-page: http://werkzeug.pocoo.org/ -Author: Armin Ronacher -Author-email: armin.ronacher@active-4.com -License: BSD -Platform: any -Classifier: Development Status :: 5 - Production/Stable -Classifier: Environment :: Web Environment -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.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: Topic :: Internet :: WWW/HTTP :: Dynamic Content -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Provides-Extra: termcolor -Requires-Dist: termcolor; extra == 'termcolor' -Provides-Extra: watchdog -Requires-Dist: watchdog; extra == 'watchdog' - -Werkzeug -======== - -Werkzeug started as simple collection of various utilities for WSGI -applications and has become one of the most advanced WSGI utility -modules. It includes a powerful debugger, full featured request and -response objects, HTTP utilities to handle entity tags, cache control -headers, HTTP dates, cookie handling, file uploads, a powerful URL -routing system and a bunch of community contributed addon modules. - -Werkzeug is unicode aware and doesn't enforce a specific template -engine, database adapter or anything else. It doesn't even enforce -a specific way of handling requests and leaves all that up to the -developer. It's most useful for end user applications which should work -on as many server environments as possible (such as blogs, wikis, -bulletin boards, etc.). - -Details and example applications are available on the -`Werkzeug website `_. - - -Features --------- - -- unicode awareness - -- request and response objects - -- various utility functions for dealing with HTTP headers such as - `Accept` and `Cache-Control` headers. - -- thread local objects with proper cleanup at request end - -- an interactive debugger - -- A simple WSGI server with support for threading and forking - with an automatic reloader. - -- a flexible URL routing system with REST support. - -- fully WSGI compatible - - -Development Version -------------------- - -The Werkzeug development version can be installed by cloning the git -repository from `github`_:: - - git clone git@github.com:pallets/werkzeug.git - -.. _github: http://github.com/pallets/werkzeug - - diff --git a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/RECORD b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/RECORD deleted file mode 100644 index fa5d024cd..000000000 --- a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/RECORD +++ /dev/null @@ -1,95 +0,0 @@ -Werkzeug-0.12.2.dist-info/DESCRIPTION.rst,sha256=z9r9xqJ0fYSAn1Tz7KRBdFGDerL2y4pHWSW_72pUgTc,1591 -Werkzeug-0.12.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -Werkzeug-0.12.2.dist-info/LICENSE.txt,sha256=F84h8-PZAuC-Hq-_252D3yhH6mqIc-WUbXUPbfOtjXM,1532 -Werkzeug-0.12.2.dist-info/METADATA,sha256=SphYykCCskmOJK7mV1-M2T1PTOrx5K3DJ8n3E5jA298,2738 -Werkzeug-0.12.2.dist-info/RECORD,, -Werkzeug-0.12.2.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110 -Werkzeug-0.12.2.dist-info/metadata.json,sha256=6taKobd3cQ5zOY5MVKlvuCJGaX7VPLaHYuRwzwwkORI,1276 -Werkzeug-0.12.2.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9 -werkzeug/__init__.py,sha256=NDY8HsYsT3dguTLu4MhuH-GpQE5XS9aKhrdfwHnzOEk,6864 -werkzeug/__pycache__/__init__.cpython-37.pyc,, -werkzeug/__pycache__/_compat.cpython-37.pyc,, -werkzeug/__pycache__/_internal.cpython-37.pyc,, -werkzeug/__pycache__/_reloader.cpython-37.pyc,, -werkzeug/__pycache__/datastructures.cpython-37.pyc,, -werkzeug/__pycache__/exceptions.cpython-37.pyc,, -werkzeug/__pycache__/filesystem.cpython-37.pyc,, -werkzeug/__pycache__/formparser.cpython-37.pyc,, -werkzeug/__pycache__/http.cpython-37.pyc,, -werkzeug/__pycache__/local.cpython-37.pyc,, -werkzeug/__pycache__/posixemulation.cpython-37.pyc,, -werkzeug/__pycache__/routing.cpython-37.pyc,, -werkzeug/__pycache__/script.cpython-37.pyc,, -werkzeug/__pycache__/security.cpython-37.pyc,, -werkzeug/__pycache__/serving.cpython-37.pyc,, -werkzeug/__pycache__/test.cpython-37.pyc,, -werkzeug/__pycache__/testapp.cpython-37.pyc,, -werkzeug/__pycache__/urls.cpython-37.pyc,, -werkzeug/__pycache__/useragents.cpython-37.pyc,, -werkzeug/__pycache__/utils.cpython-37.pyc,, -werkzeug/__pycache__/wrappers.cpython-37.pyc,, -werkzeug/__pycache__/wsgi.cpython-37.pyc,, -werkzeug/_compat.py,sha256=8c4U9o6A_TR9nKCcTbpZNxpqCXcXDVIbFawwKM2s92c,6311 -werkzeug/_internal.py,sha256=sE2JbLnMzN9mRI1iipTYWrFAGEWaZVECqtHAiNEhqUE,13841 -werkzeug/_reloader.py,sha256=NkIXQCTa6b22wWLpXob_jIVUxux8LtAsfWehLkKt0iM,8816 -werkzeug/contrib/__init__.py,sha256=f7PfttZhbrImqpr5Ezre8CXgwvcGUJK7zWNpO34WWrw,623 -werkzeug/contrib/__pycache__/__init__.cpython-37.pyc,, -werkzeug/contrib/__pycache__/atom.cpython-37.pyc,, -werkzeug/contrib/__pycache__/cache.cpython-37.pyc,, -werkzeug/contrib/__pycache__/fixers.cpython-37.pyc,, -werkzeug/contrib/__pycache__/iterio.cpython-37.pyc,, -werkzeug/contrib/__pycache__/jsrouting.cpython-37.pyc,, -werkzeug/contrib/__pycache__/limiter.cpython-37.pyc,, -werkzeug/contrib/__pycache__/lint.cpython-37.pyc,, -werkzeug/contrib/__pycache__/profiler.cpython-37.pyc,, -werkzeug/contrib/__pycache__/securecookie.cpython-37.pyc,, -werkzeug/contrib/__pycache__/sessions.cpython-37.pyc,, -werkzeug/contrib/__pycache__/testtools.cpython-37.pyc,, -werkzeug/contrib/__pycache__/wrappers.cpython-37.pyc,, -werkzeug/contrib/atom.py,sha256=qqfJcfIn2RYY-3hO3Oz0aLq9YuNubcPQ_KZcNsDwVJo,15575 -werkzeug/contrib/cache.py,sha256=nyUUxsS0MTHiFmu-481y9PHd8NvWH5pzCoEX1yA0mHY,30341 -werkzeug/contrib/fixers.py,sha256=gR06T-w71ur-tHQ_31kP_4jpOncPJ4Wc1dOqTvYusr8,10179 -werkzeug/contrib/iterio.py,sha256=RlqDvGhz0RneTpzE8dVc-yWCUv4nkPl1jEc_EDp2fH0,10814 -werkzeug/contrib/jsrouting.py,sha256=QTmgeDoKXvNK02KzXgx9lr3cAH6fAzpwF5bBdPNvJPs,8564 -werkzeug/contrib/limiter.py,sha256=iS8-ahPZ-JLRnmfIBzxpm7O_s3lPsiDMVWv7llAIDCI,1334 -werkzeug/contrib/lint.py,sha256=qZlmqiWJ5tQJOEzLnPmHWA8eUEpcBIWkAb_V2RKJg4o,12558 -werkzeug/contrib/profiler.py,sha256=ISwCWvwVyGpDLRBRpLjo_qUWma6GXYBrTAco4PEQSHY,5151 -werkzeug/contrib/securecookie.py,sha256=bDsAJmslkwmXjycnPjEjWtfLBvhz0ud4z3k7tdezUVs,12174 -werkzeug/contrib/sessions.py,sha256=39LVNvLbm5JWpbxM79WC2l87MJFbqeISARjwYbkJatw,12577 -werkzeug/contrib/testtools.py,sha256=G9xN-qeihJlhExrIZMCahvQOIDxdL9NiX874jiiHFMs,2453 -werkzeug/contrib/wrappers.py,sha256=v7OYlz7wQtDlS9fey75UiRZ1IkUWqCpzbhsLy4k14Hw,10398 -werkzeug/datastructures.py,sha256=rq0zICISMUetS3xvUVvrhIvyue9oUzrs_NU3b83zwuQ,89066 -werkzeug/debug/__init__.py,sha256=GTsOsjE3PqUAlsUVm2Mgc_KWA2kjjSsUz0JsM7Qu41w,17266 -werkzeug/debug/__pycache__/__init__.cpython-37.pyc,, -werkzeug/debug/__pycache__/console.cpython-37.pyc,, -werkzeug/debug/__pycache__/repr.cpython-37.pyc,, -werkzeug/debug/__pycache__/tbtools.cpython-37.pyc,, -werkzeug/debug/console.py,sha256=n3-dsKk1TsjnN-u4ZgmuWCU_HO0qw5IA7ttjhyyMM6I,5607 -werkzeug/debug/repr.py,sha256=bKqstDYGfECpeLerd48s_hxuqK4b6UWnjMu3d_DHO8I,9340 -werkzeug/debug/shared/FONT_LICENSE,sha256=LwAVEI1oYnvXiNMT9SnCH_TaLCxCpeHziDrMg0gPkAI,4673 -werkzeug/debug/shared/console.png,sha256=bxax6RXXlvOij_KeqvSNX0ojJf83YbnZ7my-3Gx9w2A,507 -werkzeug/debug/shared/debugger.js,sha256=PKPVYuyO4SX1hkqLOwCLvmIEO5154WatFYaXE-zIfKI,6264 -werkzeug/debug/shared/jquery.js,sha256=7LkWEzqTdpEfELxcZZlS6wAx5Ff13zZ83lYO2_ujj7g,95957 -werkzeug/debug/shared/less.png,sha256=-4-kNRaXJSONVLahrQKUxMwXGm9R4OnZ9SxDGpHlIR4,191 -werkzeug/debug/shared/more.png,sha256=GngN7CioHQoV58rH6ojnkYi8c_qED2Aka5FO5UXrReY,200 -werkzeug/debug/shared/source.png,sha256=RoGcBTE4CyCB85GBuDGTFlAnUqxwTBiIfDqW15EpnUQ,818 -werkzeug/debug/shared/style.css,sha256=IEO0PC2pWmh2aEyGCaN--txuWsRCliuhlbEhPDFwh0A,6270 -werkzeug/debug/shared/ubuntu.ttf,sha256=1eaHFyepmy4FyDvjLVzpITrGEBu_CZYY94jE0nED1c0,70220 -werkzeug/debug/tbtools.py,sha256=rBudXCmkVdAKIcdhxANxgf09g6kQjJWW9_5bjSpr4OY,18451 -werkzeug/exceptions.py,sha256=3wp95Hqj9FqV8MdikV99JRcHse_fSMn27V8tgP5Hw2c,20505 -werkzeug/filesystem.py,sha256=hHWeWo_gqLMzTRfYt8-7n2wWcWUNTnDyudQDLOBEICE,2175 -werkzeug/formparser.py,sha256=DxN53eOCb6i7PxqtldrF2Kv9Mx00BqW297N4t-RxkWE,21241 -werkzeug/http.py,sha256=nrk-ASJzcKOuoBEz274TWA8jKt0CQSOBZuP_A0UASTA,36658 -werkzeug/local.py,sha256=QdQhWV5L8p1Y1CJ1CDStwxaUs24SuN5aebHwjVD08C8,14553 -werkzeug/posixemulation.py,sha256=xEF2Bxc-vUCPkiu4IbfWVd3LW7DROYAT-ExW6THqyzw,3519 -werkzeug/routing.py,sha256=g25wg0GNfff8WcfRlc1ZxTGvz1KbVj09w2S7wxopseQ,66746 -werkzeug/script.py,sha256=Jh9OAktqjLNc_IBBUatVM7uP5LDcbxaYA8n2ObnS4bo,11666 -werkzeug/security.py,sha256=Z0v0ojdo7T4FNyfIjx86BFQKwasy3ZR9euikIJDQYP8,9191 -werkzeug/serving.py,sha256=aAS3EgiD-VjemsYfSf1yqdjaGEfpB4I3M4PKlLotJLo,29069 -werkzeug/test.py,sha256=xnabNSpty66ftZiXHcoZaYFP1E4WUNxydw5Oe8Mjhoo,34795 -werkzeug/testapp.py,sha256=3HQRW1sHZKXuAjCvFMet4KXtQG3loYTFnvn6LWt-4zI,9396 -werkzeug/urls.py,sha256=fSbI4Gb29_p02Zk21VAZQRN1QdOVY9CNTgpb2rbajNQ,36710 -werkzeug/useragents.py,sha256=Ck3G977Y0Rzdk9wFcLpL0PyOrONtdK1_d2Zexb78cX4,5640 -werkzeug/utils.py,sha256=lkybtv_mq35zV1qhelvEcILTzrMUwZ9yon6E8XwapJE,22972 -werkzeug/wrappers.py,sha256=wceh1RhvhIZVzKuok3XMQ5jqjYYCEYv5JqKY3Nc_oRY,82986 -werkzeug/wsgi.py,sha256=TjPo5ups3NI1RVVGdMvd3XaceqFtqlMX5X169gWWFrQ,42838 diff --git a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/metadata.json b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/metadata.json deleted file mode 100644 index d77fbb8dd..000000000 --- a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"license": "BSD", "name": "Werkzeug", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "summary": "The Swiss Army knife of Python web development", "platform": "any", "run_requires": [{"requires": ["watchdog"], "extra": "watchdog"}, {"requires": ["termcolor"], "extra": "termcolor"}], "version": "0.12.2", "extensions": {"python.details": {"project_urls": {"Home": "http://werkzeug.pocoo.org/"}, "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "contacts": [{"role": "author", "email": "armin.ronacher@active-4.com", "name": "Armin Ronacher"}]}}, "classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules"], "extras": ["termcolor", "watchdog"]} \ No newline at end of file diff --git a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/INSTALLER b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/LICENSE.rst b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/LICENSE.rst new file mode 100644 index 000000000..c37cae49e --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/METADATA b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/METADATA new file mode 100644 index 000000000..ae87c9294 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/METADATA @@ -0,0 +1,133 @@ +Metadata-Version: 2.1 +Name: Werkzeug +Version: 0.15.4 +Summary: The comprehensive WSGI web application library. +Home-page: https://palletsprojects.com/p/werkzeug/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: The Pallets Team +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Documentation, https://werkzeug.palletsprojects.com/ +Project-URL: Code, https://github.com/pallets/werkzeug +Project-URL: Issue tracker, https://github.com/pallets/werkzeug/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* +Provides-Extra: dev +Requires-Dist: pytest ; extra == 'dev' +Requires-Dist: coverage ; extra == 'dev' +Requires-Dist: tox ; extra == 'dev' +Requires-Dist: sphinx ; extra == 'dev' +Requires-Dist: pallets-sphinx-themes ; extra == 'dev' +Requires-Dist: sphinx-issues ; extra == 'dev' +Provides-Extra: termcolor +Requires-Dist: termcolor ; extra == 'termcolor' +Provides-Extra: watchdog +Requires-Dist: watchdog ; extra == 'watchdog' + +Werkzeug +======== + +*werkzeug* German noun: "tool". Etymology: *werk* ("work"), *zeug* ("stuff") + +Werkzeug is a comprehensive `WSGI`_ web application library. It began as +a simple collection of various utilities for WSGI applications and has +become one of the most advanced WSGI utility libraries. + +It includes: + +- An interactive debugger that allows inspecting stack traces and + source code in the browser with an interactive interpreter for any + frame in the stack. +- A full-featured request object with objects to interact with + headers, query args, form data, files, and cookies. +- A response object that can wrap other WSGI applications and handle + streaming data. +- A routing system for matching URLs to endpoints and generating URLs + for endpoints, with an extensible system for capturing variables + from URLs. +- HTTP utilities to handle entity tags, cache control, dates, user + agents, cookies, files, and more. +- A threaded WSGI server for use while developing applications + locally. +- A test client for simulating HTTP requests during testing without + requiring running a server. + +Werkzeug is Unicode aware and doesn't enforce any dependencies. It is up +to the developer to choose a template engine, database adapter, and even +how to handle requests. It can be used to build all sorts of end user +applications such as blogs, wikis, or bulletin boards. + +`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while +providing more structure and patterns for defining powerful +applications. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U Werkzeug + + +A Simple Example +---------------- + +.. code-block:: python + + from werkzeug.wrappers import Request, Response + + @Request.application + def application(request): + return Response('Hello, World!') + + if __name__ == '__main__': + from werkzeug.serving import run_simple + run_simple('localhost', 4000, application) + + +Links +----- + +- Website: https://www.palletsprojects.com/p/werkzeug/ +- Documentation: https://werkzeug.palletsprojects.com/ +- Releases: https://pypi.org/project/Werkzeug/ +- Code: https://github.com/pallets/werkzeug +- Issue tracker: https://github.com/pallets/werkzeug/issues +- Test status: + + - Linux, Mac: https://travis-ci.org/pallets/werkzeug + - Windows: https://ci.appveyor.com/project/pallets/werkzeug + +- Test coverage: https://codecov.io/gh/pallets/werkzeug +- Official chat: https://discord.gg/t6rrQZH + +.. _WSGI: https://wsgi.readthedocs.io/en/latest/ +.. _Flask: https://www.palletsprojects.com/p/flask/ +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + diff --git a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/RECORD b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/RECORD new file mode 100644 index 000000000..91578f11b --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/RECORD @@ -0,0 +1,119 @@ +Werkzeug-0.15.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Werkzeug-0.15.4.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Werkzeug-0.15.4.dist-info/METADATA,sha256=BFCzIm51w-fKsnG13iHz4No7Upu9-y_vJUq_rkY_bqY,4864 +Werkzeug-0.15.4.dist-info/RECORD,, +Werkzeug-0.15.4.dist-info/WHEEL,sha256=HX-v9-noUkyUoxyZ1PMSuS7auUxDAR4VBdoYLqD0xws,110 +Werkzeug-0.15.4.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9 +werkzeug/__init__.py,sha256=LBPc9xBJsifxGUsUY5EkreS0o_D8ixJD3YYSh9ARtxE,6805 +werkzeug/__pycache__/__init__.cpython-37.pyc,, +werkzeug/__pycache__/_compat.cpython-37.pyc,, +werkzeug/__pycache__/_internal.cpython-37.pyc,, +werkzeug/__pycache__/_reloader.cpython-37.pyc,, +werkzeug/__pycache__/datastructures.cpython-37.pyc,, +werkzeug/__pycache__/exceptions.cpython-37.pyc,, +werkzeug/__pycache__/filesystem.cpython-37.pyc,, +werkzeug/__pycache__/formparser.cpython-37.pyc,, +werkzeug/__pycache__/http.cpython-37.pyc,, +werkzeug/__pycache__/local.cpython-37.pyc,, +werkzeug/__pycache__/posixemulation.cpython-37.pyc,, +werkzeug/__pycache__/routing.cpython-37.pyc,, +werkzeug/__pycache__/security.cpython-37.pyc,, +werkzeug/__pycache__/serving.cpython-37.pyc,, +werkzeug/__pycache__/test.cpython-37.pyc,, +werkzeug/__pycache__/testapp.cpython-37.pyc,, +werkzeug/__pycache__/urls.cpython-37.pyc,, +werkzeug/__pycache__/useragents.cpython-37.pyc,, +werkzeug/__pycache__/utils.cpython-37.pyc,, +werkzeug/__pycache__/wsgi.cpython-37.pyc,, +werkzeug/_compat.py,sha256=oBEVVrJT4sqYdIZbUWmgV9T9w257RhTSDBlTjh0Zbb0,6431 +werkzeug/_internal.py,sha256=Wx7cpTRWqeBd0LAqobo0lCO4pNUW4oav6XKf7Taumgk,14590 +werkzeug/_reloader.py,sha256=8B8T1npsQT-96nGeVJjV1KXWK_ong6ZlTXOWgxfRLpg,11241 +werkzeug/contrib/__init__.py,sha256=EvNyiiCF49j5P0fZYJ3ZGe82ofXdSBvUNqWFwwBMibQ,553 +werkzeug/contrib/__pycache__/__init__.cpython-37.pyc,, +werkzeug/contrib/__pycache__/atom.cpython-37.pyc,, +werkzeug/contrib/__pycache__/cache.cpython-37.pyc,, +werkzeug/contrib/__pycache__/fixers.cpython-37.pyc,, +werkzeug/contrib/__pycache__/iterio.cpython-37.pyc,, +werkzeug/contrib/__pycache__/lint.cpython-37.pyc,, +werkzeug/contrib/__pycache__/profiler.cpython-37.pyc,, +werkzeug/contrib/__pycache__/securecookie.cpython-37.pyc,, +werkzeug/contrib/__pycache__/sessions.cpython-37.pyc,, +werkzeug/contrib/__pycache__/wrappers.cpython-37.pyc,, +werkzeug/contrib/atom.py,sha256=KpPJcTfzNW1J0VNQckCbVtVGBe3V8s451tOUya4qByI,15415 +werkzeug/contrib/cache.py,sha256=AEh5UIw-Ui7sHZnlpvrD7ueOKUhCaAD55FXiPtXbbRs,32115 +werkzeug/contrib/fixers.py,sha256=peEtAiIWYT5bh00EWEPOGKzGZXivOzVhhzKPvvzk1RM,9193 +werkzeug/contrib/iterio.py,sha256=KKHa_8aCF_uhoeQVyPGUwrivuB6y6nNdXYo2D2vzOA8,10928 +werkzeug/contrib/lint.py,sha256=NdIxP0E2kVt1xDIxoaIz3Rcl8ZdgmHaFbGTOaybGpN4,296 +werkzeug/contrib/profiler.py,sha256=k_oMLU-AtsVvQ9TxNdermY6FuzSTYr-WE-ZmWb_DMyU,1229 +werkzeug/contrib/securecookie.py,sha256=xbtElskGmtbiApgOJ5WhGgqGDs_68_PcWzqDIAY_QZY,13076 +werkzeug/contrib/sessions.py,sha256=oVXh_7-6_CWOMxDKqcaK05H8RpYoWqAd3al-KzMFPYs,13042 +werkzeug/contrib/wrappers.py,sha256=ZmNk0wpzD66yomPnQxapndZQs4c0kNJaRzqI-BVxeQk,13199 +werkzeug/datastructures.py,sha256=8HoA4Gu9i7ZWi5OBjx244OLWvDEE4JTQQUUTRoAYKog,91761 +werkzeug/debug/__init__.py,sha256=Bo3HvgTNY4NQ_2jROTSk3r1ScZcT_g_4EnuHTjKyrKM,18275 +werkzeug/debug/__pycache__/__init__.cpython-37.pyc,, +werkzeug/debug/__pycache__/console.cpython-37.pyc,, +werkzeug/debug/__pycache__/repr.cpython-37.pyc,, +werkzeug/debug/__pycache__/tbtools.cpython-37.pyc,, +werkzeug/debug/console.py,sha256=HoBL21bbcmtiCLqiLDJLZi1LYnWMZxjoXYH5WaZB1XY,5469 +werkzeug/debug/repr.py,sha256=lIwuhbyrMwVe3P_cFqNyqzHL7P93TLKod7lw9clydEw,9621 +werkzeug/debug/shared/FONT_LICENSE,sha256=LwAVEI1oYnvXiNMT9SnCH_TaLCxCpeHziDrMg0gPkAI,4673 +werkzeug/debug/shared/console.png,sha256=bxax6RXXlvOij_KeqvSNX0ojJf83YbnZ7my-3Gx9w2A,507 +werkzeug/debug/shared/debugger.js,sha256=rOhqZMRfpZnnu6_XCGn6wMWPhtfwRAcyZKksdIxPJas,6400 +werkzeug/debug/shared/jquery.js,sha256=FgpCb_KJQlLNfOu91ta32o_NMZxltwRo8QtmkMRdAu8,86927 +werkzeug/debug/shared/less.png,sha256=-4-kNRaXJSONVLahrQKUxMwXGm9R4OnZ9SxDGpHlIR4,191 +werkzeug/debug/shared/more.png,sha256=GngN7CioHQoV58rH6ojnkYi8c_qED2Aka5FO5UXrReY,200 +werkzeug/debug/shared/source.png,sha256=RoGcBTE4CyCB85GBuDGTFlAnUqxwTBiIfDqW15EpnUQ,818 +werkzeug/debug/shared/style.css,sha256=_Y98F6dR2CBUZNKylsOdgSHjwVaVy717WqE3-xJVcmE,6581 +werkzeug/debug/shared/ubuntu.ttf,sha256=1eaHFyepmy4FyDvjLVzpITrGEBu_CZYY94jE0nED1c0,70220 +werkzeug/debug/tbtools.py,sha256=SkAAA4KKfwsXJinUbf-AEP4GqONTsR4uU7WPUloXcSE,20318 +werkzeug/exceptions.py,sha256=SNbLn_vitnNiG_nwirLT0k6Nf6CZ_1R0Fyk_ub2XCbE,23230 +werkzeug/filesystem.py,sha256=HzKl-j0Hd8Jl66j778UbPTAYNnY6vUZgYLlBZ0e7uw0,2101 +werkzeug/formparser.py,sha256=tN6SO4mn6RUsxRZq4qVBWXbNWNuasn2KaBznTieMaVk,21790 +werkzeug/http.py,sha256=t0ET2tySAf9ZWdEelVWJoLaZzFViYpjoUmiYHPz10-E,43304 +werkzeug/local.py,sha256=USVEcgIg-oCiUJFPIecFIW9jkIejfw4Fjf1u5yN-Np4,14456 +werkzeug/middleware/__init__.py,sha256=f1SFZo67IlW4k1uqKzNHxYQlsakUS-D6KK_j0e3jjwQ,549 +werkzeug/middleware/__pycache__/__init__.cpython-37.pyc,, +werkzeug/middleware/__pycache__/dispatcher.cpython-37.pyc,, +werkzeug/middleware/__pycache__/http_proxy.cpython-37.pyc,, +werkzeug/middleware/__pycache__/lint.cpython-37.pyc,, +werkzeug/middleware/__pycache__/profiler.cpython-37.pyc,, +werkzeug/middleware/__pycache__/proxy_fix.cpython-37.pyc,, +werkzeug/middleware/__pycache__/shared_data.cpython-37.pyc,, +werkzeug/middleware/dispatcher.py,sha256=_-KoMzHtcISHS7ouWKAOraqlCLprdh83YOAn_8DjLp8,2240 +werkzeug/middleware/http_proxy.py,sha256=lRjTdMmghHiZuZrS7_UJ3gZc-vlFizhBbFZ-XZPLwIA,7117 +werkzeug/middleware/lint.py,sha256=ItTwuWJnflF8xMT1uqU_Ty1ryhux-CjeUfskqaUpxsw,12967 +werkzeug/middleware/profiler.py,sha256=8B_s23d6BGrU_q54gJsm6kcCbOJbTSqrXCsioHON0Xs,4471 +werkzeug/middleware/proxy_fix.py,sha256=Y86VcU2oAQ--x0mi4iFVJyEFMzp3Ao8q0zvr_SsrpNw,8506 +werkzeug/middleware/shared_data.py,sha256=6aUzMABeOLul0Krf5S_hs-T7oUc7ZIQ3B8tAO4p8C7E,8541 +werkzeug/posixemulation.py,sha256=gSSiv1SCmOyzOM_nq1ZaZCtxP__C5MeDJl_4yXJmi4Q,3541 +werkzeug/routing.py,sha256=51zsLuN3qZcpRxpy3K3XoEuL8kyFfuqo28MquJsjZjw,72902 +werkzeug/security.py,sha256=mfxfcM-D6U8LhsyDK5W_rnL1oVTZWgyt-E8E4FlSdrI,8026 +werkzeug/serving.py,sha256=tUFUMg7Bj9iw3nA8ZgC_czMDJJKN7vFskajEmgEFhzE,36597 +werkzeug/test.py,sha256=Cnb5xa3vLDL0hzFCH1fkG_YRpndViGQgCh4D744iSQk,40645 +werkzeug/testapp.py,sha256=hcKBzorVlSHC-uGvGXXjCm3FzCwGWq4yjbTG3Pr7MV8,9301 +werkzeug/urls.py,sha256=8yHdYI99N__-isoTwvGqvuj9QhOh66dd1Xh1DIp0q0g,39261 +werkzeug/useragents.py,sha256=FIonyUF790Ro8OG8cJqG1zixhg5YzXdHmkZbrnK0QRo,5965 +werkzeug/utils.py,sha256=O20Y0qWk5O1IWamC_A5gkmzR5cgBd3yDIHviwBTfNB0,27387 +werkzeug/wrappers/__init__.py,sha256=S4VioKAmF_av9Ec9zQvG71X1EOkYfPx1TYck9jyDiyY,1384 +werkzeug/wrappers/__pycache__/__init__.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/accept.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/auth.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/base_request.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/base_response.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/common_descriptors.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/etag.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/json.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/request.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/response.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/user_agent.cpython-37.pyc,, +werkzeug/wrappers/accept.py,sha256=TIvjUc0g73fhTWX54wg_D9NNzKvpnG1X8u1w26tK1o8,1760 +werkzeug/wrappers/auth.py,sha256=Pmn6iaGHBrUyHbJpW0lZhO_q9RVoAa5QalaTqcavdAI,1158 +werkzeug/wrappers/base_request.py,sha256=k5mu1UU99X_xrPqmXj44pzJbkPRpgvwMuP2j9vl8QFU,26873 +werkzeug/wrappers/base_response.py,sha256=ZA1XlxtsbvG4SpbdOEMT5--z7aZM0w6C5y33W8wOXa4,27906 +werkzeug/wrappers/common_descriptors.py,sha256=OJ8jOwMun4L-BxCuFPkK1vaefx_-Y5IndVXvvn_ems4,12089 +werkzeug/wrappers/etag.py,sha256=TwMO1fvluXbBqnFTj2DvrCNa3mYhbHYe1UZAVzfXvuU,12533 +werkzeug/wrappers/json.py,sha256=HvK_A4NpO0sLqgb10sTJcoZydYOwyNiPCJPV7SVgcgE,4343 +werkzeug/wrappers/request.py,sha256=qPo2zmmBv4HxboywtWZb2pJL8OPXo07BUXBKw2j9Fi8,1338 +werkzeug/wrappers/response.py,sha256=vDZFEGzDOG0jjmS0uVVjeT3hqRt1hFaf15npnx7RD28,2329 +werkzeug/wrappers/user_agent.py,sha256=4bTgQKTLQmGUyxOREYOzbeiFP2VwIOE7E14AhUB5NqM,444 +werkzeug/wsgi.py,sha256=h-zyAeInwE6X6ciSnHI14ImA85adV-F861PmR7UGtRk,36681 diff --git a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/WHEEL b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/WHEEL new file mode 100644 index 000000000..c8240f03e --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.1) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/top_level.txt b/backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/top_level.txt similarity index 100% rename from backend/venv/lib/python3.7/site-packages/Werkzeug-0.12.2.dist-info/top_level.txt rename to backend/venv/lib/python3.7/site-packages/Werkzeug-0.15.4.dist-info/top_level.txt diff --git a/backend/venv/lib/python3.7/site-packages/bson/__init__.py b/backend/venv/lib/python3.7/site-packages/bson/__init__.py new file mode 100644 index 000000000..c802cbfcb --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/__init__.py @@ -0,0 +1,1170 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""BSON (Binary JSON) encoding and decoding. + +The mapping from Python types to BSON types is as follows: + +======================================= ============= =================== +Python Type BSON Type Supported Direction +======================================= ============= =================== +None null both +bool boolean both +int [#int]_ int32 / int64 py -> bson +long int64 py -> bson +`bson.int64.Int64` int64 both +float number (real) both +string string py -> bson +unicode string both +list array both +dict / `SON` object both +datetime.datetime [#dt]_ [#dt2]_ date both +`bson.regex.Regex` regex both +compiled re [#re]_ regex py -> bson +`bson.binary.Binary` binary both +`bson.objectid.ObjectId` oid both +`bson.dbref.DBRef` dbref both +None undefined bson -> py +unicode code bson -> py +`bson.code.Code` code py -> bson +unicode symbol bson -> py +bytes (Python 3) [#bytes]_ binary both +======================================= ============= =================== + +Note that, when using Python 2.x, to save binary data it must be wrapped as +an instance of `bson.binary.Binary`. Otherwise it will be saved as a BSON +string and retrieved as unicode. Users of Python 3.x can use the Python bytes +type. + +.. [#int] A Python int will be saved as a BSON int32 or BSON int64 depending + on its size. A BSON int32 will always decode to a Python int. A BSON + int64 will always decode to a :class:`~bson.int64.Int64`. +.. [#dt] datetime.datetime instances will be rounded to the nearest + millisecond when saved +.. [#dt2] all datetime.datetime instances are treated as *naive*. clients + should always use UTC. +.. [#re] :class:`~bson.regex.Regex` instances and regular expression + objects from ``re.compile()`` are both saved as BSON regular expressions. + BSON regular expressions are decoded as :class:`~bson.regex.Regex` + instances. +.. [#bytes] The bytes type from Python 3.x is encoded as BSON binary with + subtype 0. In Python 3.x it will be decoded back to bytes. In Python 2.x + it will be decoded to an instance of :class:`~bson.binary.Binary` with + subtype 0. +""" + +import calendar +import datetime +import itertools +import re +import struct +import sys +import uuid + +from codecs import (utf_8_decode as _utf_8_decode, + utf_8_encode as _utf_8_encode) + +from bson.binary import (Binary, OLD_UUID_SUBTYPE, + JAVA_LEGACY, CSHARP_LEGACY, + UUIDLegacy) +from bson.code import Code +from bson.codec_options import ( + CodecOptions, DEFAULT_CODEC_OPTIONS, _raw_document_class) +from bson.dbref import DBRef +from bson.decimal128 import Decimal128 +from bson.errors import (InvalidBSON, + InvalidDocument, + InvalidStringData) +from bson.int64 import Int64 +from bson.max_key import MaxKey +from bson.min_key import MinKey +from bson.objectid import ObjectId +from bson.py3compat import (abc, + b, + PY3, + iteritems, + text_type, + string_type, + reraise) +from bson.regex import Regex +from bson.son import SON, RE_TYPE +from bson.timestamp import Timestamp +from bson.tz_util import utc + + +try: + from bson import _cbson + _USE_C = True +except ImportError: + _USE_C = False + + +EPOCH_AWARE = datetime.datetime.fromtimestamp(0, utc) +EPOCH_NAIVE = datetime.datetime.utcfromtimestamp(0) + + +BSONNUM = b"\x01" # Floating point +BSONSTR = b"\x02" # UTF-8 string +BSONOBJ = b"\x03" # Embedded document +BSONARR = b"\x04" # Array +BSONBIN = b"\x05" # Binary +BSONUND = b"\x06" # Undefined +BSONOID = b"\x07" # ObjectId +BSONBOO = b"\x08" # Boolean +BSONDAT = b"\x09" # UTC Datetime +BSONNUL = b"\x0A" # Null +BSONRGX = b"\x0B" # Regex +BSONREF = b"\x0C" # DBRef +BSONCOD = b"\x0D" # Javascript code +BSONSYM = b"\x0E" # Symbol +BSONCWS = b"\x0F" # Javascript code with scope +BSONINT = b"\x10" # 32bit int +BSONTIM = b"\x11" # Timestamp +BSONLON = b"\x12" # 64bit int +BSONDEC = b"\x13" # Decimal128 +BSONMIN = b"\xFF" # Min key +BSONMAX = b"\x7F" # Max key + + +_UNPACK_FLOAT = struct.Struct("= obj_end: + raise InvalidBSON("invalid object length") + # If this is the top-level document, validate the total size too. + if position == 0 and obj_size != obj_end: + raise InvalidBSON("invalid object length") + return obj_size, end + + +def _get_object(data, position, obj_end, opts, dummy): + """Decode a BSON subdocument to opts.document_class or bson.dbref.DBRef.""" + obj_size, end = _get_object_size(data, position, obj_end) + if _raw_document_class(opts.document_class): + return (opts.document_class(data[position:end + 1], opts), + position + obj_size) + + obj = _elements_to_dict(data, position + 4, end, opts) + + position += obj_size + if "$ref" in obj: + return (DBRef(obj.pop("$ref"), obj.pop("$id", None), + obj.pop("$db", None), obj), position) + return obj, position + + +def _get_array(data, position, obj_end, opts, element_name): + """Decode a BSON array to python list.""" + size = _UNPACK_INT(data[position:position + 4])[0] + end = position + size - 1 + if data[end:end + 1] != b"\x00": + raise InvalidBSON("bad eoo") + + position += 4 + end -= 1 + result = [] + + # Avoid doing global and attribute lookups in the loop. + append = result.append + index = data.index + getter = _ELEMENT_GETTER + decoder_map = opts.type_registry._decoder_map + + while position < end: + element_type = data[position:position + 1] + # Just skip the keys. + position = index(b'\x00', position) + 1 + try: + value, position = getter[element_type]( + data, position, obj_end, opts, element_name) + except KeyError: + _raise_unknown_type(element_type, element_name) + + if decoder_map: + custom_decoder = decoder_map.get(type(value)) + if custom_decoder is not None: + value = custom_decoder(value) + + append(value) + + if position != end + 1: + raise InvalidBSON('bad array length') + return result, position + 1 + + +def _get_binary(data, position, obj_end, opts, dummy1): + """Decode a BSON binary to bson.binary.Binary or python UUID.""" + length, subtype = _UNPACK_LENGTH_SUBTYPE(data[position:position + 5]) + position += 5 + if subtype == 2: + length2 = _UNPACK_INT(data[position:position + 4])[0] + position += 4 + if length2 != length - 4: + raise InvalidBSON("invalid binary (st 2) - lengths don't match!") + length = length2 + end = position + length + if length < 0 or end > obj_end: + raise InvalidBSON('bad binary object length') + if subtype == 3: + # Java Legacy + uuid_representation = opts.uuid_representation + if uuid_representation == JAVA_LEGACY: + java = data[position:end] + value = uuid.UUID(bytes=java[0:8][::-1] + java[8:16][::-1]) + # C# legacy + elif uuid_representation == CSHARP_LEGACY: + value = uuid.UUID(bytes_le=data[position:end]) + # Python + else: + value = uuid.UUID(bytes=data[position:end]) + return value, end + if subtype == 4: + return uuid.UUID(bytes=data[position:end]), end + # Python3 special case. Decode subtype 0 to 'bytes'. + if PY3 and subtype == 0: + value = data[position:end] + else: + value = Binary(data[position:end], subtype) + return value, end + + +def _get_oid(data, position, dummy0, dummy1, dummy2): + """Decode a BSON ObjectId to bson.objectid.ObjectId.""" + end = position + 12 + return ObjectId(data[position:end]), end + + +def _get_boolean(data, position, dummy0, dummy1, dummy2): + """Decode a BSON true/false to python True/False.""" + end = position + 1 + boolean_byte = data[position:end] + if boolean_byte == b'\x00': + return False, end + elif boolean_byte == b'\x01': + return True, end + raise InvalidBSON('invalid boolean value: %r' % boolean_byte) + + +def _get_date(data, position, dummy0, opts, dummy1): + """Decode a BSON datetime to python datetime.datetime.""" + end = position + 8 + millis = _UNPACK_LONG(data[position:end])[0] + return _millis_to_datetime(millis, opts), end + + +def _get_code(data, position, obj_end, opts, element_name): + """Decode a BSON code to bson.code.Code.""" + code, position = _get_string(data, position, obj_end, opts, element_name) + return Code(code), position + + +def _get_code_w_scope(data, position, obj_end, opts, element_name): + """Decode a BSON code_w_scope to bson.code.Code.""" + code_end = position + _UNPACK_INT(data[position:position + 4])[0] + code, position = _get_string( + data, position + 4, code_end, opts, element_name) + scope, position = _get_object(data, position, code_end, opts, element_name) + if position != code_end: + raise InvalidBSON('scope outside of javascript code boundaries') + return Code(code, scope), position + + +def _get_regex(data, position, dummy0, opts, dummy1): + """Decode a BSON regex to bson.regex.Regex or a python pattern object.""" + pattern, position = _get_c_string(data, position, opts) + bson_flags, position = _get_c_string(data, position, opts) + bson_re = Regex(pattern, bson_flags) + return bson_re, position + + +def _get_ref(data, position, obj_end, opts, element_name): + """Decode (deprecated) BSON DBPointer to bson.dbref.DBRef.""" + collection, position = _get_string( + data, position, obj_end, opts, element_name) + oid, position = _get_oid(data, position, obj_end, opts, element_name) + return DBRef(collection, oid), position + + +def _get_timestamp(data, position, dummy0, dummy1, dummy2): + """Decode a BSON timestamp to bson.timestamp.Timestamp.""" + end = position + 8 + inc, timestamp = _UNPACK_TIMESTAMP(data[position:end]) + return Timestamp(timestamp, inc), end + + +def _get_int64(data, position, dummy0, dummy1, dummy2): + """Decode a BSON int64 to bson.int64.Int64.""" + end = position + 8 + return Int64(_UNPACK_LONG(data[position:end])[0]), end + + +def _get_decimal128(data, position, dummy0, dummy1, dummy2): + """Decode a BSON decimal128 to bson.decimal128.Decimal128.""" + end = position + 16 + return Decimal128.from_bid(data[position:end]), end + + +# Each decoder function's signature is: +# - data: bytes +# - position: int, beginning of object in 'data' to decode +# - obj_end: int, end of object to decode in 'data' if variable-length type +# - opts: a CodecOptions +_ELEMENT_GETTER = { + BSONNUM: _get_float, + BSONSTR: _get_string, + BSONOBJ: _get_object, + BSONARR: _get_array, + BSONBIN: _get_binary, + BSONUND: lambda v, w, x, y, z: (None, w), # Deprecated undefined + BSONOID: _get_oid, + BSONBOO: _get_boolean, + BSONDAT: _get_date, + BSONNUL: lambda v, w, x, y, z: (None, w), + BSONRGX: _get_regex, + BSONREF: _get_ref, # Deprecated DBPointer + BSONCOD: _get_code, + BSONSYM: _get_string, # Deprecated symbol + BSONCWS: _get_code_w_scope, + BSONINT: _get_int, + BSONTIM: _get_timestamp, + BSONLON: _get_int64, + BSONDEC: _get_decimal128, + BSONMIN: lambda v, w, x, y, z: (MinKey(), w), + BSONMAX: lambda v, w, x, y, z: (MaxKey(), w)} + + +def _element_to_dict(data, position, obj_end, opts): + """Decode a single key, value pair.""" + element_type = data[position:position + 1] + position += 1 + element_name, position = _get_c_string(data, position, opts) + try: + value, position = _ELEMENT_GETTER[element_type](data, position, + obj_end, opts, + element_name) + except KeyError: + _raise_unknown_type(element_type, element_name) + + if opts.type_registry._decoder_map: + custom_decoder = opts.type_registry._decoder_map.get(type(value)) + if custom_decoder is not None: + value = custom_decoder(value) + + return element_name, value, position +if _USE_C: + _element_to_dict = _cbson._element_to_dict + + +def _elements_to_dict(data, position, obj_end, opts, result=None): + """Decode a BSON document into result.""" + if result is None: + result = opts.document_class() + end = obj_end - 1 + while position < end: + key, value, position = _element_to_dict(data, position, obj_end, opts) + result[key] = value + if position != obj_end: + raise InvalidBSON('bad object or element length') + return result + + +def _bson_to_dict(data, opts): + """Decode a BSON string to document_class.""" + try: + if _raw_document_class(opts.document_class): + return opts.document_class(data, opts) + _, end = _get_object_size(data, 0, len(data)) + return _elements_to_dict(data, 4, end, opts) + except InvalidBSON: + raise + except Exception: + # Change exception type to InvalidBSON but preserve traceback. + _, exc_value, exc_tb = sys.exc_info() + reraise(InvalidBSON, exc_value, exc_tb) +if _USE_C: + _bson_to_dict = _cbson._bson_to_dict + + +_PACK_FLOAT = struct.Struct(">> import collections # From Python standard library. + >>> import bson + >>> from bson.codec_options import CodecOptions + >>> data = bson.BSON.encode({'a': 1}) + >>> decoded_doc = bson.BSON(data).decode() + + >>> options = CodecOptions(document_class=collections.OrderedDict) + >>> decoded_doc = bson.BSON(data).decode(codec_options=options) + >>> type(decoded_doc) + + + :Parameters: + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.0 + Removed `compile_re` option: PyMongo now always represents BSON + regular expressions as :class:`~bson.regex.Regex` objects. Use + :meth:`~bson.regex.Regex.try_compile` to attempt to convert from a + BSON regular expression to a Python regular expression object. + + Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with + `codec_options`. + + .. versionchanged:: 2.7 + Added `compile_re` option. If set to False, PyMongo represented BSON + regular expressions as :class:`~bson.regex.Regex` objects instead of + attempting to compile BSON regular expressions as Python native + regular expressions, thus preventing errors for some incompatible + patterns, see `PYTHON-500`_. + + .. _PYTHON-500: https://jira.mongodb.org/browse/PYTHON-500 + """ + if not isinstance(codec_options, CodecOptions): + raise _CODEC_OPTIONS_TYPE_ERROR + + return _bson_to_dict(self, codec_options) + + +def has_c(): + """Is the C extension installed? + """ + return _USE_C diff --git a/backend/venv/lib/python3.7/site-packages/bson/__pycache__/__init__.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/bson/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..68352a24a11dbc5ec58c4c4c0e65c36a91b78b30 GIT binary patch literal 32798 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/bson/__pycache__/binary.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/bson/__pycache__/binary.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00fa84bc2e87276741ead576997844fbd5138391 GIT binary patch literal 5272 zcmbVQ%WoUU8Q&KlqNu0sG)`X=KO(Iwld@$uiRu`k9!^v^v>?eTyoNDa?F^-r$X#Z3 zX_X396t#g|d+)`%rN;sVdgy<$rvg0$Mo+!=)bE>pkd!5%=u$JgGyBc=p80;OrLnP+ z0@wEsK6vr*PZZ^!I7qJ|JbVr<_#<>oVQN!psg|nZv)0sFx~1c@WiZ{!F~iEUoK;|X ztH_Ji2rpPAR7i@PWouOJ|y8@LWS>Ix}9YR-PAFj^$q~uT-n}odWX(R(uVqZny{hf>5~L zZb0lnG70y1%s-wdVYkgI(Ul6xJw$WYN(n)>@l8EaCp^Z9%TNj5hukB`MK&jJgK+f~|Idb>@FU`c3N`-iS$QKtSPeRr* zO%^f}0+|}IBM;qhk0fl#JUMimP4qb9=%?7dgfLfdCv0}nZ>gHG?TOUIbz9gi9&!<+ zf^R1-TqKw%gJB!Ytwc*8&P<#2+nBy~(~OsHCuI5M= z_*6zoZysGs191)&q{dqXPBDQe+0o6@)UZuL+x1|V4CIAbh)QmdW^a^}lA@6;*&`je zMx}nDMFmPden@FFNta}jP?|_GjYxNzL1dsQQ6ZU)jDR;Ae^vz42<1Pl+7|9hJLJ`u zwx~AzChXgw+7i{BeM&zG?t34aqv?b}k%X7l#l z-#P~!+NITYx8-}gzKn7;&V)+4E8YW9Sb2hJ=oGc27G;}+ds)q^N8_>PmBbVW9flX) zvB_BDvENhn6^M^|baOM_qPRCgJRg&^i;qn_HSsc4@grCTw;#hzS{!#j#o-6g{Rvut zMMg0)EfPm%THtHUK5W4~=t3n_d&(>AjdlX1^r~{KhPt>F8jy!M$huoS^-Z2>C+e}< z1G@b}Lj#@ldYlJ^Cgi$z`;J+RazUrfg_r=A(Kw~MJcLNvVmF{vpfE9o9r`TF$46mo zjuxA96<$EG9HCSmaIgqNDx_^cho=*00qz||n^woc<40FAKM%@sr2R++E!$eRV32Mt z@e!B-TMCR;8(L4@H{cnzT0=Vk)0hh8Y4t~DE>;-rk%~&yrJK!IR<7h0?7vE z-s6=16<5nhr~`K6n<2Zi_*@2h21nf+NU;-4Gp(V$ha$Cln#r>_gE?|DFiky9A}}9e z0`wQ`iW%Vhw*g)HX2)yIi3s?3P(Q$pB}!X9>ooDXOzAfryP3R!V~P0=rB<0@DWxr2 z9B>hhFRj&U8_($G)1|Fv>#Oo)h>C6D!$t_ZGFJrVuDFgJhCqCT9cmSgC7JF39!awu z41Aa3Y6brcL zc%J21L1I8ae{2NiimU`4AB{=~Tp#mY+v(arK!|WC5fzw)Wf(Sk+ZQ1@^u+<7GAP_o z$B`%i$m1hYvI8=NX!`&G+z7ZO1dNar$A=Fe61OG!bvkZZ^>MZ#{1(9qTS<8t#-$ywXDnMD>cSvU6m?JT?<@&S>V@Cp-I~@EV()0TpN7;qUkzFT`Z;v;fQpeIW_= z<8;ORDW6o%UD?m3R7KD%S$*d;mxk-HzR)w5KA(T_JU0DBvKq2`Q=d~w8vJ(Q)&1!0obp84_P z@9dxRsgmLU-^sm+3U6Qd5rC`~N$ul>?^tM#oKirchg*3jWuTCaVzx@nDmwQ@fdWLd-j2I>PD;s6_pwkQ$ Me?<-62BG` zhz-e#4Zo1Eg8yK}f)AK2D}I3$=l0ClR(Oo6dwRP2_C5F9N9&!<&9;N*=Ql2W%{Cq9 z5BjK|0E4&C^mTN?89KrhUgFNYp*Qn~{wx>9zla9;X`=J|9QLhBgYj zL$k@){ z?z#Zj66^PzVN0~d2Hxw!AGSqDY+}42e1N}D1n&YG{}CJzwioq3P!q0RyUp-?&SNb# z8$=1$dNA6<%KJ)XYQ*9R8EL~g4K(<$3B|is9G5@L3(f= zw-!7sz+%IW<0N6(kyI*1fbeudA~tv^n#`1&bLXY@m~-IH!LIBb+~SJQq>)Mw>~6-c zRh}8Jw^>O$qA@i*PX8@NO-B64Bc9B;k)otIgHBZ)$IkneQSiX-)KZpk1I6H2%a`ng zbsyYc$HrJj*$iGV(^#J#M@Jy+1`t+*%HzE=>}3eBD_L$GlynE@tX|DGVy0AyKsuhr z(ezYF6`XpS%@eU|;WhYd#`B!mn5b;V;0fgKBnF4<3O@r{lBc^291%Yf9)C|x_&hN% zDPwhL_ShcmBlqLfBnF9mLcGAcr+7-32)`_fEzifXnF7dud?eGFE% zn?~B4W1~E(j=D0$d7`Z!DZokpnGp{MA#B*2qGBP8(N^$Qz5s4fA*zC`*JFrSjeLow zJl&UKFksugS1aRi91|riB~7+4uDF-DsoApOBsn4tVKVymyHTCGf1*q~JCTo`o(5VA z?GrTpJ38YmUE|*M^hJXzXxsz;an!u;T@&bmU2EL+)W9_FuPw3jeoJ@{+y~xwLE5S3 z=xfngx(DmaAZ&l(q?ZE6Y#g>#;yUyb4$Xh*q~2|3=?Uj>UHy!cJ<~bZgx(*Z*C&n3 z;Gk_+f1=U-ttB+MPTE|0B)|04Z>Kwct#F+`lV_=dve{gtI8iZJ$+XM5 z29J38;Kf^=ccUv$|L!;Ps6tG` zRf_h4s23&ehX)Aq=NnPvN3xXzmP_9oaY`${s`0FHrQr0%WSjCch1EHL7H-RsyY*?09*nW z_i^rb&iT%{crZ6Nlf&OXeD=}T|9Uf*`%n6z|MDo@#}WP;PF}8>^9;{y8dU>Vt7*2Z zs%6kUyJ@%b)jXH;%|dIcI@L0(W~*2&wx+Aoe813~Y0Xw=Tcv8LHCLU(y{YQFSM;X8 z%T-VPBInI`v)|>sS%2!-sxIJn$(zIPIsSbbzvsPE_@J}Dx)ib!W;GM>u)Bah! zan8?sXS}n=X7xOtob%4$$r=AVo?P%R9OtSZc;~$f=;=exe4Z#(BV_-za0nOU#-`VgZU$%m_FM@_}h1aR~T7Eln>PH&){08ANtQi;XaOV2`4`X;ODBQXH+fU_Ds+EE_ZBH^M1jzy*!tv zyaEJ070+&Cr8nC&1nEA-GCgoZUx|`2=lPAGO^72zB#2`{`=L183+j8q^TWCfc6_nd zITY@Uz+b%=Mx9pjirW@=2tc+&fEYLwQAc#Ju$_j8_V5G^hYHZWHc2&d+g@0i;g;4J z;=02Y+7&?yFeWVU+qGKv2qcmAw1 zf2h-GbP&&GWV1rulKSPW{s);wuVm`f`8lf4b>djp)u>qXyTMDptw-M3+4t*MobMm2 zDM|0+{7bjl^T$WLFuqJTP*)H8Qg+zm6}QU8xac@!=^Q7Xahz7i>ouuda-45^ZZml@ z<&a`Ijx3;Md7e%*TloQKwo`|;Fo!&mBe3nKq9)Q_D5 zK~>ZzDxaHG&>ya7WUKxcYDU#3DjA&C?_J5Rl9CPOGEv1kJt;beZC1imu^6mlkcg*K zQLr*6T6Lb_nXF|ct?|7&weqe`@jzKWltN7!nNlNpfgn1BEvx=$g}!CP}Z%oJrQV=K=o6=WQ*`Z4zoCG8-lcSaSQ$18wR=e;Sfh-<-{_GWOMV$hZ15`pfSzZ-!39=U&y zu0V*~ZrE#r??I?+NePpo=!NW%Q=-5mV?}_RYeBJzyNpL`ayyoSrOE) zkgDSVJ;7YJI}v$*U@vF{=*w+@2Xj-*A=^>FR3pJ$t(F}+mO3%`plqlJl7JQtgJ=)K z6ag9{&cM8DI3?yu+yUGIM#xnt+5nc`8S?6?!nzRm?%fltH-w%Fvmwo3iJ=jh$_|Y? zP;Zeqon+ES;_gVbr7v0cuPqC{ev(|TFN;d0^7V4k3d+PXPyW-Ac9_rkvaCk^9y+JT z^L>ntN3i58cFyjT!nO)M!k?>5V;CkP&votSRj4=dtPBWXsEVv>B4 zcCetGcI2X}X!V*=09L6uIH|e|2CP}Na774m*i>wnB%^2@Av1M*%egw;zEJwD-vDlzjT8p2EN9tw<#sMISgQf0A1+@tRxL(QYEk~hr^=T z=_xSCRi~PGqk%p5Wq{@o9k^nr7c?V8Ph#8{(+sI8#j%Be{Kpr4bP-Z$v)tTz-S0*e zL@KFiWC_u0^H5j|5)mp#%+Z7vHzLr4>$=K3P^+o9GJ|D>R4A;7A^!AOuOtAyqK8*Y z8`06<$&tlYolGD7QTAgB$2yMiUvNTHZFGyf2>J#&#~7G6n*kzi$Kta+$nBe6?qFKp zi}C~W*a+`M1rPNu;EivMR}#;SjojCJuPx7fZAViBD=HqFG8*LGOb@KL267_SYkOer z&!DD->+EMau2n*<-zLxI@Qmurf0i5A;Y0LbN5%b9Q3=Pw{;7f0uvLHGJp46sHVB6D zTm8#gG+KAXWl7;a*~OuOdbr)cx%mp|5Q>k;wk5hmsJR?2^8pM|aThOA6Q}{#T|OP# zP1yPPBWD}%(MI1KHNYePH}09i`GIa-J> zIKWZh!K0|YlJ->G!C?rICCT)P*%os#@^*1!N106hTB9@qrHZ_Xruu(u>J zM#Q(LoChx+eZK8H+H$tPc)Iz#{{^kzrb=9-p0mE^ws%z~A~hW(HE6o@6Tj<;r9}3? z$IyZ%e*SV;F6XN@c{NGWp@j^4JDh<~(c&TvNG*x{W0bjCwE$y*j^Zg_0u|xznA~L< zHUAn%Si&i1lpxrWVM65nk7*)9kxoQOouoj2bWH_J+#gz-+lRK|3@zWc8rB8UBy+33 zcsX36Atk=#?q#{0`g4tD+$xd`=dD!I1bFt4U}z^?WCMXInVh)W2;<{ zl<10W7W({FCwipQLcS+2&Sq6J;(koecvefd^mX;O>eSp=%!%r;B+>_v6w!5zQwmr;Az6 z)6HVkcicljI6m9!1s)6E50L_6=MZ1&wF6TA#40(MWJfws1+VGLxDfS&cB2!|F&H$_ zsiWQWk3zirI6_j{EQO}zA@bFr(VpEXFpV}qpB0UsN@#S^oApYd)6<>}ebb&nd4I@@aX54xOwf?&97eFxrPFM_%5zDaJ~K2t2*xA-odl$RCQ5B z1%xs^<0SE&L{V-V{#R%{$kwUG?2-H~ic6o40rFTwlzF zn?TlTZ6(;zMM29)h)5Pn--oHv7Nn+aJ@zu%s#Dw|3$AUS-W%a?V#MC;1Z`Iy zRcJ5nbAQ*ZADz?-&!slOm*_qc3p_c})!LGno-WXYFc?H zc2E3kZ56RNV(@L*^J}%`WMX>F#wWp!1B=;*xk?AcemV_ELW)RqufNf&U(tnHMLZ!= za)^+b{Kt{*B5khiVwYuf61Cbxw;5u5nF&l<%2h$8jiZ0!VMD?|Fn69A9i}pAs(3{@^Wwa@|*!QP)xir-&P}m7~3Af|#zCP7Wp32~H1Nq#^i*>v_Jnx+<<$ zZtEsg9xWZt&~*-92BW5}ZM?Tm;UVuX!`MXb0rY~EgX2k5NgI71(}uC7jRc|y@WW(t zlm`?`dU4BIT8!!x7bCEs)GzF1BKRa7%v6_0lv-ImBsg&x*q6XN_l|X^9b(8uo04HJ z8RnQC*P8+Do`aw?gE~hLx-EhycJ3t_<3x2rj8%JT><{CD0`^v&GEVeI*J&KXuLnWt|N{6|v|2{&yuDymxlcCw9;e6A#tw`m`u(*y3BAwFmDqlg(#;&|`Uy!+QiLe!J`%i56g zDR~8mlazRh=SR6We+^FS8bq=B=}ZJ>Z$Ne*9673I8Vw2Mfv;rFre z+K8+-l(Tcy!@S-b^DS(l&gvDA)uSAxCFz46m9Be;(&S7XXQDo)u~F(RhXmc| zXn5>M{!#bzN3}?9zrqpHzAKsXH~5GWATi$pIw%uxtfXwAY$s)gSyCpr!L)My0?Jcw z&13%F%-us*kKtfpBI}**5z%ADPrXZ|*PSO%`RhgP@6tvl)`C4#S+|kuY?w#xP(Z7*TX)6erpWl|G{}-}myMo|Xn8EW zt(vqoxqGscdJmT3{&yMB&2(_@H&yuDgEDn;lTMUoJkh9?lLTtv0E}rj9M^?Xc5*)_ zuu-lvS28(^TH?(GWK%C@m$;IB2g=K7=dlk@NS7rM3%N!oLb=MU(g!V>K@2miG@pOM z!2>8huuYTg>ikcO3q>mhIf0!(&bIR@3IKVIKpvS4Y$}^56R@d_?FzD;T((grVAK6P z$^bT(3n&-gn%>l35zLGIi`XJYgvH@Toie%_A(f%1t7pi>jD}`o?j%V7pS&{D!!?Z0 zPlVpEze)ZIJ1bKO-ZB#9ouYG2m*$|h4>We~)AG`{K{=K!?-xPFbR{oFJ4cd?NBN@q zc<9eiX6MWw55=KJn(UZP=U=A5D^E`A0Ij!y^gokER}wh?TT jHpNQjhvvDwZ50a(=N8VLIkoWTv?*4w7ydGLx?ueeQRYcA literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/bson/__pycache__/dbref.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/bson/__pycache__/dbref.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0914fec2c5c1dac6a4c1e34c423639d051c24f75 GIT binary patch literal 4368 zcma)9U31&U6~zJ|1ktoC%Zc+j>5j*)O^;0_PSQ9U#bdk4csyy<*7ifosS85br3?`S z;9XD-In+z!>3jcy{nq{^y!I)7A${q&3jq{0b{lf`1K7pIz2~02_u~H9vuz8%e{O&H z^l-zn{!JJ4&&A{}nx;-zeM>N5Cv0r@ZANn^amH@n9k==|bL}SHc%{F>tOu583GbyP zJn0`f{Z+B@*y{STyHBz#(R`RGJ`U43pCn-sr+fU{`;X*M^J_{DrIKkRH7_#$`z+nd zzP-;y7EQ)7Ep)f{6ZEjUtn`JHd6ebTu3fIig;Z!`U7pc}g1n%Z<`TP{$A8#~=!yF0 zU~(5t{~evQ`V2PNg7qC?_g&${*3cHNXuV|pmh^-tR$f|1tiOV}FILUm$Gk1hnE9&c zh_f%Pep{@Ga~RKvb#WeJM{I}-7|)7##Jd>R#6|HQ#&hC*@d3tl@uBz#<9Xo#m5J+)=7){pTo;*=g2!o%Nw3ZE`$@ozov4MLejXZG_&u8b8@j@pvw|Jj`uze{ z7Hr?S2z!2HbJ#mtRkyM3nDyIO$E;arb1QJ?Hu~1wLGR65uU6*nYsRd33)jA|p}8CW z$F{78Xd zRK!_2;NZtVghjX;YKc|N)2wL3geic~MF^VkY}kmSy_Ne1{964E+`#iG&!@$+Eajhf zE8s|*yN%lf7ykXebu#kN7B2%Mrm$gs=ykXq3s8|fP%-(}AnKW|h2d`>A7+eZsacMB% z#)jpmYlPsS8t?A1M)eNDxH$~VLatvQy#SDUS;+rI`-BjFjQj}~-Qdv#CXXwv^@6j# z9v@Q}f{9yOTaLdUSl7H(6nM+k=7f}f z5Rl`7pxg+|xe9{CtIKwMFBau`J+I$cdXpS;S+3P96@Fc>bhS)|W&4CA8xs8b$-WoJ z9|{$gUiHMkEcGt*`Z?OnReS)En+KuV9A=3C=QhV`b9X-oZtE}8uMWf)QIH?p`m`9{ z-rHBnXTvYQ{(AGtW*+A^jg3ii^VSd1XcA0=&3tMQ-rUt$x@i#ZOz^l!7-n!%%2Z;u#l#g9g6zeUp&8t2x&g{Z}5+Ye-6NW3@;V7w6n z1~1Uyq2`k`jxr$^VWZ2`83@C$j==y@b)=#?kM0{ZjlgJG?rgnMZXxp*sP{>w-U*d( zqLvisK=xY{KnAZYK32f56o|zycyabWaj98xBIG=h#YCl=?_{Z@7+yT9l}3P)I=xzO zoy#a5A{sW~amkXiZ@1(EmL8&2P06B5Oa;q&ezmz_AF`bV5`xZm_$0iVsl10w${w1Qy}69r$DV? zPpwG{*$;7^4&#D*WhV%7cICI#v`=)zD5A^-2& zLwnCYVuy6k5e}+`8#J3U;cf%tS1ztvrY>MWvo+H-hfsPoSqh;%M-oE`hjI^T*rcDD zDku2#K&Db5`A#X{qn41A>UKe%JWt}NdRE8-nPfR4&LRxEml2j{&gEF_pclJcPf<-| zPNSj|p@>WdLgVWiM+Z^;G>TrR1KfU!2Q-~Ct3|GC+_n|R@|*`=B~@$%=>L zJdyOpB1PA&f=4Su!KF%jqWdmp&?Kv{Y^ zQvkCa{K`^eWDfzfM`X6qDSs9A~FJh0tEh z3W6UYp~^FM2P^^on*hJq)ZI`-2$Y7wI1`hEN=Ha}J_(cKLUIK%vj~}@V?LLIai~U8 zsrTq9KUWlDi|OeQ`VnbQ6`A7HU8C*>bvLQIMco(FEmD)2nc9es3`sX}%eP&}wby>t z@$mQkRp0S3n6YJm+Ud5{SJ+*;1f?R_`fF4oqB#7V;#62vv4niw_st4cP2exRY}_(53XZD^%`K>NjAzcE?_49p}H-oGUi~ literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/bson/__pycache__/decimal128.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/bson/__pycache__/decimal128.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..11b76a1d497346f7e7c8b25b19eacb4d7cfed3a1 GIT binary patch literal 9684 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/bson/__pycache__/errors.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/bson/__pycache__/errors.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc38f575cca4a04eadce9b4a23b27243096251f2 GIT binary patch literal 1316 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/bson/__pycache__/max_key.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/bson/__pycache__/max_key.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fabed3812c23c98a6c7be22bf29a398a60c985d4 GIT binary patch literal 1561 zcma)6OK;Oa5cVsNrb*vI2q9sCkQ!CBX+_nCDpVB&2h<#(r$}hydNzp@zq)op6IJ5S z{uua6zH;I(aAL-8S_eX5tDUhu+xh1EW;VyQT1CS3_3+WBX9Y?6PA`+C;lmN`=ph=A zn$kN7WKgapP`G@hHWg4mN_B0#^MQwgN8FFC*a>{S9SA+{a{VOmJHeY{-SJ~Ce9P5O ztc&-2q{pL>8xmr5l#WQse;nfKn3kuY8IdXMf61|K^c7s=AeSU z4D(P$KL<5fKwp7HSVBJ!%dmpJ3aeP=T2jp5$HV*Us_3{3LmzSxAsf4E`5g`i2l}?L zo5nEQYGt`vE!_`3>y8(?+(Z76ZU|4 zrhs{t=yOq*1s+0n!Y3oXcgM

&7+6~Of&23hCSfgH}N%bucsU{W1yZ{E&K%8-QKCs-INQp5B zY{mrLc2OoXPlj|5H8P82mdGrVSs_E0JZI=$q@%Tp578Q$azRrxrL5-v(i4rkDjwj0 cq!^s_xE-e-bE(qI**!bVN`6Jy={?ibUwFAblmGw# literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/bson/__pycache__/min_key.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/bson/__pycache__/min_key.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b4cbc1218b985e12352a2634ea18f95d77f74d0 GIT binary patch literal 1561 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 Jx8ciX{{keG8-M@+ literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/bson/__pycache__/raw_bson.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/bson/__pycache__/raw_bson.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..784fdd5827b853d3f3356b279582eabc82e3531d GIT binary patch literal 4211 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 SpHV#8k^zR@8e8=$5B~*<4m>RY literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/bson/__pycache__/son.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/bson/__pycache__/son.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..abfd4d3e9c68ea241ea390a1b19b1e21dd220531 GIT binary patch literal 6377 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/bson/__pycache__/tz_util.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/bson/__pycache__/tz_util.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b50303a0ebe73b98cc704f8e3eb7e998b1b30317 GIT binary patch literal 1515 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�^(R8G^V?i!FVEbzu()@0rm?8`>dBQ7eM|6kx{OK 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 z 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 + to use + """ + + _type_marker = 5 + + def __new__(cls, data, subtype=BINARY_SUBTYPE): + if not isinstance(data, bytes): + raise TypeError("data must be an instance of bytes") + if not isinstance(subtype, int): + raise TypeError("subtype must be an instance of int") + if subtype >= 256 or subtype < 0: + raise ValueError("subtype must be contained in [0, 256)") + self = bytes.__new__(cls, data) + self.__subtype = subtype + return self + + @property + def subtype(self): + """Subtype of this binary data. + """ + return self.__subtype + + def __getnewargs__(self): + # Work around http://bugs.python.org/issue7382 + data = super(Binary, self).__getnewargs__()[0] + if PY3 and not isinstance(data, bytes): + data = data.encode('latin-1') + return data, self.__subtype + + def __eq__(self, other): + if isinstance(other, Binary): + return ((self.__subtype, bytes(self)) == + (other.subtype, bytes(other))) + # We don't return NotImplemented here because if we did then + # Binary("foo") == "foo" would return True, since Binary is a + # subclass of str... + return False + + def __hash__(self): + return super(Binary, self).__hash__() ^ hash(self.__subtype) + + def __ne__(self, other): + return not self == other + + def __repr__(self): + return "Binary(%s, %s)" % (bytes.__repr__(self), self.__subtype) + + +class UUIDLegacy(Binary): + """UUID wrapper to support working with UUIDs stored as PYTHON_LEGACY. + + .. doctest:: + + >>> import uuid + >>> from bson.binary import Binary, UUIDLegacy, STANDARD + >>> from bson.codec_options import CodecOptions + >>> my_uuid = uuid.uuid4() + >>> coll = db.get_collection('test', + ... CodecOptions(uuid_representation=STANDARD)) + >>> coll.insert_one({'uuid': Binary(my_uuid.bytes, 3)}).inserted_id + ObjectId('...') + >>> coll.count_documents({'uuid': my_uuid}) + 0 + >>> coll.count_documents({'uuid': UUIDLegacy(my_uuid)}) + 1 + >>> coll.find({'uuid': UUIDLegacy(my_uuid)})[0]['uuid'] + UUID('...') + >>> + >>> # Convert from subtype 3 to subtype 4 + >>> doc = coll.find_one({'uuid': UUIDLegacy(my_uuid)}) + >>> coll.replace_one({"_id": doc["_id"]}, doc).matched_count + 1 + >>> coll.count_documents({'uuid': UUIDLegacy(my_uuid)}) + 0 + >>> coll.count_documents({'uuid': {'$in': [UUIDLegacy(my_uuid), my_uuid]}}) + 1 + >>> coll.find_one({'uuid': my_uuid})['uuid'] + UUID('...') + + Raises TypeError if `obj` is not an instance of :class:`~uuid.UUID`. + + :Parameters: + - `obj`: An instance of :class:`~uuid.UUID`. + """ + + def __new__(cls, obj): + if not isinstance(obj, UUID): + raise TypeError("obj must be an instance of uuid.UUID") + self = Binary.__new__(cls, obj.bytes, OLD_UUID_SUBTYPE) + self.__uuid = obj + return self + + def __getnewargs__(self): + # Support copy and deepcopy + return (self.__uuid,) + + @property + def uuid(self): + """UUID instance wrapped by this UUIDLegacy instance. + """ + return self.__uuid + + def __repr__(self): + return "UUIDLegacy('%s')" % self.__uuid diff --git a/backend/venv/lib/python3.7/site-packages/bson/code.py b/backend/venv/lib/python3.7/site-packages/bson/code.py new file mode 100644 index 000000000..3f6e50435 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/code.py @@ -0,0 +1,99 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing JavaScript code in BSON. +""" + +from bson.py3compat import abc, string_type, PY3, text_type + + +class Code(str): + """BSON's JavaScript code type. + + Raises :class:`TypeError` if `code` is not an instance of + :class:`basestring` (:class:`str` in python 3) or `scope` + is not ``None`` or an instance of :class:`dict`. + + Scope variables can be set by passing a dictionary as the `scope` + argument or by using keyword arguments. If a variable is set as a + keyword argument it will override any setting for that variable in + the `scope` dictionary. + + :Parameters: + - `code`: A string containing JavaScript code to be evaluated or another + instance of Code. In the latter case, the scope of `code` becomes this + Code's :attr:`scope`. + - `scope` (optional): dictionary representing the scope in which + `code` should be evaluated - a mapping from identifiers (as + strings) to values. Defaults to ``None``. This is applied after any + scope associated with a given `code` above. + - `**kwargs` (optional): scope variables can also be passed as + keyword arguments. These are applied after `scope` and `code`. + + .. versionchanged:: 3.4 + The default value for :attr:`scope` is ``None`` instead of ``{}``. + + """ + + _type_marker = 13 + + def __new__(cls, code, scope=None, **kwargs): + if not isinstance(code, string_type): + raise TypeError("code must be an " + "instance of %s" % (string_type.__name__)) + + if not PY3 and isinstance(code, text_type): + self = str.__new__(cls, code.encode('utf8')) + else: + self = str.__new__(cls, code) + + try: + self.__scope = code.scope + except AttributeError: + self.__scope = None + + if scope is not None: + if not isinstance(scope, abc.Mapping): + raise TypeError("scope must be an instance of dict") + if self.__scope is not None: + self.__scope.update(scope) + else: + self.__scope = scope + + if kwargs: + if self.__scope is not None: + self.__scope.update(kwargs) + else: + self.__scope = kwargs + + return self + + @property + def scope(self): + """Scope dictionary for this instance or ``None``. + """ + return self.__scope + + def __repr__(self): + return "Code(%s, %r)" % (str.__repr__(self), self.__scope) + + def __eq__(self, other): + if isinstance(other, Code): + return (self.__scope, str(self)) == (other.__scope, str(other)) + return False + + __hash__ = None + + def __ne__(self, other): + return not self == other diff --git a/backend/venv/lib/python3.7/site-packages/bson/codec_options.py b/backend/venv/lib/python3.7/site-packages/bson/codec_options.py new file mode 100644 index 000000000..471d695a9 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/codec_options.py @@ -0,0 +1,334 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for specifying BSON codec options.""" + +import datetime + +from abc import abstractmethod +from collections import namedtuple + +from bson.py3compat import ABC, abc, abstractproperty, string_type + +from bson.binary import (ALL_UUID_REPRESENTATIONS, + PYTHON_LEGACY, + UUID_REPRESENTATION_NAMES) + + +_RAW_BSON_DOCUMENT_MARKER = 101 + + +def _raw_document_class(document_class): + """Determine if a document_class is a RawBSONDocument class.""" + marker = getattr(document_class, '_type_marker', None) + return marker == _RAW_BSON_DOCUMENT_MARKER + + +class TypeEncoder(ABC): + """Base class for defining type codec classes which describe how a + custom type can be transformed to one of the types BSON understands. + + Codec classes must implement the ``python_type`` attribute, and the + ``transform_python`` method to support encoding. + + See :ref:`custom-type-type-codec` documentation for an example. + """ + @abstractproperty + def python_type(self): + """The Python type to be converted into something serializable.""" + pass + + @abstractmethod + def transform_python(self, value): + """Convert the given Python object into something serializable.""" + pass + + +class TypeDecoder(ABC): + """Base class for defining type codec classes which describe how a + BSON type can be transformed to a custom type. + + Codec classes must implement the ``bson_type`` attribute, and the + ``transform_bson`` method to support decoding. + + See :ref:`custom-type-type-codec` documentation for an example. + """ + @abstractproperty + def bson_type(self): + """The BSON type to be converted into our own type.""" + pass + + @abstractmethod + def transform_bson(self, value): + """Convert the given BSON value into our own type.""" + pass + + +class TypeCodec(TypeEncoder, TypeDecoder): + """Base class for defining type codec classes which describe how a + custom type can be transformed to/from one of the types :mod:`bson` + can already encode/decode. + + Codec classes must implement the ``python_type`` attribute, and the + ``transform_python`` method to support encoding, as well as the + ``bson_type`` attribute, and the ``transform_bson`` method to support + decoding. + + See :ref:`custom-type-type-codec` documentation for an example. + """ + pass + + +class TypeRegistry(object): + """Encapsulates type codecs used in encoding and / or decoding BSON, as + well as the fallback encoder. Type registries cannot be modified after + instantiation. + + ``TypeRegistry`` can be initialized with an iterable of type codecs, and + a callable for the fallback encoder:: + + >>> from bson.codec_options import TypeRegistry + >>> type_registry = TypeRegistry([Codec1, Codec2, Codec3, ...], + ... fallback_encoder) + + See :ref:`custom-type-type-registry` documentation for an example. + + :Parameters: + - `type_codecs` (optional): iterable of type codec instances. If + ``type_codecs`` contains multiple codecs that transform a single + python or BSON type, the transformation specified by the type codec + occurring last prevails. A TypeError will be raised if one or more + type codecs modify the encoding behavior of a built-in :mod:`bson` + type. + - `fallback_encoder` (optional): callable that accepts a single, + unencodable python value and transforms it into a type that + :mod:`bson` can encode. See :ref:`fallback-encoder-callable` + documentation for an example. + """ + def __init__(self, type_codecs=None, fallback_encoder=None): + self.__type_codecs = list(type_codecs or []) + self._fallback_encoder = fallback_encoder + self._encoder_map = {} + self._decoder_map = {} + + if self._fallback_encoder is not None: + if not callable(fallback_encoder): + raise TypeError("fallback_encoder %r is not a callable" % ( + fallback_encoder)) + + for codec in self.__type_codecs: + is_valid_codec = False + if isinstance(codec, TypeEncoder): + self._validate_type_encoder(codec) + is_valid_codec = True + self._encoder_map[codec.python_type] = codec.transform_python + if isinstance(codec, TypeDecoder): + is_valid_codec = True + self._decoder_map[codec.bson_type] = codec.transform_bson + if not is_valid_codec: + raise TypeError( + "Expected an instance of %s, %s, or %s, got %r instead" % ( + TypeEncoder.__name__, TypeDecoder.__name__, + TypeCodec.__name__, codec)) + + def _validate_type_encoder(self, codec): + from bson import _BUILT_IN_TYPES + for pytype in _BUILT_IN_TYPES: + if issubclass(codec.python_type, pytype): + err_msg = ("TypeEncoders cannot change how built-in types are " + "encoded (encoder %s transforms type %s)" % + (codec, pytype)) + raise TypeError(err_msg) + + def __repr__(self): + return ('%s(type_codecs=%r, fallback_encoder=%r)' % ( + self.__class__.__name__, self.__type_codecs, + self._fallback_encoder)) + + def __eq__(self, other): + if not isinstance(other, type(self)): + return NotImplemented + return ((self._decoder_map == other._decoder_map) and + (self._encoder_map == other._encoder_map) and + (self._fallback_encoder == other._fallback_encoder)) + + +_options_base = namedtuple( + 'CodecOptions', + ('document_class', 'tz_aware', 'uuid_representation', + 'unicode_decode_error_handler', 'tzinfo', 'type_registry')) + + +class CodecOptions(_options_base): + """Encapsulates options used encoding and / or decoding BSON. + + The `document_class` option is used to define a custom type for use + decoding BSON documents. Access to the underlying raw BSON bytes for + a document is available using the :class:`~bson.raw_bson.RawBSONDocument` + type:: + + >>> from bson.raw_bson import RawBSONDocument + >>> from bson.codec_options import CodecOptions + >>> codec_options = CodecOptions(document_class=RawBSONDocument) + >>> coll = db.get_collection('test', codec_options=codec_options) + >>> doc = coll.find_one() + >>> doc.raw + '\\x16\\x00\\x00\\x00\\x07_id\\x00[0\\x165\\x91\\x10\\xea\\x14\\xe8\\xc5\\x8b\\x93\\x00' + + The document class can be any type that inherits from + :class:`~collections.MutableMapping`:: + + >>> class AttributeDict(dict): + ... # A dict that supports attribute access. + ... def __getattr__(self, key): + ... return self[key] + ... def __setattr__(self, key, value): + ... self[key] = value + ... + >>> codec_options = CodecOptions(document_class=AttributeDict) + >>> coll = db.get_collection('test', codec_options=codec_options) + >>> doc = coll.find_one() + >>> doc._id + ObjectId('5b3016359110ea14e8c58b93') + + See :doc:`/examples/datetimes` for examples using the `tz_aware` and + `tzinfo` options. + + See :class:`~bson.binary.UUIDLegacy` for examples using the + `uuid_representation` option. + + :Parameters: + - `document_class`: BSON documents returned in queries will be decoded + to an instance of this class. Must be a subclass of + :class:`~collections.MutableMapping`. Defaults to :class:`dict`. + - `tz_aware`: If ``True``, BSON datetimes will be decoded to timezone + aware instances of :class:`~datetime.datetime`. Otherwise they will be + naive. Defaults to ``False``. + - `uuid_representation`: The BSON representation to use when encoding + and decoding instances of :class:`~uuid.UUID`. Defaults to + :data:`~bson.binary.PYTHON_LEGACY`. + - `unicode_decode_error_handler`: The error handler to apply when + a Unicode-related error occurs during BSON decoding that would + otherwise raise :exc:`UnicodeDecodeError`. Valid options include + 'strict', 'replace', and 'ignore'. Defaults to 'strict'. + - `tzinfo`: A :class:`~datetime.tzinfo` subclass that specifies the + timezone to/from which :class:`~datetime.datetime` objects should be + encoded/decoded. + - `type_registry`: Instance of :class:`TypeRegistry` used to customize + encoding and decoding behavior. + + .. versionadded:: 3.8 + `type_registry` attribute. + + .. warning:: Care must be taken when changing + `unicode_decode_error_handler` from its default value ('strict'). + The 'replace' and 'ignore' modes should not be used when documents + retrieved from the server will be modified in the client application + and stored back to the server. + """ + + def __new__(cls, document_class=dict, + tz_aware=False, uuid_representation=PYTHON_LEGACY, + unicode_decode_error_handler="strict", + tzinfo=None, type_registry=None): + if not (issubclass(document_class, abc.MutableMapping) or + _raw_document_class(document_class)): + raise TypeError("document_class must be dict, bson.son.SON, " + "bson.raw_bson.RawBSONDocument, or a " + "sublass of collections.MutableMapping") + if not isinstance(tz_aware, bool): + raise TypeError("tz_aware must be True or False") + if uuid_representation not in ALL_UUID_REPRESENTATIONS: + raise ValueError("uuid_representation must be a value " + "from bson.binary.ALL_UUID_REPRESENTATIONS") + if not isinstance(unicode_decode_error_handler, (string_type, None)): + raise ValueError("unicode_decode_error_handler must be a string " + "or None") + if tzinfo is not None: + if not isinstance(tzinfo, datetime.tzinfo): + raise TypeError( + "tzinfo must be an instance of datetime.tzinfo") + if not tz_aware: + raise ValueError( + "cannot specify tzinfo without also setting tz_aware=True") + + type_registry = type_registry or TypeRegistry() + + if not isinstance(type_registry, TypeRegistry): + raise TypeError("type_registry must be an instance of TypeRegistry") + + return tuple.__new__( + cls, (document_class, tz_aware, uuid_representation, + unicode_decode_error_handler, tzinfo, type_registry)) + + def _arguments_repr(self): + """Representation of the arguments used to create this object.""" + document_class_repr = ( + 'dict' if self.document_class is dict + else repr(self.document_class)) + + uuid_rep_repr = UUID_REPRESENTATION_NAMES.get(self.uuid_representation, + self.uuid_representation) + + return ('document_class=%s, tz_aware=%r, uuid_representation=%s, ' + 'unicode_decode_error_handler=%r, tzinfo=%r, ' + 'type_registry=%r' % + (document_class_repr, self.tz_aware, uuid_rep_repr, + self.unicode_decode_error_handler, self.tzinfo, + self.type_registry)) + + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, self._arguments_repr()) + + def with_options(self, **kwargs): + """Make a copy of this CodecOptions, overriding some options:: + + >>> from bson.codec_options import DEFAULT_CODEC_OPTIONS + >>> DEFAULT_CODEC_OPTIONS.tz_aware + False + >>> options = DEFAULT_CODEC_OPTIONS.with_options(tz_aware=True) + >>> options.tz_aware + True + + .. versionadded:: 3.5 + """ + return CodecOptions( + kwargs.get('document_class', self.document_class), + kwargs.get('tz_aware', self.tz_aware), + kwargs.get('uuid_representation', self.uuid_representation), + kwargs.get('unicode_decode_error_handler', + self.unicode_decode_error_handler), + kwargs.get('tzinfo', self.tzinfo), + kwargs.get('type_registry', self.type_registry) + ) + + +DEFAULT_CODEC_OPTIONS = CodecOptions() + + +def _parse_codec_options(options): + """Parse BSON codec options.""" + return CodecOptions( + document_class=options.get( + 'document_class', DEFAULT_CODEC_OPTIONS.document_class), + tz_aware=options.get( + 'tz_aware', DEFAULT_CODEC_OPTIONS.tz_aware), + uuid_representation=options.get( + 'uuidrepresentation', DEFAULT_CODEC_OPTIONS.uuid_representation), + unicode_decode_error_handler=options.get( + 'unicode_decode_error_handler', + DEFAULT_CODEC_OPTIONS.unicode_decode_error_handler), + tzinfo=options.get('tzinfo', DEFAULT_CODEC_OPTIONS.tzinfo), + type_registry=options.get( + 'type_registry', DEFAULT_CODEC_OPTIONS.type_registry)) diff --git a/backend/venv/lib/python3.7/site-packages/bson/dbref.py b/backend/venv/lib/python3.7/site-packages/bson/dbref.py new file mode 100644 index 000000000..3ec546349 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/dbref.py @@ -0,0 +1,135 @@ +# Copyright 2009-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for manipulating DBRefs (references to MongoDB documents).""" + +from copy import deepcopy + +from bson.py3compat import iteritems, string_type +from bson.son import SON + + +class DBRef(object): + """A reference to a document stored in MongoDB. + """ + + # DBRef isn't actually a BSON "type" so this number was arbitrarily chosen. + _type_marker = 100 + + def __init__(self, collection, id, database=None, _extra={}, **kwargs): + """Initialize a new :class:`DBRef`. + + Raises :class:`TypeError` if `collection` or `database` is not + an instance of :class:`basestring` (:class:`str` in python 3). + `database` is optional and allows references to documents to work + across databases. Any additional keyword arguments will create + additional fields in the resultant embedded document. + + :Parameters: + - `collection`: name of the collection the document is stored in + - `id`: the value of the document's ``"_id"`` field + - `database` (optional): name of the database to reference + - `**kwargs` (optional): additional keyword arguments will + create additional, custom fields + + .. mongodoc:: dbrefs + """ + if not isinstance(collection, string_type): + raise TypeError("collection must be an " + "instance of %s" % string_type.__name__) + if database is not None and not isinstance(database, string_type): + raise TypeError("database must be an " + "instance of %s" % string_type.__name__) + + self.__collection = collection + self.__id = id + self.__database = database + kwargs.update(_extra) + self.__kwargs = kwargs + + @property + def collection(self): + """Get the name of this DBRef's collection as unicode. + """ + return self.__collection + + @property + def id(self): + """Get this DBRef's _id. + """ + return self.__id + + @property + def database(self): + """Get the name of this DBRef's database. + + Returns None if this DBRef doesn't specify a database. + """ + return self.__database + + def __getattr__(self, key): + try: + return self.__kwargs[key] + except KeyError: + raise AttributeError(key) + + # Have to provide __setstate__ to avoid + # infinite recursion since we override + # __getattr__. + def __setstate__(self, state): + self.__dict__.update(state) + + def as_doc(self): + """Get the SON document representation of this DBRef. + + Generally not needed by application developers + """ + doc = SON([("$ref", self.collection), + ("$id", self.id)]) + if self.database is not None: + doc["$db"] = self.database + doc.update(self.__kwargs) + return doc + + def __repr__(self): + extra = "".join([", %s=%r" % (k, v) + for k, v in iteritems(self.__kwargs)]) + if self.database is None: + return "DBRef(%r, %r%s)" % (self.collection, self.id, extra) + return "DBRef(%r, %r, %r%s)" % (self.collection, self.id, + self.database, extra) + + def __eq__(self, other): + if isinstance(other, DBRef): + us = (self.__database, self.__collection, + self.__id, self.__kwargs) + them = (other.__database, other.__collection, + other.__id, other.__kwargs) + return us == them + return NotImplemented + + def __ne__(self, other): + return not self == other + + def __hash__(self): + """Get a hash value for this :class:`DBRef`.""" + return hash((self.__collection, self.__id, self.__database, + tuple(sorted(self.__kwargs.items())))) + + def __deepcopy__(self, memo): + """Support function for `copy.deepcopy()`.""" + return DBRef(deepcopy(self.__collection, memo), + deepcopy(self.__id, memo), + deepcopy(self.__database, memo), + deepcopy(self.__kwargs, memo)) diff --git a/backend/venv/lib/python3.7/site-packages/bson/decimal128.py b/backend/venv/lib/python3.7/site-packages/bson/decimal128.py new file mode 100644 index 000000000..0c0fc10c6 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/decimal128.py @@ -0,0 +1,335 @@ +# Copyright 2016-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for working with the BSON decimal128 type. + +.. versionadded:: 3.4 + +.. note:: The Decimal128 BSON type requires MongoDB 3.4+. +""" + +import decimal +import struct +import sys + +from bson.py3compat import (PY3 as _PY3, + string_type as _string_type) + + +if _PY3: + _from_bytes = int.from_bytes # pylint: disable=no-member, invalid-name +else: + import binascii + def _from_bytes(value, dummy, _int=int, _hexlify=binascii.hexlify): + "An implementation of int.from_bytes for python 2.x." + return _int(_hexlify(value), 16) + + +_PACK_64 = struct.Struct("= 3.3, cdecimal + decimal.Context(clamp=1) # pylint: disable=unexpected-keyword-arg + _CTX_OPTIONS['clamp'] = 1 +except TypeError: + # Python < 3.3 + _CTX_OPTIONS['_clamp'] = 1 + +_DEC128_CTX = decimal.Context(**_CTX_OPTIONS.copy()) + + +def create_decimal128_context(): + """Returns an instance of :class:`decimal.Context` appropriate + for working with IEEE-754 128-bit decimal floating point values. + """ + opts = _CTX_OPTIONS.copy() + opts['traps'] = [] + return decimal.Context(**opts) + + +def _decimal_to_128(value): + """Converts a decimal.Decimal to BID (high bits, low bits). + + :Parameters: + - `value`: An instance of decimal.Decimal + """ + with decimal.localcontext(_DEC128_CTX) as ctx: + value = ctx.create_decimal(value) + + if value.is_infinite(): + return _NINF if value.is_signed() else _PINF + + sign, digits, exponent = value.as_tuple() + + if value.is_nan(): + if digits: + raise ValueError("NaN with debug payload is not supported") + if value.is_snan(): + return _NSNAN if value.is_signed() else _PSNAN + return _NNAN if value.is_signed() else _PNAN + + significand = int("".join([str(digit) for digit in digits])) + bit_length = significand.bit_length() + + high = 0 + low = 0 + for i in range(min(64, bit_length)): + if significand & (1 << i): + low |= 1 << i + + for i in range(64, bit_length): + if significand & (1 << i): + high |= 1 << (i - 64) + + biased_exponent = exponent + _EXPONENT_BIAS + + if high >> 49 == 1: + high = high & 0x7fffffffffff + high |= _EXPONENT_MASK + high |= (biased_exponent & 0x3fff) << 47 + else: + high |= biased_exponent << 49 + + if sign: + high |= _SIGN + + return high, low + + +class Decimal128(object): + """BSON Decimal128 type:: + + >>> Decimal128(Decimal("0.0005")) + Decimal128('0.0005') + >>> Decimal128("0.0005") + Decimal128('0.0005') + >>> Decimal128((3474527112516337664, 5)) + Decimal128('0.0005') + + :Parameters: + - `value`: An instance of :class:`decimal.Decimal`, string, or tuple of + (high bits, low bits) from Binary Integer Decimal (BID) format. + + .. note:: :class:`~Decimal128` uses an instance of :class:`decimal.Context` + configured for IEEE-754 Decimal128 when validating parameters. + Signals like :class:`decimal.InvalidOperation`, :class:`decimal.Inexact`, + and :class:`decimal.Overflow` are trapped and raised as exceptions:: + + >>> Decimal128(".13.1") + Traceback (most recent call last): + File "", line 1, in + ... + decimal.InvalidOperation: [] + >>> + >>> Decimal128("1E-6177") + Traceback (most recent call last): + File "", line 1, in + ... + decimal.Inexact: [] + >>> + >>> Decimal128("1E6145") + Traceback (most recent call last): + File "", line 1, in + ... + decimal.Overflow: [, ] + + To ensure the result of a calculation can always be stored as BSON + Decimal128 use the context returned by + :func:`create_decimal128_context`:: + + >>> import decimal + >>> decimal128_ctx = create_decimal128_context() + >>> with decimal.localcontext(decimal128_ctx) as ctx: + ... Decimal128(ctx.create_decimal(".13.3")) + ... + Decimal128('NaN') + >>> + >>> with decimal.localcontext(decimal128_ctx) as ctx: + ... Decimal128(ctx.create_decimal("1E-6177")) + ... + Decimal128('0E-6176') + >>> + >>> with decimal.localcontext(DECIMAL128_CTX) as ctx: + ... Decimal128(ctx.create_decimal("1E6145")) + ... + Decimal128('Infinity') + + To match the behavior of MongoDB's Decimal128 implementation + str(Decimal(value)) may not match str(Decimal128(value)) for NaN values:: + + >>> Decimal128(Decimal('NaN')) + Decimal128('NaN') + >>> Decimal128(Decimal('-NaN')) + Decimal128('NaN') + >>> Decimal128(Decimal('sNaN')) + Decimal128('NaN') + >>> Decimal128(Decimal('-sNaN')) + Decimal128('NaN') + + However, :meth:`~Decimal128.to_decimal` will return the exact value:: + + >>> Decimal128(Decimal('NaN')).to_decimal() + Decimal('NaN') + >>> Decimal128(Decimal('-NaN')).to_decimal() + Decimal('-NaN') + >>> Decimal128(Decimal('sNaN')).to_decimal() + Decimal('sNaN') + >>> Decimal128(Decimal('-sNaN')).to_decimal() + Decimal('-sNaN') + + Two instances of :class:`Decimal128` compare equal if their Binary + Integer Decimal encodings are equal:: + + >>> Decimal128('NaN') == Decimal128('NaN') + True + >>> Decimal128('NaN').bid == Decimal128('NaN').bid + True + + This differs from :class:`decimal.Decimal` comparisons for NaN:: + + >>> Decimal('NaN') == Decimal('NaN') + False + """ + __slots__ = ('__high', '__low') + + _type_marker = 19 + + def __init__(self, value): + if isinstance(value, (_string_type, decimal.Decimal)): + self.__high, self.__low = _decimal_to_128(value) + elif isinstance(value, (list, tuple)): + if len(value) != 2: + raise ValueError('Invalid size for creation of Decimal128 ' + 'from list or tuple. Must have exactly 2 ' + 'elements.') + self.__high, self.__low = value + else: + raise TypeError("Cannot convert %r to Decimal128" % (value,)) + + def to_decimal(self): + """Returns an instance of :class:`decimal.Decimal` for this + :class:`Decimal128`. + """ + high = self.__high + low = self.__low + sign = 1 if (high & _SIGN) else 0 + + if (high & _SNAN) == _SNAN: + return decimal.Decimal((sign, (), 'N')) + elif (high & _NAN) == _NAN: + return decimal.Decimal((sign, (), 'n')) + elif (high & _INF) == _INF: + return decimal.Decimal((sign, (), 'F')) + + if (high & _EXPONENT_MASK) == _EXPONENT_MASK: + exponent = ((high & 0x1fffe00000000000) >> 47) - _EXPONENT_BIAS + return decimal.Decimal((sign, (0,), exponent)) + else: + exponent = ((high & 0x7fff800000000000) >> 49) - _EXPONENT_BIAS + + arr = bytearray(15) + mask = 0x00000000000000ff + for i in range(14, 6, -1): + arr[i] = (low & mask) >> ((14 - i) << 3) + mask = mask << 8 + + mask = 0x00000000000000ff + for i in range(6, 0, -1): + arr[i] = (high & mask) >> ((6 - i) << 3) + mask = mask << 8 + + mask = 0x0001000000000000 + arr[0] = (high & mask) >> 48 + + # cdecimal only accepts a tuple for digits. + digits = tuple( + int(digit) for digit in str(_from_bytes(arr, 'big'))) + + with decimal.localcontext(_DEC128_CTX) as ctx: + return ctx.create_decimal((sign, digits, exponent)) + + @classmethod + def from_bid(cls, value): + """Create an instance of :class:`Decimal128` from Binary Integer + Decimal string. + + :Parameters: + - `value`: 16 byte string (128-bit IEEE 754-2008 decimal floating + point in Binary Integer Decimal (BID) format). + """ + if not isinstance(value, bytes): + raise TypeError("value must be an instance of bytes") + if len(value) != 16: + raise ValueError("value must be exactly 16 bytes") + return cls((_UNPACK_64(value[8:])[0], _UNPACK_64(value[:8])[0])) + + @property + def bid(self): + """The Binary Integer Decimal (BID) encoding of this instance.""" + return _PACK_64(self.__low) + _PACK_64(self.__high) + + def __str__(self): + dec = self.to_decimal() + if dec.is_nan(): + # Required by the drivers spec to match MongoDB behavior. + return "NaN" + return str(dec) + + def __repr__(self): + return "Decimal128('%s')" % (str(self),) + + def __setstate__(self, value): + self.__high, self.__low = value + + def __getstate__(self): + return self.__high, self.__low + + def __eq__(self, other): + if isinstance(other, Decimal128): + return self.bid == other.bid + return NotImplemented + + def __ne__(self, other): + return not self == other diff --git a/backend/venv/lib/python3.7/site-packages/bson/errors.py b/backend/venv/lib/python3.7/site-packages/bson/errors.py new file mode 100644 index 000000000..9bdb74137 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/errors.py @@ -0,0 +1,40 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Exceptions raised by the BSON package.""" + + +class BSONError(Exception): + """Base class for all BSON exceptions. + """ + + +class InvalidBSON(BSONError): + """Raised when trying to create a BSON object from invalid data. + """ + + +class InvalidStringData(BSONError): + """Raised when trying to encode a string containing non-UTF8 data. + """ + + +class InvalidDocument(BSONError): + """Raised when trying to create a BSON object from an invalid document. + """ + + +class InvalidId(BSONError): + """Raised when trying to create an ObjectId from invalid data. + """ diff --git a/backend/venv/lib/python3.7/site-packages/bson/int64.py b/backend/venv/lib/python3.7/site-packages/bson/int64.py new file mode 100644 index 000000000..77e981230 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/int64.py @@ -0,0 +1,34 @@ +# Copyright 2014-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A BSON wrapper for long (int in python3)""" + +from bson.py3compat import PY3 + +if PY3: + long = int + + +class Int64(long): + """Representation of the BSON int64 type. + + This is necessary because every integral number is an :class:`int` in + Python 3. Small integral numbers are encoded to BSON int32 by default, + but Int64 numbers will always be encoded to BSON int64. + + :Parameters: + - `value`: the numeric value to represent + """ + + _type_marker = 18 diff --git a/backend/venv/lib/python3.7/site-packages/bson/json_util.py b/backend/venv/lib/python3.7/site-packages/bson/json_util.py new file mode 100644 index 000000000..871f71143 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/json_util.py @@ -0,0 +1,829 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for using Python's :mod:`json` module with BSON documents. + +This module provides two helper methods `dumps` and `loads` that wrap the +native :mod:`json` methods and provide explicit BSON conversion to and from +JSON. :class:`~bson.json_util.JSONOptions` provides a way to control how JSON +is emitted and parsed, with the default being the legacy PyMongo format. +:mod:`~bson.json_util` can also generate Canonical or Relaxed `Extended JSON`_ +when :const:`CANONICAL_JSON_OPTIONS` or :const:`RELAXED_JSON_OPTIONS` is +provided, respectively. + +.. _Extended JSON: https://github.com/mongodb/specifications/blob/master/source/extended-json.rst + +Example usage (deserialization): + +.. doctest:: + + >>> from bson.json_util import loads + >>> loads('[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$scope": {}, "$code": "function x() { return 1; }"}}, {"bin": {"$type": "80", "$binary": "AQIDBA=="}}]') + [{u'foo': [1, 2]}, {u'bar': {u'hello': u'world'}}, {u'code': Code('function x() { return 1; }', {})}, {u'bin': Binary('...', 128)}] + +Example usage (serialization): + +.. doctest:: + + >>> from bson import Binary, Code + >>> from bson.json_util import dumps + >>> dumps([{'foo': [1, 2]}, + ... {'bar': {'hello': 'world'}}, + ... {'code': Code("function x() { return 1; }", {})}, + ... {'bin': Binary(b"\x01\x02\x03\x04")}]) + '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }", "$scope": {}}}, {"bin": {"$binary": "AQIDBA==", "$type": "00"}}]' + +Example usage (with :const:`CANONICAL_JSON_OPTIONS`): + +.. doctest:: + + >>> from bson import Binary, Code + >>> from bson.json_util import dumps, CANONICAL_JSON_OPTIONS + >>> dumps([{'foo': [1, 2]}, + ... {'bar': {'hello': 'world'}}, + ... {'code': Code("function x() { return 1; }")}, + ... {'bin': Binary(b"\x01\x02\x03\x04")}], + ... json_options=CANONICAL_JSON_OPTIONS) + '[{"foo": [{"$numberInt": "1"}, {"$numberInt": "2"}]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }"}}, {"bin": {"$binary": {"base64": "AQIDBA==", "subType": "00"}}}]' + +Example usage (with :const:`RELAXED_JSON_OPTIONS`): + +.. doctest:: + + >>> from bson import Binary, Code + >>> from bson.json_util import dumps, RELAXED_JSON_OPTIONS + >>> dumps([{'foo': [1, 2]}, + ... {'bar': {'hello': 'world'}}, + ... {'code': Code("function x() { return 1; }")}, + ... {'bin': Binary(b"\x01\x02\x03\x04")}], + ... json_options=RELAXED_JSON_OPTIONS) + '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }"}}, {"bin": {"$binary": {"base64": "AQIDBA==", "subType": "00"}}}]' + +Alternatively, you can manually pass the `default` to :func:`json.dumps`. +It won't handle :class:`~bson.binary.Binary` and :class:`~bson.code.Code` +instances (as they are extended strings you can't provide custom defaults), +but it will be faster as there is less recursion. + +.. note:: + If your application does not need the flexibility offered by + :class:`JSONOptions` and spends a large amount of time in the `json_util` + module, look to + `python-bsonjs `_ for a nice + performance improvement. `python-bsonjs` is a fast BSON to MongoDB + Extended JSON converter for Python built on top of + `libbson `_. `python-bsonjs` works best + with PyMongo when using :class:`~bson.raw_bson.RawBSONDocument`. + +.. versionchanged:: 2.8 + The output format for :class:`~bson.timestamp.Timestamp` has changed from + '{"t": , "i": }' to '{"$timestamp": {"t": , "i": }}'. + This new format will be decoded to an instance of + :class:`~bson.timestamp.Timestamp`. The old format will continue to be + decoded to a python dict as before. Encoding to the old format is no longer + supported as it was never correct and loses type information. + Added support for $numberLong and $undefined - new in MongoDB 2.6 - and + parsing $date in ISO-8601 format. + +.. versionchanged:: 2.7 + Preserves order when rendering SON, Timestamp, Code, Binary, and DBRef + instances. + +.. versionchanged:: 2.3 + Added dumps and loads helpers to automatically handle conversion to and + from json and supports :class:`~bson.binary.Binary` and + :class:`~bson.code.Code` +""" + +import base64 +import datetime +import json +import math +import re +import sys +import uuid + +from pymongo.errors import ConfigurationError + +import bson +from bson import EPOCH_AWARE, EPOCH_NAIVE, RE_TYPE, SON +from bson.binary import (Binary, JAVA_LEGACY, CSHARP_LEGACY, OLD_UUID_SUBTYPE, + UUID_SUBTYPE) +from bson.code import Code +from bson.codec_options import CodecOptions +from bson.dbref import DBRef +from bson.decimal128 import Decimal128 +from bson.int64 import Int64 +from bson.max_key import MaxKey +from bson.min_key import MinKey +from bson.objectid import ObjectId +from bson.py3compat import (PY3, iteritems, integer_types, string_type, + text_type) +from bson.regex import Regex +from bson.timestamp import Timestamp +from bson.tz_util import utc + + +_RE_OPT_TABLE = { + "i": re.I, + "l": re.L, + "m": re.M, + "s": re.S, + "u": re.U, + "x": re.X, +} + +# Dollar-prefixed keys which may appear in DBRefs. +_DBREF_KEYS = frozenset(['$id', '$ref', '$db']) + + +class DatetimeRepresentation: + LEGACY = 0 + """Legacy MongoDB Extended JSON datetime representation. + + :class:`datetime.datetime` instances will be encoded to JSON in the + format `{"$date": }`, where `dateAsMilliseconds` is + a 64-bit signed integer giving the number of milliseconds since the Unix + epoch UTC. This was the default encoding before PyMongo version 3.4. + + .. versionadded:: 3.4 + """ + + NUMBERLONG = 1 + """NumberLong datetime representation. + + :class:`datetime.datetime` instances will be encoded to JSON in the + format `{"$date": {"$numberLong": ""}}`, + where `dateAsMilliseconds` is the string representation of a 64-bit signed + integer giving the number of milliseconds since the Unix epoch UTC. + + .. versionadded:: 3.4 + """ + + ISO8601 = 2 + """ISO-8601 datetime representation. + + :class:`datetime.datetime` instances greater than or equal to the Unix + epoch UTC will be encoded to JSON in the format `{"$date": ""}`. + :class:`datetime.datetime` instances before the Unix epoch UTC will be + encoded as if the datetime representation is + :const:`~DatetimeRepresentation.NUMBERLONG`. + + .. versionadded:: 3.4 + """ + + +class JSONMode: + LEGACY = 0 + """Legacy Extended JSON representation. + + In this mode, :func:`~bson.json_util.dumps` produces PyMongo's legacy + non-standard JSON output. Consider using + :const:`~bson.json_util.JSONMode.RELAXED` or + :const:`~bson.json_util.JSONMode.CANONICAL` instead. + + .. versionadded:: 3.5 + """ + + RELAXED = 1 + """Relaxed Extended JSON representation. + + In this mode, :func:`~bson.json_util.dumps` produces Relaxed Extended JSON, + a mostly JSON-like format. Consider using this for things like a web API, + where one is sending a document (or a projection of a document) that only + uses ordinary JSON type primitives. In particular, the ``int``, + :class:`~bson.int64.Int64`, and ``float`` numeric types are represented in + the native JSON number format. This output is also the most human readable + and is useful for debugging and documentation. + + .. seealso:: The specification for Relaxed `Extended JSON`_. + + .. versionadded:: 3.5 + """ + + CANONICAL = 2 + """Canonical Extended JSON representation. + + In this mode, :func:`~bson.json_util.dumps` produces Canonical Extended + JSON, a type preserving format. Consider using this for things like + testing, where one has to precisely specify expected types in JSON. In + particular, the ``int``, :class:`~bson.int64.Int64`, and ``float`` numeric + types are encoded with type wrappers. + + .. seealso:: The specification for Canonical `Extended JSON`_. + + .. versionadded:: 3.5 + """ + + +class JSONOptions(CodecOptions): + """Encapsulates JSON options for :func:`dumps` and :func:`loads`. + + :Parameters: + - `strict_number_long`: If ``True``, :class:`~bson.int64.Int64` objects + are encoded to MongoDB Extended JSON's *Strict mode* type + `NumberLong`, ie ``'{"$numberLong": "" }'``. Otherwise they + will be encoded as an `int`. Defaults to ``False``. + - `datetime_representation`: The representation to use when encoding + instances of :class:`datetime.datetime`. Defaults to + :const:`~DatetimeRepresentation.LEGACY`. + - `strict_uuid`: If ``True``, :class:`uuid.UUID` object are encoded to + MongoDB Extended JSON's *Strict mode* type `Binary`. Otherwise it + will be encoded as ``'{"$uuid": "" }'``. Defaults to ``False``. + - `json_mode`: The :class:`JSONMode` to use when encoding BSON types to + Extended JSON. Defaults to :const:`~JSONMode.LEGACY`. + - `document_class`: BSON documents returned by :func:`loads` will be + decoded to an instance of this class. Must be a subclass of + :class:`collections.MutableMapping`. Defaults to :class:`dict`. + - `uuid_representation`: The BSON representation to use when encoding + and decoding instances of :class:`uuid.UUID`. Defaults to + :const:`~bson.binary.PYTHON_LEGACY`. + - `tz_aware`: If ``True``, MongoDB Extended JSON's *Strict mode* type + `Date` will be decoded to timezone aware instances of + :class:`datetime.datetime`. Otherwise they will be naive. Defaults + to ``True``. + - `tzinfo`: A :class:`datetime.tzinfo` subclass that specifies the + timezone from which :class:`~datetime.datetime` objects should be + decoded. Defaults to :const:`~bson.tz_util.utc`. + - `args`: arguments to :class:`~bson.codec_options.CodecOptions` + - `kwargs`: arguments to :class:`~bson.codec_options.CodecOptions` + + .. seealso:: The specification for Relaxed and Canonical `Extended JSON`_. + + .. versionadded:: 3.4 + + .. versionchanged:: 3.5 + Accepts the optional parameter `json_mode`. + + """ + + def __new__(cls, strict_number_long=False, + datetime_representation=DatetimeRepresentation.LEGACY, + strict_uuid=False, json_mode=JSONMode.LEGACY, + *args, **kwargs): + kwargs["tz_aware"] = kwargs.get("tz_aware", True) + if kwargs["tz_aware"]: + kwargs["tzinfo"] = kwargs.get("tzinfo", utc) + if datetime_representation not in (DatetimeRepresentation.LEGACY, + DatetimeRepresentation.NUMBERLONG, + DatetimeRepresentation.ISO8601): + raise ConfigurationError( + "JSONOptions.datetime_representation must be one of LEGACY, " + "NUMBERLONG, or ISO8601 from DatetimeRepresentation.") + self = super(JSONOptions, cls).__new__(cls, *args, **kwargs) + if json_mode not in (JSONMode.LEGACY, + JSONMode.RELAXED, + JSONMode.CANONICAL): + raise ConfigurationError( + "JSONOptions.json_mode must be one of LEGACY, RELAXED, " + "or CANONICAL from JSONMode.") + self.json_mode = json_mode + if self.json_mode == JSONMode.RELAXED: + self.strict_number_long = False + self.datetime_representation = DatetimeRepresentation.ISO8601 + self.strict_uuid = True + elif self.json_mode == JSONMode.CANONICAL: + self.strict_number_long = True + self.datetime_representation = DatetimeRepresentation.NUMBERLONG + self.strict_uuid = True + else: + self.strict_number_long = strict_number_long + self.datetime_representation = datetime_representation + self.strict_uuid = strict_uuid + return self + + def _arguments_repr(self): + return ('strict_number_long=%r, ' + 'datetime_representation=%r, ' + 'strict_uuid=%r, json_mode=%r, %s' % ( + self.strict_number_long, + self.datetime_representation, + self.strict_uuid, + self.json_mode, + super(JSONOptions, self)._arguments_repr())) + + +LEGACY_JSON_OPTIONS = JSONOptions(json_mode=JSONMode.LEGACY) +""":class:`JSONOptions` for encoding to PyMongo's legacy JSON format. + +.. seealso:: The documentation for :const:`bson.json_util.JSONMode.LEGACY`. + +.. versionadded:: 3.5 +""" + +DEFAULT_JSON_OPTIONS = LEGACY_JSON_OPTIONS +"""The default :class:`JSONOptions` for JSON encoding/decoding. + +The same as :const:`LEGACY_JSON_OPTIONS`. This will change to +:const:`RELAXED_JSON_OPTIONS` in a future release. + +.. versionadded:: 3.4 +""" + +CANONICAL_JSON_OPTIONS = JSONOptions(json_mode=JSONMode.CANONICAL) +""":class:`JSONOptions` for Canonical Extended JSON. + +.. seealso:: The documentation for :const:`bson.json_util.JSONMode.CANONICAL`. + +.. versionadded:: 3.5 +""" + +RELAXED_JSON_OPTIONS = JSONOptions(json_mode=JSONMode.RELAXED) +""":class:`JSONOptions` for Relaxed Extended JSON. + +.. seealso:: The documentation for :const:`bson.json_util.JSONMode.RELAXED`. + +.. versionadded:: 3.5 +""" + +STRICT_JSON_OPTIONS = JSONOptions( + strict_number_long=True, + datetime_representation=DatetimeRepresentation.ISO8601, + strict_uuid=True) +"""**DEPRECATED** - :class:`JSONOptions` for MongoDB Extended JSON's *Strict +mode* encoding. + +.. versionadded:: 3.4 + +.. versionchanged:: 3.5 + Deprecated. Use :const:`RELAXED_JSON_OPTIONS` or + :const:`CANONICAL_JSON_OPTIONS` instead. +""" + + +def dumps(obj, *args, **kwargs): + """Helper function that wraps :func:`json.dumps`. + + Recursive function that handles all BSON types including + :class:`~bson.binary.Binary` and :class:`~bson.code.Code`. + + :Parameters: + - `json_options`: A :class:`JSONOptions` instance used to modify the + encoding of MongoDB Extended JSON types. Defaults to + :const:`DEFAULT_JSON_OPTIONS`. + + .. versionchanged:: 3.4 + Accepts optional parameter `json_options`. See :class:`JSONOptions`. + + .. versionchanged:: 2.7 + Preserves order when rendering SON, Timestamp, Code, Binary, and DBRef + instances. + """ + json_options = kwargs.pop("json_options", DEFAULT_JSON_OPTIONS) + return json.dumps(_json_convert(obj, json_options), *args, **kwargs) + + +def loads(s, *args, **kwargs): + """Helper function that wraps :func:`json.loads`. + + Automatically passes the object_hook for BSON type conversion. + + Raises ``TypeError``, ``ValueError``, ``KeyError``, or + :exc:`~bson.errors.InvalidId` on invalid MongoDB Extended JSON. + + :Parameters: + - `json_options`: A :class:`JSONOptions` instance used to modify the + decoding of MongoDB Extended JSON types. Defaults to + :const:`DEFAULT_JSON_OPTIONS`. + + .. versionchanged:: 3.5 + Parses Relaxed and Canonical Extended JSON as well as PyMongo's legacy + format. Now raises ``TypeError`` or ``ValueError`` when parsing JSON + type wrappers with values of the wrong type or any extra keys. + + .. versionchanged:: 3.4 + Accepts optional parameter `json_options`. See :class:`JSONOptions`. + """ + json_options = kwargs.pop("json_options", DEFAULT_JSON_OPTIONS) + kwargs["object_pairs_hook"] = lambda pairs: object_pairs_hook( + pairs, json_options) + return json.loads(s, *args, **kwargs) + + +def _json_convert(obj, json_options=DEFAULT_JSON_OPTIONS): + """Recursive helper method that converts BSON types so they can be + converted into json. + """ + if hasattr(obj, 'iteritems') or hasattr(obj, 'items'): # PY3 support + return SON(((k, _json_convert(v, json_options)) + for k, v in iteritems(obj))) + elif hasattr(obj, '__iter__') and not isinstance(obj, (text_type, bytes)): + return list((_json_convert(v, json_options) for v in obj)) + try: + return default(obj, json_options) + except TypeError: + return obj + + +def object_pairs_hook(pairs, json_options=DEFAULT_JSON_OPTIONS): + return object_hook(json_options.document_class(pairs), json_options) + + +def object_hook(dct, json_options=DEFAULT_JSON_OPTIONS): + if "$oid" in dct: + return _parse_canonical_oid(dct) + if "$ref" in dct: + return _parse_canonical_dbref(dct) + if "$date" in dct: + return _parse_canonical_datetime(dct, json_options) + if "$regex" in dct: + return _parse_legacy_regex(dct) + if "$minKey" in dct: + return _parse_canonical_minkey(dct) + if "$maxKey" in dct: + return _parse_canonical_maxkey(dct) + if "$binary" in dct: + if "$type" in dct: + return _parse_legacy_binary(dct, json_options) + else: + return _parse_canonical_binary(dct, json_options) + if "$code" in dct: + return _parse_canonical_code(dct) + if "$uuid" in dct: + return _parse_legacy_uuid(dct) + if "$undefined" in dct: + return None + if "$numberLong" in dct: + return _parse_canonical_int64(dct) + if "$timestamp" in dct: + tsp = dct["$timestamp"] + return Timestamp(tsp["t"], tsp["i"]) + if "$numberDecimal" in dct: + return _parse_canonical_decimal128(dct) + if "$dbPointer" in dct: + return _parse_canonical_dbpointer(dct) + if "$regularExpression" in dct: + return _parse_canonical_regex(dct) + if "$symbol" in dct: + return _parse_canonical_symbol(dct) + if "$numberInt" in dct: + return _parse_canonical_int32(dct) + if "$numberDouble" in dct: + return _parse_canonical_double(dct) + return dct + + +def _parse_legacy_regex(doc): + pattern = doc["$regex"] + # Check if this is the $regex query operator. + if isinstance(pattern, Regex): + return doc + flags = 0 + # PyMongo always adds $options but some other tools may not. + for opt in doc.get("$options", ""): + flags |= _RE_OPT_TABLE.get(opt, 0) + return Regex(pattern, flags) + + +def _parse_legacy_uuid(doc): + """Decode a JSON legacy $uuid to Python UUID.""" + if len(doc) != 1: + raise TypeError('Bad $uuid, extra field(s): %s' % (doc,)) + return uuid.UUID(doc["$uuid"]) + + +def _binary_or_uuid(data, subtype, json_options): + # special handling for UUID + if subtype == OLD_UUID_SUBTYPE: + if json_options.uuid_representation == CSHARP_LEGACY: + return uuid.UUID(bytes_le=data) + if json_options.uuid_representation == JAVA_LEGACY: + data = data[7::-1] + data[:7:-1] + return uuid.UUID(bytes=data) + if subtype == UUID_SUBTYPE: + return uuid.UUID(bytes=data) + if PY3 and subtype == 0: + return data + return Binary(data, subtype) + + +def _parse_legacy_binary(doc, json_options): + if isinstance(doc["$type"], int): + doc["$type"] = "%02x" % doc["$type"] + subtype = int(doc["$type"], 16) + if subtype >= 0xffffff80: # Handle mongoexport values + subtype = int(doc["$type"][6:], 16) + data = base64.b64decode(doc["$binary"].encode()) + return _binary_or_uuid(data, subtype, json_options) + + +def _parse_canonical_binary(doc, json_options): + binary = doc["$binary"] + b64 = binary["base64"] + subtype = binary["subType"] + if not isinstance(b64, string_type): + raise TypeError('$binary base64 must be a string: %s' % (doc,)) + if not isinstance(subtype, string_type) or len(subtype) > 2: + raise TypeError('$binary subType must be a string at most 2 ' + 'characters: %s' % (doc,)) + if len(binary) != 2: + raise TypeError('$binary must include only "base64" and "subType" ' + 'components: %s' % (doc,)) + + data = base64.b64decode(b64.encode()) + return _binary_or_uuid(data, int(subtype, 16), json_options) + + +def _parse_canonical_datetime(doc, json_options): + """Decode a JSON datetime to python datetime.datetime.""" + dtm = doc["$date"] + if len(doc) != 1: + raise TypeError('Bad $date, extra field(s): %s' % (doc,)) + # mongoexport 2.6 and newer + if isinstance(dtm, string_type): + # Parse offset + if dtm[-1] == 'Z': + dt = dtm[:-1] + offset = 'Z' + elif dtm[-3] == ':': + # (+|-)HH:MM + dt = dtm[:-6] + offset = dtm[-6:] + elif dtm[-5] in ('+', '-'): + # (+|-)HHMM + dt = dtm[:-5] + offset = dtm[-5:] + elif dtm[-3] in ('+', '-'): + # (+|-)HH + dt = dtm[:-3] + offset = dtm[-3:] + else: + dt = dtm + offset = '' + + # Parse the optional factional seconds portion. + dot_index = dt.rfind('.') + microsecond = 0 + if dot_index != -1: + microsecond = int(float(dt[dot_index:]) * 1000000) + dt = dt[:dot_index] + + aware = datetime.datetime.strptime( + dt, "%Y-%m-%dT%H:%M:%S").replace(microsecond=microsecond, + tzinfo=utc) + + if offset and offset != 'Z': + if len(offset) == 6: + hours, minutes = offset[1:].split(':') + secs = (int(hours) * 3600 + int(minutes) * 60) + elif len(offset) == 5: + secs = (int(offset[1:3]) * 3600 + int(offset[3:]) * 60) + elif len(offset) == 3: + secs = int(offset[1:3]) * 3600 + if offset[0] == "-": + secs *= -1 + aware = aware - datetime.timedelta(seconds=secs) + + if json_options.tz_aware: + if json_options.tzinfo: + aware = aware.astimezone(json_options.tzinfo) + return aware + else: + return aware.replace(tzinfo=None) + return bson._millis_to_datetime(int(dtm), json_options) + + +def _parse_canonical_oid(doc): + """Decode a JSON ObjectId to bson.objectid.ObjectId.""" + if len(doc) != 1: + raise TypeError('Bad $oid, extra field(s): %s' % (doc,)) + return ObjectId(doc['$oid']) + + +def _parse_canonical_symbol(doc): + """Decode a JSON symbol to Python string.""" + symbol = doc['$symbol'] + if len(doc) != 1: + raise TypeError('Bad $symbol, extra field(s): %s' % (doc,)) + return text_type(symbol) + + +def _parse_canonical_code(doc): + """Decode a JSON code to bson.code.Code.""" + for key in doc: + if key not in ('$code', '$scope'): + raise TypeError('Bad $code, extra field(s): %s' % (doc,)) + return Code(doc['$code'], scope=doc.get('$scope')) + + +def _parse_canonical_regex(doc): + """Decode a JSON regex to bson.regex.Regex.""" + regex = doc['$regularExpression'] + if len(doc) != 1: + raise TypeError('Bad $regularExpression, extra field(s): %s' % (doc,)) + if len(regex) != 2: + raise TypeError('Bad $regularExpression must include only "pattern"' + 'and "options" components: %s' % (doc,)) + return Regex(regex['pattern'], regex['options']) + + +def _parse_canonical_dbref(doc): + """Decode a JSON DBRef to bson.dbref.DBRef.""" + for key in doc: + if key.startswith('$') and key not in _DBREF_KEYS: + # Other keys start with $, so dct cannot be parsed as a DBRef. + return doc + return DBRef(doc.pop('$ref'), doc.pop('$id'), + database=doc.pop('$db', None), **doc) + + +def _parse_canonical_dbpointer(doc): + """Decode a JSON (deprecated) DBPointer to bson.dbref.DBRef.""" + dbref = doc['$dbPointer'] + if len(doc) != 1: + raise TypeError('Bad $dbPointer, extra field(s): %s' % (doc,)) + if isinstance(dbref, DBRef): + dbref_doc = dbref.as_doc() + # DBPointer must not contain $db in its value. + if dbref.database is not None: + raise TypeError( + 'Bad $dbPointer, extra field $db: %s' % (dbref_doc,)) + if not isinstance(dbref.id, ObjectId): + raise TypeError( + 'Bad $dbPointer, $id must be an ObjectId: %s' % (dbref_doc,)) + if len(dbref_doc) != 2: + raise TypeError( + 'Bad $dbPointer, extra field(s) in DBRef: %s' % (dbref_doc,)) + return dbref + else: + raise TypeError('Bad $dbPointer, expected a DBRef: %s' % (doc,)) + + +def _parse_canonical_int32(doc): + """Decode a JSON int32 to python int.""" + i_str = doc['$numberInt'] + if len(doc) != 1: + raise TypeError('Bad $numberInt, extra field(s): %s' % (doc,)) + if not isinstance(i_str, string_type): + raise TypeError('$numberInt must be string: %s' % (doc,)) + return int(i_str) + + +def _parse_canonical_int64(doc): + """Decode a JSON int64 to bson.int64.Int64.""" + l_str = doc['$numberLong'] + if len(doc) != 1: + raise TypeError('Bad $numberLong, extra field(s): %s' % (doc,)) + return Int64(l_str) + + +def _parse_canonical_double(doc): + """Decode a JSON double to python float.""" + d_str = doc['$numberDouble'] + if len(doc) != 1: + raise TypeError('Bad $numberDouble, extra field(s): %s' % (doc,)) + if not isinstance(d_str, string_type): + raise TypeError('$numberDouble must be string: %s' % (doc,)) + return float(d_str) + + +def _parse_canonical_decimal128(doc): + """Decode a JSON decimal128 to bson.decimal128.Decimal128.""" + d_str = doc['$numberDecimal'] + if len(doc) != 1: + raise TypeError('Bad $numberDecimal, extra field(s): %s' % (doc,)) + if not isinstance(d_str, string_type): + raise TypeError('$numberDecimal must be string: %s' % (doc,)) + return Decimal128(d_str) + + +def _parse_canonical_minkey(doc): + """Decode a JSON MinKey to bson.min_key.MinKey.""" + if doc['$minKey'] is not 1: + raise TypeError('$minKey value must be 1: %s' % (doc,)) + if len(doc) != 1: + raise TypeError('Bad $minKey, extra field(s): %s' % (doc,)) + return MinKey() + + +def _parse_canonical_maxkey(doc): + """Decode a JSON MaxKey to bson.max_key.MaxKey.""" + if doc['$maxKey'] is not 1: + raise TypeError('$maxKey value must be 1: %s', (doc,)) + if len(doc) != 1: + raise TypeError('Bad $minKey, extra field(s): %s' % (doc,)) + return MaxKey() + + +def _encode_binary(data, subtype, json_options): + if json_options.json_mode == JSONMode.LEGACY: + return SON([ + ('$binary', base64.b64encode(data).decode()), + ('$type', "%02x" % subtype)]) + return {'$binary': SON([ + ('base64', base64.b64encode(data).decode()), + ('subType', "%02x" % subtype)])} + + +def default(obj, json_options=DEFAULT_JSON_OPTIONS): + # We preserve key order when rendering SON, DBRef, etc. as JSON by + # returning a SON for those types instead of a dict. + if isinstance(obj, ObjectId): + return {"$oid": str(obj)} + if isinstance(obj, DBRef): + return _json_convert(obj.as_doc(), json_options=json_options) + if isinstance(obj, datetime.datetime): + if (json_options.datetime_representation == + DatetimeRepresentation.ISO8601): + if not obj.tzinfo: + obj = obj.replace(tzinfo=utc) + if obj >= EPOCH_AWARE: + off = obj.tzinfo.utcoffset(obj) + if (off.days, off.seconds, off.microseconds) == (0, 0, 0): + tz_string = 'Z' + else: + tz_string = obj.strftime('%z') + millis = int(obj.microsecond / 1000) + fracsecs = ".%03d" % (millis,) if millis else "" + return {"$date": "%s%s%s" % ( + obj.strftime("%Y-%m-%dT%H:%M:%S"), fracsecs, tz_string)} + + millis = bson._datetime_to_millis(obj) + if (json_options.datetime_representation == + DatetimeRepresentation.LEGACY): + return {"$date": millis} + return {"$date": {"$numberLong": str(millis)}} + if json_options.strict_number_long and isinstance(obj, Int64): + return {"$numberLong": str(obj)} + if isinstance(obj, (RE_TYPE, Regex)): + flags = "" + if obj.flags & re.IGNORECASE: + flags += "i" + if obj.flags & re.LOCALE: + flags += "l" + if obj.flags & re.MULTILINE: + flags += "m" + if obj.flags & re.DOTALL: + flags += "s" + if obj.flags & re.UNICODE: + flags += "u" + if obj.flags & re.VERBOSE: + flags += "x" + if isinstance(obj.pattern, text_type): + pattern = obj.pattern + else: + pattern = obj.pattern.decode('utf-8') + if json_options.json_mode == JSONMode.LEGACY: + return SON([("$regex", pattern), ("$options", flags)]) + return {'$regularExpression': SON([("pattern", pattern), + ("options", flags)])} + if isinstance(obj, MinKey): + return {"$minKey": 1} + if isinstance(obj, MaxKey): + return {"$maxKey": 1} + if isinstance(obj, Timestamp): + return {"$timestamp": SON([("t", obj.time), ("i", obj.inc)])} + if isinstance(obj, Code): + if obj.scope is None: + return {'$code': str(obj)} + return SON([ + ('$code', str(obj)), + ('$scope', _json_convert(obj.scope, json_options))]) + if isinstance(obj, Binary): + return _encode_binary(obj, obj.subtype, json_options) + if PY3 and isinstance(obj, bytes): + return _encode_binary(obj, 0, json_options) + if isinstance(obj, uuid.UUID): + if json_options.strict_uuid: + data = obj.bytes + subtype = OLD_UUID_SUBTYPE + if json_options.uuid_representation == CSHARP_LEGACY: + data = obj.bytes_le + elif json_options.uuid_representation == JAVA_LEGACY: + data = data[7::-1] + data[:7:-1] + elif json_options.uuid_representation == UUID_SUBTYPE: + subtype = UUID_SUBTYPE + return _encode_binary(data, subtype, json_options) + else: + return {"$uuid": obj.hex} + if isinstance(obj, Decimal128): + return {"$numberDecimal": str(obj)} + if isinstance(obj, bool): + return obj + if (json_options.json_mode == JSONMode.CANONICAL and + isinstance(obj, integer_types)): + if -2 ** 31 <= obj < 2 ** 31: + return {'$numberInt': text_type(obj)} + return {'$numberLong': text_type(obj)} + if json_options.json_mode != JSONMode.LEGACY and isinstance(obj, float): + if math.isnan(obj): + return {'$numberDouble': 'NaN'} + elif math.isinf(obj): + representation = 'Infinity' if obj > 0 else '-Infinity' + return {'$numberDouble': representation} + elif json_options.json_mode == JSONMode.CANONICAL: + # repr() will return the shortest string guaranteed to produce the + # original value, when float() is called on it. str produces a + # shorter string in Python 2. + return {'$numberDouble': text_type(repr(obj))} + raise TypeError("%r is not JSON serializable" % obj) diff --git a/backend/venv/lib/python3.7/site-packages/bson/max_key.py b/backend/venv/lib/python3.7/site-packages/bson/max_key.py new file mode 100644 index 000000000..7e89dd70d --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/max_key.py @@ -0,0 +1,50 @@ +# Copyright 2010-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Representation for the MongoDB internal MaxKey type. +""" + + +class MaxKey(object): + """MongoDB internal MaxKey type. + + .. versionchanged:: 2.7 + ``MaxKey`` now implements comparison operators. + """ + + _type_marker = 127 + + def __eq__(self, other): + return isinstance(other, MaxKey) + + def __hash__(self): + return hash(self._type_marker) + + def __ne__(self, other): + return not self == other + + def __le__(self, other): + return isinstance(other, MaxKey) + + def __lt__(self, dummy): + return False + + def __ge__(self, dummy): + return True + + def __gt__(self, other): + return not isinstance(other, MaxKey) + + def __repr__(self): + return "MaxKey()" diff --git a/backend/venv/lib/python3.7/site-packages/bson/min_key.py b/backend/venv/lib/python3.7/site-packages/bson/min_key.py new file mode 100644 index 000000000..b03520e9c --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/min_key.py @@ -0,0 +1,50 @@ +# Copyright 2010-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Representation for the MongoDB internal MinKey type. +""" + + +class MinKey(object): + """MongoDB internal MinKey type. + + .. versionchanged:: 2.7 + ``MinKey`` now implements comparison operators. + """ + + _type_marker = 255 + + def __eq__(self, other): + return isinstance(other, MinKey) + + def __hash__(self): + return hash(self._type_marker) + + def __ne__(self, other): + return not self == other + + def __le__(self, dummy): + return True + + def __lt__(self, other): + return not isinstance(other, MinKey) + + def __ge__(self, other): + return isinstance(other, MinKey) + + def __gt__(self, dummy): + return False + + def __repr__(self): + return "MinKey()" diff --git a/backend/venv/lib/python3.7/site-packages/bson/objectid.py b/backend/venv/lib/python3.7/site-packages/bson/objectid.py new file mode 100644 index 000000000..8b7ad8ec8 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/objectid.py @@ -0,0 +1,299 @@ +# Copyright 2009-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for working with MongoDB `ObjectIds +`_. +""" + +import binascii +import calendar +import datetime +import os +import struct +import threading +import time + +from random import SystemRandom + +from bson.errors import InvalidId +from bson.py3compat import PY3, bytes_from_hex, string_type, text_type +from bson.tz_util import utc + + +_MAX_COUNTER_VALUE = 0xFFFFFF + + +def _raise_invalid_id(oid): + raise InvalidId( + "%r is not a valid ObjectId, it must be a 12-byte input" + " or a 24-character hex string" % oid) + + +def _random_bytes(): + """Get the 5-byte random field of an ObjectId.""" + return struct.pack(">Q", SystemRandom().randint(0, 0xFFFFFFFFFF))[3:] + + +class ObjectId(object): + """A MongoDB ObjectId. + """ + + _pid = os.getpid() + + _inc = SystemRandom().randint(0, _MAX_COUNTER_VALUE) + _inc_lock = threading.Lock() + + __random = _random_bytes() + + __slots__ = ('__id',) + + _type_marker = 7 + + def __init__(self, oid=None): + """Initialize a new ObjectId. + + An ObjectId is a 12-byte unique identifier consisting of: + + - a 4-byte value representing the seconds since the Unix epoch, + - a 5-byte random value, + - a 3-byte counter, starting with a random value. + + By default, ``ObjectId()`` creates a new unique identifier. The + optional parameter `oid` can be an :class:`ObjectId`, or any 12 + :class:`bytes` or, in Python 2, any 12-character :class:`str`. + + For example, the 12 bytes b'foo-bar-quux' do not follow the ObjectId + specification but they are acceptable input:: + + >>> ObjectId(b'foo-bar-quux') + ObjectId('666f6f2d6261722d71757578') + + `oid` can also be a :class:`unicode` or :class:`str` of 24 hex digits:: + + >>> ObjectId('0123456789ab0123456789ab') + ObjectId('0123456789ab0123456789ab') + >>> + >>> # A u-prefixed unicode literal: + >>> ObjectId(u'0123456789ab0123456789ab') + ObjectId('0123456789ab0123456789ab') + + Raises :class:`~bson.errors.InvalidId` if `oid` is not 12 bytes nor + 24 hex digits, or :class:`TypeError` if `oid` is not an accepted type. + + :Parameters: + - `oid` (optional): a valid ObjectId. + + .. mongodoc:: objectids + + .. versionchanged:: 3.8 + :class:`~bson.objectid.ObjectId` now implements the `ObjectID + specification version 0.2 + `_. + """ + if oid is None: + self.__generate() + elif isinstance(oid, bytes) and len(oid) == 12: + self.__id = oid + else: + self.__validate(oid) + + @classmethod + def from_datetime(cls, generation_time): + """Create a dummy ObjectId instance with a specific generation time. + + This method is useful for doing range queries on a field + containing :class:`ObjectId` instances. + + .. warning:: + It is not safe to insert a document containing an ObjectId + generated using this method. This method deliberately + eliminates the uniqueness guarantee that ObjectIds + generally provide. ObjectIds generated with this method + should be used exclusively in queries. + + `generation_time` will be converted to UTC. Naive datetime + instances will be treated as though they already contain UTC. + + An example using this helper to get documents where ``"_id"`` + was generated before January 1, 2010 would be: + + >>> gen_time = datetime.datetime(2010, 1, 1) + >>> dummy_id = ObjectId.from_datetime(gen_time) + >>> result = collection.find({"_id": {"$lt": dummy_id}}) + + :Parameters: + - `generation_time`: :class:`~datetime.datetime` to be used + as the generation time for the resulting ObjectId. + """ + if generation_time.utcoffset() is not None: + generation_time = generation_time - generation_time.utcoffset() + timestamp = calendar.timegm(generation_time.timetuple()) + oid = struct.pack( + ">I", int(timestamp)) + b"\x00\x00\x00\x00\x00\x00\x00\x00" + return cls(oid) + + @classmethod + def is_valid(cls, oid): + """Checks if a `oid` string is valid or not. + + :Parameters: + - `oid`: the object id to validate + + .. versionadded:: 2.3 + """ + if not oid: + return False + + try: + ObjectId(oid) + return True + except (InvalidId, TypeError): + return False + + @classmethod + def _random(cls): + """Generate a 5-byte random number once per process. + """ + pid = os.getpid() + if pid != cls._pid: + cls._pid = pid + cls.__random = _random_bytes() + return cls.__random + + def __generate(self): + """Generate a new value for this ObjectId. + """ + + # 4 bytes current time + oid = struct.pack(">I", int(time.time())) + + # 5 bytes random + oid += ObjectId._random() + + # 3 bytes inc + with ObjectId._inc_lock: + oid += struct.pack(">I", ObjectId._inc)[1:4] + ObjectId._inc = (ObjectId._inc + 1) % (_MAX_COUNTER_VALUE + 1) + + self.__id = oid + + def __validate(self, oid): + """Validate and use the given id for this ObjectId. + + Raises TypeError if id is not an instance of + (:class:`basestring` (:class:`str` or :class:`bytes` + in python 3), ObjectId) and InvalidId if it is not a + valid ObjectId. + + :Parameters: + - `oid`: a valid ObjectId + """ + if isinstance(oid, ObjectId): + self.__id = oid.binary + # bytes or unicode in python 2, str in python 3 + elif isinstance(oid, string_type): + if len(oid) == 24: + try: + self.__id = bytes_from_hex(oid) + except (TypeError, ValueError): + _raise_invalid_id(oid) + else: + _raise_invalid_id(oid) + else: + raise TypeError("id must be an instance of (bytes, %s, ObjectId), " + "not %s" % (text_type.__name__, type(oid))) + + @property + def binary(self): + """12-byte binary representation of this ObjectId. + """ + return self.__id + + @property + def generation_time(self): + """A :class:`datetime.datetime` instance representing the time of + generation for this :class:`ObjectId`. + + The :class:`datetime.datetime` is timezone aware, and + represents the generation time in UTC. It is precise to the + second. + """ + timestamp = struct.unpack(">I", self.__id[0:4])[0] + return datetime.datetime.fromtimestamp(timestamp, utc) + + def __getstate__(self): + """return value of object for pickling. + needed explicitly because __slots__() defined. + """ + return self.__id + + def __setstate__(self, value): + """explicit state set from pickling + """ + # Provide backwards compatability with OIDs + # pickled with pymongo-1.9 or older. + if isinstance(value, dict): + oid = value["_ObjectId__id"] + else: + oid = value + # ObjectIds pickled in python 2.x used `str` for __id. + # In python 3.x this has to be converted to `bytes` + # by encoding latin-1. + if PY3 and isinstance(oid, text_type): + self.__id = oid.encode('latin-1') + else: + self.__id = oid + + def __str__(self): + if PY3: + return binascii.hexlify(self.__id).decode() + return binascii.hexlify(self.__id) + + def __repr__(self): + return "ObjectId('%s')" % (str(self),) + + def __eq__(self, other): + if isinstance(other, ObjectId): + return self.__id == other.binary + return NotImplemented + + def __ne__(self, other): + if isinstance(other, ObjectId): + return self.__id != other.binary + return NotImplemented + + def __lt__(self, other): + if isinstance(other, ObjectId): + return self.__id < other.binary + return NotImplemented + + def __le__(self, other): + if isinstance(other, ObjectId): + return self.__id <= other.binary + return NotImplemented + + def __gt__(self, other): + if isinstance(other, ObjectId): + return self.__id > other.binary + return NotImplemented + + def __ge__(self, other): + if isinstance(other, ObjectId): + return self.__id >= other.binary + return NotImplemented + + def __hash__(self): + """Get a hash value for this :class:`ObjectId`.""" + return hash(self.__id) diff --git a/backend/venv/lib/python3.7/site-packages/bson/py3compat.py b/backend/venv/lib/python3.7/site-packages/bson/py3compat.py new file mode 100644 index 000000000..84d1ea00f --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/py3compat.py @@ -0,0 +1,107 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Utility functions and definitions for python3 compatibility.""" + +import sys + +PY3 = sys.version_info[0] == 3 + +if PY3: + import codecs + import collections.abc as abc + import _thread as thread + from abc import ABC, abstractmethod + from io import BytesIO as StringIO + + def abstractproperty(func): + return property(abstractmethod(func)) + + MAXSIZE = sys.maxsize + + imap = map + + def b(s): + # BSON and socket operations deal in binary data. In + # python 3 that means instances of `bytes`. In python + # 2.7 you can create an alias for `bytes` using + # the b prefix (e.g. b'foo'). + # See http://python3porting.com/problems.html#nicer-solutions + return codecs.latin_1_encode(s)[0] + + def bytes_from_hex(h): + return bytes.fromhex(h) + + def iteritems(d): + return iter(d.items()) + + def itervalues(d): + return iter(d.values()) + + def reraise(exctype, value, trace=None): + raise exctype(str(value)).with_traceback(trace) + + def reraise_instance(exc_instance, trace=None): + raise exc_instance.with_traceback(trace) + + def _unicode(s): + return s + + text_type = str + string_type = str + integer_types = int +else: + import collections as abc + import thread + from abc import ABCMeta, abstractproperty + + from itertools import imap + try: + from cStringIO import StringIO + except ImportError: + from StringIO import StringIO + + ABC = ABCMeta('ABC', (object,), {}) + + MAXSIZE = sys.maxint + + def b(s): + # See comments above. In python 2.x b('foo') is just 'foo'. + return s + + def bytes_from_hex(h): + return h.decode('hex') + + def iteritems(d): + return d.iteritems() + + def itervalues(d): + return d.itervalues() + + def reraise(exctype, value, trace=None): + _reraise(exctype, str(value), trace) + + def reraise_instance(exc_instance, trace=None): + _reraise(exc_instance, None, trace) + + # "raise x, y, z" raises SyntaxError in Python 3 + exec("""def _reraise(exc, value, trace): + raise exc, value, trace +""") + + _unicode = unicode + + string_type = basestring + text_type = unicode + integer_types = (int, long) diff --git a/backend/venv/lib/python3.7/site-packages/bson/raw_bson.py b/backend/venv/lib/python3.7/site-packages/bson/raw_bson.py new file mode 100644 index 000000000..429b2acc3 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/raw_bson.py @@ -0,0 +1,124 @@ +# Copyright 2015-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing raw BSON documents. +""" + +from bson import _elements_to_dict, _get_object_size +from bson.py3compat import abc, iteritems +from bson.codec_options import ( + DEFAULT_CODEC_OPTIONS as DEFAULT, _RAW_BSON_DOCUMENT_MARKER) +from bson.son import SON + + +class RawBSONDocument(abc.Mapping): + """Representation for a MongoDB document that provides access to the raw + BSON bytes that compose it. + + Only when a field is accessed or modified within the document does + RawBSONDocument decode its bytes. + """ + + __slots__ = ('__raw', '__inflated_doc', '__codec_options') + _type_marker = _RAW_BSON_DOCUMENT_MARKER + + def __init__(self, bson_bytes, codec_options=None): + """Create a new :class:`RawBSONDocument` + + :class:`RawBSONDocument` is a representation of a BSON document that + provides access to the underlying raw BSON bytes. Only when a field is + accessed or modified within the document does RawBSONDocument decode + its bytes. + + :class:`RawBSONDocument` implements the ``Mapping`` abstract base + class from the standard library so it can be used like a read-only + ``dict``:: + + >>> raw_doc = RawBSONDocument(BSON.encode({'_id': 'my_doc'})) + >>> raw_doc.raw + b'...' + >>> raw_doc['_id'] + 'my_doc' + + :Parameters: + - `bson_bytes`: the BSON bytes that compose this document + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions` whose ``document_class`` + must be :class:`RawBSONDocument`. The default is + :attr:`DEFAULT_RAW_BSON_OPTIONS`. + + .. versionchanged:: 3.8 + :class:`RawBSONDocument` now validates that the ``bson_bytes`` + passed in represent a single bson document. + + .. versionchanged:: 3.5 + If a :class:`~bson.codec_options.CodecOptions` is passed in, its + `document_class` must be :class:`RawBSONDocument`. + """ + self.__raw = bson_bytes + self.__inflated_doc = None + # Can't default codec_options to DEFAULT_RAW_BSON_OPTIONS in signature, + # it refers to this class RawBSONDocument. + if codec_options is None: + codec_options = DEFAULT_RAW_BSON_OPTIONS + elif codec_options.document_class is not RawBSONDocument: + raise TypeError( + "RawBSONDocument cannot use CodecOptions with document " + "class %s" % (codec_options.document_class, )) + self.__codec_options = codec_options + # Validate the bson object size. + _get_object_size(bson_bytes, 0, len(bson_bytes)) + + @property + def raw(self): + """The raw BSON bytes composing this document.""" + return self.__raw + + def items(self): + """Lazily decode and iterate elements in this document.""" + return iteritems(self.__inflated) + + @property + def __inflated(self): + if self.__inflated_doc is None: + # We already validated the object's size when this document was + # created, so no need to do that again. + # Use SON to preserve ordering of elements. + self.__inflated_doc = _elements_to_dict( + self.__raw, 4, len(self.__raw)-1, self.__codec_options, SON()) + return self.__inflated_doc + + def __getitem__(self, item): + return self.__inflated[item] + + def __iter__(self): + return iter(self.__inflated) + + def __len__(self): + return len(self.__inflated) + + def __eq__(self, other): + if isinstance(other, RawBSONDocument): + return self.__raw == other.raw + return NotImplemented + + def __repr__(self): + return ("RawBSONDocument(%r, codec_options=%r)" + % (self.raw, self.__codec_options)) + + +DEFAULT_RAW_BSON_OPTIONS = DEFAULT.with_options(document_class=RawBSONDocument) +"""The default :class:`~bson.codec_options.CodecOptions` for +:class:`RawBSONDocument`. +""" diff --git a/backend/venv/lib/python3.7/site-packages/bson/regex.py b/backend/venv/lib/python3.7/site-packages/bson/regex.py new file mode 100644 index 000000000..f9d39ad83 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/regex.py @@ -0,0 +1,128 @@ +# Copyright 2013-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing MongoDB regular expressions. +""" + +import re + +from bson.son import RE_TYPE +from bson.py3compat import string_type, text_type + + +def str_flags_to_int(str_flags): + flags = 0 + if "i" in str_flags: + flags |= re.IGNORECASE + if "l" in str_flags: + flags |= re.LOCALE + if "m" in str_flags: + flags |= re.MULTILINE + if "s" in str_flags: + flags |= re.DOTALL + if "u" in str_flags: + flags |= re.UNICODE + if "x" in str_flags: + flags |= re.VERBOSE + + return flags + + +class Regex(object): + """BSON regular expression data.""" + _type_marker = 11 + + @classmethod + def from_native(cls, regex): + """Convert a Python regular expression into a ``Regex`` instance. + + Note that in Python 3, a regular expression compiled from a + :class:`str` has the ``re.UNICODE`` flag set. If it is undesirable + to store this flag in a BSON regular expression, unset it first:: + + >>> pattern = re.compile('.*') + >>> regex = Regex.from_native(pattern) + >>> regex.flags ^= re.UNICODE + >>> db.collection.insert({'pattern': regex}) + + :Parameters: + - `regex`: A regular expression object from ``re.compile()``. + + .. warning:: + Python regular expressions use a different syntax and different + set of flags than MongoDB, which uses `PCRE`_. A regular + expression retrieved from the server may not compile in + Python, or may match a different set of strings in Python than + when used in a MongoDB query. + + .. _PCRE: http://www.pcre.org/ + """ + if not isinstance(regex, RE_TYPE): + raise TypeError( + "regex must be a compiled regular expression, not %s" + % type(regex)) + + return Regex(regex.pattern, regex.flags) + + def __init__(self, pattern, flags=0): + """BSON regular expression data. + + This class is useful to store and retrieve regular expressions that are + incompatible with Python's regular expression dialect. + + :Parameters: + - `pattern`: string + - `flags`: (optional) an integer bitmask, or a string of flag + characters like "im" for IGNORECASE and MULTILINE + """ + if not isinstance(pattern, (text_type, bytes)): + raise TypeError("pattern must be a string, not %s" % type(pattern)) + self.pattern = pattern + + if isinstance(flags, string_type): + self.flags = str_flags_to_int(flags) + elif isinstance(flags, int): + self.flags = flags + else: + raise TypeError( + "flags must be a string or int, not %s" % type(flags)) + + def __eq__(self, other): + if isinstance(other, Regex): + return self.pattern == other.pattern and self.flags == other.flags + else: + return NotImplemented + + __hash__ = None + + def __ne__(self, other): + return not self == other + + def __repr__(self): + return "Regex(%r, %r)" % (self.pattern, self.flags) + + def try_compile(self): + """Compile this :class:`Regex` as a Python regular expression. + + .. warning:: + Python regular expressions use a different syntax and different + set of flags than MongoDB, which uses `PCRE`_. A regular + expression retrieved from the server may not compile in + Python, or may match a different set of strings in Python than + when used in a MongoDB query. :meth:`try_compile()` may raise + :exc:`re.error`. + + .. _PCRE: http://www.pcre.org/ + """ + return re.compile(self.pattern, self.flags) diff --git a/backend/venv/lib/python3.7/site-packages/bson/son.py b/backend/venv/lib/python3.7/site-packages/bson/son.py new file mode 100644 index 000000000..701cb2318 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/son.py @@ -0,0 +1,200 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for creating and manipulating SON, the Serialized Ocument Notation. + +Regular dictionaries can be used instead of SON objects, but not when the order +of keys is important. A SON object can be used just like a normal Python +dictionary.""" + +import copy +import re + +from bson.py3compat import abc, iteritems + + +# This sort of sucks, but seems to be as good as it gets... +# This is essentially the same as re._pattern_type +RE_TYPE = type(re.compile("")) + + +class SON(dict): + """SON data. + + A subclass of dict that maintains ordering of keys and provides a + few extra niceties for dealing with SON. SON provides an API + similar to collections.OrderedDict from Python 2.7+. + """ + + def __init__(self, data=None, **kwargs): + self.__keys = [] + dict.__init__(self) + self.update(data) + self.update(kwargs) + + def __new__(cls, *args, **kwargs): + instance = super(SON, cls).__new__(cls, *args, **kwargs) + instance.__keys = [] + return instance + + def __repr__(self): + result = [] + for key in self.__keys: + result.append("(%r, %r)" % (key, self[key])) + return "SON([%s])" % ", ".join(result) + + def __setitem__(self, key, value): + if key not in self.__keys: + self.__keys.append(key) + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + self.__keys.remove(key) + dict.__delitem__(self, key) + + def keys(self): + return list(self.__keys) + + def copy(self): + other = SON() + other.update(self) + return other + + # TODO this is all from UserDict.DictMixin. it could probably be made more + # efficient. + # second level definitions support higher levels + def __iter__(self): + for k in self.__keys: + yield k + + def has_key(self, key): + return key in self.__keys + + # third level takes advantage of second level definitions + def iteritems(self): + for k in self: + yield (k, self[k]) + + def iterkeys(self): + return self.__iter__() + + # fourth level uses definitions from lower levels + def itervalues(self): + for _, v in self.iteritems(): + yield v + + def values(self): + return [v for _, v in self.iteritems()] + + def items(self): + return [(key, self[key]) for key in self] + + def clear(self): + self.__keys = [] + super(SON, self).clear() + + def setdefault(self, key, default=None): + try: + return self[key] + except KeyError: + self[key] = default + return default + + def pop(self, key, *args): + if len(args) > 1: + raise TypeError("pop expected at most 2 arguments, got "\ + + repr(1 + len(args))) + try: + value = self[key] + except KeyError: + if args: + return args[0] + raise + del self[key] + return value + + def popitem(self): + try: + k, v = next(self.iteritems()) + except StopIteration: + raise KeyError('container is empty') + del self[k] + return (k, v) + + def update(self, other=None, **kwargs): + # Make progressively weaker assumptions about "other" + if other is None: + pass + elif hasattr(other, 'iteritems'): # iteritems saves memory and lookups + for k, v in other.iteritems(): + self[k] = v + elif hasattr(other, 'keys'): + for k in other.keys(): + self[k] = other[k] + else: + for k, v in other: + self[k] = v + if kwargs: + self.update(kwargs) + + def get(self, key, default=None): + try: + return self[key] + except KeyError: + return default + + def __eq__(self, other): + """Comparison to another SON is order-sensitive while comparison to a + regular dictionary is order-insensitive. + """ + if isinstance(other, SON): + return len(self) == len(other) and self.items() == other.items() + return self.to_dict() == other + + def __ne__(self, other): + return not self == other + + def __len__(self): + return len(self.__keys) + + def to_dict(self): + """Convert a SON document to a normal Python dictionary instance. + + This is trickier than just *dict(...)* because it needs to be + recursive. + """ + + def transform_value(value): + if isinstance(value, list): + return [transform_value(v) for v in value] + elif isinstance(value, abc.Mapping): + return dict([ + (k, transform_value(v)) + for k, v in iteritems(value)]) + else: + return value + + return transform_value(dict(self)) + + def __deepcopy__(self, memo): + out = SON() + val_id = id(self) + if val_id in memo: + return memo.get(val_id) + memo[val_id] = out + for k, v in self.iteritems(): + if not isinstance(v, RE_TYPE): + v = copy.deepcopy(v, memo) + out[k] = v + return out diff --git a/backend/venv/lib/python3.7/site-packages/bson/timestamp.py b/backend/venv/lib/python3.7/site-packages/bson/timestamp.py new file mode 100644 index 000000000..7ea755117 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/timestamp.py @@ -0,0 +1,120 @@ +# Copyright 2010-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing MongoDB internal Timestamps. +""" + +import calendar +import datetime + +from bson.py3compat import integer_types +from bson.tz_util import utc + +UPPERBOUND = 4294967296 + + +class Timestamp(object): + """MongoDB internal timestamps used in the opLog. + """ + + _type_marker = 17 + + def __init__(self, time, inc): + """Create a new :class:`Timestamp`. + + This class is only for use with the MongoDB opLog. If you need + to store a regular timestamp, please use a + :class:`~datetime.datetime`. + + Raises :class:`TypeError` if `time` is not an instance of + :class: `int` or :class:`~datetime.datetime`, or `inc` is not + an instance of :class:`int`. Raises :class:`ValueError` if + `time` or `inc` is not in [0, 2**32). + + :Parameters: + - `time`: time in seconds since epoch UTC, or a naive UTC + :class:`~datetime.datetime`, or an aware + :class:`~datetime.datetime` + - `inc`: the incrementing counter + """ + if isinstance(time, datetime.datetime): + if time.utcoffset() is not None: + time = time - time.utcoffset() + time = int(calendar.timegm(time.timetuple())) + if not isinstance(time, integer_types): + raise TypeError("time must be an instance of int") + if not isinstance(inc, integer_types): + raise TypeError("inc must be an instance of int") + if not 0 <= time < UPPERBOUND: + raise ValueError("time must be contained in [0, 2**32)") + if not 0 <= inc < UPPERBOUND: + raise ValueError("inc must be contained in [0, 2**32)") + + self.__time = time + self.__inc = inc + + @property + def time(self): + """Get the time portion of this :class:`Timestamp`. + """ + return self.__time + + @property + def inc(self): + """Get the inc portion of this :class:`Timestamp`. + """ + return self.__inc + + def __eq__(self, other): + if isinstance(other, Timestamp): + return (self.__time == other.time and self.__inc == other.inc) + else: + return NotImplemented + + def __hash__(self): + return hash(self.time) ^ hash(self.inc) + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + if isinstance(other, Timestamp): + return (self.time, self.inc) < (other.time, other.inc) + return NotImplemented + + def __le__(self, other): + if isinstance(other, Timestamp): + return (self.time, self.inc) <= (other.time, other.inc) + return NotImplemented + + def __gt__(self, other): + if isinstance(other, Timestamp): + return (self.time, self.inc) > (other.time, other.inc) + return NotImplemented + + def __ge__(self, other): + if isinstance(other, Timestamp): + return (self.time, self.inc) >= (other.time, other.inc) + return NotImplemented + + def __repr__(self): + return "Timestamp(%s, %s)" % (self.__time, self.__inc) + + def as_datetime(self): + """Return a :class:`~datetime.datetime` instance corresponding + to the time portion of this :class:`Timestamp`. + + The returned datetime's timezone is UTC. + """ + return datetime.datetime.fromtimestamp(self.__time, utc) diff --git a/backend/venv/lib/python3.7/site-packages/bson/tz_util.py b/backend/venv/lib/python3.7/site-packages/bson/tz_util.py new file mode 100644 index 000000000..6ec918fb2 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/bson/tz_util.py @@ -0,0 +1,52 @@ +# Copyright 2010-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Timezone related utilities for BSON.""" + +from datetime import (timedelta, + tzinfo) + +ZERO = timedelta(0) + + +class FixedOffset(tzinfo): + """Fixed offset timezone, in minutes east from UTC. + + Implementation based from the Python `standard library documentation + `_. + Defining __getinitargs__ enables pickling / copying. + """ + + def __init__(self, offset, name): + if isinstance(offset, timedelta): + self.__offset = offset + else: + self.__offset = timedelta(minutes=offset) + self.__name = name + + def __getinitargs__(self): + return self.__offset, self.__name + + def utcoffset(self, dt): + return self.__offset + + def tzname(self, dt): + return self.__name + + def dst(self, dt): + return ZERO + + +utc = FixedOffset(0, "UTC") +"""Fixed offset timezone representing UTC.""" diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/__init__.py b/backend/venv/lib/python3.7/site-packages/flask_pymongo/__init__.py new file mode 100644 index 000000000..1b3200c0f --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/flask_pymongo/__init__.py @@ -0,0 +1,241 @@ +# Copyright (c) 2011-2017, Dan Crosta +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + + +__all__ = ("PyMongo", "ASCENDING", "DESCENDING") + +from mimetypes import guess_type +import sys + +from bson.errors import InvalidId +from bson.objectid import ObjectId +from flask import abort, current_app, request +from gridfs import GridFS, NoFile +from pymongo import uri_parser +from werkzeug.routing import BaseConverter +from werkzeug.wsgi import wrap_file +import pymongo + +from flask_pymongo.wrappers import MongoClient + + +PY2 = sys.version_info[0] == 2 + +# Python 3 compatibility +if PY2: + text_type = (str, unicode) + num_type = (int, long) +else: + text_type = str + num_type = int + + +DESCENDING = pymongo.DESCENDING +"""Descending sort order.""" + +ASCENDING = pymongo.ASCENDING +"""Ascending sort order.""" + + +class BSONObjectIdConverter(BaseConverter): + + """A simple converter for the RESTful URL routing system of Flask. + + .. code-block:: python + + @app.route("/") + def show_task(task_id): + task = mongo.db.tasks.find_one_or_404(task_id) + return render_template("task.html", task=task) + + Valid object ID strings are converted into + :class:`~bson.objectid.ObjectId` objects; invalid strings result + in a 404 error. The converter is automatically registered by the + initialization of :class:`~flask_pymongo.PyMongo` with keyword + :attr:`ObjectId`. + + """ + + def to_python(self, value): + try: + return ObjectId(value) + except InvalidId: + raise abort(404) + + def to_url(self, value): + return str(value) + + +class PyMongo(object): + + """Manages MongoDB connections for your Flask app. + + PyMongo objects provide access to the MongoDB server via the :attr:`db` + and :attr:`cx` attributes. You must either pass the :class:`~flask.Flask` + app to the constructor, or call :meth:`init_app`. + + PyMongo accepts a MongoDB URI via the ``MONGO_URI`` Flask configuration + variable, or as an argument to the constructor or ``init_app``. See + :meth:`init_app` for more detail. + + """ + + def __init__(self, app=None, uri=None, *args, **kwargs): + self.cx = None + self.db = None + + if app is not None: + self.init_app(app, uri, *args, **kwargs) + + def init_app(self, app, uri=None, *args, **kwargs): + """Initialize this :class:`PyMongo` for use. + + Configure a :class:`~pymongo.mongo_client.MongoClient` + in the following scenarios: + + 1. If ``uri`` is not ``None``, pass the ``uri`` and any positional + or keyword arguments to :class:`~pymongo.mongo_client.MongoClient` + 2. If ``uri`` is ``None``, and a Flask config variable named + ``MONGO_URI`` exists, use that as the ``uri`` as above. + + The caller is responsible for ensuring that additional positional + and keyword arguments result in a valid call. + + .. versionchanged:: 2.2 + + The ``uri`` is no longer required to contain a database name. If it + does not, then the :attr:`db` attribute will be ``None``. + + .. versionchanged:: 2.0 + + Flask-PyMongo no longer accepts many of the configuration variables + it did in previous versions. You must now use a MongoDB URI to + configure Flask-PyMongo. + + """ + if uri is None: + uri = app.config.get("MONGO_URI", None) + if uri is not None: + args = tuple([uri] + list(args)) + else: + raise ValueError( + "You must specify a URI or set the MONGO_URI Flask config variable", + ) + + parsed_uri = uri_parser.parse_uri(uri) + database_name = parsed_uri["database"] + + # Try to delay connecting, in case the app is loaded before forking, per + # http://api.mongodb.com/python/current/faq.html#is-pymongo-fork-safe + kwargs.setdefault("connect", False) + + self.cx = MongoClient(*args, **kwargs) + if database_name: + self.db = self.cx[database_name] + + app.url_map.converters["ObjectId"] = BSONObjectIdConverter + + # view helpers + def send_file(self, filename, base="fs", version=-1, cache_for=31536000): + """Respond with a file from GridFS. + + Returns an instance of the :attr:`~flask.Flask.response_class` + containing the named file, and implement conditional GET semantics + (using :meth:`~werkzeug.wrappers.ETagResponseMixin.make_conditional`). + + .. code-block:: python + + @app.route("/uploads/") + def get_upload(filename): + return mongo.send_file(filename) + + :param str filename: the filename of the file to return + :param str base: the base name of the GridFS collections to use + :param bool version: if positive, return the Nth revision of the file + identified by filename; if negative, return the Nth most recent + revision. If no such version exists, return with HTTP status 404. + :param int cache_for: number of seconds that browsers should be + instructed to cache responses + """ + if not isinstance(base, text_type): + raise TypeError("'base' must be string or unicode") + if not isinstance(version, num_type): + raise TypeError("'version' must be an integer") + if not isinstance(cache_for, num_type): + raise TypeError("'cache_for' must be an integer") + + storage = GridFS(self.db, base) + + try: + fileobj = storage.get_version(filename=filename, version=version) + except NoFile: + abort(404) + + # mostly copied from flask/helpers.py, with + # modifications for GridFS + data = wrap_file(request.environ, fileobj, buffer_size=1024 * 255) + response = current_app.response_class( + data, + mimetype=fileobj.content_type, + direct_passthrough=True, + ) + response.content_length = fileobj.length + response.last_modified = fileobj.upload_date + response.set_etag(fileobj.md5) + response.cache_control.max_age = cache_for + response.cache_control.public = True + response.make_conditional(request) + return response + + def save_file(self, filename, fileobj, base="fs", content_type=None, **kwargs): + """Save a file-like object to GridFS using the given filename. + + .. code-block:: python + + @app.route("/uploads/", methods=["POST"]) + def save_upload(filename): + mongo.save_file(filename, request.files["file"]) + return redirect(url_for("get_upload", filename=filename)) + + :param str filename: the filename of the file to return + :param file fileobj: the file-like object to save + :param str base: base the base name of the GridFS collections to use + :param str content_type: the MIME content-type of the file. If + ``None``, the content-type is guessed from the filename using + :func:`~mimetypes.guess_type` + :param kwargs: extra attributes to be stored in the file's document, + passed directly to :meth:`gridfs.GridFS.put` + """ + if not isinstance(base, text_type): + raise TypeError("'base' must be string or unicode") + if not (hasattr(fileobj, "read") and callable(fileobj.read)): + raise TypeError("'fileobj' must have read() method") + + if content_type is None: + content_type, _ = guess_type(filename) + + storage = GridFS(self.db, base) + id = storage.put(fileobj, filename=filename, content_type=content_type, **kwargs) + return id diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/__pycache__/__init__.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/flask_pymongo/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2ae373923f98cd34dbcdc6763e984d1449e881e GIT binary patch literal 7363 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/__pycache__/_version.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/flask_pymongo/__pycache__/_version.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46bb9488c5c661fc6381fe6f835a45a8b0d056fd GIT binary patch literal 270 zcmXv}%WA_g5R@OKp>3e&{6P-3VmBc%w58^L^LP^&ya?%Xc`?Z^7Jm9m7}J} zs(F5F+b;70Xh!6=|5-v5*@v|WIE7|rhN;V4DLjv8m ar*u9n)%|gF-zvHn>@gbti@n4_9Q*-kuS)3v literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/__pycache__/wrappers.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/flask_pymongo/__pycache__/wrappers.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd1cd5e1d27aa4e2c6cfcefeadcbe5df97bbcae3 GIT binary patch literal 3428 zcmcgu&2Jk;6yHyK<2baGwt;}eC(0z3 zN^rQOQg5921K`Mke`v3qIB~` zX##H^3slevBnP@aNRyqEhH=Cb72pK*(8CA_Bc6{ zPr;$ERf)rXk0&GuK^Msx*^g9@uvC#GRlqMzCW?pF#DHkXV#p8SMH$K*8OPR7*2w(X zJhGHMvXs+zk1g?ma!1CH6RhdV49q7g@M*qC=?(`uN>G~DB3YP4V6f$0 zam|LEJCeuU+)07~&mH^#yuCod(_nu}e=+{fnnMxo1&TNK0@3WIG2=ou2cp^OQ+iw8 zOm6M5jgSuaZd_O0+dF*`f7HEs=T390IgEzuT5ue%^IJib45J&(IiED=+|Zo5VR#@s zP!z2okuc1QX5Flrhs!0``=vX+GAoKINGsbgQhY$ESiog8p1?$I*kTd>atC+C60Vry z4cxte6J)p{-h}BojF(+tETB=%Jxb9F=rBqLDa&G{>y$ptf_So1rIe*1=z~_#BJ!n) zvqB!nu%Q*esnAF(gF!)HNF+SZvFa6l$yb%i+A4>Co}0p_Q{>g=?!#&E`p?-rt9yQ# z%m19gN8aTk>0e{sXx%aJ$N7S~i??Aa zl)eOX?&^py!gD@hK7#}2s$FD@cnf)7#_0-9S8Vdvr_?eyal723b=N5Dr~^c zzv*hM!m7`puj%eqg_7*)iyuCIl6BT~F~v?YX(6ZE+lvAan?{evP}k+jeu2dZ4U9wH zfawMO0=k6oM@HX0vZ0?q4*{4^>KoN@)S;J7M%@)U3p=Pglh&vsui@wdIE%vEVx7}2 zMTcymerVuv5wAcYKfF3)d}cJhhP$1J$`#OO?j{(T;vJmch3WKF@cVCIy!0yL2n|g* zi%)TtFM*Z>CU6C`?veF_d1Q}lX6}08Jq1YV7!cpchRCslKj7m>QNl<-pmXoU9DqZZ z4FGx(z(yj7B!{hy>l>4pKmfGWivayd5=Xn7v<6)DO6cj#@bbGLf~E=IzD7Ek()b8D zi9q!LhN)hV5TRie=s>o&H}NQ(4TG$ZPc{M2JW4ZA_4BQ*#{{sK%H)(&ngACLOrB<3 zs*J#n)DwAW<~$;*5>({%T_?1CKJ^ zCAde2BJD;oUn>qaT65>{EN+o|MB_=Hb$oo6Q&Z4XP_XOTcdImFt1W#RB_7QSL`GxN zz+VD@hyo56#sHGmR*|9KQ-gT*+9~0M0{8GxV|L%o=tppQ!)kklPXxlkNtOXF6FV}m z?C$G-w~Gd!RW}Vc@(ab6lPh#E-Z1}+i*?h3-{DFr=}sJmYIS}+eINLoS5&clY{JkI zJqPebwO-W~aI>+{b~Vh?S87)$h*@CW8Y3zfDFh}OMd+_ex0^PI+`BtQMfWjzB{sl1 N6chN_mzFOr{{yU6EMx!x literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/_version.py b/backend/venv/lib/python3.7/site-packages/flask_pymongo/_version.py new file mode 100644 index 000000000..f7b77b5ff --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/flask_pymongo/_version.py @@ -0,0 +1,5 @@ + +# This file is automatically generated by setup.py. +__version__ = '2.3.0' +__sha__ = 'g91c4c19' +__revision__ = 'g91c4c19' diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__init__.py b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/__init__.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c0ed8cbc6afb9b34c5832bc95ba36f4ab569ea54 GIT binary patch literal 210 zcmZ?b<>g`k0x4UmSP=afM8E(ekl_Ht#VkM~g&~+hlhJP_LlHAEY8*qsPxUxOV8I&1M%VuDs#cSlGNgoV*U8|%)HE! Z_;|g7%3B;Zx%nxjIjMFaM->Ax0{}J@IIsW! literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/test_config.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/test_config.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..386002ec04ef43500f937451e6336d932f295160 GIT binary patch literal 4544 zcmb7I&vVb^W8oG=o4~PzDVG z@M0-jG}S}onVd2?r_%#<%%9SK0oNWnz4hEvzqcSMS(WJqGkCj;1sD6i_ulv3@}tGY zs)6g=^>bgmwP+aspuzOA&{@TuZ9xb_u*m2$moe(|h`U_3rfcffaxJuGRO;KVJ$a|> zmOn9sB}&f?Q3{-0)2#$7sCJpKMfo|~W$psz9Z|u2C9pAH6N_R&)SfeUQSd()t@^+4 zZKK8VdMAlh@Kp8v*xwA~IvzDXj{NM=o#62x$kbij9eQ_qF?3dOXElhxaG5Z$V}><( zerzonL}EQrYe^gj9kt#vbI0>yzaMyBUiG|wA_fulYo7Ob;77&G_l9(^m=^!}OFNa} zmal^LmM`1gBocwl+I`vH=y~3i?7jH?E%9#0OShKaQQa$>JsDl@zIXL%`))f8(@SBT zDL;xX1@HS|oQBJ7t;?m~ZGWD`n@PJ%ntADVUyl@Y%i1J8pwJCBm(p!X)>$C^IE2IW zxI;?QMJ;WW5Xs(!*scoWJ{~_X;`5d%)uBcA8K8tSS943q*5cpqkl30O%D#k{+VX1ye#8WS?zpDz zo5DgL-X`qlhFcP4;h?oeMO4w2wO<*o0}rcXToK1a18r5jCXS zX(E+trD43;a`I9(NCPPe3iJbYFU`vn>*ovFCDx)ah?Q*68s<#C9!xHB8VV0B(+!`F{ib4hPt=1Zh3Ih$5?1)cfIj%}!3 zCS^yg=nH-tzI8zPLJPLsa^!1RuTa}jfiG_+Phxq3o}DB?+JXpVl6t~d{)V3gxh*y* z8e3eRrdQ5Dw3sB-4ru#h4Bx5??|fKoB%N*to~{ zIN20ESXuBDLzQ}@vMJzy-|!u-$|Jr@>hqq{tI#hV@$6$&{fdbaMy$&*v(T&I>7Vse z3N?Fucvc6y*tphiM@h$z9wwQ(_H3v9Y$vaLetZ3s+upr9pWcOiVR+42d1dxqYf%!- zX~5*`=;fxLrnz0vMZWj~pXB9d&PcecOKaD=h(2O zX@fObjW@W%j0u%JD&V7cc^EV!a7 zupq^CVZmx|0T#TYpOOVj^DGFn$)DoelAN+tJ7g^RGn#sfM3cmM5*J8ZBth`VUyx`) zOkkI9(}Q1J`QwCvqHHs-gV9)KKwZ`?6vxkvs4zXnA#(a1L;ax+a* zNt@1XojJ0+df$%*f%eUP5P`L(hC{fV)wsajDM4k0>%<9pw13a%_bWPocqBGp2inj^D^_#9XE;5v_->@!srrCs(Fn>=5j zDpNRr)0tP*_TSzGeK=UQ_5Y>lkc9R4(gz1GMH@Ez#wU}E1dz|l4nx9--FOj)UH;jds#DOL& zw~F!>X@ef1SlLd4mQ{o#?fm7;b*^Bpqd@$W)Tb1O6k#@y9?tNSe0cc>8)N2vCYGA< z)KQT-i$jV^q=a!fhpJRp$JxB zxn1_{NpQY3F2Y#jx{n`@NRaUrkVdfEw6a_{{_P^69v znAUFjO583S!cF36iG1majpAsC!Ug>K1=gEjSL0PU;&A2v!|v$iP`Vvr0xH!dRj`td z7V;7a%GYw4#8na>khl(!FHVa6P7p=Yf}aRvS`(9x>jIlnVn$0lyk^%Nr|MK38#lUo z{-2n__3s-UVi~$9MCe|_ol%J3sA%jNJN)U}MUR;HwDAZV123~4mlw%wm@|bwNkh$x}sx$>ntz^fXH^O)+ALIs&nrRgw>PMtIZ)NW8y&#Tx1{ofFU z8T?#>gC4m{{l|~S<(Oi literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/test_gridfs.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/test_gridfs.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..51f0bc1759e3aa81d71ec54b32fc285aa918cec2 GIT binary patch literal 4930 zcmeHL&2!tv702R>;HMecO&mFOnbdW{q!Lv}uAOO;acoDfXKE#_)kz049t_A`lAu9= z-d)NPNA4l=OnOQ45A;AC`!C_rLr=N))KlJDfDp-$NqcU=!NcNXKi=>C-pBH8wOZ2P z*!*bo$v?`P_D{T-9Tqh1z$yL$f@+k6+K{+}U|SFMq2U^;ZG`5~axK+1Lwi_o3#x5} z&amhfRof0rZVCDo!t$`;)M$}aPYt&Q_a$0}`!cJs%BksIfF~7Ng(ubQ zNuAn{wPx*GSdG>si7})%;ZnGJESY%t88n^G+5^^+4=J?m2RxvAkMZe#EcfDZMEBvQ zz8Cu9>F39f;;0uNFd;iQ+Uzj0hIj)SOmhjUGpODLiGOUE_7(oD@YVHHNQkxp;CS38v@x*-)QajNGWT2mt7epE-WNMyi(AJMGpXm3sC-oQj%sMd! z_8H*&?CKYqEJ)`>JJqMfGYt`hImv#r#L*~;HN@p7I&CLr7zmk|-5{cgO5=Y%a6Cf&jUI#!Dp7(d{-+yk6 zcyQ=T);jcgs~d-uanTy`R(s%ix5drq)*;>Pc%#D`JFM%{8b&&6mI$YEc`m7A;T*t~M=#O-o4yS@_sL zWP1RA&LCtCGUy(TdK-iU|3mg5kES}+Pso{0G~g2Q%0w=m=u->k(gSiv#ICfz1opxV z=DxUy{rgO?hR<08T8%dP%%m_p-a`8)4oZZjS>QM`FM>#l0#@bWE5$mFCP-{{1cVbw z?Gqswmq8qT%6ZJ227d)#7x44AJ>VNSOb(~QF}a8)_5$hQz(S2IUWe}l0;~~7ujv&s zY0LuxM*Uuw9Hg0>DUj~M5dR%W;B??{)O@Ook7Xf)%UTatIfH9k9!dUFm~K*%>`1mo zA+WpYoFm7t!pk|#0N#jtam12JCypeGq$iKTIUDd<(^SCmH?fNX?Gm<@kb7$eLvPFg zccJ=wT^uhw3|#}z zy_=(JuSYj0qtW&`gdw9F#|q^iK$ia;v3eUt1H^xZ<<%LMb1d|p2eR*tcsvsC!azBW z8$d|F@x7HeuI`^>h>nzFxD7Y!Fnpnd8$)W4=2J(!E3F@A;tVT(9knJ3fu_Agz{FG} zLnlemNnJE$8phORl5Na@;Y^NsBp{2t51$De1bXL&KKa>7AkM$vhX8UJheo&Nn<(Bw z@e361pg?%%CRRLGK^9XkHdHWhbjNXuPLbo&4xIUcsNx}h2Vxd#=Tr3ha7#-Nb_=vj zD{ysamDb=|R4Kg#f$~BYidjVWAc*C5#H5ZO;>={4W^A|Nrqnhd_YTnUsddm=`f|fP-8B4)OOuK#j)aA4ke>6FdDz zrCyalz^^f-&MJ{}Pr)MNBRDh1tQioYHk4y#)ZJFYE)WwSY6hZx0d)Pn_6O}xDQ)%rNj-|U#JJN}WrAA1mDnRJ9;9n?KY&!kF+9u)23UYf zNZH!w935Rd$wTk%&MxHtn0v!Oh#=~vwZMH`AiA0cWnLC^XDZEN)9RkNu2_koqbgk+ z&;b2FThtF~C#b-uP$5Bm^oBIEFl+43n`a@VTd6TxFbuy2A}K*R%ca0%k%m+~t$Iw( z#AsRB*Jn!xYY1;X@&6XaI@k01g6Y>47%yA3d8UznHpzWK&bIIUDe3u!0Kuzk7KX{u^o11NIeA^ z|24FdVxJw+pogAf_@j|~Nuk5pGpOKXN(U$-&n-{}Zpkqc1tw^~2w6$(R#ZDF4Z?DH@q2B@?dGNvF zXW28wtJ28A&XO;hb1Iet;R(qZ6!mmI%&fHiYq>tEhwva2jYBEgs7xM9udP;Pu}ibO zT)SbW;aKu;ECl&26qtGV9TcCSxPl@N1hZtNDmqMYzkyR=vFYfQf@7**)v;9iYrX>h z#CS@&de0>~vVQiC5*NTFbc#X-lOm=fm``gO9NU6u3Eq{~usSfIra3 JsFydceFr4E>{I{% literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/test_url_converter.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/test_url_converter.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0cfa662970750c57150386199290cd25180420c6 GIT binary patch literal 942 zcmaJ`c6Ti?C<4sap4p^Ge^Nc<7JkQ&W#xjDn_H6Cw$U*3r-7J;|;02i81;-G> z0;QN>Y_TJp)Fp0-UE!q_QYo<~s%eeXFnWzxg;g&QtMb~pOMF&4KrR0_R6;FW`1`{# zkIY+U@j#kAIms-3xP7ocSmbtOHs#821&QXK2=(#%lXo&3$wRJ9WGgKeFrP7f0DvQc z86pnD%(+0sWiE8~isnZpZYq`)@cg>HT>;wx({I2Tco$J}##?C7?se8&*2&LUTsxe(p8QHE^S!8Hzu+j+7hN9YF|nUW-Gdv~KZ0B()6no>8mb_cf^nsTR0YE^rG4GYHm9r`(R{k`*u?$OScy*D z+u8~agFMOClT4dXh;_ahCRv_r1f>Y;a}|QvN&*B^t4Ux$6kU4DiQZ6Y=O@J@o1{Z6 zGb&5rX~O=Vus=}O!6EEBP3+?pT*s&FfAwnrx5xk__9@N6lv7$PQ<_RP5f;}e{X7Z9 zETbCGrku?!XM3wEI3G;gQTtB0AGDT;XK9!7C-=W_b$rSvqc;B<@!WtAbEO)0W-$|V o^-ip0uiF!o2xUK8S^pLXE0?lF3jgVh`g8j~wWU`%-eZy-~Qh3{h8e;6mkew<$mSmR|BDMS{W_S z(K>8(2LwkPd&tK&#`;V=;!~Sq^aydvjZ?%8K~Jb{aPt|eX1>E0sEU)q<3?k5>rGQc zu^R?pnA`Ro)!iLD4TE;r5GrnJ&(R`qSck2OAOx~8M>gS@lT&0N}pSp>-33z9Km(_S9 zzs~fSU1h>{HNRi~@cZ$9eh}eGhF~8@ndJBZtBny$j-cYxI|*ejPt5q08?@bzP}yNg zI<69Y{!l_2=f$YMHf@Drl}Hm>kkG#4$tgGuXWL}yqP$?mG|F@(JrJZvFt!r;P=Z8|916FBb zDB-p2Y!Jq+uorNd0gx|9=Hc=4ofS#9@5I9DJJM=}9v4zszO)WHjIFBWV5QINO&0Z+ z7UR}xyCc23t>v{ft6@cMv;ZMv$MY7%isJ^6yJV$fy>KztR!a{#iw0?yRt#gVQppab z6GafH77dc3wzIzy%>p+y4+7yiLU0++;oGD{i?~RyV}dKGUZy2Hymk2=)UHC?f%}qW zEO30mSdwGR4|&hi=R9MtdyaShM$SOIv@8>%pl$jWpsS{I{j((1cdDj+<50-%Q1sfh tar0H$BfESl_mq{Y^hwi@11jdpzpKNaq}cke*>ulFL(d| literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/util.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/__pycache__/util.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..021bbb082965ee988b38875ce185cc3699cf243a GIT binary patch literal 2016 zcmb`I-EP}96vs(@$F);q%etl8Fk!&f8Caq=Sdli^N3j;`RnlV3fI&dP$|jx2l_iy= z(d4&9h zjrrw+a|dqqI~YzlEl4_j(v;e^lREb4rtW>h9qzs)+!fx5oBGi9xDRb#_<|0C6E6*+ zFW@2cg(8G85vPwxC;A(rldT@w`Q^?W6@w!P&IU8l)O|(z)Xo#7-Yepf)Q1S3S=_Gn z?^V60LT={!d3m_mh~au9w2C(}t>wm(x{~@y+|88WaaG28SHz;+i}x}a_hmJVb+y03 zZG>;*ydT$6C{gNF8>dHS>EIJZ!SvMEU^LP6h@3cMXF@2^uJ#80IN*@t#5sXCEaU_d zbhpyWn-8|`KVWO?*Ke$|?Yj^2KXL-K8()oTA*BPsz!&(W4$6%im^Y*>*q-=^Sw-q1S(4URl$W+$xtTU0b{q+&2oK@f7fI6 z-r9BDzqK=v#f|>W+qaYLq|WP=yi__XiWRY*|Uv1*Iz1SVpv%!Yz7(_m~^@lk<(ld)%`teCwC z8G}V&%!W0sS%p1O6wud%39RW*c?ta%8dPopZks^ld5-P5n5J)F+?*y$5-ilqXhxOG zJ@DU9<(syZ;aBl&1SN07eGUYKz=CiGobwCIP}$cs=`!(S*O)5f@cEWMX8K$*Ch3AhQ`h6I$grgnVe?JdR3{# zzBb|9@#>M<1?6zN&jqxKS`~4ckKtDKG=ODn&?a4^hgbeB!E`w<7UDAq@U{?R@3cs| z)$!ldW+E*zd9Qk2%4PV{xOG*(Gr><`#21+0DQC-Xr8W=O2_%XokxSV|V^zcu@&hy% zlZ1MI1oxCAry1E5_#a7{?Wc1jA-M@Mwx07VOy z9cSxs+3ro=-AIz6>Se`lrS!&&IF=maj%0o@KG$Hd3c%ng(4M1nrsnsd^Cf%+b&&xbLtO)RLg6^|v~LgB z;4DC)jA3l*^!C#hmsQPprZYU(Z7FAJ#pQl4PAf2AhEv-)oZ@sg-T#Oa3jWo6ipXgO z5_>ue5;aPrQJHJpu@bkPM6^v%bqAuS?E#iXh{;{7%bo*v`A)f=5{x_7MBh)((jW29 QN#TfyMh^Vkc5|uy4@2IzdjJ3c literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_config.py b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_config.py new file mode 100644 index 000000000..9da8914ca --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_config.py @@ -0,0 +1,108 @@ +from contextlib import contextmanager +import time + +import pymongo +import pytest + +from flask_pymongo.tests.util import FlaskRequestTest +import flask_pymongo + + +class CouldNotConnect(Exception): + pass + + +@contextmanager +def doesnt_raise(exc=BaseException): + try: + yield + except exc: + pytest.fail("{} was raised but should not have been".format(exc)) + + +class FlaskPyMongoConfigTest(FlaskRequestTest): + + def setUp(self): + super(FlaskPyMongoConfigTest, self).setUp() + + conn = pymongo.MongoClient(port=self.port) + conn.test.command("ping") # wait for server + + def tearDown(self): + super(FlaskPyMongoConfigTest, self).tearDown() + + conn = pymongo.MongoClient(port=self.port) + + conn.drop_database(self.dbname) + conn.drop_database(self.dbname + "2") + + def test_config_with_uri_in_flask_conf_var(self): + uri = "mongodb://localhost:{}/{}".format(self.port, self.dbname) + self.app.config["MONGO_URI"] = uri + + mongo = flask_pymongo.PyMongo(self.app, connect=True) + + _wait_until_connected(mongo) + assert mongo.db.name == self.dbname + assert ("localhost", self.port) == mongo.cx.address + + def test_config_with_uri_passed_directly(self): + uri = "mongodb://localhost:{}/{}".format(self.port, self.dbname) + + mongo = flask_pymongo.PyMongo(self.app, uri, connect=True) + + _wait_until_connected(mongo) + assert mongo.db.name == self.dbname + assert ("localhost", self.port) == mongo.cx.address + + def test_it_fails_with_no_uri(self): + self.app.config.pop("MONGO_URI", None) + + with pytest.raises(ValueError): + flask_pymongo.PyMongo(self.app) + + def test_multiple_pymongos(self): + uri1 = "mongodb://localhost:{}/{}".format(self.port, self.dbname) + uri2 = "mongodb://localhost:{}/{}".format(self.port, self.dbname + "2") + + mongo1 = flask_pymongo.PyMongo(self.app, uri1) # noqa: F841 unused variable + mongo2 = flask_pymongo.PyMongo(self.app, uri2) # noqa: F841 unused variable + + # this test passes if it raises no exceptions + + def test_custom_document_class(self): + class CustomDict(dict): + pass + + uri = "mongodb://localhost:{}/{}".format(self.port, self.dbname) + mongo = flask_pymongo.PyMongo(self.app, uri, document_class=CustomDict) + assert mongo.db.things.find_one() is None, "precondition failed" + + mongo.db.things.insert_one({"_id": "thing", "val": "foo"}) + + assert type(mongo.db.things.find_one()) == CustomDict + + def test_it_doesnt_connect_by_default(self): + uri = "mongodb://localhost:{}/{}".format(self.port, self.dbname) + + mongo = flask_pymongo.PyMongo(self.app, uri) + + with pytest.raises(CouldNotConnect): + _wait_until_connected(mongo, timeout=0.2) + + def test_it_doesnt_require_db_name_in_uri(self): + uri = "mongodb://localhost:{}".format(self.port) + + with doesnt_raise(Exception): + mongo = flask_pymongo.PyMongo(self.app, uri) + + assert mongo.db is None + + +def _wait_until_connected(mongo, timeout=1.0): + start = time.time() + while time.time() < (start + timeout): + if mongo.cx.nodes: + return + time.sleep(0.05) + raise CouldNotConnect("could not prove mongodb connected in %r seconds" % timeout) diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_gridfs.py b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_gridfs.py new file mode 100644 index 000000000..5dc57dae7 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_gridfs.py @@ -0,0 +1,100 @@ +from hashlib import md5 +from io import BytesIO + +from bson.objectid import ObjectId +from gridfs import GridFS +from werkzeug.exceptions import NotFound +import pytest + +from flask_pymongo.tests.util import FlaskPyMongoTest + + +class GridFSCleanupMixin(object): + + def tearDown(self): + gridfs = GridFS(self.mongo.db) + files = list(gridfs.find()) + for gridfile in files: + gridfs.delete(gridfile._id) + + super(GridFSCleanupMixin, self).tearDown() + + +class TestSaveFile(GridFSCleanupMixin, FlaskPyMongoTest): + + def test_it_saves_files(self): + fileobj = BytesIO(b"these are the bytes") + + self.mongo.save_file("my-file", fileobj) + + gridfs = GridFS(self.mongo.db) + assert gridfs.exists({"filename": "my-file"}) + + def test_it_guesses_type_from_filename(self): + fileobj = BytesIO(b"these are the bytes") + + self.mongo.save_file("my-file.txt", fileobj) + + gridfs = GridFS(self.mongo.db) + gridfile = gridfs.find_one({"filename": "my-file.txt"}) + assert gridfile.content_type == "text/plain" + + def test_it_saves_files_with_props(self): + fileobj = BytesIO(b"these are the bytes") + + self.mongo.save_file("my-file", fileobj, foo="bar") + + gridfs = GridFS(self.mongo.db) + gridfile = gridfs.find_one({"filename": "my-file"}) + assert gridfile.foo == "bar" + + def test_it_returns_id(self): + fileobj = BytesIO(b"these are the bytes") + + _id = self.mongo.save_file("my-file", fileobj, foo="bar") + + assert type(_id) is ObjectId + + +class TestSendFile(GridFSCleanupMixin, FlaskPyMongoTest): + + def setUp(self): + super(TestSendFile, self).setUp() + + # make it bigger than 1 gridfs chunk + self.myfile = BytesIO(b"a" * 500 * 1024) + self.mongo.save_file("myfile.txt", self.myfile) + + def test_it_404s_for_missing_files(self): + with pytest.raises(NotFound): + self.mongo.send_file("no-such-file.txt") + + def test_it_sets_content_type(self): + resp = self.mongo.send_file("myfile.txt") + assert resp.content_type.startswith("text/plain") + + def test_it_sets_content_length(self): + resp = self.mongo.send_file("myfile.txt") + assert resp.content_length == len(self.myfile.getvalue()) + + def test_it_sets_supports_conditional_gets(self): + # a basic conditional GET + environ_args = { + "method": "GET", + "headers": { + "If-None-Match": md5(self.myfile.getvalue()).hexdigest(), + }, + } + + with self.app.test_request_context(**environ_args): + resp = self.mongo.send_file("myfile.txt") + assert resp.status_code == 304 + + def test_it_sets_cache_headers(self): + resp = self.mongo.send_file("myfile.txt", cache_for=60) + assert resp.cache_control.max_age == 60 + assert resp.cache_control.public is True + + def test_it_streams_results(self): + resp = self.mongo.send_file("myfile.txt") + assert resp.is_streamed diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_url_converter.py b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_url_converter.py new file mode 100644 index 000000000..aeb349c6e --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_url_converter.py @@ -0,0 +1,17 @@ +from bson import ObjectId +from werkzeug.exceptions import NotFound + +from flask_pymongo import BSONObjectIdConverter +from flask_pymongo.tests.util import FlaskPyMongoTest + + +class UrlConverterTest(FlaskPyMongoTest): + + def test_bson_object_id_converter(self): + converter = BSONObjectIdConverter("/") + + self.assertRaises(NotFound, converter.to_python, ("132")) + assert converter.to_python("4e4ac5cfffc84958fa1f45fb") == \ + ObjectId("4e4ac5cfffc84958fa1f45fb") + assert converter.to_url(ObjectId("4e4ac5cfffc84958fa1f45fb")) == \ + "4e4ac5cfffc84958fa1f45fb" diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_wrappers.py b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_wrappers.py new file mode 100644 index 000000000..1bb334b28 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/test_wrappers.py @@ -0,0 +1,33 @@ +from werkzeug.exceptions import HTTPException + +from flask_pymongo.tests.util import FlaskPyMongoTest + + +class CollectionTest(FlaskPyMongoTest): + + def test_find_one_or_404(self): + self.mongo.db.things.delete_many({}) + + try: + self.mongo.db.things.find_one_or_404({"_id": "thing"}) + except HTTPException as notfound: + assert notfound.code == 404, "raised wrong exception" + + self.mongo.db.things.insert_one({"_id": "thing", "val": "foo"}) + + # now it should not raise + thing = self.mongo.db.things.find_one_or_404({"_id": "thing"}) + assert thing["val"] == "foo", "got wrong thing" + + # also test with dotted-named collections + self.mongo.db.things.morethings.delete_many({}) + try: + self.mongo.db.things.morethings.find_one_or_404({"_id": "thing"}) + except HTTPException as notfound: + assert notfound.code == 404, "raised wrong exception" + + self.mongo.db.things.morethings.insert_one({"_id": "thing", "val": "foo"}) + + # now it should not raise + thing = self.mongo.db.things.morethings.find_one_or_404({"_id": "thing"}) + assert thing["val"] == "foo", "got wrong thing" diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/util.py b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/util.py new file mode 100644 index 000000000..3ee5d4a4d --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/flask_pymongo/tests/util.py @@ -0,0 +1,48 @@ +import os +import unittest + +import flask + +import flask_pymongo + + +class ToxDockerMixin(object): + + """ + Sets :attr:`port` based on the env var from tox-docker, if present. + """ + + def setUp(self): + super(ToxDockerMixin, self).setUp() + + self.port = int(os.environ.get("MONGO_27017_TCP", 27017)) + + +class FlaskRequestTest(ToxDockerMixin, unittest.TestCase): + + def setUp(self): + super(FlaskRequestTest, self).setUp() + + self.dbname = self.__class__.__name__ + self.app = flask.Flask("test") + self.context = self.app.test_request_context("/") + self.context.push() + + def tearDown(self): + super(FlaskRequestTest, self).tearDown() + + self.context.pop() + + +class FlaskPyMongoTest(FlaskRequestTest): + + def setUp(self): + super(FlaskPyMongoTest, self).setUp() + + uri = "mongodb://localhost:{}/{}".format(self.port, self.dbname) + self.mongo = flask_pymongo.PyMongo(self.app, uri) + + def tearDown(self): + self.mongo.cx.drop_database(self.dbname) + + super(FlaskPyMongoTest, self).tearDown() diff --git a/backend/venv/lib/python3.7/site-packages/flask_pymongo/wrappers.py b/backend/venv/lib/python3.7/site-packages/flask_pymongo/wrappers.py new file mode 100644 index 000000000..5e82c5484 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/flask_pymongo/wrappers.py @@ -0,0 +1,117 @@ +# Copyright (c) 2011-2017, Dan Crosta +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +from flask import abort +from pymongo import collection +from pymongo import database +from pymongo import mongo_client + + +class MongoClient(mongo_client.MongoClient): + + """Wrapper for :class:`~pymongo.mongo_client.MongoClient`. + + Returns instances of Flask-PyMongo + :class:`~flask_pymongo.wrappers.Database` instead of native PyMongo + :class:`~pymongo.database.Database` when accessed with dot notation. + + """ + + def __getattr__(self, name): # noqa: D105 + attr = super(MongoClient, self).__getattr__(name) + if isinstance(attr, database.Database): + return Database(self, name) + return attr + + def __getitem__(self, item): # noqa: D105 + attr = super(MongoClient, self).__getitem__(item) + if isinstance(attr, database.Database): + return Database(self, item) + return attr + + +class Database(database.Database): + + """Wrapper for :class:`~pymongo.database.Database`. + + Returns instances of Flask-PyMongo + :class:`~flask_pymongo.wrappers.Collection` instead of native PyMongo + :class:`~pymongo.collection.Collection` when accessed with dot notation. + + """ + + def __getattr__(self, name): # noqa: D105 + attr = super(Database, self).__getattr__(name) + if isinstance(attr, collection.Collection): + return Collection(self, name) + return attr + + def __getitem__(self, item): # noqa: D105 + item_ = super(Database, self).__getitem__(item) + if isinstance(item_, collection.Collection): + return Collection(self, item) + return item_ + + +class Collection(collection.Collection): + + """Sub-class of PyMongo :class:`~pymongo.collection.Collection` with helpers. + + """ + + def __getattr__(self, name): # noqa: D105 + attr = super(Collection, self).__getattr__(name) + if isinstance(attr, collection.Collection): + db = self._Collection__database + return Collection(db, attr.name) + return attr + + def __getitem__(self, item): # noqa: D105 + item_ = super(Collection, self).__getitem__(item) + if isinstance(item_, collection.Collection): + db = self._Collection__database + return Collection(db, item_.name) + return item_ + + def find_one_or_404(self, *args, **kwargs): + """Find a single document or raise a 404. + + This is like :meth:`~pymongo.collection.Collection.find_one`, but + rather than returning ``None``, cause a 404 Not Found HTTP status + on the request. + + .. code-block:: python + + @app.route("/user/") + def user_profile(username): + user = mongo.db.users.find_one_or_404({"_id": username}) + return render_template("user.html", + user=user) + + """ + found = self.find_one(*args, **kwargs) + if found is None: + abort(404) + return found diff --git a/backend/venv/lib/python3.7/site-packages/gridfs/__init__.py b/backend/venv/lib/python3.7/site-packages/gridfs/__init__.py new file mode 100644 index 000000000..6c56a605e --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/gridfs/__init__.py @@ -0,0 +1,930 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""GridFS is a specification for storing large objects in Mongo. + +The :mod:`gridfs` package is an implementation of GridFS on top of +:mod:`pymongo`, exposing a file-like interface. + +.. mongodoc:: gridfs +""" + +from bson.py3compat import abc +from gridfs.errors import NoFile +from gridfs.grid_file import (GridIn, + GridOut, + GridOutCursor, + DEFAULT_CHUNK_SIZE, + _clear_entity_type_registry) +from pymongo import (ASCENDING, + DESCENDING) +from pymongo.common import UNAUTHORIZED_CODES, validate_string +from pymongo.database import Database +from pymongo.errors import ConfigurationError, OperationFailure + + +class GridFS(object): + """An instance of GridFS on top of a single Database. + """ + def __init__(self, database, collection="fs", disable_md5=False): + """Create a new instance of :class:`GridFS`. + + Raises :class:`TypeError` if `database` is not an instance of + :class:`~pymongo.database.Database`. + + :Parameters: + - `database`: database to use + - `collection` (optional): root collection to use + - `disable_md5` (optional): When True, MD5 checksums will not be + computed for uploaded files. Useful in environments where MD5 + cannot be used for regulatory or other reasons. Defaults to False. + + .. versionchanged:: 3.1 + Indexes are only ensured on the first write to the DB. + + .. versionchanged:: 3.0 + `database` must use an acknowledged + :attr:`~pymongo.database.Database.write_concern` + + .. mongodoc:: gridfs + """ + if not isinstance(database, Database): + raise TypeError("database must be an instance of Database") + + database = _clear_entity_type_registry(database) + + if not database.write_concern.acknowledged: + raise ConfigurationError('database must use ' + 'acknowledged write_concern') + + self.__database = database + self.__collection = database[collection] + self.__files = self.__collection.files + self.__chunks = self.__collection.chunks + self.__disable_md5 = disable_md5 + + def new_file(self, **kwargs): + """Create a new file in GridFS. + + Returns a new :class:`~gridfs.grid_file.GridIn` instance to + which data can be written. Any keyword arguments will be + passed through to :meth:`~gridfs.grid_file.GridIn`. + + If the ``"_id"`` of the file is manually specified, it must + not already exist in GridFS. Otherwise + :class:`~gridfs.errors.FileExists` is raised. + + :Parameters: + - `**kwargs` (optional): keyword arguments for file creation + """ + # No need for __ensure_index_files_id() here; GridIn ensures + # the (files_id, n) index when needed. + return GridIn( + self.__collection, disable_md5=self.__disable_md5, **kwargs) + + def put(self, data, **kwargs): + """Put data in GridFS as a new file. + + Equivalent to doing:: + + try: + f = new_file(**kwargs) + f.write(data) + finally: + f.close() + + `data` can be either an instance of :class:`str` (:class:`bytes` + in python 3) or a file-like object providing a :meth:`read` method. + If an `encoding` keyword argument is passed, `data` can also be a + :class:`unicode` (:class:`str` in python 3) instance, which will + be encoded as `encoding` before being written. Any keyword arguments + will be passed through to the created file - see + :meth:`~gridfs.grid_file.GridIn` for possible arguments. Returns the + ``"_id"`` of the created file. + + If the ``"_id"`` of the file is manually specified, it must + not already exist in GridFS. Otherwise + :class:`~gridfs.errors.FileExists` is raised. + + :Parameters: + - `data`: data to be written as a file. + - `**kwargs` (optional): keyword arguments for file creation + + .. versionchanged:: 3.0 + w=0 writes to GridFS are now prohibited. + """ + grid_file = GridIn( + self.__collection, disable_md5=self.__disable_md5, **kwargs) + try: + grid_file.write(data) + finally: + grid_file.close() + + return grid_file._id + + def get(self, file_id, session=None): + """Get a file from GridFS by ``"_id"``. + + Returns an instance of :class:`~gridfs.grid_file.GridOut`, + which provides a file-like interface for reading. + + :Parameters: + - `file_id`: ``"_id"`` of the file to get + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + gout = GridOut(self.__collection, file_id, session=session) + + # Raise NoFile now, instead of on first attribute access. + gout._ensure_file() + return gout + + def get_version(self, filename=None, version=-1, session=None, **kwargs): + """Get a file from GridFS by ``"filename"`` or metadata fields. + + Returns a version of the file in GridFS whose filename matches + `filename` and whose metadata fields match the supplied keyword + arguments, as an instance of :class:`~gridfs.grid_file.GridOut`. + + Version numbering is a convenience atop the GridFS API provided + by MongoDB. If more than one file matches the query (either by + `filename` alone, by metadata fields, or by a combination of + both), then version ``-1`` will be the most recently uploaded + matching file, ``-2`` the second most recently + uploaded, etc. Version ``0`` will be the first version + uploaded, ``1`` the second version, etc. So if three versions + have been uploaded, then version ``0`` is the same as version + ``-3``, version ``1`` is the same as version ``-2``, and + version ``2`` is the same as version ``-1``. + + Raises :class:`~gridfs.errors.NoFile` if no such version of + that file exists. + + :Parameters: + - `filename`: ``"filename"`` of the file to get, or `None` + - `version` (optional): version of the file to get (defaults + to -1, the most recent version uploaded) + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + - `**kwargs` (optional): find files by custom metadata. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.1 + ``get_version`` no longer ensures indexes. + """ + query = kwargs + if filename is not None: + query["filename"] = filename + + cursor = self.__files.find(query, session=session) + if version < 0: + skip = abs(version) - 1 + cursor.limit(-1).skip(skip).sort("uploadDate", DESCENDING) + else: + cursor.limit(-1).skip(version).sort("uploadDate", ASCENDING) + try: + doc = next(cursor) + return GridOut( + self.__collection, file_document=doc, session=session) + except StopIteration: + raise NoFile("no version %d for filename %r" % (version, filename)) + + def get_last_version(self, filename=None, session=None, **kwargs): + """Get the most recent version of a file in GridFS by ``"filename"`` + or metadata fields. + + Equivalent to calling :meth:`get_version` with the default + `version` (``-1``). + + :Parameters: + - `filename`: ``"filename"`` of the file to get, or `None` + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + - `**kwargs` (optional): find files by custom metadata. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + return self.get_version(filename=filename, session=session, **kwargs) + + # TODO add optional safe mode for chunk removal? + def delete(self, file_id, session=None): + """Delete a file from GridFS by ``"_id"``. + + Deletes all data belonging to the file with ``"_id"``: + `file_id`. + + .. warning:: Any processes/threads reading from the file while + this method is executing will likely see an invalid/corrupt + file. Care should be taken to avoid concurrent reads to a file + while it is being deleted. + + .. note:: Deletes of non-existent files are considered successful + since the end result is the same: no file with that _id remains. + + :Parameters: + - `file_id`: ``"_id"`` of the file to delete + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.1 + ``delete`` no longer ensures indexes. + """ + self.__files.delete_one({"_id": file_id}, session=session) + self.__chunks.delete_many({"files_id": file_id}, session=session) + + def list(self, session=None): + """List the names of all files stored in this instance of + :class:`GridFS`. + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.1 + ``list`` no longer ensures indexes. + """ + # With an index, distinct includes documents with no filename + # as None. + return [ + name for name in self.__files.distinct("filename", session=session) + if name is not None] + + def find_one(self, filter=None, session=None, *args, **kwargs): + """Get a single file from gridfs. + + All arguments to :meth:`find` are also valid arguments for + :meth:`find_one`, although any `limit` argument will be + ignored. Returns a single :class:`~gridfs.grid_file.GridOut`, + or ``None`` if no matching file is found. For example:: + + file = fs.find_one({"filename": "lisa.txt"}) + + :Parameters: + - `filter` (optional): a dictionary specifying + the query to be performing OR any other type to be used as + the value for a query for ``"_id"`` in the file collection. + - `*args` (optional): any additional positional arguments are + the same as the arguments to :meth:`find`. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + - `**kwargs` (optional): any additional keyword arguments + are the same as the arguments to :meth:`find`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + if filter is not None and not isinstance(filter, abc.Mapping): + filter = {"_id": filter} + + for f in self.find(filter, *args, session=session, **kwargs): + return f + + return None + + def find(self, *args, **kwargs): + """Query GridFS for files. + + Returns a cursor that iterates across files matching + arbitrary queries on the files collection. Can be combined + with other modifiers for additional control. For example:: + + for grid_out in fs.find({"filename": "lisa.txt"}, + no_cursor_timeout=True): + data = grid_out.read() + + would iterate through all versions of "lisa.txt" stored in GridFS. + Note that setting no_cursor_timeout to True may be important to + prevent the cursor from timing out during long multi-file processing + work. + + As another example, the call:: + + most_recent_three = fs.find().sort("uploadDate", -1).limit(3) + + would return a cursor to the three most recently uploaded files + in GridFS. + + Follows a similar interface to + :meth:`~pymongo.collection.Collection.find` + in :class:`~pymongo.collection.Collection`. + + If a :class:`~pymongo.client_session.ClientSession` is passed to + :meth:`find`, all returned :class:`~gridfs.grid_file.GridOut` instances + are associated with that session. + + :Parameters: + - `filter` (optional): a SON object specifying elements which + must be present for a document to be included in the + result set + - `skip` (optional): the number of files to omit (from + the start of the result set) when returning the results + - `limit` (optional): the maximum number of results to + return + - `no_cursor_timeout` (optional): if False (the default), any + returned cursor is closed by the server after 10 minutes of + inactivity. If set to True, the returned cursor will never + time out on the server. Care should be taken to ensure that + cursors with no_cursor_timeout turned on are properly closed. + - `sort` (optional): a list of (key, direction) pairs + specifying the sort order for this query. See + :meth:`~pymongo.cursor.Cursor.sort` for details. + + Raises :class:`TypeError` if any of the arguments are of + improper type. Returns an instance of + :class:`~gridfs.grid_file.GridOutCursor` + corresponding to this query. + + .. versionchanged:: 3.0 + Removed the read_preference, tag_sets, and + secondary_acceptable_latency_ms options. + .. versionadded:: 2.7 + .. mongodoc:: find + """ + return GridOutCursor(self.__collection, *args, **kwargs) + + def exists(self, document_or_id=None, session=None, **kwargs): + """Check if a file exists in this instance of :class:`GridFS`. + + The file to check for can be specified by the value of its + ``_id`` key, or by passing in a query document. A query + document can be passed in as dictionary, or by using keyword + arguments. Thus, the following three calls are equivalent: + + >>> fs.exists(file_id) + >>> fs.exists({"_id": file_id}) + >>> fs.exists(_id=file_id) + + As are the following two calls: + + >>> fs.exists({"filename": "mike.txt"}) + >>> fs.exists(filename="mike.txt") + + And the following two: + + >>> fs.exists({"foo": {"$gt": 12}}) + >>> fs.exists(foo={"$gt": 12}) + + Returns ``True`` if a matching file exists, ``False`` + otherwise. Calls to :meth:`exists` will not automatically + create appropriate indexes; application developers should be + sure to create indexes if needed and as appropriate. + + :Parameters: + - `document_or_id` (optional): query document, or _id of the + document to check for + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + - `**kwargs` (optional): keyword arguments are used as a + query document, if they're present. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + if kwargs: + f = self.__files.find_one(kwargs, ["_id"], session=session) + else: + f = self.__files.find_one(document_or_id, ["_id"], session=session) + + return f is not None + + +class GridFSBucket(object): + """An instance of GridFS on top of a single Database.""" + + def __init__(self, db, bucket_name="fs", + chunk_size_bytes=DEFAULT_CHUNK_SIZE, write_concern=None, + read_preference=None, disable_md5=False): + """Create a new instance of :class:`GridFSBucket`. + + Raises :exc:`TypeError` if `database` is not an instance of + :class:`~pymongo.database.Database`. + + Raises :exc:`~pymongo.errors.ConfigurationError` if `write_concern` + is not acknowledged. + + :Parameters: + - `database`: database to use. + - `bucket_name` (optional): The name of the bucket. Defaults to 'fs'. + - `chunk_size_bytes` (optional): The chunk size in bytes. Defaults + to 255KB. + - `write_concern` (optional): The + :class:`~pymongo.write_concern.WriteConcern` to use. If ``None`` + (the default) db.write_concern is used. + - `read_preference` (optional): The read preference to use. If + ``None`` (the default) db.read_preference is used. + - `disable_md5` (optional): When True, MD5 checksums will not be + computed for uploaded files. Useful in environments where MD5 + cannot be used for regulatory or other reasons. Defaults to False. + + .. versionadded:: 3.1 + + .. mongodoc:: gridfs + """ + if not isinstance(db, Database): + raise TypeError("database must be an instance of Database") + + db = _clear_entity_type_registry(db) + + wtc = write_concern if write_concern is not None else db.write_concern + if not wtc.acknowledged: + raise ConfigurationError('write concern must be acknowledged') + + self._db = db + self._bucket_name = bucket_name + self._collection = db[bucket_name] + self._disable_md5 = disable_md5 + + self._chunks = self._collection.chunks.with_options( + write_concern=write_concern, + read_preference=read_preference) + + self._files = self._collection.files.with_options( + write_concern=write_concern, + read_preference=read_preference) + + self._chunk_size_bytes = chunk_size_bytes + + def open_upload_stream(self, filename, chunk_size_bytes=None, + metadata=None, session=None): + """Opens a Stream that the application can write the contents of the + file to. + + The user must specify the filename, and can choose to add any + additional information in the metadata field of the file document or + modify the chunk size. + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + grid_in = fs.open_upload_stream( + "test_file", chunk_size_bytes=4, + metadata={"contentType": "text/plain"}) + grid_in.write("data I want to store!") + grid_in.close() # uploaded on close + + Returns an instance of :class:`~gridfs.grid_file.GridIn`. + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + Raises :exc:`~ValueError` if `filename` is not a string. + + :Parameters: + - `filename`: The name of the file to upload. + - `chunk_size_bytes` (options): The number of bytes per chunk of this + file. Defaults to the chunk_size_bytes in :class:`GridFSBucket`. + - `metadata` (optional): User data for the 'metadata' field of the + files collection document. If not provided the metadata field will + be omitted from the files collection document. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + validate_string("filename", filename) + + opts = {"filename": filename, + "chunk_size": (chunk_size_bytes if chunk_size_bytes + is not None else self._chunk_size_bytes)} + if metadata is not None: + opts["metadata"] = metadata + + return GridIn( + self._collection, + session=session, + disable_md5=self._disable_md5, + **opts) + + def open_upload_stream_with_id( + self, file_id, filename, chunk_size_bytes=None, metadata=None, + session=None): + """Opens a Stream that the application can write the contents of the + file to. + + The user must specify the file id and filename, and can choose to add + any additional information in the metadata field of the file document + or modify the chunk size. + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + grid_in = fs.open_upload_stream_with_id( + ObjectId(), + "test_file", + chunk_size_bytes=4, + metadata={"contentType": "text/plain"}) + grid_in.write("data I want to store!") + grid_in.close() # uploaded on close + + Returns an instance of :class:`~gridfs.grid_file.GridIn`. + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + Raises :exc:`~ValueError` if `filename` is not a string. + + :Parameters: + - `file_id`: The id to use for this file. The id must not have + already been used for another file. + - `filename`: The name of the file to upload. + - `chunk_size_bytes` (options): The number of bytes per chunk of this + file. Defaults to the chunk_size_bytes in :class:`GridFSBucket`. + - `metadata` (optional): User data for the 'metadata' field of the + files collection document. If not provided the metadata field will + be omitted from the files collection document. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + validate_string("filename", filename) + + opts = {"_id": file_id, + "filename": filename, + "chunk_size": (chunk_size_bytes if chunk_size_bytes + is not None else self._chunk_size_bytes)} + if metadata is not None: + opts["metadata"] = metadata + + return GridIn( + self._collection, + session=session, + disable_md5=self._disable_md5, + **opts) + + def upload_from_stream(self, filename, source, chunk_size_bytes=None, + metadata=None, session=None): + """Uploads a user file to a GridFS bucket. + + Reads the contents of the user file from `source` and uploads + it to the file `filename`. Source can be a string or file-like object. + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + file_id = fs.upload_from_stream( + "test_file", + "data I want to store!", + chunk_size_bytes=4, + metadata={"contentType": "text/plain"}) + + Returns the _id of the uploaded file. + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + Raises :exc:`~ValueError` if `filename` is not a string. + + :Parameters: + - `filename`: The name of the file to upload. + - `source`: The source stream of the content to be uploaded. Must be + a file-like object that implements :meth:`read` or a string. + - `chunk_size_bytes` (options): The number of bytes per chunk of this + file. Defaults to the chunk_size_bytes of :class:`GridFSBucket`. + - `metadata` (optional): User data for the 'metadata' field of the + files collection document. If not provided the metadata field will + be omitted from the files collection document. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + with self.open_upload_stream( + filename, chunk_size_bytes, metadata, session=session) as gin: + gin.write(source) + + return gin._id + + def upload_from_stream_with_id(self, file_id, filename, source, + chunk_size_bytes=None, metadata=None, + session=None): + """Uploads a user file to a GridFS bucket with a custom file id. + + Reads the contents of the user file from `source` and uploads + it to the file `filename`. Source can be a string or file-like object. + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + file_id = fs.upload_from_stream( + ObjectId(), + "test_file", + "data I want to store!", + chunk_size_bytes=4, + metadata={"contentType": "text/plain"}) + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + Raises :exc:`~ValueError` if `filename` is not a string. + + :Parameters: + - `file_id`: The id to use for this file. The id must not have + already been used for another file. + - `filename`: The name of the file to upload. + - `source`: The source stream of the content to be uploaded. Must be + a file-like object that implements :meth:`read` or a string. + - `chunk_size_bytes` (options): The number of bytes per chunk of this + file. Defaults to the chunk_size_bytes of :class:`GridFSBucket`. + - `metadata` (optional): User data for the 'metadata' field of the + files collection document. If not provided the metadata field will + be omitted from the files collection document. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + with self.open_upload_stream_with_id( + file_id, filename, chunk_size_bytes, metadata, + session=session) as gin: + gin.write(source) + + def open_download_stream(self, file_id, session=None): + """Opens a Stream from which the application can read the contents of + the stored file specified by file_id. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # get _id of file to read. + file_id = fs.upload_from_stream("test_file", "data I want to store!") + grid_out = fs.open_download_stream(file_id) + contents = grid_out.read() + + Returns an instance of :class:`~gridfs.grid_file.GridOut`. + + Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists. + + :Parameters: + - `file_id`: The _id of the file to be downloaded. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + gout = GridOut(self._collection, file_id, session=session) + + # Raise NoFile now, instead of on first attribute access. + gout._ensure_file() + return gout + + def download_to_stream(self, file_id, destination, session=None): + """Downloads the contents of the stored file specified by file_id and + writes the contents to `destination`. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # Get _id of file to read + file_id = fs.upload_from_stream("test_file", "data I want to store!") + # Get file to write to + file = open('myfile','wb+') + fs.download_to_stream(file_id, file) + file.seek(0) + contents = file.read() + + Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists. + + :Parameters: + - `file_id`: The _id of the file to be downloaded. + - `destination`: a file-like object implementing :meth:`write`. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + with self.open_download_stream(file_id, session=session) as gout: + for chunk in gout: + destination.write(chunk) + + def delete(self, file_id, session=None): + """Given an file_id, delete this stored file's files collection document + and associated chunks from a GridFS bucket. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # Get _id of file to delete + file_id = fs.upload_from_stream("test_file", "data I want to store!") + fs.delete(file_id) + + Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists. + + :Parameters: + - `file_id`: The _id of the file to be deleted. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + res = self._files.delete_one({"_id": file_id}, session=session) + self._chunks.delete_many({"files_id": file_id}, session=session) + if not res.deleted_count: + raise NoFile( + "no file could be deleted because none matched %s" % file_id) + + def find(self, *args, **kwargs): + """Find and return the files collection documents that match ``filter`` + + Returns a cursor that iterates across files matching + arbitrary queries on the files collection. Can be combined + with other modifiers for additional control. + + For example:: + + for grid_data in fs.find({"filename": "lisa.txt"}, + no_cursor_timeout=True): + data = grid_data.read() + + would iterate through all versions of "lisa.txt" stored in GridFS. + Note that setting no_cursor_timeout to True may be important to + prevent the cursor from timing out during long multi-file processing + work. + + As another example, the call:: + + most_recent_three = fs.find().sort("uploadDate", -1).limit(3) + + would return a cursor to the three most recently uploaded files + in GridFS. + + Follows a similar interface to + :meth:`~pymongo.collection.Collection.find` + in :class:`~pymongo.collection.Collection`. + + If a :class:`~pymongo.client_session.ClientSession` is passed to + :meth:`find`, all returned :class:`~gridfs.grid_file.GridOut` instances + are associated with that session. + + :Parameters: + - `filter`: Search query. + - `batch_size` (optional): The number of documents to return per + batch. + - `limit` (optional): The maximum number of documents to return. + - `no_cursor_timeout` (optional): The server normally times out idle + cursors after an inactivity period (10 minutes) to prevent excess + memory use. Set this option to True prevent that. + - `skip` (optional): The number of documents to skip before + returning. + - `sort` (optional): The order by which to sort results. Defaults to + None. + """ + return GridOutCursor(self._collection, *args, **kwargs) + + def open_download_stream_by_name(self, filename, revision=-1, session=None): + """Opens a Stream from which the application can read the contents of + `filename` and optional `revision`. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + grid_out = fs.open_download_stream_by_name("test_file") + contents = grid_out.read() + + Returns an instance of :class:`~gridfs.grid_file.GridOut`. + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + + Raises :exc:`~ValueError` filename is not a string. + + :Parameters: + - `filename`: The name of the file to read from. + - `revision` (optional): Which revision (documents with the same + filename and different uploadDate) of the file to retrieve. + Defaults to -1 (the most recent revision). + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + + :Note: Revision numbers are defined as follows: + + - 0 = the original stored file + - 1 = the first revision + - 2 = the second revision + - etc... + - -2 = the second most recent revision + - -1 = the most recent revision + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + validate_string("filename", filename) + + query = {"filename": filename} + + cursor = self._files.find(query, session=session) + if revision < 0: + skip = abs(revision) - 1 + cursor.limit(-1).skip(skip).sort("uploadDate", DESCENDING) + else: + cursor.limit(-1).skip(revision).sort("uploadDate", ASCENDING) + try: + grid_file = next(cursor) + return GridOut( + self._collection, file_document=grid_file, session=session) + except StopIteration: + raise NoFile( + "no version %d for filename %r" % (revision, filename)) + + def download_to_stream_by_name(self, filename, destination, revision=-1, + session=None): + """Write the contents of `filename` (with optional `revision`) to + `destination`. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # Get file to write to + file = open('myfile','wb') + fs.download_to_stream_by_name("test_file", file) + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + + Raises :exc:`~ValueError` if `filename` is not a string. + + :Parameters: + - `filename`: The name of the file to read from. + - `destination`: A file-like object that implements :meth:`write`. + - `revision` (optional): Which revision (documents with the same + filename and different uploadDate) of the file to retrieve. + Defaults to -1 (the most recent revision). + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + + :Note: Revision numbers are defined as follows: + + - 0 = the original stored file + - 1 = the first revision + - 2 = the second revision + - etc... + - -2 = the second most recent revision + - -1 = the most recent revision + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + with self.open_download_stream_by_name( + filename, revision, session=session) as gout: + for chunk in gout: + destination.write(chunk) + + def rename(self, file_id, new_filename, session=None): + """Renames the stored file with the specified file_id. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # Get _id of file to rename + file_id = fs.upload_from_stream("test_file", "data I want to store!") + fs.rename(file_id, "new_test_name") + + Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists. + + :Parameters: + - `file_id`: The _id of the file to be renamed. + - `new_filename`: The new name of the file. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + result = self._files.update_one({"_id": file_id}, + {"$set": {"filename": new_filename}}, + session=session) + if not result.matched_count: + raise NoFile("no files could be renamed %r because none " + "matched file_id %i" % (new_filename, file_id)) diff --git a/backend/venv/lib/python3.7/site-packages/gridfs/__pycache__/__init__.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/gridfs/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1d02c4319aae99e29a8e7b15209dc3661ae025d GIT binary patch literal 34102 zcmeHw+jAV(ncv(n0EQq5UPM!}WVI;K1Y~k}lU6b;DyBeE%h&>CUMw$motegT4=~_d zpza=$fQQ+t*j3pVW|ODgR5>oGDwRr7`v>Im52W&xr=)70QngQhNgnc$s(sn~e&4zD z>6rmYNu+E!9C8{x-KWp>JKz2L`h)%ZD>;1r?Rzi%{C_^m<^GmG%%_BtAL1MTH4Z^; zH5cTALOb6ntQPV-FSd)F(rQW0OYNynd9~b`UY+jDtj=_1S7$qw)r#DkYVYgJt5RkIlOvU&S%<3I!9NJc3xP0A)os&7t98gU*&>Ic!)8wYuI%z3zIiTB+RL2)(6FFIcLtqoQV9_qP1TT_1&6W7lhSw%TDQ z>?W$tUenVJqJgBh<@K5s_4w9yhgH^3d*OqvUd%>)ui0vcXWFg1sI8lXQPXdPXtP@N zq-@Y@EG>De-O6t<&$-3?u;{Nf@Ta`eyM~7sr-o%Vc)dHE=HDCrWVm0SEcc_h7Yz?z zxq9vLogd$>E&uVIl^@k^U0=OA{C2I;4*jTxez%hCTC%+r)}nB|6(`a5VqrLY`PTB) zl`Gd*J{(rATs7yoarn;412=~U@A>Uk;3r`XPh(i<+{_g}@z?w~ zfa;|Dl|xz#+ts}ST@i^0o5 zDVX|Iezg>ogXv%8R;PlQU>1MN!7H-(`PJ#*JHdhAAj-@HuLkqMA)L(yUT`=#g0o8S zT5vRY0cZPy6Tz|ITR58wjt4KIj{U*w!Arrnapgcz0qDG;CidE`op&x{nY(e~cN+ka z@dYMeVchM|GsCV{JbZ?QW_+!|jlvKAa%9W+#y`U$$qn*J{+EULaDvhOGXGEVNpXO` z5A%ckW@&ROelsZt1zsQI2f5lbj)fOZmwEZ|9p8d%;W!9LwwadST)n*wz>4NANidSz#lK!Dm?Fu)83M2YQT>2`f->o*XXs| z*tOWvb#I}!#S_21xa37W^fA3T@!6mi`)lp6)(PIuKK#i>*!6Bl{qVH+(UrHo#zxq< z8}~c0cfZwc%jnnKZg^;@v(-<+K(I=GtKIVh{=gQEtKOYBZ1&qkD`EFuE9!NLFi_V< z7=^6g84zmnyPVy%kV94VVQco=K2X)R2b|SQ@SKO`jeA{GekE-B{Whw@lr%Q{?s^yipIoe-xA#%*dN&9kVAXwerPpn5ql+8zR!)BeihfnlWu44^PXv>$tWIBPdg zG{fKfGhH6exnqwGpv3B-n%a=HnrVD^#9m6lcsM06H9TPNso&+sM!$PE9v-yU^@XGM zf&=V}rC})!+f86ey{lI%P8<82qW>-zeFy#fyN$D3QR^O1%GrBzh&geDm$^fB)?5vs&Mi)`NWyN_yWHLO?uOg*5B|Jg1@uK*$zixD9tI>y7d zd7%)!j6cJ&s$M`|9bV<-8LYW1???C*3rU|Z0!uC0ZD>kzGSTzjJqTbVE{sBs*1 zaPQ0ZAeTY3cgBlDXYW1+vk0dHNhWTAgGzB$)ian9_1j*IAsx42XXpPR;3$&Rx?)~k2KLU$lR3Q%8+QNFImNjMt4KNpxtMGpxG?UQjn*2NJZEbN zs$5wtkKoGTlrS^HDcRt|BB;q?NukdY#~!@~gg2bEBykj(7QrajQ6!$jA@^z_e=J|f zA1G7`I~CQbDhjMLAj8OqxO58NWqe~IX@yHE4h8u~#fL@U%mQ)dfWl9~|5qUCABGT5 z6i_##UdPDfYula$LctkW$JW* zu_L5lasf-W*BP1(;93t;+AN?10gmi`7LrmNMXJ;dwi@IK8qrjGXD*J3`!5D24D^E&PGQ8Q&U0V zJ+g~n7za~>Vo-ckcvuFcmG0)En@M2+fjpQxo_jbwD1TNA%Ae;yD-EV`oC>Cs^5@Wf zb&orD3i{48?`$7WW(H*l_%jKF_sz;+2KCQ80@Rjr$-cXl=-=c4#ra$^7t9U{gM71a zEJyMGPjcPD&!AHSbXWWZ402EC=6rP_Q450?k=*&*lmN)E9mEdYS*SN6Vg|NSM&tel z$hg2Ws|A;sKpKwis@F|}b(Lhs%SyMXits1;Rnq<st4g6#A5F;fpzmr0}Qnfa~P&y6&%bOl=DFyZlfogH@C(?xZ~w|Z1GLAVJ+GsqOq zHvD_!d@%mBJ0sJwx5lX3bC(Eek)iWblU8cLFp z_r4N6Gh1p8&I=-q*4=SaHXs0lhcH{#S=O&g5$*g;t|+6YtJg8LHM10TLJF|SIwUAX zHEFdFO79%g_eWH)3`?9bnkM4mRJ+w_B~UNjZEfM$i;{>|q=LA1kiL#!LoAF)^weIDfs_Bqq_YZMaJG52~Y z4<%Az9>=--ONHa$JZJDdfPXuOHTUT}*kF(NF-)ik^wb)VMQB2UPbT!&5ZkRrX&-a| zA7T>_3pwc6P9l^pnlnvFEPxCgo|#wdP74?y3kL46_eUsrR@84m3MX#Y(ylWK(uf4w zBJ@b_Ok0250F<|&$jPDu-p?4V{6TMRgJqytA|wj{V-voQbIsbO*F|6&h2MEDAS^X@ z&;K_(8jFHye&<^bsJdk=yb^8+v&($wG!BZTg$6Va{bwO4JOV?CYlTg)xbC%}1U7TZ zucLx1VHh!Og} zE~!3zw$Y2C{+26Y$q1_6GR5Y2qt|Z-l`h=og%8> zR2O+io^(3|kr9&t1uJ@^G&+GyxT^@o@S$-HF?KVzKp)*+_l)RX*#PGZGmh?{)wl(v zJa}uc5S%2~oZJRsq5$j5845w(qA-Tl$mL*`NK?`k5`F{XCrWmF=&PJn*&8uDMr2i^ zbUOA-sJyLzGHHEssr4}xjnHT{IB>Q;argr-(hT{ZAy(Mu>f z+@~)A_q>8dx>NuLLEU7_8BHe&*{L+*oWf%6 ze%~Fko<6m(-T8V!q>Z_IG5~3iQR9aTL~MQwU$JPdV{v{yPfvj$2gOBckpCBjPl>tn zD*^~$<_gtwV7IV5BarcTmG{^K+Nz>i~w(+Z>@K^gHlDh?YG%CMt)@PQ>$ZEBMHv2u7o)wSZ1VuBK~Ht-$gUmP&9nt z(~&7_DT2+1BKA^^v%@HF59J9G*M#hc85k2AP?6Y4L6`N742yskRF{gM(9zP0s(#w|%)*5WeSo96_ zBd%Y9+1M9j4Ibow1C6q{BIN!53JLiqqC`~KQDBrC(B-^RrLB^GG0D&h!E6HwntWg* zf!j}@dR<)T@ z&<7)$M7{PNh=n^&a2K@s+;AGd?221bJBwN0@xR?(O-)HHX>~$We2MN`i>Y{_9uc|Z zl5M?82Hmw$-xr=Iyh_wLWo}h^R5vN8&1W z6Po00A}x56LSf>I?cOfiRun>Ut@tgy>Ppsuq6G+$a~SlM7b~PK_-nM2))~RvT3B+i zDI84hy5tTp0||qMcufM7hK|aFEHR)i13scuE!UKa*3yNA#VYkY3nx--(uvbh_AOS0 z)Gb_eXoH&HNStYafejQZ@le(4XiXd?wgPH9DOP#n4CWd<0ekl`BGCCx3ogBYE$j?> z)ncOUqnbWj|1t;em#r_M5WG|mgXS#pwPv%laQd9f%))5kPk3xEYa7Y3vDA27JJO8 zmog<3kXcTH1W6f%D6t4?W&J(qE4>A7DW?L(b7114#Ms5O#Ega(>AKm~i@-g|n$_)e zl7&&wyq@c<#Iha#L95g6IK9zNyKu_cQS0pV3j-U&BH2;GiXd*F-U3_F3P0G6K);=O zOx2iEy$1n#V$g?1V_OCkhY@g|=Qpv3yz}QgU>kV268~lDfwchKPVXLoS3#H`)PO&l zP<}zh1q$Y`!^ACC9H6G|us}6K86lU}yIZBa_e_-Wf)@bEp zYH0Ws69mm3gJSfOQtQHxAiu8Ey2Xd3?ooXeUJdEhK`AKSofUcbC#Ytb9__R^YGZ^b z6eaRyRBK~NjN)%x0(TpbB#Z_&ytv2qTntiIThkfUP9mDD^tkh2nK*jNqhy zgbv_(Bcd5{06$p;EOwsF9phD~i6@_p9l_Gn-ItoR>|!giyP@jc-sl5t2vu-#1~;Lw zJ1x>=iA03+LhEAbI99&@{`=%()jTe6Rxq&Cn3=u(3vt0+lF36c2+s=KJTF@g_b%D0 zY?;fd8zY^(k2PQyckNSFa_>OnZ6x=}jk94YkS?9DWgSNDGIYpeJ=m-5Uhl+`_lpy6 ztS9(;{=&mu2i@%TE;;3#HjVUCuamq0Y6O0boTETdEdZSxgbKR0G;~+olSx##mVjD~ zY1Kp8mqL7;`!M;!D9IU3$)<8z^dLe4M@D3x0Hk7c{Q)nwO=MXB>eeP@iBltiT|K%u zv1whIu0V|cVMrc_ZZrU1r%4lL>dqyxYWu%9K zBS2`J63;|gZ{Tkall+*vLFl(u@;FYotZH<$fD$bI_9-Fw!eu%Wnqqi2MCQru1E%k; zHh)E{N24DDO)pM~mH}a9divD@DNU(R17h?Mk}xbQklZ3wpubw~yQTcG!qI}&;j8;- z=n!sRgW`9wxFSY`AXt zt3@8Fj85>dz{43HXt|930T0VOe8>ZbC&VkFU%WE}p>IOk=5gaBzM{6CDU|Y+lQTzV z7Ai+8Z&i*|UdCT<=2FB`4Sel~gzks<#{V3LOblQl1R=w}QgbSp3d)Grn?lUrbTEUn zGGhK16v{Y2Pe%mKAm(p>Z~)h45%YI2n8#TKv3`ew!#LX)yb&A;j^b=Ccp*3@aeyZ! zB9L)_-_;R;2ZK|HJ$wbF<|V>zVK}FV;RpT3-7wj?a5=*s8Lu*OvJwIcI!3w!T`D{+ zB#5pm%eHc@F^%!@!!@=B+rPjMrS+Z|9#{$rQ2Kv&Q0hYQzgdDW;xxhqmk=&kbixG- z5-JF;K!OHm2W1^FIJ>!Tb8b+Arz5O73>pNtFuQpmbM2sn6V3+(vH1KMI<^c&i5PGVxg{ zziRsBM1MKBV|U@0Y9<-kSny)XTMD*Y69|&MnMK*R!GdsK8jf8R%!d1&YR0EV>XWFi zTHM+RYbt0WLkC!?lmI;Lka&SNgGiUw>R@_^WIgY~+i(8}(S8QWNtd%zHCn$@c7m&M z=Gp2ej4H&6NEjcR13niavaT%`naVQqtQV|h!vG0FBnKm%rGC$m4aVn~1t13+;lb%e zrg77wtW?cjjI^*zKb(lVY#;vMB2A5;#5hwsg`l#Iq(miEGnE0JJTh}-=Y&9zxL6?e z(`t|0cuLSj1`Q4mj;ze$A!P_)fM#@r2O?c58@<3Y#$h6yAy`|Tt2z5}xDTP8CdL>f z(^<;sMLe=PM_ofr%Z|h8nu@%PzQyXwVxNnLhtySPXD-et>q7zU?Q@%(AKk;Mi!}a9 zM8<0IelqIrCM5fFG7BWzxmQ%Y-8^j`BhA*9Dkdi$azzfOLSgyqANsJWRAO(6S8T_TeQr zrM=|J{@53 z)q}prc2evmO0$-t;1pY3gFa`X6}Gbth=dVVe9={G1{ChV+OEE{8KZ=|qzm#`Dpz{S z+8)A5x2DwfOxu9@U$7lD$o>S|5b%XIe-tJ!y)|x>F`kN$-tLOen-v{fd6uX8hT@e@esjRR+i^j>L%HDRYDgp_#|&-R=yCeR>HhjVvdj zcZ;656as~~kFK*NRxwg4NKFz|L1-7&fu?xPQiy7?m7;YHlWnEk1@{>C?HDj|NA1E# znz@qB8341Ll0*-8xX%T2s1s%Vr%b_9=m4B%_1<&80o3eziYzbY6rha#X&V!PN>Lh~=b|HEeTt<96z8ulBwP82@o)f*AaBi(& zocP_4R8ZI98!%4iT*7DvYYyIKBynIkT4wEiofSicsF}_$oJ<7ua1HXkQqT2yO9K(1YC@=Aq9j?odQvXtfzU)+m>Q`+!u7 zn&I5ZQhq)UldBGl(c4U91alqeQ!z`Q#MD;wFG5_r^F-81 zIk?e-R-)5|qO{!D!JF7(&uJF&aMt#qS-XpQzn|1hyZ4DP)u=DOA==1ql#unDE;^Pf zvI5W5vsr5Yl~Ryc?(}J?XLVF26|{8_E}@Isz8Fb}ut;-0Z)J&Mw8m?rBHcKNGlE#h z630`_q*2N5WJS2$pR|6gR0ms~E90*olh5t#;+O_iL@kwajeNc9dPjpHnafdO%}zNV z&>_k~Gc#_gTHJ2KU6@eepznIb?`3d4B{692LEe8NY^#f647H z#*-H%&E9L{`kRp_Y~npWWqD(9U)P!&aK(kc{LaJDgJXjdg0~OAlb`_pc#8b-r^h~# zw75!#qj}^=0OYB}!Mq*+dB#>p!`ccGlcpQZOgGD>vo+rOYo?LBhw(SvTz2>#$ERR6T0$Y3-P$XA+oC2sE7kosgfjg#z8)(Oel8h zKW2>>c9oU8zYWHeIl*&uBG_Fa{ky?JYM8d?Pah8vfyh**0W`L?-=}SF5;c9(cB&hs z5d9GQVdtF5`P4AUtkeQ9uT{f`c?2<(NOvkRHgJVG88aXB&+ue)nTJn!_y7mIKSxap zd?ovc{|Aj8XEcUI1pkffcL^u>AKc)QPT|;5ICIdAW9LYQw@|fz!ITPD;oor$0XAZd ziB!zFVelq8=8z45zFJ0$2qIu@rYmvYFk;zT6E*vLoPQxTU+s!5n$W=0$RY0>p%)f5 zAcGFS?o15y-WsFx&r>ldAr>KM&r>ltAsMN!pbhRw>1*}><5Ubw2qgC-WDnNy)EVlu z#=PC4)XOTeZbRTrlOh<$WgtRoAs8)B4^K;kS*6r8!lh2gbHIwn^BhcQV5F+CnEmno z>dXX4lht8-rsNKQQ#g~!1lg>Q8coC0FIfykD`C~Ofq z484P>bEMA!S!B>Y-PI9wq8$JEq@;2&kCQTt3~f9m#U8WI!XUyx(OE|$wJPEVoLS)U zYz3@bB^*FT2dyqcere_*(CH>JuDO|!G7WUvdvNxY-$jS;MA7X3hXftpT&}uvC_~6e zUL;P@Y6fU)<98X~_}}A@#u_L_{h>q|Y|@H`7y~5ahlT*b?78S;yfKdn`AbO1Umi?- zhA4y2k&1u7wEX3uoJiVzB;-%;Fd;wh%zUmA^3M*YkdS{m;hXgK4W?25v`)xBhlKq9 zS|{Y+AIwZh$j@N>`()Fhr zGq9(g5KfK~ytd$|Aa@bwh0#D|V}#x^RbRwS|4Zu(zeIlZtmxr7>YLTF0gTq2!S1HL z6_9lM+rtA;RM~{5bT5j3goTDo_}wS+E+KOddDwNtvCf8)03R?Ro9!C$haCasP-OIy zBpP@}pBSO9Am$TqSaRMIrpN-4huw3~1M6{LLwM%A3X^j(IRWD+_(g(g7FVBV>-@g7 z!rZyuP2I94b1HcP^5V3ZKM8^MD$#?RH-S@p4(cvW7~T&HzX9w(N3PF|;JhxVUVdPP znJIEX7twE7m{^9dR;Xs@=FG@*lcqD%4(45TdSXdi^l2%W-Grc$Sw1=k>@CPWI*&6@ zjSIX$-~PuWlE+IFRm6aZmKXWdTRgnY1GUA`J3LTCluYu`yF5$v+TZ6-kvUaVor+VK zF*B`{PCsCw_jr&RPw{L->;3X1t+&B(ucHB#M*h2qqnnsS9&+nqhTQE{UxWSUhhYd- zH_nlzUX6Uk5wzn|(2h@ub{sFGfp#1e4%+eK6SU)HRQic}TPL8`>Qb?X9n!KOHj@d& zg$$5|li#rloS2DR)fZ4lKVBPc*3zX{Ml)>(p2hd1Tg>dD-d*@*ec5-Z+$AQ+Q{k+U z2|fRsD2k(^1wFbfbRna$-o=w|qSD&zyDaPeoIHVjOSLl3$rJPa8Bj^*kv}C#5NAR7LmU|QhuHl|EB=rO@GIhv zLH@=@5$N33$Zn04CwSXkX;9iM#BU|V076juj&k(t8iuj-mnhwumvOIzX z+zK1a+^WLDV88e}B?@I}9J6kc4YnBanb;MO8V+q3yTWPj1YdY65zf4hAYTS|HY+45hGKh z8$5i>!%ui1&nQ_RlqYnZKN~y*JcK-K@IbVpax@7NZ1ZQAhZM=lnPh9ai5tIRaP0z4 zX7VM3)B>$t%;hWmzJu@g@O`uLD*nAZbFT6(uS9%mW${Jn*Xn^ayp|rX@xI764ETwp z-Co_F;h}_4U!B+2_(!q~s6b(&LZxhy5fZnqPV1DriuI=uuzDcP!rMV+k!T+u#crFb9vkkv)At@o|#uBRYr&hGi94BT1-aER&{YIP;#$n)N8nu5XY$x+lu2f89Ee22QD2v#A<^CN!*|aQaG7v6ip@r1d_o1_`tu}%2>*VDZ>J#z@#cF^ir!dVG7(qrv zIKoYx%pJK7xgf$5{!b!&>HhFW9_WT>f^JF=v=6!^0?i;L$Cg$iF~By3Vrmsr;z&i5h6FbcCC$ z;lSb)rr7}_$;bh4S2!b2xZu!RZeG9_&o8Tsi`Qq|NE)Tw7#b^0d79GtoXQ8T-mvR0 z1IBWmNye6K#(->=;=04w*BMXmPg;x#6*2ak=m4Bo=3g8ZI+=4T!#USstWqJh2{RpD z-!OJ$4)Vjf*pFB--+N=@qsfg<-^K^W$Kh32B*ku$8w*godw0?8jFl`b$_(QU>%4># z<2Svc)E;L+fbOTKjO$*IKUh35b?qLJIfVm<^y%u?MsLm01|1q%DWs*;* zRa8qZXslHRLF6jmm3N7;GPhMkYmsgO*{esSsMjKGBj*79o5j)7v=QK1xyrYlatmFF zA(YUPcyTS_4iM5u#HfeeR#lAAv-@W1)m@~qbE>|nN}@43jTfG(+A=t)|AP11@P@&f L`EJMC@$vZ&IbHoz literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/gridfs/__pycache__/grid_file.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/gridfs/__pycache__/grid_file.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8956e689f320fdee112418fb9982f3bd4b957f08 GIT binary patch literal 26461 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 jcKHBl9gd$;b2=P5F6 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 Xk7Em0Zt^FspR_JXIz9Q$`_ may be passed as + keyword arguments. Any additional keyword arguments will be + set as additional fields on the file document. Valid keyword + arguments include: + + - ``"_id"``: unique ID for this file (default: + :class:`~bson.objectid.ObjectId`) - this ``"_id"`` must + not have already been used for another file + + - ``"filename"``: human name for the file + + - ``"contentType"`` or ``"content_type"``: valid mime-type + for the file + + - ``"chunkSize"`` or ``"chunk_size"``: size of each of the + chunks, in bytes (default: 255 kb) + + - ``"encoding"``: encoding used for this file. In Python 2, + any :class:`unicode` that is written to the file will be + converted to a :class:`str`. In Python 3, any :class:`str` + that is written to the file will be converted to + :class:`bytes`. + + :Parameters: + - `root_collection`: root collection to write to + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` to use for all + commands + - `disable_md5` (optional): When True, an MD5 checksum will not be + computed for the uploaded file. Useful in environments where + MD5 cannot be used for regulatory or other reasons. Defaults to + False. + - `**kwargs` (optional): file level options (see above) + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.0 + `root_collection` must use an acknowledged + :attr:`~pymongo.collection.Collection.write_concern` + """ + if not isinstance(root_collection, Collection): + raise TypeError("root_collection must be an " + "instance of Collection") + + if not root_collection.write_concern.acknowledged: + raise ConfigurationError('root_collection must use ' + 'acknowledged write_concern') + + # Handle alternative naming + if "content_type" in kwargs: + kwargs["contentType"] = kwargs.pop("content_type") + if "chunk_size" in kwargs: + kwargs["chunkSize"] = kwargs.pop("chunk_size") + + coll = _clear_entity_type_registry( + root_collection, read_preference=ReadPreference.PRIMARY) + + if not disable_md5: + kwargs["md5"] = hashlib.md5() + # Defaults + kwargs["_id"] = kwargs.get("_id", ObjectId()) + kwargs["chunkSize"] = kwargs.get("chunkSize", DEFAULT_CHUNK_SIZE) + object.__setattr__(self, "_session", session) + object.__setattr__(self, "_coll", coll) + object.__setattr__(self, "_chunks", coll.chunks) + object.__setattr__(self, "_file", kwargs) + object.__setattr__(self, "_buffer", StringIO()) + object.__setattr__(self, "_position", 0) + object.__setattr__(self, "_chunk_number", 0) + object.__setattr__(self, "_closed", False) + object.__setattr__(self, "_ensured_index", False) + + def __create_index(self, collection, index_key, unique): + doc = collection.find_one(projection={"_id": 1}, session=self._session) + if doc is None: + try: + index_keys = [index_spec['key'] for index_spec in + collection.list_indexes(session=self._session)] + except OperationFailure: + index_keys = [] + if index_key not in index_keys: + collection.create_index( + index_key.items(), unique=unique, session=self._session) + + def __ensure_indexes(self): + if not object.__getattribute__(self, "_ensured_index"): + self.__create_index(self._coll.files, _F_INDEX, False) + self.__create_index(self._coll.chunks, _C_INDEX, True) + object.__setattr__(self, "_ensured_index", True) + + def abort(self): + """Remove all chunks/files that may have been uploaded and close. + """ + self._coll.chunks.delete_many( + {"files_id": self._file['_id']}, session=self._session) + self._coll.files.delete_one( + {"_id": self._file['_id']}, session=self._session) + object.__setattr__(self, "_closed", True) + + @property + def closed(self): + """Is this file closed? + """ + return self._closed + + _id = _grid_in_property("_id", "The ``'_id'`` value for this file.", + read_only=True) + filename = _grid_in_property("filename", "Name of this file.") + name = _grid_in_property("filename", "Alias for `filename`.") + content_type = _grid_in_property("contentType", "Mime-type for this file.") + length = _grid_in_property("length", "Length (in bytes) of this file.", + closed_only=True) + chunk_size = _grid_in_property("chunkSize", "Chunk size for this file.", + read_only=True) + upload_date = _grid_in_property("uploadDate", + "Date that this file was uploaded.", + closed_only=True) + md5 = _grid_in_property("md5", "MD5 of the contents of this file " + "if an md5 sum was created.", + closed_only=True) + + def __getattr__(self, name): + if name in self._file: + return self._file[name] + raise AttributeError("GridIn object has no attribute '%s'" % name) + + def __setattr__(self, name, value): + # For properties of this instance like _buffer, or descriptors set on + # the class like filename, use regular __setattr__ + if name in self.__dict__ or name in self.__class__.__dict__: + object.__setattr__(self, name, value) + else: + # All other attributes are part of the document in db.fs.files. + # Store them to be sent to server on close() or if closed, send + # them now. + self._file[name] = value + if self._closed: + self._coll.files.update_one({"_id": self._file["_id"]}, + {"$set": {name: value}}) + + def __flush_data(self, data): + """Flush `data` to a chunk. + """ + self.__ensure_indexes() + if 'md5' in self._file: + self._file['md5'].update(data) + + if not data: + return + assert(len(data) <= self.chunk_size) + + chunk = {"files_id": self._file["_id"], + "n": self._chunk_number, + "data": Binary(data)} + + try: + self._chunks.insert_one(chunk, session=self._session) + except DuplicateKeyError: + self._raise_file_exists(self._file['_id']) + self._chunk_number += 1 + self._position += len(data) + + def __flush_buffer(self): + """Flush the buffer contents out to a chunk. + """ + self.__flush_data(self._buffer.getvalue()) + self._buffer.close() + self._buffer = StringIO() + + def __flush(self): + """Flush the file to the database. + """ + try: + self.__flush_buffer() + + if "md5" in self._file: + self._file["md5"] = self._file["md5"].hexdigest() + self._file["length"] = self._position + self._file["uploadDate"] = datetime.datetime.utcnow() + + return self._coll.files.insert_one( + self._file, session=self._session) + except DuplicateKeyError: + self._raise_file_exists(self._id) + + def _raise_file_exists(self, file_id): + """Raise a FileExists exception for the given file_id.""" + raise FileExists("file with _id %r already exists" % file_id) + + def close(self): + """Flush the file and close it. + + A closed file cannot be written any more. Calling + :meth:`close` more than once is allowed. + """ + if not self._closed: + self.__flush() + object.__setattr__(self, "_closed", True) + + def read(self, size=-1): + raise io.UnsupportedOperation('read') + + def readable(self): + return False + + def seekable(self): + return False + + def write(self, data): + """Write data to the file. There is no return value. + + `data` can be either a string of bytes or a file-like object + (implementing :meth:`read`). If the file has an + :attr:`encoding` attribute, `data` can also be a + :class:`unicode` (:class:`str` in python 3) instance, which + will be encoded as :attr:`encoding` before being written. + + Due to buffering, the data may not actually be written to the + database until the :meth:`close` method is called. Raises + :class:`ValueError` if this file is already closed. Raises + :class:`TypeError` if `data` is not an instance of + :class:`str` (:class:`bytes` in python 3), a file-like object, + or an instance of :class:`unicode` (:class:`str` in python 3). + Unicode data is only allowed if the file has an :attr:`encoding` + attribute. + + :Parameters: + - `data`: string of bytes or file-like object to be written + to the file + """ + if self._closed: + raise ValueError("cannot write to a closed file") + + try: + # file-like + read = data.read + except AttributeError: + # string + if not isinstance(data, (text_type, bytes)): + raise TypeError("can only write strings or file-like objects") + if isinstance(data, text_type): + try: + data = data.encode(self.encoding) + except AttributeError: + raise TypeError("must specify an encoding for file in " + "order to write %s" % (text_type.__name__,)) + read = StringIO(data).read + + if self._buffer.tell() > 0: + # Make sure to flush only when _buffer is complete + space = self.chunk_size - self._buffer.tell() + if space: + try: + to_write = read(space) + except: + self.abort() + raise + self._buffer.write(to_write) + if len(to_write) < space: + return # EOF or incomplete + self.__flush_buffer() + to_write = read(self.chunk_size) + while to_write and len(to_write) == self.chunk_size: + self.__flush_data(to_write) + to_write = read(self.chunk_size) + self._buffer.write(to_write) + + def writelines(self, sequence): + """Write a sequence of strings to the file. + + Does not add seperators. + """ + for line in sequence: + self.write(line) + + def writeable(self): + return True + + def __enter__(self): + """Support for the context manager protocol. + """ + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Support for the context manager protocol. + + Close the file and allow exceptions to propagate. + """ + self.close() + + # propagate exceptions + return False + + +class GridOut(object): + """Class to read data out of GridFS. + """ + def __init__(self, root_collection, file_id=None, file_document=None, + session=None): + """Read a file from GridFS + + Application developers should generally not need to + instantiate this class directly - instead see the methods + provided by :class:`~gridfs.GridFS`. + + Either `file_id` or `file_document` must be specified, + `file_document` will be given priority if present. Raises + :class:`TypeError` if `root_collection` is not an instance of + :class:`~pymongo.collection.Collection`. + + :Parameters: + - `root_collection`: root collection to read from + - `file_id` (optional): value of ``"_id"`` for the file to read + - `file_document` (optional): file document from + `root_collection.files` + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` to use for all + commands + + .. versionchanged:: 3.8 + For better performance and to better follow the GridFS spec, + :class:`GridOut` now uses a single cursor to read all the chunks in + the file. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.0 + Creating a GridOut does not immediately retrieve the file metadata + from the server. Metadata is fetched when first needed. + """ + if not isinstance(root_collection, Collection): + raise TypeError("root_collection must be an " + "instance of Collection") + + root_collection = _clear_entity_type_registry(root_collection) + + self.__chunks = root_collection.chunks + self.__files = root_collection.files + self.__file_id = file_id + self.__buffer = EMPTY + self.__chunk_iter = None + self.__position = 0 + self._file = file_document + self._session = session + + _id = _grid_out_property("_id", "The ``'_id'`` value for this file.") + filename = _grid_out_property("filename", "Name of this file.") + name = _grid_out_property("filename", "Alias for `filename`.") + content_type = _grid_out_property("contentType", "Mime-type for this file.") + length = _grid_out_property("length", "Length (in bytes) of this file.") + chunk_size = _grid_out_property("chunkSize", "Chunk size for this file.") + upload_date = _grid_out_property("uploadDate", + "Date that this file was first uploaded.") + aliases = _grid_out_property("aliases", "List of aliases for this file.") + metadata = _grid_out_property("metadata", "Metadata attached to this file.") + md5 = _grid_out_property("md5", "MD5 of the contents of this file " + "if an md5 sum was created.") + + def _ensure_file(self): + if not self._file: + self._file = self.__files.find_one({"_id": self.__file_id}, + session=self._session) + if not self._file: + raise NoFile("no file in gridfs collection %r with _id %r" % + (self.__files, self.__file_id)) + + def __getattr__(self, name): + self._ensure_file() + if name in self._file: + return self._file[name] + raise AttributeError("GridOut object has no attribute '%s'" % name) + + def readable(self): + return True + + def readchunk(self): + """Reads a chunk at a time. If the current position is within a + chunk the remainder of the chunk is returned. + """ + received = len(self.__buffer) + chunk_data = EMPTY + chunk_size = int(self.chunk_size) + + if received > 0: + chunk_data = self.__buffer + elif self.__position < int(self.length): + chunk_number = int((received + self.__position) / chunk_size) + if self.__chunk_iter is None: + self.__chunk_iter = _GridOutChunkIterator( + self, self.__chunks, self._session, chunk_number) + + chunk = self.__chunk_iter.next() + chunk_data = chunk["data"][self.__position % chunk_size:] + + if not chunk_data: + raise CorruptGridFile("truncated chunk") + + self.__position += len(chunk_data) + self.__buffer = EMPTY + return chunk_data + + def read(self, size=-1): + """Read at most `size` bytes from the file (less if there + isn't enough data). + + The bytes are returned as an instance of :class:`str` (:class:`bytes` + in python 3). If `size` is negative or omitted all data is read. + + :Parameters: + - `size` (optional): the number of bytes to read + + .. versionchanged:: 3.8 + This method now only checks for extra chunks after reading the + entire file. Previously, this method would check for extra chunks + on every call. + """ + self._ensure_file() + + remainder = int(self.length) - self.__position + if size < 0 or size > remainder: + size = remainder + + if size == 0: + return EMPTY + + received = 0 + data = StringIO() + while received < size: + chunk_data = self.readchunk() + received += len(chunk_data) + data.write(chunk_data) + + # Detect extra chunks after reading the entire file. + if size == remainder and self.__chunk_iter: + try: + self.__chunk_iter.next() + except StopIteration: + pass + + self.__position -= received - size + + # Return 'size' bytes and store the rest. + data.seek(size) + self.__buffer = data.read() + data.seek(0) + return data.read(size) + + def readline(self, size=-1): + """Read one line or up to `size` bytes from the file. + + :Parameters: + - `size` (optional): the maximum number of bytes to read + """ + remainder = int(self.length) - self.__position + if size < 0 or size > remainder: + size = remainder + + if size == 0: + return EMPTY + + received = 0 + data = StringIO() + while received < size: + chunk_data = self.readchunk() + pos = chunk_data.find(NEWLN, 0, size) + if pos != -1: + size = received + pos + 1 + + received += len(chunk_data) + data.write(chunk_data) + if pos != -1: + break + + self.__position -= received - size + + # Return 'size' bytes and store the rest. + data.seek(size) + self.__buffer = data.read() + data.seek(0) + return data.read(size) + + def tell(self): + """Return the current position of this file. + """ + return self.__position + + def seek(self, pos, whence=_SEEK_SET): + """Set the current position of this file. + + :Parameters: + - `pos`: the position (or offset if using relative + positioning) to seek to + - `whence` (optional): where to seek + from. :attr:`os.SEEK_SET` (``0``) for absolute file + positioning, :attr:`os.SEEK_CUR` (``1``) to seek relative + to the current position, :attr:`os.SEEK_END` (``2``) to + seek relative to the file's end. + """ + if whence == _SEEK_SET: + new_pos = pos + elif whence == _SEEK_CUR: + new_pos = self.__position + pos + elif whence == _SEEK_END: + new_pos = int(self.length) + pos + else: + raise IOError(22, "Invalid value for `whence`") + + if new_pos < 0: + raise IOError(22, "Invalid value for `pos` - must be positive") + + # Optimization, continue using the same buffer and chunk iterator. + if new_pos == self.__position: + return + + self.__position = new_pos + self.__buffer = EMPTY + if self.__chunk_iter: + self.__chunk_iter.close() + self.__chunk_iter = None + + def seekable(self): + return True + + def __iter__(self): + """Return an iterator over all of this file's data. + + The iterator will return chunk-sized instances of + :class:`str` (:class:`bytes` in python 3). This can be + useful when serving files using a webserver that handles + such an iterator efficiently. + + .. note:: + This is different from :py:class:`io.IOBase` which iterates over + *lines* in the file. Use :meth:`GridOut.readline` to read line by + line instead of chunk by chunk. + + .. versionchanged:: 3.8 + The iterator now raises :class:`CorruptGridFile` when encountering + any truncated, missing, or extra chunk in a file. The previous + behavior was to only raise :class:`CorruptGridFile` on a missing + chunk. + """ + return GridOutIterator(self, self.__chunks, self._session) + + def close(self): + """Make GridOut more generically file-like.""" + if self.__chunk_iter: + self.__chunk_iter.close() + self.__chunk_iter = None + + def write(self, value): + raise io.UnsupportedOperation('write') + + def __enter__(self): + """Makes it possible to use :class:`GridOut` files + with the context manager protocol. + """ + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Makes it possible to use :class:`GridOut` files + with the context manager protocol. + """ + self.close() + return False + + +class _GridOutChunkIterator(object): + """Iterates over a file's chunks using a single cursor. + + Raises CorruptGridFile when encountering any truncated, missing, or extra + chunk in a file. + """ + def __init__(self, grid_out, chunks, session, next_chunk): + self._id = grid_out._id + self._chunk_size = int(grid_out.chunk_size) + self._length = int(grid_out.length) + self._chunks = chunks + self._session = session + self._next_chunk = next_chunk + self._num_chunks = math.ceil(float(self._length) / self._chunk_size) + self._cursor = None + + def expected_chunk_length(self, chunk_n): + if chunk_n < self._num_chunks - 1: + return self._chunk_size + return self._length - (self._chunk_size * (self._num_chunks - 1)) + + def __iter__(self): + return self + + def _create_cursor(self): + filter = {"files_id": self._id} + if self._next_chunk > 0: + filter["n"] = {"$gte": self._next_chunk} + self._cursor = self._chunks.find(filter, sort=[("n", 1)], + session=self._session) + + def _next_with_retry(self): + """Return the next chunk and retry once on CursorNotFound. + + We retry on CursorNotFound to maintain backwards compatibility in + cases where two calls to read occur more than 10 minutes apart (the + server's default cursor timeout). + """ + if self._cursor is None: + self._create_cursor() + + try: + return self._cursor.next() + except CursorNotFound: + self._cursor.close() + self._create_cursor() + return self._cursor.next() + + def next(self): + try: + chunk = self._next_with_retry() + except StopIteration: + if self._next_chunk >= self._num_chunks: + raise + raise CorruptGridFile("no chunk #%d" % self._next_chunk) + + if chunk["n"] != self._next_chunk: + self.close() + raise CorruptGridFile( + "Missing chunk: expected chunk #%d but found " + "chunk with n=%d" % (self._next_chunk, chunk["n"])) + + if chunk["n"] >= self._num_chunks: + # According to spec, ignore extra chunks if they are empty. + if len(chunk["data"]): + self.close() + raise CorruptGridFile( + "Extra chunk found: expected %d chunks but found " + "chunk with n=%d" % (self._num_chunks, chunk["n"])) + + expected_length = self.expected_chunk_length(chunk["n"]) + if len(chunk["data"]) != expected_length: + self.close() + raise CorruptGridFile( + "truncated chunk #%d: expected chunk length to be %d but " + "found chunk with length %d" % ( + chunk["n"], expected_length, len(chunk["data"]))) + + self._next_chunk += 1 + return chunk + + __next__ = next + + def close(self): + if self._cursor: + self._cursor.close() + self._cursor = None + + +class GridOutIterator(object): + def __init__(self, grid_out, chunks, session): + self.__chunk_iter = _GridOutChunkIterator(grid_out, chunks, session, 0) + + def __iter__(self): + return self + + def next(self): + chunk = self.__chunk_iter.next() + return bytes(chunk["data"]) + + __next__ = next + + +class GridOutCursor(Cursor): + """A cursor / iterator for returning GridOut objects as the result + of an arbitrary query against the GridFS files collection. + """ + def __init__(self, collection, filter=None, skip=0, limit=0, + no_cursor_timeout=False, sort=None, batch_size=0, + session=None): + """Create a new cursor, similar to the normal + :class:`~pymongo.cursor.Cursor`. + + Should not be called directly by application developers - see + the :class:`~gridfs.GridFS` method :meth:`~gridfs.GridFS.find` instead. + + .. versionadded 2.7 + + .. mongodoc:: cursors + """ + collection = _clear_entity_type_registry(collection) + + # Hold on to the base "fs" collection to create GridOut objects later. + self.__root_collection = collection + + super(GridOutCursor, self).__init__( + collection.files, filter, skip=skip, limit=limit, + no_cursor_timeout=no_cursor_timeout, sort=sort, + batch_size=batch_size, session=session) + + def next(self): + """Get next GridOut object from cursor. + """ + # Work around "super is not iterable" issue in Python 3.x + next_file = super(GridOutCursor, self).next() + return GridOut(self.__root_collection, file_document=next_file, + session=self.session) + + __next__ = next + + def add_option(self, *args, **kwargs): + raise NotImplementedError("Method does not exist for GridOutCursor") + + def remove_option(self, *args, **kwargs): + raise NotImplementedError("Method does not exist for GridOutCursor") + + def _clone_base(self, session): + """Creates an empty GridOutCursor for information to be copied into. + """ + return GridOutCursor(self.__root_collection, session=session) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/INSTALLER b/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/METADATA b/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/METADATA new file mode 100644 index 000000000..7efe655f6 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/METADATA @@ -0,0 +1,243 @@ +Metadata-Version: 2.1 +Name: pymongo +Version: 3.8.0 +Summary: Python driver for MongoDB +Home-page: http://github.com/mongodb/mongo-python-driver +Author: Mike Dirolf +Author-email: mongodb-user@googlegroups.com +Maintainer: Bernie Hackett +Maintainer-email: bernie@mongodb.com +License: Apache License, Version 2.0 +Keywords: mongo,mongodb,pymongo,gridfs,bson +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 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 :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Database +Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* +Provides-Extra: gssapi +Provides-Extra: tls +Provides-Extra: srv +Provides-Extra: snappy +Provides-Extra: gssapi +Requires-Dist: pykerberos; extra == 'gssapi' +Provides-Extra: snappy +Requires-Dist: python-snappy; extra == 'snappy' +Provides-Extra: srv +Requires-Dist: dnspython (<2.0.0,>=1.13.0); extra == 'srv' +Provides-Extra: tls + +======= +PyMongo +======= +:Info: See `the mongo site `_ for more information. See `GitHub `_ for the latest source. +:Author: Mike Dirolf +:Maintainer: Bernie Hackett + +About +===== + +The PyMongo distribution contains tools for interacting with MongoDB +database from Python. The ``bson`` package is an implementation of +the `BSON format `_ for Python. The ``pymongo`` +package is a native Python driver for MongoDB. The ``gridfs`` package +is a `gridfs +`_ +implementation on top of ``pymongo``. + +PyMongo supports MongoDB 2.6, 3.0, 3.2, 3.4, 3.6 and 4.0. + +Support / Feedback +================== + +For issues with, questions about, or feedback for PyMongo, please look into +our `support channels `_. Please +do not email any of the PyMongo developers directly with issues or +questions - you're more likely to get an answer on the `mongodb-user +`_ list on Google Groups. + +Bugs / Feature Requests +======================= + +Think you’ve found a bug? Want to see a new feature in PyMongo? Please open a +case in our issue management tool, JIRA: + +- `Create an account and login `_. +- Navigate to `the PYTHON project `_. +- Click **Create Issue** - Please provide as much information as possible about the issue type and how to reproduce it. + +Bug reports in JIRA for all driver projects (i.e. PYTHON, CSHARP, JAVA) and the +Core Server (i.e. SERVER) project are **public**. + +How To Ask For Help +------------------- + +Please include all of the following information when opening an issue: + +- Detailed steps to reproduce the problem, including full traceback, if possible. +- The exact python version used, with patch level:: + + $ python -c "import sys; print(sys.version)" + +- The exact version of PyMongo used, with patch level:: + + $ python -c "import pymongo; print(pymongo.version); print(pymongo.has_c())" + +- The operating system and version (e.g. Windows 7, OSX 10.8, ...) +- Web framework or asynchronous network library used, if any, with version (e.g. + Django 1.7, mod_wsgi 4.3.0, gevent 1.0.1, Tornado 4.0.2, ...) + +Security Vulnerabilities +------------------------ + +If you’ve identified a security vulnerability in a driver or any other +MongoDB project, please report it according to the `instructions here +`_. + +Installation +============ + +PyMongo can be installed with `pip `_:: + + $ python -m pip install pymongo + +Or ``easy_install`` from +`setuptools `_:: + + $ python -m easy_install pymongo + +You can also download the project source and do:: + + $ python setup.py install + +Do **not** install the "bson" package from pypi. PyMongo comes with its own +bson package; doing "easy_install bson" installs a third-party package that +is incompatible with PyMongo. + +Dependencies +============ + +PyMongo supports CPython 2.7, 3.4+, PyPy, and PyPy3.5+. + +Optional dependencies: + +GSSAPI authentication requires `pykerberos +`_ on Unix or `WinKerberos +`_ on Windows. The correct +dependency can be installed automatically along with PyMongo:: + + $ python -m pip install pymongo[gssapi] + +Support for mongodb+srv:// URIs requires `dnspython +`_:: + + $ python -m pip install pymongo[srv] + +TLS / SSL support may require `ipaddress +`_ and `certifi +`_ or `wincertstore +`_ depending on the Python +version in use. The necessary dependencies can be installed along with +PyMongo:: + + $ python -m pip install pymongo[tls] + +Wire protocol compression with snappy requires `python-snappy +`_:: + + $ python -m pip install pymongo[snappy] + +You can install all dependencies automatically with the following +command:: + + $ python -m pip install pymongo[snappy,gssapi,srv,tls] + +Other optional packages: + +- `backports.pbkdf2 `_, + improves authentication performance with SCRAM-SHA-1 and SCRAM-SHA-256. + It especially improves performance on Python versions older than 2.7.8. +- `monotonic `_ adds support for + a monotonic clock, which improves reliability in environments + where clock adjustments are frequent. Not needed in Python 3. + + +Additional dependencies are: + +- (to generate documentation) sphinx_ + +Examples +======== +Here's a basic example (for more see the *examples* section of the docs): + +.. code-block:: python + + >>> import pymongo + >>> client = pymongo.MongoClient("localhost", 27017) + >>> db = client.test + >>> db.name + u'test' + >>> db.my_collection + Collection(Database(MongoClient('localhost', 27017), u'test'), u'my_collection') + >>> db.my_collection.insert_one({"x": 10}).inserted_id + ObjectId('4aba15ebe23f6b53b0000000') + >>> db.my_collection.insert_one({"x": 8}).inserted_id + ObjectId('4aba160ee23f6b543e000000') + >>> db.my_collection.insert_one({"x": 11}).inserted_id + ObjectId('4aba160ee23f6b543e000002') + >>> db.my_collection.find_one() + {u'x': 10, u'_id': ObjectId('4aba15ebe23f6b53b0000000')} + >>> for item in db.my_collection.find(): + ... print(item["x"]) + ... + 10 + 8 + 11 + >>> db.my_collection.create_index("x") + u'x_1' + >>> for item in db.my_collection.find().sort("x", pymongo.ASCENDING): + ... print(item["x"]) + ... + 8 + 10 + 11 + >>> [item["x"] for item in db.my_collection.find().limit(2).skip(1)] + [8, 11] + +Documentation +============= + +You will need sphinx_ installed to generate the +documentation. Documentation can be generated by running **python +setup.py doc**. Generated documentation can be found in the +*doc/build/html/* directory. + +Testing +======= + +The easiest way to run the tests is to run **python setup.py test** in +the root of the distribution. + +To verify that PyMongo works with Gevent's monkey-patching:: + + $ python green_framework_test.py gevent + +Or with Eventlet's:: + + $ python green_framework_test.py eventlet + +.. _sphinx: http://sphinx.pocoo.org/ + + diff --git a/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/RECORD b/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/RECORD new file mode 100644 index 000000000..ffd9f6aba --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/RECORD @@ -0,0 +1,145 @@ +bson/__init__.py,sha256=fA5HyVYXaQiiGTPImc6lU1DZmPwpEOdolj4H8wi6GWo,41831 +bson/__pycache__/__init__.cpython-37.pyc,, +bson/__pycache__/binary.cpython-37.pyc,, +bson/__pycache__/code.cpython-37.pyc,, +bson/__pycache__/codec_options.cpython-37.pyc,, +bson/__pycache__/dbref.cpython-37.pyc,, +bson/__pycache__/decimal128.cpython-37.pyc,, +bson/__pycache__/errors.cpython-37.pyc,, +bson/__pycache__/int64.cpython-37.pyc,, +bson/__pycache__/json_util.cpython-37.pyc,, +bson/__pycache__/max_key.cpython-37.pyc,, +bson/__pycache__/min_key.cpython-37.pyc,, +bson/__pycache__/objectid.cpython-37.pyc,, +bson/__pycache__/py3compat.cpython-37.pyc,, +bson/__pycache__/raw_bson.cpython-37.pyc,, +bson/__pycache__/regex.cpython-37.pyc,, +bson/__pycache__/son.cpython-37.pyc,, +bson/__pycache__/timestamp.cpython-37.pyc,, +bson/__pycache__/tz_util.cpython-37.pyc,, +bson/_cbson.cpython-37m-darwin.so,sha256=h9ZveLbtwlMV0HQh0y19k_9cWn8WFfLhoGlqa3fCNLo,55768 +bson/binary.py,sha256=Og_jHotkpGCrUbOcyP8J3jNP2l6Q51NVj8GZ-3h-bXE,7223 +bson/code.py,sha256=Bj9q2xc3hJ-IuNwzUTSi1r0qshBU1J1pCjVJIJExquk,3360 +bson/codec_options.py,sha256=q-9JB_wMZeRFIl-N3iHE7HHnl1SorntWMROew64Uqws,13752 +bson/dbref.py,sha256=pMBnQj36MsJHr-OeTOnJ0gQBF239Mff5E3ioXp_x2vs,4733 +bson/decimal128.py,sha256=RA9r0OcH_XzxAW0Bdi8oD7axD6yIgRBSq69zBu-iDbI,10425 +bson/errors.py,sha256=AkDIISytky_6NFP-U2ecdXooIr53yt0ZiAT42DmuoI8,1159 +bson/int64.py,sha256=NNAMdrdFUMfrhmTfd9cGo2qLSpnS4xjSyVvDnJKmagc,1056 +bson/json_util.py,sha256=yqRnD6yYW0H47TXmknCwOGMnirkafQ7u5uSPSdVXSAk,31707 +bson/max_key.py,sha256=21OvVcOVm6sb7bd4oRFiapZMgmG1dqnTNOjEm1QaGZQ,1315 +bson/min_key.py,sha256=AIejvYyTgDFTJna81weTarOb5zBhZGWTW8M2fU1GZJQ,1315 +bson/objectid.py,sha256=5lIAMfIEI6GH0PGQaCO43ySRBAIu-TAJtqIAWMvwyT0,9426 +bson/py3compat.py,sha256=nC6q-RwR7iCHN3NFVoiwO3s3Y6GeKe_qQAcIL4Gc9J4,2815 +bson/raw_bson.py,sha256=nQkLPwICqaK6_cKMn1dWbg6G-ZDqAxBbK-FKLCqDwyc,4588 +bson/regex.py,sha256=44nO3645IcX3gRQ9X9ChUDVkQErHSLzX67BLYsyyuII,4291 +bson/son.py,sha256=vv0ervx8YNBTIJpd1vao621OAZ5mss7MhWr031rUajQ,5788 +bson/timestamp.py,sha256=KmPD75UR8zE95sTOQxjvutIJ6A65UOcqfTRFL2MAE-k,3932 +bson/tz_util.py,sha256=Zy_bA8x2YWrTbIx08HQYYJVghKKwChcCkO4VSBwaNVU,1518 +gridfs/__init__.py,sha256=16Mn5rm1dVHCCA3Ur93Az6tMUdyQ9XnkeWCcDemQVe4,36320 +gridfs/__pycache__/__init__.cpython-37.pyc,, +gridfs/__pycache__/errors.cpython-37.pyc,, +gridfs/__pycache__/grid_file.cpython-37.pyc,, +gridfs/errors.py,sha256=Z7E-XkxtrWNfob3cTBSgeRlTvHcG02DCPv4X_EmvBkQ,1056 +gridfs/grid_file.py,sha256=o7jeNNH_OEx-tN3w9uMFSZXKPKNgmMImaVsAWt9LUU4,30383 +pymongo-3.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pymongo-3.8.0.dist-info/METADATA,sha256=fOw5E48QwzpbCzgOvXB6wSjCCm4lIqlAc6lzQU6cXiQ,8020 +pymongo-3.8.0.dist-info/RECORD,, +pymongo-3.8.0.dist-info/WHEEL,sha256=_k7-jlcomYi2KWQV8j4dnQP2hhlJPv6RZQa_A7vSCGc,110 +pymongo-3.8.0.dist-info/top_level.txt,sha256=OinVojDdOfo1Dsp-NRfrZdp6gcJJ4bPRq61vSg5vyAs,20 +pymongo/__init__.py,sha256=81DOJzt4jqEltYecEUMnW_Cxd83L5PiWph1PL4-yCW0,2821 +pymongo/__pycache__/__init__.cpython-37.pyc,, +pymongo/__pycache__/auth.cpython-37.pyc,, +pymongo/__pycache__/bulk.cpython-37.pyc,, +pymongo/__pycache__/change_stream.cpython-37.pyc,, +pymongo/__pycache__/client_options.cpython-37.pyc,, +pymongo/__pycache__/client_session.cpython-37.pyc,, +pymongo/__pycache__/collation.cpython-37.pyc,, +pymongo/__pycache__/collection.cpython-37.pyc,, +pymongo/__pycache__/command_cursor.cpython-37.pyc,, +pymongo/__pycache__/common.cpython-37.pyc,, +pymongo/__pycache__/compression_support.cpython-37.pyc,, +pymongo/__pycache__/cursor.cpython-37.pyc,, +pymongo/__pycache__/cursor_manager.cpython-37.pyc,, +pymongo/__pycache__/database.cpython-37.pyc,, +pymongo/__pycache__/driver_info.cpython-37.pyc,, +pymongo/__pycache__/errors.cpython-37.pyc,, +pymongo/__pycache__/helpers.cpython-37.pyc,, +pymongo/__pycache__/ismaster.cpython-37.pyc,, +pymongo/__pycache__/max_staleness_selectors.cpython-37.pyc,, +pymongo/__pycache__/message.cpython-37.pyc,, +pymongo/__pycache__/mongo_client.cpython-37.pyc,, +pymongo/__pycache__/mongo_replica_set_client.cpython-37.pyc,, +pymongo/__pycache__/monitor.cpython-37.pyc,, +pymongo/__pycache__/monitoring.cpython-37.pyc,, +pymongo/__pycache__/monotonic.cpython-37.pyc,, +pymongo/__pycache__/network.cpython-37.pyc,, +pymongo/__pycache__/operations.cpython-37.pyc,, +pymongo/__pycache__/periodic_executor.cpython-37.pyc,, +pymongo/__pycache__/pool.cpython-37.pyc,, +pymongo/__pycache__/read_concern.cpython-37.pyc,, +pymongo/__pycache__/read_preferences.cpython-37.pyc,, +pymongo/__pycache__/response.cpython-37.pyc,, +pymongo/__pycache__/results.cpython-37.pyc,, +pymongo/__pycache__/saslprep.cpython-37.pyc,, +pymongo/__pycache__/server.cpython-37.pyc,, +pymongo/__pycache__/server_description.cpython-37.pyc,, +pymongo/__pycache__/server_selectors.cpython-37.pyc,, +pymongo/__pycache__/server_type.cpython-37.pyc,, +pymongo/__pycache__/settings.cpython-37.pyc,, +pymongo/__pycache__/son_manipulator.cpython-37.pyc,, +pymongo/__pycache__/ssl_context.cpython-37.pyc,, +pymongo/__pycache__/ssl_match_hostname.cpython-37.pyc,, +pymongo/__pycache__/ssl_support.cpython-37.pyc,, +pymongo/__pycache__/thread_util.cpython-37.pyc,, +pymongo/__pycache__/topology.cpython-37.pyc,, +pymongo/__pycache__/topology_description.cpython-37.pyc,, +pymongo/__pycache__/uri_parser.cpython-37.pyc,, +pymongo/__pycache__/write_concern.cpython-37.pyc,, +pymongo/_cmessage.cpython-37m-darwin.so,sha256=ICPjtSBB9FHIzDa2RwhmCpRjXc5UBAbbGBAlWttP5hc,31272 +pymongo/auth.py,sha256=Us_2nWCfUAMo8YUFHCesVNl36QzdFcp71zOUH6trgcE,20621 +pymongo/bulk.py,sha256=EX0Ywp_iNGI-t8lD0PP3S9I4ees5Vl5XjhqF9Eccj7Y,27237 +pymongo/change_stream.py,sha256=5dh1Fnia-F39woBq-jvwE7U2iJrXmlDhUQDwAwy0qKo,11924 +pymongo/client_options.py,sha256=C8HZx2FNdnKN_5u1lypzjENiJf1lf9N8us2Il7sEd9A,8868 +pymongo/client_session.py,sha256=WkFxjmKporUVdaZT2rCWq5ejqFMeb-8kDo-lNPCIet0,24237 +pymongo/collation.py,sha256=-dQ4Aoclig9lx-nZTo92Jw5NsV8Q6QVWzzpxTb9FKvs,7808 +pymongo/collection.py,sha256=eAJvZ0_wRnnT-ZJWyanTi8lBMmj8EwOkGlULm-pJqE0,144875 +pymongo/command_cursor.py,sha256=k3GAgo4T54UrJjtU3i5PygJWANTAlfPrQvtbGBX19pY,12393 +pymongo/common.py,sha256=saIGQW5XhiwUDoZz0jUaOu-lNJL9PcSDj6WvlAoA-4Y,24440 +pymongo/compression_support.py,sha256=9HkL52o0UJdNRGaiJOT7DVbF_K_E3R2kkhFbGVo_6MI,4147 +pymongo/cursor.py,sha256=CzgUSMHBzuEJQvDWiZhheuCQ1Vxic8LqeZr-UkU-ETw,49943 +pymongo/cursor_manager.py,sha256=lJerbsskemaaB5bUy_Rv0Ola4BJqEEezSo3zOAoAGak,2088 +pymongo/database.py,sha256=cyyIRYIBkyyERkhLf1Oxz2rvUNZgbDnplCWror_h_34,60249 +pymongo/driver_info.py,sha256=fk9u7Ni4Ln-Rx1DkqZmvsJeI49l72o3kiRJvfHpcmlI,1703 +pymongo/errors.py,sha256=tE3rNsYdBrtJuG1S98x9b4Nen0Ko5loevwygSECpEd4,7756 +pymongo/helpers.py,sha256=rlLgk-0L-P1GiBNjk72sjsTSAstDULxcYqQh79NoyXA,9922 +pymongo/ismaster.py,sha256=FOLjQm86XxBPiQ2sCRWHc1i_g-ap721tTBLX5X1WywQ,4527 +pymongo/max_staleness_selectors.py,sha256=rXA_frTXGvAwAWY_pBbu4GvnLsOLlaG2xnIbYdCizgI,4326 +pymongo/message.py,sha256=AvCYQPCHplrGsyohjiJshWz5CmrVJ3BFWy_jxRCNXJQ,54961 +pymongo/mongo_client.py,sha256=U584fea4EAWOUvtp9rgfqRVMaKe55ZTaibaJkWlvk04,85049 +pymongo/mongo_replica_set_client.py,sha256=sjv9GYkF-m_sTa9OeqALj0vnMT1s9C-Vo8yAbL6u_4M,1955 +pymongo/monitor.py,sha256=avigyQxsYwWEfH6LR-qFgCI9QvJ_CB7cgsTHIcBht1U,6706 +pymongo/monitoring.py,sha256=zJ1RATkTNlPIU5GMI2Dz6s3-bTETLzzm8Kq9WJdUvFw,32460 +pymongo/monotonic.py,sha256=NOgK1fiHf-yqoF5vZ2xP0QhMLB0DYRvAc5U8q4utxSE,1100 +pymongo/network.py,sha256=MsM63PCn9mbRIWJGo2_G71kcI98_89aHzLkZGbeRl9Q,11738 +pymongo/operations.py,sha256=acY2LxLYx8ZwgQelduw2inu_8SliR5O17CAcv5pTtS8,13510 +pymongo/periodic_executor.py,sha256=7YuG_Bh9NVkSXSfAYHJtL1csYQXI3kb1jvP17NA0khk,5535 +pymongo/pool.py,sha256=Ljc0jB7N0TOm0MRwNkp269hqVd3IeFLz9ZYrc-dHG5I,43433 +pymongo/read_concern.py,sha256=s-lPeqjpraky4TabOul_stU6ps9UELs1tHGGAPvaoIk,2337 +pymongo/read_preferences.py,sha256=3FAArDFnwp4grk9Emv7lg2LECQ_Dmg8OILVnnK5Wp0U,16494 +pymongo/response.py,sha256=8kwNkEwCAaVtQ2AFaFD8wZFtANniAS6f9bPVG8bizgY,3474 +pymongo/results.py,sha256=FE8M0suvJW50LMMEEE1j6546dg6B_fRWkt2-uQkJq5Y,7818 +pymongo/saslprep.py,sha256=Ybzpu66Rm4KRFIfbqooRocDNjFGJ3Xkpzu1uI0f7pc8,4275 +pymongo/server.py,sha256=SXmorrZJrE7rvoH7dzBBbxpT_CIrTVQGMomZgwC4Nok,5874 +pymongo/server_description.py,sha256=uErJUi1UF9guWb13NI0IEiaUD5m3EkwV3DbAjnwtgkY,6081 +pymongo/server_selectors.py,sha256=KoItAlFYGYujfq_nrNaHjWVW_IDSCpKP7f-2YLl_k5I,5307 +pymongo/server_type.py,sha256=AScVY81CujqY93-0kfnB1LG4OowXqO3Ts-CeXyMPa9g,882 +pymongo/settings.py,sha256=cNo29xuxjv6qy5KpAqB52fuL5X04rqSIErLJfexaVj8,4095 +pymongo/son_manipulator.py,sha256=aINKIHNhAE_f6j1Do8ejNVYD0_u-Pa1e6oerKsMxe7s,6699 +pymongo/ssl_context.py,sha256=_iBrSiqyD7I0MQMIwOUOc1q_CxTODeALfepfZ3353rE,3670 +pymongo/ssl_match_hostname.py,sha256=u5QdUf8wogzS0SzsfsKcOYcAwWXo97e8COGWJbbl7dY,4677 +pymongo/ssl_support.py,sha256=5Yuim-Bp50vZUCKEn4e8qp3YkglnaptG1_L5lTg8Eus,7763 +pymongo/thread_util.py,sha256=YF-_hCRZEH931lCfhBBIZBXQE39WPamHwG7BU_QHlzo,3959 +pymongo/topology.py,sha256=C4Yd1pYMzAYVwnczkVjhPQR6Tze5ZyCUHRQgjlgAC_E,25389 +pymongo/topology_description.py,sha256=z9KHcuCrsIwSvndj49a95750fVqBMF_nrqA6fgH6JkQ,20631 +pymongo/uri_parser.py,sha256=Od1L3n1wb7yIOxV__l5u0Nk4ramkvIjThWuXsw01jDQ,14553 +pymongo/write_concern.py,sha256=PQmoV6RqaTlMgvoKU-KoQ_oowJsH9Tabi8KF-kSCZTg,5000 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/WHEEL b/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/WHEEL new file mode 100644 index 000000000..d7a3f1a74 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.31.1) +Root-Is-Purelib: false +Tag: cp37-cp37m-macosx_10_9_x86_64 + diff --git a/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/top_level.txt b/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/top_level.txt new file mode 100644 index 000000000..7b660e26d --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo-3.8.0.dist-info/top_level.txt @@ -0,0 +1,3 @@ +bson +gridfs +pymongo diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__init__.py b/backend/venv/lib/python3.7/site-packages/pymongo/__init__.py new file mode 100644 index 000000000..7f7dc3896 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/__init__.py @@ -0,0 +1,99 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Python driver for MongoDB.""" + +ASCENDING = 1 +"""Ascending sort order.""" +DESCENDING = -1 +"""Descending sort order.""" + +GEO2D = "2d" +"""Index specifier for a 2-dimensional `geospatial index`_. + +.. _geospatial index: http://docs.mongodb.org/manual/core/2d/ +""" + +GEOHAYSTACK = "geoHaystack" +"""Index specifier for a 2-dimensional `haystack index`_. + +.. versionadded:: 2.1 + +.. _haystack index: http://docs.mongodb.org/manual/core/geohaystack/ +""" + +GEOSPHERE = "2dsphere" +"""Index specifier for a `spherical geospatial index`_. + +.. versionadded:: 2.5 + +.. _spherical geospatial index: http://docs.mongodb.org/manual/core/2dsphere/ +""" + +HASHED = "hashed" +"""Index specifier for a `hashed index`_. + +.. versionadded:: 2.5 + +.. _hashed index: http://docs.mongodb.org/manual/core/index-hashed/ +""" + +TEXT = "text" +"""Index specifier for a `text index`_. + +.. versionadded:: 2.7.1 + +.. _text index: http://docs.mongodb.org/manual/core/index-text/ +""" + +OFF = 0 +"""No database profiling.""" +SLOW_ONLY = 1 +"""Only profile slow operations.""" +ALL = 2 +"""Profile all operations.""" + +version_tuple = (3, 8, 0) + +def get_version_string(): + if isinstance(version_tuple[-1], str): + return '.'.join(map(str, version_tuple[:-1])) + version_tuple[-1] + return '.'.join(map(str, version_tuple)) + +__version__ = version = get_version_string() +"""Current version of PyMongo.""" + +from pymongo.collection import ReturnDocument +from pymongo.common import (MIN_SUPPORTED_WIRE_VERSION, + MAX_SUPPORTED_WIRE_VERSION) +from pymongo.cursor import CursorType +from pymongo.mongo_client import MongoClient +from pymongo.mongo_replica_set_client import MongoReplicaSetClient +from pymongo.operations import (IndexModel, + InsertOne, + DeleteOne, + DeleteMany, + UpdateOne, + UpdateMany, + ReplaceOne) +from pymongo.read_preferences import ReadPreference +from pymongo.write_concern import WriteConcern + +def has_c(): + """Is the C extension installed?""" + try: + from pymongo import _cmessage + return True + except ImportError: + return False diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/__init__.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..51d226b285c25e9e4c9181d2e62af54e2ecd2b7a GIT binary patch literal 1579 zcmZ`(TTkOg6!s z)F;~i(Z02Rr9Z*1ec~_lr9I<>u=~)l{P{cQ%=Mga{Ay;Vpg{ZM#oD`cQBnSu#(mR( zdAS@1q#J7_22i{37Kj2;88k|o&9r)1euYAf&8PDbJ) z@D0MQZsM;DaD zDM4?<9gI-!WVUMhoi2F}fTsZ*c&9B3tO;-Q5mi)q7PNkC^ZI$<5g1x$y#C`Q#?PXs{x{d;YX^td`(KLlXM;=b zeRcly`SW_e9=hR^WJS+=vVY@T4er4@{Yf z41l7}s)owvfz{M_wnxQ~`>ntkGU|((7Ox$*JJ@=2a?LFy z#`zZYsGyP-#`M_sM{)j5NFarYdrT8|K~8Kt(my_wO-?vH2kk(Sz*9QquAt2TEbdQL zBDE*Alc?l#AfqyY8|NuZ{aFDO>lWN4p3cm>9w*@plFx$*@$(c;IEL^QlAos0L~6KP{75f5LIA>-o^ z9}5XbAoNMMW!Y`IlOUwrmK%xq3IzD7gf$845;i1kN_Yf-R_CY75!V_VCm3wlW{YbB^` KN!`r?YX1TB1*skY literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/auth.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/auth.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b9b33f5c33e527b44669ae05efdbc4659baeb1b GIT binary patch literal 13164 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 Khus=x;=cji40Qeg literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/bulk.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/bulk.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..db75aef0dcd77743b2af2bdf4158bc62513dadf6 GIT binary patch literal 19199 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 I`Qlsu7mFt32><{9 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/change_stream.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/change_stream.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c0d4a1eb93a54f78312f8fae961cf9e23cb23a0 GIT binary patch literal 10520 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 MA1ix}na0fj06YWu$N&HU literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/client_options.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/client_options.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f46842830dec68ac0ff8677ec4fecbe58d674f0b GIT binary patch literal 7951 zcma)B&2QYs6(_mePwlSOE7_JUf6)1GBeYqkX_Gjv>)Mgmv0GVkBqdJR1`M^Nq_xQ9 zX2_KzVbwzwpg9$2kv|{?a%g)gQ1sGEQJ|L`dT1{_u$KZwfu^UPb7_C?k=*63q(r;m z@XgHQ%;Woh!`CX6l7!zcub+MQ)2AfqpVUcyDuA1~{C}gdBwey(OL1gZ(G{8CjFWLy zU1eBxvTjb#F`RYsZb2_FoO6n9NiQ*+cP8AjUS_!9RNSgwWw_`}x;4GVaLGC0*7Z8W z6V8-7txvlr^^@E$JEz>!`e}wM&Ww9TKf`d* zuufX1aKC7swq|hGtTWbG+%LT@S+mx;4<+lI{nUO&f7&{~BF$aceP-439A67Ot!oP3 z)?BZ%;b|?WZFd6A>jrJF<2ODBPoz0HoHiKAXnB_1GU8c`&lqdHwqqGBVOw~vZ92XY z^tz6Xx!Shrv@J8RjWz6Kn;nehTAquI08Pz%Zdcg8k9}6`AZT|sd<@s;z0P`jqbE!v zpeckWFgj^8x3T;}+YjuHEeLVq0Tx|~$fWMFZCZG?WsA<75>C73mTd@(8Q5gq7B*(W znZbbJ;zf*g;kib~bcxz2v$JFPw%E1>ZjRjwqMbMF!0>$sZ{7*)4+3D8ABuKh$9uI% zedw3NmB%%Y%fF7MpZNsp)0afmQaXy2d7|v8Jf=R8_p*J-zuZ^F)j;m6ef0WrKhszC zb1RaSfq1gJmmaWG%wDibjBPPLv=pBkdW9xkyR~U{+P)j+Z#VDUdUIiQQ7_Si@AX8> z1}`#r1(5+obGfk4!zhBhF|KrXSnUS)Nc|E8Yjk(QdZKwl4ZB1vE~Al@X}Kg9WJRvY z;tcvNnMU!;;v&hgH0}T(No?grtqnD#@}9zDB%KUHSt~~}3zWVB z*{J&@m;CO9NG_yF+EBFy1`|#WbSzRjy-EW^Og#?!TBax#8B&8r1lrp$llFPEVh>;PllAaH^%6yW+F>sgo$!+F|48FzOj5x_75m$4-R*B;= zS5@ZfxrnQe$+9%OHJR~{r}E>l@Hl9@w$}^vQUbYtC_mEkkD%ur(+RWd{!XVgr$l_3 z1epno^6`R#6i2}SOX6v2NTE}lNqig=LH15WyaF7bgNIU*>-d*s@hrv$JWe$=1le`4&3z+0Ao}p%p;;FPO;{|R4&u6Cug2iR@ z2BebO6mSu_{$J31L_&+ihnTlj6z2jNh9d7H^6?x*xu-D1X2DRF1jrDHGRsgNP>!L3 zRfO>K43{uk_^GrmTN76K!@@_>Z3$*p?8{bVOA#OUi{j%zv8vYOCz*cniF^vPKanx| zDKM0+g80-bLa}742>7!=jT8pXGS@Hk^ZWU|Qoq#CuPbnwN_3yA?bahk!($KCCxhOi zUS=OFh(~GC_&)smE>W>l=uCRA8^f}wP|d6 zen2L;8ZFrp4lOz0IjKh$*A(%bRBr|F|7&!jUVD7eaY!PL33Ub%9L!i_&!mff13ldt;ThA?fENMc%y}>j$ltMD**9 z*Fu!O(HK5-XV(RWwrToW#|yMI+wN#~$6Rx4OIzy&8X}MePw#k-5u`Oj?V19w9KiMC zsck!Y#J7WM+OvK`dw^h%gaUo-Jz#%#RrKulG*4*4cD-%8v8#MTgKe@uBU%N;%}{;h zwL5}b(A)&Sp`J&uqSzZ2NC?E%FrSF5G!zx4V=;wALX1RI9TktBk0qm5v7)i{*c2Ni ze?QE$f)CiR62`KdOu`N!$E>3;;=hbWf@Q%yqD}r~^y+Y$YNo1`6%{U28TsdcFJ8iv z1HGuBLq3Y##(x1sZX=K%DX^c|ouWr+gA&M}s8$BOEP9a}&Ge&$Lbg=c?ILVL-crS4 zkh8LEMP%7IJdwu}NH5UKTLr*5hGA`cd4||V8JfUYk)bl65$8$$j}p%3gJEGAOZgr?e&yOD*=R{mFhVnCO@G<^Dv!+^_6cq2=`? zl)`a!kZvK})4Is%26n4YAdu0B-`<76y6w)OLm{qhIX1-v*I(TI7A7elTmh!$_Iy%e z&3BP%*n$+#e!pirl(jeVdo39N zBRC3(S*XQaIkFS-A$sNht+$N_OG^vJ%00b_?!CoScV>`p$K)EGFgjkx)@$_Kz1s^- zWA)zsrg48IEWKqqJ)6^OeR6(jaj`kS%9BfPt_lkAtgWI*5QU7nT4a)(TaXwG7D@$t zH-_D5%U(CZ0)u3OEE){9eElTlBRoQMCBxxl%zh)LRqk#=@e_Re^hLQ90EWDrS;-DLOvqA@(adJzy+Rv4UME!<*KZ5 zmQ;gzSCK6x?ttzJE*ewW5vaK5@F%w*zKf?@v_$fwfP52||3@^oq|271E4VULilT&5 z?W~ot)DK}|dJZr$Q-K{0OnW%1z%0`%Ptgo0U3A^i{c!Sb~l!lU{)2BCWMD;b>4AzlN^iZ1a0Chs3Oxxk) z(+?-dE795Tv@ya}BMD1_QjGBY`0Plf&c#tUdjMxxQ>DC2ePL<-)`GEmce%N8cWL1^ z`HQE6g_Y*=Tg_!-rMUoaa&KudaVX()<4=#g9T=8aGn+2b0Ks({0YSQqs2a&vY09pmBhz18N*VcSDbj7U*Lg@k{A8zcP1IYt&4 zrR}T>s!s%^qGFmFE-c2JUcoTh9e7mZ7r^kTevsrGQXPf8T{)x1elGHMG?*K_so&AXT&4ehcZWprqK; z?$hzv?bk5f^&D?wN5dJ6ha42f?>IXR5cva8a5$~vdzktSu7u2!qc0k()G?@h>6Uh_ z<)DhW_TG=ze6Q0OaRZHcI=+kD@_QPe#z2dSxYv#(F072K@w*h6v*To@4hN4x>~~Vc z#s@(SI=N~vFXxBOX-Fz1bBjo6h-6Zv!}%Fbq|V8HpCb6o7{S9WjzR8aQd9POP{!E# zqb`C*kZPuxdHZO}>IWU?A5sL4Y4}0pW6*hx=!`r7Zgi4KsdXHa4iXH?fd&<`eBx^J zXxig22NxPInz-#hrQYE3D2qotQOeVQ^?NLWn8hQB5jQdQ*xr-44(H)u z8%`tJIQS)#9{A%i1}z*(Q#f^qH~;g1#?0=eQ5p}yJ_enQL#&1ZWa9yvLv5Q1*CPz2 zoDQvjO_6$bl+-lZF=(8sXjS1}SHjqYR9XBd7vP zyO6s_5)$H2ZZu)|dy1+Hqud!sJqB4<50T@e*b7{%jp&Vl_GXb`_gE~p!L;z>!UtdL!#buEa)UM zC(ZA5yPgP;T`&`naM;n;CiZ{#zm(iz=JlU1BX@|lI5!<4r6R>MjIdHjV3O`-6S2*rt5N_-eGT*Rgif<7M@>h8!Wd>?)Go z>R12W4-<*M;zjUFqjC#}v#BK# zW}=cXH8a`NDw>>=m86_gl@!jYX1bNBWHh!(H?yr=B`5VvGv6AijI;`sf?Q{tqph*Z znACI4@zz9TqIINlM6UDA$=1=z(biODs&%Y#tTkPkZXK^2Z_QL@S|=(eq|Hckwso>{ zQtE}~sn+SrX{nDk&$P}~&bH1~&dK#y^L$IM=vv~_ggI_b{3KycSZ_Q_RpxMg#GJ(S zq+Gvg9yOmA#)?0S0(cfZ`T+HM^UI=hx#b=jm;D6HUq z-L@S4L8radxqC-{w{#P?OC|l0WwW2EX4xad(|v8tw1 z_~@{o^i}Ja*8E1ZQ`Qaufp(?6U9;+1MyG9E{_2uZH!m&d?>xPt ze|71CW4XBa>e3V5y7m+|{TY4apNoEPj)l3IReRs4aoBJUC9cWn>U)k=U^BmWy=q&!>JvCq-E=Km2M8Y3JA01mpxQRo#G_cLf9?=vtA+5@5D(m->nnP- ztygz@P^jUWFxCm8&f~V~)4idJEpjVH zGKE4we>Y0kbkH2od~bKRW4l7e(XMnLdUR^LcD+@#IWhfqA<~c=S9P3DtqxoVlu2t< zeWBLG&4u+vp}JMYE7nW;T1OWOM3YD#1&megfgIbbFSIOoyYGFUcq1y)d>ydGrX0d%gb6w^ z?x1dgSn6$TzT2_)V~@kTXmL1`8pe&JwU7RTV7XijxE3i~O`=q9I~G`Nc3v0D%{wN7idX$3rLef?FiSzp&hMqH7-jbcG{&+W9p?I7R4 zNLV_<5fMYS>TZ2PimI%Y8f+zc|*o53`PFx2FSE;$igQO zzAbRd`aGD#9%deWm$%vil8 zoVi_4NZWf32!sDW?(8*9tir*{(O$qVLr^+HPF3}`^*AyLcqxUPC@gxby04(IU46v4 zk?5PKfP>;Xh7K+syY&{9D=@~wqnuAbCETLg>d7Jkd&2^tg!w=yv@tT3q8g;;P=)9H zeoh~mg9BxVoYV?cpTZ03z~!RmW$)D6&-YJPS_C?-S$1398h;pVfho@zEBDq` zzPNqoyZ4NHD=SMY#^Tc5d#iXdN~Oks8OxdAWlzV!n$*w_pbet+Sahr!ffxKzsNBNg zTt{IgU_2yfIKXHqm}xWf6RncAGG^Ay{Uq^Bt7K8nn6%6u##s@hjK zJ53mG&efKEb)#VzA2{!|-+yG@s2RJDu3vLEKiF#6&3886`}Vi5u3g=&@6O8_n$7v^ zPii}R#=ddYhfsL#gVgT6mp8zRATJGTzsfH20|WC$Eei`?1wXTntk*&Q0T2M z=){dcH2hk?W}Qm$peUAu=49}>OgbTVCC1{3|P6=W99OE6VH=w z7SC7kJQ-#6jhwUT=1n{g-$}_kzkzpB?g-xb4ZM>+NCnSDFl5Tz2T`!UN)kldMz<&w z0Nv#nU|-c=tZ{4?dOdy^*=0vJJCrq{>Au&l)^^&R$4$%JqFkqLMaD-wqd@AeU9`yy z`HtvqB8FudaFh6+qLRL9S)sJ+d#JoY>RqLUIHh928#kh`@FtAdQoKp^xHpD<0y}RX zWpR?lGzxDlI(Zu&#)N$gMKLXG+8c`vF&-b`NZ&jotVFnoj*k8f4(BZtiDXveALULO zQYScPDTQ!PU3X6p^6CEVfSF0jy9Tx7w$vt zevZQ#Ly<3}H3-4ku?)l@pVUNCL51g#xt4HofZoAQHLz2h8wuxas7soC#?)35ZW2th z$5g3ArMh2#kLDarbg{3+V?nQEJ#>Ty`~$KeaPqZvVLX=tmIL#aAYHNCJsWWWC}n{* z@5?Tw{o{7qdgAtMdbJbf?yuZ?5+769E$|1x!2_?%2CQ^(sJx2b6;pN*Q1C{KI-C_; zg|HQviaFoBP;eJOhtT%U8^YiKg+R-X^RxekKAlMviA-9X$Y#Mrl=^C4#l0FI1V8S` zEgTLJE2fkr%@jg2X|XR872mq7WX(MGVFY$V1iKbHa3nu*yT1hM9-JJWF1M#alE3pQ z+!^ia7Svzr0|PoqDTX7Ql?Y(!R7wDg0MzJBI5T8uBLGA(sdQ!{R|Hc-9{cCGuXvKy z9oEq+7$~)a15xM8())1khy66*;IeGP_>0JRXNOE2J9x)C0z)Qh6T=1@a0Plt2V8Z5Fw4UcXt*%*yc}!5H!mx< zHu|7S&6+Mm!}v=sHIE_z0hQOrl7|6UH7^%)hw~l!IqYw<=%Idy|2C~^4~LAF*V0-( zl~2jt7;e)HyTxYYXK~yHUZ;T5Y2b6Fk~6cFyqN=D=dp8ZPui<=(7)j8FSrpe$g*B= z>tAG8(5f5cnqIoRRKDlsSJ!T@tlckv>W$tn8_O$8pRU|nUG+v5mmWO0zqWSou6HD? z8uuP7uYKX=Zr@p2!IPnDUBF;}i$if%tTp(v`6RYp&h-|X>L}vt?Wbj|H3#b_Lc`rV zA@Pb1Ku)*~gF8mb^)`r?erGsA;E<{6v8c%*G)xJgXxP;n^m5Se(eriKgfl!5X{c+C zEXux(J6;An!nM4K2rEHkGNA8YL6Zo)Li+xD+;_M^3DKXr$D?Z;PTbtxMZPwLg7m~` z;z(^Kcd`H!m`>Whk7sIfcUWWlLufy%{Iman76GG)G0T#AUM^-2NB{3|jA5AnDIWY! z_RJ*;=4Wi)i|Hc7R9Vm*DhG+@NGJG4)3B@q$kgXx7H0Bf!c6@v38s<80yEvaE7rVt zV(C)~LjX#KQ96F!rq*i$ov9m#md|3(XkU`RlFwwG~! zaSKIvb$MUHiuy&GCJ6FJjT3vX-KI1DB~c1G|D5eAatKUB*5QVOJF13`!(R%uTR0rf z;N>JpXD!7k9kpOV1#=YVyg6o$<2+&&%n9>|Sp1`?Pnt)iK8E^~c}(i#s85^6r9OfB zjCn%pN6cCCB>I>%-#1U2XW&;jsn}UIe(x^KGXnvbRTlHqu`NBq4O<6u@6!2&cs0c3I<1&kPMC~ zM?V8ckk~-F`?F8r^K`c3Zf!d5W1(+-o2e%BC0Qn-YK-wAwZK=UsZQU=HPZ`pA?QGp zjw;tG;URj5eOd^tnLQ(C4FsQNW(;+`F=$6-utA3~+|fvrvjh~8C6Vc#jDsf9DyC~v z#T3+{JQe8BxpT8Sg*u#);Gd#uJ%oaVJE`YDwFXe|8*)#gTH^bm>XotA(1-mli}zTp zqNvP(Q_;}qL$^SZ{eTVo_WW0I?*f-Xa|)&uto|$%s!6STD!!;Fcw#ajBp5o9{Z-}d zDHpaDqYXk1h%?MCssJb*XR;3gg<^KHmQlQZ2;9ev+O}2O;S?-$p0gE>i`fp$GSVx)6LtH27UVho zJ4D^|Mr0-fbf}cv;j1M_BjG0(QXfBB30gQxK>0z&}XBu8HT8@_WOB)))BxBtxf=b^R|9xb*T&mK;(pobtKh#VEM_lTz=jOvHjA@Ni2XMfljf_x8S4qt{h78QT@!;Q%F zlX$e*Ypmx7Tu&%U|M^$+@bDE)gu{viUyiKlOnisp4Z{F0H#Foa0?^r6OfU7Mdu;cEIbYNOb+Pq`^XNEqHuG&1&OH><=C+U4ln`zHAmPf$lB1a&|N zbwoW-Pq}&Md4S?G#9SI9M!_9zj7e=AttR|Yjx;9a-lTi<0Gb{ko9X@z%y;M}LEev; zk^T8b5w&Hlx&uRiB;+tVrfP3Vb`o<1L$(|-e3n!K1-!B$X&oL`qFU5FA9-7^HtFs~ zz_*RW&(0PygPcD9YH>>O8POe17^m%I*CK&M^g8M(_I?%KOXW^f8o+04 zb`3=!80nH4>JApW?5OH0q6=4vWmIqw9c3^BYHL0@ri~Hf;Fp|`fX0~CJs-!qfqp`^ zLgEXxHe|%d(5^w0(%e)dy_2^uFfd2-YhROlS=`Ujthev52Gr3Ec$q=Ed?SxH*6>D( zZ*>1?e*%a$Vy0<3KTNcLkxsaUor3*`TH+~mzwXRI^7*Kl5zBh)Ao)yMndt3l^4F5z zPkfPR{}6ACzCeZGG`R!&8l8qzomb*&Rc4Np9vtns{O^L>^a; z4!lu0DbJbv9%(39CILwZ0{1*Iw~2^?8J8F^iF;zg6Z?7L_O)j$$8WNJ%%yHB6L zfqPF$1`MDiGWjGUcm-_|LJv9IUk>P@J*#L0?M8%xwi%!nO1UDGQc$!2N|^;d zkGf-MRj_}dLHW{14LywY=`O_B@7@+;@^u;ZKNVr;q6piMkh=T0UbEszJ2wWgAV zWIutC+IZLSaZ^T8{khf!?CC?Hv{ZFXuBZl*w4h3j0ARG-uqbs8Mnq>0_ zxJN=Vec^C6&Vp=JA?^-qG`|AQ*x`*~oRP@>KL}YJl?2p*0{CE75Er%7DRM%AGOxN< zUJ2(2n-4)w&Pa?FkkhOu1|#MSe~5A=wsk)SeH~}`#^pKAJ82tt=;L8L8qw#okr;`1 zf0gC3Z8ec8Rw{d=z8S;>VHi|mSNQ?SD#q&Ho zm}tl`3zR5f8~K)A3`vu=1LV>7_EuLN%H#77*I6F06;?Cp9sd0|#I!4ux1> z;`WI3RfTh(>6U$;_JldJJrlTlGul-z3krEl8!8xv^xEhb1`qI|6}501!0k@^Ip%?V zE^9;1p~@lRMtHg41tt@DDcEmzmbJq7Xx)j9V>`GJF#BQ1GCge1|BkpKPQv_{Fr-Pc zqq#VohP@a=77~aMhf2xlBMu1uAVGtK0ir;vMo1KpDj`vX26LP&KnY@x`;@SO(fTRj zL`Vr?9|FdI?571d7ca(FcgUNu1%}kGV{AnY!1*a;m7 zkB4pbOn{E@kyAiI6}ZjQM(kdQ@AB}MLgg1|(u)z1cLppT+nMaXAzvWW`PEa;1IA#5 zlJbZ?Al4w(?jc^V5yUB!pL>$i!~u!Y?|ly2ynI$vqcAJ2`^!UT#fS)EDE>!2Di#S) zs}BaFmRJcu7QOO2n9T5bix=Q{WX6MzotXMZDDuZM58(;Zs1TYqJuMI-4SY}{;xO5L z*Y{!3rNQr5{RB;Zj3AJ9BX%Q%MHyf80n%yZoRGPTH-bjd@CA6&27Ot58FHsWRDml2 z455$R4SS`Q!5I7zPeX3-cen_wBv+%u3QJ5GjPRry8M3_(c_-^!b90S6YH&lrop)EB zkK%dqLs5j-#{QdwbYsl^zB}$tP`)CK7tX1jl)dX7g?lSZt`-*G-E>B3SwKyDV^%h-R0pr>Z$ zq>of1m>=9|=7^eSu~4q0n@-)dTUdGb+x`0{DIL*G3%)c_pVaP$S-xwPN$~?j4E4&S zSVxOWzLP;zM%h1d!2psNj}Y3K9?WKr7G*A9 zKoItHzL-CgAI+c0=krH!j5EJApUuB*vzHo<@J9}F3x|CF)k_00lSqz8$;TeZti(qh z;m01PeCmNTkP+-%0r}~x{x^y8C1`B-L(QHDZ9f~45gXeKGThs%o0Sa98(wB(-?bb?g(On@fW=d`JMwz?qoeII@rRr? zk(?oeJ`rC|FH3uF5hr2g#%x@rFxsC);q{C8-bLsYPaxKGSiJ#%o4>%iv z7tGN9x%A{L2s<{R5UOO52XHV@xqlzJ5^P%XiKK!g27bx-U*p|sI6mARed7-@7n(nB z;4OQO#hWa27Qe+pD4uJw+3`@ie1->095IgyY~XA@i(GQvt>FlNXpy*uLzr%l&Ol>_ zW-s@rTLA{+-c)=+%biZM>#(FM6Mv?6HX8WGnE&;H2J0F%N@XLW!gMlsn__R?sc!P) z^I8oN9^}d3-)Yp570!%G`okG+TI=j~nw_nEoyjiB7!9|M3kwN-4i{VSA3vEzy8IcK z?PU7UA{#K?wZxa%hcM-_46=ML3t5DO3@^3Y*%dPORdFv>HwSP%heifG^bHK>XYQsU zr>2p>5nnVxQSuvyhE)pF;<1QBedy}@v9!dKbC~I6HA|B3bH@5K2jP#^#Ft|a;f;Fc z3#l|xd}>XriYzR*+C;W7Mrm4`uELaO=sIvA{cF@cZD0*kFsA!5Yj(LDF^aPV|LaW{ z6c>jqY4F`J${Sl9cYuNxQA$7@JAlX})`L~5l|_Z_FVI$I4=<nqQZwcj(!n zV;~@D5OsgbNphTo*1h(+6AWG+KGDnQ?8zK(2#2Yi^zq6jyn*+?`Q$PVrep~Vd+~qp z!vci6{4BDVA&|DYKmUqTCTroBRtP|*y61;XJc^iu`QG*gyl(%T#qYBCeHMSfLNK5Q z!k)JA+jwemXd-7m4V^EWpUh{FyUr)YYtkj0h~Ym^a_k4}@TVv$qhf<^$bV80$z3VP z9mtWJH$;R~CgjHM{=3j(@g0gy4k9VF{u9b9G26xaRLS|Daw_>ra8xFOuRA4&&ru3? zf!&O<;JlTum#8FxvdWR*73BqW#P3^~4(^KW=6~WRAJkQ5V~=`fqa#YZd{S3A9<+%C z1Jq}DS;c|n16vi?zRJm5Vrfb@J8@|i@Fy6os4TYd2@>ewKWkXUdH7AyAa?< z=rP-dxK^`vCvUC1$%je2VG!sL7TnaRn?k;zef2dov+hz+DB Nf0X!9;?CsE{{?BJmBs)7 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/collation.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/collation.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f63955add1efd5e2688424a2e6ccd7fee4720c75 GIT binary patch literal 6501 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/collection.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/collection.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cbc3ef1722475936866cb97f0e5c80745ab4ed68 GIT binary patch literal 117932 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/command_cursor.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/command_cursor.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca4e877974229aa4ecaad4aef8a9ab22aaaa44b5 GIT binary patch literal 9927 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/compression_support.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/compression_support.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1cb97d1cf1e29301d2c519463ebe309a7a4398a2 GIT binary patch literal 3349 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 hj=6_hA$}-r}5&fMERc=)>=KTL}GT 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

    the Swiss Army knife of Python web development.

    %s\n\n\n
    -''' % gyver).encode('latin1')] +""" + % gyver + ).encode("latin1") + ] + return easteregged diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/_reloader.py b/backend/venv/lib/python3.7/site-packages/werkzeug/_reloader.py index c4ca27142..f06a63d5f 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/_reloader.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/_reloader.py @@ -1,12 +1,14 @@ import os -import sys -import time import subprocess +import sys import threading +import time from itertools import chain -from werkzeug._internal import _log -from werkzeug._compat import PY2, iteritems, text_type +from ._compat import iteritems +from ._compat import PY2 +from ._compat import text_type +from ._internal import _log def _iter_module_files(): @@ -19,8 +21,13 @@ def _iter_module_files(): for module in list(sys.modules.values()): if module is None: continue - filename = getattr(module, '__file__', None) + filename = getattr(module, "__file__", None) if filename: + if os.path.isdir(filename) and os.path.exists( + os.path.join(filename, "__init__.py") + ): + filename = os.path.join(filename, "__init__.py") + old = None while not os.path.isfile(filename): old = filename @@ -28,20 +35,23 @@ def _iter_module_files(): if filename == old: break else: - if filename[-4:] in ('.pyc', '.pyo'): + if filename[-4:] in (".pyc", ".pyo"): filename = filename[:-1] yield filename def _find_observable_paths(extra_files=None): """Finds all paths that should be observed.""" - rv = set(os.path.abspath(x) for x in sys.path) + rv = set( + os.path.dirname(os.path.abspath(x)) if os.path.isfile(x) else os.path.abspath(x) + for x in sys.path + ) for filename in extra_files or (): rv.add(os.path.dirname(os.path.abspath(filename))) for module in list(sys.modules.values()): - fn = getattr(module, '__file__', None) + fn = getattr(module, "__file__", None) if fn is None: continue fn = os.path.abspath(fn) @@ -53,15 +63,53 @@ def _find_observable_paths(extra_files=None): def _get_args_for_reloading(): """Returns the executable. This contains a workaround for windows if the executable is incorrectly reported to not have the .exe - extension which can cause bugs on reloading. + extension which can cause bugs on reloading. This also contains + a workaround for linux where the file is executable (possibly with + a program other than python) """ rv = [sys.executable] - py_script = sys.argv[0] - if os.name == 'nt' and not os.path.exists(py_script) and \ - os.path.exists(py_script + '.exe'): - py_script += '.exe' - rv.append(py_script) - rv.extend(sys.argv[1:]) + py_script = os.path.abspath(sys.argv[0]) + args = sys.argv[1:] + # Need to look at main module to determine how it was executed. + __main__ = sys.modules["__main__"] + + if __main__.__package__ is None: + # Executed a file, like "python app.py". + if os.name == "nt": + # Windows entry points have ".exe" extension and should be + # called directly. + if not os.path.exists(py_script) and os.path.exists(py_script + ".exe"): + py_script += ".exe" + + if ( + os.path.splitext(rv[0])[1] == ".exe" + and os.path.splitext(py_script)[1] == ".exe" + ): + rv.pop(0) + + elif os.path.isfile(py_script) and os.access(py_script, os.X_OK): + # The file is marked as executable. Nix adds a wrapper that + # shouldn't be called with the Python executable. + rv.pop(0) + + rv.append(py_script) + else: + # Executed a module, like "python -m werkzeug.serving". + if sys.argv[0] == "-m": + # Flask works around previous behavior by putting + # "-m flask" in sys.argv. + # TODO remove this once Flask no longer misbehaves + args = sys.argv + else: + py_module = __main__.__package__ + name = os.path.splitext(os.path.basename(py_script))[0] + + if name != "__main__": + py_module += "." + name + + rv.extend(("-m", py_module.lstrip("."))) + + rv.extend(args) return rv @@ -81,7 +129,8 @@ def _find_common_roots(paths): for prefix, child in iteritems(node): _walk(child, path + (prefix,)) if not node: - rv.add('/'.join(path)) + rv.add("/".join(path)) + _walk(root, ()) return rv @@ -95,8 +144,7 @@ class ReloaderLoop(object): _sleep = staticmethod(time.sleep) def __init__(self, extra_files=None, interval=1): - self.extra_files = set(os.path.abspath(x) - for x in extra_files or ()) + self.extra_files = set(os.path.abspath(x) for x in extra_files or ()) self.interval = interval def run(self): @@ -107,21 +155,25 @@ class ReloaderLoop(object): but running the reloader thread. """ while 1: - _log('info', ' * Restarting with %s' % self.name) + _log("info", " * Restarting with %s" % self.name) args = _get_args_for_reloading() - new_environ = os.environ.copy() - new_environ['WERKZEUG_RUN_MAIN'] = 'true' # a weird bug on windows. sometimes unicode strings end up in the # environment and subprocess.call does not like this, encode them # to latin1 and continue. - if os.name == 'nt' and PY2: - for key, value in iteritems(new_environ): + if os.name == "nt" and PY2: + new_environ = {} + for key, value in iteritems(os.environ): + if isinstance(key, text_type): + key = key.encode("iso-8859-1") if isinstance(value, text_type): - new_environ[key] = value.encode('iso-8859-1') + value = value.encode("iso-8859-1") + new_environ[key] = value + else: + new_environ = os.environ.copy() - exit_code = subprocess.call(args, env=new_environ, - close_fds=False) + new_environ["WERKZEUG_RUN_MAIN"] = "true" + exit_code = subprocess.call(args, env=new_environ, close_fds=False) if exit_code != 3: return exit_code @@ -131,17 +183,16 @@ class ReloaderLoop(object): def log_reload(self, filename): filename = os.path.abspath(filename) - _log('info', ' * Detected change in %r, reloading' % filename) + _log("info", " * Detected change in %r, reloading" % filename) class StatReloaderLoop(ReloaderLoop): - name = 'stat' + name = "stat" def run(self): mtimes = {} while 1: - for filename in chain(_iter_module_files(), - self.extra_files): + for filename in chain(_iter_module_files(), self.extra_files): try: mtime = os.stat(filename).st_mtime except OSError: @@ -157,11 +208,11 @@ class StatReloaderLoop(ReloaderLoop): class WatchdogReloaderLoop(ReloaderLoop): - def __init__(self, *args, **kwargs): ReloaderLoop.__init__(self, *args, **kwargs) from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler + self.observable_paths = set() def _check_modification(filename): @@ -169,13 +220,10 @@ class WatchdogReloaderLoop(ReloaderLoop): self.trigger_reload(filename) dirname = os.path.dirname(filename) if dirname.startswith(tuple(self.observable_paths)): - if filename.endswith(('.pyc', '.pyo')): - self.trigger_reload(filename[:-1]) - elif filename.endswith('.py'): + if filename.endswith((".pyc", ".pyo", ".py")): self.trigger_reload(filename) class _CustomHandler(FileSystemEventHandler): - def on_created(self, event): _check_modification(event.src_path) @@ -190,9 +238,9 @@ class WatchdogReloaderLoop(ReloaderLoop): _check_modification(event.src_path) reloader_name = Observer.__name__.lower() - if reloader_name.endswith('observer'): + if reloader_name.endswith("observer"): reloader_name = reloader_name[:-8] - reloader_name += ' reloader' + reloader_name += " reloader" self.name = reloader_name @@ -212,51 +260,70 @@ class WatchdogReloaderLoop(ReloaderLoop): observer = self.observer_class() observer.start() - while not self.should_reload: - to_delete = set(watches) - paths = _find_observable_paths(self.extra_files) - for path in paths: - if path not in watches: - try: - watches[path] = observer.schedule( - self.event_handler, path, recursive=True) - except OSError: - # Clear this path from list of watches We don't want - # the same error message showing again in the next - # iteration. - watches[path] = None - to_delete.discard(path) - for path in to_delete: - watch = watches.pop(path, None) - if watch is not None: - observer.unschedule(watch) - self.observable_paths = paths - self._sleep(self.interval) + try: + while not self.should_reload: + to_delete = set(watches) + paths = _find_observable_paths(self.extra_files) + for path in paths: + if path not in watches: + try: + watches[path] = observer.schedule( + self.event_handler, path, recursive=True + ) + except OSError: + # Clear this path from list of watches We don't want + # the same error message showing again in the next + # iteration. + watches[path] = None + to_delete.discard(path) + for path in to_delete: + watch = watches.pop(path, None) + if watch is not None: + observer.unschedule(watch) + self.observable_paths = paths + self._sleep(self.interval) + finally: + observer.stop() + observer.join() sys.exit(3) -reloader_loops = { - 'stat': StatReloaderLoop, - 'watchdog': WatchdogReloaderLoop, -} +reloader_loops = {"stat": StatReloaderLoop, "watchdog": WatchdogReloaderLoop} try: - __import__('watchdog.observers') + __import__("watchdog.observers") except ImportError: - reloader_loops['auto'] = reloader_loops['stat'] + reloader_loops["auto"] = reloader_loops["stat"] else: - reloader_loops['auto'] = reloader_loops['watchdog'] + reloader_loops["auto"] = reloader_loops["watchdog"] -def run_with_reloader(main_func, extra_files=None, interval=1, - reloader_type='auto'): +def ensure_echo_on(): + """Ensure that echo mode is enabled. Some tools such as PDB disable + it which causes usability issues after reload.""" + # tcgetattr will fail if stdin isn't a tty + if not sys.stdin.isatty(): + return + try: + import termios + except ImportError: + return + attributes = termios.tcgetattr(sys.stdin) + if not attributes[3] & termios.ECHO: + attributes[3] |= termios.ECHO + termios.tcsetattr(sys.stdin, termios.TCSANOW, attributes) + + +def run_with_reloader(main_func, extra_files=None, interval=1, reloader_type="auto"): """Run the given function in an independent python interpreter.""" import signal + reloader = reloader_loops[reloader_type](extra_files, interval) signal.signal(signal.SIGTERM, lambda *args: sys.exit(0)) try: - if os.environ.get('WERKZEUG_RUN_MAIN') == 'true': + if os.environ.get("WERKZEUG_RUN_MAIN") == "true": + ensure_echo_on() t = threading.Thread(target=main_func, args=()) t.setDaemon(True) t.start() diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__init__.py b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__init__.py index 3e572ebed..0e741f07f 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__init__.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__init__.py @@ -11,6 +11,6 @@ This file itself is mostly for informational purposes and to tell the Python interpreter that `contrib` is a package. - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. + :copyright: 2007 Pallets + :license: BSD-3-Clause """ diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/__init__.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/__init__.cpython-37.pyc index 9794b7127e60b9ed0e0f940906b15d4d7fa71466..09976462d23f1f1b2079b2a3b624f493d395cd4d 100644 GIT binary patch delta 84 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 diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/cache.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/cache.cpython-37.pyc index dcced546e5dd98236681da46ec41efae8c1e1616..9af74f1a490bf4df9323fffe252c02b66855bc25 100644 GIT binary patch delta 10771 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 Cp!f0s diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/fixers.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/fixers.cpython-37.pyc index 6ce4165e5de7a242656e5709b06d6791ac40b808..b2c782d57f2989cf6fda87ca0fdbc261545ce2bb 100644 GIT binary patch literal 9540 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 literal 10101 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 diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/iterio.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/iterio.cpython-37.pyc index 7d25b8d7db831efdac42ac5e6cf04814117f18ad..4e0b56ac309ddd2ac13980ce048cf351de332b9c 100644 GIT binary patch delta 3178 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 diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/limiter.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/limiter.cpython-37.pyc deleted file mode 100644 index f7012eed5cc06d1070006c5d4bf840fa12e7b92f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1765 zcmaJ?&2Aev5a#ac$5CV-(iBCCwqSc`BcR!pgQT^Epm6*HCxD{>l>!3-3W8cv5}n=U zmgGv2q4r`wh=ZQ`0)3c0wx_;APaSeC!-upqIO39=nUCK$r1v*BTL{`8e?Iu>yE;OD z*^hOr!C()%?t)=xgqX+t#7q5=@7ZyX1Zizl^UyJ3HCDewtj_Bf!KlF+1Jr5K-#mm` z7}|oXx97Z=hOx|y65}utsW4o*#b5uUwcP4+oC?MgzMzU@GouDOUyVf;%T#1j%&4Iu z#>1J=cp=qWjj57F#&E9WIN>SG3Nv%ThKj}nON9^5Xf~Auk9kZBZ39cNOeh|kWQny= zoTiY%NLb=H<*8K5utF4unAku)JToTmMUjDzkh3XZOp;KlY2=dGeN~>Dor}dHWG*#e zJA+(6NE%gPd$G)yN=#>_habir{J6XQ1do?4|LHYc{LS#i>mLVr0;d6R z0G=Bv5*@nSy+p)3)4YfG2Zxoz$;*SI{@~~X4u6BXsN{ni-o`0b~i;Ae5~Q zn3w}ZP^!oiNtrE4BQp)4T{KsomBfd9LW{&;fWO^+wSQAi;I9E`JcFX-tk=WcaM$_5 z)j8xj6eTsB-MR>s$e`O5imfEJ*7y@DZ{`u$b z#@xDH0E^vZ>(rDFLCSk;008h5>I05f20(AwPU$<57AeuX#J!_*H231o&~PLd|7S~A~InJ`4SN51{mbEy@q%G#RrQq zTyMYO(ms6F&2hN{1A|uR%rpMVy9${1-plVZADH!j2~2GT58(r}3NB!k1D z-}}IGtzT{(yzUQ=`a^PZ)IT1+P*C~kytU9%5xTPPG|B)TGRImOz?)MBQ*M;i($NV@ z+<|e~f-CL!5wvH#$~&+wP_ z>Z!lr$ywVf7|1Zgyv%!f^H8f*5uCHz)0Yd3&}Ug3P6d*Cxb7SRLkvqaLd;{n!~?|9 z!easVp{~677`o4>akK>_dfIvr8@RxioA>&f+vSF@W>KP5X2m3uLS=O_j~z*M&Kb!x z`2(ebG0E4lkxE7FSET@ous)S=nhvNDrYRJtk+R8qu~0^I n=x}csJvc}A&e77j$h;QeO{&>S@;lfVa^(U;FZ2TJ`5yiT3*wB= literal 11885 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 diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/profiler.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/profiler.cpython-37.pyc index c97decbe53e52af97e91cd644759673932365c5b..6b33373c7ac09e662e3c88b35230c5fa25c0bdad 100644 GIT binary patch literal 1583 zcma)6PjA~c6elIw)|{ln)*gm+7>FIFXh@}`XwWuThj#1GQ-cA`5Wqm8&?4>7r9_pa zoyBl5VC{A1UAN;t%)WrGJM}Bvw11nc&rPhb8OAoREU zSgj6R9>X^GfH1_cL<7V;=1Y8nIGT7Y;68kJe)TbWhFF*N-XYfG!CQaOVfY1#!}lPG zj^BGgV_f5}=D$T3T)&$0={U(%X|)(8l~yAmxsJ#=HKb58&WKf4e1%|4@(Zf@6>CCH z&V?bOsFbz@Zj4u&=hT8MC{d#s*G8z4>?a3@gqDn42r0>s6U|!-q9iMxgJeGe@A}5l z{FUT0E_3JhYB}d0!!`s6M+3~zz+-seLigXHLBImmfwRN95OS~Hdd~Hjzp$Fqg8qyV zdQuWKobcQddrmEQzy$be4LLiz(8BVwGm=v&4Ma_fskEY!oR~)5Bz*$wN5erD!UPVf z#_kv@dkzZBrAdrsYHC4?i<1(U%%h`zBj|)jZ#uR+?SRkgQtKW@E9k_~*0jv7$bhTGUU#A11q3RM&?5CG^> zO*OGx7owyRei+LsTSA{pYRp>6HRw82oA(?bXqE%g)$}vi=1U+JO)$gOb3nQpUSRW? z^;z&1|AH5IfwF)hfV6Wwe-mw_CAzNQ*46ZXE3N_cKM_WTM394spG&Crh zfAPc;$*HkKm8&v=vVm`??y;EhGKo8N2>nuua%}3r-Rh5@E*k`z;abU4Yf2j;N6KV$duteV~3 zx1$H+iIxYW!-o&kle7}m9S0j!_Z@zpin0=S)6?YuNLP~}T}}cp(@uh@`8xou(&aUvGg_9WZKX-EGs;q@yJ}@TflovJ2ZdO9*e{`A3^h zHHwbou8XCl1<$g&pJjz&Q|a#8S@wEL^w`^Ghmadty5^dRv*BtG~T+xWED literal 5459 zcmb_g&vV3hqP zWm$P@$%?8!DYR$tT$MFE*Z6r(F35R#_Nm=IBb{}t@sfYlv8AXOrKkp%q#S{|(5x8Le3^bk+ zvB~P&6kR_IS#^D=Cm@qEy36HeSV_61yeFoXy?7#gN%T(oPUO5$s{oz%ZH9F197BST z?pwH%^C-r4Qcpn+Pwda^u|2jtfC})Owb5$;DO7%via44;A^Jl+ca+!)c2(4Hvg*d( zK&@yU>#Urkg;qCFVNaJJ{wqrpt9_d+4|K5Wr)qiE*UP;)gxx30eZAb-@w|7Ex1(S0 z%D1}SVE4_-Y44q_9UWfjy?yoS^2YKY7+eY>xK$Wl8uquZ_*a6<%M*iKp770b&NrCG z5JYpZmsLD3h=SDf-k{ymaxB}mEB5Hz6vE9(r!L__R{^(+`vcs`&rv+SFt&EAo7N|D z`_9-o1Wre`ZGBpK0Fnk)_FBW?&1OZmq((uPF)DMVpZZzJz``(?qKcP#2E}FE3C(2L zb$fJndQmKBtx=*qM1Eg+URLuw!;PrdJ@3<@ALeh$o+sn3=V=NpEO}}+f?xBLG}CLi z8Hq~8bt*+oZKr&U8m>{NU!XYUV^s;i0_VFXHo(h>`)k=k?zL0APRjd`?USxi*wvENN|#=WsemP|BGv0xGgJTL=XS#J&dm}yB2h}4_RNbK4y zP$d4Yl7{L1l*JoDiS^zRy9}{_7$|+Rfd@gl4Vn-Eg!&>G_WQox6QC402X;l$SkR`i znN5VRBvro5SG$TpN9*Jrh`#b8T6x?91%dy)&Yt0)P2hmo+a-(=&>Bh*8jWP(&qX{~ z4sX*0M-vO0O|c7531cahY_-JY=2aeySaO}3u=oK79SXkBO-K>(6qNb$TLWJs5<=T8 z-o|%y4@S*ZpGOfhz<^=Km^ud$LL%>uoU`#Om%1?rEB z1dt?pPF%#~E$eefo_*##b9=>CY9HE1)_!s9 z9y;R!MIc!`EF3sT_I}B{8y7EF`{e`ZOj-}C`s=i~Q$i1VE*;pi{IYdmA0n|ovJWe= zin2;+{gE^HtDG5^W&NCGrMcoUy4945kLzl4v>-{EpeU{ zARIy1gj+i+L60hujRF~jpAM6(ybYoRX=N4lxQn>oi;;-TLbn_eyiP3lvN`kxQBE39 z!g2Ov91gnM@KBi5beBKTLTE)CqTl^qC6jx3Uh~~BMlE^2`2ta0l0p~dV#k|Y!oxw~ z5F0%p%&h~;FBrAd8B<>7=*6*hSU93XoypAL&|#dYhN~}Na8^=}5fCK+@Do2xahyPF z!)})W5soDq{T8WTSw*{njwAg`Jh3-S?Z2dl5&C<0l~Be&+pXbm^v3_SWi$HUG;nn8 z^`uGvmwzSu_>p`B%;xT|{NbaO^$qXt3Jwl8+tqv5H*R}()>c3K)?Uu+X6EWRPPa&c zb?tB08a8|B=-#Ct_!`w}G;>S5u4|+FKd|siwHjrE3!11^<9Khi%YcRqBy)G-Akvgh zW=@=B?!ZsCv%;22vmynJK~~mk5c*x!u2HZcJ4_Qz4xwKmK{e(p)B00tr3{kj3YkOR z`DbWp)QoD$uJlDJE}_Wm0ZjwT&Rq6{vI>!aDcPPSzVj$mp=iRLFfExtqsVrnS<;Ek zuuLK`N==RFe~hPul3AqJ&b+%&Dj~@x>!~|bI^f5eJ&$tEsTbzZTDQ-`zUs~kSkp*t zxNDktfER;QSQG2&GgQn|ah3|sc;BZQ=eZ{Z6E-o(+ql`JE9YIuvTHR>tyz`5bvsVd zZw@faH{_IGIjl4NBh*=5mt6r*B&~>>)R8QrkT?W@vJoVEAmg#ybDyK_D(JHUwknSP zabKlF9SL7V>Jc9@JAU^er!XcJo19^qNf&m2LnP;}tSw5Bd7&KA6Qz-siATsHIZGkc z(YF-=-67ac(!~Gy$P9Ru#JkxH(P9rJJr1ND*>9Jjzd=O)P%VzHI zcPBpmx68Syv`c(i%9)G4Fk~h3*@e%VtOj0*0gt9NHC}$@U30{JpL~>$pmbiRKL>yF V_ME){;;7)aR+v9Ge-}Z~{x29n%^Cmz diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/securecookie.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/securecookie.cpython-37.pyc index 494bca9ca7dd9357666879ecadada5ef64b71d09..432da582a21c06177baddc1e698ef0c4235fc63e 100644 GIT binary patch delta 2583 zcmZuyO>Er873PrKCHGe=?T>BAKa7(|-8foFPW)3hapKsP;RF!U*hUJX>6Ti~YH4xF z^_yAQ5;oX_bIl>(p+Ex!Hj17K^an!QOMwQH@o#Vc`u6mVN~Ngbv+(NtdySVg?eD}qeR4>=jzfHb;A^htGhYvx%has9 zx|$8wz}X1RVb0An(lJA8XuFo8b75gva0^K1eJd;uOKwTgHqvExT2@Q}nTlIcbP?&Q zTUB%^oEg^Knxf0$>~PMVQ*cO;tetbb$an4sVd!jf2lz1FLA4-Vq#TJ$D=S-p9E>+Ry?D4X%6zgScyA1RxfjRV0iWrhaYZ4$-gd}$ zc$oaK^5c9HFAvS+di714U+{ux6r4?dP(3qSL-`noAn(`=YWts7e`ZYIL{4gt*hHJK zQQ2pYb*4>pU)wIgX{jTnKh)pHd7^cViGgTNw7WT(-?m|0T6b;V@J&DWA%5j+7|Hww zkO6zSvFtBj-22Jx0Y?Y*y>Td=rCZAQtIGC{z}R@Jz^01$pz9&66rfKIiwT*IG1})qbjw1XBhnPdqSk16mk>y!lFEW$q zX0lknsYiNpuij>}$;b6)kDWxd!<#&ZvR@{j*Z=yptIy-=Z<0SX)`}KVS8<5TiPb#$ zjZ41%$dKkE4Dtlq%R~J!dt^P<5bej>`)tB6%;w%F-%kGBa+0m)!%AvmurH#+=5dom zZ*_9YSIKJY^b1)wq8#X6X-?J9@9D5If@T$28SiDXd3}Gcb)4OK0fq1)LR#27|FX|} zvCq}MX)cUCU!;ZW4|;q=DFc+zZ5>wWlQn`vhgkU0oT&v{Tu4|!rk#hANoV2Ib6;mc z;c0U4f1s*3n%rBcTc&=T*?MdLqlGW@*S>}FCk#OuzX7ih`Avj>k?>WxNc;ta^>!;Q z1|o>C+@l@`3L0EXetz=V>185cBe+gbC7^gh0U<5li2}Mzc4Ku3{N12`Z(}dagcJ%` zC&&|=Cm`p7iO?=752ty&FlI6(t>%_CJ;Bev+{LyvxCy67_X@#Hgw&*-3)EOtSlm)C z40t4!^5Gbj#@O7c5d?euKl>k_`lEh_4rm*xmGwB08e3e#yClqImRP{=jmiJ&2vW~1 zI28U8B}_Nl<=poA4UDd!iqMW0S(R$~|KH^G z#l&g;K@}3U5ui4Z+9QZZ z9ON$0?NbbiCq&kBRqAI!C#NV^!*_}56MT6MMXC051Q&L32udfrY1+0mHBD15&e>Mk zD$kVb#bf0QIx9Cc_SF~Y)Ak8y;FMLAGWj^NsYTfo2Ae>CC>Ri^)HaBsR7@@O-*Du~ zEKgMlP*ZF4aE?w=WkGj1?3XL5zuB)0V}Bg-%Py)2ih;>&^_H>Gm34(gJ$-cy Ly`eMPteO7>kvVN4 delta 2121 zcmZuy+iw(A7@spUyE{8Odui$Rf|U*zp(9XRtXu_Jpa@Da)~SZ#3rJbZ5evSR|3^m2>Mh% z6J+d+kTZTZ$l02Zvwl9%Z9OR11)=ABBPiO%pk&`H34`XBgNj`da^CL=dhK2z>;8_Q z&+Zd)0T!v+RU*A3xrSSOB)LV}|4^|9Krgvv(95(J^g&mim8^=h(^xN)Zwj5!hGiZZ zpO`ckR?K*bn%5~?zE4|=<{Wi`Lnf!xoSvV%^#0XZ^H#*nAY#;XY3z7DAJa|vPWoPh zhMb->&&^)wPPlUU{7h|jW}6AmAyQJ!BB>J6yU?kZVlSX5@Fi!$lGD5wQwA;IX+Ea+ z;(EN&q+l<#m|wr$ieg%C`YoPTP;Z4VEy>B^l%9{Hdg#R79h$sX`YPKCo3Y;H-_pBU zD_ELvCcN?FQu$y{5$ql`E(2&XE3Q8(f2(xHL5rnT(w5q!X}DxfCQ@5=rR6*`W4SFq zBo6@FQe9~)fT}A&B;^{zZLADKqNG~A?uA}lucw)Xm6-BW=d{6STwkUu47amXxGVK< zrK;=14jTZIu(RIRP!vbrfl?LVF*Gg+NGgg(G?FFBUwxP5=5R7zwMZ$kt9yqAH|M>a zybEk!CXcJXZtEQZ?~~;7{s}z;(s^k7c=D`&*T|$RuPU*+nrV|YvYK6!0CQ{71JWjN zRVB#}JdrIeY4!~yKMovDUJUFR?Zz0EV0h{@BQo+eE-B8P(}@A69wh6h28YS~J{-6o z2j&-!PP()axs>e@6aC0>A?Y*s8nhXEQOGb$mdswqDF+d@Y?a$c{}1a=hkla#Fp{Zp zwG|?xW*CbY%O{@=k4@)r?jiJMyBnri8sf%jw$+3rP$nXer(y@X!@NbxV`hP3W!Yfz zVtC}jb|=Vl;Hu+$_vtS%lw;5(U57NOaA}oPNIzUXOZvdlkNWzRSAUbwz6GP66993c z8as;On*jfy@iaSv`UJp5t3TB}?u9&d!Uknobeu_+&ApxDD4sw#i=ZK#Mlb+UV?Olo zg54;HiycOLeh!MMYb@a4DFmbdL+XowGX*58AfldTAqZZ$C`f7-7938GPS%$kzI2%l z;e=BNNS{>2l4TcA$3WSIhVRiZ7K69utVtl!@EvrMLwV+Y1N$appbfb|$}jIa!b zwUcU$@@9mG601*x%`S7wIk2!!?=?N}vm3a=M+i2;#|Sq8(hNL{keqFg!<{=6H><*} zCLp`rnkea{gjJE`M)x1Q3TxD?JQIDb#;&7YyLi#6vJ&o5MyMe4BsWHPcTRwqW}9AP z+3!{k6E%ge1w#U+T9ZXh%HkDoiPYk&~)N1Es^ndanlEHeupzr&&YVYchdupF4S@8_$-f(p-o34 z3W385V;kw@;+3|0HvRBAx8VO~g^7Z|>S6FVzflW#n=Mf}(tYN%w}Z%S`Sc8uf$s!R Up~yAJD*WUMl)jLNp;R*e0k!7g$N&HU diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/sessions.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/sessions.cpython-37.pyc index 23aefeffd1d31ea8e703a48ced44ce87f75d0c69..823ce7e27deccefcf3cc7c86b42f770957192187 100644 GIT binary patch delta 4089 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 E55?;J>;M1& diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/testtools.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/testtools.cpython-37.pyc deleted file mode 100644 index 8cd1b9da9ceb4ccb249d29140e2ac1fee623ba1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2675 zcmZ`*OK;mo5ayDUM9FgMxOq1<_Rs(!5KDHDpovoyaU3H_ktl_&!U=(trnxJNHXq9F z$`8TrAO2u=P79Wy+G5oX3DF+DS}Jd5DmitVK2mGsz-%Spwn5QA5^!%8RQgb?G2 z!OE=i-e478J+ZtR%pF#Rd6k!V?Zoz`SZ&>Kr-EOIVbmx*`&{e}cz?SQW~maA zRF=h3FaPw(r>582-AK|TWBr)ZDCxyK;i;08A1IzO&gi}fdOa>A?PP*V+!1vmc#pS4 z>2{-#ve|~|8kfB+m3&hRM%|XF7`_|Hq5It^?9zZSN%sN~WqnD*Anby~K%x455R7+? zWlY=|NXdnY;0~Ilr^mr=(3z^k9+yLXmcp!eD5CAITB0{Ym)>4jyhFDRsp@jNF?1Mh z^B}oJCFgYchxXH#YwOy^Bomx6u7W5AX=ue#9EBY0TB48E9~TFnKU-OCt*@SO;BNrm zaLxR)!YaQfGH{?BfwCT5zk^Kv)&t?4g9iCh-WjKm0(_m zc^T&Af{$JW<`tM%m;?N)<})jW+n2+TOPL8dh_$npX%ZboDGg&lOZ#U`foyAshrlh> z4HN~x>_rT)4rrM5fp;|P(7xoIejz6q$<;FYX{5%CLrg-TzkV#}G4fWR$u;O0IWl&P z-HN!PApUTC$v7eJ$iXdUJvQEabxf3bM9|8g&13V(WaZtem^(5*Sg_arjjS7Q=Qa#g zVqon%!pdN+Gr0PMD{wmHN&sD=4((-9Mq4p&y)}67I_uM!{Cd78Llf?`&M>7%4tsURLC-0_T@3A|f z-`l&rpgQ-qcSO9{xqJV9v)$}Pz4@Wu^Zlf|7%WB$%~7N_M{&^{=C~$uLFc#zgn0$7 zo`WW5p)<@HG2vGuHM33}GD8Lz{!3N#!AAsg;G8Cqg29+T*9-apm97JoEKLWXO$_P$ zm=NP|21r!Kgnn!iW5Y2 zDvptIK(V zL>X>PK-2uFio=G!%RP}BjI8FCK(0UR>8_&-=sfiW(au*SHwU^2#K76Tj600b;%kMIBl zC?H@10*?v=^lGU^_Q)2un!%dm-H#Oa_V8Pb}Ca`1vA7Y?5<#U89LW29I2Nuh$ei9n;t-u=xQXk zGxXr>S>8kCTCS5jzMlpO_x-%)`$d?-xbFLJ`$0ThsrWw2Lf_YX66k6TSfSRb7J8)N zEW;L5SF7TbY}1)`Os5X5DsU}?Hhz$W51~n9$H%n>9B<|I$%Hg`@I6)vh2J5NuQo@q zuD@d%D{%zrZc`Tl9>|CbI#~-^t}U*?CE^S0u4DHlc4wJ@DdGtI|PJ8^uaqE-IdOkiPCLy>jQ9 W0;F#dWm$*LhA^o^_Nv=63;zO8DAExC diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/wrappers.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/__pycache__/wrappers.cpython-37.pyc index 2d72152aecb1eeec5847b72974645fb4fd81d6f6..8afdc03691c11d2ac675ddb3395ba322ce970a7a 100644 GIT binary patch literal 12424 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 delta 3792 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 diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/atom.py b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/atom.py index 51d1a4e64..d079d2bf2 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/atom.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/atom.py @@ -18,28 +18,39 @@ updated=post.last_update, published=post.pub_date) return feed.get_response() - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. + :copyright: 2007 Pallets + :license: BSD-3-Clause """ +import warnings from datetime import datetime -from werkzeug.utils import escape -from werkzeug.wrappers import BaseResponse -from werkzeug._compat import implements_to_string, string_types +from .._compat import implements_to_string +from .._compat import string_types +from ..utils import escape +from ..wrappers import BaseResponse +warnings.warn( + "'werkzeug.contrib.atom' is deprecated as of version 0.15 and will" + " be removed in version 1.0.", + DeprecationWarning, + stacklevel=2, +) -XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml' +XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml" def _make_text_block(name, content, content_type=None): """Helper function for the builder that creates an XML text block.""" - if content_type == 'xhtml': - return u'<%s type="xhtml">
    %s
    \n' % \ - (name, XHTML_NAMESPACE, content, name) + if content_type == "xhtml": + return u'<%s type="xhtml">
    %s
    \n' % ( + name, + XHTML_NAMESPACE, + content, + name, + ) if not content_type: - return u'<%s>%s\n' % (name, escape(content), name) - return u'<%s type="%s">%s\n' % (name, content_type, - escape(content), name) + return u"<%s>%s\n" % (name, escape(content), name) + return u'<%s type="%s">%s\n' % (name, content_type, escape(content), name) def format_iso8601(obj): @@ -47,7 +58,7 @@ def format_iso8601(obj): iso8601 = obj.isoformat() if obj.tzinfo: return iso8601 - return iso8601 + 'Z' + return iso8601 + "Z" @implements_to_string @@ -100,42 +111,43 @@ class AtomFeed(object): Everywhere where a list is demanded, any iterable can be used. """ - default_generator = ('Werkzeug', None, None) + default_generator = ("Werkzeug", None, None) def __init__(self, title=None, entries=None, **kwargs): self.title = title - self.title_type = kwargs.get('title_type', 'text') - self.url = kwargs.get('url') - self.feed_url = kwargs.get('feed_url', self.url) - self.id = kwargs.get('id', self.feed_url) - self.updated = kwargs.get('updated') - self.author = kwargs.get('author', ()) - self.icon = kwargs.get('icon') - self.logo = kwargs.get('logo') - self.rights = kwargs.get('rights') - self.rights_type = kwargs.get('rights_type') - self.subtitle = kwargs.get('subtitle') - self.subtitle_type = kwargs.get('subtitle_type', 'text') - self.generator = kwargs.get('generator') + self.title_type = kwargs.get("title_type", "text") + self.url = kwargs.get("url") + self.feed_url = kwargs.get("feed_url", self.url) + self.id = kwargs.get("id", self.feed_url) + self.updated = kwargs.get("updated") + self.author = kwargs.get("author", ()) + self.icon = kwargs.get("icon") + self.logo = kwargs.get("logo") + self.rights = kwargs.get("rights") + self.rights_type = kwargs.get("rights_type") + self.subtitle = kwargs.get("subtitle") + self.subtitle_type = kwargs.get("subtitle_type", "text") + self.generator = kwargs.get("generator") if self.generator is None: self.generator = self.default_generator - self.links = kwargs.get('links', []) - self.entries = entries and list(entries) or [] + self.links = kwargs.get("links", []) + self.entries = list(entries) if entries else [] - if not hasattr(self.author, '__iter__') \ - or isinstance(self.author, string_types + (dict,)): + if not hasattr(self.author, "__iter__") or isinstance( + self.author, string_types + (dict,) + ): self.author = [self.author] for i, author in enumerate(self.author): if not isinstance(author, dict): - self.author[i] = {'name': author} + self.author[i] = {"name": author} if not self.title: - raise ValueError('title is required') + raise ValueError("title is required") if not self.id: - raise ValueError('id is required') + raise ValueError("id is required") for author in self.author: - if 'name' not in author: - raise TypeError('author must contain at least a name') + if "name" not in author: + raise TypeError("author must contain at least a name") def add(self, *args, **kwargs): """Add a new entry to the feed. This function can either be called @@ -145,14 +157,14 @@ class AtomFeed(object): if len(args) == 1 and not kwargs and isinstance(args[0], FeedEntry): self.entries.append(args[0]) else: - kwargs['feed_url'] = self.feed_url + kwargs["feed_url"] = self.feed_url self.entries.append(FeedEntry(*args, **kwargs)) def __repr__(self): - return '<%s %r (%d entries)>' % ( + return "<%s %r (%d entries)>" % ( self.__class__.__name__, self.title, - len(self.entries) + len(self.entries), ) def generate(self): @@ -160,64 +172,62 @@ class AtomFeed(object): # atom demands either an author element in every entry or a global one if not self.author: if any(not e.author for e in self.entries): - self.author = ({'name': 'Unknown author'},) + self.author = ({"name": "Unknown author"},) if not self.updated: dates = sorted([entry.updated for entry in self.entries]) - self.updated = dates and dates[-1] or datetime.utcnow() + self.updated = dates[-1] if dates else datetime.utcnow() yield u'\n' yield u'\n' - yield ' ' + _make_text_block('title', self.title, self.title_type) - yield u' %s\n' % escape(self.id) - yield u' %s\n' % format_iso8601(self.updated) + yield " " + _make_text_block("title", self.title, self.title_type) + yield u" %s\n" % escape(self.id) + yield u" %s\n" % format_iso8601(self.updated) if self.url: yield u' \n' % escape(self.url) if self.feed_url: - yield u' \n' % \ - escape(self.feed_url) + yield u' \n' % escape(self.feed_url) for link in self.links: - yield u' \n' % ''.join('%s="%s" ' % - (k, escape(link[k])) for k in link) + yield u" \n" % "".join( + '%s="%s" ' % (k, escape(link[k])) for k in link + ) for author in self.author: - yield u' \n' - yield u' %s\n' % escape(author['name']) - if 'uri' in author: - yield u' %s\n' % escape(author['uri']) - if 'email' in author: - yield ' %s\n' % escape(author['email']) - yield ' \n' + yield u" \n" + yield u" %s\n" % escape(author["name"]) + if "uri" in author: + yield u" %s\n" % escape(author["uri"]) + if "email" in author: + yield " %s\n" % escape(author["email"]) + yield " \n" if self.subtitle: - yield ' ' + _make_text_block('subtitle', self.subtitle, - self.subtitle_type) + yield " " + _make_text_block("subtitle", self.subtitle, self.subtitle_type) if self.icon: - yield u' %s\n' % escape(self.icon) + yield u" %s\n" % escape(self.icon) if self.logo: - yield u' %s\n' % escape(self.logo) + yield u" %s\n" % escape(self.logo) if self.rights: - yield ' ' + _make_text_block('rights', self.rights, - self.rights_type) + yield " " + _make_text_block("rights", self.rights, self.rights_type) generator_name, generator_url, generator_version = self.generator if generator_name or generator_url or generator_version: - tmp = [u' %s\n' % escape(generator_name)) - yield u''.join(tmp) + tmp.append(u">%s\n" % escape(generator_name)) + yield u"".join(tmp) for entry in self.entries: for line in entry.generate(): - yield u' ' + line - yield u'\n' + yield u" " + line + yield u"\n" def to_string(self): """Convert the feed into a string.""" - return u''.join(self.generate()) + return u"".join(self.generate()) def get_response(self): """Return a response object for the feed.""" - return BaseResponse(self.to_string(), mimetype='application/atom+xml') + return BaseResponse(self.to_string(), mimetype="application/atom+xml") def __call__(self, environ, start_response): """Use the class as WSGI response object.""" @@ -276,80 +286,77 @@ class FeedEntry(object): def __init__(self, title=None, content=None, feed_url=None, **kwargs): self.title = title - self.title_type = kwargs.get('title_type', 'text') + self.title_type = kwargs.get("title_type", "text") self.content = content - self.content_type = kwargs.get('content_type', 'html') - self.url = kwargs.get('url') - self.id = kwargs.get('id', self.url) - self.updated = kwargs.get('updated') - self.summary = kwargs.get('summary') - self.summary_type = kwargs.get('summary_type', 'html') - self.author = kwargs.get('author', ()) - self.published = kwargs.get('published') - self.rights = kwargs.get('rights') - self.links = kwargs.get('links', []) - self.categories = kwargs.get('categories', []) - self.xml_base = kwargs.get('xml_base', feed_url) - - if not hasattr(self.author, '__iter__') \ - or isinstance(self.author, string_types + (dict,)): + self.content_type = kwargs.get("content_type", "html") + self.url = kwargs.get("url") + self.id = kwargs.get("id", self.url) + self.updated = kwargs.get("updated") + self.summary = kwargs.get("summary") + self.summary_type = kwargs.get("summary_type", "html") + self.author = kwargs.get("author", ()) + self.published = kwargs.get("published") + self.rights = kwargs.get("rights") + self.links = kwargs.get("links", []) + self.categories = kwargs.get("categories", []) + self.xml_base = kwargs.get("xml_base", feed_url) + + if not hasattr(self.author, "__iter__") or isinstance( + self.author, string_types + (dict,) + ): self.author = [self.author] for i, author in enumerate(self.author): if not isinstance(author, dict): - self.author[i] = {'name': author} + self.author[i] = {"name": author} if not self.title: - raise ValueError('title is required') + raise ValueError("title is required") if not self.id: - raise ValueError('id is required') + raise ValueError("id is required") if not self.updated: - raise ValueError('updated is required') + raise ValueError("updated is required") def __repr__(self): - return '<%s %r>' % ( - self.__class__.__name__, - self.title - ) + return "<%s %r>" % (self.__class__.__name__, self.title) def generate(self): """Yields pieces of ATOM XML.""" - base = '' + base = "" if self.xml_base: base = ' xml:base="%s"' % escape(self.xml_base) - yield u'\n' % base - yield u' ' + _make_text_block('title', self.title, self.title_type) - yield u' %s\n' % escape(self.id) - yield u' %s\n' % format_iso8601(self.updated) + yield u"\n" % base + yield u" " + _make_text_block("title", self.title, self.title_type) + yield u" %s\n" % escape(self.id) + yield u" %s\n" % format_iso8601(self.updated) if self.published: - yield u' %s\n' % \ - format_iso8601(self.published) + yield u" %s\n" % format_iso8601(self.published) if self.url: yield u' \n' % escape(self.url) for author in self.author: - yield u' \n' - yield u' %s\n' % escape(author['name']) - if 'uri' in author: - yield u' %s\n' % escape(author['uri']) - if 'email' in author: - yield u' %s\n' % escape(author['email']) - yield u' \n' + yield u" \n" + yield u" %s\n" % escape(author["name"]) + if "uri" in author: + yield u" %s\n" % escape(author["uri"]) + if "email" in author: + yield u" %s\n" % escape(author["email"]) + yield u" \n" for link in self.links: - yield u' \n' % ''.join('%s="%s" ' % - (k, escape(link[k])) for k in link) + yield u" \n" % "".join( + '%s="%s" ' % (k, escape(link[k])) for k in link + ) for category in self.categories: - yield u' \n' % ''.join('%s="%s" ' % - (k, escape(category[k])) for k in category) + yield u" \n" % "".join( + '%s="%s" ' % (k, escape(category[k])) for k in category + ) if self.summary: - yield u' ' + _make_text_block('summary', self.summary, - self.summary_type) + yield u" " + _make_text_block("summary", self.summary, self.summary_type) if self.content: - yield u' ' + _make_text_block('content', self.content, - self.content_type) - yield u'\n' + yield u" " + _make_text_block("content", self.content, self.content_type) + yield u"\n" def to_string(self): """Convert the feed item into a unicode object.""" - return u''.join(self.generate()) + return u"".join(self.generate()) def __str__(self): return self.to_string() diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/cache.py b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/cache.py index 170b8cdf1..79c749b5e 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/cache.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/cache.py @@ -53,24 +53,37 @@ you have access to it (either as a module global you can import or you just put it into your WSGI application). - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. + :copyright: 2007 Pallets + :license: BSD-3-Clause """ +import errno import os +import platform import re -import errno import tempfile -import platform +import warnings from hashlib import md5 from time import time + +from .._compat import integer_types +from .._compat import iteritems +from .._compat import string_types +from .._compat import text_type +from .._compat import to_native +from ..posixemulation import rename + try: import cPickle as pickle except ImportError: # pragma: no cover import pickle -from werkzeug._compat import iteritems, string_types, text_type, \ - integer_types, to_native -from werkzeug.posixemulation import rename +warnings.warn( + "'werkzeug.contrib.cache' is deprecated as of version 0.15 and will" + " be removed in version 1.0. It has moved to https://github.com" + "/pallets/cachelib.", + DeprecationWarning, + stacklevel=2, +) def _items(mappingorseq): @@ -84,13 +97,12 @@ def _items(mappingorseq): ... assert k*k == v """ - if hasattr(mappingorseq, 'items'): + if hasattr(mappingorseq, "items"): return iteritems(mappingorseq) return mappingorseq class BaseCache(object): - """Baseclass for the cache systems. All the cache systems implement this API or a superset of it. @@ -126,7 +138,7 @@ class BaseCache(object): def get_many(self, *keys): """Returns a list of values for the given keys. - For each key a item in the list is created:: + For each key an item in the list is created:: foo, bar = cache.get_many("foo", "bar") @@ -135,7 +147,7 @@ class BaseCache(object): :param keys: The function accepts multiple keys as positional arguments. """ - return map(self.get, keys) + return [self.get(k) for k in keys] def get_dict(self, *keys): """Like :meth:`get_many` but return a dict:: @@ -215,10 +227,10 @@ class BaseCache(object): :param key: the key to check """ raise NotImplementedError( - '%s doesn\'t have an efficient implementation of `has`. That ' - 'means it is impossible to check whether a key exists without ' - 'fully loading the key\'s data. Consider using `self.get` ' - 'explicitly if you don\'t care about performance.' + "%s doesn't have an efficient implementation of `has`. That " + "means it is impossible to check whether a key exists without " + "fully loading the key's data. Consider using `self.get` " + "explicitly if you don't care about performance." ) def clear(self): @@ -258,16 +270,17 @@ class BaseCache(object): class NullCache(BaseCache): - """A cache that doesn't cache. This can be useful for unit testing. :param default_timeout: a dummy parameter that is ignored but exists for API compatibility with other caches. """ + def has(self, key): + return False + class SimpleCache(BaseCache): - """Simple memory cache for single process environments. This class exists mainly for the development server and is not 100% thread safe. It tries to use as many atomic operations as possible and no locks for simplicity @@ -313,15 +326,13 @@ class SimpleCache(BaseCache): def set(self, key, value, timeout=None): expires = self._normalize_timeout(timeout) self._prune() - self._cache[key] = (expires, pickle.dumps(value, - pickle.HIGHEST_PROTOCOL)) + self._cache[key] = (expires, pickle.dumps(value, pickle.HIGHEST_PROTOCOL)) return True def add(self, key, value, timeout=None): expires = self._normalize_timeout(timeout) self._prune() - item = (expires, pickle.dumps(value, - pickle.HIGHEST_PROTOCOL)) + item = (expires, pickle.dumps(value, pickle.HIGHEST_PROTOCOL)) if key in self._cache: return False self._cache.setdefault(key, item) @@ -337,11 +348,11 @@ class SimpleCache(BaseCache): except KeyError: return False -_test_memcached_key = re.compile(r'[^\x00-\x21\xff]{1,250}$').match +_test_memcached_key = re.compile(r"[^\x00-\x21\xff]{1,250}$").match -class MemcachedCache(BaseCache): +class MemcachedCache(BaseCache): """A cache that uses memcached as backend. The first argument can either be an object that resembles the API of a @@ -355,6 +366,7 @@ class MemcachedCache(BaseCache): - ``pylibmc`` - ``google.appengine.api.memcached`` - ``memcached`` + - ``libmc`` Implementation notes: This cache backend works around some limitations in memcached to simplify the interface. For example unicode keys are encoded @@ -367,7 +379,7 @@ class MemcachedCache(BaseCache): a :class:`memcache.Client` or a compatible client. :param default_timeout: the default timeout that is used if no timeout is specified on :meth:`~BaseCache.set`. A timeout of - 0 indicates taht the cache never expires. + 0 indicates that the cache never expires. :param key_prefix: a prefix that is added before all keys. This makes it possible to use the same memcached server for different applications. Keep in mind that @@ -379,10 +391,10 @@ class MemcachedCache(BaseCache): BaseCache.__init__(self, default_timeout) if servers is None or isinstance(servers, (list, tuple)): if servers is None: - servers = ['127.0.0.1:11211'] + servers = ["127.0.0.1:11211"] self._client = self.import_preferred_memcache_lib(servers) if self._client is None: - raise RuntimeError('no memcache module found') + raise RuntimeError("no memcache module found") else: # NOTE: servers is actually an already initialized memcache # client. @@ -391,7 +403,7 @@ class MemcachedCache(BaseCache): self.key_prefix = to_native(key_prefix) def _normalize_key(self, key): - key = to_native(key, 'utf-8') + key = to_native(key, "utf-8") if self.key_prefix: key = self.key_prefix + key return key @@ -419,7 +431,8 @@ class MemcachedCache(BaseCache): have_encoded_keys = True if _test_memcached_key(key): key_mapping[encoded_key] = key - d = rv = self._client.get_multi(key_mapping.keys()) + _keys = list(key_mapping) + d = rv = self._client.get_multi(_keys) if have_encoded_keys or self.key_prefix: rv = {} for key, value in iteritems(d): @@ -470,7 +483,7 @@ class MemcachedCache(BaseCache): def has(self, key): key = self._normalize_key(key) if _test_memcached_key(key): - return self._client.append(key, '') + return self._client.append(key, "") return False def clear(self): @@ -507,13 +520,19 @@ class MemcachedCache(BaseCache): else: return memcache.Client(servers) + try: + import libmc + except ImportError: + pass + else: + return libmc.Client(servers) + # backwards compatibility GAEMemcachedCache = MemcachedCache class RedisCache(BaseCache): - """Uses the Redis key-value store as a cache backend. The first argument can be either a string denoting address of the Redis @@ -549,22 +568,32 @@ class RedisCache(BaseCache): Any additional keyword arguments will be passed to ``redis.Redis``. """ - def __init__(self, host='localhost', port=6379, password=None, - db=0, default_timeout=300, key_prefix=None, **kwargs): + def __init__( + self, + host="localhost", + port=6379, + password=None, + db=0, + default_timeout=300, + key_prefix=None, + **kwargs + ): BaseCache.__init__(self, default_timeout) + if host is None: + raise ValueError("RedisCache host parameter may not be None") if isinstance(host, string_types): try: import redis except ImportError: - raise RuntimeError('no redis module found') - if kwargs.get('decode_responses', None): - raise ValueError('decode_responses is not supported by ' - 'RedisCache.') - self._client = redis.Redis(host=host, port=port, password=password, - db=db, **kwargs) + raise RuntimeError("no redis module found") + if kwargs.get("decode_responses", None): + raise ValueError("decode_responses is not supported by RedisCache.") + self._client = redis.Redis( + host=host, port=port, password=password, db=db, **kwargs + ) else: self._client = host - self.key_prefix = key_prefix or '' + self.key_prefix = key_prefix or "" def _normalize_timeout(self, timeout): timeout = BaseCache._normalize_timeout(self, timeout) @@ -578,8 +607,8 @@ class RedisCache(BaseCache): """ t = type(value) if t in integer_types: - return str(value).encode('ascii') - return b'!' + pickle.dumps(value) + return str(value).encode("ascii") + return b"!" + pickle.dumps(value) def load_object(self, value): """The reversal of :meth:`dump_object`. This might be called with @@ -587,7 +616,7 @@ class RedisCache(BaseCache): """ if value is None: return None - if value.startswith(b'!'): + if value.startswith(b"!"): try: return pickle.loads(value[1:]) except pickle.PickleError: @@ -610,20 +639,19 @@ class RedisCache(BaseCache): timeout = self._normalize_timeout(timeout) dump = self.dump_object(value) if timeout == -1: - result = self._client.set(name=self.key_prefix + key, - value=dump) + result = self._client.set(name=self.key_prefix + key, value=dump) else: - result = self._client.setex(name=self.key_prefix + key, - value=dump, time=timeout) + result = self._client.setex( + name=self.key_prefix + key, value=dump, time=timeout + ) return result def add(self, key, value, timeout=None): timeout = self._normalize_timeout(timeout) dump = self.dump_object(value) - return ( - self._client.setnx(name=self.key_prefix + key, value=dump) and - self._client.expire(name=self.key_prefix + key, time=timeout) - ) + return self._client.setnx( + name=self.key_prefix + key, value=dump + ) and self._client.expire(name=self.key_prefix + key, time=timeout) def set_many(self, mapping, timeout=None): timeout = self._normalize_timeout(timeout) @@ -636,8 +664,7 @@ class RedisCache(BaseCache): if timeout == -1: pipe.set(name=self.key_prefix + key, value=dump) else: - pipe.setex(name=self.key_prefix + key, value=dump, - time=timeout) + pipe.setex(name=self.key_prefix + key, value=dump, time=timeout) return pipe.execute() def delete(self, key): @@ -656,7 +683,7 @@ class RedisCache(BaseCache): def clear(self): status = False if self.key_prefix: - keys = self._client.keys(self.key_prefix + '*') + keys = self._client.keys(self.key_prefix + "*") if keys: status = self._client.delete(*keys) else: @@ -671,7 +698,6 @@ class RedisCache(BaseCache): class FileSystemCache(BaseCache): - """A cache that stores the items on the file system. This cache depends on being the only user of the `cache_dir`. Make absolutely sure that nobody but this cache stores files there or otherwise the cache will @@ -679,7 +705,8 @@ class FileSystemCache(BaseCache): :param cache_dir: the directory where cache files are stored. :param threshold: the maximum number of items the cache stores before - it starts deleting some. + it starts deleting some. A threshold value of 0 + indicates no threshold. :param default_timeout: the default timeout that is used if no timeout is specified on :meth:`~BaseCache.set`. A timeout of 0 indicates that the cache never expires. @@ -687,10 +714,11 @@ class FileSystemCache(BaseCache): """ #: used for temporary files by the FileSystemCache - _fs_transaction_suffix = '.__wz_cache' + _fs_transaction_suffix = ".__wz_cache" + #: keep amount of files in a cache element + _fs_count_file = "__wz_cache_count" - def __init__(self, cache_dir, threshold=500, default_timeout=300, - mode=0o600): + def __init__(self, cache_dir, threshold=500, default_timeout=300, mode=0o600): BaseCache.__init__(self, default_timeout) self._path = cache_dir self._threshold = threshold @@ -702,6 +730,23 @@ class FileSystemCache(BaseCache): if ex.errno != errno.EEXIST: raise + self._update_count(value=len(self._list_dir())) + + @property + def _file_count(self): + return self.get(self._fs_count_file) or 0 + + def _update_count(self, delta=None, value=None): + # If we have no threshold, don't count files + if self._threshold == 0: + return + + if delta: + new_count = self._file_count + delta + else: + new_count = value or 0 + self.set(self._fs_count_file, new_count, mgmt_element=True) + def _normalize_timeout(self, timeout): timeout = BaseCache._normalize_timeout(self, timeout) if timeout != 0: @@ -711,43 +756,54 @@ class FileSystemCache(BaseCache): def _list_dir(self): """return a list of (fully qualified) cache filenames """ - return [os.path.join(self._path, fn) for fn in os.listdir(self._path) - if not fn.endswith(self._fs_transaction_suffix)] + mgmt_files = [ + self._get_filename(name).split("/")[-1] for name in (self._fs_count_file,) + ] + return [ + os.path.join(self._path, fn) + for fn in os.listdir(self._path) + if not fn.endswith(self._fs_transaction_suffix) and fn not in mgmt_files + ] def _prune(self): + if self._threshold == 0 or not self._file_count > self._threshold: + return + entries = self._list_dir() - if len(entries) > self._threshold: - now = time() - for idx, fname in enumerate(entries): - try: - remove = False - with open(fname, 'rb') as f: - expires = pickle.load(f) - remove = (expires != 0 and expires <= now) or idx % 3 == 0 - - if remove: - os.remove(fname) - except (IOError, OSError): - pass + now = time() + for idx, fname in enumerate(entries): + try: + remove = False + with open(fname, "rb") as f: + expires = pickle.load(f) + remove = (expires != 0 and expires <= now) or idx % 3 == 0 + + if remove: + os.remove(fname) + except (IOError, OSError): + pass + self._update_count(value=len(self._list_dir())) def clear(self): for fname in self._list_dir(): try: os.remove(fname) except (IOError, OSError): + self._update_count(value=len(self._list_dir())) return False + self._update_count(value=0) return True def _get_filename(self, key): if isinstance(key, text_type): - key = key.encode('utf-8') # XXX unicode review + key = key.encode("utf-8") # XXX unicode review hash = md5(key).hexdigest() return os.path.join(self._path, hash) def get(self, key): filename = self._get_filename(key) try: - with open(filename, 'rb') as f: + with open(filename, "rb") as f: pickle_time = pickle.load(f) if pickle_time == 0 or pickle_time >= time(): return pickle.load(f) @@ -763,14 +819,22 @@ class FileSystemCache(BaseCache): return self.set(key, value, timeout) return False - def set(self, key, value, timeout=None): + def set(self, key, value, timeout=None, mgmt_element=False): + # Management elements have no timeout + if mgmt_element: + timeout = 0 + + # Don't prune on management element update, to avoid loop + else: + self._prune() + timeout = self._normalize_timeout(timeout) filename = self._get_filename(key) - self._prune() try: - fd, tmp = tempfile.mkstemp(suffix=self._fs_transaction_suffix, - dir=self._path) - with os.fdopen(fd, 'wb') as f: + fd, tmp = tempfile.mkstemp( + suffix=self._fs_transaction_suffix, dir=self._path + ) + with os.fdopen(fd, "wb") as f: pickle.dump(timeout, f, 1) pickle.dump(value, f, pickle.HIGHEST_PROTOCOL) rename(tmp, filename) @@ -778,20 +842,26 @@ class FileSystemCache(BaseCache): except (IOError, OSError): return False else: + # Management elements should not count towards threshold + if not mgmt_element: + self._update_count(delta=1) return True - def delete(self, key): + def delete(self, key, mgmt_element=False): try: os.remove(self._get_filename(key)) except (IOError, OSError): return False else: + # Management elements should not count towards threshold + if not mgmt_element: + self._update_count(delta=-1) return True def has(self, key): filename = self._get_filename(key) try: - with open(filename, 'rb') as f: + with open(filename, "rb") as f: pickle_time = pickle.load(f) if pickle_time == 0 or pickle_time >= time(): return True @@ -803,7 +873,7 @@ class FileSystemCache(BaseCache): class UWSGICache(BaseCache): - """ Implements the cache using uWSGI's caching framework. + """Implements the cache using uWSGI's caching framework. .. note:: This class cannot be used when running under PyPy, because the uWSGI @@ -816,19 +886,24 @@ class UWSGICache(BaseCache): same instance as the werkzeug app, you only have to provide the name of the cache. """ - def __init__(self, default_timeout=300, cache=''): + + def __init__(self, default_timeout=300, cache=""): BaseCache.__init__(self, default_timeout) - if platform.python_implementation() == 'PyPy': - raise RuntimeError("uWSGI caching does not work under PyPy, see " - "the docs for more details.") + if platform.python_implementation() == "PyPy": + raise RuntimeError( + "uWSGI caching does not work under PyPy, see " + "the docs for more details." + ) try: import uwsgi + self._uwsgi = uwsgi except ImportError: - raise RuntimeError("uWSGI could not be imported, are you " - "running under uWSGI?") + raise RuntimeError( + "uWSGI could not be imported, are you running under uWSGI?" + ) self.cache = cache @@ -842,14 +917,14 @@ class UWSGICache(BaseCache): return self._uwsgi.cache_del(key, self.cache) def set(self, key, value, timeout=None): - return self._uwsgi.cache_update(key, pickle.dumps(value), - self._normalize_timeout(timeout), - self.cache) + return self._uwsgi.cache_update( + key, pickle.dumps(value), self._normalize_timeout(timeout), self.cache + ) def add(self, key, value, timeout=None): - return self._uwsgi.cache_set(key, pickle.dumps(value), - self._normalize_timeout(timeout), - self.cache) + return self._uwsgi.cache_set( + key, pickle.dumps(value), self._normalize_timeout(timeout), self.cache + ) def clear(self): return self._uwsgi.cache_clear(self.cache) diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/fixers.py b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/fixers.py index b88a861b2..8df0afdab 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/fixers.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/fixers.py @@ -1,69 +1,91 @@ -# -*- coding: utf-8 -*- """ - werkzeug.contrib.fixers - ~~~~~~~~~~~~~~~~~~~~~~~ +Fixers +====== - .. versionadded:: 0.5 +.. warning:: + .. deprecated:: 0.15 + ``ProxyFix`` has moved to :mod:`werkzeug.middleware.proxy_fix`. + All other code in this module is deprecated and will be removed + in version 1.0. - This module includes various helpers that fix bugs in web servers. They may - be necessary for some versions of a buggy web server but not others. We try - to stay updated with the status of the bugs as good as possible but you have - to make sure whether they fix the problem you encounter. +.. versionadded:: 0.5 - If you notice bugs in webservers not fixed in this module consider - contributing a patch. +This module includes various helpers that fix web server behavior. - :copyright: Copyright 2009 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. +.. autoclass:: ProxyFix + :members: + +.. autoclass:: CGIRootFix + +.. autoclass:: PathInfoFromRequestUriFix + +.. autoclass:: HeaderRewriterFix + +.. autoclass:: InternetExplorerFix + +:copyright: 2007 Pallets +:license: BSD-3-Clause """ +import warnings + +from ..datastructures import Headers +from ..datastructures import ResponseCacheControl +from ..http import parse_cache_control_header +from ..http import parse_options_header +from ..http import parse_set_header +from ..middleware.proxy_fix import ProxyFix as _ProxyFix +from ..useragents import UserAgent + try: - from urllib import unquote -except ImportError: from urllib.parse import unquote - -from werkzeug.http import parse_options_header, parse_cache_control_header, \ - parse_set_header -from werkzeug.useragents import UserAgent -from werkzeug.datastructures import Headers, ResponseCacheControl +except ImportError: + from urllib import unquote class CGIRootFix(object): + """Wrap the application in this middleware if you are using FastCGI + or CGI and you have problems with your app root being set to the CGI + script's path instead of the path users are going to visit. - """Wrap the application in this middleware if you are using FastCGI or CGI - and you have problems with your app root being set to the cgi script's path - instead of the path users are going to visit + :param app: the WSGI application + :param app_root: Defaulting to ``'/'``, you can set this to + something else if your app is mounted somewhere else. - .. versionchanged:: 0.9 - Added `app_root` parameter and renamed from `LighttpdCGIRootFix`. + .. deprecated:: 0.15 + This middleware will be removed in version 1.0. - :param app: the WSGI application - :param app_root: Defaulting to ``'/'``, you can set this to something else - if your app is mounted somewhere else. + .. versionchanged:: 0.9 + Added `app_root` parameter and renamed from + ``LighttpdCGIRootFix``. """ - def __init__(self, app, app_root='/'): + def __init__(self, app, app_root="/"): + warnings.warn( + "'CGIRootFix' is deprecated as of version 0.15 and will be" + " removed in version 1.0.", + DeprecationWarning, + stacklevel=2, + ) self.app = app - self.app_root = app_root + self.app_root = app_root.strip("/") def __call__(self, environ, start_response): - # only set PATH_INFO for older versions of Lighty or if no - # server software is provided. That's because the test was - # added in newer Werkzeug versions and we don't want to break - # people's code if they are using this fixer in a test that - # does not set the SERVER_SOFTWARE key. - if 'SERVER_SOFTWARE' not in environ or \ - environ['SERVER_SOFTWARE'] < 'lighttpd/1.4.28': - environ['PATH_INFO'] = environ.get('SCRIPT_NAME', '') + \ - environ.get('PATH_INFO', '') - environ['SCRIPT_NAME'] = self.app_root.strip('/') + environ["SCRIPT_NAME"] = self.app_root return self.app(environ, start_response) -# backwards compatibility -LighttpdCGIRootFix = CGIRootFix +class LighttpdCGIRootFix(CGIRootFix): + def __init__(self, *args, **kwargs): + warnings.warn( + "'LighttpdCGIRootFix' is renamed 'CGIRootFix'. Both will be" + " removed in version 1.0.", + DeprecationWarning, + stacklevel=2, + ) + super(LighttpdCGIRootFix, self).__init__(*args, **kwargs) -class PathInfoFromRequestUriFix(object): +class PathInfoFromRequestUriFix(object): """On windows environment variables are limited to the system charset which makes it impossible to store the `PATH_INFO` variable in the environment without loss of information on some systems. @@ -76,84 +98,52 @@ class PathInfoFromRequestUriFix(object): variables. :param app: the WSGI application + + .. deprecated:: 0.15 + This middleware will be removed in version 1.0. """ def __init__(self, app): + warnings.warn( + "'PathInfoFromRequestUriFix' is deprecated as of version" + " 0.15 and will be removed in version 1.0.", + DeprecationWarning, + stacklevel=2, + ) self.app = app def __call__(self, environ, start_response): - for key in 'REQUEST_URL', 'REQUEST_URI', 'UNENCODED_URL': + for key in "REQUEST_URL", "REQUEST_URI", "UNENCODED_URL": if key not in environ: continue request_uri = unquote(environ[key]) - script_name = unquote(environ.get('SCRIPT_NAME', '')) + script_name = unquote(environ.get("SCRIPT_NAME", "")) if request_uri.startswith(script_name): - environ['PATH_INFO'] = request_uri[len(script_name):] \ - .split('?', 1)[0] + environ["PATH_INFO"] = request_uri[len(script_name) :].split("?", 1)[0] break return self.app(environ, start_response) -class ProxyFix(object): - - """This middleware can be applied to add HTTP proxy support to an - application that was not designed with HTTP proxies in mind. It - sets `REMOTE_ADDR`, `HTTP_HOST` from `X-Forwarded` headers. While - Werkzeug-based applications already can use - :py:func:`werkzeug.wsgi.get_host` to retrieve the current host even if - behind proxy setups, this middleware can be used for applications which - access the WSGI environment directly. - - If you have more than one proxy server in front of your app, set - `num_proxies` accordingly. - - Do not use this middleware in non-proxy setups for security reasons. - - The original values of `REMOTE_ADDR` and `HTTP_HOST` are stored in - the WSGI environment as `werkzeug.proxy_fix.orig_remote_addr` and - `werkzeug.proxy_fix.orig_http_host`. - - :param app: the WSGI application - :param num_proxies: the number of proxy servers in front of the app. +class ProxyFix(_ProxyFix): + """ + .. deprecated:: 0.15 + ``werkzeug.contrib.fixers.ProxyFix`` has moved to + :mod:`werkzeug.middleware.proxy_fix`. This import will be + removed in 1.0. """ - def __init__(self, app, num_proxies=1): - self.app = app - self.num_proxies = num_proxies - - def get_remote_addr(self, forwarded_for): - """Selects the new remote addr from the given list of ips in - X-Forwarded-For. By default it picks the one that the `num_proxies` - proxy server provides. Before 0.9 it would always pick the first. - - .. versionadded:: 0.8 - """ - if len(forwarded_for) >= self.num_proxies: - return forwarded_for[-self.num_proxies] - - def __call__(self, environ, start_response): - getter = environ.get - forwarded_proto = getter('HTTP_X_FORWARDED_PROTO', '') - forwarded_for = getter('HTTP_X_FORWARDED_FOR', '').split(',') - forwarded_host = getter('HTTP_X_FORWARDED_HOST', '') - environ.update({ - 'werkzeug.proxy_fix.orig_wsgi_url_scheme': getter('wsgi.url_scheme'), - 'werkzeug.proxy_fix.orig_remote_addr': getter('REMOTE_ADDR'), - 'werkzeug.proxy_fix.orig_http_host': getter('HTTP_HOST') - }) - forwarded_for = [x for x in [x.strip() for x in forwarded_for] if x] - remote_addr = self.get_remote_addr(forwarded_for) - if remote_addr is not None: - environ['REMOTE_ADDR'] = remote_addr - if forwarded_host: - environ['HTTP_HOST'] = forwarded_host - if forwarded_proto: - environ['wsgi.url_scheme'] = forwarded_proto - return self.app(environ, start_response) + def __init__(self, *args, **kwargs): + warnings.warn( + "'werkzeug.contrib.fixers.ProxyFix' has moved to 'werkzeug" + ".middleware.proxy_fix.ProxyFix'. This import is deprecated" + " as of version 0.15 and will be removed in 1.0.", + DeprecationWarning, + stacklevel=2, + ) + super(ProxyFix, self).__init__(*args, **kwargs) class HeaderRewriterFix(object): - """This middleware can remove response headers and add others. This is for example useful to remove the `Date` header from responses if you are using a server that adds that header, no matter if it's present or @@ -167,9 +157,18 @@ class HeaderRewriterFix(object): removed. :param add_headers: a sequence of ``(key, value)`` tuples that should be added. + + .. deprecated:: 0.15 + This middleware will be removed in 1.0. """ def __init__(self, app, remove_headers=None, add_headers=None): + warnings.warn( + "'HeaderRewriterFix' is deprecated as of version 0.15 and" + " will be removed in version 1.0.", + DeprecationWarning, + stacklevel=2, + ) self.app = app self.remove_headers = set(x.lower() for x in (remove_headers or ())) self.add_headers = list(add_headers or ()) @@ -182,18 +181,18 @@ class HeaderRewriterFix(object): new_headers.append((key, value)) new_headers += self.add_headers return start_response(status, new_headers, exc_info) + return self.app(environ, rewriting_start_response) class InternetExplorerFix(object): - """This middleware fixes a couple of bugs with Microsoft Internet Explorer. Currently the following fixes are applied: - removing of `Vary` headers for unsupported mimetypes which causes troubles with caching. Can be disabled by passing ``fix_vary=False`` to the constructor. - see: http://support.microsoft.com/kb/824847/en-us + see: https://support.microsoft.com/en-us/help/824847 - removes offending headers to work around caching bugs in Internet Explorer if `Content-Disposition` is set. Can be @@ -201,54 +200,63 @@ class InternetExplorerFix(object): If it does not detect affected Internet Explorer versions it won't touch the request / response. + + .. deprecated:: 0.15 + This middleware will be removed in 1.0. """ # This code was inspired by Django fixers for the same bugs. The # fix_vary and fix_attach fixers were originally implemented in Django # by Michael Axiak and is available as part of the Django project: - # http://code.djangoproject.com/ticket/4148 + # https://code.djangoproject.com/ticket/4148 def __init__(self, app, fix_vary=True, fix_attach=True): + warnings.warn( + "'InternetExplorerFix' is deprecated as of version 0.15 and" + " will be removed in version 1.0.", + DeprecationWarning, + stacklevel=2, + ) self.app = app self.fix_vary = fix_vary self.fix_attach = fix_attach def fix_headers(self, environ, headers, status=None): if self.fix_vary: - header = headers.get('content-type', '') + header = headers.get("content-type", "") mimetype, options = parse_options_header(header) - if mimetype not in ('text/html', 'text/plain', 'text/sgml'): - headers.pop('vary', None) + if mimetype not in ("text/html", "text/plain", "text/sgml"): + headers.pop("vary", None) - if self.fix_attach and 'content-disposition' in headers: - pragma = parse_set_header(headers.get('pragma', '')) - pragma.discard('no-cache') + if self.fix_attach and "content-disposition" in headers: + pragma = parse_set_header(headers.get("pragma", "")) + pragma.discard("no-cache") header = pragma.to_header() if not header: - headers.pop('pragma', '') + headers.pop("pragma", "") else: - headers['Pragma'] = header - header = headers.get('cache-control', '') + headers["Pragma"] = header + header = headers.get("cache-control", "") if header: - cc = parse_cache_control_header(header, - cls=ResponseCacheControl) + cc = parse_cache_control_header(header, cls=ResponseCacheControl) cc.no_cache = None cc.no_store = False header = cc.to_header() if not header: - headers.pop('cache-control', '') + headers.pop("cache-control", "") else: - headers['Cache-Control'] = header + headers["Cache-Control"] = header def run_fixed(self, environ, start_response): def fixing_start_response(status, headers, exc_info=None): headers = Headers(headers) self.fix_headers(environ, headers, status) return start_response(status, headers.to_wsgi_list(), exc_info) + return self.app(environ, fixing_start_response) def __call__(self, environ, start_response): ua = UserAgent(environ) - if ua.browser != 'msie': + if ua.browser != "msie": return self.app(environ, start_response) return self.run_fixed(environ, start_response) diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/iterio.py b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/iterio.py index c0ced3700..b67245409 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/iterio.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/iterio.py @@ -36,15 +36,24 @@ r""" .. _greenlet: https://github.com/python-greenlet/greenlet - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. + :copyright: 2007 Pallets + :license: BSD-3-Clause """ +import warnings + +from .._compat import implements_iterator + try: import greenlet except ImportError: greenlet = None -from werkzeug._compat import implements_iterator +warnings.warn( + "'werkzeug.contrib.iterio' is deprecated as of version 0.15 and" + " will be removed in version 1.0.", + DeprecationWarning, + stacklevel=2, +) def _mixed_join(iterable, sentinel): @@ -52,19 +61,18 @@ def _mixed_join(iterable, sentinel): iterator = iter(iterable) first_item = next(iterator, sentinel) if isinstance(first_item, bytes): - return first_item + b''.join(iterator) - return first_item + u''.join(iterator) + return first_item + b"".join(iterator) + return first_item + u"".join(iterator) def _newline(reference_string): if isinstance(reference_string, bytes): - return b'\n' - return u'\n' + return b"\n" + return u"\n" @implements_iterator class IterIO(object): - """Instances of this object implement an interface compatible with the standard Python :class:`file` object. Streams are either read-only or write-only depending on how the object is created. @@ -75,7 +83,7 @@ class IterIO(object): If the first argument is a callable then the stream object will be created and passed to that function. The caller itself however will - not receive a stream but an iterable. The function will be be executed + not receive a stream but an iterable. The function will be executed step by step as something iterates over the returned iterable. Each call to :meth:`flush` will create an item for the iterable. If :meth:`flush` is called without any writes in-between the sentinel @@ -91,7 +99,7 @@ class IterIO(object): `sentinel` parameter was added. """ - def __new__(cls, obj, sentinel=''): + def __new__(cls, obj, sentinel=""): try: iterator = iter(obj) except TypeError: @@ -103,53 +111,53 @@ class IterIO(object): def tell(self): if self.closed: - raise ValueError('I/O operation on closed file') + raise ValueError("I/O operation on closed file") return self.pos def isatty(self): if self.closed: - raise ValueError('I/O operation on closed file') + raise ValueError("I/O operation on closed file") return False def seek(self, pos, mode=0): if self.closed: - raise ValueError('I/O operation on closed file') - raise IOError(9, 'Bad file descriptor') + raise ValueError("I/O operation on closed file") + raise IOError(9, "Bad file descriptor") def truncate(self, size=None): if self.closed: - raise ValueError('I/O operation on closed file') - raise IOError(9, 'Bad file descriptor') + raise ValueError("I/O operation on closed file") + raise IOError(9, "Bad file descriptor") def write(self, s): if self.closed: - raise ValueError('I/O operation on closed file') - raise IOError(9, 'Bad file descriptor') + raise ValueError("I/O operation on closed file") + raise IOError(9, "Bad file descriptor") def writelines(self, list): if self.closed: - raise ValueError('I/O operation on closed file') - raise IOError(9, 'Bad file descriptor') + raise ValueError("I/O operation on closed file") + raise IOError(9, "Bad file descriptor") def read(self, n=-1): if self.closed: - raise ValueError('I/O operation on closed file') - raise IOError(9, 'Bad file descriptor') + raise ValueError("I/O operation on closed file") + raise IOError(9, "Bad file descriptor") def readlines(self, sizehint=0): if self.closed: - raise ValueError('I/O operation on closed file') - raise IOError(9, 'Bad file descriptor') + raise ValueError("I/O operation on closed file") + raise IOError(9, "Bad file descriptor") def readline(self, length=None): if self.closed: - raise ValueError('I/O operation on closed file') - raise IOError(9, 'Bad file descriptor') + raise ValueError("I/O operation on closed file") + raise IOError(9, "Bad file descriptor") def flush(self): if self.closed: - raise ValueError('I/O operation on closed file') - raise IOError(9, 'Bad file descriptor') + raise ValueError("I/O operation on closed file") + raise IOError(9, "Bad file descriptor") def __next__(self): if self.closed: @@ -161,12 +169,11 @@ class IterIO(object): class IterI(IterIO): - """Convert an stream into an iterator.""" - def __new__(cls, func, sentinel=''): + def __new__(cls, func, sentinel=""): if greenlet is None: - raise RuntimeError('IterI requires greenlet support') + raise RuntimeError("IterI requires greenlet support") stream = object.__new__(cls) stream._parent = greenlet.getcurrent() stream._buffer = [] @@ -192,7 +199,7 @@ class IterI(IterIO): def write(self, s): if self.closed: - raise ValueError('I/O operation on closed file') + raise ValueError("I/O operation on closed file") if s: self.pos += len(s) self._buffer.append(s) @@ -203,7 +210,7 @@ class IterI(IterIO): def flush(self): if self.closed: - raise ValueError('I/O operation on closed file') + raise ValueError("I/O operation on closed file") self._flush_impl() def _flush_impl(self): @@ -216,10 +223,9 @@ class IterI(IterIO): class IterO(IterIO): - """Iter output. Wrap an iterator and give it a stream like interface.""" - def __new__(cls, gen, sentinel=''): + def __new__(cls, gen, sentinel=""): self = object.__new__(cls) self._gen = gen self._buf = None @@ -232,8 +238,8 @@ class IterO(IterIO): return self def _buf_append(self, string): - '''Replace string directly without appending to an empty string, - avoiding type issues.''' + """Replace string directly without appending to an empty string, + avoiding type issues.""" if not self._buf: self._buf = string else: @@ -242,12 +248,12 @@ class IterO(IterIO): def close(self): if not self.closed: self.closed = True - if hasattr(self._gen, 'close'): + if hasattr(self._gen, "close"): self._gen.close() def seek(self, pos, mode=0): if self.closed: - raise ValueError('I/O operation on closed file') + raise ValueError("I/O operation on closed file") if mode == 1: pos += self.pos elif mode == 2: @@ -255,10 +261,10 @@ class IterO(IterIO): self.pos = min(self.pos, self.pos + pos) return elif mode != 0: - raise IOError('Invalid argument') + raise IOError("Invalid argument") buf = [] try: - tmp_end_pos = len(self._buf) + tmp_end_pos = len(self._buf or "") while pos > tmp_end_pos: item = next(self._gen) tmp_end_pos += len(item) @@ -271,10 +277,10 @@ class IterO(IterIO): def read(self, n=-1): if self.closed: - raise ValueError('I/O operation on closed file') + raise ValueError("I/O operation on closed file") if n < 0: self._buf_append(_mixed_join(self._gen, self.sentinel)) - result = self._buf[self.pos:] + result = self._buf[self.pos :] self.pos += len(result) return result new_pos = self.pos + n @@ -295,13 +301,13 @@ class IterO(IterIO): new_pos = max(0, new_pos) try: - return self._buf[self.pos:new_pos] + return self._buf[self.pos : new_pos] finally: self.pos = min(new_pos, len(self._buf)) def readline(self, length=None): if self.closed: - raise ValueError('I/O operation on closed file') + raise ValueError("I/O operation on closed file") nl_pos = -1 if self._buf: @@ -335,7 +341,7 @@ class IterO(IterIO): if length is not None and self.pos + length < new_pos: new_pos = self.pos + length try: - return self._buf[self.pos:new_pos] + return self._buf[self.pos : new_pos] finally: self.pos = min(new_pos, len(self._buf)) diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/jsrouting.py b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/jsrouting.py deleted file mode 100644 index d8158927e..000000000 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/jsrouting.py +++ /dev/null @@ -1,264 +0,0 @@ -# -*- coding: utf-8 -*- -""" - werkzeug.contrib.jsrouting - ~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Addon module that allows to create a JavaScript function from a map - that generates rules. - - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. -""" -try: - from simplejson import dumps -except ImportError: - try: - from json import dumps - except ImportError: - def dumps(*args): - raise RuntimeError('simplejson required for jsrouting') - -from inspect import getmro -from werkzeug.routing import NumberConverter -from werkzeug._compat import iteritems - - -def render_template(name_parts, rules, converters): - result = u'' - if name_parts: - for idx in range(0, len(name_parts) - 1): - name = u'.'.join(name_parts[:idx + 1]) - result += u"if (typeof %s === 'undefined') %s = {}\n" % (name, name) - result += '%s = ' % '.'.join(name_parts) - result += """(function (server_name, script_name, subdomain, url_scheme) { - var converters = [%(converters)s]; - var rules = %(rules)s; - function in_array(array, value) { - if (array.indexOf != undefined) { - return array.indexOf(value) != -1; - } - for (var i = 0; i < array.length; i++) { - if (array[i] == value) { - return true; - } - } - return false; - } - function array_diff(array1, array2) { - array1 = array1.slice(); - for (var i = array1.length-1; i >= 0; i--) { - if (in_array(array2, array1[i])) { - array1.splice(i, 1); - } - } - return array1; - } - function split_obj(obj) { - var names = []; - var values = []; - for (var name in obj) { - if (typeof(obj[name]) != 'function') { - names.push(name); - values.push(obj[name]); - } - } - return {names: names, values: values, original: obj}; - } - function suitable(rule, args) { - var default_args = split_obj(rule.defaults || {}); - var diff_arg_names = array_diff(rule.arguments, default_args.names); - - for (var i = 0; i < diff_arg_names.length; i++) { - if (!in_array(args.names, diff_arg_names[i])) { - return false; - } - } - - if (array_diff(rule.arguments, args.names).length == 0) { - if (rule.defaults == null) { - return true; - } - for (var i = 0; i < default_args.names.length; i++) { - var key = default_args.names[i]; - var value = default_args.values[i]; - if (value != args.original[key]) { - return false; - } - } - } - - return true; - } - function build(rule, args) { - var tmp = []; - var processed = rule.arguments.slice(); - for (var i = 0; i < rule.trace.length; i++) { - var part = rule.trace[i]; - if (part.is_dynamic) { - var converter = converters[rule.converters[part.data]]; - var data = converter(args.original[part.data]); - if (data == null) { - return null; - } - tmp.push(data); - processed.push(part.name); - } else { - tmp.push(part.data); - } - } - tmp = tmp.join(''); - var pipe = tmp.indexOf('|'); - var subdomain = tmp.substring(0, pipe); - var url = tmp.substring(pipe+1); - - var unprocessed = array_diff(args.names, processed); - var first_query_var = true; - for (var i = 0; i < unprocessed.length; i++) { - if (first_query_var) { - url += '?'; - } else { - url += '&'; - } - first_query_var = false; - url += encodeURIComponent(unprocessed[i]); - url += '='; - url += encodeURIComponent(args.original[unprocessed[i]]); - } - return {subdomain: subdomain, path: url}; - } - function lstrip(s, c) { - while (s && s.substring(0, 1) == c) { - s = s.substring(1); - } - return s; - } - function rstrip(s, c) { - while (s && s.substring(s.length-1, s.length) == c) { - s = s.substring(0, s.length-1); - } - return s; - } - return function(endpoint, args, force_external) { - args = split_obj(args); - var rv = null; - for (var i = 0; i < rules.length; i++) { - var rule = rules[i]; - if (rule.endpoint != endpoint) continue; - if (suitable(rule, args)) { - rv = build(rule, args); - if (rv != null) { - break; - } - } - } - if (rv == null) { - return null; - } - if (!force_external && rv.subdomain == subdomain) { - return rstrip(script_name, '/') + '/' + lstrip(rv.path, '/'); - } else { - return url_scheme + '://' - + (rv.subdomain ? rv.subdomain + '.' : '') - + server_name + rstrip(script_name, '/') - + '/' + lstrip(rv.path, '/'); - } - }; -})""" % {'converters': u', '.join(converters), - 'rules': rules} - - return result - - -def generate_map(map, name='url_map'): - """ - Generates a JavaScript function containing the rules defined in - this map, to be used with a MapAdapter's generate_javascript - method. If you don't pass a name the returned JavaScript code is - an expression that returns a function. Otherwise it's a standalone - script that assigns the function with that name. Dotted names are - resolved (so you an use a name like 'obj.url_for') - - In order to use JavaScript generation, simplejson must be installed. - - Note that using this feature will expose the rules - defined in your map to users. If your rules contain sensitive - information, don't use JavaScript generation! - """ - from warnings import warn - warn(DeprecationWarning('This module is deprecated')) - map.update() - rules = [] - converters = [] - for rule in map.iter_rules(): - trace = [{ - 'is_dynamic': is_dynamic, - 'data': data - } for is_dynamic, data in rule._trace] - rule_converters = {} - for key, converter in iteritems(rule._converters): - js_func = js_to_url_function(converter) - try: - index = converters.index(js_func) - except ValueError: - converters.append(js_func) - index = len(converters) - 1 - rule_converters[key] = index - rules.append({ - u'endpoint': rule.endpoint, - u'arguments': list(rule.arguments), - u'converters': rule_converters, - u'trace': trace, - u'defaults': rule.defaults - }) - - return render_template(name_parts=name and name.split('.') or [], - rules=dumps(rules), - converters=converters) - - -def generate_adapter(adapter, name='url_for', map_name='url_map'): - """Generates the url building function for a map.""" - values = { - u'server_name': dumps(adapter.server_name), - u'script_name': dumps(adapter.script_name), - u'subdomain': dumps(adapter.subdomain), - u'url_scheme': dumps(adapter.url_scheme), - u'name': name, - u'map_name': map_name - } - return u'''\ -var %(name)s = %(map_name)s( - %(server_name)s, - %(script_name)s, - %(subdomain)s, - %(url_scheme)s -);''' % values - - -def js_to_url_function(converter): - """Get the JavaScript converter function from a rule.""" - if hasattr(converter, 'js_to_url_function'): - data = converter.js_to_url_function() - else: - for cls in getmro(type(converter)): - if cls in js_to_url_functions: - data = js_to_url_functions[cls](converter) - break - else: - return 'encodeURIComponent' - return '(function(value) { %s })' % data - - -def NumberConverter_js_to_url(conv): - if conv.fixed_digits: - return u'''\ -var result = value.toString(); -while (result.length < %s) - result = '0' + result; -return result;''' % conv.fixed_digits - return u'return value.toString();' - - -js_to_url_functions = { - NumberConverter: NumberConverter_js_to_url -} diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/limiter.py b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/limiter.py deleted file mode 100644 index 6bc9d3978..000000000 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/limiter.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -""" - werkzeug.contrib.limiter - ~~~~~~~~~~~~~~~~~~~~~~~~ - - A middleware that limits incoming data. This works around problems with - Trac_ or Django_ because those directly stream into the memory. - - .. _Trac: http://trac.edgewall.org/ - .. _Django: http://www.djangoproject.com/ - - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. -""" -from warnings import warn - -from werkzeug.wsgi import LimitedStream - - -class StreamLimitMiddleware(object): - - """Limits the input stream to a given number of bytes. This is useful if - you have a WSGI application that reads form data into memory (django for - example) and you don't want users to harm the server by uploading tons of - data. - - Default is 10MB - - .. versionchanged:: 0.9 - Deprecated middleware. - """ - - def __init__(self, app, maximum_size=1024 * 1024 * 10): - warn(DeprecationWarning('This middleware is deprecated')) - self.app = app - self.maximum_size = maximum_size - - def __call__(self, environ, start_response): - limit = min(self.maximum_size, int(environ.get('CONTENT_LENGTH') or 0)) - environ['wsgi.input'] = LimitedStream(environ['wsgi.input'], limit) - return self.app(environ, start_response) diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/lint.py b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/lint.py index 7ec80ce99..8bd8b8ab2 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/lint.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/lint.py @@ -1,343 +1,11 @@ -# -*- coding: utf-8 -*- -""" - werkzeug.contrib.lint - ~~~~~~~~~~~~~~~~~~~~~ +import warnings - .. versionadded:: 0.5 +from ..middleware.lint import * # noqa: F401, F403 - This module provides a middleware that performs sanity checks of the WSGI - application. It checks that :pep:`333` is properly implemented and warns - on some common HTTP errors such as non-empty responses for 304 status - codes. - - This module provides a middleware, the :class:`LintMiddleware`. Wrap your - application with it and it will warn about common problems with WSGI and - HTTP while your application is running. - - It's strongly recommended to use it during development. - - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. -""" -try: - from urllib.parse import urlparse -except ImportError: - from urlparse import urlparse - -from warnings import warn - -from werkzeug.datastructures import Headers -from werkzeug.http import is_entity_header -from werkzeug.wsgi import FileWrapper -from werkzeug._compat import string_types - - -class WSGIWarning(Warning): - - """Warning class for WSGI warnings.""" - - -class HTTPWarning(Warning): - - """Warning class for HTTP warnings.""" - - -def check_string(context, obj, stacklevel=3): - if type(obj) is not str: - warn(WSGIWarning('%s requires bytestrings, got %s' % - (context, obj.__class__.__name__))) - - -class InputStream(object): - - def __init__(self, stream): - self._stream = stream - - def read(self, *args): - if len(args) == 0: - warn(WSGIWarning('wsgi does not guarantee an EOF marker on the ' - 'input stream, thus making calls to ' - 'wsgi.input.read() unsafe. Conforming servers ' - 'may never return from this call.'), - stacklevel=2) - elif len(args) != 1: - warn(WSGIWarning('too many parameters passed to wsgi.input.read()'), - stacklevel=2) - return self._stream.read(*args) - - def readline(self, *args): - if len(args) == 0: - warn(WSGIWarning('Calls to wsgi.input.readline() without arguments' - ' are unsafe. Use wsgi.input.read() instead.'), - stacklevel=2) - elif len(args) == 1: - warn(WSGIWarning('wsgi.input.readline() was called with a size hint. ' - 'WSGI does not support this, although it\'s available ' - 'on all major servers.'), - stacklevel=2) - else: - raise TypeError('too many arguments passed to wsgi.input.readline()') - return self._stream.readline(*args) - - def __iter__(self): - try: - return iter(self._stream) - except TypeError: - warn(WSGIWarning('wsgi.input is not iterable.'), stacklevel=2) - return iter(()) - - def close(self): - warn(WSGIWarning('application closed the input stream!'), - stacklevel=2) - self._stream.close() - - -class ErrorStream(object): - - def __init__(self, stream): - self._stream = stream - - def write(self, s): - check_string('wsgi.error.write()', s) - self._stream.write(s) - - def flush(self): - self._stream.flush() - - def writelines(self, seq): - for line in seq: - self.write(seq) - - def close(self): - warn(WSGIWarning('application closed the error stream!'), - stacklevel=2) - self._stream.close() - - -class GuardedWrite(object): - - def __init__(self, write, chunks): - self._write = write - self._chunks = chunks - - def __call__(self, s): - check_string('write()', s) - self._write.write(s) - self._chunks.append(len(s)) - - -class GuardedIterator(object): - - def __init__(self, iterator, headers_set, chunks): - self._iterator = iterator - self._next = iter(iterator).next - self.closed = False - self.headers_set = headers_set - self.chunks = chunks - - def __iter__(self): - return self - - def next(self): - if self.closed: - warn(WSGIWarning('iterated over closed app_iter'), - stacklevel=2) - rv = self._next() - if not self.headers_set: - warn(WSGIWarning('Application returned before it ' - 'started the response'), stacklevel=2) - check_string('application iterator items', rv) - self.chunks.append(len(rv)) - return rv - - def close(self): - self.closed = True - if hasattr(self._iterator, 'close'): - self._iterator.close() - - if self.headers_set: - status_code, headers = self.headers_set - bytes_sent = sum(self.chunks) - content_length = headers.get('content-length', type=int) - - if status_code == 304: - for key, value in headers: - key = key.lower() - if key not in ('expires', 'content-location') and \ - is_entity_header(key): - warn(HTTPWarning('entity header %r found in 304 ' - 'response' % key)) - if bytes_sent: - warn(HTTPWarning('304 responses must not have a body')) - elif 100 <= status_code < 200 or status_code == 204: - if content_length != 0: - warn(HTTPWarning('%r responses must have an empty ' - 'content length') % status_code) - if bytes_sent: - warn(HTTPWarning('%r responses must not have a body' % - status_code)) - elif content_length is not None and content_length != bytes_sent: - warn(WSGIWarning('Content-Length and the number of bytes ' - 'sent to the client do not match.')) - - def __del__(self): - if not self.closed: - try: - warn(WSGIWarning('Iterator was garbage collected before ' - 'it was closed.')) - except Exception: - pass - - -class LintMiddleware(object): - - """This middleware wraps an application and warns on common errors. - Among other thing it currently checks for the following problems: - - - invalid status codes - - non-bytestrings sent to the WSGI server - - strings returned from the WSGI application - - non-empty conditional responses - - unquoted etags - - relative URLs in the Location header - - unsafe calls to wsgi.input - - unclosed iterators - - Detected errors are emitted using the standard Python :mod:`warnings` - system and usually end up on :data:`stderr`. - - :: - - from werkzeug.contrib.lint import LintMiddleware - app = LintMiddleware(app) - - :param app: the application to wrap - """ - - def __init__(self, app): - self.app = app - - def check_environ(self, environ): - if type(environ) is not dict: - warn(WSGIWarning('WSGI environment is not a standard python dict.'), - stacklevel=4) - for key in ('REQUEST_METHOD', 'SERVER_NAME', 'SERVER_PORT', - 'wsgi.version', 'wsgi.input', 'wsgi.errors', - 'wsgi.multithread', 'wsgi.multiprocess', - 'wsgi.run_once'): - if key not in environ: - warn(WSGIWarning('required environment key %r not found' - % key), stacklevel=3) - if environ['wsgi.version'] != (1, 0): - warn(WSGIWarning('environ is not a WSGI 1.0 environ'), - stacklevel=3) - - script_name = environ.get('SCRIPT_NAME', '') - if script_name and script_name[:1] != '/': - warn(WSGIWarning('SCRIPT_NAME does not start with a slash: %r' - % script_name), stacklevel=3) - path_info = environ.get('PATH_INFO', '') - if path_info[:1] != '/': - warn(WSGIWarning('PATH_INFO does not start with a slash: %r' - % path_info), stacklevel=3) - - def check_start_response(self, status, headers, exc_info): - check_string('status', status) - status_code = status.split(None, 1)[0] - if len(status_code) != 3 or not status_code.isdigit(): - warn(WSGIWarning('Status code must be three digits'), stacklevel=3) - if len(status) < 4 or status[3] != ' ': - warn(WSGIWarning('Invalid value for status %r. Valid ' - 'status strings are three digits, a space ' - 'and a status explanation'), stacklevel=3) - status_code = int(status_code) - if status_code < 100: - warn(WSGIWarning('status code < 100 detected'), stacklevel=3) - - if type(headers) is not list: - warn(WSGIWarning('header list is not a list'), stacklevel=3) - for item in headers: - if type(item) is not tuple or len(item) != 2: - warn(WSGIWarning('Headers must tuple 2-item tuples'), - stacklevel=3) - name, value = item - if type(name) is not str or type(value) is not str: - warn(WSGIWarning('header items must be strings'), - stacklevel=3) - if name.lower() == 'status': - warn(WSGIWarning('The status header is not supported due to ' - 'conflicts with the CGI spec.'), - stacklevel=3) - - if exc_info is not None and not isinstance(exc_info, tuple): - warn(WSGIWarning('invalid value for exc_info'), stacklevel=3) - - headers = Headers(headers) - self.check_headers(headers) - - return status_code, headers - - def check_headers(self, headers): - etag = headers.get('etag') - if etag is not None: - if etag.startswith(('W/', 'w/')): - if etag.startswith('w/'): - warn(HTTPWarning('weak etag indicator should be upcase.'), - stacklevel=4) - etag = etag[2:] - if not (etag[:1] == etag[-1:] == '"'): - warn(HTTPWarning('unquoted etag emitted.'), stacklevel=4) - - location = headers.get('location') - if location is not None: - if not urlparse(location).netloc: - warn(HTTPWarning('absolute URLs required for location header'), - stacklevel=4) - - def check_iterator(self, app_iter): - if isinstance(app_iter, string_types): - warn(WSGIWarning('application returned string. Response will ' - 'send character for character to the client ' - 'which will kill the performance. Return a ' - 'list or iterable instead.'), stacklevel=3) - - def __call__(self, *args, **kwargs): - if len(args) != 2: - warn(WSGIWarning('Two arguments to WSGI app required'), stacklevel=2) - if kwargs: - warn(WSGIWarning('No keyword arguments to WSGI app allowed'), - stacklevel=2) - environ, start_response = args - - self.check_environ(environ) - environ['wsgi.input'] = InputStream(environ['wsgi.input']) - environ['wsgi.errors'] = ErrorStream(environ['wsgi.errors']) - - # hook our own file wrapper in so that applications will always - # iterate to the end and we can check the content length - environ['wsgi.file_wrapper'] = FileWrapper - - headers_set = [] - chunks = [] - - def checking_start_response(*args, **kwargs): - if len(args) not in (2, 3): - warn(WSGIWarning('Invalid number of arguments: %s, expected ' - '2 or 3' % len(args), stacklevel=2)) - if kwargs: - warn(WSGIWarning('no keyword arguments allowed.')) - - status, headers = args[:2] - if len(args) == 3: - exc_info = args[2] - else: - exc_info = None - - headers_set[:] = self.check_start_response(status, headers, - exc_info) - return GuardedWrite(start_response(status, headers, exc_info), - chunks) - - app_iter = self.app(environ, checking_start_response) - self.check_iterator(app_iter) - return GuardedIterator(app_iter, headers_set, chunks) +warnings.warn( + "'werkzeug.contrib.lint' has moved to 'werkzeug.middleware.lint'." + " This import is deprecated as of version 0.15 and will be removed" + " in version 1.0.", + DeprecationWarning, + stacklevel=2, +) diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/profiler.py b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/profiler.py index be860afbc..b79fe567f 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/profiler.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/profiler.py @@ -1,147 +1,42 @@ -# -*- coding: utf-8 -*- -""" - werkzeug.contrib.profiler - ~~~~~~~~~~~~~~~~~~~~~~~~~ +import warnings - This module provides a simple WSGI profiler middleware for finding - bottlenecks in web application. It uses the :mod:`profile` or - :mod:`cProfile` module to do the profiling and writes the stats to the - stream provided (defaults to stderr). +from ..middleware.profiler import * # noqa: F401, F403 - Example usage:: - - from werkzeug.contrib.profiler import ProfilerMiddleware - app = ProfilerMiddleware(app) - - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. -""" -import sys -import time -import os.path -try: - try: - from cProfile import Profile - except ImportError: - from profile import Profile - from pstats import Stats - available = True -except ImportError: - available = False +warnings.warn( + "'werkzeug.contrib.profiler' has moved to" + "'werkzeug.middleware.profiler'. This import is deprecated as of" + "version 0.15 and will be removed in version 1.0.", + DeprecationWarning, + stacklevel=2, +) class MergeStream(object): - - """An object that redirects `write` calls to multiple streams. - Use this to log to both `sys.stdout` and a file:: + """An object that redirects ``write`` calls to multiple streams. + Use this to log to both ``sys.stdout`` and a file:: f = open('profiler.log', 'w') stream = MergeStream(sys.stdout, f) profiler = ProfilerMiddleware(app, stream) + + .. deprecated:: 0.15 + Use the ``tee`` command in your terminal instead. This class + will be removed in 1.0. """ def __init__(self, *streams): + warnings.warn( + "'MergeStream' is deprecated as of version 0.15 and will be removed in" + " version 1.0. Use your terminal's 'tee' command instead.", + DeprecationWarning, + stacklevel=2, + ) + if not streams: - raise TypeError('at least one stream must be given') + raise TypeError("At least one stream must be given.") + self.streams = streams def write(self, data): for stream in self.streams: stream.write(data) - - -class ProfilerMiddleware(object): - - """Simple profiler middleware. Wraps a WSGI application and profiles - a request. This intentionally buffers the response so that timings are - more exact. - - By giving the `profile_dir` argument, pstat.Stats files are saved to that - directory, one file per request. Without it, a summary is printed to - `stream` instead. - - For the exact meaning of `sort_by` and `restrictions` consult the - :mod:`profile` documentation. - - .. versionadded:: 0.9 - Added support for `restrictions` and `profile_dir`. - - :param app: the WSGI application to profile. - :param stream: the stream for the profiled stats. defaults to stderr. - :param sort_by: a tuple of columns to sort the result by. - :param restrictions: a tuple of profiling strictions, not used if dumping - to `profile_dir`. - :param profile_dir: directory name to save pstat files - """ - - def __init__(self, app, stream=None, - sort_by=('time', 'calls'), restrictions=(), profile_dir=None): - if not available: - raise RuntimeError('the profiler is not available because ' - 'profile or pstat is not installed.') - self._app = app - self._stream = stream or sys.stdout - self._sort_by = sort_by - self._restrictions = restrictions - self._profile_dir = profile_dir - - def __call__(self, environ, start_response): - response_body = [] - - def catching_start_response(status, headers, exc_info=None): - start_response(status, headers, exc_info) - return response_body.append - - def runapp(): - appiter = self._app(environ, catching_start_response) - response_body.extend(appiter) - if hasattr(appiter, 'close'): - appiter.close() - - p = Profile() - start = time.time() - p.runcall(runapp) - body = b''.join(response_body) - elapsed = time.time() - start - - if self._profile_dir is not None: - prof_filename = os.path.join(self._profile_dir, - '%s.%s.%06dms.%d.prof' % ( - environ['REQUEST_METHOD'], - environ.get('PATH_INFO').strip( - '/').replace('/', '.') or 'root', - elapsed * 1000.0, - time.time() - )) - p.dump_stats(prof_filename) - - else: - stats = Stats(p, stream=self._stream) - stats.sort_stats(*self._sort_by) - - self._stream.write('-' * 80) - self._stream.write('\nPATH: %r\n' % environ.get('PATH_INFO')) - stats.print_stats(*self._restrictions) - self._stream.write('-' * 80 + '\n\n') - - return [body] - - -def make_action(app_factory, hostname='localhost', port=5000, - threaded=False, processes=1, stream=None, - sort_by=('time', 'calls'), restrictions=()): - """Return a new callback for :mod:`werkzeug.script` that starts a local - server with the profiler enabled. - - :: - - from werkzeug.contrib import profiler - action_profile = profiler.make_action(make_app) - """ - def action(hostname=('h', hostname), port=('p', port), - threaded=threaded, processes=processes): - """Start a new development server.""" - from werkzeug.serving import run_simple - app = ProfilerMiddleware(app_factory(), stream, sort_by, restrictions) - run_simple(hostname, port, app, False, None, threaded, processes) - return action diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/securecookie.py b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/securecookie.py index bc757261b..c4c9eee25 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/securecookie.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/securecookie.py @@ -85,30 +85,40 @@ r""" request.client_session.save_cookie(response) return response(environ, start_response) - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. + :copyright: 2007 Pallets + :license: BSD-3-Clause """ -import pickle import base64 +import pickle +import warnings +from hashlib import sha1 as _default_hash from hmac import new as hmac from time import time -from hashlib import sha1 as _default_hash -from werkzeug._compat import iteritems, text_type -from werkzeug.urls import url_quote_plus, url_unquote_plus -from werkzeug._internal import _date_to_unix -from werkzeug.contrib.sessions import ModificationTrackingDict -from werkzeug.security import safe_str_cmp -from werkzeug._compat import to_native +from .._compat import iteritems +from .._compat import text_type +from .._compat import to_bytes +from .._compat import to_native +from .._internal import _date_to_unix +from ..contrib.sessions import ModificationTrackingDict +from ..security import safe_str_cmp +from ..urls import url_quote_plus +from ..urls import url_unquote_plus + +warnings.warn( + "'werkzeug.contrib.securecookie' is deprecated as of version 0.15" + " and will be removed in version 1.0. It has moved to" + " https://github.com/pallets/secure-cookie.", + DeprecationWarning, + stacklevel=2, +) class UnquoteError(Exception): - """Internal exception used to signal failures on quoting.""" class SecureCookie(ModificationTrackingDict): - """Represents a secure cookie. You can subclass this class and provide an alternative mac method. The import thing is that the mac method is a function with a similar interface to the hashlib. Required @@ -139,8 +149,11 @@ class SecureCookie(ModificationTrackingDict): #: as a function. hash_method = staticmethod(_default_hash) - #: the module used for serialization. Unless overriden by subclasses - #: the standard pickle module is used. + #: The module used for serialization. Should have a ``dumps`` and a + #: ``loads`` method that takes bytes. The default is :mod:`pickle`. + #: + #: .. versionchanged:: 0.15 + #: The default of ``pickle`` will change to :mod:`json` in 1.0. serialization_method = pickle #: if the contents should be base64 quoted. This can be disabled if the @@ -152,15 +165,24 @@ class SecureCookie(ModificationTrackingDict): # explicitly convert it into a bytestring because python 2.6 # no longer performs an implicit string conversion on hmac if secret_key is not None: - secret_key = bytes(secret_key) + secret_key = to_bytes(secret_key, "utf-8") self.secret_key = secret_key self.new = new + if self.serialization_method is pickle: + warnings.warn( + "The default 'SecureCookie.serialization_method' will" + " change from pickle to json in version 1.0. To upgrade" + " existing tokens, override 'unquote' to try pickle if" + " json fails.", + stacklevel=2, + ) + def __repr__(self): - return '<%s %s%s>' % ( + return "<%s %s%s>" % ( self.__class__.__name__, dict.__repr__(self), - self.should_save and '*' or '' + "*" if self.should_save else "", ) @property @@ -180,7 +202,9 @@ class SecureCookie(ModificationTrackingDict): if cls.serialization_method is not None: value = cls.serialization_method.dumps(value) if cls.quote_base64: - value = b''.join(base64.b64encode(value).splitlines()).strip() + value = b"".join( + base64.b64encode(to_bytes(value, "utf8")).splitlines() + ).strip() return value @classmethod @@ -213,21 +237,19 @@ class SecureCookie(ModificationTrackingDict): :class:`datetime.datetime` object) """ if self.secret_key is None: - raise RuntimeError('no secret key defined') + raise RuntimeError("no secret key defined") if expires: - self['_expires'] = _date_to_unix(expires) + self["_expires"] = _date_to_unix(expires) result = [] mac = hmac(self.secret_key, None, self.hash_method) for key, value in sorted(self.items()): - result.append(('%s=%s' % ( - url_quote_plus(key), - self.quote(value).decode('ascii') - )).encode('ascii')) - mac.update(b'|' + result[-1]) - return b'?'.join([ - base64.b64encode(mac.digest()).strip(), - b'&'.join(result) - ]) + result.append( + ( + "%s=%s" % (url_quote_plus(key), self.quote(value).decode("ascii")) + ).encode("ascii") + ) + mac.update(b"|" + result[-1]) + return b"?".join([base64.b64encode(mac.digest()).strip(), b"&".join(result)]) @classmethod def unserialize(cls, string, secret_key): @@ -238,24 +260,24 @@ class SecureCookie(ModificationTrackingDict): :return: a new :class:`SecureCookie`. """ if isinstance(string, text_type): - string = string.encode('utf-8', 'replace') + string = string.encode("utf-8", "replace") if isinstance(secret_key, text_type): - secret_key = secret_key.encode('utf-8', 'replace') + secret_key = secret_key.encode("utf-8", "replace") try: - base64_hash, data = string.split(b'?', 1) + base64_hash, data = string.split(b"?", 1) except (ValueError, IndexError): items = () else: items = {} mac = hmac(secret_key, None, cls.hash_method) - for item in data.split(b'&'): - mac.update(b'|' + item) - if b'=' not in item: + for item in data.split(b"&"): + mac.update(b"|" + item) + if b"=" not in item: items = None break - key, value = item.split(b'=', 1) + key, value = item.split(b"=", 1) # try to make the key a string - key = url_unquote_plus(key.decode('ascii')) + key = url_unquote_plus(key.decode("ascii")) try: key = to_native(key) except UnicodeError: @@ -275,17 +297,17 @@ class SecureCookie(ModificationTrackingDict): except UnquoteError: items = () else: - if '_expires' in items: - if time() > items['_expires']: + if "_expires" in items: + if time() > items["_expires"]: items = () else: - del items['_expires'] + del items["_expires"] else: items = () return cls(items, secret_key, False) @classmethod - def load_cookie(cls, request, key='session', secret_key=None): + def load_cookie(cls, request, key="session", secret_key=None): """Loads a :class:`SecureCookie` from a cookie in request. If the cookie is not set, a new :class:`SecureCookie` instanced is returned. @@ -302,9 +324,19 @@ class SecureCookie(ModificationTrackingDict): return cls(secret_key=secret_key) return cls.unserialize(data, secret_key) - def save_cookie(self, response, key='session', expires=None, - session_expires=None, max_age=None, path='/', domain=None, - secure=None, httponly=False, force=False): + def save_cookie( + self, + response, + key="session", + expires=None, + session_expires=None, + max_age=None, + path="/", + domain=None, + secure=None, + httponly=False, + force=False, + ): """Saves the SecureCookie in a cookie on response object. All parameters that are not described here are forwarded directly to :meth:`~BaseResponse.set_cookie`. @@ -318,6 +350,13 @@ class SecureCookie(ModificationTrackingDict): """ if force or self.should_save: data = self.serialize(session_expires or expires) - response.set_cookie(key, data, expires=expires, max_age=max_age, - path=path, domain=domain, secure=secure, - httponly=httponly) + response.set_cookie( + key, + data, + expires=expires, + max_age=max_age, + path=path, + domain=domain, + secure=secure, + httponly=httponly, + ) diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/sessions.py b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/sessions.py index a9c3aa7ca..866e827c1 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/sessions.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/sessions.py @@ -48,51 +48,60 @@ r""" response.set_cookie('cookie_name', request.session.sid) return response(environ, start_response) - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. + :copyright: 2007 Pallets + :license: BSD-3-Clause """ -import re import os +import re import tempfile +import warnings +from hashlib import sha1 from os import path -from time import time +from pickle import dump +from pickle import HIGHEST_PROTOCOL +from pickle import load from random import random -from hashlib import sha1 -from pickle import dump, load, HIGHEST_PROTOCOL +from time import time -from werkzeug.datastructures import CallbackDict -from werkzeug.utils import dump_cookie, parse_cookie -from werkzeug.wsgi import ClosingIterator -from werkzeug.posixemulation import rename -from werkzeug._compat import PY2, text_type -from werkzeug.filesystem import get_filesystem_encoding +from .._compat import PY2 +from .._compat import text_type +from ..datastructures import CallbackDict +from ..filesystem import get_filesystem_encoding +from ..posixemulation import rename +from ..utils import dump_cookie +from ..utils import parse_cookie +from ..wsgi import ClosingIterator +warnings.warn( + "'werkzeug.contrib.sessions' is deprecated as of version 0.15 and" + " will be removed in version 1.0. It has moved to" + " https://github.com/pallets/secure-cookie.", + DeprecationWarning, + stacklevel=2, +) -_sha1_re = re.compile(r'^[a-f0-9]{40}$') +_sha1_re = re.compile(r"^[a-f0-9]{40}$") def _urandom(): - if hasattr(os, 'urandom'): + if hasattr(os, "urandom"): return os.urandom(30) - return text_type(random()).encode('ascii') + return text_type(random()).encode("ascii") def generate_key(salt=None): if salt is None: - salt = repr(salt).encode('ascii') - return sha1(b''.join([ - salt, - str(time()).encode('ascii'), - _urandom() - ])).hexdigest() + salt = repr(salt).encode("ascii") + return sha1(b"".join([salt, str(time()).encode("ascii"), _urandom()])).hexdigest() class ModificationTrackingDict(CallbackDict): - __slots__ = ('modified',) + __slots__ = ("modified",) def __init__(self, *args, **kwargs): def on_update(self): self.modified = True + self.modified = False CallbackDict.__init__(self, on_update=on_update) dict.update(self, *args, **kwargs) @@ -112,12 +121,12 @@ class ModificationTrackingDict(CallbackDict): class Session(ModificationTrackingDict): - """Subclass of a dict that keeps track of direct object changes. Changes in mutable structures are not tracked, for those you have to set `modified` to `True` by hand. """ - __slots__ = ModificationTrackingDict.__slots__ + ('sid', 'new') + + __slots__ = ModificationTrackingDict.__slots__ + ("sid", "new") def __init__(self, data, sid, new=False): ModificationTrackingDict.__init__(self, data) @@ -125,10 +134,10 @@ class Session(ModificationTrackingDict): self.new = new def __repr__(self): - return '<%s %s%s>' % ( + return "<%s %s%s>" % ( self.__class__.__name__, dict.__repr__(self), - self.should_save and '*' or '' + "*" if self.should_save else "", ) @property @@ -143,7 +152,6 @@ class Session(ModificationTrackingDict): class SessionStore(object): - """Baseclass for all session stores. The Werkzeug contrib module does not implement any useful stores besides the filesystem store, application developers are encouraged to create their own stores. @@ -189,11 +197,10 @@ class SessionStore(object): #: used for temporary files by the filesystem session store -_fs_transaction_suffix = '.__wz_sess' +_fs_transaction_suffix = ".__wz_sess" class FilesystemSessionStore(SessionStore): - """Simple example session store that saves sessions on the filesystem. This store works best on POSIX systems and Windows Vista / Windows Server 2008 and newer. @@ -215,17 +222,23 @@ class FilesystemSessionStore(SessionStore): not yet saved. """ - def __init__(self, path=None, filename_template='werkzeug_%s.sess', - session_class=None, renew_missing=False, mode=0o644): + def __init__( + self, + path=None, + filename_template="werkzeug_%s.sess", + session_class=None, + renew_missing=False, + mode=0o644, + ): SessionStore.__init__(self, session_class) if path is None: path = tempfile.gettempdir() self.path = path if isinstance(filename_template, text_type) and PY2: - filename_template = filename_template.encode( - get_filesystem_encoding()) - assert not filename_template.endswith(_fs_transaction_suffix), \ - 'filename templates may not end with %s' % _fs_transaction_suffix + filename_template = filename_template.encode(get_filesystem_encoding()) + assert not filename_template.endswith(_fs_transaction_suffix), ( + "filename templates may not end with %s" % _fs_transaction_suffix + ) self.filename_template = filename_template self.renew_missing = renew_missing self.mode = mode @@ -240,9 +253,8 @@ class FilesystemSessionStore(SessionStore): def save(self, session): fn = self.get_session_filename(session.sid) - fd, tmp = tempfile.mkstemp(suffix=_fs_transaction_suffix, - dir=self.path) - f = os.fdopen(fd, 'wb') + fd, tmp = tempfile.mkstemp(suffix=_fs_transaction_suffix, dir=self.path) + f = os.fdopen(fd, "wb") try: dump(dict(session), f, HIGHEST_PROTOCOL) finally: @@ -264,7 +276,7 @@ class FilesystemSessionStore(SessionStore): if not self.is_valid_key(sid): return self.new() try: - f = open(self.get_session_filename(sid), 'rb') + f = open(self.get_session_filename(sid), "rb") except IOError: if self.renew_missing: return self.new() @@ -284,9 +296,10 @@ class FilesystemSessionStore(SessionStore): .. versionadded:: 0.6 """ - before, after = self.filename_template.split('%s', 1) - filename_re = re.compile(r'%s(.{5,})%s$' % (re.escape(before), - re.escape(after))) + before, after = self.filename_template.split("%s", 1) + filename_re = re.compile( + r"%s(.{5,})%s$" % (re.escape(before), re.escape(after)) + ) result = [] for filename in os.listdir(self.path): #: this is a session that is still being saved. @@ -299,7 +312,6 @@ class FilesystemSessionStore(SessionStore): class SessionMiddleware(object): - """A simple middleware that puts the session object of a store provided into the WSGI environ. It automatically sets cookies and restores sessions. @@ -315,10 +327,20 @@ class SessionMiddleware(object): compatibility. """ - def __init__(self, app, store, cookie_name='session_id', - cookie_age=None, cookie_expires=None, cookie_path='/', - cookie_domain=None, cookie_secure=None, - cookie_httponly=False, environ_key='werkzeug.session'): + def __init__( + self, + app, + store, + cookie_name="session_id", + cookie_age=None, + cookie_expires=None, + cookie_path="/", + cookie_domain=None, + cookie_secure=None, + cookie_httponly=False, + cookie_samesite="Lax", + environ_key="werkzeug.session", + ): self.app = app self.store = store self.cookie_name = cookie_name @@ -328,10 +350,11 @@ class SessionMiddleware(object): self.cookie_domain = cookie_domain self.cookie_secure = cookie_secure self.cookie_httponly = cookie_httponly + self.cookie_samesite = cookie_samesite self.environ_key = environ_key def __call__(self, environ, start_response): - cookie = parse_cookie(environ.get('HTTP_COOKIE', '')) + cookie = parse_cookie(environ.get("HTTP_COOKIE", "")) sid = cookie.get(self.cookie_name, None) if sid is None: session = self.store.new() @@ -342,11 +365,25 @@ class SessionMiddleware(object): def injecting_start_response(status, headers, exc_info=None): if session.should_save: self.store.save(session) - headers.append(('Set-Cookie', dump_cookie(self.cookie_name, - session.sid, self.cookie_age, - self.cookie_expires, self.cookie_path, - self.cookie_domain, self.cookie_secure, - self.cookie_httponly))) + headers.append( + ( + "Set-Cookie", + dump_cookie( + self.cookie_name, + session.sid, + self.cookie_age, + self.cookie_expires, + self.cookie_path, + self.cookie_domain, + self.cookie_secure, + self.cookie_httponly, + samesite=self.cookie_samesite, + ), + ) + ) return start_response(status, headers, exc_info) - return ClosingIterator(self.app(environ, injecting_start_response), - lambda: self.store.save_if_modified(session)) + + return ClosingIterator( + self.app(environ, injecting_start_response), + lambda: self.store.save_if_modified(session), + ) diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/testtools.py b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/testtools.py deleted file mode 100644 index 3dab6dbde..000000000 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/testtools.py +++ /dev/null @@ -1,73 +0,0 @@ -# -*- coding: utf-8 -*- -""" - werkzeug.contrib.testtools - ~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This module implements extended wrappers for simplified testing. - - `TestResponse` - A response wrapper which adds various cached attributes for - simplified assertions on various content types. - - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. -""" -from werkzeug.utils import cached_property, import_string -from werkzeug.wrappers import Response - -from warnings import warn -warn(DeprecationWarning('werkzeug.contrib.testtools is deprecated and ' - 'will be removed with Werkzeug 1.0')) - - -class ContentAccessors(object): - - """ - A mixin class for response objects that provides a couple of useful - accessors for unittesting. - """ - - def xml(self): - """Get an etree if possible.""" - if 'xml' not in self.mimetype: - raise AttributeError( - 'Not a XML response (Content-Type: %s)' - % self.mimetype) - for module in ['xml.etree.ElementTree', 'ElementTree', - 'elementtree.ElementTree']: - etree = import_string(module, silent=True) - if etree is not None: - return etree.XML(self.body) - raise RuntimeError('You must have ElementTree installed ' - 'to use TestResponse.xml') - xml = cached_property(xml) - - def lxml(self): - """Get an lxml etree if possible.""" - if ('html' not in self.mimetype and 'xml' not in self.mimetype): - raise AttributeError('Not an HTML/XML response') - from lxml import etree - try: - from lxml.html import fromstring - except ImportError: - fromstring = etree.HTML - if self.mimetype == 'text/html': - return fromstring(self.data) - return etree.XML(self.data) - lxml = cached_property(lxml) - - def json(self): - """Get the result of simplejson.loads if possible.""" - if 'json' not in self.mimetype: - raise AttributeError('Not a JSON response') - try: - from simplejson import loads - except ImportError: - from json import loads - return loads(self.data) - json = cached_property(json) - - -class TestResponse(Response, ContentAccessors): - - """Pass this to `werkzeug.test.Client` for easier unittesting.""" diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/wrappers.py b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/wrappers.py index 6c8645291..49b82a71e 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/wrappers.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/contrib/wrappers.py @@ -17,19 +17,18 @@ Afterwards this request object provides the extra functionality of the :class:`JSONRequestMixin`. - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. + :copyright: 2007 Pallets + :license: BSD-3-Clause """ import codecs -try: - from simplejson import loads -except ImportError: - from json import loads +import warnings -from werkzeug.exceptions import BadRequest -from werkzeug.utils import cached_property -from werkzeug.http import dump_options_header, parse_options_header -from werkzeug._compat import wsgi_decoding_dance +from .._compat import wsgi_decoding_dance +from ..exceptions import BadRequest +from ..http import dump_options_header +from ..http import parse_options_header +from ..utils import cached_property +from ..wrappers.json import JSONMixin as _JSONMixin def is_known_charset(charset): @@ -41,24 +40,23 @@ def is_known_charset(charset): return True -class JSONRequestMixin(object): - - """Add json method to a request object. This will parse the input data - through simplejson if possible. - - :exc:`~werkzeug.exceptions.BadRequest` will be raised if the content-type - is not json or if the data itself cannot be parsed as json. +class JSONRequestMixin(_JSONMixin): + """ + .. deprecated:: 0.15 + Moved to :class:`werkzeug.wrappers.json.JSONMixin`. This old + import will be removed in version 1.0. """ - @cached_property + @property def json(self): - """Get the result of simplejson.loads if possible.""" - if 'json' not in self.environ.get('CONTENT_TYPE', ''): - raise BadRequest('Not a JSON request') - try: - return loads(self.data.decode(self.charset, self.encoding_errors)) - except Exception: - raise BadRequest('Unable to read JSON request') + warnings.warn( + "'werkzeug.contrib.wrappers.JSONRequestMixin' has moved to" + " 'werkzeug.wrappers.json.JSONMixin'. This old import will" + " be removed in version 1.0.", + DeprecationWarning, + stacklevel=2, + ) + return super(JSONRequestMixin, self).json class ProtobufRequestMixin(object): @@ -69,7 +67,10 @@ class ProtobufRequestMixin(object): :exc:`~werkzeug.exceptions.BadRequest` will be raised if the content-type is not protobuf or if the data itself cannot be parsed property. - .. _protobuf: http://code.google.com/p/protobuf/ + .. _protobuf: https://github.com/protocolbuffers/protobuf + + .. deprecated:: 0.15 + This mixin will be removed in version 1.0. """ #: by default the :class:`ProtobufRequestMixin` will raise a @@ -80,8 +81,15 @@ class ProtobufRequestMixin(object): def parse_protobuf(self, proto_type): """Parse the data into an instance of proto_type.""" - if 'protobuf' not in self.environ.get('CONTENT_TYPE', ''): - raise BadRequest('Not a Protobuf request') + warnings.warn( + "'werkzeug.contrib.wrappers.ProtobufRequestMixin' is" + " deprecated as of version 0.15 and will be removed in" + " version 1.0.", + DeprecationWarning, + stacklevel=2, + ) + if "protobuf" not in self.environ.get("CONTENT_TYPE", ""): + raise BadRequest("Not a Protobuf request") obj = proto_type() try: @@ -101,25 +109,56 @@ class RoutingArgsRequestMixin(object): """This request mixin adds support for the wsgiorg routing args `specification`_. - .. _specification: https://wsgi.readthedocs.io/en/latest/specifications/routing_args.html + .. _specification: https://wsgi.readthedocs.io/en/latest/ + specifications/routing_args.html + + .. deprecated:: 0.15 + This mixin will be removed in version 1.0. """ def _get_routing_args(self): - return self.environ.get('wsgiorg.routing_args', (()))[0] + warnings.warn( + "'werkzeug.contrib.wrappers.RoutingArgsRequestMixin' is" + " deprecated as of version 0.15 and will be removed in" + " version 1.0.", + DeprecationWarning, + stacklevel=2, + ) + return self.environ.get("wsgiorg.routing_args", (()))[0] def _set_routing_args(self, value): + warnings.warn( + "'werkzeug.contrib.wrappers.RoutingArgsRequestMixin' is" + " deprecated as of version 0.15 and will be removed in" + " version 1.0.", + DeprecationWarning, + stacklevel=2, + ) if self.shallow: - raise RuntimeError('A shallow request tried to modify the WSGI ' - 'environment. If you really want to do that, ' - 'set `shallow` to False.') - self.environ['wsgiorg.routing_args'] = (value, self.routing_vars) - - routing_args = property(_get_routing_args, _set_routing_args, doc=''' - The positional URL arguments as `tuple`.''') + raise RuntimeError( + "A shallow request tried to modify the WSGI " + "environment. If you really want to do that, " + "set `shallow` to False." + ) + self.environ["wsgiorg.routing_args"] = (value, self.routing_vars) + + routing_args = property( + _get_routing_args, + _set_routing_args, + doc=""" + The positional URL arguments as `tuple`.""", + ) del _get_routing_args, _set_routing_args def _get_routing_vars(self): - rv = self.environ.get('wsgiorg.routing_args') + warnings.warn( + "'werkzeug.contrib.wrappers.RoutingArgsRequestMixin' is" + " deprecated as of version 0.15 and will be removed in" + " version 1.0.", + DeprecationWarning, + stacklevel=2, + ) + rv = self.environ.get("wsgiorg.routing_args") if rv is not None: return rv[1] rv = {} @@ -128,14 +167,27 @@ class RoutingArgsRequestMixin(object): return rv def _set_routing_vars(self, value): + warnings.warn( + "'werkzeug.contrib.wrappers.RoutingArgsRequestMixin' is" + " deprecated as of version 0.15 and will be removed in" + " version 1.0.", + DeprecationWarning, + stacklevel=2, + ) if self.shallow: - raise RuntimeError('A shallow request tried to modify the WSGI ' - 'environment. If you really want to do that, ' - 'set `shallow` to False.') - self.environ['wsgiorg.routing_args'] = (self.routing_args, value) - - routing_vars = property(_get_routing_vars, _set_routing_vars, doc=''' - The keyword URL arguments as `dict`.''') + raise RuntimeError( + "A shallow request tried to modify the WSGI " + "environment. If you really want to do that, " + "set `shallow` to False." + ) + self.environ["wsgiorg.routing_args"] = (self.routing_args, value) + + routing_vars = property( + _get_routing_vars, + _set_routing_vars, + doc=""" + The keyword URL arguments as `dict`.""", + ) del _get_routing_vars, _set_routing_vars @@ -161,6 +213,9 @@ class ReverseSlashBehaviorRequestMixin(object): +---------------+-------------------+---------------------+ | `path` | ``/foo/bar`` | ``foo/bar`` | +---------------+-------------------+---------------------+ + + .. deprecated:: 0.15 + This mixin will be removed in version 1.0. """ @cached_property @@ -168,16 +223,32 @@ class ReverseSlashBehaviorRequestMixin(object): """Requested path as unicode. This works a bit like the regular path info in the WSGI environment but will not include a leading slash. """ - path = wsgi_decoding_dance(self.environ.get('PATH_INFO') or '', - self.charset, self.encoding_errors) - return path.lstrip('/') + warnings.warn( + "'werkzeug.contrib.wrappers.ReverseSlashBehaviorRequestMixin'" + " is deprecated as of version 0.15 and will be removed in" + " version 1.0.", + DeprecationWarning, + stacklevel=2, + ) + path = wsgi_decoding_dance( + self.environ.get("PATH_INFO") or "", self.charset, self.encoding_errors + ) + return path.lstrip("/") @cached_property def script_root(self): """The root path of the script includling a trailing slash.""" - path = wsgi_decoding_dance(self.environ.get('SCRIPT_NAME') or '', - self.charset, self.encoding_errors) - return path.rstrip('/') + '/' + warnings.warn( + "'werkzeug.contrib.wrappers.ReverseSlashBehaviorRequestMixin'" + " is deprecated as of version 0.15 and will be removed in" + " version 1.0.", + DeprecationWarning, + stacklevel=2, + ) + path = wsgi_decoding_dance( + self.environ.get("SCRIPT_NAME") or "", self.charset, self.encoding_errors + ) + return path.rstrip("/") + "/" class DynamicCharsetRequestMixin(object): @@ -202,6 +273,9 @@ class DynamicCharsetRequestMixin(object): class MyRequest(DynamicCharsetRequestMixin, Request): url_charset = 'utf-8' + .. deprecated:: 0.15 + This mixin will be removed in version 1.0. + .. versionadded:: 0.6 """ @@ -210,7 +284,7 @@ class DynamicCharsetRequestMixin(object): #: is latin1 which is what HTTP specifies as default charset. #: You may however want to set this to utf-8 to better support #: browsers that do not transmit a charset for incoming data. - default_charset = 'latin1' + default_charset = "latin1" def unknown_charset(self, charset): """Called if a charset was provided but is not supported by @@ -221,15 +295,22 @@ class DynamicCharsetRequestMixin(object): :param charset: the charset that was not found. :return: the replacement charset. """ - return 'latin1' + return "latin1" @cached_property def charset(self): """The charset from the content type.""" - header = self.environ.get('CONTENT_TYPE') + warnings.warn( + "'werkzeug.contrib.wrappers.DynamicCharsetRequestMixin'" + " is deprecated as of version 0.15 and will be removed in" + " version 1.0.", + DeprecationWarning, + stacklevel=2, + ) + header = self.environ.get("CONTENT_TYPE") if header: ct, options = parse_options_header(header) - charset = options.get('charset') + charset = options.get("charset") if charset: if is_known_charset(charset): return charset @@ -255,30 +336,50 @@ class DynamicCharsetResponseMixin(object): class MyResponse(DynamicCharsetResponseMixin, Response): pass + .. deprecated:: 0.15 + This mixin will be removed in version 1.0. + .. versionadded:: 0.6 """ #: the default charset. - default_charset = 'utf-8' + default_charset = "utf-8" def _get_charset(self): - header = self.headers.get('content-type') + warnings.warn( + "'werkzeug.contrib.wrappers.DynamicCharsetResponseMixin'" + " is deprecated as of version 0.15 and will be removed in" + " version 1.0.", + DeprecationWarning, + stacklevel=2, + ) + header = self.headers.get("content-type") if header: - charset = parse_options_header(header)[1].get('charset') + charset = parse_options_header(header)[1].get("charset") if charset: return charset return self.default_charset def _set_charset(self, charset): - header = self.headers.get('content-type') + warnings.warn( + "'werkzeug.contrib.wrappers.DynamicCharsetResponseMixin'" + " is deprecated as of version 0.15 and will be removed in" + " version 1.0.", + DeprecationWarning, + stacklevel=2, + ) + header = self.headers.get("content-type") ct, options = parse_options_header(header) if not ct: - raise TypeError('Cannot set charset if Content-Type ' - 'header is missing.') - options['charset'] = charset - self.headers['Content-Type'] = dump_options_header(ct, options) - - charset = property(_get_charset, _set_charset, doc=""" + raise TypeError("Cannot set charset if Content-Type header is missing.") + options["charset"] = charset + self.headers["Content-Type"] = dump_options_header(ct, options) + + charset = property( + _get_charset, + _set_charset, + doc=""" The charset for the response. It's stored inside the - Content-Type header as a parameter.""") + Content-Type header as a parameter.""", + ) del _get_charset, _set_charset diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/datastructures.py b/backend/venv/lib/python3.7/site-packages/werkzeug/datastructures.py index ee620e935..9643db96c 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/datastructures.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/datastructures.py @@ -5,28 +5,35 @@ This module provides mixins and classes with an immutable interface. - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. + :copyright: 2007 Pallets + :license: BSD-3-Clause """ -import re import codecs import mimetypes +import re from copy import deepcopy from itertools import repeat -from collections import Container, Iterable, MutableSet -from werkzeug._internal import _missing, _empty_stream -from werkzeug._compat import iterkeys, itervalues, iteritems, iterlists, \ - PY2, text_type, integer_types, string_types, make_literal_wrapper, \ - to_native -from werkzeug.filesystem import get_filesystem_encoding +from ._compat import BytesIO +from ._compat import collections_abc +from ._compat import integer_types +from ._compat import iteritems +from ._compat import iterkeys +from ._compat import iterlists +from ._compat import itervalues +from ._compat import make_literal_wrapper +from ._compat import PY2 +from ._compat import string_types +from ._compat import text_type +from ._compat import to_native +from ._internal import _missing +from .filesystem import get_filesystem_encoding - -_locale_delim_re = re.compile(r'[_-]') +_locale_delim_re = re.compile(r"[_-]") def is_immutable(self): - raise TypeError('%r objects are immutable' % self.__class__.__name__) + raise TypeError("%r objects are immutable" % self.__class__.__name__) def iter_multi_items(mapping): @@ -53,18 +60,28 @@ def native_itermethods(names): return lambda x: x def setviewmethod(cls, name): - viewmethod_name = 'view%s' % name - viewmethod = lambda self, *a, **kw: ViewItems(self, name, 'view_%s' % name, *a, **kw) - viewmethod.__doc__ = \ - '"""`%s()` object providing a view on %s"""' % (viewmethod_name, name) + viewmethod_name = "view%s" % name + repr_name = "view_%s" % name + + def viewmethod(self, *a, **kw): + return ViewItems(self, name, repr_name, *a, **kw) + + viewmethod.__name__ = viewmethod_name + viewmethod.__doc__ = "`%s()` object providing a view on %s" % ( + viewmethod_name, + name, + ) setattr(cls, viewmethod_name, viewmethod) def setitermethod(cls, name): itermethod = getattr(cls, name) - setattr(cls, 'iter%s' % name, itermethod) - listmethod = lambda self, *a, **kw: list(itermethod(self, *a, **kw)) - listmethod.__doc__ = \ - 'Like :py:meth:`iter%s`, but returns a list.' % name + setattr(cls, "iter%s" % name, itermethod) + + def listmethod(self, *a, **kw): + return list(itermethod(self, *a, **kw)) + + listmethod.__name__ = name + listmethod.__doc__ = "Like :py:meth:`iter%s`, but returns a list." % name setattr(cls, name, listmethod) def wrap(cls): @@ -72,11 +89,11 @@ def native_itermethods(names): setitermethod(cls, name) setviewmethod(cls, name) return cls + return wrap class ImmutableListMixin(object): - """Makes a :class:`list` immutable. .. versionadded:: 0.5 @@ -100,6 +117,7 @@ class ImmutableListMixin(object): def __iadd__(self, other): is_immutable(self) + __imul__ = __iadd__ def __setitem__(self, key, value): @@ -107,6 +125,7 @@ class ImmutableListMixin(object): def append(self, item): is_immutable(self) + remove = append def extend(self, iterable): @@ -126,7 +145,6 @@ class ImmutableListMixin(object): class ImmutableList(ImmutableListMixin, list): - """An immutable :class:`list`. .. versionadded:: 0.5 @@ -135,20 +153,17 @@ class ImmutableList(ImmutableListMixin, list): """ def __repr__(self): - return '%s(%s)' % ( - self.__class__.__name__, - list.__repr__(self), - ) + return "%s(%s)" % (self.__class__.__name__, list.__repr__(self)) class ImmutableDictMixin(object): - """Makes a :class:`dict` immutable. .. versionadded:: 0.5 :private: """ + _hash_cache = None @classmethod @@ -192,7 +207,6 @@ class ImmutableDictMixin(object): class ImmutableMultiDictMixin(ImmutableDictMixin): - """Makes a :class:`MultiDict` immutable. .. versionadded:: 0.5 @@ -223,7 +237,6 @@ class ImmutableMultiDictMixin(ImmutableDictMixin): class UpdateDictMixin(object): - """Makes dicts call `self.on_update` on modifications. .. versionadded:: 0.5 @@ -233,12 +246,13 @@ class UpdateDictMixin(object): on_update = None - def calls_update(name): + def calls_update(name): # noqa: B902 def oncall(self, *args, **kw): rv = getattr(super(UpdateDictMixin, self), name)(*args, **kw) if self.on_update is not None: self.on_update(self) return rv + oncall.__name__ = name return oncall @@ -259,16 +273,15 @@ class UpdateDictMixin(object): self.on_update(self) return rv - __setitem__ = calls_update('__setitem__') - __delitem__ = calls_update('__delitem__') - clear = calls_update('clear') - popitem = calls_update('popitem') - update = calls_update('update') + __setitem__ = calls_update("__setitem__") + __delitem__ = calls_update("__delitem__") + clear = calls_update("clear") + popitem = calls_update("popitem") + update = calls_update("update") del calls_update class TypeConversionDict(dict): - """Works like a regular dict but the :meth:`get` method can perform type conversions. :class:`MultiDict` and :class:`CombinedMultiDict` are subclasses of this class and provide the same feature. @@ -299,15 +312,17 @@ class TypeConversionDict(dict): """ try: rv = self[key] - if type is not None: + except KeyError: + return default + if type is not None: + try: rv = type(rv) - except (KeyError, ValueError): - rv = default + except ValueError: + rv = default return rv class ImmutableTypeConversionDict(ImmutableDictMixin, TypeConversionDict): - """Works like a :class:`TypeConversionDict` but does not support modifications. @@ -326,7 +341,6 @@ class ImmutableTypeConversionDict(ImmutableDictMixin, TypeConversionDict): class ViewItems(object): - def __init__(self, multi_dict, method, repr_name, *a, **kw): self.__multi_dict = multi_dict self.__method = method @@ -338,15 +352,14 @@ class ViewItems(object): return getattr(self.__multi_dict, self.__method)(*self.__a, **self.__kw) def __repr__(self): - return '%s(%r)' % (self.__repr_name, list(self.__get_items())) + return "%s(%r)" % (self.__repr_name, list(self.__get_items())) def __iter__(self): return iter(self.__get_items()) -@native_itermethods(['keys', 'values', 'items', 'lists', 'listvalues']) +@native_itermethods(["keys", "values", "items", "lists", "listvalues"]) class MultiDict(TypeConversionDict): - """A :class:`MultiDict` is a dictionary subclass customized to deal with multiple values for the same key which is for example used by the parsing functions in the wrappers. This is necessary because some HTML form @@ -421,6 +434,7 @@ class MultiDict(TypeConversionDict): :param key: The key to be looked up. :raise KeyError: if the key does not exist. """ + if key in self: lst = dict.__getitem__(self, key) if len(lst) > 0: @@ -515,9 +529,9 @@ class MultiDict(TypeConversionDict): [1, 2, 3] :param key: The key to be looked up. - :param default: An iterable of default values. It is either copied - (in case it was a list) or converted into a list - before returned. + :param default_list: An iterable of default values. It is either copied + (in case it was a list) or converted into a list + before returned. :return: a :class:`list` """ if key not in self: @@ -543,7 +557,7 @@ class MultiDict(TypeConversionDict): yield key, values[0] def lists(self): - """Return a list of ``(key, values)`` pairs, where values is the list + """Return a iterator of ``(key, values)`` pairs, where values is the list of all values associated with the key.""" for key, values in iteritems(dict, self): @@ -631,13 +645,13 @@ class MultiDict(TypeConversionDict): lst = dict.pop(self, key) if len(lst) == 0: - raise exceptions.BadRequestKeyError() + raise exceptions.BadRequestKeyError(key) return lst[0] - except KeyError as e: + except KeyError: if default is not _missing: return default - raise exceptions.BadRequestKeyError(str(e)) + raise exceptions.BadRequestKeyError(key) def popitem(self): """Pop an item from the dict.""" @@ -645,11 +659,11 @@ class MultiDict(TypeConversionDict): item = dict.popitem(self) if len(item[1]) == 0: - raise exceptions.BadRequestKeyError() + raise exceptions.BadRequestKeyError(item) return (item[0], item[1][0]) except KeyError as e: - raise exceptions.BadRequestKeyError(str(e)) + raise exceptions.BadRequestKeyError(e.args[0]) def poplist(self, key): """Pop the list for a key from the dict. If the key is not in the dict @@ -666,7 +680,7 @@ class MultiDict(TypeConversionDict): try: return dict.popitem(self) except KeyError as e: - raise exceptions.BadRequestKeyError(str(e)) + raise exceptions.BadRequestKeyError(e.args[0]) def __copy__(self): return self.copy() @@ -675,17 +689,17 @@ class MultiDict(TypeConversionDict): return self.deepcopy(memo=memo) def __repr__(self): - return '%s(%r)' % (self.__class__.__name__, list(iteritems(self, multi=True))) + return "%s(%r)" % (self.__class__.__name__, list(iteritems(self, multi=True))) class _omd_bucket(object): - """Wraps values in the :class:`OrderedMultiDict`. This makes it possible to keep an order over multiple different keys. It requires a lot of extra memory and slows down access a lot, but makes it possible to access elements in O(1) and iterate in O(n). """ - __slots__ = ('prev', 'key', 'value', 'next') + + __slots__ = ("prev", "key", "value", "next") def __init__(self, omd, key, value): self.prev = omd._last_bucket @@ -710,9 +724,8 @@ class _omd_bucket(object): omd._last_bucket = self.prev -@native_itermethods(['keys', 'values', 'items', 'lists', 'listvalues']) +@native_itermethods(["keys", "values", "items", "lists", "listvalues"]) class OrderedMultiDict(MultiDict): - """Works like a regular :class:`MultiDict` but preserves the order of the fields. To convert the ordered multi dict into a list you can use the :meth:`items` method and pass it ``multi=True``. @@ -819,7 +832,7 @@ class OrderedMultiDict(MultiDict): ptr = ptr.next def listvalues(self): - for key, values in iterlists(self): + for _key, values in iterlists(self): yield values def add(self, key, value): @@ -846,8 +859,7 @@ class OrderedMultiDict(MultiDict): self.add(key, value) def setlistdefault(self, key, default_list=None): - raise TypeError('setlistdefault is unsupported for ' - 'ordered multi dicts') + raise TypeError("setlistdefault is unsupported for ordered multi dicts") def update(self, mapping): for key, value in iter_multi_items(mapping): @@ -862,10 +874,10 @@ class OrderedMultiDict(MultiDict): def pop(self, key, default=_missing): try: buckets = dict.pop(self, key) - except KeyError as e: + except KeyError: if default is not _missing: return default - raise exceptions.BadRequestKeyError(str(e)) + raise exceptions.BadRequestKeyError(key) for bucket in buckets: bucket.unlink(self) return buckets[0].value @@ -874,7 +886,7 @@ class OrderedMultiDict(MultiDict): try: key, buckets = dict.popitem(self) except KeyError as e: - raise exceptions.BadRequestKeyError(str(e)) + raise exceptions.BadRequestKeyError(e.args[0]) for bucket in buckets: bucket.unlink(self) return key, buckets[0].value @@ -883,28 +895,28 @@ class OrderedMultiDict(MultiDict): try: key, buckets = dict.popitem(self) except KeyError as e: - raise exceptions.BadRequestKeyError(str(e)) + raise exceptions.BadRequestKeyError(e.args[0]) for bucket in buckets: bucket.unlink(self) return key, [x.value for x in buckets] def _options_header_vkw(value, kw): - return dump_options_header(value, dict((k.replace('_', '-'), v) - for k, v in kw.items())) + return dump_options_header( + value, dict((k.replace("_", "-"), v) for k, v in kw.items()) + ) def _unicodify_header_value(value): if isinstance(value, bytes): - value = value.decode('latin-1') + value = value.decode("latin-1") if not isinstance(value, text_type): value = text_type(value) return value -@native_itermethods(['keys', 'values', 'items']) +@native_itermethods(["keys", "values", "items"]) class Headers(object): - """An object that stores some headers. It has a dict-like interface but is ordered and can store the same keys multiple times. @@ -965,8 +977,7 @@ class Headers(object): raise exceptions.BadRequestKeyError(key) def __eq__(self, other): - return other.__class__ is self.__class__ and \ - set(other._list) == set(self._list) + return other.__class__ is self.__class__ and set(other._list) == set(self._list) __hash__ = None @@ -1004,7 +1015,7 @@ class Headers(object): except KeyError: return default if as_bytes: - rv = rv.encode('latin1') + rv = rv.encode("latin1") if type is None: return rv try: @@ -1033,7 +1044,7 @@ class Headers(object): for k, v in self: if k.lower() == ikey: if as_bytes: - v = v.encode('latin1') + v = v.encode("latin1") if type is not None: try: v = type(v) @@ -1158,16 +1169,19 @@ class Headers(object): """ if kw: _value = _options_header_vkw(_value, kw) + _key = _unicodify_header_value(_key) _value = _unicodify_header_value(_value) self._validate_value(_value) self._list.append((_key, _value)) def _validate_value(self, value): if not isinstance(value, text_type): - raise TypeError('Value should be unicode.') - if u'\n' in value or u'\r' in value: - raise ValueError('Detected newline in header value. This is ' - 'a potential security problem') + raise TypeError("Value should be unicode.") + if u"\n" in value or u"\r" in value: + raise ValueError( + "Detected newline in header value. This is " + "a potential security problem" + ) def add_header(self, _key, _value, **_kw): """Add a new header tuple to the list. @@ -1198,6 +1212,7 @@ class Headers(object): """ if kw: _value = _options_header_vkw(_value, kw) + _key = _unicodify_header_value(_key) _value = _unicodify_header_value(_value) self._validate_value(_value) if not self._list: @@ -1205,7 +1220,7 @@ class Headers(object): return listiter = iter(self._list) ikey = _key.lower() - for idx, (old_key, old_value) in enumerate(listiter): + for idx, (old_key, _old_value) in enumerate(listiter): if old_key.lower() == ikey: # replace first ocurrence self._list[idx] = (_key, _value) @@ -1213,9 +1228,9 @@ class Headers(object): else: self._list.append((_key, _value)) return - self._list[idx + 1:] = [t for t in listiter if t[0].lower() != ikey] + self._list[idx + 1 :] = [t for t in listiter if t[0].lower() != ikey] - def setdefault(self, key, value): + def setdefault(self, key, default): """Returns the value for the key if it is in the dict, otherwise it returns `default` and sets that value for `key`. @@ -1225,15 +1240,18 @@ class Headers(object): """ if key in self: return self[key] - self.set(key, value) - return value + self.set(key, default) + return default def __setitem__(self, key, value): """Like :meth:`set` but also supports index/slice based setting.""" if isinstance(key, (slice, integer_types)): if isinstance(key, integer_types): value = [value] - value = [(k, _unicodify_header_value(v)) for (k, v) in value] + value = [ + (_unicodify_header_value(k), _unicodify_header_value(v)) + for (k, v) in value + ] [self._validate_value(v) for (k, v) in value] if isinstance(key, integer_types): self._list[key] = value[0] @@ -1242,11 +1260,19 @@ class Headers(object): else: self.set(key, value) - def to_list(self, charset='iso-8859-1'): - """Convert the headers into a list suitable for WSGI.""" + def to_list(self, charset="iso-8859-1"): + """Convert the headers into a list suitable for WSGI. + + .. deprecated:: 0.9 + """ from warnings import warn - warn(DeprecationWarning('Method removed, use to_wsgi_list instead'), - stacklevel=2) + + warn( + "'to_list' deprecated as of version 0.9 and will be removed" + " in version 1.0. Use 'to_wsgi_list' instead.", + DeprecationWarning, + stacklevel=2, + ) return self.to_wsgi_list() def to_wsgi_list(self): @@ -1258,7 +1284,7 @@ class Headers(object): :return: list """ if PY2: - return [(to_native(k), v.encode('latin1')) for k, v in self] + return [(to_native(k), v.encode("latin1")) for k, v in self] return list(self) def copy(self): @@ -1271,19 +1297,15 @@ class Headers(object): """Returns formatted headers suitable for HTTP transmission.""" strs = [] for key, value in self.to_wsgi_list(): - strs.append('%s: %s' % (key, value)) - strs.append('\r\n') - return '\r\n'.join(strs) + strs.append("%s: %s" % (key, value)) + strs.append("\r\n") + return "\r\n".join(strs) def __repr__(self): - return '%s(%r)' % ( - self.__class__.__name__, - list(self) - ) + return "%s(%r)" % (self.__class__.__name__, list(self)) class ImmutableHeadersMixin(object): - """Makes a :class:`Headers` immutable. We do not mark them as hashable though since the only usecase for this datastructure in Werkzeug is a view on a mutable structure. @@ -1293,15 +1315,17 @@ class ImmutableHeadersMixin(object): :private: """ - def __delitem__(self, key): + def __delitem__(self, key, **kwargs): is_immutable(self) def __setitem__(self, key, value): is_immutable(self) + set = __setitem__ def add(self, item): is_immutable(self) + remove = add_header = add def extend(self, iterable): @@ -1321,7 +1345,6 @@ class ImmutableHeadersMixin(object): class EnvironHeaders(ImmutableHeadersMixin, Headers): - """Read only version of the headers from a WSGI environment. This provides the same interface as `Headers` and is constructed from a WSGI environment. @@ -1343,10 +1366,12 @@ class EnvironHeaders(ImmutableHeadersMixin, Headers): def __getitem__(self, key, _get_mode=False): # _get_mode is a no-op for this class as there is no index but # used because get() calls it. - key = key.upper().replace('-', '_') - if key in ('CONTENT_TYPE', 'CONTENT_LENGTH'): + if not isinstance(key, string_types): + raise KeyError(key) + key = key.upper().replace("-", "_") + if key in ("CONTENT_TYPE", "CONTENT_LENGTH"): return _unicodify_header_value(self.environ[key]) - return _unicodify_header_value(self.environ['HTTP_' + key]) + return _unicodify_header_value(self.environ["HTTP_" + key]) def __len__(self): # the iter is necessary because otherwise list calls our @@ -1355,21 +1380,23 @@ class EnvironHeaders(ImmutableHeadersMixin, Headers): def __iter__(self): for key, value in iteritems(self.environ): - if key.startswith('HTTP_') and key not in \ - ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'): - yield (key[5:].replace('_', '-').title(), - _unicodify_header_value(value)) - elif key in ('CONTENT_TYPE', 'CONTENT_LENGTH'): - yield (key.replace('_', '-').title(), - _unicodify_header_value(value)) + if key.startswith("HTTP_") and key not in ( + "HTTP_CONTENT_TYPE", + "HTTP_CONTENT_LENGTH", + ): + yield ( + key[5:].replace("_", "-").title(), + _unicodify_header_value(value), + ) + elif key in ("CONTENT_TYPE", "CONTENT_LENGTH") and value: + yield (key.replace("_", "-").title(), _unicodify_header_value(value)) def copy(self): - raise TypeError('cannot create %r copies' % self.__class__.__name__) + raise TypeError("cannot create %r copies" % self.__class__.__name__) -@native_itermethods(['keys', 'values', 'items', 'lists', 'listvalues']) +@native_itermethods(["keys", "values", "items", "lists", "listvalues"]) class CombinedMultiDict(ImmutableMultiDictMixin, MultiDict): - """A read only :class:`MultiDict` that you can pass multiple :class:`MultiDict` instances as sequence and it will combine the return values of all wrapped dicts: @@ -1400,8 +1427,7 @@ class CombinedMultiDict(ImmutableMultiDictMixin, MultiDict): @classmethod def fromkeys(cls): - raise TypeError('cannot create %r instances by fromkeys' % - cls.__name__) + raise TypeError("cannot create %r instances by fromkeys" % cls.__name__) def __getitem__(self, key): for d in self.dicts: @@ -1454,7 +1480,7 @@ class CombinedMultiDict(ImmutableMultiDictMixin, MultiDict): yield key, value def values(self): - for key, value in iteritems(self): + for _key, value in iteritems(self): yield value def lists(self): @@ -1468,8 +1494,16 @@ class CombinedMultiDict(ImmutableMultiDictMixin, MultiDict): return (x[1] for x in self.lists()) def copy(self): - """Return a shallow copy of this object.""" - return self.__class__(self.dicts[:]) + """Return a shallow mutable copy of this object. + + This returns a :class:`MultiDict` representing the data at the + time of copying. The copy will no longer reflect changes to the + wrapped dicts. + + .. versionchanged:: 0.15 + Return a mutable :class:`MultiDict`. + """ + return MultiDict(self) def to_dict(self, flat=True): """Return the contents as regular dict. If `flat` is `True` the @@ -1498,11 +1532,10 @@ class CombinedMultiDict(ImmutableMultiDictMixin, MultiDict): has_key = __contains__ def __repr__(self): - return '%s(%r)' % (self.__class__.__name__, self.dicts) + return "%s(%r)" % (self.__class__.__name__, self.dicts) class FileMultiDict(MultiDict): - """A special :class:`MultiDict` that has convenience methods to add files to it. This is used for :class:`EnvironBuilder` and generally useful for unittesting. @@ -1525,27 +1558,24 @@ class FileMultiDict(MultiDict): if isinstance(file, string_types): if filename is None: filename = file - file = open(file, 'rb') + file = open(file, "rb") if filename and content_type is None: - content_type = mimetypes.guess_type(filename)[0] or \ - 'application/octet-stream' + content_type = ( + mimetypes.guess_type(filename)[0] or "application/octet-stream" + ) value = FileStorage(file, filename, name, content_type) self.add(name, value) class ImmutableDict(ImmutableDictMixin, dict): - """An immutable :class:`dict`. .. versionadded:: 0.5 """ def __repr__(self): - return '%s(%s)' % ( - self.__class__.__name__, - dict.__repr__(self), - ) + return "%s(%s)" % (self.__class__.__name__, dict.__repr__(self)) def copy(self): """Return a shallow mutable copy of this object. Keep in mind that @@ -1559,7 +1589,6 @@ class ImmutableDict(ImmutableDictMixin, dict): class ImmutableMultiDict(ImmutableMultiDictMixin, MultiDict): - """An immutable :class:`MultiDict`. .. versionadded:: 0.5 @@ -1577,7 +1606,6 @@ class ImmutableMultiDict(ImmutableMultiDictMixin, MultiDict): class ImmutableOrderedMultiDict(ImmutableMultiDictMixin, OrderedMultiDict): - """An immutable :class:`OrderedMultiDict`. .. versionadded:: 0.6 @@ -1597,11 +1625,11 @@ class ImmutableOrderedMultiDict(ImmutableMultiDictMixin, OrderedMultiDict): return self -@native_itermethods(['values']) +@native_itermethods(["values"]) class Accept(ImmutableList): - """An :class:`Accept` object is just a list subclass for lists of - ``(value, quality)`` tuples. It is automatically sorted by quality. + ``(value, quality)`` tuples. It is automatically sorted by specificity + and quality. All :class:`Accept` objects work similar to a list but provide extra functionality for working with the data. Containment checks are @@ -1637,12 +1665,20 @@ class Accept(ImmutableList): list.__init__(self, values) else: self.provided = True - values = sorted(values, key=lambda x: (x[1], x[0]), reverse=True) + values = sorted( + values, + key=lambda x: (self._specificity(x[0]), x[1], x[0]), + reverse=True, + ) list.__init__(self, values) + def _specificity(self, value): + """Returns a tuple describing the value's specificity.""" + return (value != "*",) + def _value_matches(self, value, item): """Check if a value matches a given accept item.""" - return item == '*' or item.lower() == value.lower() + return item == "*" or item.lower() == value.lower() def __getitem__(self, key): """Besides index lookup (getting item n) you can also pass it a string @@ -1666,15 +1702,15 @@ class Accept(ImmutableList): return 0 def __contains__(self, value): - for item, quality in self: + for item, _quality in self: if self._value_matches(value, item): return True return False def __repr__(self): - return '%s([%s])' % ( + return "%s([%s])" % ( self.__class__.__name__, - ', '.join('(%r, %s)' % (x, y) for x, y in self) + ", ".join("(%r, %s)" % (x, y) for x, y in self), ) def index(self, key): @@ -1687,7 +1723,7 @@ class Accept(ImmutableList): with the list API. """ if isinstance(key, string_types): - for idx, (item, quality) in enumerate(self): + for idx, (item, _quality) in enumerate(self): if self._value_matches(key, item): return idx raise ValueError(key) @@ -1713,31 +1749,43 @@ class Accept(ImmutableList): result = [] for value, quality in self: if quality != 1: - value = '%s;q=%s' % (value, quality) + value = "%s;q=%s" % (value, quality) result.append(value) - return ','.join(result) + return ",".join(result) def __str__(self): return self.to_header() + def _best_single_match(self, match): + for client_item, quality in self: + if self._value_matches(match, client_item): + # self is sorted by specificity descending, we can exit + return client_item, quality + def best_match(self, matches, default=None): """Returns the best match from a list of possible matches based - on the quality of the client. If two items have the same quality, - the one is returned that comes first. + on the specificity and quality of the client. If two items have the + same quality and specificity, the one is returned that comes first. :param matches: a list of matches to check for :param default: the value that is returned if none match """ - best_quality = -1 result = default + best_quality = -1 + best_specificity = (-1,) for server_item in matches: - for client_item, quality in self: - if quality <= best_quality: - break - if self._value_matches(server_item, client_item) \ - and quality > 0: - best_quality = quality - result = server_item + match = self._best_single_match(server_item) + if not match: + continue + client_item, quality = match + specificity = self._specificity(client_item) + if quality <= 0 or quality < best_quality: + continue + # better quality or same quality but more specific => better match + if quality > best_quality or specificity > best_specificity: + result = server_item + best_quality = quality + best_specificity = specificity return result @property @@ -1748,72 +1796,71 @@ class Accept(ImmutableList): class MIMEAccept(Accept): - """Like :class:`Accept` but with special methods and behavior for mimetypes. """ + def _specificity(self, value): + return tuple(x != "*" for x in value.split("/", 1)) + def _value_matches(self, value, item): def _normalize(x): x = x.lower() - return x == '*' and ('*', '*') or x.split('/', 1) + return ("*", "*") if x == "*" else x.split("/", 1) # this is from the application which is trusted. to avoid developer # frustration we actually check these for valid values - if '/' not in value: - raise ValueError('invalid mimetype %r' % value) + if "/" not in value: + raise ValueError("invalid mimetype %r" % value) value_type, value_subtype = _normalize(value) - if value_type == '*' and value_subtype != '*': - raise ValueError('invalid mimetype %r' % value) + if value_type == "*" and value_subtype != "*": + raise ValueError("invalid mimetype %r" % value) - if '/' not in item: + if "/" not in item: return False item_type, item_subtype = _normalize(item) - if item_type == '*' and item_subtype != '*': + if item_type == "*" and item_subtype != "*": return False return ( - (item_type == item_subtype == '*' or - value_type == value_subtype == '*') or - (item_type == value_type and (item_subtype == '*' or - value_subtype == '*' or - item_subtype == value_subtype)) + item_type == item_subtype == "*" or value_type == value_subtype == "*" + ) or ( + item_type == value_type + and ( + item_subtype == "*" + or value_subtype == "*" + or item_subtype == value_subtype + ) ) @property def accept_html(self): """True if this object accepts HTML.""" return ( - 'text/html' in self or - 'application/xhtml+xml' in self or - self.accept_xhtml + "text/html" in self or "application/xhtml+xml" in self or self.accept_xhtml ) @property def accept_xhtml(self): """True if this object accepts XHTML.""" - return ( - 'application/xhtml+xml' in self or - 'application/xml' in self - ) + return "application/xhtml+xml" in self or "application/xml" in self @property def accept_json(self): """True if this object accepts JSON.""" - return 'application/json' in self + return "application/json" in self class LanguageAccept(Accept): - """Like :class:`Accept` but with normalization for languages.""" def _value_matches(self, value, item): def _normalize(language): return _locale_delim_re.split(language.lower()) - return item == '*' or _normalize(value) == _normalize(item) + return item == "*" or _normalize(value) == _normalize(item) -class CharsetAccept(Accept): +class CharsetAccept(Accept): """Like :class:`Accept` but with normalization for charsets.""" def _value_matches(self, value, item): @@ -1822,20 +1869,22 @@ class CharsetAccept(Accept): return codecs.lookup(name).name except LookupError: return name.lower() - return item == '*' or _normalize(value) == _normalize(item) + + return item == "*" or _normalize(value) == _normalize(item) def cache_property(key, empty, type): """Return a new property object for a cache header. Useful if you want to add support for a cache extension in a subclass.""" - return property(lambda x: x._get_cache_value(key, empty, type), - lambda x, v: x._set_cache_value(key, v, type), - lambda x: x._del_cache_value(key), - 'accessor for %r' % key) + return property( + lambda x: x._get_cache_value(key, empty, type), + lambda x, v: x._set_cache_value(key, v, type), + lambda x: x._del_cache_value(key), + "accessor for %r" % key, + ) class _CacheControl(UpdateDictMixin, dict): - """Subclass of a dict that stores values for a Cache-Control header. It has accessors for all the cache-control directives specified in RFC 2616. The class does not differentiate between request and response directives. @@ -1867,10 +1916,10 @@ class _CacheControl(UpdateDictMixin, dict): no longer existing `CacheControl` class. """ - no_cache = cache_property('no-cache', '*', None) - no_store = cache_property('no-store', None, bool) - max_age = cache_property('max-age', -1, int) - no_transform = cache_property('no-transform', None, None) + no_cache = cache_property("no-cache", "*", None) + no_store = cache_property("no-store", None, bool) + max_age = cache_property("max-age", -1, int) + no_transform = cache_property("no-transform", None, None) def __init__(self, values=(), on_update=None): dict.__init__(self, values or ()) @@ -1920,16 +1969,13 @@ class _CacheControl(UpdateDictMixin, dict): return self.to_header() def __repr__(self): - return '<%s %s>' % ( + return "<%s %s>" % ( self.__class__.__name__, - " ".join( - "%s=%r" % (k, v) for k, v in sorted(self.items()) - ), + " ".join("%s=%r" % (k, v) for k, v in sorted(self.items())), ) class RequestCacheControl(ImmutableDictMixin, _CacheControl): - """A cache control for requests. This is immutable and gives access to all the request-relevant cache control headers. @@ -1943,14 +1989,13 @@ class RequestCacheControl(ImmutableDictMixin, _CacheControl): both for request and response. """ - max_stale = cache_property('max-stale', '*', int) - min_fresh = cache_property('min-fresh', '*', int) - no_transform = cache_property('no-transform', None, None) - only_if_cached = cache_property('only-if-cached', None, bool) + max_stale = cache_property("max-stale", "*", int) + min_fresh = cache_property("min-fresh", "*", int) + no_transform = cache_property("no-transform", None, None) + only_if_cached = cache_property("only-if-cached", None, bool) class ResponseCacheControl(_CacheControl): - """A cache control for responses. Unlike :class:`RequestCacheControl` this is mutable and gives access to response-relevant cache control headers. @@ -1965,11 +2010,11 @@ class ResponseCacheControl(_CacheControl): both for request and response. """ - public = cache_property('public', None, bool) - private = cache_property('private', '*', None) - must_revalidate = cache_property('must-revalidate', None, bool) - proxy_revalidate = cache_property('proxy-revalidate', None, bool) - s_maxage = cache_property('s-maxage', None, None) + public = cache_property("public", None, bool) + private = cache_property("private", "*", None) + must_revalidate = cache_property("must-revalidate", None, bool) + proxy_revalidate = cache_property("proxy-revalidate", None, bool) + s_maxage = cache_property("s-maxage", None, None) # attach cache_property to the _CacheControl as staticmethod @@ -1978,7 +2023,6 @@ _CacheControl.cache_property = staticmethod(cache_property) class CallbackDict(UpdateDictMixin, dict): - """A dict that calls a function passed every time something is changed. The function is passed the dict instance. """ @@ -1988,14 +2032,10 @@ class CallbackDict(UpdateDictMixin, dict): self.on_update = on_update def __repr__(self): - return '<%s %s>' % ( - self.__class__.__name__, - dict.__repr__(self) - ) + return "<%s %s>" % (self.__class__.__name__, dict.__repr__(self)) -class HeaderSet(MutableSet): - +class HeaderSet(collections_abc.MutableSet): """Similar to the :class:`ETags` class this implements a set-like structure. Unlike :class:`ETags` this is case insensitive and used for vary, allow, and content-language headers. @@ -2107,7 +2147,7 @@ class HeaderSet(MutableSet): def to_header(self): """Convert the header set into an HTTP header string.""" - return ', '.join(map(quote_header_value, self._headers)) + return ", ".join(map(quote_header_value, self._headers)) def __getitem__(self, idx): return self._headers[idx] @@ -2142,14 +2182,10 @@ class HeaderSet(MutableSet): return self.to_header() def __repr__(self): - return '%s(%r)' % ( - self.__class__.__name__, - self._headers - ) - + return "%s(%r)" % (self.__class__.__name__, self._headers) -class ETags(Container, Iterable): +class ETags(collections_abc.Container, collections_abc.Iterable): """A set that can be used to check if one etag is present in a collection of etags. """ @@ -2171,6 +2207,10 @@ class ETags(Container, Iterable): """Check if an etag is weak.""" return etag in self._weak + def is_strong(self, etag): + """Check if an etag is strong.""" + return etag in self._strong + def contains_weak(self, etag): """Check if an etag is part of the set including weak and strong tags.""" return self.is_weak(etag) or self.contains(etag) @@ -2178,11 +2218,10 @@ class ETags(Container, Iterable): def contains(self, etag): """Check if an etag is part of the set ignoring weak tags. It is also possible to use the ``in`` operator. - """ if self.star_tag: return True - return etag in self._strong + return self.is_strong(etag) def contains_raw(self, etag): """When passed a quoted tag it will check if this tag is part of the @@ -2196,15 +2235,14 @@ class ETags(Container, Iterable): def to_header(self): """Convert the etags set into a HTTP header string.""" if self.star_tag: - return '*' - return ', '.join( - ['"%s"' % x for x in self._strong] + - ['W/"%s"' % x for x in self._weak] + return "*" + return ", ".join( + ['"%s"' % x for x in self._strong] + ['W/"%s"' % x for x in self._weak] ) def __call__(self, etag=None, data=None, include_weak=False): if [etag, data].count(None) != 1: - raise TypeError('either tag or data required, but at least one') + raise TypeError("either tag or data required, but at least one") if etag is None: etag = generate_etag(data) if include_weak: @@ -2227,11 +2265,10 @@ class ETags(Container, Iterable): return self.contains(etag) def __repr__(self): - return '<%s %r>' % (self.__class__.__name__, str(self)) + return "<%s %r>" % (self.__class__.__name__, str(self)) class IfRange(object): - """Very simple object that represents the `If-Range` header in parsed form. It will either have neither a etag or date or one of either but never both. @@ -2252,21 +2289,25 @@ class IfRange(object): return http_date(self.date) if self.etag is not None: return quote_etag(self.etag) - return '' + return "" def __str__(self): return self.to_header() def __repr__(self): - return '<%s %r>' % (self.__class__.__name__, str(self)) + return "<%s %r>" % (self.__class__.__name__, str(self)) class Range(object): - - """Represents a range header. All the methods are only supporting bytes - as unit. It does store multiple ranges but :meth:`range_for_length` will + """Represents a ``Range`` header. All methods only support only + bytes as the unit. Stores a list of ranges if given, but the methods only work if only one range is provided. + :raise ValueError: If the ranges provided are invalid. + + .. versionchanged:: 0.15 + The ranges passed in are validated. + .. versionadded:: 0.7 """ @@ -2277,12 +2318,16 @@ class Range(object): #: The ranges are non-inclusive. self.ranges = ranges + for start, end in ranges: + if start is None or (end is not None and (start < 0 or start >= end)): + raise ValueError("{} is not a valid range.".format((start, end))) + def range_for_length(self, length): """If the range is for bytes, the length is not None and there is exactly one range and it is satisfiable it returns a ``(start, stop)`` tuple, otherwise `None`. """ - if self.units != 'bytes' or length is None or len(self.ranges) != 1: + if self.units != "bytes" or length is None or len(self.ranges) != 1: return None start, end = self.ranges[0] if end is None: @@ -2305,10 +2350,10 @@ class Range(object): ranges = [] for begin, end in self.ranges: if end is None: - ranges.append(begin >= 0 and '%s-' % begin or str(begin)) + ranges.append("%s-" % begin if begin >= 0 else str(begin)) else: - ranges.append('%s-%s' % (begin, end - 1)) - return '%s=%s' % (self.units, ','.join(ranges)) + ranges.append("%s-%s" % (begin, end - 1)) + return "%s=%s" % (self.units, ",".join(ranges)) def to_content_range_header(self, length): """Converts the object into `Content-Range` HTTP header, @@ -2316,32 +2361,33 @@ class Range(object): """ range_for_length = self.range_for_length(length) if range_for_length is not None: - return '%s %d-%d/%d' % (self.units, - range_for_length[0], - range_for_length[1] - 1, length) + return "%s %d-%d/%d" % ( + self.units, + range_for_length[0], + range_for_length[1] - 1, + length, + ) return None def __str__(self): return self.to_header() def __repr__(self): - return '<%s %r>' % (self.__class__.__name__, str(self)) + return "<%s %r>" % (self.__class__.__name__, str(self)) class ContentRange(object): - """Represents the content range header. .. versionadded:: 0.7 """ def __init__(self, units, start, stop, length=None, on_update=None): - assert is_byte_range_valid(start, stop, length), \ - 'Bad range provided' + assert is_byte_range_valid(start, stop, length), "Bad range provided" self.on_update = on_update self.set(start, stop, length, units) - def _callback_property(name): + def _callback_property(name): # noqa: B902 def fget(self): return getattr(self, name) @@ -2349,22 +2395,23 @@ class ContentRange(object): setattr(self, name, value) if self.on_update is not None: self.on_update(self) + return property(fget, fset) #: The units to use, usually "bytes" - units = _callback_property('_units') + units = _callback_property("_units") #: The start point of the range or `None`. - start = _callback_property('_start') + start = _callback_property("_start") #: The stop point of the range (non-inclusive) or `None`. Can only be #: `None` if also start is `None`. - stop = _callback_property('_stop') + stop = _callback_property("_stop") #: The length of the range or `None`. - length = _callback_property('_length') + length = _callback_property("_length") + del _callback_property - def set(self, start, stop, length=None, units='bytes'): + def set(self, start, stop, length=None, units="bytes"): """Simple method to update the ranges.""" - assert is_byte_range_valid(start, stop, length), \ - 'Bad range provided' + assert is_byte_range_valid(start, stop, length), "Bad range provided" self._units = units self._start = start self._stop = stop @@ -2380,19 +2427,14 @@ class ContentRange(object): def to_header(self): if self.units is None: - return '' + return "" if self.length is None: - length = '*' + length = "*" else: length = self.length if self.start is None: - return '%s */%s' % (self.units, length) - return '%s %s-%s/%s' % ( - self.units, - self.start, - self.stop - 1, - length - ) + return "%s */%s" % (self.units, length) + return "%s %s-%s/%s" % (self.units, self.start, self.stop - 1, length) def __nonzero__(self): return self.units is not None @@ -2403,11 +2445,10 @@ class ContentRange(object): return self.to_header() def __repr__(self): - return '<%s %r>' % (self.__class__.__name__, str(self)) + return "<%s %r>" % (self.__class__.__name__, str(self)) class Authorization(ImmutableDictMixin, dict): - """Represents an `Authorization` header sent by the client. You should not create this kind of object yourself but use it when it's returned by the `parse_authorization_header` function. @@ -2424,84 +2465,107 @@ class Authorization(ImmutableDictMixin, dict): dict.__init__(self, data or {}) self.type = auth_type - username = property(lambda x: x.get('username'), doc=''' + username = property( + lambda self: self.get("username"), + doc=""" The username transmitted. This is set for both basic and digest - auth all the time.''') - password = property(lambda x: x.get('password'), doc=''' + auth all the time.""", + ) + password = property( + lambda self: self.get("password"), + doc=""" When the authentication type is basic this is the password - transmitted by the client, else `None`.''') - realm = property(lambda x: x.get('realm'), doc=''' - This is the server realm sent back for HTTP digest auth.''') - nonce = property(lambda x: x.get('nonce'), doc=''' + transmitted by the client, else `None`.""", + ) + realm = property( + lambda self: self.get("realm"), + doc=""" + This is the server realm sent back for HTTP digest auth.""", + ) + nonce = property( + lambda self: self.get("nonce"), + doc=""" The nonce the server sent for digest auth, sent back by the client. A nonce should be unique for every 401 response for HTTP digest - auth.''') - uri = property(lambda x: x.get('uri'), doc=''' + auth.""", + ) + uri = property( + lambda self: self.get("uri"), + doc=""" The URI from Request-URI of the Request-Line; duplicated because proxies are allowed to change the Request-Line in transit. HTTP - digest auth only.''') - nc = property(lambda x: x.get('nc'), doc=''' + digest auth only.""", + ) + nc = property( + lambda self: self.get("nc"), + doc=""" The nonce count value transmitted by clients if a qop-header is - also transmitted. HTTP digest auth only.''') - cnonce = property(lambda x: x.get('cnonce'), doc=''' + also transmitted. HTTP digest auth only.""", + ) + cnonce = property( + lambda self: self.get("cnonce"), + doc=""" If the server sent a qop-header in the ``WWW-Authenticate`` header, the client has to provide this value for HTTP digest auth. - See the RFC for more details.''') - response = property(lambda x: x.get('response'), doc=''' + See the RFC for more details.""", + ) + response = property( + lambda self: self.get("response"), + doc=""" A string of 32 hex digits computed as defined in RFC 2617, which - proves that the user knows a password. Digest auth only.''') - opaque = property(lambda x: x.get('opaque'), doc=''' + proves that the user knows a password. Digest auth only.""", + ) + opaque = property( + lambda self: self.get("opaque"), + doc=""" The opaque header from the server returned unchanged by the client. It is recommended that this string be base64 or hexadecimal data. - Digest auth only.''') - - @property - def qop(self): - """Indicates what "quality of protection" the client has applied to - the message for HTTP digest auth.""" - def on_update(header_set): - if not header_set and 'qop' in self: - del self['qop'] - elif header_set: - self['qop'] = header_set.to_header() - return parse_set_header(self.get('qop'), on_update) + Digest auth only.""", + ) + qop = property( + lambda self: self.get("qop"), + doc=""" + Indicates what "quality of protection" the client has applied to + the message for HTTP digest auth. Note that this is a single token, + not a quoted list of alternatives as in WWW-Authenticate.""", + ) class WWWAuthenticate(UpdateDictMixin, dict): - """Provides simple access to `WWW-Authenticate` headers.""" #: list of keys that require quoting in the generated header - _require_quoting = frozenset(['domain', 'nonce', 'opaque', 'realm', 'qop']) + _require_quoting = frozenset(["domain", "nonce", "opaque", "realm", "qop"]) def __init__(self, auth_type=None, values=None, on_update=None): dict.__init__(self, values or ()) if auth_type: - self['__auth_type__'] = auth_type + self["__auth_type__"] = auth_type self.on_update = on_update - def set_basic(self, realm='authentication required'): + def set_basic(self, realm="authentication required"): """Clear the auth info and enable basic auth.""" dict.clear(self) - dict.update(self, {'__auth_type__': 'basic', 'realm': realm}) + dict.update(self, {"__auth_type__": "basic", "realm": realm}) if self.on_update: self.on_update(self) - def set_digest(self, realm, nonce, qop=('auth',), opaque=None, - algorithm=None, stale=False): + def set_digest( + self, realm, nonce, qop=("auth",), opaque=None, algorithm=None, stale=False + ): """Clear the auth info and enable digest auth.""" d = { - '__auth_type__': 'digest', - 'realm': realm, - 'nonce': nonce, - 'qop': dump_header(qop) + "__auth_type__": "digest", + "realm": realm, + "nonce": nonce, + "qop": dump_header(qop), } if stale: - d['stale'] = 'TRUE' + d["stale"] = "TRUE" if opaque is not None: - d['opaque'] = opaque + d["opaque"] = opaque if algorithm is not None: - d['algorithm'] = algorithm + d["algorithm"] = algorithm dict.clear(self) dict.update(self, d) if self.on_update: @@ -2510,23 +2574,30 @@ class WWWAuthenticate(UpdateDictMixin, dict): def to_header(self): """Convert the stored values into a WWW-Authenticate header.""" d = dict(self) - auth_type = d.pop('__auth_type__', None) or 'basic' - return '%s %s' % (auth_type.title(), ', '.join([ - '%s=%s' % (key, quote_header_value(value, - allow_token=key not in self._require_quoting)) - for key, value in iteritems(d) - ])) + auth_type = d.pop("__auth_type__", None) or "basic" + return "%s %s" % ( + auth_type.title(), + ", ".join( + [ + "%s=%s" + % ( + key, + quote_header_value( + value, allow_token=key not in self._require_quoting + ), + ) + for key, value in iteritems(d) + ] + ), + ) def __str__(self): return self.to_header() def __repr__(self): - return '<%s %r>' % ( - self.__class__.__name__, - self.to_header() - ) + return "<%s %r>" % (self.__class__.__name__, self.to_header()) - def auth_property(name, doc=None): + def auth_property(name, doc=None): # noqa: B902 """A static helper function for subclasses to add extra authentication system properties onto a class:: @@ -2542,70 +2613,90 @@ class WWWAuthenticate(UpdateDictMixin, dict): self.pop(name, None) else: self[name] = str(value) + return property(lambda x: x.get(name), _set_value, doc=doc) - def _set_property(name, doc=None): + def _set_property(name, doc=None): # noqa: B902 def fget(self): def on_update(header_set): if not header_set and name in self: del self[name] elif header_set: self[name] = header_set.to_header() + return parse_set_header(self.get(name), on_update) + return property(fget, doc=doc) - type = auth_property('__auth_type__', doc=''' - The type of the auth mechanism. HTTP currently specifies - `Basic` and `Digest`.''') - realm = auth_property('realm', doc=''' - A string to be displayed to users so they know which username and - password to use. This string should contain at least the name of - the host performing the authentication and might additionally - indicate the collection of users who might have access.''') - domain = _set_property('domain', doc=''' - A list of URIs that define the protection space. If a URI is an - absolute path, it is relative to the canonical root URL of the - server being accessed.''') - nonce = auth_property('nonce', doc=''' + type = auth_property( + "__auth_type__", + doc="""The type of the auth mechanism. HTTP currently specifies + ``Basic`` and ``Digest``.""", + ) + realm = auth_property( + "realm", + doc="""A string to be displayed to users so they know which + username and password to use. This string should contain at + least the name of the host performing the authentication and + might additionally indicate the collection of users who might + have access.""", + ) + domain = _set_property( + "domain", + doc="""A list of URIs that define the protection space. If a URI + is an absolute path, it is relative to the canonical root URL of + the server being accessed.""", + ) + nonce = auth_property( + "nonce", + doc=""" A server-specified data string which should be uniquely generated - each time a 401 response is made. It is recommended that this - string be base64 or hexadecimal data.''') - opaque = auth_property('opaque', doc=''' - A string of data, specified by the server, which should be returned - by the client unchanged in the Authorization header of subsequent - requests with URIs in the same protection space. It is recommended - that this string be base64 or hexadecimal data.''') - algorithm = auth_property('algorithm', doc=''' - A string indicating a pair of algorithms used to produce the digest - and a checksum. If this is not present it is assumed to be "MD5". - If the algorithm is not understood, the challenge should be ignored - (and a different one used, if there is more than one).''') - qop = _set_property('qop', doc=''' - A set of quality-of-privacy directives such as auth and auth-int.''') - - def _get_stale(self): - val = self.get('stale') + each time a 401 response is made. It is recommended that this + string be base64 or hexadecimal data.""", + ) + opaque = auth_property( + "opaque", + doc="""A string of data, specified by the server, which should + be returned by the client unchanged in the Authorization header + of subsequent requests with URIs in the same protection space. + It is recommended that this string be base64 or hexadecimal + data.""", + ) + algorithm = auth_property( + "algorithm", + doc="""A string indicating a pair of algorithms used to produce + the digest and a checksum. If this is not present it is assumed + to be "MD5". If the algorithm is not understood, the challenge + should be ignored (and a different one used, if there is more + than one).""", + ) + qop = _set_property( + "qop", + doc="""A set of quality-of-privacy directives such as auth and + auth-int.""", + ) + + @property + def stale(self): + """A flag, indicating that the previous request from the client + was rejected because the nonce value was stale. + """ + val = self.get("stale") if val is not None: - return val.lower() == 'true' + return val.lower() == "true" - def _set_stale(self, value): + @stale.setter + def stale(self, value): if value is None: - self.pop('stale', None) + self.pop("stale", None) else: - self['stale'] = value and 'TRUE' or 'FALSE' - stale = property(_get_stale, _set_stale, doc=''' - A flag, indicating that the previous request from the client was - rejected because the nonce value was stale.''') - del _get_stale, _set_stale - - # make auth_property a staticmethod so that subclasses of - # `WWWAuthenticate` can use it for new properties. + self["stale"] = "TRUE" if value else "FALSE" + auth_property = staticmethod(auth_property) del _set_property class FileStorage(object): - """The :class:`FileStorage` class is a thin wrapper over incoming files. It is used by the request object to represent uploaded files. All the attributes of the wrapper stream are proxied by the file storage so @@ -2613,52 +2704,56 @@ class FileStorage(object): ``storage.stream.read()``. """ - def __init__(self, stream=None, filename=None, name=None, - content_type=None, content_length=None, - headers=None): + def __init__( + self, + stream=None, + filename=None, + name=None, + content_type=None, + content_length=None, + headers=None, + ): self.name = name - self.stream = stream or _empty_stream + self.stream = stream or BytesIO() # if no filename is provided we can attempt to get the filename # from the stream object passed. There we have to be careful to # skip things like , etc. Python marks these # special filenames with angular brackets. if filename is None: - filename = getattr(stream, 'name', None) + filename = getattr(stream, "name", None) s = make_literal_wrapper(filename) - if filename and filename[0] == s('<') and filename[-1] == s('>'): + if filename and filename[0] == s("<") and filename[-1] == s(">"): filename = None # On Python 3 we want to make sure the filename is always unicode. # This might not be if the name attribute is bytes due to the # file being opened from the bytes API. if not PY2 and isinstance(filename, bytes): - filename = filename.decode(get_filesystem_encoding(), - 'replace') + filename = filename.decode(get_filesystem_encoding(), "replace") self.filename = filename if headers is None: headers = Headers() self.headers = headers if content_type is not None: - headers['Content-Type'] = content_type + headers["Content-Type"] = content_type if content_length is not None: - headers['Content-Length'] = str(content_length) + headers["Content-Length"] = str(content_length) def _parse_content_type(self): - if not hasattr(self, '_parsed_content_type'): - self._parsed_content_type = \ - parse_options_header(self.content_type) + if not hasattr(self, "_parsed_content_type"): + self._parsed_content_type = parse_options_header(self.content_type) @property def content_type(self): """The content-type sent in the header. Usually not available""" - return self.headers.get('content-type') + return self.headers.get("content-type") @property def content_length(self): """The content-length sent in the header. Usually not available""" - return int(self.headers.get('content-length') or 0) + return int(self.headers.get("content-length") or 0) @property def mimetype(self): @@ -2698,9 +2793,10 @@ class FileStorage(object): :func:`shutil.copyfileobj`. """ from shutil import copyfileobj + close_dst = False if isinstance(dst, string_types): - dst = open(dst, 'wb') + dst = open(dst, "wb") close_dst = True try: copyfileobj(self.stream, dst, buffer_size) @@ -2717,24 +2813,40 @@ class FileStorage(object): def __nonzero__(self): return bool(self.filename) + __bool__ = __nonzero__ def __getattr__(self, name): - return getattr(self.stream, name) + try: + return getattr(self.stream, name) + except AttributeError: + # SpooledTemporaryFile doesn't implement IOBase, get the + # attribute from its backing file instead. + # https://github.com/python/cpython/pull/3249 + if hasattr(self.stream, "_file"): + return getattr(self.stream._file, name) + raise def __iter__(self): return iter(self.stream) def __repr__(self): - return '<%s: %r (%r)>' % ( + return "<%s: %r (%r)>" % ( self.__class__.__name__, self.filename, - self.content_type + self.content_type, ) # circular dependencies -from werkzeug.http import dump_options_header, dump_header, generate_etag, \ - quote_header_value, parse_set_header, unquote_etag, quote_etag, \ - parse_options_header, http_date, is_byte_range_valid -from werkzeug import exceptions +from . import exceptions +from .http import dump_header +from .http import dump_options_header +from .http import generate_etag +from .http import http_date +from .http import is_byte_range_valid +from .http import parse_options_header +from .http import parse_set_header +from .http import quote_etag +from .http import quote_header_value +from .http import unquote_etag diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/debug/__init__.py b/backend/venv/lib/python3.7/site-packages/werkzeug/debug/__init__.py index 6124727c5..9195c79aa 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/debug/__init__.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/debug/__init__.py @@ -5,33 +5,46 @@ WSGI application traceback debugger. - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. + :copyright: 2007 Pallets + :license: BSD-3-Clause """ +import getpass +import hashlib +import json +import mimetypes import os +import pkgutil import re import sys -import uuid -import json import time -import getpass -import hashlib -import mimetypes +import uuid from itertools import chain -from os.path import join, dirname, basename, isfile -from werkzeug.wrappers import BaseRequest as Request, BaseResponse as Response -from werkzeug.http import parse_cookie -from werkzeug.debug.tbtools import get_current_traceback, render_console_html -from werkzeug.debug.console import Console -from werkzeug.security import gen_salt -from werkzeug._internal import _log -from werkzeug._compat import text_type - - -# DEPRECATED -#: import this here because it once was documented as being available -#: from this module. In case there are users left ... -from werkzeug.debug.repr import debug_repr # noqa +from os.path import basename +from os.path import join + +from .._compat import text_type +from .._internal import _log +from ..http import parse_cookie +from ..security import gen_salt +from ..wrappers import BaseRequest as Request +from ..wrappers import BaseResponse as Response +from .console import Console +from .repr import debug_repr as _debug_repr +from .tbtools import get_current_traceback +from .tbtools import render_console_html + + +def debug_repr(*args, **kwargs): + import warnings + + warnings.warn( + "'debug_repr' has moved to 'werkzeug.debug.repr.debug_repr'" + " as of version 0.7. This old import will be removed in version" + " 1.0.", + DeprecationWarning, + stacklevel=2, + ) + return _debug_repr(*args, **kwargs) # A week @@ -40,8 +53,8 @@ PIN_TIME = 60 * 60 * 24 * 7 def hash_pin(pin): if isinstance(pin, text_type): - pin = pin.encode('utf-8', 'replace') - return hashlib.md5(pin + b'shittysalt').hexdigest()[:12] + pin = pin.encode("utf-8", "replace") + return hashlib.md5(pin + b"shittysalt").hexdigest()[:12] _machine_id = None @@ -54,11 +67,24 @@ def get_machine_id(): return rv def _generate(): + # docker containers share the same machine id, get the + # container id instead + try: + with open("/proc/self/cgroup") as f: + value = f.readline() + except IOError: + pass + else: + value = value.strip().partition("/docker/")[2] + + if value: + return value + # Potential sources of secret information on linux. The machine-id # is stable across boots, the boot id is not - for filename in '/etc/machine-id', '/proc/sys/kernel/random/boot_id': + for filename in "/etc/machine-id", "/proc/sys/kernel/random/boot_id": try: - with open(filename, 'rb') as f: + with open(filename, "rb") as f: return f.readline().strip() except IOError: continue @@ -70,8 +96,10 @@ def get_machine_id(): # Google App Engine # See https://github.com/pallets/werkzeug/issues/925 from subprocess import Popen, PIPE - dump = Popen(['ioreg', '-c', 'IOPlatformExpertDevice', '-d', '2'], - stdout=PIPE).communicate()[0] + + dump = Popen( + ["ioreg", "-c", "IOPlatformExpertDevice", "-d", "2"], stdout=PIPE + ).communicate()[0] match = re.search(b'"serial-number" = <([^>]+)', dump) if match is not None: return match.group(1) @@ -89,10 +117,17 @@ def get_machine_id(): pass if wr is not None: try: - with wr.OpenKey(wr.HKEY_LOCAL_MACHINE, - 'SOFTWARE\\Microsoft\\Cryptography', 0, - wr.KEY_READ | wr.KEY_WOW64_64KEY) as rk: - return wr.QueryValueEx(rk, 'MachineGuid')[0] + with wr.OpenKey( + wr.HKEY_LOCAL_MACHINE, + "SOFTWARE\\Microsoft\\Cryptography", + 0, + wr.KEY_READ | wr.KEY_WOW64_64KEY, + ) as rk: + machineGuid, wrType = wr.QueryValueEx(rk, "MachineGuid") + if wrType == wr.REG_SZ: + return machineGuid.encode("utf-8") + else: + return machineGuid except WindowsError: pass @@ -101,7 +136,6 @@ def get_machine_id(): class _ConsoleFrame(object): - """Helper class so that we can reuse the frame console code for the standalone console. """ @@ -119,30 +153,30 @@ def get_pin_and_cookie_name(app): Second item in the resulting tuple is the cookie name for remembering. """ - pin = os.environ.get('WERKZEUG_DEBUG_PIN') + pin = os.environ.get("WERKZEUG_DEBUG_PIN") rv = None num = None # Pin was explicitly disabled - if pin == 'off': + if pin == "off": return None, None # Pin was provided explicitly - if pin is not None and pin.replace('-', '').isdigit(): + if pin is not None and pin.replace("-", "").isdigit(): # If there are separators in the pin, return it directly - if '-' in pin: + if "-" in pin: rv = pin else: num = pin - modname = getattr(app, '__module__', - getattr(app.__class__, '__module__')) + modname = getattr(app, "__module__", app.__class__.__module__) try: - # `getpass.getuser()` imports the `pwd` module, - # which does not exist in the Google App Engine sandbox. + # getuser imports the pwd module, which does not exist in Google + # App Engine. It may also raise a KeyError if the UID does not + # have a username, such as in Docker. username = getpass.getuser() - except ImportError: + except (ImportError, KeyError): username = None mod = sys.modules.get(modname) @@ -152,42 +186,41 @@ def get_pin_and_cookie_name(app): probably_public_bits = [ username, modname, - getattr(app, '__name__', getattr(app.__class__, '__name__')), - getattr(mod, '__file__', None), + getattr(app, "__name__", app.__class__.__name__), + getattr(mod, "__file__", None), ] # This information is here to make it harder for an attacker to # guess the cookie name. They are unlikely to be contained anywhere # within the unauthenticated debug page. - private_bits = [ - str(uuid.getnode()), - get_machine_id(), - ] + private_bits = [str(uuid.getnode()), get_machine_id()] h = hashlib.md5() for bit in chain(probably_public_bits, private_bits): if not bit: continue if isinstance(bit, text_type): - bit = bit.encode('utf-8') + bit = bit.encode("utf-8") h.update(bit) - h.update(b'cookiesalt') + h.update(b"cookiesalt") - cookie_name = '__wzd' + h.hexdigest()[:20] + cookie_name = "__wzd" + h.hexdigest()[:20] # If we need to generate a pin we salt it a bit more so that we don't # end up with the same value and generate out 9 digits if num is None: - h.update(b'pinsalt') - num = ('%09d' % int(h.hexdigest(), 16))[:9] + h.update(b"pinsalt") + num = ("%09d" % int(h.hexdigest(), 16))[:9] # Format the pincode in groups of digits for easier remembering if # we don't have a result yet. if rv is None: for group_size in 5, 4, 3: if len(num) % group_size == 0: - rv = '-'.join(num[x:x + group_size].rjust(group_size, '0') - for x in range(0, len(num), group_size)) + rv = "-".join( + num[x : x + group_size].rjust(group_size, "0") + for x in range(0, len(num), group_size) + ) break else: rv = num @@ -225,13 +258,28 @@ class DebuggedApplication(object): :param pin_logging: enables the logging of the pin system. """ - def __init__(self, app, evalex=False, request_key='werkzeug.request', - console_path='/console', console_init_func=None, - show_hidden_frames=False, lodgeit_url=None, - pin_security=True, pin_logging=True): + def __init__( + self, + app, + evalex=False, + request_key="werkzeug.request", + console_path="/console", + console_init_func=None, + show_hidden_frames=False, + lodgeit_url=None, + pin_security=True, + pin_logging=True, + ): if lodgeit_url is not None: from warnings import warn - warn(DeprecationWarning('Werkzeug now pastes into gists.')) + + warn( + "'lodgeit_url' is no longer used as of version 0.9 and" + " will be removed in version 1.0. Werkzeug uses" + " https://gist.github.com/ instead.", + DeprecationWarning, + stacklevel=2, + ) if not console_init_func: console_init_func = None self.app = app @@ -248,19 +296,17 @@ class DebuggedApplication(object): self.pin_logging = pin_logging if pin_security: # Print out the pin for the debugger on standard out. - if os.environ.get('WERKZEUG_RUN_MAIN') == 'true' and \ - pin_logging: - _log('warning', ' * Debugger is active!') + if os.environ.get("WERKZEUG_RUN_MAIN") == "true" and pin_logging: + _log("warning", " * Debugger is active!") if self.pin is None: - _log('warning', ' * Debugger PIN disabled. ' - 'DEBUGGER UNSECURED!') + _log("warning", " * Debugger PIN disabled. DEBUGGER UNSECURED!") else: - _log('info', ' * Debugger PIN: %s' % self.pin) + _log("info", " * Debugger PIN: %s" % self.pin) else: self.pin = None def _get_pin(self): - if not hasattr(self, '_pin'): + if not hasattr(self, "_pin"): self._pin, self._pin_cookie = get_pin_and_cookie_name(self.app) return self._pin @@ -273,7 +319,7 @@ class DebuggedApplication(object): @property def pin_cookie_name(self): """The name of the pin cookie.""" - if not hasattr(self, '_pin_cookie'): + if not hasattr(self, "_pin_cookie"): self._pin, self._pin_cookie = get_pin_and_cookie_name(self.app) return self._pin_cookie @@ -284,46 +330,51 @@ class DebuggedApplication(object): app_iter = self.app(environ, start_response) for item in app_iter: yield item - if hasattr(app_iter, 'close'): + if hasattr(app_iter, "close"): app_iter.close() except Exception: - if hasattr(app_iter, 'close'): + if hasattr(app_iter, "close"): app_iter.close() traceback = get_current_traceback( - skip=1, show_hidden_frames=self.show_hidden_frames, - ignore_system_exceptions=True) + skip=1, + show_hidden_frames=self.show_hidden_frames, + ignore_system_exceptions=True, + ) for frame in traceback.frames: self.frames[frame.id] = frame self.tracebacks[traceback.id] = traceback try: - start_response('500 INTERNAL SERVER ERROR', [ - ('Content-Type', 'text/html; charset=utf-8'), - # Disable Chrome's XSS protection, the debug - # output can cause false-positives. - ('X-XSS-Protection', '0'), - ]) + start_response( + "500 INTERNAL SERVER ERROR", + [ + ("Content-Type", "text/html; charset=utf-8"), + # Disable Chrome's XSS protection, the debug + # output can cause false-positives. + ("X-XSS-Protection", "0"), + ], + ) except Exception: # if we end up here there has been output but an error # occurred. in that situation we can do nothing fancy any # more, better log something into the error log and fall # back gracefully. - environ['wsgi.errors'].write( - 'Debugging middleware caught exception in streamed ' - 'response at a point where response headers were already ' - 'sent.\n') + environ["wsgi.errors"].write( + "Debugging middleware caught exception in streamed " + "response at a point where response headers were already " + "sent.\n" + ) else: is_trusted = bool(self.check_pin_trust(environ)) - yield traceback.render_full(evalex=self.evalex, - evalex_trusted=is_trusted, - secret=self.secret) \ - .encode('utf-8', 'replace') + yield traceback.render_full( + evalex=self.evalex, evalex_trusted=is_trusted, secret=self.secret + ).encode("utf-8", "replace") - traceback.log(environ['wsgi.errors']) + traceback.log(environ["wsgi.errors"]) def execute_command(self, request, command, frame): """Execute a command in a console.""" - return Response(frame.console.eval(command), mimetype='text/html') + return Response(frame.console.eval(command), mimetype="text/html") def display_console(self, request): """Display a standalone shell.""" @@ -332,30 +383,30 @@ class DebuggedApplication(object): ns = {} else: ns = dict(self.console_init_func()) - ns.setdefault('app', self.app) + ns.setdefault("app", self.app) self.frames[0] = _ConsoleFrame(ns) is_trusted = bool(self.check_pin_trust(request.environ)) - return Response(render_console_html(secret=self.secret, - evalex_trusted=is_trusted), - mimetype='text/html') + return Response( + render_console_html(secret=self.secret, evalex_trusted=is_trusted), + mimetype="text/html", + ) def paste_traceback(self, request, traceback): """Paste the traceback and return a JSON response.""" rv = traceback.paste() - return Response(json.dumps(rv), mimetype='application/json') + return Response(json.dumps(rv), mimetype="application/json") def get_resource(self, request, filename): """Return a static resource from the shared folder.""" - filename = join(dirname(__file__), 'shared', basename(filename)) - if isfile(filename): - mimetype = mimetypes.guess_type(filename)[0] \ - or 'application/octet-stream' - f = open(filename, 'rb') - try: - return Response(f.read(), mimetype=mimetype) - finally: - f.close() - return Response('Not Found', status=404) + filename = join("shared", basename(filename)) + try: + data = pkgutil.get_data(__package__, filename) + except OSError: + data = None + if data is not None: + mimetype = mimetypes.guess_type(filename)[0] or "application/octet-stream" + return Response(data, mimetype=mimetype) + return Response("Not Found", status=404) def check_pin_trust(self, environ): """Checks if the request passed the pin test. This returns `True` if the @@ -366,9 +417,9 @@ class DebuggedApplication(object): if self.pin is None: return True val = parse_cookie(environ).get(self.pin_cookie_name) - if not val or '|' not in val: + if not val or "|" not in val: return False - ts, pin_hash = val.split('|', 1) + ts, pin_hash = val.split("|", 1) if not ts.isdigit(): return False if pin_hash != hash_pin(self.pin): @@ -376,7 +427,7 @@ class DebuggedApplication(object): return (time.time() - PIN_TIME) < int(ts) def _fail_pin_auth(self): - time.sleep(self._failed_pin_auth > 5 and 5.0 or 0.5) + time.sleep(5.0 if self._failed_pin_auth > 5 else 0.5) self._failed_pin_auth += 1 def pin_auth(self, request): @@ -405,23 +456,23 @@ class DebuggedApplication(object): # Otherwise go through pin based authentication else: - entered_pin = request.args.get('pin') - if entered_pin.strip().replace('-', '') == \ - self.pin.replace('-', ''): + entered_pin = request.args.get("pin") + if entered_pin.strip().replace("-", "") == self.pin.replace("-", ""): self._failed_pin_auth = 0 auth = True else: self._fail_pin_auth() - rv = Response(json.dumps({ - 'auth': auth, - 'exhausted': exhausted, - }), mimetype='application/json') + rv = Response( + json.dumps({"auth": auth, "exhausted": exhausted}), + mimetype="application/json", + ) if auth: - rv.set_cookie(self.pin_cookie_name, '%s|%s' % ( - int(time.time()), - hash_pin(self.pin) - ), httponly=True) + rv.set_cookie( + self.pin_cookie_name, + "%s|%s" % (int(time.time()), hash_pin(self.pin)), + httponly=True, + ) elif bad_cookie: rv.delete_cookie(self.pin_cookie_name) return rv @@ -429,10 +480,11 @@ class DebuggedApplication(object): def log_pin_request(self): """Log the pin if needed.""" if self.pin_logging and self.pin is not None: - _log('info', ' * To enable the debugger you need to ' - 'enter the security pin:') - _log('info', ' * Debugger pin code: %s' % self.pin) - return Response('') + _log( + "info", " * To enable the debugger you need to enter the security pin:" + ) + _log("info", " * Debugger pin code: %s" % self.pin) + return Response("") def __call__(self, environ, start_response): """Dispatch the requests.""" @@ -441,26 +493,32 @@ class DebuggedApplication(object): # any more! request = Request(environ) response = self.debug_application - if request.args.get('__debugger__') == 'yes': - cmd = request.args.get('cmd') - arg = request.args.get('f') - secret = request.args.get('s') - traceback = self.tracebacks.get(request.args.get('tb', type=int)) - frame = self.frames.get(request.args.get('frm', type=int)) - if cmd == 'resource' and arg: + if request.args.get("__debugger__") == "yes": + cmd = request.args.get("cmd") + arg = request.args.get("f") + secret = request.args.get("s") + traceback = self.tracebacks.get(request.args.get("tb", type=int)) + frame = self.frames.get(request.args.get("frm", type=int)) + if cmd == "resource" and arg: response = self.get_resource(request, arg) - elif cmd == 'paste' and traceback is not None and \ - secret == self.secret: + elif cmd == "paste" and traceback is not None and secret == self.secret: response = self.paste_traceback(request, traceback) - elif cmd == 'pinauth' and secret == self.secret: + elif cmd == "pinauth" and secret == self.secret: response = self.pin_auth(request) - elif cmd == 'printpin' and secret == self.secret: + elif cmd == "printpin" and secret == self.secret: response = self.log_pin_request() - elif self.evalex and cmd is not None and frame is not None \ - and self.secret == secret and \ - self.check_pin_trust(environ): + elif ( + self.evalex + and cmd is not None + and frame is not None + and self.secret == secret + and self.check_pin_trust(environ) + ): response = self.execute_command(request, cmd, frame) - elif self.evalex and self.console_path is not None and \ - request.path == self.console_path: + elif ( + self.evalex + and self.console_path is not None + and request.path == self.console_path + ): response = self.display_console(request) return response(environ, start_response) diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/debug/__pycache__/__init__.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/debug/__pycache__/__init__.cpython-37.pyc index 76e2f0a3de81331f483e08a0a562c17e4960f5e9..a6dba5b66cee5293e31711bcc88f4f17f17265c3 100644 GIT binary patch delta 5634 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 delta 4910 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 I4-=*T0(cX_4*&oF delta 3377 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 L9ccYT>vR7DT$zHo delta 6371 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 diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/debug/console.py b/backend/venv/lib/python3.7/site-packages/werkzeug/debug/console.py index 30e89063f..adbd170b7 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/debug/console.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/debug/console.py @@ -5,23 +5,24 @@ Interactive console support. - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD. + :copyright: 2007 Pallets + :license: BSD-3-Clause """ -import sys import code +import sys from types import CodeType -from werkzeug.utils import escape -from werkzeug.local import Local -from werkzeug.debug.repr import debug_repr, dump, helper +from ..local import Local +from ..utils import escape +from .repr import debug_repr +from .repr import dump +from .repr import helper _local = Local() class HTMLStringO(object): - """A StringO version that HTML escapes on write.""" def __init__(self): @@ -41,46 +42,46 @@ class HTMLStringO(object): def readline(self): if len(self._buffer) == 0: - return '' + return "" ret = self._buffer[0] del self._buffer[0] return ret def reset(self): - val = ''.join(self._buffer) + val = "".join(self._buffer) del self._buffer[:] return val def _write(self, x): if isinstance(x, bytes): - x = x.decode('utf-8', 'replace') + x = x.decode("utf-8", "replace") self._buffer.append(x) def write(self, x): self._write(escape(x)) def writelines(self, x): - self._write(escape(''.join(x))) + self._write(escape("".join(x))) class ThreadedStream(object): - """Thread-local wrapper for sys.stdout for the interactive console.""" + @staticmethod def push(): if not isinstance(sys.stdout, ThreadedStream): sys.stdout = ThreadedStream() _local.stream = HTMLStringO() - push = staticmethod(push) + @staticmethod def fetch(): try: stream = _local.stream except AttributeError: - return '' + return "" return stream.reset() - fetch = staticmethod(fetch) + @staticmethod def displayhook(obj): try: stream = _local.stream @@ -89,18 +90,17 @@ class ThreadedStream(object): # stream._write bypasses escaping as debug_repr is # already generating HTML for us. if obj is not None: - _local._current_ipy.locals['_'] = obj + _local._current_ipy.locals["_"] = obj stream._write(debug_repr(obj)) - displayhook = staticmethod(displayhook) def __setattr__(self, name, value): - raise AttributeError('read only attribute %s' % name) + raise AttributeError("read only attribute %s" % name) def __dir__(self): return dir(sys.__stdout__) def __getattribute__(self, name): - if name == '__members__': + if name == "__members__": return dir(sys.__stdout__) try: stream = _local.stream @@ -118,7 +118,6 @@ sys.displayhook = ThreadedStream.displayhook class _ConsoleLoader(object): - def __init__(self): self._storage = {} @@ -143,29 +142,30 @@ def _wrap_compiler(console): code = compile(source, filename, symbol) console.loader.register(code, source) return code + console.compile = func class _InteractiveConsole(code.InteractiveInterpreter): - def __init__(self, globals, locals): code.InteractiveInterpreter.__init__(self, locals) self.globals = dict(globals) - self.globals['dump'] = dump - self.globals['help'] = helper - self.globals['__loader__'] = self.loader = _ConsoleLoader() + self.globals["dump"] = dump + self.globals["help"] = helper + self.globals["__loader__"] = self.loader = _ConsoleLoader() self.more = False self.buffer = [] _wrap_compiler(self) def runsource(self, source): - source = source.rstrip() + '\n' + source = source.rstrip() + "\n" ThreadedStream.push() - prompt = self.more and '... ' or '>>> ' + prompt = "... " if self.more else ">>> " try: - source_to_eval = ''.join(self.buffer + [source]) - if code.InteractiveInterpreter.runsource(self, - source_to_eval, '', 'single'): + source_to_eval = "".join(self.buffer + [source]) + if code.InteractiveInterpreter.runsource( + self, source_to_eval, "", "single" + ): self.more = True self.buffer.append(source) else: @@ -182,12 +182,14 @@ class _InteractiveConsole(code.InteractiveInterpreter): self.showtraceback() def showtraceback(self): - from werkzeug.debug.tbtools import get_current_traceback + from .tbtools import get_current_traceback + tb = get_current_traceback(skip=1) sys.stdout._write(tb.render_summary()) def showsyntaxerror(self, filename=None): - from werkzeug.debug.tbtools import get_current_traceback + from .tbtools import get_current_traceback + tb = get_current_traceback(skip=4) sys.stdout._write(tb.render_summary()) @@ -196,7 +198,6 @@ class _InteractiveConsole(code.InteractiveInterpreter): class Console(object): - """An interactive console.""" def __init__(self, globals=None, locals=None): diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/debug/repr.py b/backend/venv/lib/python3.7/site-packages/werkzeug/debug/repr.py index 0a024576e..d7a7285ca 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/debug/repr.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/debug/repr.py @@ -10,40 +10,41 @@ Together with the CSS and JavaScript files of the debugger this gives a colorful and more compact output. - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD. + :copyright: 2007 Pallets + :license: BSD-3-Clause """ -import sys -import re import codecs +import re +import sys +from collections import deque from traceback import format_exception_only -try: - from collections import deque -except ImportError: # pragma: no cover - deque = None -from werkzeug.utils import escape -from werkzeug._compat import iteritems, PY2, text_type, integer_types, \ - string_types + +from .._compat import integer_types +from .._compat import iteritems +from .._compat import PY2 +from .._compat import string_types +from .._compat import text_type +from ..utils import escape missing = object() -_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}') +_paragraph_re = re.compile(r"(?:\r\n|\r|\n){2,}") RegexType = type(_paragraph_re) -HELP_HTML = '''\ +HELP_HTML = """\

    %(title)s

    %(text)s
    \ -''' -OBJECT_DUMP_HTML = '''\ +""" +OBJECT_DUMP_HTML = """\

    %(title)s

    %(repr)s %(items)s
    \ -''' +""" def debug_repr(obj): @@ -64,31 +65,31 @@ def dump(obj=missing): class _Helper(object): - """Displays an HTML version of the normal help, for the interactive debugger only because it requires a patched sys.stdout. """ def __repr__(self): - return 'Type help(object) for help about object.' + return "Type help(object) for help about object." def __call__(self, topic=None): if topic is None: - sys.stdout._write('%s' % repr(self)) + sys.stdout._write("%s" % repr(self)) return import pydoc + pydoc.help(topic) rv = sys.stdout.reset() if isinstance(rv, bytes): - rv = rv.decode('utf-8', 'ignore') + rv = rv.decode("utf-8", "ignore") paragraphs = _paragraph_re.split(rv) if len(paragraphs) > 1: title = paragraphs[0] - text = '\n\n'.join(paragraphs[1:]) + text = "\n\n".join(paragraphs[1:]) else: # pragma: no cover - title = 'Help' + title = "Help" text = paragraphs[0] - sys.stdout._write(HELP_HTML % {'title': title, 'text': text}) + sys.stdout._write(HELP_HTML % {"title": title, "text": text}) helper = _Helper() @@ -101,95 +102,109 @@ def _add_subclass_info(inner, obj, base): return inner elif type(obj) is base: return inner - module = '' - if obj.__class__.__module__ not in ('__builtin__', 'exceptions'): + module = "" + if obj.__class__.__module__ not in ("__builtin__", "exceptions"): module = '%s.' % obj.__class__.__module__ - return '%s%s(%s)' % (module, obj.__class__.__name__, inner) + return "%s%s(%s)" % (module, obj.__class__.__name__, inner) class DebugReprGenerator(object): - def __init__(self): self._stack = [] - def _sequence_repr_maker(left, right, base=object(), limit=8): + def _sequence_repr_maker(left, right, base=object(), limit=8): # noqa: B008, B902 def proxy(self, obj, recursive): if recursive: - return _add_subclass_info(left + '...' + right, obj, base) + return _add_subclass_info(left + "..." + right, obj, base) buf = [left] have_extended_section = False for idx, item in enumerate(obj): if idx: - buf.append(', ') + buf.append(", ") if idx == limit: buf.append('') have_extended_section = True buf.append(self.repr(item)) if have_extended_section: - buf.append('') + buf.append("") buf.append(right) - return _add_subclass_info(u''.join(buf), obj, base) + return _add_subclass_info(u"".join(buf), obj, base) + return proxy - list_repr = _sequence_repr_maker('[', ']', list) - tuple_repr = _sequence_repr_maker('(', ')', tuple) - set_repr = _sequence_repr_maker('set([', '])', set) - frozenset_repr = _sequence_repr_maker('frozenset([', '])', frozenset) - if deque is not None: - deque_repr = _sequence_repr_maker('collections.' - 'deque([', '])', deque) + list_repr = _sequence_repr_maker("[", "]", list) + tuple_repr = _sequence_repr_maker("(", ")", tuple) + set_repr = _sequence_repr_maker("set([", "])", set) + frozenset_repr = _sequence_repr_maker("frozenset([", "])", frozenset) + deque_repr = _sequence_repr_maker( + 'collections.' "deque([", "])", deque + ) del _sequence_repr_maker def regex_repr(self, obj): pattern = repr(obj.pattern) if PY2: - pattern = pattern.decode('string-escape', 'ignore') + pattern = pattern.decode("string-escape", "ignore") else: - pattern = codecs.decode(pattern, 'unicode-escape', 'ignore') - if pattern[:1] == 'u': - pattern = 'ur' + pattern[1:] + pattern = codecs.decode(pattern, "unicode-escape", "ignore") + if pattern[:1] == "u": + pattern = "ur" + pattern[1:] else: - pattern = 'r' + pattern + pattern = "r" + pattern return u're.compile(%s)' % pattern def string_repr(self, obj, limit=70): buf = [''] - a = repr(obj[:limit]) - b = repr(obj[limit:]) - if isinstance(obj, text_type) and PY2: - buf.append('u') - a = a[1:] - b = b[1:] - if b != "''": - buf.extend((escape(a[:-1]), '', escape(b[1:]), '')) + r = repr(obj) + + # shorten the repr when the hidden part would be at least 3 chars + if len(r) - limit > 2: + buf.extend( + ( + escape(r[:limit]), + '', + escape(r[limit:]), + "", + ) + ) else: - buf.append(escape(a)) - buf.append('') - return _add_subclass_info(u''.join(buf), obj, (bytes, text_type)) + buf.append(escape(r)) + + buf.append("") + out = u"".join(buf) + + # if the repr looks like a standard string, add subclass info if needed + if r[0] in "'\"" or (r[0] in "ub" and r[1] in "'\""): + return _add_subclass_info(out, obj, (bytes, text_type)) + + # otherwise, assume the repr distinguishes the subclass already + return out def dict_repr(self, d, recursive, limit=5): if recursive: - return _add_subclass_info(u'{...}', d, dict) - buf = ['{'] + return _add_subclass_info(u"{...}", d, dict) + buf = ["{"] have_extended_section = False for idx, (key, value) in enumerate(iteritems(d)): if idx: - buf.append(', ') + buf.append(", ") if idx == limit - 1: buf.append('') have_extended_section = True - buf.append('%s: ' - '%s' % - (self.repr(key), self.repr(value))) + buf.append( + '%s: ' + '%s' + % (self.repr(key), self.repr(value)) + ) if have_extended_section: - buf.append('') - buf.append('}') - return _add_subclass_info(u''.join(buf), d, dict) + buf.append("") + buf.append("}") + return _add_subclass_info(u"".join(buf), d, dict) def object_repr(self, obj): r = repr(obj) if PY2: - r = r.decode('utf-8', 'replace') + r = r.decode("utf-8", "replace") return u'%s' % escape(r) def dispatch_repr(self, obj, recursive): @@ -197,7 +212,7 @@ class DebugReprGenerator(object): return u'%r' % helper if isinstance(obj, (integer_types, float, complex)): return u'%r' % obj - if isinstance(obj, string_types): + if isinstance(obj, string_types) or isinstance(obj, bytes): return self.string_repr(obj) if isinstance(obj, RegexType): return self.regex_repr(obj) @@ -217,13 +232,14 @@ class DebugReprGenerator(object): def fallback_repr(self): try: - info = ''.join(format_exception_only(*sys.exc_info()[:2])) + info = "".join(format_exception_only(*sys.exc_info()[:2])) except Exception: # pragma: no cover - info = '?' + info = "?" if PY2: - info = info.decode('utf-8', 'ignore') - return u'<broken repr (%s)>' \ - u'' % escape(info.strip()) + info = info.decode("utf-8", "ignore") + return u'<broken repr (%s)>' u"" % escape( + info.strip() + ) def repr(self, obj): recursive = False @@ -243,7 +259,7 @@ class DebugReprGenerator(object): def dump_object(self, obj): repr = items = None if isinstance(obj, dict): - title = 'Contents of' + title = "Contents of" items = [] for key, value in iteritems(obj): if not isinstance(key, string_types): @@ -258,23 +274,24 @@ class DebugReprGenerator(object): items.append((key, self.repr(getattr(obj, key)))) except Exception: pass - title = 'Details for' - title += ' ' + object.__repr__(obj)[1:-1] + title = "Details for" + title += " " + object.__repr__(obj)[1:-1] return self.render_object_dump(items, title, repr) def dump_locals(self, d): items = [(key, self.repr(value)) for key, value in d.items()] - return self.render_object_dump(items, 'Local variables in frame') + return self.render_object_dump(items, "Local variables in frame") def render_object_dump(self, items, title, repr=None): html_items = [] for key, value in items: - html_items.append('%s
    %s
    ' % - (escape(key), value)) + html_items.append( + "%s
    %s
    " % (escape(key), value) + ) if not html_items: - html_items.append('Nothing') + html_items.append("Nothing") return OBJECT_DUMP_HTML % { - 'title': escape(title), - 'repr': repr and '
    %s
    ' % repr or '', - 'items': '\n'.join(html_items) + "title": escape(title), + "repr": "
    %s
    " % repr if repr else "", + "items": "\n".join(html_items), } diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/debug/shared/debugger.js b/backend/venv/lib/python3.7/site-packages/werkzeug/debug/shared/debugger.js index 534019dc4..ba267ed60 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/debug/shared/debugger.js +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/debug/shared/debugger.js @@ -10,6 +10,10 @@ $(function() { openShell(null, $('div.console div.inner').empty(), 0); } + $("div.detail").click(function() { + $("div.traceback").get(0).scrollIntoView(false); + }); + $('div.traceback div.frame').each(function() { var target = $('pre', this), @@ -185,11 +189,12 @@ function openShell(consoleNode, target, frameID) { var command = $('') .appendTo(form) .keydown(function(e) { - if (e.charCode == 100 && e.ctrlKey) { + if (e.key == 'l' && e.ctrlKey) { output.text('--- screen cleared ---'); return false; } else if (e.charCode == 0 && (e.keyCode == 38 || e.keyCode == 40)) { + // handle up arrow and down arrow if (e.keyCode == 38 && historyPos > 0) historyPos--; else if (e.keyCode == 40 && historyPos < history.length) diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/debug/shared/jquery.js b/backend/venv/lib/python3.7/site-packages/werkzeug/debug/shared/jquery.js index 0f60b7bd0..4d9b3a258 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/debug/shared/jquery.js +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/debug/shared/jquery.js @@ -1,5 +1,2 @@ -/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */ -!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="
    ",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1; - -return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
    a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),ha=/^\s+/,ia=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja=/<([\w:]+)/,ka=/\s*$/g,ra={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:k.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]},sa=da(y),ta=sa.appendChild(y.createElement("div"));ra.optgroup=ra.option,ra.tbody=ra.tfoot=ra.colgroup=ra.caption=ra.thead,ra.th=ra.td;function ua(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ua(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function va(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wa(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xa(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function ya(a){var b=pa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function za(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Aa(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Ba(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xa(b).text=a.text,ya(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!ga.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ta.innerHTML=a.outerHTML,ta.removeChild(f=ta.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ua(f),h=ua(a),g=0;null!=(e=h[g]);++g)d[g]&&Ba(e,d[g]);if(b)if(c)for(h=h||ua(a),d=d||ua(f),g=0;null!=(e=h[g]);g++)Aa(e,d[g]);else Aa(a,f);return d=ua(f,"script"),d.length>0&&za(d,!i&&ua(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=da(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(la.test(f)){h=h||o.appendChild(b.createElement("div")),i=(ja.exec(f)||["",""])[1].toLowerCase(),l=ra[i]||ra._default,h.innerHTML=l[1]+f.replace(ia,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&ha.test(f)&&p.push(b.createTextNode(ha.exec(f)[0])),!k.tbody){f="table"!==i||ka.test(f)?""!==l[1]||ka.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ua(p,"input"),va),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ua(o.appendChild(f),"script"),g&&za(h),c)){e=0;while(f=h[e++])oa.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ua(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&za(ua(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ua(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fa,""):void 0;if(!("string"!=typeof a||ma.test(a)||!k.htmlSerialize&&ga.test(a)||!k.leadingWhitespace&&ha.test(a)||ra[(ja.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ia,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ua(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ua(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&na.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ua(i,"script"),xa),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ua(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,ya),j=0;f>j;j++)d=g[j],oa.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qa,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Ca,Da={};function Ea(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fa(a){var b=y,c=Da[a];return c||(c=Ea(a,b),"none"!==c&&c||(Ca=(Ca||m("

    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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/errors.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/errors.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..81e1207464bdd23614e3cc5b6db36abc7ed5796f GIT binary patch literal 10105 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 ObuN|0e=wCzo%|oGH#e97 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/ismaster.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/ismaster.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..13d915dac45f4d7816c965d5d49c9dd599d40edf GIT binary patch literal 5102 zcmb7IOLH5?5#ATxAO%U(gN$r#Bv}$H3AFX7$cif^(Pr69nUIQLEPJa`Q%lT}TxuW4 z?2-~tN)D0Aha6I=oN~y4yd|gnpS|YfzmQYDp2aQ~6eLkVP2-#0o|$iYdU|@WG&56I z@cGx53%~!*Nk#cDRkDu(@DL^X4uUI|!d0$$s-@yr_cUL(bd{bNp5d3QlEfvi>{qM` zaG6(LD^~Rzg;#m)wZdyo?UioTxzN1&3M}}uep(LN3kQW znf=iB?SOZF#_&qFttN)++XMWT=}i~_HG0cy-!HGR^&g)s_f^uvhkkXGuTY}DLA+G< z!Sg_kTCuu=@nu~*P=$%QKCbJi3;D(XdVn{^_)Q7*-=ba`*UP9|sF%m}3hFzkS9(g? zDp-*HR1Z{Md!~t>@U$x5)p`oAWB!fN-LD)* zM_Z)EH@1Y!yX|_SZ#l7`)lMqE3!Vkx^T4XC^}g8-qnN;BFce8`t@lJ=0mLwAto3?% z-+2Bg?L8^4hQU@CCG}p6b+f%NaIi)?5BvXCL<{}@&s`h{ch8QU#XVarZiXIrM6~FO z#f=@t?nn26U+wX`eKy#;{aL(ue``m0pKsp#;)})g#eqAxUt$oWv)9&+~bSTfhta7ZRTYzQ8|{_!RKR{G!CC zfldBPiRXYX@yimQ0lvaNk@zg|Renw4bHLYmTjKM;pYj_L&-0t`<6B8}C0do+wQVE@ z!%8R3qRmlrBlcXlvAy9rX-zmbr&?bd8<&2``{aIo2oNhT)mQ3Eg{dz|YA=-?HP&|w zloCpLQ-4;*^9r6989qzhLx80BnF3wu&rkf)~t5mia5jJbguGj<2Hh+awuR1NuU>P>ZY zGP7bQ)13%rWFToAiq)8?vacQ}n5H`VVI9E((Qx1)oE%1sapcS+hlz#R^_?K@)a&N> z)9IK9IguL%HUbBK@W8y&xtqUjYtj&j(GLg1wkqZ^@*5M&dVPqOkzCDkVJg|%iK$R* zmQyJM&{Sv|i0#t$)5j6Calg%n&_;8)wF-+dF$%4m9OXVhjHn!IJ9=#FmoQ>^zrxim z<$%13uvWl2;RF4Z_KLj782$P?H;T=0)1*)s-82Vw6uEm25aOFT4yutULuPt_xzRY5 zcaoB9WL2|9RyAwTt|mI-wjh%U9U`gsJ;x4M5OOD}QKXK;(2J5%f7^D0)Fo@sn{J?_ zhM>sgXmnCXRR{%cMc>h>+*rBNgsJk4M~v3x#!tI# znF{Lv{gzHz<0JvmIA+v?jdq#Y=W}95wI&$mzCl4NV@O*m^^UG}(zjnMMPcxGW5?;o zJ$K~1X}+1&M?bKo-s3Lotys&`t(VN8BesE_ScFbCbE?|tY)*ATWrAl3`qGjVyEC4g zHn=vIda~ZNPNV&*6GirxLqwN`u@n7FJFu-6R{SbGm>%w0M8HpbkTiFon_3hLHbJwf zHNo^)7JZ82(pj?O{`Pc>&XGl*uC1&uv!zGtUw!*7!_E#F zl&$^ZJ#5L&$6-r0F0kF3&i07b1lxB6gRWJ_9?oo#?qA3XCxGBltZq4NIYg~qp;V&ns!8nj}H2DWHBU32a=zPxVh|&bb z+-7g48{MQqcL@9yL%w_rHo1BzqJI{c9QsMt9~8Rn1-b)K4#HScXUq@zu1C1Z*dKOn zZ~UglSmcFq#F$t>pGkEf!T~bjp}2smyak9$gf5eyd`o;nf{tu)okW|&rzCEWxJlv` zi4KWH5}%RyoP=C+x{`@|Bn~fa64A{~oQ9wV5%pA64XrxUtTqjlk}L-Bo8NNNkS*Jd zq{8S*j0u_Oz?!852Y*f6A>-I-`fpXn|6!!L=IL>*Q@Girm(f$`At{G4(URRyW1LjK VOpW^+x`;-!(jX1r#aFMM|3Bv}p!xs+ literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/max_staleness_selectors.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/max_staleness_selectors.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b70c2604fc1a5912db832bb97b7d8bbe61e0240f GIT binary patch literal 2466 zcmbVO&u`l{6ecOzabzcLlQwC$9f$zMVl1`0V%^%JD4HcM)*?xR$ZI+SouHVsBgd9h zQcmJPxvV?xk7#e(zohGcVY%+O%l4l9BTkxO-30LQ>5(EI-}mvezn+@12s}SO{OHwh zmkIe5Uxp6@=P@+>4H!XcL{P!nw8LtQqHnZ~j#)FoH)|P@sbxi0O#Db{6Cx)jq0b3R z;VyZilkYwcFDV`1Y>!d9*8)>2#v>>-xm zR3ChVG;15Gl_oUW{r$B6F`0w3mJ7JX0+tABJGT9xOp-ShoC39c0@|2F=XX0L?JqV z9+u%I?E}!|eOHwmL0d?r%Nn9qsF~vOSM05+`WIlTrG!Q zc+2x~m-ekYhxNU#({svUuM_yqVAP*U#OVt}pmZthCG*a{+xGCV4}991m?y`XI1`gSs=kknx4GDSM=5zdGwyH0%(s)V@s0xT=uu@k4BI^<&1t2V zxaSY~w1lK_K@3q7;ezz_=bU_%idHf)kg{Y}r^4xoG38A1gJ9GPr2=k|B)?_%l@s)? zb{Q|H(@oXO0i~14r8IJY;^km*)g*qDb;D5lBDs#&8*o5{KC@9Vt8zGTvX2oYtfuhY z&_X9vLwu;JDR25Fk29v6bj`sj?n6jl1w&Ytnyf$z<{Zrdf(16s)OCnoj9>^OP+#&6 zFpQa=1D`et);dM>8KA@f(Z?yGMg&L#qAwA{0itJ7+!;e|Vcbtqq$3oc{4W&G3wWtG zSz8z>c)uP;!Xec(noDR9PK8WYSI{6(Y6i>z3x#E@nHpA7*KlkO%{&^sx?>RJ z9Y1ge0BpdJ#yS%t2LMC=SL lg~u{UVMs4Q?qMeo-L`!634D;MS6cRrNlhbnC3h`%<8RH9vHt)7 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/message.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/message.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..efaad6a097d74dd58a0b8547cb0cf23109c1da1f GIT binary patch literal 36820 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 O?Awav;{BK`O8qY^k9(c~ literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/mongo_client.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/mongo_client.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a3673603533f00fd9d2ab101c0aed8a73bf903d GIT binary patch literal 72783 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 tma-%}4r@Eq&9Qu;6-%*`P2AORXGpHJ+@)t` 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 jYZe$px(N6M!KTG0X_Z9x(|< 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 V+H(Tn#m}j`3&wYY3Ox1d{{fXLqPk` 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 iY#9Y(MHiCBJND23 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/read_concern.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/read_concern.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d05156d61c85fdf5875eb4abbe506b02eab401b0 GIT binary patch literal 2303 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/response.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/response.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6b1d4834c352d55e4188a362b02be16e01ba343 GIT binary patch literal 3330 zcmcgu&u`l{6sBa^Q5?HX*JeLjU|{$)Ed_etU$EV_e@T}e_FwF@_oyFM(`0oorNJlC63LJ6eUBd>bh~W=<W46CRT|K`4^=+}Bgl>e%emZvDP7)pMRq*V93(t!0X!`DbfG?ks;_~WIWxk@%mwSr8erX-2evV5 zg7vrq2;3lc2wBKC?KUmS=HoyGV_}5So7JC`-lETN1J5?;_cRgaP^#y1Pmf_k!Jg>9 z`F*id7;3kDXYxbYwBI z?3T-(HLUJ*7#iguD=xu;$Ry~evB9|l%6DlIq&b4EUQ=O|6w*r}omCSJMz7b(JT2m3 z=2<2zl|*RIcboYo+^kav^cPW(D&*g2Fj3J#V8q}cP=le2IYenNR)gJr#y0eN^6dft zCS;R?JGaenV{c!@Uk}&s-W@z0OrpuHDA6W}<6C!LhR;)W#0Hb2u}t=4^(=SWpBw>E zS(HSEv5#S4ZBw@0a`NBw!jpp7{qn02h-M2uv09$uwr6CY02iI<)oq)G6>GlKR0W6h z!S>Hvboa;z-S0WpdGJ_*fp*KeIfq>z%ok8KI+!%3-3kQya{&O?-c3VT8Y6cGf@OI1 z71R>JYK5SPb;qH4GOk409SS0ONCnQIhTL+!wpdQCjC<3pDD+4&z{j)GL1(g8ge zHm7CyT!7FdLkU7OkunZ3o3P*$vklj`vTKL|WjS_#cR^uXP?Oed>5ryEtj08~#+2&P zgiJhDU5unbLPd^*hN;q0LAv9C5(O~@*;7aT_mG?qp*b}l71FV7z4i#4MpG>~9ZF?I z;KVjc;j|iwzAc77hGu>TIUGgd$PTSd83GIE!eIx`Gq{-IgUjA2Ik}+hWlfzzj;J-@ zku9T=}q{;jAUx) zVE{Rh>>-(|iS~r~Nh%m*k3DpF7)NlKy-Cx=M6qQKyvCb{7KXyCMQQ;lb51HzO$Ki< z_mq-qPA@n;F&t-{=byo=U(l;oS^epilb)=FYk`yf20CUD_5&QtkJHce%YbONLe-dF zI*AH-(_2=n@Qu2F1`k_x5e@Q0T|$FP$uj?5E->FA9F3yxHCFro0U0bn AFaQ7m literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/results.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/results.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23abab02353bcf170498225724ba0dac305e8b1b GIT binary patch literal 8754 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 Gss907`h)QR literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/saslprep.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/saslprep.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..44d48a635158f4a7fce0e7a16b40889c990a0568 GIT binary patch literal 2444 zcma)8Pfr_16rY)0jJ+5`6E#siepjoZWq15JVu3L&IHtq1}_T1(Ng-WkJ^{m0BK z!Ir72CQ=e9m)>(oZtc0>p&w$eJ>@Ip(!Q}-Sky-CSZ{XT?wdF7&+onQtR|2wtMu5sdqd*m)F`#jv589>XIaXpt(1Dx7%-BFx ztNZz4NWCETdB8*EC@%_W)TZlqmS!fWrs>Aw#&@ycaqTTwi>f#+DSoJI)rtA)9T)4D zF!a*^S^|<6U{a`u6n;X)IYk(Lf`7(61gDX1B~wUQ5(J&@*5)p!QVB2I(Z|prVu{bG zCutZd>Kr(p@3eegqxU5jp%ZXPosiMkk@9gQn501>6>V|4xUsada;!<#szz#g(hH?> zLYJqOePVT5+63E|g@^=rV=_lMzacRUHox=gvG5KY#p?%-sJA1ZaUtu0sJHfP`?{PB zuN|-%*NzVwld64vXHWQ3?b#bQ>YMf0i?8+%^Zl!hL-$c)ckFuH38HW(I(bS*`u$MG zowS%S7j?vnFd<@Lf(t}wd~CWzzk*=`4FT!s%0FPrA}cF8qCG6ABFfk^+xQ|nBB80s zk>-b9=(+F$GyHn^8Gc6Egb`*uE%wlD^aQ`bM@SX-O0O+u_Rt#woUu1@Wb}+4Vndfe zLcHv+3wkYoRAn~Y&!W~kdVH4EBRylVKKxSaefa4Mq$Df6U)B1=5E3+197a~_^ivb~>h;c>Z5FQ3{%`#~GnLLJ|a z9&-lngzW5AskCcZ@x5O|<}E0x(c8T3B)&SE3rO1bwsv{@{aSQJ@|tq+Sm!DcVGbJ_ z4_U|g0!%yrPpnW)Tgr7sfK{=Suv3`mPhCNU5rF)p&no7$jj62on3kzNR@2q(r^5aH9yl_t+ zsx2ts?nKrmYV&^N0t6OdkqM^e>YZ|ANu|Ec0}ZwxMr=yDg7-g1fq@J3e)H zx%=phle()+>+^Ka1{W(w;MjAh*)3@4`-JIA#jD3oi82T^i-X-i+*;XOzMWd9 zH%Knt&XgF_@)a#_?m0`Q#C~aR5w4#Ony%lH?2&7Gclnp($`JZ5E@^7>jrjeB=p<=BrN0=n9#U|O=98- zF$)D;z$PwZ%dC(BJj-U$s0>+#g)hLxVUa4{te#JYZJR}|Z8s||8HKgDGp#}8D4~<^raY^sc0ZhlUoZD_=3aF8bg*%;R zX7bEzp7}b@T+1`p^UMwLp*~gzC^zZIq{aEcpMwQ$Q@<=tQYHjKQGviziY6`=OjzZA GMe;YzxSYTM literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/server.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/server.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a1860d7c594d23a4a80f3c97dc986d09b9d52ae GIT binary patch literal 4643 zcmb7I&2QYs6`$ePazC_Mtt5XXPMI`Ly{IjvZi6_6VK`RgAV4iRl94#93!2)QmAK-P zn;EWREx1Jy`4FH85}-ZwC>;W{{}Qh~8R)6U9*Vv<^gD8od&6k#CLVv*<-;V1qmGu@ zn_Ld#Nb+oc{n@TRlF9T1OsQ|JZ+^DE={@@5;d&R>oL)L~?!qnq2n{D7FL5d>ujF2pjYKJw!qrY$su*?yxwoH z#d1_*OKe$vnPcbJd03&zF0hO6oM#ktbUCxOlo~d=72pRid}?s7!7cAYlhTxUB-Nm$ zsSd4{8qn&g$>?4+emgb$I5txgT8kldB_(}R+)J%Pf+Mw+Hom;chk{EUB`>kV%0|b| z8Xn`aD*~nB%=CtF9A>sRh@&8h1$>^`$qSg<=8?Z0a_047;pN5z+TmzB4CHQB_27~4 zh>NV^@qO4-b}E@6dDzR^IrXW{4BX4D6kECVnVqugbSarx`BHx;VhL39@15152=>8K zR`-3e+KWTRg5!Qm_490+Hf7py|DpTFoi02cs|v zh9P&lAcb^x{eA94vBP^k-c43YvW<==5C}3O9`dL|#XJm@{_;hA96agO)tPa{19841nuy$sm6DF_V;{s5u!kzP>jT;3YYR)M} zOrTOhavM6RQ1Ldj^aui_uVYB`&AtU~rKk0@V@i?6KB}V9p;tSq?LE7^AZh&d9x zQOQ);XT}DbypXd1I8fKs&aCbmq5=E~w0fowM~Rq&p1WMcNf9_+H;g4`!bYl!&lrd+ zMI9$9%bDuCsNxIM&?}UO&iQM&rNVvFq7B-nHCifBt#V3<%)gJWLxem6n~Ed%tS|30~-xr~qZ+ClEm? zIU<06=CSrY{qCa)Al;;r8htvkQ(L@~Xh-@nnN(AHWTX~SO={3L0XMC*cC1h8FyBh+ z$MlIlX-wuOO_=pfJ}a$WAd`8ZuA~-x*CwsWLTVh@X)9ew8>!W=9zz10?r?A^wT^1( zT-rQ_6bb!$+Bl|@_U9yeVvwYxI1E$GadpvPQ02CO72hvgD z2BUa11NW$!PZB4Pg~8yvA3|Eo0V+tO^AO+|>n=p;sVz^q*}yyv)t4_WLHN0KskVjm z2iZsAx6MYmk+Rw}?gN%JdLkY`p*9$RuXIdt878Y0#2eWA88!|yo#m{C*2tH2mu9Po zVzv|r5t$yvU%7PkbBM;`y0fhEtXnOe&~2V} z)NKBY?+L6c+)CjIZWUFO>m5~?Ri?F-sibimsG42?!gL2Ls}us{nnvxrje-BnV{zA$ z!I--XkjP^OrG;18Plt(6^KM5j_x7K~cdF4`r{0xFAY@Wa+nJ*3UY8Xdn`hJ((oSXwa4@gyIG(VIQj#PlP>q5i=iZfLfwqTjF;=c>;zJpt$UWwMG zKTTS!R$eZx!jZq=ib@o%tTwSwEXBj!(3(oTcc7DifQOJ3_cYN;;rNtduGS_xNNV6+rscA78D5xc)yhoie|@Kv4v`1|Dzq z5GrvaSNSjTgThCJnRu|7Da?Nk!@t5U(Q3qkpwewRUM}3?GzOM-LA93kG5R)o;RI9% z9s=k5i|8qE&c6snO<#w;agp@&bLb_;#(42|zPR(&6?yB5e9O5a?jgYy5>kaa%X-PdkQe@xQFg|gfE>_Rc-{{%Hy27peT$BpQ=xK#j=r%0y1I{l zEj)omP&9a6R`cLQ%0?mf8=m*&$Pe=`0{+R7pJY^#EU+9F7#Cs%8;tkN9ts5H8y)l(-VJeXrfl-Ssm&|1T;*oIxVYqh&Iy3=uR(Z{xFOHcjQ^ZEka9h*2Q%n`;|0=V%i_>p23KmNDR$hXsO<%@! YX5Y?rbqAwc;!(uV0Ga`uTePA77qtSAnE(I) literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/server_description.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/server_description.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a7296cf4580a999f9e6d869efe0529a21fa305d GIT binary patch literal 6261 zcma)A&2QVt6(^|=OS1eGJC5yqc`Fx=s+M3#@=*me$ZVWr|dX zloJbHpg?lyp@$xN=%I%#u!rrjmmYd3us!zN6R$n>U+Ag7Hcf(2grzmTt$^l<$7tC!Ed;G3Vm`06>`Zashf)$`Z<&DXE!Huu8+ z()0sM0;dDJZdK{A&MyV<6qjGoHOLe zC2>~a8Q^o`yuzn|FNkG@XMvemQFsn`Ra{i~H1H*HS>bu$E8?ocXMnGXlEMqX*ToHm z7lGHrO@)_$%VJ&Ov%t5+ZH3PP-w}5eJ`a3P+*kMl@F(Jd!pp!9#Uq88tyNY;3e)>I zoY_(n`nly-rQHZ@&oztq<}d2?W?*hR7L%RnH;Ku-otPVx3k8PX#w$~rbqkZ}Z>SE& zZnAGoA>cRuHyi9zc5Tn|gEiLhWKd!a)0W4>Y`?Dj0OMXp@*URk4Q9K3V7gW7c)0YM zuHXT5<>WQ-r*<6A430-yQhGARUsF!&Kp0ajW(I*(he&r9Zrkx;g5=zhpR2c(=EEFs zTN4&}#NokV!wL)BbR15``C*0!=B^*kaC|Yxu9_Gaa2MYNPdB7pH|1eC$?N7jzU_N1 z_wAO2!E|58XEJxKsOz-qdMoX~;@f6W-Rq9DUH;aV7RRFVF%|8Hd%C81-}ebqcsrD~ zeNL~W8IA6wWeRmS%N<{RO0OC4y6rXt%MY_$eIjLT&=pwZmV;#u+1wW41g|*5p)UkEa*AV z^Pm?%mq8im%25jKD()_VUJ5S1*FdivkuF!!drkg}+Mu+ceVaMTe0TlN#J}NYg`z^k z^3pd;YjOEt3i9D}zZ=PM>K<#mk}T5QM2|bk3H4yV zOUX&QMcqQ0W3Sj0eOx%$cb7w)#rzj{4AUZygFzs8;PB_+H+?8A>7==^)>nIZ~h>WnD!2F9QKfw$B=dWWn4&dGE2c}%# z@f?Arxn7s++cnM~`uE)j2jX6pHxBOH3U(gu)}(WL=l-Kd>u=T@cH^cBZjN*F&b#V< zlOOW+#$nxacfBrcyNfuE&E>`+q6xQMJK($o6@1b|%Vzboo-=a#$NyQy&l*{ypcjqS z(pG=yFLz#DMG8^I zbYmDT$3Uwgiq(w8=nHL;HqnETQl`&Lw%4qiF0yJAewmL{=I*u=DJ9c#nY9Bv?6$rF9&T1R9aCB*qOZF)W?QQtwNVZ4F*Ml;9{<8dI>pCU zDG&+X#JWAO{558}f|+s~SxowD-}T?=->+GPu-Pn1uqMMLH zQFVy0Ww3U-!i?7ZpfC!|C(CIqqFScpk9iLmC4c5gwu!=-mH`FuAsR5SYPdQsm@qy4 z`CtTX9=$_EiR>$)q}Ah|DWseK6|CRJWG2S< zZcQ_qI4X07h`R>X?>;UrRxphwj!LK@;;0PxeN5biLGOuIV?FYE(if$6T>6d?m9B=W z?o>gSEIOR*+7UDxj`fF_7Hm+9-oZV3BxV;14OHSK3zs*+`eRJu+@M69D$twhM|7wZ zDl3ObSp(}&xT3&pv~CYdIiYABO$AkuCYMO-5dU3Fg_Q&qdbeJqiKLP_+3Ik*f9Ljn zOnyR`=pWvrK-bAAr+1ueKP#~dQ}vQY^%AghyVfPDnyO7H>BPxIDTJLdb5LWu)x+cb zlu|WixS;mlJn;JWy=s$?Y@ECcHGUv_sGil*MXH4pzG>eMpvR;{Pij3O)#+1=e!$R^ za%uZAMCW^8{S{ZIJ~%Vz18ReEvX-=}=7tNp4%Xkgf{a!|tc-2sMiWPSyCHVn0PF83 zitAL%J>t4HCnfx?OD}*pV>Wo7Unk_bYA9T zuMo&Xbrz>80KdFh%0xS1D*ly?mF$7Xs7@;?v+bjRa?CNKiJtCoI!6`iXsI&O z>Bb3y7kI8+m2}i5%V@%^w_U@zost%_N0~=Pg}EouT>X@aRG&6VX#-)A{;B^1uV(tD literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/server_selectors.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/server_selectors.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e0eba4dfdb69a06113bc5b959b29302dfd88a41f GIT binary patch literal 5714 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 Ftsi^mgpmLM literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/settings.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/settings.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d3f69c86c5ebfd889e0c2dbcc3e5cd9746e79350 GIT binary patch literal 4187 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 QZr4!>dv?RAe(Wv$5AO~aa{vGU literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/ssl_support.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/ssl_support.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6b640b41356dacbbce6a9af1b87d28a3e516d5d2 GIT binary patch literal 3818 zcmb6cO>^7E6}tdQ@Iw+seOR^=7t@*6W^GfAWyf~L)2U2TvBnCih>Snbq!2`a5=0PS z7faRTfSpd%p6f%8Jy<=Z{R2HEzo5ThuRZya>A9!Ax1cCR%}iThu>1Dy+qZAuzArqU znaL^eeD&zc7tL#m@(&!GJ~{wDhc9|W6va_Y#UT#$iAiKnO)7iUR2|jSh6OoFnW+~_ zu%an$x|?H9h=Q-9nc6EQpf3^142zOdH+5K-0qVsGb&ya`DYL^xpiKF>Uf#?D7uC$3 zqyE!(sEL#I3%wb02Iz>B@n?H;<{VL6^=mt11{QbhS5x z-H`I+{8VqrTp|kW@WVtNm@7==Cr-{^PUa`l<(D&>XI)#jkz4;eQ)Av2oIEyD|p?cpb3upd_5p~CB}jWQ8>)n$Cc^Id@R5ocbo zZ}DLthox#5biDlmv$+=rHO4~rj>5t+eWybAE`u-;IbaTy+P$gfmuJ1wi;sMmIPZ=FXqwp*Kx`uf)HF7TQ%IJah}_UUJvJGCdfyOrs5 zee{bKNC+|wK!lF*qi8BsBF#s}g-v14sT9*KQTj9Vbfa=sSTs@8QGM)@tAD-#^nHBOH+aY}lr!IeW2&^R^L(L=f7P{3%%>M=h2po5T`?^5)7h_${1!!CCC8fc;idEJ=!Qfnrfo{BlA;fk%Ze$#(e<%V6-AU`HLs z%Y5Y+c;Oj7Rd$q@3QE8)9bQJcf@}N=&^(d2t8d>sQ6yG}lPpW!>2Qws<19GKrP1{& zb1^2{fdk=~4_JWSa+W5HaPQD<^GaPTZ0|H$jcQ}dYHc;G>egnh-V(Fz1Gn9^4#J2B zcF)}qnI!l`BH!4Ch-?9;qdN?p`aEV$%qP>;#wXi5wPq8dv{*<`JGFZ4<3?+99YZ%u z!OF5KJBCT@T^1UaMl6+`Y(~${XHbkqeYa=z?I`LWFgtQF(+oTY`JzAM2Vr2`sobwT zylLF%5BozH+^c+Wvy1_It+IA=RNhV^rO|bVh7-C`a2*np&D#euZFwE94J!Kx5`!ol zxyqnaSRL zn;%4@5Od90wB0ql$OuAi0QM|B%4-fnBN{>s>lxMc^9PEIb~x}I%)Tgq3kf;lX(uvB zmJ>xfmGU)~$E%pb28%(4K}*a{twi4Z8pALKQF8m$+WqaV&zfuoNq>Nifio(IBDEMn z%CfuwDj+OI#GKTDOdjn6+Y5va{A7j`OV4XNn;WmJ>dux`eOjwNv$ksM8|=rpY)LMg zD8>)ro`7w{uH&5E_iYaRd#sGZ`KRm8Yt!i6YV6d+%yhWbs6Jy?flS6_p`me$f?R=F zWpiUwWKcM;1-P06m$@Q8*|irsZn+>do9##JJ=`e+c7$eN=FThYcgA}ddcDbmbL z3`WdKbDR1Vdx(70Hh(SXktbC5b^Bt9nS-jqk|vPUcP6ZeI?yP3iImcm6xAG=)pZIP zC%sITDZG$*D12#knXCYmrbSwW#7xOG%t?q*b$}2q&;lfE=wVbMn6-*z7WyJZsx)Db zVW;oZl+z?$f*Ht5H#@ZDPmvpsOt|Ey#yqx!{y42&B*!Ppyc zqiv&mg3+Fq9ALLde#ZIK73?G1^X)y~t(S?kG6*5_Ae=}!gI;fVL8G!P`5R0}Xpjok z=rlcJIKu)#re`#JJf19d|KEfCH%Ti~Q3=C}=%S3M^{?TJP#lGpuZWCgIbqwf*a{%n zEoej*D`wk)-vv{bCJe45omb>Hd-&fW{fd2v3+`jH28~%btyZv1O6sB6y(kQzyjyFB zJ%~E8cr|AyrC`Oyzm6zzUu`RFciCNJCcWr3LN~F&P+=~dt~qD0Qs2_uq%EC0Db}w{ z)=~PS#8!TWQ777fMx#1`mug_G1+d7Rgb)YTIl(3{+AtKJv;-bN!On}MMD?Ox%6*}y F{|!(y=|%tm literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/thread_util.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/thread_util.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4aee15c2022dea9175eb508948b72c174d680125 GIT binary patch literal 4192 zcma)9O>^7E8QxuhBuGjWrMi*p>7+>9I$>K&j;Cpz87K9`b=uA}YGoUou81ODXYop~2#L(;doIPQq&}V6--%$5CqZ_k6W(Cdu zg^k3Y-AL7qV74--4TEmL*{_+b%GygNYje@9qZi0JdiAtE_qq-A8?uRhGi{>3hW?sd zM}OV+*U@ju4fHqCb7A`X~=k^p8=^xa57N+BuVA&cEV0e?$2$e4P#sO~rQq%@hE|{x{eitKLbRr@JSy+C9t$5-!{ws@;R*D7vRV9{u7( z{w#^cCwK4UhxeWytHDQyAAj=6?*8t$H@@8)={z0`Zr?pk`jhA^+8v)EN>8)$>C}rR z2=&hR%mh(HrbST**_sf{c!0ajr(30GcNQ=0?8{Iw_AT|W>IC(bdsq_mQd{u=_`<NM(L%<54v?Fg zN(X7I(@sUL(`2w%)YJ-bMySNp(vhUTjkK%UwEPyTTPT_;#@7%4A9n*Sp9>q1HPOIR z@V20_2A^&&!&B&$5NPS5C*j}5W(N)UguUX{C+t{AzD?c|b`_UAAgyk5d?`t!0G=0|ZJCP!(~*RFjtEaI%go(}JS8s1!_ zm$rVnny24^GC7bs1Dl)+;F35QoZ_P5%&>kkElXQqHFt)J3s#nObzemv)alqsB zd#k3D1a}D>8l=LcCX?wsQ{k#NPD1r8^4SWteAKIE+V*bTkr* zBBLnF9j{Pa-j+>|@s1`eIbxVz|3B>uag-_+b5#}MZS@{i6kk(|BAF$Clf@XN=mjTP zBw76Y2oqnTSZPE-4PVjN)yVk}GbM8BQVAccUDMl0mGsB04b5VSF07uk~s~DIX3|3U0tob@TQ5F9EeY1`(nHl!vs)T~u}} z#ZmK|>#bbO*;jnp!k7nAgH`7^Y1=pKW{Yz^*(dhC05i}T)or!pA@vig>^}Q#Pzzg2NeXo>0jZ?I&6RGT^YmY!saHG?=Rw`4ctbNV$PteM)=G>`LKSga#ygluNp^v8zY!=JOHj&-EPjBZ zDdS5jtCSe`VSCktuUC8D{v0`6(_(bdlY~A*(brJ94J{Wo{`kXNVA`_^xr8N_?G%Bw zd)QAgaKSK3KLpn2CK;SAr(*&M*e9~brB&5_D;5G`DQF+$YR1;jgckFhHQX2B5Is(aH^UEu=LPVhIH zr%M)?;rjyk0j~=Dx(G#S*lDW|Xlcq#MLH@nKz#?5Y5sOdKeG0Jt-D$LAnlYtv)$HW zD5XC-t!zG4=yM8_sb&YqX_8yYbRi_CSg98xR?@8r?z;ouXEda5pu(YyPV&6Pw{V(Y P^IIRa*W1nZX1n?g@+o0d literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/topology.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/topology.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a9f8075ee2bb9a2c8ab9d39e6de0d9885d9c813 GIT binary patch literal 17465 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 gNj;-y(?&{DbGp%Qk literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/write_concern.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/pymongo/__pycache__/write_concern.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d768bd4277b8486582e938e485b38c1a9117942 GIT binary patch literal 4584 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/auth.py b/backend/venv/lib/python3.7/site-packages/pymongo/auth.py new file mode 100644 index 000000000..3667257af --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/auth.py @@ -0,0 +1,570 @@ +# Copyright 2013-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Authentication helpers.""" + +import functools +import hashlib +import hmac +import socket + +try: + from urllib import quote +except ImportError: + from urllib.parse import quote + +HAVE_KERBEROS = True +_USE_PRINCIPAL = False +try: + import winkerberos as kerberos + if tuple(map(int, kerberos.__version__.split('.')[:2])) >= (0, 5): + _USE_PRINCIPAL = True +except ImportError: + try: + import kerberos + except ImportError: + HAVE_KERBEROS = False + +from base64 import standard_b64decode, standard_b64encode +from collections import namedtuple +from random import SystemRandom + +from bson.binary import Binary +from bson.py3compat import string_type, _unicode, PY3 +from bson.son import SON +from pymongo.errors import ConfigurationError, OperationFailure +from pymongo.saslprep import saslprep + + +MECHANISMS = frozenset( + ['GSSAPI', + 'MONGODB-CR', + 'MONGODB-X509', + 'PLAIN', + 'SCRAM-SHA-1', + 'SCRAM-SHA-256', + 'DEFAULT']) +"""The authentication mechanisms supported by PyMongo.""" + + +class _Cache(object): + __slots__ = ("data",) + + _hash_val = hash('_Cache') + + def __init__(self): + self.data = None + + def __eq__(self, other): + # Two instances must always compare equal. + if isinstance(other, _Cache): + return True + return NotImplemented + + def __ne__(self, other): + if isinstance(other, _Cache): + return False + return NotImplemented + + def __hash__(self): + return self._hash_val + + + +MongoCredential = namedtuple( + 'MongoCredential', + ['mechanism', + 'source', + 'username', + 'password', + 'mechanism_properties', + 'cache']) +"""A hashable namedtuple of values used for authentication.""" + + +GSSAPIProperties = namedtuple('GSSAPIProperties', + ['service_name', + 'canonicalize_host_name', + 'service_realm']) +"""Mechanism properties for GSSAPI authentication.""" + + +def _build_credentials_tuple(mech, source, user, passwd, extra, database): + """Build and return a mechanism specific credentials tuple. + """ + if mech != 'MONGODB-X509' and user is None: + raise ConfigurationError("%s requires a username." % (mech,)) + if mech == 'GSSAPI': + if source is not None and source != '$external': + raise ValueError( + "authentication source must be $external or None for GSSAPI") + properties = extra.get('authmechanismproperties', {}) + service_name = properties.get('SERVICE_NAME', 'mongodb') + canonicalize = properties.get('CANONICALIZE_HOST_NAME', False) + service_realm = properties.get('SERVICE_REALM') + props = GSSAPIProperties(service_name=service_name, + canonicalize_host_name=canonicalize, + service_realm=service_realm) + # Source is always $external. + return MongoCredential(mech, '$external', user, passwd, props, None) + elif mech == 'MONGODB-X509': + if passwd is not None: + raise ConfigurationError( + "Passwords are not supported by MONGODB-X509") + if source is not None and source != '$external': + raise ValueError( + "authentication source must be " + "$external or None for MONGODB-X509") + # user can be None. + return MongoCredential(mech, '$external', user, None, None, None) + elif mech == 'PLAIN': + source_database = source or database or '$external' + return MongoCredential(mech, source_database, user, passwd, None, None) + else: + source_database = source or database or 'admin' + if passwd is None: + raise ConfigurationError("A password is required.") + return MongoCredential( + mech, source_database, user, passwd, None, _Cache()) + + +if PY3: + def _xor(fir, sec): + """XOR two byte strings together (python 3.x).""" + return b"".join([bytes([x ^ y]) for x, y in zip(fir, sec)]) + + + _from_bytes = int.from_bytes + _to_bytes = int.to_bytes +else: + from binascii import (hexlify as _hexlify, + unhexlify as _unhexlify) + + + def _xor(fir, sec): + """XOR two byte strings together (python 2.x).""" + return b"".join([chr(ord(x) ^ ord(y)) for x, y in zip(fir, sec)]) + + + def _from_bytes(value, dummy, _int=int, _hexlify=_hexlify): + """An implementation of int.from_bytes for python 2.x.""" + return _int(_hexlify(value), 16) + + + def _to_bytes(value, length, dummy, _unhexlify=_unhexlify): + """An implementation of int.to_bytes for python 2.x.""" + fmt = '%%0%dx' % (2 * length,) + return _unhexlify(fmt % value) + + +try: + # The fastest option, if it's been compiled to use OpenSSL's HMAC. + from backports.pbkdf2 import pbkdf2_hmac as _hi +except ImportError: + try: + # Python 2.7.8+, or Python 3.4+. + from hashlib import pbkdf2_hmac as _hi + except ImportError: + + def _hi(hash_name, data, salt, iterations): + """A simple implementation of PBKDF2-HMAC.""" + mac = hmac.HMAC(data, None, getattr(hashlib, hash_name)) + + def _digest(msg, mac=mac): + """Get a digest for msg.""" + _mac = mac.copy() + _mac.update(msg) + return _mac.digest() + + from_bytes = _from_bytes + to_bytes = _to_bytes + + _u1 = _digest(salt + b'\x00\x00\x00\x01') + _ui = from_bytes(_u1, 'big') + for _ in range(iterations - 1): + _u1 = _digest(_u1) + _ui ^= from_bytes(_u1, 'big') + return to_bytes(_ui, mac.digest_size, 'big') + +try: + from hmac import compare_digest +except ImportError: + if PY3: + def _xor_bytes(a, b): + return a ^ b + else: + def _xor_bytes(a, b, _ord=ord): + return _ord(a) ^ _ord(b) + + # Python 2.x < 2.7.7 + # Note: This method is intentionally obtuse to prevent timing attacks. Do + # not refactor it! + # References: + # - http://bugs.python.org/issue14532 + # - http://bugs.python.org/issue14955 + # - http://bugs.python.org/issue15061 + def compare_digest(a, b, _xor_bytes=_xor_bytes): + left = None + right = b + if len(a) == len(b): + left = a + result = 0 + if len(a) != len(b): + left = b + result = 1 + + for x, y in zip(left, right): + result |= _xor_bytes(x, y) + return result == 0 + + +def _parse_scram_response(response): + """Split a scram response into key, value pairs.""" + return dict(item.split(b"=", 1) for item in response.split(b",")) + + +def _authenticate_scram(credentials, sock_info, mechanism): + """Authenticate using SCRAM.""" + + username = credentials.username + if mechanism == 'SCRAM-SHA-256': + digest = "sha256" + digestmod = hashlib.sha256 + data = saslprep(credentials.password).encode("utf-8") + else: + digest = "sha1" + digestmod = hashlib.sha1 + data = _password_digest(username, credentials.password).encode("utf-8") + source = credentials.source + cache = credentials.cache + + # Make local + _hmac = hmac.HMAC + + user = username.encode("utf-8").replace(b"=", b"=3D").replace(b",", b"=2C") + nonce = standard_b64encode( + (("%s" % (SystemRandom().random(),))[2:]).encode("utf-8")) + first_bare = b"n=" + user + b",r=" + nonce + + cmd = SON([('saslStart', 1), + ('mechanism', mechanism), + ('payload', Binary(b"n,," + first_bare)), + ('autoAuthorize', 1)]) + res = sock_info.command(source, cmd) + + server_first = res['payload'] + parsed = _parse_scram_response(server_first) + iterations = int(parsed[b'i']) + if iterations < 4096: + raise OperationFailure("Server returned an invalid iteration count.") + salt = parsed[b's'] + rnonce = parsed[b'r'] + if not rnonce.startswith(nonce): + raise OperationFailure("Server returned an invalid nonce.") + + without_proof = b"c=biws,r=" + rnonce + if cache.data: + client_key, server_key, csalt, citerations = cache.data + else: + client_key, server_key, csalt, citerations = None, None, None, None + + # Salt and / or iterations could change for a number of different + # reasons. Either changing invalidates the cache. + if not client_key or salt != csalt or iterations != citerations: + salted_pass = _hi( + digest, data, standard_b64decode(salt), iterations) + client_key = _hmac(salted_pass, b"Client Key", digestmod).digest() + server_key = _hmac(salted_pass, b"Server Key", digestmod).digest() + cache.data = (client_key, server_key, salt, iterations) + stored_key = digestmod(client_key).digest() + auth_msg = b",".join((first_bare, server_first, without_proof)) + client_sig = _hmac(stored_key, auth_msg, digestmod).digest() + client_proof = b"p=" + standard_b64encode(_xor(client_key, client_sig)) + client_final = b",".join((without_proof, client_proof)) + + server_sig = standard_b64encode( + _hmac(server_key, auth_msg, digestmod).digest()) + + cmd = SON([('saslContinue', 1), + ('conversationId', res['conversationId']), + ('payload', Binary(client_final))]) + res = sock_info.command(source, cmd) + + parsed = _parse_scram_response(res['payload']) + if not compare_digest(parsed[b'v'], server_sig): + raise OperationFailure("Server returned an invalid signature.") + + # Depending on how it's configured, Cyrus SASL (which the server uses) + # requires a third empty challenge. + if not res['done']: + cmd = SON([('saslContinue', 1), + ('conversationId', res['conversationId']), + ('payload', Binary(b''))]) + res = sock_info.command(source, cmd) + if not res['done']: + raise OperationFailure('SASL conversation failed to complete.') + + +def _password_digest(username, password): + """Get a password digest to use for authentication. + """ + if not isinstance(password, string_type): + raise TypeError("password must be an " + "instance of %s" % (string_type.__name__,)) + if len(password) == 0: + raise ValueError("password can't be empty") + if not isinstance(username, string_type): + raise TypeError("password must be an " + "instance of %s" % (string_type.__name__,)) + + md5hash = hashlib.md5() + data = "%s:mongo:%s" % (username, password) + md5hash.update(data.encode('utf-8')) + return _unicode(md5hash.hexdigest()) + + +def _auth_key(nonce, username, password): + """Get an auth key to use for authentication. + """ + digest = _password_digest(username, password) + md5hash = hashlib.md5() + data = "%s%s%s" % (nonce, username, digest) + md5hash.update(data.encode('utf-8')) + return _unicode(md5hash.hexdigest()) + + +def _authenticate_gssapi(credentials, sock_info): + """Authenticate using GSSAPI. + """ + if not HAVE_KERBEROS: + raise ConfigurationError('The "kerberos" module must be ' + 'installed to use GSSAPI authentication.') + + try: + username = credentials.username + password = credentials.password + props = credentials.mechanism_properties + # Starting here and continuing through the while loop below - establish + # the security context. See RFC 4752, Section 3.1, first paragraph. + host = sock_info.address[0] + if props.canonicalize_host_name: + host = socket.getfqdn(host) + service = props.service_name + '@' + host + if props.service_realm is not None: + service = service + '@' + props.service_realm + + if password is not None: + if _USE_PRINCIPAL: + # Note that, though we use unquote_plus for unquoting URI + # options, we use quote here. Microsoft's UrlUnescape (used + # by WinKerberos) doesn't support +. + principal = ":".join((quote(username), quote(password))) + result, ctx = kerberos.authGSSClientInit( + service, principal, gssflags=kerberos.GSS_C_MUTUAL_FLAG) + else: + if '@' in username: + user, domain = username.split('@', 1) + else: + user, domain = username, None + result, ctx = kerberos.authGSSClientInit( + service, gssflags=kerberos.GSS_C_MUTUAL_FLAG, + user=user, domain=domain, password=password) + else: + result, ctx = kerberos.authGSSClientInit( + service, gssflags=kerberos.GSS_C_MUTUAL_FLAG) + + if result != kerberos.AUTH_GSS_COMPLETE: + raise OperationFailure('Kerberos context failed to initialize.') + + try: + # pykerberos uses a weird mix of exceptions and return values + # to indicate errors. + # 0 == continue, 1 == complete, -1 == error + # Only authGSSClientStep can return 0. + if kerberos.authGSSClientStep(ctx, '') != 0: + raise OperationFailure('Unknown kerberos ' + 'failure in step function.') + + # Start a SASL conversation with mongod/s + # Note: pykerberos deals with base64 encoded byte strings. + # Since mongo accepts base64 strings as the payload we don't + # have to use bson.binary.Binary. + payload = kerberos.authGSSClientResponse(ctx) + cmd = SON([('saslStart', 1), + ('mechanism', 'GSSAPI'), + ('payload', payload), + ('autoAuthorize', 1)]) + response = sock_info.command('$external', cmd) + + # Limit how many times we loop to catch protocol / library issues + for _ in range(10): + result = kerberos.authGSSClientStep(ctx, + str(response['payload'])) + if result == -1: + raise OperationFailure('Unknown kerberos ' + 'failure in step function.') + + payload = kerberos.authGSSClientResponse(ctx) or '' + + cmd = SON([('saslContinue', 1), + ('conversationId', response['conversationId']), + ('payload', payload)]) + response = sock_info.command('$external', cmd) + + if result == kerberos.AUTH_GSS_COMPLETE: + break + else: + raise OperationFailure('Kerberos ' + 'authentication failed to complete.') + + # Once the security context is established actually authenticate. + # See RFC 4752, Section 3.1, last two paragraphs. + if kerberos.authGSSClientUnwrap(ctx, + str(response['payload'])) != 1: + raise OperationFailure('Unknown kerberos ' + 'failure during GSS_Unwrap step.') + + if kerberos.authGSSClientWrap(ctx, + kerberos.authGSSClientResponse(ctx), + username) != 1: + raise OperationFailure('Unknown kerberos ' + 'failure during GSS_Wrap step.') + + payload = kerberos.authGSSClientResponse(ctx) + cmd = SON([('saslContinue', 1), + ('conversationId', response['conversationId']), + ('payload', payload)]) + sock_info.command('$external', cmd) + + finally: + kerberos.authGSSClientClean(ctx) + + except kerberos.KrbError as exc: + raise OperationFailure(str(exc)) + + +def _authenticate_plain(credentials, sock_info): + """Authenticate using SASL PLAIN (RFC 4616) + """ + source = credentials.source + username = credentials.username + password = credentials.password + payload = ('\x00%s\x00%s' % (username, password)).encode('utf-8') + cmd = SON([('saslStart', 1), + ('mechanism', 'PLAIN'), + ('payload', Binary(payload)), + ('autoAuthorize', 1)]) + sock_info.command(source, cmd) + + +def _authenticate_cram_md5(credentials, sock_info): + """Authenticate using CRAM-MD5 (RFC 2195) + """ + source = credentials.source + username = credentials.username + password = credentials.password + # The password used as the mac key is the + # same as what we use for MONGODB-CR + passwd = _password_digest(username, password) + cmd = SON([('saslStart', 1), + ('mechanism', 'CRAM-MD5'), + ('payload', Binary(b'')), + ('autoAuthorize', 1)]) + response = sock_info.command(source, cmd) + # MD5 as implicit default digest for digestmod is deprecated + # in python 3.4 + mac = hmac.HMAC(key=passwd.encode('utf-8'), digestmod=hashlib.md5) + mac.update(response['payload']) + challenge = username.encode('utf-8') + b' ' + mac.hexdigest().encode('utf-8') + cmd = SON([('saslContinue', 1), + ('conversationId', response['conversationId']), + ('payload', Binary(challenge))]) + sock_info.command(source, cmd) + + +def _authenticate_x509(credentials, sock_info): + """Authenticate using MONGODB-X509. + """ + query = SON([('authenticate', 1), + ('mechanism', 'MONGODB-X509')]) + if credentials.username is not None: + query['user'] = credentials.username + elif sock_info.max_wire_version < 5: + raise ConfigurationError( + "A username is required for MONGODB-X509 authentication " + "when connected to MongoDB versions older than 3.4.") + sock_info.command('$external', query) + + +def _authenticate_mongo_cr(credentials, sock_info): + """Authenticate using MONGODB-CR. + """ + source = credentials.source + username = credentials.username + password = credentials.password + # Get a nonce + response = sock_info.command(source, {'getnonce': 1}) + nonce = response['nonce'] + key = _auth_key(nonce, username, password) + + # Actually authenticate + query = SON([('authenticate', 1), + ('user', username), + ('nonce', nonce), + ('key', key)]) + sock_info.command(source, query) + + +def _authenticate_default(credentials, sock_info): + if sock_info.max_wire_version >= 7: + source = credentials.source + cmd = SON([ + ('ismaster', 1), + ('saslSupportedMechs', source + '.' + credentials.username)]) + mechs = sock_info.command( + source, cmd, publish_events=False).get('saslSupportedMechs', []) + if 'SCRAM-SHA-256' in mechs: + return _authenticate_scram(credentials, sock_info, 'SCRAM-SHA-256') + else: + return _authenticate_scram(credentials, sock_info, 'SCRAM-SHA-1') + elif sock_info.max_wire_version >= 3: + return _authenticate_scram(credentials, sock_info, 'SCRAM-SHA-1') + else: + return _authenticate_mongo_cr(credentials, sock_info) + + +_AUTH_MAP = { + 'CRAM-MD5': _authenticate_cram_md5, + 'GSSAPI': _authenticate_gssapi, + 'MONGODB-CR': _authenticate_mongo_cr, + 'MONGODB-X509': _authenticate_x509, + 'PLAIN': _authenticate_plain, + 'SCRAM-SHA-1': functools.partial( + _authenticate_scram, mechanism='SCRAM-SHA-1'), + 'SCRAM-SHA-256': functools.partial( + _authenticate_scram, mechanism='SCRAM-SHA-256'), + 'DEFAULT': _authenticate_default, +} + + +def authenticate(credentials, sock_info): + """Authenticate sock_info.""" + mechanism = credentials.mechanism + auth_func = _AUTH_MAP.get(mechanism) + auth_func(credentials, sock_info) + + +def logout(source, sock_info): + """Log out from a database.""" + sock_info.command(source, {'logout': 1}) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/bulk.py b/backend/venv/lib/python3.7/site-packages/pymongo/bulk.py new file mode 100644 index 000000000..bc8c31c50 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/bulk.py @@ -0,0 +1,702 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The bulk write operations interface. + +.. versionadded:: 2.7 +""" +import copy + +from itertools import islice + +from bson.objectid import ObjectId +from bson.raw_bson import RawBSONDocument +from bson.son import SON +from pymongo.client_session import _validate_session_write_concern +from pymongo.common import (validate_is_mapping, + validate_is_document_type, + validate_ok_for_replace, + validate_ok_for_update) +from pymongo.helpers import _RETRYABLE_ERROR_CODES +from pymongo.collation import validate_collation_or_none +from pymongo.errors import (BulkWriteError, + ConfigurationError, + InvalidOperation, + OperationFailure) +from pymongo.message import (_INSERT, _UPDATE, _DELETE, + _do_batched_insert, + _do_bulk_write_command, + _randint, + _BulkWriteContext) +from pymongo.read_preferences import ReadPreference +from pymongo.write_concern import WriteConcern + + +_DELETE_ALL = 0 +_DELETE_ONE = 1 + +# For backwards compatibility. See MongoDB src/mongo/base/error_codes.err +_BAD_VALUE = 2 +_UNKNOWN_ERROR = 8 +_WRITE_CONCERN_ERROR = 64 + +_COMMANDS = ('insert', 'update', 'delete') + + +# These string literals are used when we create fake server return +# documents client side. We use unicode literals in python 2.x to +# match the actual return values from the server. +_UOP = u"op" + + +class _Run(object): + """Represents a batch of write operations. + """ + def __init__(self, op_type): + """Initialize a new Run object. + """ + self.op_type = op_type + self.index_map = [] + self.ops = [] + self.idx_offset = 0 + + def index(self, idx): + """Get the original index of an operation in this run. + + :Parameters: + - `idx`: The Run index that maps to the original index. + """ + return self.index_map[idx] + + def add(self, original_index, operation): + """Add an operation to this Run instance. + + :Parameters: + - `original_index`: The original index of this operation + within a larger bulk operation. + - `operation`: The operation document. + """ + self.index_map.append(original_index) + self.ops.append(operation) + + +def _merge_command(run, full_result, offset, result): + """Merge a write command result into the full bulk result. + """ + affected = result.get("n", 0) + + if run.op_type == _INSERT: + full_result["nInserted"] += affected + + elif run.op_type == _DELETE: + full_result["nRemoved"] += affected + + elif run.op_type == _UPDATE: + upserted = result.get("upserted") + if upserted: + n_upserted = len(upserted) + for doc in upserted: + doc["index"] = run.index(doc["index"] + offset) + full_result["upserted"].extend(upserted) + full_result["nUpserted"] += n_upserted + full_result["nMatched"] += (affected - n_upserted) + else: + full_result["nMatched"] += affected + full_result["nModified"] += result["nModified"] + + write_errors = result.get("writeErrors") + if write_errors: + for doc in write_errors: + # Leave the server response intact for APM. + replacement = doc.copy() + idx = doc["index"] + offset + replacement["index"] = run.index(idx) + # Add the failed operation to the error document. + replacement[_UOP] = run.ops[idx] + full_result["writeErrors"].append(replacement) + + wc_error = result.get("writeConcernError") + if wc_error: + full_result["writeConcernErrors"].append(wc_error) + + +def _raise_bulk_write_error(full_result): + """Raise a BulkWriteError from the full bulk api result. + """ + if full_result["writeErrors"]: + full_result["writeErrors"].sort( + key=lambda error: error["index"]) + raise BulkWriteError(full_result) + + +class _Bulk(object): + """The private guts of the bulk write API. + """ + def __init__(self, collection, ordered, bypass_document_validation): + """Initialize a _Bulk instance. + """ + self.collection = collection.with_options( + codec_options=collection.codec_options._replace( + unicode_decode_error_handler='replace', + document_class=dict)) + self.ordered = ordered + self.ops = [] + self.name = "%s.%s" % (collection.database.name, collection.name) + self.namespace = collection.database.name + '.$cmd' + self.executed = False + self.bypass_doc_val = bypass_document_validation + self.uses_collation = False + self.uses_array_filters = False + self.is_retryable = True + self.retrying = False + self.started_retryable_write = False + # Extra state so that we know where to pick up on a retry attempt. + self.current_run = None + + def add_insert(self, document): + """Add an insert document to the list of ops. + """ + validate_is_document_type("document", document) + # Generate ObjectId client side. + if not (isinstance(document, RawBSONDocument) or '_id' in document): + document['_id'] = ObjectId() + self.ops.append((_INSERT, document)) + + def add_update(self, selector, update, multi=False, upsert=False, + collation=None, array_filters=None): + """Create an update document and add it to the list of ops. + """ + validate_ok_for_update(update) + cmd = SON([('q', selector), ('u', update), + ('multi', multi), ('upsert', upsert)]) + collation = validate_collation_or_none(collation) + if collation is not None: + self.uses_collation = True + cmd['collation'] = collation + if array_filters is not None: + self.uses_array_filters = True + cmd['arrayFilters'] = array_filters + if multi: + # A bulk_write containing an update_many is not retryable. + self.is_retryable = False + self.ops.append((_UPDATE, cmd)) + + def add_replace(self, selector, replacement, upsert=False, + collation=None): + """Create a replace document and add it to the list of ops. + """ + validate_ok_for_replace(replacement) + cmd = SON([('q', selector), ('u', replacement), + ('multi', False), ('upsert', upsert)]) + collation = validate_collation_or_none(collation) + if collation is not None: + self.uses_collation = True + cmd['collation'] = collation + self.ops.append((_UPDATE, cmd)) + + def add_delete(self, selector, limit, collation=None): + """Create a delete document and add it to the list of ops. + """ + cmd = SON([('q', selector), ('limit', limit)]) + collation = validate_collation_or_none(collation) + if collation is not None: + self.uses_collation = True + cmd['collation'] = collation + if limit == _DELETE_ALL: + # A bulk_write containing a delete_many is not retryable. + self.is_retryable = False + self.ops.append((_DELETE, cmd)) + + def gen_ordered(self): + """Generate batches of operations, batched by type of + operation, in the order **provided**. + """ + run = None + for idx, (op_type, operation) in enumerate(self.ops): + if run is None: + run = _Run(op_type) + elif run.op_type != op_type: + yield run + run = _Run(op_type) + run.add(idx, operation) + yield run + + def gen_unordered(self): + """Generate batches of operations, batched by type of + operation, in arbitrary order. + """ + operations = [_Run(_INSERT), _Run(_UPDATE), _Run(_DELETE)] + for idx, (op_type, operation) in enumerate(self.ops): + operations[op_type].add(idx, operation) + + for run in operations: + if run.ops: + yield run + + def _execute_command(self, generator, write_concern, session, + sock_info, op_id, retryable, full_result): + if sock_info.max_wire_version < 5 and self.uses_collation: + raise ConfigurationError( + 'Must be connected to MongoDB 3.4+ to use a collation.') + if sock_info.max_wire_version < 6 and self.uses_array_filters: + raise ConfigurationError( + 'Must be connected to MongoDB 3.6+ to use arrayFilters.') + + db_name = self.collection.database.name + client = self.collection.database.client + listeners = client._event_listeners + + if not self.current_run: + self.current_run = next(generator) + run = self.current_run + + # sock_info.command validates the session, but we use + # sock_info.write_command. + sock_info.validate_session(client, session) + while run: + cmd = SON([(_COMMANDS[run.op_type], self.collection.name), + ('ordered', self.ordered)]) + if not write_concern.is_server_default: + cmd['writeConcern'] = write_concern.document + if self.bypass_doc_val and sock_info.max_wire_version >= 4: + cmd['bypassDocumentValidation'] = True + bwc = _BulkWriteContext(db_name, cmd, sock_info, op_id, + listeners, session) + + while run.idx_offset < len(run.ops): + if session: + # Start a new retryable write unless one was already + # started for this command. + if retryable and not self.started_retryable_write: + session._start_retryable_write() + self.started_retryable_write = True + session._apply_to(cmd, retryable, ReadPreference.PRIMARY, + sock_info) + sock_info.send_cluster_time(cmd, session, client) + check_keys = run.op_type == _INSERT + ops = islice(run.ops, run.idx_offset, None) + # Run as many ops as possible. + request_id, msg, to_send = _do_bulk_write_command( + self.namespace, run.op_type, cmd, ops, check_keys, + self.collection.codec_options, bwc) + if not to_send: + raise InvalidOperation("cannot do an empty bulk write") + result = bwc.write_command(request_id, msg, to_send) + client._receive_cluster_time(result, session) + + # Retryable writeConcernErrors halt the execution of this run. + wce = result.get('writeConcernError', {}) + if wce.get('code', 0) in _RETRYABLE_ERROR_CODES: + # Synthesize the full bulk result without modifying the + # current one because this write operation may be retried. + full = copy.deepcopy(full_result) + _merge_command(run, full, run.idx_offset, result) + _raise_bulk_write_error(full) + + _merge_command(run, full_result, run.idx_offset, result) + # We're no longer in a retry once a command succeeds. + self.retrying = False + self.started_retryable_write = False + + if self.ordered and "writeErrors" in result: + break + run.idx_offset += len(to_send) + + # We're supposed to continue if errors are + # at the write concern level (e.g. wtimeout) + if self.ordered and full_result['writeErrors']: + break + # Reset our state + self.current_run = run = next(generator, None) + + def execute_command(self, generator, write_concern, session): + """Execute using write commands. + """ + # nModified is only reported for write commands, not legacy ops. + full_result = { + "writeErrors": [], + "writeConcernErrors": [], + "nInserted": 0, + "nUpserted": 0, + "nMatched": 0, + "nModified": 0, + "nRemoved": 0, + "upserted": [], + } + op_id = _randint() + + def retryable_bulk(session, sock_info, retryable): + self._execute_command( + generator, write_concern, session, sock_info, op_id, + retryable, full_result) + + client = self.collection.database.client + with client._tmp_session(session) as s: + client._retry_with_session( + self.is_retryable, retryable_bulk, s, self) + + if full_result["writeErrors"] or full_result["writeConcernErrors"]: + _raise_bulk_write_error(full_result) + return full_result + + def execute_insert_no_results(self, sock_info, run, op_id, acknowledged): + """Execute insert, returning no results. + """ + command = SON([('insert', self.collection.name), + ('ordered', self.ordered)]) + concern = {'w': int(self.ordered)} + command['writeConcern'] = concern + if self.bypass_doc_val and sock_info.max_wire_version >= 4: + command['bypassDocumentValidation'] = True + db = self.collection.database + bwc = _BulkWriteContext( + db.name, command, sock_info, op_id, db.client._event_listeners, + session=None) + # Legacy batched OP_INSERT. + _do_batched_insert( + self.collection.full_name, run.ops, True, acknowledged, concern, + not self.ordered, self.collection.codec_options, bwc) + + def execute_op_msg_no_results(self, sock_info, generator): + """Execute write commands with OP_MSG and w=0 writeConcern, unordered. + """ + db_name = self.collection.database.name + client = self.collection.database.client + listeners = client._event_listeners + op_id = _randint() + + if not self.current_run: + self.current_run = next(generator) + run = self.current_run + + while run: + cmd = SON([(_COMMANDS[run.op_type], self.collection.name), + ('ordered', False), + ('writeConcern', {'w': 0})]) + bwc = _BulkWriteContext(db_name, cmd, sock_info, op_id, + listeners, None) + + while run.idx_offset < len(run.ops): + check_keys = run.op_type == _INSERT + ops = islice(run.ops, run.idx_offset, None) + # Run as many ops as possible. + request_id, msg, to_send = _do_bulk_write_command( + self.namespace, run.op_type, cmd, ops, check_keys, + self.collection.codec_options, bwc) + if not to_send: + raise InvalidOperation("cannot do an empty bulk write") + run.idx_offset += len(to_send) + # Though this isn't strictly a "legacy" write, the helper + # handles publishing commands and sending our message + # without receiving a result. Send 0 for max_doc_size + # to disable size checking. Size checking is handled while + # the documents are encoded to BSON. + bwc.legacy_write(request_id, msg, 0, False, to_send) + self.current_run = run = next(generator, None) + + def execute_command_no_results(self, sock_info, generator): + """Execute write commands with OP_MSG and w=0 WriteConcern, ordered. + """ + full_result = { + "writeErrors": [], + "writeConcernErrors": [], + "nInserted": 0, + "nUpserted": 0, + "nMatched": 0, + "nModified": 0, + "nRemoved": 0, + "upserted": [], + } + # Ordered bulk writes have to be acknowledged so that we stop + # processing at the first error, even when the application + # specified unacknowledged writeConcern. + write_concern = WriteConcern() + op_id = _randint() + try: + self._execute_command( + generator, write_concern, None, + sock_info, op_id, False, full_result) + except OperationFailure: + pass + + def execute_no_results(self, sock_info, generator): + """Execute all operations, returning no results (w=0). + """ + if self.uses_collation: + raise ConfigurationError( + 'Collation is unsupported for unacknowledged writes.') + if self.uses_array_filters: + raise ConfigurationError( + 'arrayFilters is unsupported for unacknowledged writes.') + # Cannot have both unacknowledged writes and bypass document validation. + if self.bypass_doc_val and sock_info.max_wire_version >= 4: + raise OperationFailure("Cannot set bypass_document_validation with" + " unacknowledged write concern") + + # OP_MSG + if sock_info.max_wire_version > 5: + if self.ordered: + return self.execute_command_no_results(sock_info, generator) + return self.execute_op_msg_no_results(sock_info, generator) + + coll = self.collection + # If ordered is True we have to send GLE or use write + # commands so we can abort on the first error. + write_concern = WriteConcern(w=int(self.ordered)) + op_id = _randint() + + next_run = next(generator) + while next_run: + # An ordered bulk write needs to send acknowledged writes to short + # circuit the next run. However, the final message on the final + # run can be unacknowledged. + run = next_run + next_run = next(generator, None) + needs_ack = self.ordered and next_run is not None + try: + if run.op_type == _INSERT: + self.execute_insert_no_results( + sock_info, run, op_id, needs_ack) + elif run.op_type == _UPDATE: + for operation in run.ops: + doc = operation['u'] + check_keys = True + if doc and next(iter(doc)).startswith('$'): + check_keys = False + coll._update( + sock_info, + operation['q'], + doc, + operation['upsert'], + check_keys, + operation['multi'], + write_concern=write_concern, + op_id=op_id, + ordered=self.ordered, + bypass_doc_val=self.bypass_doc_val) + else: + for operation in run.ops: + coll._delete(sock_info, + operation['q'], + not operation['limit'], + write_concern, + op_id, + self.ordered) + except OperationFailure: + if self.ordered: + break + + def execute(self, write_concern, session): + """Execute operations. + """ + if not self.ops: + raise InvalidOperation('No operations to execute') + if self.executed: + raise InvalidOperation('Bulk operations can ' + 'only be executed once.') + self.executed = True + write_concern = write_concern or self.collection.write_concern + session = _validate_session_write_concern(session, write_concern) + + if self.ordered: + generator = self.gen_ordered() + else: + generator = self.gen_unordered() + + client = self.collection.database.client + if not write_concern.acknowledged: + with client._socket_for_writes() as sock_info: + self.execute_no_results(sock_info, generator) + else: + return self.execute_command(generator, write_concern, session) + + +class BulkUpsertOperation(object): + """An interface for adding upsert operations. + """ + + __slots__ = ('__selector', '__bulk', '__collation') + + def __init__(self, selector, bulk, collation): + self.__selector = selector + self.__bulk = bulk + self.__collation = collation + + def update_one(self, update): + """Update one document matching the selector. + + :Parameters: + - `update` (dict): the update operations to apply + """ + self.__bulk.add_update(self.__selector, + update, multi=False, upsert=True, + collation=self.__collation) + + def update(self, update): + """Update all documents matching the selector. + + :Parameters: + - `update` (dict): the update operations to apply + """ + self.__bulk.add_update(self.__selector, + update, multi=True, upsert=True, + collation=self.__collation) + + def replace_one(self, replacement): + """Replace one entire document matching the selector criteria. + + :Parameters: + - `replacement` (dict): the replacement document + """ + self.__bulk.add_replace(self.__selector, replacement, upsert=True, + collation=self.__collation) + + +class BulkWriteOperation(object): + """An interface for adding update or remove operations. + """ + + __slots__ = ('__selector', '__bulk', '__collation') + + def __init__(self, selector, bulk, collation): + self.__selector = selector + self.__bulk = bulk + self.__collation = collation + + def update_one(self, update): + """Update one document matching the selector criteria. + + :Parameters: + - `update` (dict): the update operations to apply + """ + self.__bulk.add_update(self.__selector, update, multi=False, + collation=self.__collation) + + def update(self, update): + """Update all documents matching the selector criteria. + + :Parameters: + - `update` (dict): the update operations to apply + """ + self.__bulk.add_update(self.__selector, update, multi=True, + collation=self.__collation) + + def replace_one(self, replacement): + """Replace one entire document matching the selector criteria. + + :Parameters: + - `replacement` (dict): the replacement document + """ + self.__bulk.add_replace(self.__selector, replacement, + collation=self.__collation) + + def remove_one(self): + """Remove a single document matching the selector criteria. + """ + self.__bulk.add_delete(self.__selector, _DELETE_ONE, + collation=self.__collation) + + def remove(self): + """Remove all documents matching the selector criteria. + """ + self.__bulk.add_delete(self.__selector, _DELETE_ALL, + collation=self.__collation) + + def upsert(self): + """Specify that all chained update operations should be + upserts. + + :Returns: + - A :class:`BulkUpsertOperation` instance, used to add + update operations to this bulk operation. + """ + return BulkUpsertOperation(self.__selector, self.__bulk, + self.__collation) + + +class BulkOperationBuilder(object): + """**DEPRECATED**: An interface for executing a batch of write operations. + """ + + __slots__ = '__bulk' + + def __init__(self, collection, ordered=True, + bypass_document_validation=False): + """**DEPRECATED**: Initialize a new BulkOperationBuilder instance. + + :Parameters: + - `collection`: A :class:`~pymongo.collection.Collection` instance. + - `ordered` (optional): If ``True`` all operations will be executed + serially, in the order provided, and the entire execution will + abort on the first error. If ``False`` operations will be executed + in arbitrary order (possibly in parallel on the server), reporting + any errors that occurred after attempting all operations. Defaults + to ``True``. + - `bypass_document_validation`: (optional) If ``True``, allows the + write to opt-out of document level validation. Default is + ``False``. + + .. note:: `bypass_document_validation` requires server version + **>= 3.2** + + .. versionchanged:: 3.5 + Deprecated. Use :meth:`~pymongo.collection.Collection.bulk_write` + instead. + + .. versionchanged:: 3.2 + Added bypass_document_validation support + """ + self.__bulk = _Bulk(collection, ordered, bypass_document_validation) + + def find(self, selector, collation=None): + """Specify selection criteria for bulk operations. + + :Parameters: + - `selector` (dict): the selection criteria for update + and remove operations. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only + supported on MongoDB 3.4 and above. + + :Returns: + - A :class:`BulkWriteOperation` instance, used to add + update and remove operations to this bulk operation. + + .. versionchanged:: 3.4 + Added the `collation` option. + + """ + validate_is_mapping("selector", selector) + return BulkWriteOperation(selector, self.__bulk, collation) + + def insert(self, document): + """Insert a single document. + + :Parameters: + - `document` (dict): the document to insert + + .. seealso:: :ref:`writes-and-ids` + """ + self.__bulk.add_insert(document) + + def execute(self, write_concern=None): + """Execute all provided operations. + + :Parameters: + - write_concern (optional): the write concern for this bulk + execution. + """ + if write_concern is not None: + write_concern = WriteConcern(**write_concern) + return self.__bulk.execute(write_concern, session=None) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/change_stream.py b/backend/venv/lib/python3.7/site-packages/pymongo/change_stream.py new file mode 100644 index 000000000..920daae7a --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/change_stream.py @@ -0,0 +1,334 @@ +# Copyright 2017 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Watch changes on a collection, a database, or the entire cluster.""" + +import copy + +from bson import _bson_to_dict +from bson.raw_bson import RawBSONDocument +from bson.son import SON + +from pymongo import common +from pymongo.collation import validate_collation_or_none +from pymongo.command_cursor import CommandCursor +from pymongo.errors import (ConnectionFailure, + InvalidOperation, + OperationFailure, + PyMongoError) + + +# The change streams spec considers the following server errors from the +# getMore command non-resumable. All other getMore errors are resumable. +_NON_RESUMABLE_GETMORE_ERRORS = frozenset([ + 11601, # Interrupted + 136, # CappedPositionLost + 237, # CursorKilled + None, # No error code was returned. +]) + + +class ChangeStream(object): + """The internal abstract base class for change stream cursors. + + Should not be called directly by application developers. Use + :meth:`pymongo.collection.Collection.watch`, + :meth:`pymongo.database.Database.watch`, or + :meth:`pymongo.mongo_client.MongoClient.watch` instead. + + .. versionadded:: 3.6 + .. mongodoc:: changeStreams + """ + def __init__(self, target, pipeline, full_document, resume_after, + max_await_time_ms, batch_size, collation, + start_at_operation_time, session): + if pipeline is None: + pipeline = [] + elif not isinstance(pipeline, list): + raise TypeError("pipeline must be a list") + + common.validate_string_or_none('full_document', full_document) + validate_collation_or_none(collation) + common.validate_non_negative_integer_or_none("batchSize", batch_size) + + self._decode_custom = False + self._orig_codec_options = target.codec_options + if target.codec_options.type_registry._decoder_map: + self._decode_custom = True + # Keep the type registry so that we support encoding custom types + # in the pipeline. + self._target = target.with_options( + codec_options=target.codec_options.with_options( + document_class=RawBSONDocument)) + else: + self._target = target + + self._pipeline = copy.deepcopy(pipeline) + self._full_document = full_document + self._resume_token = copy.deepcopy(resume_after) + self._max_await_time_ms = max_await_time_ms + self._batch_size = batch_size + self._collation = collation + self._start_at_operation_time = start_at_operation_time + self._session = session + self._cursor = self._create_cursor() + + @property + def _aggregation_target(self): + """The argument to pass to the aggregate command.""" + raise NotImplementedError + + @property + def _database(self): + """The database against which the aggregation commands for + this ChangeStream will be run. """ + raise NotImplementedError + + def _pipeline_options(self): + options = {} + if self._full_document is not None: + options['fullDocument'] = self._full_document + if self._resume_token is not None: + options['resumeAfter'] = self._resume_token + if self._start_at_operation_time is not None: + options['startAtOperationTime'] = self._start_at_operation_time + return options + + def _full_pipeline(self): + """Return the full aggregation pipeline for this ChangeStream.""" + options = self._pipeline_options() + full_pipeline = [{'$changeStream': options}] + full_pipeline.extend(self._pipeline) + return full_pipeline + + def _run_aggregation_cmd(self, session, explicit_session): + """Run the full aggregation pipeline for this ChangeStream and return + the corresponding CommandCursor. + """ + read_preference = self._target._read_preference_for(session) + client = self._database.client + with client._socket_for_reads(read_preference) as (sock_info, slave_ok): + pipeline = self._full_pipeline() + cmd = SON([("aggregate", self._aggregation_target), + ("pipeline", pipeline), + ("cursor", {})]) + + result = sock_info.command( + self._database.name, + cmd, + slave_ok, + read_preference, + self._target.codec_options, + parse_write_concern_error=True, + read_concern=self._target.read_concern, + collation=self._collation, + session=session, + client=self._database.client) + + cursor = result["cursor"] + + if (self._start_at_operation_time is None and + self._resume_token is None and + cursor.get("_id") is None and + sock_info.max_wire_version >= 7): + self._start_at_operation_time = result["operationTime"] + + ns = cursor["ns"] + _, collname = ns.split(".", 1) + aggregation_collection = self._database.get_collection( + collname, codec_options=self._target.codec_options, + read_preference=read_preference, + write_concern=self._target.write_concern, + read_concern=self._target.read_concern + ) + + return CommandCursor( + aggregation_collection, cursor, sock_info.address, + batch_size=self._batch_size or 0, + max_await_time_ms=self._max_await_time_ms, + session=session, explicit_session=explicit_session) + + def _create_cursor(self): + with self._database.client._tmp_session(self._session, close=False) as s: + return self._run_aggregation_cmd( + session=s, + explicit_session=self._session is not None + ) + + def _resume(self): + """Reestablish this change stream after a resumable error.""" + try: + self._cursor.close() + except PyMongoError: + pass + self._cursor = self._create_cursor() + + def close(self): + """Close this ChangeStream.""" + self._cursor.close() + + def __iter__(self): + return self + + def next(self): + """Advance the cursor. + + This method blocks until the next change document is returned or an + unrecoverable error is raised. This method is used when iterating over + all changes in the cursor. For example:: + + try: + with db.collection.watch( + [{'$match': {'operationType': 'insert'}}]) as stream: + for insert_change in stream: + print(insert_change) + except pymongo.errors.PyMongoError: + # The ChangeStream encountered an unrecoverable error or the + # resume attempt failed to recreate the cursor. + logging.error('...') + + Raises :exc:`StopIteration` if this ChangeStream is closed. + """ + while self.alive: + doc = self.try_next() + if doc is not None: + return doc + + raise StopIteration + + __next__ = next + + @property + def alive(self): + """Does this cursor have the potential to return more data? + + .. note:: Even if :attr:`alive` is ``True``, :meth:`next` can raise + :exc:`StopIteration` and :meth:`try_next` can return ``None``. + + .. versionadded:: 3.8 + """ + return self._cursor.alive + + def try_next(self): + """Advance the cursor without blocking indefinitely. + + This method returns the next change document without waiting + indefinitely for the next change. For example:: + + with db.collection.watch() as stream: + while stream.alive: + change = stream.try_next() + if change is not None: + print(change) + elif stream.alive: + # We end up here when there are no recent changes. + # Sleep for a while to avoid flooding the server with + # getMore requests when no changes are available. + time.sleep(10) + + If no change document is cached locally then this method runs a single + getMore command. If the getMore yields any documents, the next + document is returned, otherwise, if the getMore returns no documents + (because there have been no changes) then ``None`` is returned. + + :Returns: + The next change document or ``None`` when no document is available + after running a single getMore or when the cursor is closed. + + .. versionadded:: 3.8 + """ + # Attempt to get the next change with at most one getMore and at most + # one resume attempt. + try: + change = self._cursor._try_next(True) + except ConnectionFailure: + self._resume() + change = self._cursor._try_next(False) + except OperationFailure as exc: + if exc.code in _NON_RESUMABLE_GETMORE_ERRORS: + raise + self._resume() + change = self._cursor._try_next(False) + + # No changes are available. + if change is None: + return None + + try: + resume_token = change['_id'] + except KeyError: + self.close() + raise InvalidOperation( + "Cannot provide resume functionality when the resume " + "token is missing.") + self._resume_token = copy.copy(resume_token) + self._start_at_operation_time = None + + if self._decode_custom: + return _bson_to_dict(change.raw, self._orig_codec_options) + return change + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + +class CollectionChangeStream(ChangeStream): + """A change stream that watches changes on a single collection. + + Should not be called directly by application developers. Use + helper method :meth:`pymongo.collection.Collection.watch` instead. + + .. versionadded:: 3.7 + """ + @property + def _aggregation_target(self): + return self._target.name + + @property + def _database(self): + return self._target.database + + +class DatabaseChangeStream(ChangeStream): + """A change stream that watches changes on all collections in a database. + + Should not be called directly by application developers. Use + helper method :meth:`pymongo.database.Database.watch` instead. + + .. versionadded:: 3.7 + """ + @property + def _aggregation_target(self): + return 1 + + @property + def _database(self): + return self._target + + +class ClusterChangeStream(DatabaseChangeStream): + """A change stream that watches changes on all collections in the cluster. + + Should not be called directly by application developers. Use + helper method :meth:`pymongo.mongo_client.MongoClient.watch` instead. + + .. versionadded:: 3.7 + """ + def _pipeline_options(self): + options = super(ClusterChangeStream, self)._pipeline_options() + options["allChangesForCluster"] = True + return options diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/client_options.py b/backend/venv/lib/python3.7/site-packages/pymongo/client_options.py new file mode 100644 index 000000000..3c3865b32 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/client_options.py @@ -0,0 +1,237 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Tools to parse mongo client options.""" + +from bson.codec_options import _parse_codec_options +from pymongo.auth import _build_credentials_tuple +from pymongo.common import validate_boolean +from pymongo import common +from pymongo.compression_support import CompressionSettings +from pymongo.errors import ConfigurationError +from pymongo.monitoring import _EventListeners +from pymongo.pool import PoolOptions +from pymongo.read_concern import ReadConcern +from pymongo.read_preferences import (make_read_preference, + read_pref_mode_from_name) +from pymongo.server_selectors import any_server_selector +from pymongo.ssl_support import get_ssl_context +from pymongo.write_concern import WriteConcern + + +def _parse_credentials(username, password, database, options): + """Parse authentication credentials.""" + mechanism = options.get('authmechanism', 'DEFAULT' if username else None) + source = options.get('authsource') + if username or mechanism: + return _build_credentials_tuple( + mechanism, source, username, password, options, database) + return None + + +def _parse_read_preference(options): + """Parse read preference options.""" + if 'read_preference' in options: + return options['read_preference'] + + name = options.get('readpreference', 'primary') + mode = read_pref_mode_from_name(name) + tags = options.get('readpreferencetags') + max_staleness = options.get('maxstalenessseconds', -1) + return make_read_preference(mode, tags, max_staleness) + + +def _parse_write_concern(options): + """Parse write concern options.""" + concern = options.get('w') + wtimeout = options.get('wtimeout', options.get('wtimeoutms')) + j = options.get('j', options.get('journal')) + fsync = options.get('fsync') + return WriteConcern(concern, wtimeout, j, fsync) + + +def _parse_read_concern(options): + """Parse read concern options.""" + concern = options.get('readconcernlevel') + return ReadConcern(concern) + + +def _parse_ssl_options(options): + """Parse ssl options.""" + use_ssl = options.get('ssl') + if use_ssl is not None: + validate_boolean('ssl', use_ssl) + + certfile = options.get('ssl_certfile') + keyfile = options.get('ssl_keyfile') + passphrase = options.get('ssl_pem_passphrase') + ca_certs = options.get('ssl_ca_certs') + cert_reqs = options.get('ssl_cert_reqs') + match_hostname = options.get('ssl_match_hostname', True) + crlfile = options.get('ssl_crlfile') + + ssl_kwarg_keys = [k for k in options + if k.startswith('ssl_') and options[k]] + if use_ssl == False and ssl_kwarg_keys: + raise ConfigurationError("ssl has not been enabled but the " + "following ssl parameters have been set: " + "%s. Please set `ssl=True` or remove." + % ', '.join(ssl_kwarg_keys)) + + if ssl_kwarg_keys and use_ssl is None: + # ssl options imply ssl = True + use_ssl = True + + if use_ssl is True: + ctx = get_ssl_context( + certfile, + keyfile, + passphrase, + ca_certs, + cert_reqs, + crlfile, + match_hostname) + return ctx, match_hostname + return None, match_hostname + + +def _parse_pool_options(options): + """Parse connection pool options.""" + max_pool_size = options.get('maxpoolsize', common.MAX_POOL_SIZE) + min_pool_size = options.get('minpoolsize', common.MIN_POOL_SIZE) + default_idle_seconds = common.validate_timeout_or_none( + 'maxidletimems', common.MAX_IDLE_TIME_MS) + max_idle_time_seconds = options.get('maxidletimems', default_idle_seconds) + if max_pool_size is not None and min_pool_size > max_pool_size: + raise ValueError("minPoolSize must be smaller or equal to maxPoolSize") + connect_timeout = options.get('connecttimeoutms', common.CONNECT_TIMEOUT) + socket_keepalive = options.get('socketkeepalive', True) + socket_timeout = options.get('sockettimeoutms') + wait_queue_timeout = options.get('waitqueuetimeoutms') + wait_queue_multiple = options.get('waitqueuemultiple') + event_listeners = options.get('event_listeners') + appname = options.get('appname') + driver = options.get('driver') + compression_settings = CompressionSettings( + options.get('compressors', []), + options.get('zlibcompressionlevel', -1)) + ssl_context, ssl_match_hostname = _parse_ssl_options(options) + return PoolOptions(max_pool_size, + min_pool_size, + max_idle_time_seconds, + connect_timeout, socket_timeout, + wait_queue_timeout, wait_queue_multiple, + ssl_context, ssl_match_hostname, socket_keepalive, + _EventListeners(event_listeners), + appname, + driver, + compression_settings) + + +class ClientOptions(object): + + """ClientOptions""" + + def __init__(self, username, password, database, options): + self.__options = options + + self.__codec_options = _parse_codec_options(options) + self.__credentials = _parse_credentials( + username, password, database, options) + self.__local_threshold_ms = options.get( + 'localthresholdms', common.LOCAL_THRESHOLD_MS) + # self.__server_selection_timeout is in seconds. Must use full name for + # common.SERVER_SELECTION_TIMEOUT because it is set directly by tests. + self.__server_selection_timeout = options.get( + 'serverselectiontimeoutms', common.SERVER_SELECTION_TIMEOUT) + self.__pool_options = _parse_pool_options(options) + self.__read_preference = _parse_read_preference(options) + self.__replica_set_name = options.get('replicaset') + self.__write_concern = _parse_write_concern(options) + self.__read_concern = _parse_read_concern(options) + self.__connect = options.get('connect') + self.__heartbeat_frequency = options.get( + 'heartbeatfrequencyms', common.HEARTBEAT_FREQUENCY) + self.__retry_writes = options.get('retrywrites', common.RETRY_WRITES) + self.__server_selector = options.get( + 'server_selector', any_server_selector) + + @property + def _options(self): + """The original options used to create this ClientOptions.""" + return self.__options + + @property + def connect(self): + """Whether to begin discovering a MongoDB topology automatically.""" + return self.__connect + + @property + def codec_options(self): + """A :class:`~bson.codec_options.CodecOptions` instance.""" + return self.__codec_options + + @property + def credentials(self): + """A :class:`~pymongo.auth.MongoCredentials` instance or None.""" + return self.__credentials + + @property + def local_threshold_ms(self): + """The local threshold for this instance.""" + return self.__local_threshold_ms + + @property + def server_selection_timeout(self): + """The server selection timeout for this instance in seconds.""" + return self.__server_selection_timeout + + @property + def server_selector(self): + return self.__server_selector + + @property + def heartbeat_frequency(self): + """The monitoring frequency in seconds.""" + return self.__heartbeat_frequency + + @property + def pool_options(self): + """A :class:`~pymongo.pool.PoolOptions` instance.""" + return self.__pool_options + + @property + def read_preference(self): + """A read preference instance.""" + return self.__read_preference + + @property + def replica_set_name(self): + """Replica set name or None.""" + return self.__replica_set_name + + @property + def write_concern(self): + """A :class:`~pymongo.write_concern.WriteConcern` instance.""" + return self.__write_concern + + @property + def read_concern(self): + """A :class:`~pymongo.read_concern.ReadConcern` instance.""" + return self.__read_concern + + @property + def retry_writes(self): + """If this instance should retry supported write operations.""" + return self.__retry_writes diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/client_session.py b/backend/venv/lib/python3.7/site-packages/pymongo/client_session.py new file mode 100644 index 000000000..d610f61d8 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/client_session.py @@ -0,0 +1,655 @@ +# Copyright 2017 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Logical sessions for ordering sequential operations. + +Requires MongoDB 3.6. + +.. versionadded:: 3.6 + +Causally Consistent Reads +========================= + +.. code-block:: python + + with client.start_session(causal_consistency=True) as session: + collection = client.db.collection + collection.update_one({'_id': 1}, {'$set': {'x': 10}}, session=session) + secondary_c = collection.with_options( + read_preference=ReadPreference.SECONDARY) + + # A secondary read waits for replication of the write. + secondary_c.find_one({'_id': 1}, session=session) + +If `causal_consistency` is True (the default), read operations that use +the session are causally after previous read and write operations. Using a +causally consistent session, an application can read its own writes and is +guaranteed monotonic reads, even when reading from replica set secondaries. + +.. mongodoc:: causal-consistency + +.. _transactions-ref: + +Transactions +============ + +MongoDB 4.0 adds support for transactions on replica set primaries. A +transaction is associated with a :class:`ClientSession`. To start a transaction +on a session, use :meth:`ClientSession.start_transaction` in a with-statement. +Then, execute an operation within the transaction by passing the session to the +operation: + +.. code-block:: python + + orders = client.db.orders + inventory = client.db.inventory + with client.start_session() as session: + with session.start_transaction(): + orders.insert_one({"sku": "abc123", "qty": 100}, session=session) + inventory.update_one({"sku": "abc123", "qty": {"$gte": 100}}, + {"$inc": {"qty": -100}}, session=session) + +Upon normal completion of ``with session.start_transaction()`` block, the +transaction automatically calls :meth:`ClientSession.commit_transaction`. +If the block exits with an exception, the transaction automatically calls +:meth:`ClientSession.abort_transaction`. + +For multi-document transactions, you can only specify read/write (CRUD) +operations on existing collections. For example, a multi-document transaction +cannot include a create or drop collection/index operations, including an +insert operation that would result in the creation of a new collection. + +A session may only have a single active transaction at a time, multiple +transactions on the same session can be executed in sequence. + +.. versionadded:: 3.7 + +.. mongodoc:: transactions + +Classes +======= +""" + +import collections +import sys +import uuid + +from bson.binary import Binary +from bson.int64 import Int64 +from bson.py3compat import abc, reraise_instance +from bson.timestamp import Timestamp + +from pymongo import monotonic, __version__ +from pymongo.errors import (ConfigurationError, + ConnectionFailure, + InvalidOperation, + OperationFailure, + ServerSelectionTimeoutError, + WriteConcernError, + WTimeoutError) +from pymongo.helpers import _RETRYABLE_ERROR_CODES +from pymongo.read_concern import ReadConcern +from pymongo.read_preferences import ReadPreference, _ServerMode +from pymongo.write_concern import WriteConcern + + +class SessionOptions(object): + """Options for a new :class:`ClientSession`. + + :Parameters: + - `causal_consistency` (optional): If True (the default), read + operations are causally ordered within the session. + - `default_transaction_options` (optional): The default + TransactionOptions to use for transactions started on this session. + """ + def __init__(self, + causal_consistency=True, + default_transaction_options=None): + self._causal_consistency = causal_consistency + if default_transaction_options is not None: + if not isinstance(default_transaction_options, TransactionOptions): + raise TypeError( + "default_transaction_options must be an instance of " + "pymongo.client_session.TransactionOptions, not: %r" % + (default_transaction_options,)) + self._default_transaction_options = default_transaction_options + + @property + def causal_consistency(self): + """Whether causal consistency is configured.""" + return self._causal_consistency + + @property + def default_transaction_options(self): + """The default TransactionOptions to use for transactions started on + this session. + + .. versionadded:: 3.7 + """ + return self._default_transaction_options + + +class TransactionOptions(object): + """Options for :meth:`ClientSession.start_transaction`. + + :Parameters: + - `read_concern`: The :class:`~pymongo.read_concern.ReadConcern` to use + for this transaction. + - `write_concern`: The :class:`~pymongo.write_concern.WriteConcern` to + use for this transaction. + + .. versionadded:: 3.7 + """ + def __init__(self, read_concern=None, write_concern=None, + read_preference=None): + self._read_concern = read_concern + self._write_concern = write_concern + self._read_preference = read_preference + if read_concern is not None: + if not isinstance(read_concern, ReadConcern): + raise TypeError("read_concern must be an instance of " + "pymongo.read_concern.ReadConcern, not: %r" % + (read_concern,)) + if write_concern is not None: + if not isinstance(write_concern, WriteConcern): + raise TypeError("write_concern must be an instance of " + "pymongo.write_concern.WriteConcern, not: %r" % + (write_concern,)) + if not write_concern.acknowledged: + raise ConfigurationError( + "transactions do not support unacknowledged write concern" + ": %r" % (write_concern,)) + if read_preference is not None: + if not isinstance(read_preference, _ServerMode): + raise TypeError("%r is not valid for read_preference. See " + "pymongo.read_preferences for valid " + "options." % (read_preference,)) + + @property + def read_concern(self): + """This transaction's :class:`~pymongo.read_concern.ReadConcern`.""" + return self._read_concern + + @property + def write_concern(self): + """This transaction's :class:`~pymongo.write_concern.WriteConcern`.""" + return self._write_concern + + @property + def read_preference(self): + """This transaction's :class:`~pymongo.read_preferences.ReadPreference`. + """ + return self._read_preference + + +def _validate_session_write_concern(session, write_concern): + """Validate that an explicit session is not used with an unack'ed write. + + Returns the session to use for the next operation. + """ + if session: + if write_concern is not None and not write_concern.acknowledged: + # For unacknowledged writes without an explicit session, + # drivers SHOULD NOT use an implicit session. If a driver + # creates an implicit session for unacknowledged writes + # without an explicit session, the driver MUST NOT send the + # session ID. + if session._implicit: + return None + else: + raise ConfigurationError( + 'Explicit sessions are incompatible with ' + 'unacknowledged write concern: %r' % ( + write_concern,)) + return session + + +class _TransactionContext(object): + """Internal transaction context manager for start_transaction.""" + def __init__(self, session): + self.__session = session + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.__session._in_transaction: + if exc_val is None: + self.__session.commit_transaction() + else: + self.__session.abort_transaction() + + +class _TxnState(object): + NONE = 1 + STARTING = 2 + IN_PROGRESS = 3 + COMMITTED = 4 + COMMITTED_EMPTY = 5 + ABORTED = 6 + + +class _Transaction(object): + """Internal class to hold transaction information in a ClientSession.""" + def __init__(self, opts): + self.opts = opts + self.state = _TxnState.NONE + self.transaction_id = 0 + + def active(self): + return self.state in (_TxnState.STARTING, _TxnState.IN_PROGRESS) + + +def _reraise_with_unknown_commit(exc): + """Re-raise an exception with the UnknownTransactionCommitResult label.""" + exc._add_error_label("UnknownTransactionCommitResult") + reraise_instance(exc, trace=sys.exc_info()[2]) + + +# From the transactions spec, all the retryable writes errors plus +# WriteConcernFailed. +_UNKNOWN_COMMIT_ERROR_CODES = _RETRYABLE_ERROR_CODES | frozenset([ + 64, # WriteConcernFailed +]) + +_MONGOS_NOT_SUPPORTED_MSG = ( + 'PyMongo %s does not support running multi-document transactions on ' + 'sharded clusters') % (__version__,) + + +class ClientSession(object): + """A session for ordering sequential operations.""" + def __init__(self, client, server_session, options, authset, implicit): + # A MongoClient, a _ServerSession, a SessionOptions, and a set. + self._client = client + self._server_session = server_session + self._options = options + self._authset = authset + self._cluster_time = None + self._operation_time = None + # Is this an implicitly created session? + self._implicit = implicit + self._transaction = _Transaction(None) + + def end_session(self): + """Finish this session. If a transaction has started, abort it. + + It is an error to use the session after the session has ended. + """ + self._end_session(lock=True) + + def _end_session(self, lock): + if self._server_session is not None: + try: + if self._in_transaction: + self.abort_transaction() + finally: + self._client._return_server_session(self._server_session, lock) + self._server_session = None + + def _check_ended(self): + if self._server_session is None: + raise InvalidOperation("Cannot use ended session") + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._end_session(lock=True) + + @property + def client(self): + """The :class:`~pymongo.mongo_client.MongoClient` this session was + created from. + """ + return self._client + + @property + def options(self): + """The :class:`SessionOptions` this session was created with.""" + return self._options + + @property + def session_id(self): + """A BSON document, the opaque server session identifier.""" + self._check_ended() + return self._server_session.session_id + + @property + def cluster_time(self): + """The cluster time returned by the last operation executed + in this session. + """ + return self._cluster_time + + @property + def operation_time(self): + """The operation time returned by the last operation executed + in this session. + """ + return self._operation_time + + def _inherit_option(self, name, val): + """Return the inherited TransactionOption value.""" + if val: + return val + txn_opts = self.options.default_transaction_options + val = txn_opts and getattr(txn_opts, name) + if val: + return val + return getattr(self.client, name) + + def start_transaction(self, read_concern=None, write_concern=None, + read_preference=None): + """Start a multi-statement transaction. + + Takes the same arguments as :class:`TransactionOptions`. + + .. versionadded:: 3.7 + """ + self._check_ended() + + if self._client._is_mongos_non_blocking(): + raise ConfigurationError(_MONGOS_NOT_SUPPORTED_MSG) + + if self._in_transaction: + raise InvalidOperation("Transaction already in progress") + + read_concern = self._inherit_option("read_concern", read_concern) + write_concern = self._inherit_option("write_concern", write_concern) + read_preference = self._inherit_option( + "read_preference", read_preference) + + self._transaction.opts = TransactionOptions( + read_concern, write_concern, read_preference) + self._transaction.state = _TxnState.STARTING + self._start_retryable_write() + self._transaction.transaction_id = self._server_session.transaction_id + return _TransactionContext(self) + + def commit_transaction(self): + """Commit a multi-statement transaction. + + .. versionadded:: 3.7 + """ + self._check_ended() + + state = self._transaction.state + if state is _TxnState.NONE: + raise InvalidOperation("No transaction started") + elif state in (_TxnState.STARTING, _TxnState.COMMITTED_EMPTY): + # Server transaction was never started, no need to send a command. + self._transaction.state = _TxnState.COMMITTED_EMPTY + return + elif state is _TxnState.ABORTED: + raise InvalidOperation( + "Cannot call commitTransaction after calling abortTransaction") + + try: + self._finish_transaction_with_retry("commitTransaction") + except ConnectionFailure as exc: + # We do not know if the commit was successfully applied on the + # server or if it satisfied the provided write concern, set the + # unknown commit error label. + exc._remove_error_label("TransientTransactionError") + _reraise_with_unknown_commit(exc) + except WTimeoutError as exc: + # We do not know if the commit has satisfied the provided write + # concern, add the unknown commit error label. + _reraise_with_unknown_commit(exc) + except OperationFailure as exc: + if exc.code not in _UNKNOWN_COMMIT_ERROR_CODES: + # The server reports errorLabels in the case. + raise + # We do not know if the commit was successfully applied on the + # server or if it satisfied the provided write concern, set the + # unknown commit error label. + _reraise_with_unknown_commit(exc) + finally: + self._transaction.state = _TxnState.COMMITTED + + def abort_transaction(self): + """Abort a multi-statement transaction. + + .. versionadded:: 3.7 + """ + self._check_ended() + + state = self._transaction.state + if state is _TxnState.NONE: + raise InvalidOperation("No transaction started") + elif state is _TxnState.STARTING: + # Server transaction was never started, no need to send a command. + self._transaction.state = _TxnState.ABORTED + return + elif state is _TxnState.ABORTED: + raise InvalidOperation("Cannot call abortTransaction twice") + elif state in (_TxnState.COMMITTED, _TxnState.COMMITTED_EMPTY): + raise InvalidOperation( + "Cannot call abortTransaction after calling commitTransaction") + + try: + self._finish_transaction_with_retry("abortTransaction") + except (OperationFailure, ConnectionFailure): + # The transactions spec says to ignore abortTransaction errors. + pass + finally: + self._transaction.state = _TxnState.ABORTED + + def _finish_transaction(self, command_name): + with self._client._socket_for_writes() as sock_info: + return self._client.admin._command( + sock_info, + command_name, + txnNumber=self._transaction.transaction_id, + autocommit=False, + session=self, + write_concern=self._transaction.opts.write_concern, + parse_write_concern_error=True) + + def _finish_transaction_with_retry(self, command_name): + # This can be refactored with MongoClient._retry_with_session. + try: + return self._finish_transaction(command_name) + except ServerSelectionTimeoutError: + raise + except ConnectionFailure as exc: + try: + return self._finish_transaction(command_name) + except ServerSelectionTimeoutError: + # Raise the original error so the application can infer that + # an attempt was made. + raise exc + except OperationFailure as exc: + if exc.code not in _RETRYABLE_ERROR_CODES: + raise + try: + return self._finish_transaction(command_name) + except ServerSelectionTimeoutError: + # Raise the original error so the application can infer that + # an attempt was made. + raise exc + + def _advance_cluster_time(self, cluster_time): + """Internal cluster time helper.""" + if self._cluster_time is None: + self._cluster_time = cluster_time + elif cluster_time is not None: + if cluster_time["clusterTime"] > self._cluster_time["clusterTime"]: + self._cluster_time = cluster_time + + def advance_cluster_time(self, cluster_time): + """Update the cluster time for this session. + + :Parameters: + - `cluster_time`: The + :data:`~pymongo.client_session.ClientSession.cluster_time` from + another `ClientSession` instance. + """ + if not isinstance(cluster_time, abc.Mapping): + raise TypeError( + "cluster_time must be a subclass of collections.Mapping") + if not isinstance(cluster_time.get("clusterTime"), Timestamp): + raise ValueError("Invalid cluster_time") + self._advance_cluster_time(cluster_time) + + def _advance_operation_time(self, operation_time): + """Internal operation time helper.""" + if self._operation_time is None: + self._operation_time = operation_time + elif operation_time is not None: + if operation_time > self._operation_time: + self._operation_time = operation_time + + def advance_operation_time(self, operation_time): + """Update the operation time for this session. + + :Parameters: + - `operation_time`: The + :data:`~pymongo.client_session.ClientSession.operation_time` from + another `ClientSession` instance. + """ + if not isinstance(operation_time, Timestamp): + raise TypeError("operation_time must be an instance " + "of bson.timestamp.Timestamp") + self._advance_operation_time(operation_time) + + @property + def has_ended(self): + """True if this session is finished.""" + return self._server_session is None + + @property + def _in_transaction(self): + """True if this session has an active multi-statement transaction.""" + return self._transaction.active() + + def _txn_read_preference(self): + """Return read preference of this transaction or None.""" + if self._in_transaction: + return self._transaction.opts.read_preference + return None + + def _apply_to(self, command, is_retryable, read_preference, sock_info): + self._check_ended() + + self._server_session.last_use = monotonic.time() + command['lsid'] = self._server_session.session_id + + if not self._in_transaction: + self._transaction.state = _TxnState.NONE + + if is_retryable: + command['txnNumber'] = self._server_session.transaction_id + return + + if self._in_transaction: + if sock_info.is_mongos: + raise ConfigurationError(_MONGOS_NOT_SUPPORTED_MSG) + + if read_preference != ReadPreference.PRIMARY: + raise InvalidOperation( + 'read preference in a transaction must be primary, not: ' + '%r' % (read_preference,)) + + if self._transaction.state == _TxnState.STARTING: + # First command begins a new transaction. + self._transaction.state = _TxnState.IN_PROGRESS + command['startTransaction'] = True + + if self._transaction.opts.read_concern: + rc = self._transaction.opts.read_concern.document + else: + rc = {} + + if (self.options.causal_consistency + and self.operation_time is not None): + rc['afterClusterTime'] = self.operation_time + + if rc: + command['readConcern'] = rc + + command['txnNumber'] = self._server_session.transaction_id + command['autocommit'] = False + + def _start_retryable_write(self): + self._check_ended() + self._server_session.inc_transaction_id() + + +class _ServerSession(object): + def __init__(self): + # Ensure id is type 4, regardless of CodecOptions.uuid_representation. + self.session_id = {'id': Binary(uuid.uuid4().bytes, 4)} + self.last_use = monotonic.time() + self._transaction_id = 0 + + def timed_out(self, session_timeout_minutes): + idle_seconds = monotonic.time() - self.last_use + + # Timed out if we have less than a minute to live. + return idle_seconds > (session_timeout_minutes - 1) * 60 + + @property + def transaction_id(self): + """Positive 64-bit integer.""" + return Int64(self._transaction_id) + + def inc_transaction_id(self): + self._transaction_id += 1 + + +class _ServerSessionPool(collections.deque): + """Pool of _ServerSession objects. + + This class is not thread-safe, access it while holding the Topology lock. + """ + def pop_all(self): + ids = [] + while self: + ids.append(self.pop().session_id) + return ids + + def get_server_session(self, session_timeout_minutes): + # Although the Driver Sessions Spec says we only clear stale sessions + # in return_server_session, PyMongo can't take a lock when returning + # sessions from a __del__ method (like in Cursor.__die), so it can't + # clear stale sessions there. In case many sessions were returned via + # __del__, check for stale sessions here too. + self._clear_stale(session_timeout_minutes) + + # The most recently used sessions are on the left. + while self: + s = self.popleft() + if not s.timed_out(session_timeout_minutes): + return s + + return _ServerSession() + + def return_server_session(self, server_session, session_timeout_minutes): + self._clear_stale(session_timeout_minutes) + if not server_session.timed_out(session_timeout_minutes): + self.appendleft(server_session) + + def return_server_session_no_lock(self, server_session): + self.appendleft(server_session) + + def _clear_stale(self, session_timeout_minutes): + # Clear stale sessions. The least recently used are on the right. + while self: + if self[-1].timed_out(session_timeout_minutes): + self.pop() + else: + # The remaining sessions also haven't timed out. + break diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/collation.py b/backend/venv/lib/python3.7/site-packages/pymongo/collation.py new file mode 100644 index 000000000..873d60333 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/collation.py @@ -0,0 +1,225 @@ +# Copyright 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for working with `collations`_. + +.. _collations: http://userguide.icu-project.org/collation/concepts +""" + +from pymongo import common + + +class CollationStrength(object): + """ + An enum that defines values for `strength` on a + :class:`~pymongo.collation.Collation`. + """ + + PRIMARY = 1 + """Differentiate base (unadorned) characters.""" + + SECONDARY = 2 + """Differentiate character accents.""" + + TERTIARY = 3 + """Differentiate character case.""" + + QUATERNARY = 4 + """Differentiate words with and without punctuation.""" + + IDENTICAL = 5 + """Differentiate unicode code point (characters are exactly identical).""" + + +class CollationAlternate(object): + """ + An enum that defines values for `alternate` on a + :class:`~pymongo.collation.Collation`. + """ + + NON_IGNORABLE = 'non-ignorable' + """Spaces and punctuation are treated as base characters.""" + + SHIFTED = 'shifted' + """Spaces and punctuation are *not* considered base characters. + + Spaces and punctuation are distinguished regardless when the + :class:`~pymongo.collation.Collation` strength is at least + :data:`~pymongo.collation.CollationStrength.QUATERNARY`. + + """ + + +class CollationMaxVariable(object): + """ + An enum that defines values for `max_variable` on a + :class:`~pymongo.collation.Collation`. + """ + + PUNCT = 'punct' + """Both punctuation and spaces are ignored.""" + + SPACE = 'space' + """Spaces alone are ignored.""" + + +class CollationCaseFirst(object): + """ + An enum that defines values for `case_first` on a + :class:`~pymongo.collation.Collation`. + """ + + UPPER = 'upper' + """Sort uppercase characters first.""" + + LOWER = 'lower' + """Sort lowercase characters first.""" + + OFF = 'off' + """Default for locale or collation strength.""" + + +class Collation(object): + """Collation + + :Parameters: + - `locale`: (string) The locale of the collation. This should be a string + that identifies an `ICU locale ID` exactly. For example, ``en_US`` is + valid, but ``en_us`` and ``en-US`` are not. Consult the MongoDB + documentation for a list of supported locales. + - `caseLevel`: (optional) If ``True``, turn on case sensitivity if + `strength` is 1 or 2 (case sensitivity is implied if `strength` is + greater than 2). Defaults to ``False``. + - `caseFirst`: (optional) Specify that either uppercase or lowercase + characters take precedence. Must be one of the following values: + + * :data:`~CollationCaseFirst.UPPER` + * :data:`~CollationCaseFirst.LOWER` + * :data:`~CollationCaseFirst.OFF` (the default) + + - `strength`: (optional) Specify the comparison strength. This is also + known as the ICU comparison level. This must be one of the following + values: + + * :data:`~CollationStrength.PRIMARY` + * :data:`~CollationStrength.SECONDARY` + * :data:`~CollationStrength.TERTIARY` (the default) + * :data:`~CollationStrength.QUATERNARY` + * :data:`~CollationStrength.IDENTICAL` + + Each successive level builds upon the previous. For example, a + `strength` of :data:`~CollationStrength.SECONDARY` differentiates + characters based both on the unadorned base character and its accents. + + - `numericOrdering`: (optional) If ``True``, order numbers numerically + instead of in collation order (defaults to ``False``). + - `alternate`: (optional) Specify whether spaces and punctuation are + considered base characters. This must be one of the following values: + + * :data:`~CollationAlternate.NON_IGNORABLE` (the default) + * :data:`~CollationAlternate.SHIFTED` + + - `maxVariable`: (optional) When `alternate` is + :data:`~CollationAlternate.SHIFTED`, this option specifies what + characters may be ignored. This must be one of the following values: + + * :data:`~CollationMaxVariable.PUNCT` (the default) + * :data:`~CollationMaxVariable.SPACE` + + - `normalization`: (optional) If ``True``, normalizes text into Unicode + NFD. Defaults to ``False``. + - `backwards`: (optional) If ``True``, accents on characters are + considered from the back of the word to the front, as it is done in some + French dictionary ordering traditions. Defaults to ``False``. + - `kwargs`: (optional) Keyword arguments supplying any additional options + to be sent with this Collation object. + + .. versionadded: 3.4 + + """ + + __slots__ = ("__document",) + + def __init__(self, locale, + caseLevel=None, + caseFirst=None, + strength=None, + numericOrdering=None, + alternate=None, + maxVariable=None, + normalization=None, + backwards=None, + **kwargs): + locale = common.validate_string('locale', locale) + self.__document = {'locale': locale} + if caseLevel is not None: + self.__document['caseLevel'] = common.validate_boolean( + 'caseLevel', caseLevel) + if caseFirst is not None: + self.__document['caseFirst'] = common.validate_string( + 'caseFirst', caseFirst) + if strength is not None: + self.__document['strength'] = common.validate_integer( + 'strength', strength) + if numericOrdering is not None: + self.__document['numericOrdering'] = common.validate_boolean( + 'numericOrdering', numericOrdering) + if alternate is not None: + self.__document['alternate'] = common.validate_string( + 'alternate', alternate) + if maxVariable is not None: + self.__document['maxVariable'] = common.validate_string( + 'maxVariable', maxVariable) + if normalization is not None: + self.__document['normalization'] = common.validate_boolean( + 'normalization', normalization) + if backwards is not None: + self.__document['backwards'] = common.validate_boolean( + 'backwards', backwards) + self.__document.update(kwargs) + + @property + def document(self): + """The document representation of this collation. + + .. note:: + :class:`Collation` is immutable. Mutating the value of + :attr:`document` does not mutate this :class:`Collation`. + """ + return self.__document.copy() + + def __repr__(self): + document = self.document + return 'Collation(%s)' % ( + ', '.join('%s=%r' % (key, document[key]) for key in document),) + + def __eq__(self, other): + if isinstance(other, Collation): + return self.document == other.document + return NotImplemented + + def __ne__(self, other): + return not self == other + + +def validate_collation_or_none(value): + if value is None: + return None + if isinstance(value, Collation): + return value.document + if isinstance(value, dict): + return value + raise TypeError( + 'collation must be a dict, an instance of collation.Collation, ' + 'or None.') diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/collection.py b/backend/venv/lib/python3.7/site-packages/pymongo/collection.py new file mode 100644 index 000000000..2f1e521d2 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/collection.py @@ -0,0 +1,3356 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Collection level utilities for Mongo.""" + +import datetime +import warnings + +from bson.code import Code +from bson.objectid import ObjectId +from bson.py3compat import (_unicode, + abc, + integer_types, + string_type) +from bson.raw_bson import RawBSONDocument +from bson.codec_options import CodecOptions +from bson.son import SON +from pymongo import (common, + helpers, + message) +from pymongo.bulk import BulkOperationBuilder, _Bulk +from pymongo.command_cursor import CommandCursor, RawBatchCommandCursor +from pymongo.common import ORDERED_TYPES +from pymongo.collation import validate_collation_or_none +from pymongo.change_stream import CollectionChangeStream +from pymongo.cursor import Cursor, RawBatchCursor +from pymongo.errors import (BulkWriteError, + ConfigurationError, + InvalidName, + OperationFailure) +from pymongo.helpers import (_check_write_command_response, + _raise_last_error) +from pymongo.message import _UNICODE_REPLACE_CODEC_OPTIONS +from pymongo.operations import IndexModel +from pymongo.read_preferences import ReadPreference +from pymongo.results import (BulkWriteResult, + DeleteResult, + InsertOneResult, + InsertManyResult, + UpdateResult) +from pymongo.write_concern import WriteConcern + +_NO_OBJ_ERROR = "No matching object found" +_UJOIN = u"%s.%s" +_FIND_AND_MODIFY_DOC_FIELDS = {'value': 1} + + +class ReturnDocument(object): + """An enum used with + :meth:`~pymongo.collection.Collection.find_one_and_replace` and + :meth:`~pymongo.collection.Collection.find_one_and_update`. + """ + BEFORE = False + """Return the original document before it was updated/replaced, or + ``None`` if no document matches the query. + """ + AFTER = True + """Return the updated/replaced or inserted document.""" + + +class Collection(common.BaseObject): + """A Mongo collection. + """ + + def __init__(self, database, name, create=False, codec_options=None, + read_preference=None, write_concern=None, read_concern=None, + session=None, **kwargs): + """Get / create a Mongo collection. + + Raises :class:`TypeError` if `name` is not an instance of + :class:`basestring` (:class:`str` in python 3). Raises + :class:`~pymongo.errors.InvalidName` if `name` is not a valid + collection name. Any additional keyword arguments will be used + as options passed to the create command. See + :meth:`~pymongo.database.Database.create_collection` for valid + options. + + If `create` is ``True``, `collation` is specified, or any additional + keyword arguments are present, a ``create`` command will be + sent, using ``session`` if specified. Otherwise, a ``create`` command + will not be sent and the collection will be created implicitly on first + use. The optional ``session`` argument is *only* used for the ``create`` + command, it is not associated with the collection afterward. + + :Parameters: + - `database`: the database to get a collection from + - `name`: the name of the collection to get + - `create` (optional): if ``True``, force collection + creation even without options being set + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. If ``None`` (the + default) database.codec_options is used. + - `read_preference` (optional): The read preference to use. If + ``None`` (the default) database.read_preference is used. + - `write_concern` (optional): An instance of + :class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the + default) database.write_concern is used. + - `read_concern` (optional): An instance of + :class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the + default) database.read_concern is used. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. If a collation is provided, + it will be passed to the create collection command. This option is + only supported on MongoDB 3.4 and above. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession` that is used with + the create collection command + - `**kwargs` (optional): additional keyword arguments will + be passed as options for the create collection command + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.4 + Support the `collation` option. + + .. versionchanged:: 3.2 + Added the read_concern option. + + .. versionchanged:: 3.0 + Added the codec_options, read_preference, and write_concern options. + Removed the uuid_subtype attribute. + :class:`~pymongo.collection.Collection` no longer returns an + instance of :class:`~pymongo.collection.Collection` for attribute + names with leading underscores. You must use dict-style lookups + instead:: + + collection['__my_collection__'] + + Not: + + collection.__my_collection__ + + .. versionchanged:: 2.2 + Removed deprecated argument: options + + .. versionadded:: 2.1 + uuid_subtype attribute + + .. mongodoc:: collections + """ + super(Collection, self).__init__( + codec_options or database.codec_options, + read_preference or database.read_preference, + write_concern or database.write_concern, + read_concern or database.read_concern) + + if not isinstance(name, string_type): + raise TypeError("name must be an instance " + "of %s" % (string_type.__name__,)) + + if not name or ".." in name: + raise InvalidName("collection names cannot be empty") + if "$" in name and not (name.startswith("oplog.$main") or + name.startswith("$cmd")): + raise InvalidName("collection names must not " + "contain '$': %r" % name) + if name[0] == "." or name[-1] == ".": + raise InvalidName("collection names must not start " + "or end with '.': %r" % name) + if "\x00" in name: + raise InvalidName("collection names must not contain the " + "null character") + collation = validate_collation_or_none(kwargs.pop('collation', None)) + + self.__database = database + self.__name = _unicode(name) + self.__full_name = _UJOIN % (self.__database.name, self.__name) + if create or kwargs or collation: + self.__create(kwargs, collation, session) + + self.__write_response_codec_options = self.codec_options._replace( + unicode_decode_error_handler='replace', + document_class=dict) + + def _socket_for_reads(self, session): + return self.__database.client._socket_for_reads( + self._read_preference_for(session)) + + def _socket_for_primary_reads(self, session): + read_pref = ((session and session._txn_read_preference()) + or ReadPreference.PRIMARY) + return self.__database.client._socket_for_reads(read_pref), read_pref + + def _socket_for_writes(self): + return self.__database.client._socket_for_writes() + + def _command(self, sock_info, command, slave_ok=False, + read_preference=None, + codec_options=None, check=True, allowable_errors=None, + read_concern=None, + write_concern=None, + collation=None, + session=None, + retryable_write=False, + user_fields=None): + """Internal command helper. + + :Parameters: + - `sock_info` - A SocketInfo instance. + - `command` - The command itself, as a SON instance. + - `slave_ok`: whether to set the SlaveOkay wire protocol bit. + - `codec_options` (optional) - An instance of + :class:`~bson.codec_options.CodecOptions`. + - `check`: raise OperationFailure if there are errors + - `allowable_errors`: errors to ignore if `check` is True + - `read_concern` (optional) - An instance of + :class:`~pymongo.read_concern.ReadConcern`. + - `write_concern`: An instance of + :class:`~pymongo.write_concern.WriteConcern`. This option is only + valid for MongoDB 3.4 and above. + - `collation` (optional) - An instance of + :class:`~pymongo.collation.Collation`. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `retryable_write` (optional): True if this command is a retryable + write. + - `user_fields` (optional): Response fields that should be decoded + using the TypeDecoders from codec_options, passed to + bson._decode_all_selective. + + :Returns: + The result document. + """ + with self.__database.client._tmp_session(session) as s: + return sock_info.command( + self.__database.name, + command, + slave_ok, + read_preference or self._read_preference_for(session), + codec_options or self.codec_options, + check, + allowable_errors, + read_concern=read_concern, + write_concern=write_concern, + parse_write_concern_error=True, + collation=collation, + session=s, + client=self.__database.client, + retryable_write=retryable_write, + user_fields=user_fields) + + def __create(self, options, collation, session): + """Sends a create command with the given options. + """ + cmd = SON([("create", self.__name)]) + if options: + if "size" in options: + options["size"] = float(options["size"]) + cmd.update(options) + with self._socket_for_writes() as sock_info: + self._command( + sock_info, cmd, read_preference=ReadPreference.PRIMARY, + write_concern=self._write_concern_for(session), + collation=collation, session=session) + + def __getattr__(self, name): + """Get a sub-collection of this collection by name. + + Raises InvalidName if an invalid collection name is used. + + :Parameters: + - `name`: the name of the collection to get + """ + if name.startswith('_'): + full_name = _UJOIN % (self.__name, name) + raise AttributeError( + "Collection has no attribute %r. To access the %s" + " collection, use database['%s']." % ( + name, full_name, full_name)) + return self.__getitem__(name) + + def __getitem__(self, name): + return Collection(self.__database, + _UJOIN % (self.__name, name), + False, + self.codec_options, + self.read_preference, + self.write_concern, + self.read_concern) + + def __repr__(self): + return "Collection(%r, %r)" % (self.__database, self.__name) + + def __eq__(self, other): + if isinstance(other, Collection): + return (self.__database == other.database and + self.__name == other.name) + return NotImplemented + + def __ne__(self, other): + return not self == other + + @property + def full_name(self): + """The full name of this :class:`Collection`. + + The full name is of the form `database_name.collection_name`. + """ + return self.__full_name + + @property + def name(self): + """The name of this :class:`Collection`.""" + return self.__name + + @property + def database(self): + """The :class:`~pymongo.database.Database` that this + :class:`Collection` is a part of. + """ + return self.__database + + def with_options(self, codec_options=None, read_preference=None, + write_concern=None, read_concern=None): + """Get a clone of this collection changing the specified settings. + + >>> coll1.read_preference + Primary() + >>> from pymongo import ReadPreference + >>> coll2 = coll1.with_options(read_preference=ReadPreference.SECONDARY) + >>> coll1.read_preference + Primary() + >>> coll2.read_preference + Secondary(tag_sets=None) + + :Parameters: + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. If ``None`` (the + default) the :attr:`codec_options` of this :class:`Collection` + is used. + - `read_preference` (optional): The read preference to use. If + ``None`` (the default) the :attr:`read_preference` of this + :class:`Collection` is used. See :mod:`~pymongo.read_preferences` + for options. + - `write_concern` (optional): An instance of + :class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the + default) the :attr:`write_concern` of this :class:`Collection` + is used. + - `read_concern` (optional): An instance of + :class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the + default) the :attr:`read_concern` of this :class:`Collection` + is used. + """ + return Collection(self.__database, + self.__name, + False, + codec_options or self.codec_options, + read_preference or self.read_preference, + write_concern or self.write_concern, + read_concern or self.read_concern) + + def initialize_unordered_bulk_op(self, bypass_document_validation=False): + """**DEPRECATED** - Initialize an unordered batch of write operations. + + Operations will be performed on the server in arbitrary order, + possibly in parallel. All operations will be attempted. + + :Parameters: + - `bypass_document_validation`: (optional) If ``True``, allows the + write to opt-out of document level validation. Default is + ``False``. + + Returns a :class:`~pymongo.bulk.BulkOperationBuilder` instance. + + See :ref:`unordered_bulk` for examples. + + .. note:: `bypass_document_validation` requires server version + **>= 3.2** + + .. versionchanged:: 3.5 + Deprecated. Use :meth:`~pymongo.collection.Collection.bulk_write` + instead. + + .. versionchanged:: 3.2 + Added bypass_document_validation support + + .. versionadded:: 2.7 + """ + warnings.warn("initialize_unordered_bulk_op is deprecated", + DeprecationWarning, stacklevel=2) + return BulkOperationBuilder(self, False, bypass_document_validation) + + def initialize_ordered_bulk_op(self, bypass_document_validation=False): + """**DEPRECATED** - Initialize an ordered batch of write operations. + + Operations will be performed on the server serially, in the + order provided. If an error occurs all remaining operations + are aborted. + + :Parameters: + - `bypass_document_validation`: (optional) If ``True``, allows the + write to opt-out of document level validation. Default is + ``False``. + + Returns a :class:`~pymongo.bulk.BulkOperationBuilder` instance. + + See :ref:`ordered_bulk` for examples. + + .. note:: `bypass_document_validation` requires server version + **>= 3.2** + + .. versionchanged:: 3.5 + Deprecated. Use :meth:`~pymongo.collection.Collection.bulk_write` + instead. + + .. versionchanged:: 3.2 + Added bypass_document_validation support + + .. versionadded:: 2.7 + """ + warnings.warn("initialize_ordered_bulk_op is deprecated", + DeprecationWarning, stacklevel=2) + return BulkOperationBuilder(self, True, bypass_document_validation) + + def bulk_write(self, requests, ordered=True, + bypass_document_validation=False, session=None): + """Send a batch of write operations to the server. + + Requests are passed as a list of write operation instances ( + :class:`~pymongo.operations.InsertOne`, + :class:`~pymongo.operations.UpdateOne`, + :class:`~pymongo.operations.UpdateMany`, + :class:`~pymongo.operations.ReplaceOne`, + :class:`~pymongo.operations.DeleteOne`, or + :class:`~pymongo.operations.DeleteMany`). + + >>> for doc in db.test.find({}): + ... print(doc) + ... + {u'x': 1, u'_id': ObjectId('54f62e60fba5226811f634ef')} + {u'x': 1, u'_id': ObjectId('54f62e60fba5226811f634f0')} + >>> # DeleteMany, UpdateOne, and UpdateMany are also available. + ... + >>> from pymongo import InsertOne, DeleteOne, ReplaceOne + >>> requests = [InsertOne({'y': 1}), DeleteOne({'x': 1}), + ... ReplaceOne({'w': 1}, {'z': 1}, upsert=True)] + >>> result = db.test.bulk_write(requests) + >>> result.inserted_count + 1 + >>> result.deleted_count + 1 + >>> result.modified_count + 0 + >>> result.upserted_ids + {2: ObjectId('54f62ee28891e756a6e1abd5')} + >>> for doc in db.test.find({}): + ... print(doc) + ... + {u'x': 1, u'_id': ObjectId('54f62e60fba5226811f634f0')} + {u'y': 1, u'_id': ObjectId('54f62ee2fba5226811f634f1')} + {u'z': 1, u'_id': ObjectId('54f62ee28891e756a6e1abd5')} + + :Parameters: + - `requests`: A list of write operations (see examples above). + - `ordered` (optional): If ``True`` (the default) requests will be + performed on the server serially, in the order provided. If an error + occurs all remaining operations are aborted. If ``False`` requests + will be performed on the server in arbitrary order, possibly in + parallel, and all operations will be attempted. + - `bypass_document_validation`: (optional) If ``True``, allows the + write to opt-out of document level validation. Default is + ``False``. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + :Returns: + An instance of :class:`~pymongo.results.BulkWriteResult`. + + .. seealso:: :ref:`writes-and-ids` + + .. note:: `bypass_document_validation` requires server version + **>= 3.2** + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.2 + Added bypass_document_validation support + + .. versionadded:: 3.0 + """ + common.validate_list("requests", requests) + + blk = _Bulk(self, ordered, bypass_document_validation) + for request in requests: + try: + request._add_to_bulk(blk) + except AttributeError: + raise TypeError("%r is not a valid request" % (request,)) + + write_concern = self._write_concern_for(session) + bulk_api_result = blk.execute(write_concern, session) + if bulk_api_result is not None: + return BulkWriteResult(bulk_api_result, True) + return BulkWriteResult({}, False) + + def _legacy_write(self, sock_info, name, cmd, op_id, + bypass_doc_val, func, *args): + """Internal legacy unacknowledged write helper.""" + # Cannot have both unacknowledged write and bypass document validation. + if bypass_doc_val and sock_info.max_wire_version >= 4: + raise OperationFailure("Cannot set bypass_document_validation with" + " unacknowledged write concern") + listeners = self.database.client._event_listeners + publish = listeners.enabled_for_commands + + if publish: + start = datetime.datetime.now() + args = args + (sock_info.compression_context,) + rqst_id, msg, max_size = func(*args) + if publish: + duration = datetime.datetime.now() - start + listeners.publish_command_start( + cmd, self.__database.name, rqst_id, sock_info.address, op_id) + start = datetime.datetime.now() + try: + result = sock_info.legacy_write(rqst_id, msg, max_size, False) + except Exception as exc: + if publish: + dur = (datetime.datetime.now() - start) + duration + if isinstance(exc, OperationFailure): + details = exc.details + # Succeed if GLE was successful and this is a write error. + if details.get("ok") and "n" in details: + reply = message._convert_write_result( + name, cmd, details) + listeners.publish_command_success( + dur, reply, name, rqst_id, sock_info.address, op_id) + raise + else: + details = message._convert_exception(exc) + listeners.publish_command_failure( + dur, details, name, rqst_id, sock_info.address, op_id) + raise + if publish: + if result is not None: + reply = message._convert_write_result(name, cmd, result) + else: + # Comply with APM spec. + reply = {'ok': 1} + duration = (datetime.datetime.now() - start) + duration + listeners.publish_command_success( + duration, reply, name, rqst_id, sock_info.address, op_id) + return result + + def _insert_one( + self, doc, ordered, + check_keys, manipulate, write_concern, op_id, bypass_doc_val, + session): + """Internal helper for inserting a single document.""" + if manipulate: + doc = self.__database._apply_incoming_manipulators(doc, self) + if not isinstance(doc, RawBSONDocument) and '_id' not in doc: + doc['_id'] = ObjectId() + doc = self.__database._apply_incoming_copying_manipulators(doc, + self) + write_concern = write_concern or self.write_concern + acknowledged = write_concern.acknowledged + command = SON([('insert', self.name), + ('ordered', ordered), + ('documents', [doc])]) + if not write_concern.is_server_default: + command['writeConcern'] = write_concern.document + + def _insert_command(session, sock_info, retryable_write): + if not sock_info.op_msg_enabled and not acknowledged: + # Legacy OP_INSERT. + return self._legacy_write( + sock_info, 'insert', command, op_id, + bypass_doc_val, message.insert, self.__full_name, + [doc], check_keys, False, write_concern.document, False, + self.__write_response_codec_options) + + if bypass_doc_val and sock_info.max_wire_version >= 4: + command['bypassDocumentValidation'] = True + + result = sock_info.command( + self.__database.name, + command, + write_concern=write_concern, + codec_options=self.__write_response_codec_options, + check_keys=check_keys, + session=session, + client=self.__database.client, + retryable_write=retryable_write) + + _check_write_command_response(result) + + self.__database.client._retryable_write( + acknowledged, _insert_command, session) + + if not isinstance(doc, RawBSONDocument): + return doc.get('_id') + + def _insert(self, docs, ordered=True, check_keys=True, + manipulate=False, write_concern=None, op_id=None, + bypass_doc_val=False, session=None): + """Internal insert helper.""" + if isinstance(docs, abc.Mapping): + return self._insert_one( + docs, ordered, check_keys, manipulate, write_concern, op_id, + bypass_doc_val, session) + + ids = [] + + if manipulate: + def gen(): + """Generator that applies SON manipulators to each document + and adds _id if necessary. + """ + _db = self.__database + for doc in docs: + # Apply user-configured SON manipulators. This order of + # operations is required for backwards compatibility, + # see PYTHON-709. + doc = _db._apply_incoming_manipulators(doc, self) + if not (isinstance(doc, RawBSONDocument) or '_id' in doc): + doc['_id'] = ObjectId() + + doc = _db._apply_incoming_copying_manipulators(doc, self) + ids.append(doc['_id']) + yield doc + else: + def gen(): + """Generator that only tracks existing _ids.""" + for doc in docs: + # Don't inflate RawBSONDocument by touching fields. + if not isinstance(doc, RawBSONDocument): + ids.append(doc.get('_id')) + yield doc + + write_concern = write_concern or self._write_concern_for(session) + blk = _Bulk(self, ordered, bypass_doc_val) + blk.ops = [(message._INSERT, doc) for doc in gen()] + try: + blk.execute(write_concern, session=session) + except BulkWriteError as bwe: + _raise_last_error(bwe.details) + return ids + + def insert_one(self, document, bypass_document_validation=False, + session=None): + """Insert a single document. + + >>> db.test.count_documents({'x': 1}) + 0 + >>> result = db.test.insert_one({'x': 1}) + >>> result.inserted_id + ObjectId('54f112defba522406c9cc208') + >>> db.test.find_one({'x': 1}) + {u'x': 1, u'_id': ObjectId('54f112defba522406c9cc208')} + + :Parameters: + - `document`: The document to insert. Must be a mutable mapping + type. If the document does not have an _id field one will be + added automatically. + - `bypass_document_validation`: (optional) If ``True``, allows the + write to opt-out of document level validation. Default is + ``False``. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + :Returns: + - An instance of :class:`~pymongo.results.InsertOneResult`. + + .. seealso:: :ref:`writes-and-ids` + + .. note:: `bypass_document_validation` requires server version + **>= 3.2** + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.2 + Added bypass_document_validation support + + .. versionadded:: 3.0 + """ + common.validate_is_document_type("document", document) + if not (isinstance(document, RawBSONDocument) or "_id" in document): + document["_id"] = ObjectId() + + write_concern = self._write_concern_for(session) + return InsertOneResult( + self._insert(document, + write_concern=write_concern, + bypass_doc_val=bypass_document_validation, + session=session), + write_concern.acknowledged) + + def insert_many(self, documents, ordered=True, + bypass_document_validation=False, session=None): + """Insert an iterable of documents. + + >>> db.test.count_documents({}) + 0 + >>> result = db.test.insert_many([{'x': i} for i in range(2)]) + >>> result.inserted_ids + [ObjectId('54f113fffba522406c9cc20e'), ObjectId('54f113fffba522406c9cc20f')] + >>> db.test.count_documents({}) + 2 + + :Parameters: + - `documents`: A iterable of documents to insert. + - `ordered` (optional): If ``True`` (the default) documents will be + inserted on the server serially, in the order provided. If an error + occurs all remaining inserts are aborted. If ``False``, documents + will be inserted on the server in arbitrary order, possibly in + parallel, and all document inserts will be attempted. + - `bypass_document_validation`: (optional) If ``True``, allows the + write to opt-out of document level validation. Default is + ``False``. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + :Returns: + An instance of :class:`~pymongo.results.InsertManyResult`. + + .. seealso:: :ref:`writes-and-ids` + + .. note:: `bypass_document_validation` requires server version + **>= 3.2** + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.2 + Added bypass_document_validation support + + .. versionadded:: 3.0 + """ + if not isinstance(documents, abc.Iterable) or not documents: + raise TypeError("documents must be a non-empty list") + inserted_ids = [] + def gen(): + """A generator that validates documents and handles _ids.""" + for document in documents: + common.validate_is_document_type("document", document) + if not isinstance(document, RawBSONDocument): + if "_id" not in document: + document["_id"] = ObjectId() + inserted_ids.append(document["_id"]) + yield (message._INSERT, document) + + write_concern = self._write_concern_for(session) + blk = _Bulk(self, ordered, bypass_document_validation) + blk.ops = [doc for doc in gen()] + blk.execute(write_concern, session=session) + return InsertManyResult(inserted_ids, write_concern.acknowledged) + + def _update(self, sock_info, criteria, document, upsert=False, + check_keys=True, multi=False, manipulate=False, + write_concern=None, op_id=None, ordered=True, + bypass_doc_val=False, collation=None, array_filters=None, + session=None, retryable_write=False): + """Internal update / replace helper.""" + common.validate_boolean("upsert", upsert) + if manipulate: + document = self.__database._fix_incoming(document, self) + collation = validate_collation_or_none(collation) + write_concern = write_concern or self.write_concern + acknowledged = write_concern.acknowledged + update_doc = SON([('q', criteria), + ('u', document), + ('multi', multi), + ('upsert', upsert)]) + if collation is not None: + if sock_info.max_wire_version < 5: + raise ConfigurationError( + 'Must be connected to MongoDB 3.4+ to use collations.') + elif not acknowledged: + raise ConfigurationError( + 'Collation is unsupported for unacknowledged writes.') + else: + update_doc['collation'] = collation + if array_filters is not None: + if sock_info.max_wire_version < 6: + raise ConfigurationError( + 'Must be connected to MongoDB 3.6+ to use array_filters.') + elif not acknowledged: + raise ConfigurationError( + 'arrayFilters is unsupported for unacknowledged writes.') + else: + update_doc['arrayFilters'] = array_filters + command = SON([('update', self.name), + ('ordered', ordered), + ('updates', [update_doc])]) + if not write_concern.is_server_default: + command['writeConcern'] = write_concern.document + + if not sock_info.op_msg_enabled and not acknowledged: + # Legacy OP_UPDATE. + return self._legacy_write( + sock_info, 'update', command, op_id, + bypass_doc_val, message.update, self.__full_name, upsert, + multi, criteria, document, False, write_concern.document, + check_keys, self.__write_response_codec_options) + + # Update command. + if bypass_doc_val and sock_info.max_wire_version >= 4: + command['bypassDocumentValidation'] = True + + # The command result has to be published for APM unmodified + # so we make a shallow copy here before adding updatedExisting. + result = sock_info.command( + self.__database.name, + command, + write_concern=write_concern, + codec_options=self.__write_response_codec_options, + session=session, + client=self.__database.client, + retryable_write=retryable_write).copy() + _check_write_command_response(result) + # Add the updatedExisting field for compatibility. + if result.get('n') and 'upserted' not in result: + result['updatedExisting'] = True + else: + result['updatedExisting'] = False + # MongoDB >= 2.6.0 returns the upsert _id in an array + # element. Break it out for backward compatibility. + if 'upserted' in result: + result['upserted'] = result['upserted'][0]['_id'] + + if not acknowledged: + return None + return result + + def _update_retryable( + self, criteria, document, upsert=False, + check_keys=True, multi=False, manipulate=False, + write_concern=None, op_id=None, ordered=True, + bypass_doc_val=False, collation=None, array_filters=None, + session=None): + """Internal update / replace helper.""" + def _update(session, sock_info, retryable_write): + return self._update( + sock_info, criteria, document, upsert=upsert, + check_keys=check_keys, multi=multi, manipulate=manipulate, + write_concern=write_concern, op_id=op_id, ordered=ordered, + bypass_doc_val=bypass_doc_val, collation=collation, + array_filters=array_filters, session=session, + retryable_write=retryable_write) + + return self.__database.client._retryable_write( + (write_concern or self.write_concern).acknowledged and not multi, + _update, session) + + def replace_one(self, filter, replacement, upsert=False, + bypass_document_validation=False, collation=None, + session=None): + """Replace a single document matching the filter. + + >>> for doc in db.test.find({}): + ... print(doc) + ... + {u'x': 1, u'_id': ObjectId('54f4c5befba5220aa4d6dee7')} + >>> result = db.test.replace_one({'x': 1}, {'y': 1}) + >>> result.matched_count + 1 + >>> result.modified_count + 1 + >>> for doc in db.test.find({}): + ... print(doc) + ... + {u'y': 1, u'_id': ObjectId('54f4c5befba5220aa4d6dee7')} + + The *upsert* option can be used to insert a new document if a matching + document does not exist. + + >>> result = db.test.replace_one({'x': 1}, {'x': 1}, True) + >>> result.matched_count + 0 + >>> result.modified_count + 0 + >>> result.upserted_id + ObjectId('54f11e5c8891e756a6e1abd4') + >>> db.test.find_one({'x': 1}) + {u'x': 1, u'_id': ObjectId('54f11e5c8891e756a6e1abd4')} + + :Parameters: + - `filter`: A query that matches the document to replace. + - `replacement`: The new document. + - `upsert` (optional): If ``True``, perform an insert if no documents + match the filter. + - `bypass_document_validation`: (optional) If ``True``, allows the + write to opt-out of document level validation. Default is + ``False``. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only supported + on MongoDB 3.4 and above. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + :Returns: + - An instance of :class:`~pymongo.results.UpdateResult`. + + .. note:: `bypass_document_validation` requires server version + **>= 3.2** + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.4 + Added the `collation` option. + + .. versionchanged:: 3.2 + Added bypass_document_validation support + + .. versionadded:: 3.0 + """ + common.validate_is_mapping("filter", filter) + common.validate_ok_for_replace(replacement) + + write_concern = self._write_concern_for(session) + return UpdateResult( + self._update_retryable( + filter, replacement, upsert, + write_concern=write_concern, + bypass_doc_val=bypass_document_validation, + collation=collation, session=session), + write_concern.acknowledged) + + def update_one(self, filter, update, upsert=False, + bypass_document_validation=False, + collation=None, array_filters=None, session=None): + """Update a single document matching the filter. + + >>> for doc in db.test.find(): + ... print(doc) + ... + {u'x': 1, u'_id': 0} + {u'x': 1, u'_id': 1} + {u'x': 1, u'_id': 2} + >>> result = db.test.update_one({'x': 1}, {'$inc': {'x': 3}}) + >>> result.matched_count + 1 + >>> result.modified_count + 1 + >>> for doc in db.test.find(): + ... print(doc) + ... + {u'x': 4, u'_id': 0} + {u'x': 1, u'_id': 1} + {u'x': 1, u'_id': 2} + + :Parameters: + - `filter`: A query that matches the document to update. + - `update`: The modifications to apply. + - `upsert` (optional): If ``True``, perform an insert if no documents + match the filter. + - `bypass_document_validation`: (optional) If ``True``, allows the + write to opt-out of document level validation. Default is + ``False``. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only supported + on MongoDB 3.4 and above. + - `array_filters` (optional): A list of filters specifying which + array elements an update should apply. Requires MongoDB 3.6+. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + :Returns: + - An instance of :class:`~pymongo.results.UpdateResult`. + + .. note:: `bypass_document_validation` requires server version + **>= 3.2** + + .. versionchanged:: 3.6 + Added the `array_filters` and ``session`` parameters. + + .. versionchanged:: 3.4 + Added the `collation` option. + + .. versionchanged:: 3.2 + Added bypass_document_validation support + + .. versionadded:: 3.0 + """ + common.validate_is_mapping("filter", filter) + common.validate_ok_for_update(update) + common.validate_list_or_none('array_filters', array_filters) + + write_concern = self._write_concern_for(session) + return UpdateResult( + self._update_retryable( + filter, update, upsert, check_keys=False, + write_concern=write_concern, + bypass_doc_val=bypass_document_validation, + collation=collation, array_filters=array_filters, + session=session), + write_concern.acknowledged) + + def update_many(self, filter, update, upsert=False, array_filters=None, + bypass_document_validation=False, collation=None, + session=None): + """Update one or more documents that match the filter. + + >>> for doc in db.test.find(): + ... print(doc) + ... + {u'x': 1, u'_id': 0} + {u'x': 1, u'_id': 1} + {u'x': 1, u'_id': 2} + >>> result = db.test.update_many({'x': 1}, {'$inc': {'x': 3}}) + >>> result.matched_count + 3 + >>> result.modified_count + 3 + >>> for doc in db.test.find(): + ... print(doc) + ... + {u'x': 4, u'_id': 0} + {u'x': 4, u'_id': 1} + {u'x': 4, u'_id': 2} + + :Parameters: + - `filter`: A query that matches the documents to update. + - `update`: The modifications to apply. + - `upsert` (optional): If ``True``, perform an insert if no documents + match the filter. + - `bypass_document_validation` (optional): If ``True``, allows the + write to opt-out of document level validation. Default is + ``False``. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only supported + on MongoDB 3.4 and above. + - `array_filters` (optional): A list of filters specifying which + array elements an update should apply. Requires MongoDB 3.6+. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + :Returns: + - An instance of :class:`~pymongo.results.UpdateResult`. + + .. note:: `bypass_document_validation` requires server version + **>= 3.2** + + .. versionchanged:: 3.6 + Added ``array_filters`` and ``session`` parameters. + + .. versionchanged:: 3.4 + Added the `collation` option. + + .. versionchanged:: 3.2 + Added bypass_document_validation support + + .. versionadded:: 3.0 + """ + common.validate_is_mapping("filter", filter) + common.validate_ok_for_update(update) + common.validate_list_or_none('array_filters', array_filters) + + write_concern = self._write_concern_for(session) + return UpdateResult( + self._update_retryable( + filter, update, upsert, check_keys=False, multi=True, + write_concern=write_concern, + bypass_doc_val=bypass_document_validation, + collation=collation, array_filters=array_filters, + session=session), + write_concern.acknowledged) + + def drop(self, session=None): + """Alias for :meth:`~pymongo.database.Database.drop_collection`. + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + The following two calls are equivalent: + + >>> db.foo.drop() + >>> db.drop_collection("foo") + + .. versionchanged:: 3.7 + :meth:`drop` now respects this :class:`Collection`'s :attr:`write_concern`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + dbo = self.__database.client.get_database( + self.__database.name, + self.codec_options, + self.read_preference, + self.write_concern, + self.read_concern) + dbo.drop_collection(self.__name, session=session) + + def _delete( + self, sock_info, criteria, multi, + write_concern=None, op_id=None, ordered=True, + collation=None, session=None, retryable_write=False): + """Internal delete helper.""" + common.validate_is_mapping("filter", criteria) + write_concern = write_concern or self.write_concern + acknowledged = write_concern.acknowledged + delete_doc = SON([('q', criteria), + ('limit', int(not multi))]) + collation = validate_collation_or_none(collation) + if collation is not None: + if sock_info.max_wire_version < 5: + raise ConfigurationError( + 'Must be connected to MongoDB 3.4+ to use collations.') + elif not acknowledged: + raise ConfigurationError( + 'Collation is unsupported for unacknowledged writes.') + else: + delete_doc['collation'] = collation + command = SON([('delete', self.name), + ('ordered', ordered), + ('deletes', [delete_doc])]) + if not write_concern.is_server_default: + command['writeConcern'] = write_concern.document + + if not sock_info.op_msg_enabled and not acknowledged: + # Legacy OP_DELETE. + return self._legacy_write( + sock_info, 'delete', command, op_id, + False, message.delete, self.__full_name, criteria, + False, write_concern.document, + self.__write_response_codec_options, + int(not multi)) + # Delete command. + result = sock_info.command( + self.__database.name, + command, + write_concern=write_concern, + codec_options=self.__write_response_codec_options, + session=session, + client=self.__database.client, + retryable_write=retryable_write) + _check_write_command_response(result) + return result + + def _delete_retryable( + self, criteria, multi, + write_concern=None, op_id=None, ordered=True, + collation=None, session=None): + """Internal delete helper.""" + def _delete(session, sock_info, retryable_write): + return self._delete( + sock_info, criteria, multi, + write_concern=write_concern, op_id=op_id, ordered=ordered, + collation=collation, session=session, + retryable_write=retryable_write) + + return self.__database.client._retryable_write( + (write_concern or self.write_concern).acknowledged and not multi, + _delete, session) + + def delete_one(self, filter, collation=None, session=None): + """Delete a single document matching the filter. + + >>> db.test.count_documents({'x': 1}) + 3 + >>> result = db.test.delete_one({'x': 1}) + >>> result.deleted_count + 1 + >>> db.test.count_documents({'x': 1}) + 2 + + :Parameters: + - `filter`: A query that matches the document to delete. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only supported + on MongoDB 3.4 and above. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + :Returns: + - An instance of :class:`~pymongo.results.DeleteResult`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.4 + Added the `collation` option. + + .. versionadded:: 3.0 + """ + write_concern = self._write_concern_for(session) + return DeleteResult( + self._delete_retryable( + filter, False, + write_concern=write_concern, + collation=collation, session=session), + write_concern.acknowledged) + + def delete_many(self, filter, collation=None, session=None): + """Delete one or more documents matching the filter. + + >>> db.test.count_documents({'x': 1}) + 3 + >>> result = db.test.delete_many({'x': 1}) + >>> result.deleted_count + 3 + >>> db.test.count_documents({'x': 1}) + 0 + + :Parameters: + - `filter`: A query that matches the documents to delete. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only supported + on MongoDB 3.4 and above. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + :Returns: + - An instance of :class:`~pymongo.results.DeleteResult`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.4 + Added the `collation` option. + + .. versionadded:: 3.0 + """ + write_concern = self._write_concern_for(session) + return DeleteResult( + self._delete_retryable( + filter, True, + write_concern=write_concern, + collation=collation, session=session), + write_concern.acknowledged) + + def find_one(self, filter=None, *args, **kwargs): + """Get a single document from the database. + + All arguments to :meth:`find` are also valid arguments for + :meth:`find_one`, although any `limit` argument will be + ignored. Returns a single document, or ``None`` if no matching + document is found. + + The :meth:`find_one` method obeys the :attr:`read_preference` of + this :class:`Collection`. + + :Parameters: + + - `filter` (optional): a dictionary specifying + the query to be performed OR any other type to be used as + the value for a query for ``"_id"``. + + - `*args` (optional): any additional positional arguments + are the same as the arguments to :meth:`find`. + + - `**kwargs` (optional): any additional keyword arguments + are the same as the arguments to :meth:`find`. + + >>> collection.find_one(max_time_ms=100) + """ + if (filter is not None and not + isinstance(filter, abc.Mapping)): + filter = {"_id": filter} + + cursor = self.find(filter, *args, **kwargs) + for result in cursor.limit(-1): + return result + return None + + def find(self, *args, **kwargs): + """Query the database. + + The `filter` argument is a prototype document that all results + must match. For example: + + >>> db.test.find({"hello": "world"}) + + only matches documents that have a key "hello" with value + "world". Matches can have other keys *in addition* to + "hello". The `projection` argument is used to specify a subset + of fields that should be included in the result documents. By + limiting results to a certain subset of fields you can cut + down on network traffic and decoding time. + + Raises :class:`TypeError` if any of the arguments are of + improper type. Returns an instance of + :class:`~pymongo.cursor.Cursor` corresponding to this query. + + The :meth:`find` method obeys the :attr:`read_preference` of + this :class:`Collection`. + + :Parameters: + - `filter` (optional): a SON object specifying elements which + must be present for a document to be included in the + result set + - `projection` (optional): a list of field names that should be + returned in the result set or a dict specifying the fields + to include or exclude. If `projection` is a list "_id" will + always be returned. Use a dict to exclude fields from + the result (e.g. projection={'_id': False}). + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `skip` (optional): the number of documents to omit (from + the start of the result set) when returning the results + - `limit` (optional): the maximum number of results to + return. A limit of 0 (the default) is equivalent to setting no + limit. + - `no_cursor_timeout` (optional): if False (the default), any + returned cursor is closed by the server after 10 minutes of + inactivity. If set to True, the returned cursor will never + time out on the server. Care should be taken to ensure that + cursors with no_cursor_timeout turned on are properly closed. + - `cursor_type` (optional): the type of cursor to return. The valid + options are defined by :class:`~pymongo.cursor.CursorType`: + + - :attr:`~pymongo.cursor.CursorType.NON_TAILABLE` - the result of + this find call will return a standard cursor over the result set. + - :attr:`~pymongo.cursor.CursorType.TAILABLE` - the result of this + find call will be a tailable cursor - tailable cursors are only + for use with capped collections. They are not closed when the + last data is retrieved but are kept open and the cursor location + marks the final document position. If more data is received + iteration of the cursor will continue from the last document + received. For details, see the `tailable cursor documentation + `_. + - :attr:`~pymongo.cursor.CursorType.TAILABLE_AWAIT` - the result + of this find call will be a tailable cursor with the await flag + set. The server will wait for a few seconds after returning the + full result set so that it can capture and return additional data + added during the query. + - :attr:`~pymongo.cursor.CursorType.EXHAUST` - the result of this + find call will be an exhaust cursor. MongoDB will stream batched + results to the client without waiting for the client to request + each batch, reducing latency. See notes on compatibility below. + + - `sort` (optional): a list of (key, direction) pairs + specifying the sort order for this query. See + :meth:`~pymongo.cursor.Cursor.sort` for details. + - `allow_partial_results` (optional): if True, mongos will return + partial results if some shards are down instead of returning an + error. + - `oplog_replay` (optional): If True, set the oplogReplay query + flag. + - `batch_size` (optional): Limits the number of documents returned in + a single batch. + - `manipulate` (optional): **DEPRECATED** - If True (the default), + apply any outgoing SON manipulators before returning. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only supported + on MongoDB 3.4 and above. + - `return_key` (optional): If True, return only the index keys in + each document. + - `show_record_id` (optional): If True, adds a field ``$recordId`` in + each document with the storage engine's internal record identifier. + - `snapshot` (optional): **DEPRECATED** - If True, prevents the + cursor from returning a document more than once because of an + intervening write operation. + - `hint` (optional): An index, in the same format as passed to + :meth:`~pymongo.collection.Collection.create_index` (e.g. + ``[('field', ASCENDING)]``). Pass this as an alternative to calling + :meth:`~pymongo.cursor.Cursor.hint` on the cursor to tell Mongo the + proper index to use for the query. + - `max_time_ms` (optional): Specifies a time limit for a query + operation. If the specified time is exceeded, the operation will be + aborted and :exc:`~pymongo.errors.ExecutionTimeout` is raised. Pass + this as an alternative to calling + :meth:`~pymongo.cursor.Cursor.max_time_ms` on the cursor. + - `max_scan` (optional): **DEPRECATED** - The maximum number of + documents to scan. Pass this as an alternative to calling + :meth:`~pymongo.cursor.Cursor.max_scan` on the cursor. + - `min` (optional): A list of field, limit pairs specifying the + inclusive lower bound for all keys of a specific index in order. + Pass this as an alternative to calling + :meth:`~pymongo.cursor.Cursor.min` on the cursor. ``hint`` must + also be passed to ensure the query utilizes the correct index. + - `max` (optional): A list of field, limit pairs specifying the + exclusive upper bound for all keys of a specific index in order. + Pass this as an alternative to calling + :meth:`~pymongo.cursor.Cursor.max` on the cursor. ``hint`` must + also be passed to ensure the query utilizes the correct index. + - `comment` (optional): A string to attach to the query to help + interpret and trace the operation in the server logs and in profile + data. Pass this as an alternative to calling + :meth:`~pymongo.cursor.Cursor.comment` on the cursor. + - `modifiers` (optional): **DEPRECATED** - A dict specifying + additional MongoDB query modifiers. Use the keyword arguments listed + above instead. + + .. note:: There are a number of caveats to using + :attr:`~pymongo.cursor.CursorType.EXHAUST` as cursor_type: + + - The `limit` option can not be used with an exhaust cursor. + + - Exhaust cursors are not supported by mongos and can not be + used with a sharded cluster. + + - A :class:`~pymongo.cursor.Cursor` instance created with the + :attr:`~pymongo.cursor.CursorType.EXHAUST` cursor_type requires an + exclusive :class:`~socket.socket` connection to MongoDB. If the + :class:`~pymongo.cursor.Cursor` is discarded without being + completely iterated the underlying :class:`~socket.socket` + connection will be closed and discarded without being returned to + the connection pool. + + .. versionchanged:: 3.7 + Deprecated the `snapshot` option, which is deprecated in MongoDB + 3.6 and removed in MongoDB 4.0. + Deprecated the `max_scan` option. Support for this option is + deprecated in MongoDB 4.0. Use `max_time_ms` instead to limit server + side execution time. + + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.5 + Added the options `return_key`, `show_record_id`, `snapshot`, + `hint`, `max_time_ms`, `max_scan`, `min`, `max`, and `comment`. + Deprecated the option `modifiers`. + + .. versionchanged:: 3.4 + Support the `collation` option. + + .. versionchanged:: 3.0 + Changed the parameter names `spec`, `fields`, `timeout`, and + `partial` to `filter`, `projection`, `no_cursor_timeout`, and + `allow_partial_results` respectively. + Added the `cursor_type`, `oplog_replay`, and `modifiers` options. + Removed the `network_timeout`, `read_preference`, `tag_sets`, + `secondary_acceptable_latency_ms`, `max_scan`, `snapshot`, + `tailable`, `await_data`, `exhaust`, `as_class`, and slave_okay + parameters. Removed `compile_re` option: PyMongo now always + represents BSON regular expressions as :class:`~bson.regex.Regex` + objects. Use :meth:`~bson.regex.Regex.try_compile` to attempt to + convert from a BSON regular expression to a Python regular + expression object. Soft deprecated the `manipulate` option. + + .. versionchanged:: 2.7 + Added `compile_re` option. If set to False, PyMongo represented BSON + regular expressions as :class:`~bson.regex.Regex` objects instead of + attempting to compile BSON regular expressions as Python native + regular expressions, thus preventing errors for some incompatible + patterns, see `PYTHON-500`_. + + .. versionadded:: 2.3 + The `tag_sets` and `secondary_acceptable_latency_ms` parameters. + + .. _PYTHON-500: https://jira.mongodb.org/browse/PYTHON-500 + + .. mongodoc:: find + + """ + return Cursor(self, *args, **kwargs) + + def find_raw_batches(self, *args, **kwargs): + """Query the database and retrieve batches of raw BSON. + + Similar to the :meth:`find` method but returns a + :class:`~pymongo.cursor.RawBatchCursor`. + + This example demonstrates how to work with raw batches, but in practice + raw batches should be passed to an external library that can decode + BSON into another data type, rather than used with PyMongo's + :mod:`bson` module. + + >>> import bson + >>> cursor = db.test.find_raw_batches() + >>> for batch in cursor: + ... print(bson.decode_all(batch)) + + .. note:: find_raw_batches does not support sessions. + + .. versionadded:: 3.6 + """ + # OP_MSG with document stream returns is required to support + # sessions. + if "session" in kwargs: + raise ConfigurationError( + "find_raw_batches does not support sessions") + return RawBatchCursor(self, *args, **kwargs) + + def parallel_scan(self, num_cursors, session=None, **kwargs): + """**DEPRECATED**: Scan this entire collection in parallel. + + Returns a list of up to ``num_cursors`` cursors that can be iterated + concurrently. As long as the collection is not modified during + scanning, each document appears once in one of the cursors result + sets. + + For example, to process each document in a collection using some + thread-safe ``process_document()`` function: + + >>> def process_cursor(cursor): + ... for document in cursor: + ... # Some thread-safe processing function: + ... process_document(document) + >>> + >>> # Get up to 4 cursors. + ... + >>> cursors = collection.parallel_scan(4) + >>> threads = [ + ... threading.Thread(target=process_cursor, args=(cursor,)) + ... for cursor in cursors] + >>> + >>> for thread in threads: + ... thread.start() + >>> + >>> for thread in threads: + ... thread.join() + >>> + >>> # All documents have now been processed. + + The :meth:`parallel_scan` method obeys the :attr:`read_preference` of + this :class:`Collection`. + + :Parameters: + - `num_cursors`: the number of cursors to return + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs`: additional options for the parallelCollectionScan + command can be passed as keyword arguments. + + .. note:: Requires server version **>= 2.5.5**. + + .. versionchanged:: 3.7 + Deprecated. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.4 + Added back support for arbitrary keyword arguments. MongoDB 3.4 + adds support for maxTimeMS as an option to the + parallelCollectionScan command. + + .. versionchanged:: 3.0 + Removed support for arbitrary keyword arguments, since + the parallelCollectionScan command has no optional arguments. + """ + warnings.warn("parallel_scan is deprecated. MongoDB 4.2 will remove " + "the parallelCollectionScan command.", + DeprecationWarning, stacklevel=2) + cmd = SON([('parallelCollectionScan', self.__name), + ('numCursors', num_cursors)]) + cmd.update(kwargs) + + with self._socket_for_reads(session) as (sock_info, slave_ok): + # We call sock_info.command here directly, instead of + # calling self._command to avoid using an implicit session. + result = sock_info.command( + self.__database.name, + cmd, + slave_ok, + self._read_preference_for(session), + self.codec_options, + read_concern=self.read_concern, + parse_write_concern_error=True, + session=session, + client=self.__database.client) + + cursors = [] + for cursor in result['cursors']: + cursors.append(CommandCursor( + self, cursor['cursor'], sock_info.address, + session=session, explicit_session=session is not None)) + + return cursors + + def _count(self, cmd, collation=None, session=None): + """Internal count helper.""" + with self._socket_for_reads(session) as (sock_info, slave_ok): + res = self._command( + sock_info, + cmd, + slave_ok, + allowable_errors=["ns missing"], + codec_options=self.__write_response_codec_options, + read_concern=self.read_concern, + collation=collation, + session=session) + if res.get("errmsg", "") == "ns missing": + return 0 + return int(res["n"]) + + def _aggregate_one_result( + self, sock_info, slave_ok, cmd, collation=None, session=None): + """Internal helper to run an aggregate that returns a single result.""" + result = self._command( + sock_info, + cmd, + slave_ok, + codec_options=self.__write_response_codec_options, + read_concern=self.read_concern, + collation=collation, + session=session) + batch = result['cursor']['firstBatch'] + return batch[0] if batch else None + + def estimated_document_count(self, **kwargs): + """Get an estimate of the number of documents in this collection using + collection metadata. + + The :meth:`estimated_document_count` method is **not** supported in a + transaction. + + All optional parameters should be passed as keyword arguments + to this method. Valid options include: + + - `maxTimeMS` (int): The maximum amount of time to allow this + operation to run, in milliseconds. + + :Parameters: + - `**kwargs` (optional): See list of options above. + + .. versionadded:: 3.7 + """ + if 'session' in kwargs: + raise ConfigurationError( + 'estimated_document_count does not support sessions') + cmd = SON([('count', self.__name)]) + cmd.update(kwargs) + return self._count(cmd) + + def count_documents(self, filter, session=None, **kwargs): + """Count the number of documents in this collection. + + The :meth:`count_documents` method is supported in a transaction. + + All optional parameters should be passed as keyword arguments + to this method. Valid options include: + + - `skip` (int): The number of matching documents to skip before + returning results. + - `limit` (int): The maximum number of documents to count. Must be + a positive integer. If not provided, no limit is imposed. + - `maxTimeMS` (int): The maximum amount of time to allow this + operation to run, in milliseconds. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only supported + on MongoDB 3.4 and above. + - `hint` (string or list of tuples): The index to use. Specify either + the index name as a string or the index specification as a list of + tuples (e.g. [('a', pymongo.ASCENDING), ('b', pymongo.ASCENDING)]). + This option is only supported on MongoDB 3.6 and above. + + The :meth:`count_documents` method obeys the :attr:`read_preference` of + this :class:`Collection`. + + .. note:: When migrating from :meth:`count` to :meth:`count_documents` + the following query operators must be replaced: + + +-------------+-------------------------------------+ + | Operator | Replacement | + +=============+=====================================+ + | $where | `$expr`_ | + +-------------+-------------------------------------+ + | $near | `$geoWithin`_ with `$center`_ | + +-------------+-------------------------------------+ + | $nearSphere | `$geoWithin`_ with `$centerSphere`_ | + +-------------+-------------------------------------+ + + $expr requires MongoDB 3.6+ + + :Parameters: + - `filter` (required): A query document that selects which documents + to count in the collection. Can be an empty document to count all + documents. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): See list of options above. + + .. versionadded:: 3.7 + + .. _$expr: https://docs.mongodb.com/manual/reference/operator/query/expr/ + .. _$geoWithin: https://docs.mongodb.com/manual/reference/operator/query/geoWithin/ + .. _$center: https://docs.mongodb.com/manual/reference/operator/query/center/#op._S_center + .. _$centerSphere: https://docs.mongodb.com/manual/reference/operator/query/centerSphere/#op._S_centerSphere + """ + pipeline = [{'$match': filter}] + if 'skip' in kwargs: + pipeline.append({'$skip': kwargs.pop('skip')}) + if 'limit' in kwargs: + pipeline.append({'$limit': kwargs.pop('limit')}) + pipeline.append({'$group': {'_id': None, 'n': {'$sum': 1}}}) + cmd = SON([('aggregate', self.__name), + ('pipeline', pipeline), + ('cursor', {})]) + if "hint" in kwargs and not isinstance(kwargs["hint"], string_type): + kwargs["hint"] = helpers._index_document(kwargs["hint"]) + collation = validate_collation_or_none(kwargs.pop('collation', None)) + cmd.update(kwargs) + with self._socket_for_reads(session) as (sock_info, slave_ok): + result = self._aggregate_one_result( + sock_info, slave_ok, cmd, collation, session) + if not result: + return 0 + return result['n'] + + def count(self, filter=None, session=None, **kwargs): + """**DEPRECATED** - Get the number of documents in this collection. + + The :meth:`count` method is deprecated and **not** supported in a + transaction. Please use :meth:`count_documents` or + :meth:`estimated_document_count` instead. + + All optional count parameters should be passed as keyword arguments + to this method. Valid options include: + + - `skip` (int): The number of matching documents to skip before + returning results. + - `limit` (int): The maximum number of documents to count. A limit + of 0 (the default) is equivalent to setting no limit. + - `maxTimeMS` (int): The maximum amount of time to allow the count + command to run, in milliseconds. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only supported + on MongoDB 3.4 and above. + - `hint` (string or list of tuples): The index to use. Specify either + the index name as a string or the index specification as a list of + tuples (e.g. [('a', pymongo.ASCENDING), ('b', pymongo.ASCENDING)]). + + The :meth:`count` method obeys the :attr:`read_preference` of + this :class:`Collection`. + + .. note:: When migrating from :meth:`count` to :meth:`count_documents` + the following query operators must be replaced: + + +-------------+-------------------------------------+ + | Operator | Replacement | + +=============+=====================================+ + | $where | `$expr`_ | + +-------------+-------------------------------------+ + | $near | `$geoWithin`_ with `$center`_ | + +-------------+-------------------------------------+ + | $nearSphere | `$geoWithin`_ with `$centerSphere`_ | + +-------------+-------------------------------------+ + + $expr requires MongoDB 3.6+ + + :Parameters: + - `filter` (optional): A query document that selects which documents + to count in the collection. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): See list of options above. + + .. versionchanged:: 3.7 + Deprecated. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.4 + Support the `collation` option. + + .. _$expr: https://docs.mongodb.com/manual/reference/operator/query/expr/ + .. _$geoWithin: https://docs.mongodb.com/manual/reference/operator/query/geoWithin/ + .. _$center: https://docs.mongodb.com/manual/reference/operator/query/center/#op._S_center + .. _$centerSphere: https://docs.mongodb.com/manual/reference/operator/query/centerSphere/#op._S_centerSphere + """ + warnings.warn("count is deprecated. Use estimated_document_count or " + "count_documents instead. Please note that $where must " + "be replaced by $expr, $near must be replaced by " + "$geoWithin with $center, and $nearSphere must be " + "replaced by $geoWithin with $centerSphere", + DeprecationWarning, stacklevel=2) + cmd = SON([("count", self.__name)]) + if filter is not None: + if "query" in kwargs: + raise ConfigurationError("can't pass both filter and query") + kwargs["query"] = filter + if "hint" in kwargs and not isinstance(kwargs["hint"], string_type): + kwargs["hint"] = helpers._index_document(kwargs["hint"]) + collation = validate_collation_or_none(kwargs.pop('collation', None)) + cmd.update(kwargs) + return self._count(cmd, collation, session) + + def create_indexes(self, indexes, session=None, **kwargs): + """Create one or more indexes on this collection. + + >>> from pymongo import IndexModel, ASCENDING, DESCENDING + >>> index1 = IndexModel([("hello", DESCENDING), + ... ("world", ASCENDING)], name="hello_world") + >>> index2 = IndexModel([("goodbye", DESCENDING)]) + >>> db.test.create_indexes([index1, index2]) + ["hello_world", "goodbye_-1"] + + :Parameters: + - `indexes`: A list of :class:`~pymongo.operations.IndexModel` + instances. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): optional arguments to the createIndexes + command (like maxTimeMS) can be passed as keyword arguments. + + .. note:: `create_indexes` uses the `createIndexes`_ command + introduced in MongoDB **2.6** and cannot be used with earlier + versions. + + .. note:: The :attr:`~pymongo.collection.Collection.write_concern` of + this collection is automatically applied to this operation when using + MongoDB >= 3.4. + + .. versionchanged:: 3.6 + Added ``session`` parameter. Added support for arbitrary keyword + arguments. + + .. versionchanged:: 3.4 + Apply this collection's write concern automatically to this operation + when connected to MongoDB >= 3.4. + .. versionadded:: 3.0 + + .. _createIndexes: https://docs.mongodb.com/manual/reference/command/createIndexes/ + """ + common.validate_list('indexes', indexes) + names = [] + with self._socket_for_writes() as sock_info: + supports_collations = sock_info.max_wire_version >= 5 + def gen_indexes(): + for index in indexes: + if not isinstance(index, IndexModel): + raise TypeError( + "%r is not an instance of " + "pymongo.operations.IndexModel" % (index,)) + document = index.document + if "collation" in document and not supports_collations: + raise ConfigurationError( + "Must be connected to MongoDB " + "3.4+ to use collations.") + names.append(document["name"]) + yield document + cmd = SON([('createIndexes', self.name), + ('indexes', list(gen_indexes()))]) + cmd.update(kwargs) + self._command( + sock_info, cmd, read_preference=ReadPreference.PRIMARY, + codec_options=_UNICODE_REPLACE_CODEC_OPTIONS, + write_concern=self._write_concern_for(session), + session=session) + return names + + def __create_index(self, keys, index_options, session, **kwargs): + """Internal create index helper. + + :Parameters: + - `keys`: a list of tuples [(key, type), (key, type), ...] + - `index_options`: a dict of index options. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + """ + index_doc = helpers._index_document(keys) + index = {"key": index_doc} + collation = validate_collation_or_none( + index_options.pop('collation', None)) + index.update(index_options) + + with self._socket_for_writes() as sock_info: + if collation is not None: + if sock_info.max_wire_version < 5: + raise ConfigurationError( + 'Must be connected to MongoDB 3.4+ to use collations.') + else: + index['collation'] = collation + cmd = SON([('createIndexes', self.name), ('indexes', [index])]) + cmd.update(kwargs) + self._command( + sock_info, cmd, read_preference=ReadPreference.PRIMARY, + codec_options=_UNICODE_REPLACE_CODEC_OPTIONS, + write_concern=self._write_concern_for(session), + session=session) + + def create_index(self, keys, session=None, **kwargs): + """Creates an index on this collection. + + Takes either a single key or a list of (key, direction) pairs. + The key(s) must be an instance of :class:`basestring` + (:class:`str` in python 3), and the direction(s) must be one of + (:data:`~pymongo.ASCENDING`, :data:`~pymongo.DESCENDING`, + :data:`~pymongo.GEO2D`, :data:`~pymongo.GEOHAYSTACK`, + :data:`~pymongo.GEOSPHERE`, :data:`~pymongo.HASHED`, + :data:`~pymongo.TEXT`). + + To create a single key ascending index on the key ``'mike'`` we just + use a string argument:: + + >>> my_collection.create_index("mike") + + For a compound index on ``'mike'`` descending and ``'eliot'`` + ascending we need to use a list of tuples:: + + >>> my_collection.create_index([("mike", pymongo.DESCENDING), + ... ("eliot", pymongo.ASCENDING)]) + + All optional index creation parameters should be passed as + keyword arguments to this method. For example:: + + >>> my_collection.create_index([("mike", pymongo.DESCENDING)], + ... background=True) + + Valid options include, but are not limited to: + + - `name`: custom name to use for this index - if none is + given, a name will be generated. + - `unique`: if ``True`` creates a uniqueness constraint on the index. + - `background`: if ``True`` this index should be created in the + background. + - `sparse`: if ``True``, omit from the index any documents that lack + the indexed field. + - `bucketSize`: for use with geoHaystack indexes. + Number of documents to group together within a certain proximity + to a given longitude and latitude. + - `min`: minimum value for keys in a :data:`~pymongo.GEO2D` + index. + - `max`: maximum value for keys in a :data:`~pymongo.GEO2D` + index. + - `expireAfterSeconds`: Used to create an expiring (TTL) + collection. MongoDB will automatically delete documents from + this collection after seconds. The indexed field must + be a UTC datetime or the data will not expire. + - `partialFilterExpression`: A document that specifies a filter for + a partial index. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only supported + on MongoDB 3.4 and above. + + See the MongoDB documentation for a full list of supported options by + server version. + + .. warning:: `dropDups` is not supported by MongoDB 3.0 or newer. The + option is silently ignored by the server and unique index builds + using the option will fail if a duplicate value is detected. + + .. note:: `partialFilterExpression` requires server version **>= 3.2** + + .. note:: The :attr:`~pymongo.collection.Collection.write_concern` of + this collection is automatically applied to this operation when using + MongoDB >= 3.4. + + :Parameters: + - `keys`: a single key or a list of (key, direction) + pairs specifying the index to create + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): any additional index creation + options (see the above list) should be passed as keyword + arguments + + .. versionchanged:: 3.6 + Added ``session`` parameter. Added support for passing maxTimeMS + in kwargs. + .. versionchanged:: 3.4 + Apply this collection's write concern automatically to this operation + when connected to MongoDB >= 3.4. Support the `collation` option. + .. versionchanged:: 3.2 + Added partialFilterExpression to support partial indexes. + .. versionchanged:: 3.0 + Renamed `key_or_list` to `keys`. Removed the `cache_for` option. + :meth:`create_index` no longer caches index names. Removed support + for the drop_dups and bucket_size aliases. + + .. mongodoc:: indexes + """ + keys = helpers._index_list(keys) + name = kwargs.setdefault("name", helpers._gen_index_name(keys)) + cmd_options = {} + if "maxTimeMS" in kwargs: + cmd_options["maxTimeMS"] = kwargs.pop("maxTimeMS") + self.__create_index(keys, kwargs, session, **cmd_options) + return name + + def ensure_index(self, key_or_list, cache_for=300, **kwargs): + """**DEPRECATED** - Ensures that an index exists on this collection. + + .. versionchanged:: 3.0 + **DEPRECATED** + """ + warnings.warn("ensure_index is deprecated. Use create_index instead.", + DeprecationWarning, stacklevel=2) + # The types supported by datetime.timedelta. + if not (isinstance(cache_for, integer_types) or + isinstance(cache_for, float)): + raise TypeError("cache_for must be an integer or float.") + + if "drop_dups" in kwargs: + kwargs["dropDups"] = kwargs.pop("drop_dups") + + if "bucket_size" in kwargs: + kwargs["bucketSize"] = kwargs.pop("bucket_size") + + keys = helpers._index_list(key_or_list) + name = kwargs.setdefault("name", helpers._gen_index_name(keys)) + + # Note that there is a race condition here. One thread could + # check if the index is cached and be preempted before creating + # and caching the index. This means multiple threads attempting + # to create the same index concurrently could send the index + # to the server two or more times. This has no practical impact + # other than wasted round trips. + if not self.__database.client._cached(self.__database.name, + self.__name, name): + self.__create_index(keys, kwargs, session=None) + self.__database.client._cache_index(self.__database.name, + self.__name, name, cache_for) + return name + return None + + def drop_indexes(self, session=None, **kwargs): + """Drops all indexes on this collection. + + Can be used on non-existant collections or collections with no indexes. + Raises OperationFailure on an error. + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): optional arguments to the createIndexes + command (like maxTimeMS) can be passed as keyword arguments. + + .. note:: The :attr:`~pymongo.collection.Collection.write_concern` of + this collection is automatically applied to this operation when using + MongoDB >= 3.4. + + .. versionchanged:: 3.6 + Added ``session`` parameter. Added support for arbitrary keyword + arguments. + + .. versionchanged:: 3.4 + Apply this collection's write concern automatically to this operation + when connected to MongoDB >= 3.4. + + """ + self.__database.client._purge_index(self.__database.name, self.__name) + self.drop_index("*", session=session, **kwargs) + + def drop_index(self, index_or_name, session=None, **kwargs): + """Drops the specified index on this collection. + + Can be used on non-existant collections or collections with no + indexes. Raises OperationFailure on an error (e.g. trying to + drop an index that does not exist). `index_or_name` + can be either an index name (as returned by `create_index`), + or an index specifier (as passed to `create_index`). An index + specifier should be a list of (key, direction) pairs. Raises + TypeError if index is not an instance of (str, unicode, list). + + .. warning:: + + if a custom name was used on index creation (by + passing the `name` parameter to :meth:`create_index` or + :meth:`ensure_index`) the index **must** be dropped by name. + + :Parameters: + - `index_or_name`: index (or name of index) to drop + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): optional arguments to the createIndexes + command (like maxTimeMS) can be passed as keyword arguments. + + .. note:: The :attr:`~pymongo.collection.Collection.write_concern` of + this collection is automatically applied to this operation when using + MongoDB >= 3.4. + + .. versionchanged:: 3.6 + Added ``session`` parameter. Added support for arbitrary keyword + arguments. + + .. versionchanged:: 3.4 + Apply this collection's write concern automatically to this operation + when connected to MongoDB >= 3.4. + + """ + name = index_or_name + if isinstance(index_or_name, list): + name = helpers._gen_index_name(index_or_name) + + if not isinstance(name, string_type): + raise TypeError("index_or_name must be an index name or list") + + self.__database.client._purge_index( + self.__database.name, self.__name, name) + cmd = SON([("dropIndexes", self.__name), ("index", name)]) + cmd.update(kwargs) + with self._socket_for_writes() as sock_info: + self._command(sock_info, + cmd, + read_preference=ReadPreference.PRIMARY, + allowable_errors=["ns not found"], + write_concern=self._write_concern_for(session), + session=session) + + def reindex(self, session=None, **kwargs): + """Rebuilds all indexes on this collection. + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): optional arguments to the reIndex + command (like maxTimeMS) can be passed as keyword arguments. + + .. warning:: reindex blocks all other operations (indexes + are built in the foreground) and will be slow for large + collections. + + .. versionchanged:: 3.6 + Added ``session`` parameter. Added support for arbitrary keyword + arguments. + + .. versionchanged:: 3.4 + Apply this collection's write concern automatically to this operation + when connected to MongoDB >= 3.4. + + .. versionchanged:: 3.5 + We no longer apply this collection's write concern to this operation. + MongoDB 3.4 silently ignored the write concern. MongoDB 3.6+ returns + an error if we include the write concern. + """ + cmd = SON([("reIndex", self.__name)]) + cmd.update(kwargs) + with self._socket_for_writes() as sock_info: + return self._command( + sock_info, cmd, read_preference=ReadPreference.PRIMARY, + session=session) + + def list_indexes(self, session=None): + """Get a cursor over the index documents for this collection. + + >>> for index in db.test.list_indexes(): + ... print(index) + ... + SON([(u'v', 1), (u'key', SON([(u'_id', 1)])), + (u'name', u'_id_'), (u'ns', u'test.test')]) + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + :Returns: + An instance of :class:`~pymongo.command_cursor.CommandCursor`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionadded:: 3.0 + """ + codec_options = CodecOptions(SON) + coll = self.with_options(codec_options=codec_options, + read_preference=ReadPreference.PRIMARY) + sock_ctx, read_pref = self._socket_for_primary_reads(session) + with sock_ctx as (sock_info, slave_ok): + cmd = SON([("listIndexes", self.__name), ("cursor", {})]) + if sock_info.max_wire_version > 2: + with self.__database.client._tmp_session(session, False) as s: + try: + cursor = self._command(sock_info, cmd, slave_ok, + read_pref, + codec_options, + session=s)["cursor"] + except OperationFailure as exc: + # Ignore NamespaceNotFound errors to match the behavior + # of reading from *.system.indexes. + if exc.code != 26: + raise + cursor = {'id': 0, 'firstBatch': []} + return CommandCursor(coll, cursor, sock_info.address, + session=s, + explicit_session=session is not None) + else: + res = message._first_batch( + sock_info, self.__database.name, "system.indexes", + {"ns": self.__full_name}, 0, slave_ok, codec_options, + read_pref, cmd, + self.database.client._event_listeners) + cursor = res["cursor"] + # Note that a collection can only have 64 indexes, so there + # will never be a getMore call. + return CommandCursor(coll, cursor, sock_info.address) + + def index_information(self, session=None): + """Get information on this collection's indexes. + + Returns a dictionary where the keys are index names (as + returned by create_index()) and the values are dictionaries + containing information about each index. The dictionary is + guaranteed to contain at least a single key, ``"key"`` which + is a list of (key, direction) pairs specifying the index (as + passed to create_index()). It will also contain any other + metadata about the indexes, except for the ``"ns"`` and + ``"name"`` keys, which are cleaned. Example output might look + like this: + + >>> db.test.create_index("x", unique=True) + u'x_1' + >>> db.test.index_information() + {u'_id_': {u'key': [(u'_id', 1)]}, + u'x_1': {u'unique': True, u'key': [(u'x', 1)]}} + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + cursor = self.list_indexes(session=session) + info = {} + for index in cursor: + index["key"] = index["key"].items() + index = dict(index) + info[index.pop("name")] = index + return info + + def options(self, session=None): + """Get the options set on this collection. + + Returns a dictionary of options and their values - see + :meth:`~pymongo.database.Database.create_collection` for more + information on the possible options. Returns an empty + dictionary if the collection has not been created yet. + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + dbo = self.__database.client.get_database( + self.__database.name, + self.codec_options, + self.read_preference, + self.write_concern, + self.read_concern) + cursor = dbo.list_collections( + session=session, filter={"name": self.__name}) + + result = None + for doc in cursor: + result = doc + break + + if not result: + return {} + + options = result.get("options", {}) + if "create" in options: + del options["create"] + + return options + + def _aggregate(self, pipeline, cursor_class, first_batch_size, session, + explicit_session, **kwargs): + common.validate_list('pipeline', pipeline) + + if "explain" in kwargs: + raise ConfigurationError("The explain option is not supported. " + "Use Database.command instead.") + collation = validate_collation_or_none(kwargs.pop('collation', None)) + max_await_time_ms = kwargs.pop('maxAwaitTimeMS', None) + + cmd = SON([("aggregate", self.__name), + ("pipeline", pipeline)]) + + # Remove things that are not command options. + use_cursor = True + if "useCursor" in kwargs: + warnings.warn( + "The useCursor option is deprecated " + "and will be removed in PyMongo 4.0", + DeprecationWarning, stacklevel=2) + use_cursor = common.validate_boolean( + "useCursor", kwargs.pop("useCursor")) + batch_size = common.validate_non_negative_integer_or_none( + "batchSize", kwargs.pop("batchSize", None)) + # If the server does not support the "cursor" option we + # ignore useCursor and batchSize. + with self._socket_for_reads(session) as (sock_info, slave_ok): + dollar_out = pipeline and '$out' in pipeline[-1] + if use_cursor: + if "cursor" not in kwargs: + kwargs["cursor"] = {} + # Ignore batchSize when the $out pipeline stage is used. + # batchSize is meaningless in that case since the server + # doesn't return results. This also avoids SERVER-23923. + if first_batch_size is not None and not dollar_out: + kwargs["cursor"]["batchSize"] = first_batch_size + + cmd.update(kwargs) + # Apply this Collection's read concern if $out is not in the + # pipeline. + if (sock_info.max_wire_version >= 4 + and 'readConcern' not in cmd + and not dollar_out): + read_concern = self.read_concern + else: + read_concern = None + if 'writeConcern' not in cmd and dollar_out: + write_concern = self._write_concern_for(session) + else: + write_concern = None + + # Avoid auto-injecting a session: aggregate() passes a session, + # aggregate_raw_batches() passes none. + result = sock_info.command( + self.__database.name, + cmd, + slave_ok, + self._read_preference_for(session), + self.codec_options, + parse_write_concern_error=True, + read_concern=read_concern, + write_concern=write_concern, + collation=collation, + session=session, + client=self.__database.client, + user_fields={'cursor': {'firstBatch': 1}}) + + if "cursor" in result: + cursor = result["cursor"] + else: + # Pre-MongoDB 2.6 or unacknowledged write. Fake a cursor. + cursor = { + "id": 0, + "firstBatch": result.get("result", []), + "ns": self.full_name, + } + + return cursor_class( + self, cursor, sock_info.address, + batch_size=batch_size or 0, + max_await_time_ms=max_await_time_ms, + session=session, explicit_session=explicit_session) + + def aggregate(self, pipeline, session=None, **kwargs): + """Perform an aggregation using the aggregation framework on this + collection. + + All optional `aggregate command`_ parameters should be passed as + keyword arguments to this method. Valid options include, but are not + limited to: + + - `allowDiskUse` (bool): Enables writing to temporary files. When set + to True, aggregation stages can write data to the _tmp subdirectory + of the --dbpath directory. The default is False. + - `maxTimeMS` (int): The maximum amount of time to allow the operation + to run in milliseconds. + - `batchSize` (int): The maximum number of documents to return per + batch. Ignored if the connected mongod or mongos does not support + returning aggregate results using a cursor, or `useCursor` is + ``False``. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only supported + on MongoDB 3.4 and above. + - `useCursor` (bool): Deprecated. Will be removed in PyMongo 4.0. + + The :meth:`aggregate` method obeys the :attr:`read_preference` of this + :class:`Collection`. Please note that using the ``$out`` pipeline stage + requires a read preference of + :attr:`~pymongo.read_preferences.ReadPreference.PRIMARY` (the default). + The server will raise an error if the ``$out`` pipeline stage is used + with any other read preference. + + .. note:: This method does not support the 'explain' option. Please + use :meth:`~pymongo.database.Database.command` instead. An + example is included in the :ref:`aggregate-examples` documentation. + + .. note:: The :attr:`~pymongo.collection.Collection.write_concern` of + this collection is automatically applied to this operation when using + MongoDB >= 3.4. + + :Parameters: + - `pipeline`: a list of aggregation pipeline stages + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): See list of options above. + + :Returns: + A :class:`~pymongo.command_cursor.CommandCursor` over the result + set. + + .. versionchanged:: 3.6 + Added the `session` parameter. Added the `maxAwaitTimeMS` option. + Deprecated the `useCursor` option. + .. versionchanged:: 3.4 + Apply this collection's write concern automatically to this operation + when connected to MongoDB >= 3.4. Support the `collation` option. + .. versionchanged:: 3.0 + The :meth:`aggregate` method always returns a CommandCursor. The + pipeline argument must be a list. + .. versionchanged:: 2.7 + When the cursor option is used, return + :class:`~pymongo.command_cursor.CommandCursor` instead of + :class:`~pymongo.cursor.Cursor`. + .. versionchanged:: 2.6 + Added cursor support. + .. versionadded:: 2.3 + + .. seealso:: :doc:`/examples/aggregation` + + .. _aggregate command: + https://docs.mongodb.com/manual/reference/command/aggregate + """ + with self.__database.client._tmp_session(session, close=False) as s: + return self._aggregate(pipeline, + CommandCursor, + kwargs.get('batchSize'), + session=s, + explicit_session=session is not None, + **kwargs) + + def aggregate_raw_batches(self, pipeline, **kwargs): + """Perform an aggregation and retrieve batches of raw BSON. + + Similar to the :meth:`aggregate` method but returns a + :class:`~pymongo.cursor.RawBatchCursor`. + + This example demonstrates how to work with raw batches, but in practice + raw batches should be passed to an external library that can decode + BSON into another data type, rather than used with PyMongo's + :mod:`bson` module. + + >>> import bson + >>> cursor = db.test.aggregate_raw_batches([ + ... {'$project': {'x': {'$multiply': [2, '$x']}}}]) + >>> for batch in cursor: + ... print(bson.decode_all(batch)) + + .. note:: aggregate_raw_batches does not support sessions. + + .. versionadded:: 3.6 + """ + # OP_MSG with document stream returns is required to support + # sessions. + if "session" in kwargs: + raise ConfigurationError( + "aggregate_raw_batches does not support sessions") + return self._aggregate(pipeline, RawBatchCommandCursor, 0, + None, False, **kwargs) + + def watch(self, pipeline=None, full_document='default', resume_after=None, + max_await_time_ms=None, batch_size=None, collation=None, + start_at_operation_time=None, session=None): + """Watch changes on this collection. + + Performs an aggregation with an implicit initial ``$changeStream`` + stage and returns a + :class:`~pymongo.change_stream.CollectionChangeStream` cursor which + iterates over changes on this collection. + + Introduced in MongoDB 3.6. + + .. code-block:: python + + with db.collection.watch() as stream: + for change in stream: + print(change) + + The :class:`~pymongo.change_stream.CollectionChangeStream` iterable + blocks until the next change document is returned or an error is + raised. If the + :meth:`~pymongo.change_stream.CollectionChangeStream.next` method + encounters a network error when retrieving a batch from the server, + it will automatically attempt to recreate the cursor such that no + change events are missed. Any error encountered during the resume + attempt indicates there may be an outage and will be raised. + + .. code-block:: python + + try: + with db.collection.watch( + [{'$match': {'operationType': 'insert'}}]) as stream: + for insert_change in stream: + print(insert_change) + except pymongo.errors.PyMongoError: + # The ChangeStream encountered an unrecoverable error or the + # resume attempt failed to recreate the cursor. + logging.error('...') + + For a precise description of the resume process see the + `change streams specification`_. + + .. note:: Using this helper method is preferred to directly calling + :meth:`~pymongo.collection.Collection.aggregate` with a + ``$changeStream`` stage, for the purpose of supporting + resumability. + + .. warning:: This Collection's :attr:`read_concern` must be + ``ReadConcern("majority")`` in order to use the ``$changeStream`` + stage. + + :Parameters: + - `pipeline` (optional): A list of aggregation pipeline stages to + append to an initial ``$changeStream`` stage. Not all + pipeline stages are valid after a ``$changeStream`` stage, see the + MongoDB documentation on change streams for the supported stages. + - `full_document` (optional): The fullDocument to pass as an option + to the ``$changeStream`` stage. Allowed values: 'default', + 'updateLookup'. Defaults to 'default'. + When set to 'updateLookup', the change notification for partial + updates will include both a delta describing the changes to the + document, as well as a copy of the entire document that was + changed from some time after the change occurred. + - `resume_after` (optional): The logical starting point for this + change stream. + - `max_await_time_ms` (optional): The maximum time in milliseconds + for the server to wait for changes before responding to a getMore + operation. + - `batch_size` (optional): The maximum number of documents to return + per batch. + - `collation` (optional): The :class:`~pymongo.collation.Collation` + to use for the aggregation. + - `start_at_operation_time` (optional): If provided, the resulting + change stream will only return changes that occurred at or after + the specified :class:`~bson.timestamp.Timestamp`. Requires + MongoDB >= 4.0. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + :Returns: + A :class:`~pymongo.change_stream.CollectionChangeStream` cursor. + + .. versionchanged:: 3.7 + Added the ``start_at_operation_time`` parameter. + + .. versionadded:: 3.6 + + .. mongodoc:: changeStreams + + .. _change streams specification: + https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst + """ + return CollectionChangeStream( + self, pipeline, full_document, resume_after, max_await_time_ms, + batch_size, collation, start_at_operation_time, session + ) + + def group(self, key, condition, initial, reduce, finalize=None, **kwargs): + """Perform a query similar to an SQL *group by* operation. + + **DEPRECATED** - The group command was deprecated in MongoDB 3.4. The + :meth:`~group` method is deprecated and will be removed in PyMongo 4.0. + Use :meth:`~aggregate` with the `$group` stage or :meth:`~map_reduce` + instead. + + .. versionchanged:: 3.5 + Deprecated the group method. + .. versionchanged:: 3.4 + Added the `collation` option. + .. versionchanged:: 2.2 + Removed deprecated argument: command + """ + warnings.warn("The group method is deprecated and will be removed in " + "PyMongo 4.0. Use the aggregate method with the $group " + "stage or the map_reduce method instead.", + DeprecationWarning, stacklevel=2) + group = {} + if isinstance(key, string_type): + group["$keyf"] = Code(key) + elif key is not None: + group = {"key": helpers._fields_list_to_dict(key, "key")} + group["ns"] = self.__name + group["$reduce"] = Code(reduce) + group["cond"] = condition + group["initial"] = initial + if finalize is not None: + group["finalize"] = Code(finalize) + + cmd = SON([("group", group)]) + collation = validate_collation_or_none(kwargs.pop('collation', None)) + cmd.update(kwargs) + + with self._socket_for_reads(session=None) as (sock_info, slave_ok): + return self._command(sock_info, cmd, slave_ok, + collation=collation, + user_fields={'retval': 1})["retval"] + + def rename(self, new_name, session=None, **kwargs): + """Rename this collection. + + If operating in auth mode, client must be authorized as an + admin to perform this operation. Raises :class:`TypeError` if + `new_name` is not an instance of :class:`basestring` + (:class:`str` in python 3). Raises :class:`~pymongo.errors.InvalidName` + if `new_name` is not a valid collection name. + + :Parameters: + - `new_name`: new name for this collection + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): additional arguments to the rename command + may be passed as keyword arguments to this helper method + (i.e. ``dropTarget=True``) + + .. note:: The :attr:`~pymongo.collection.Collection.write_concern` of + this collection is automatically applied to this operation when using + MongoDB >= 3.4. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.4 + Apply this collection's write concern automatically to this operation + when connected to MongoDB >= 3.4. + + """ + if not isinstance(new_name, string_type): + raise TypeError("new_name must be an " + "instance of %s" % (string_type.__name__,)) + + if not new_name or ".." in new_name: + raise InvalidName("collection names cannot be empty") + if new_name[0] == "." or new_name[-1] == ".": + raise InvalidName("collecion names must not start or end with '.'") + if "$" in new_name and not new_name.startswith("oplog.$main"): + raise InvalidName("collection names must not contain '$'") + + new_name = "%s.%s" % (self.__database.name, new_name) + cmd = SON([("renameCollection", self.__full_name), ("to", new_name)]) + cmd.update(kwargs) + write_concern = self._write_concern_for_cmd(cmd, session) + + with self._socket_for_writes() as sock_info: + with self.__database.client._tmp_session(session) as s: + return sock_info.command( + 'admin', cmd, + write_concern=write_concern, + parse_write_concern_error=True, + session=s, client=self.__database.client) + + def distinct(self, key, filter=None, session=None, **kwargs): + """Get a list of distinct values for `key` among all documents + in this collection. + + Raises :class:`TypeError` if `key` is not an instance of + :class:`basestring` (:class:`str` in python 3). + + All optional distinct parameters should be passed as keyword arguments + to this method. Valid options include: + + - `maxTimeMS` (int): The maximum amount of time to allow the count + command to run, in milliseconds. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only supported + on MongoDB 3.4 and above. + + The :meth:`distinct` method obeys the :attr:`read_preference` of + this :class:`Collection`. + + :Parameters: + - `key`: name of the field for which we want to get the distinct + values + - `filter` (optional): A query document that specifies the documents + from which to retrieve the distinct values. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): See list of options above. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.4 + Support the `collation` option. + + """ + if not isinstance(key, string_type): + raise TypeError("key must be an " + "instance of %s" % (string_type.__name__,)) + cmd = SON([("distinct", self.__name), + ("key", key)]) + if filter is not None: + if "query" in kwargs: + raise ConfigurationError("can't pass both filter and query") + kwargs["query"] = filter + collation = validate_collation_or_none(kwargs.pop('collation', None)) + cmd.update(kwargs) + with self._socket_for_reads(session) as (sock_info, slave_ok): + return self._command(sock_info, cmd, slave_ok, + read_concern=self.read_concern, + collation=collation, + session=session, + user_fields={"values": 1})["values"] + + def map_reduce(self, map, reduce, out, full_response=False, session=None, + **kwargs): + """Perform a map/reduce operation on this collection. + + If `full_response` is ``False`` (default) returns a + :class:`~pymongo.collection.Collection` instance containing + the results of the operation. Otherwise, returns the full + response from the server to the `map reduce command`_. + + :Parameters: + - `map`: map function (as a JavaScript string) + - `reduce`: reduce function (as a JavaScript string) + - `out`: output collection name or `out object` (dict). See + the `map reduce command`_ documentation for available options. + Note: `out` options are order sensitive. :class:`~bson.son.SON` + can be used to specify multiple options. + e.g. SON([('replace', ), ('db', )]) + - `full_response` (optional): if ``True``, return full response to + this command - otherwise just return the result collection + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): additional arguments to the + `map reduce command`_ may be passed as keyword arguments to this + helper method, e.g.:: + + >>> db.test.map_reduce(map, reduce, "myresults", limit=2) + + .. note:: The :meth:`map_reduce` method does **not** obey the + :attr:`read_preference` of this :class:`Collection`. To run + mapReduce on a secondary use the :meth:`inline_map_reduce` method + instead. + + .. note:: The :attr:`~pymongo.collection.Collection.write_concern` of + this collection is automatically applied to this operation (if the + output is not inline) when using MongoDB >= 3.4. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.4 + Apply this collection's write concern automatically to this operation + when connected to MongoDB >= 3.4. + + .. seealso:: :doc:`/examples/aggregation` + + .. versionchanged:: 3.4 + Added the `collation` option. + .. versionchanged:: 2.2 + Removed deprecated arguments: merge_output and reduce_output + + .. _map reduce command: http://docs.mongodb.org/manual/reference/command/mapReduce/ + + .. mongodoc:: mapreduce + + """ + if not isinstance(out, (string_type, abc.Mapping)): + raise TypeError("'out' must be an instance of " + "%s or a mapping" % (string_type.__name__,)) + + cmd = SON([("mapreduce", self.__name), + ("map", map), + ("reduce", reduce), + ("out", out)]) + collation = validate_collation_or_none(kwargs.pop('collation', None)) + cmd.update(kwargs) + + inline = 'inline' in cmd['out'] + sock_ctx, read_pref = self._socket_for_primary_reads(session) + with sock_ctx as (sock_info, slave_ok): + if (sock_info.max_wire_version >= 4 and 'readConcern' not in cmd and + inline): + read_concern = self.read_concern + else: + read_concern = None + if 'writeConcern' not in cmd and not inline: + write_concern = self._write_concern_for(session) + else: + write_concern = None + if inline: + user_fields = {'results': 1} + else: + user_fields = None + + response = self._command( + sock_info, cmd, slave_ok, read_pref, + read_concern=read_concern, + write_concern=write_concern, + collation=collation, session=session, + user_fields=user_fields) + + if full_response or not response.get('result'): + return response + elif isinstance(response['result'], dict): + dbase = response['result']['db'] + coll = response['result']['collection'] + return self.__database.client[dbase][coll] + else: + return self.__database[response["result"]] + + def inline_map_reduce(self, map, reduce, full_response=False, session=None, + **kwargs): + """Perform an inline map/reduce operation on this collection. + + Perform the map/reduce operation on the server in RAM. A result + collection is not created. The result set is returned as a list + of documents. + + If `full_response` is ``False`` (default) returns the + result documents in a list. Otherwise, returns the full + response from the server to the `map reduce command`_. + + The :meth:`inline_map_reduce` method obeys the :attr:`read_preference` + of this :class:`Collection`. + + :Parameters: + - `map`: map function (as a JavaScript string) + - `reduce`: reduce function (as a JavaScript string) + - `full_response` (optional): if ``True``, return full response to + this command - otherwise just return the result collection + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): additional arguments to the + `map reduce command`_ may be passed as keyword arguments to this + helper method, e.g.:: + + >>> db.test.inline_map_reduce(map, reduce, limit=2) + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.4 + Added the `collation` option. + + """ + cmd = SON([("mapreduce", self.__name), + ("map", map), + ("reduce", reduce), + ("out", {"inline": 1})]) + user_fields = {'results': 1} + collation = validate_collation_or_none(kwargs.pop('collation', None)) + cmd.update(kwargs) + with self._socket_for_reads(session) as (sock_info, slave_ok): + if sock_info.max_wire_version >= 4 and 'readConcern' not in cmd: + res = self._command(sock_info, cmd, slave_ok, + read_concern=self.read_concern, + collation=collation, session=session, + user_fields=user_fields) + else: + res = self._command(sock_info, cmd, slave_ok, + collation=collation, session=session, + user_fields=user_fields) + + if full_response: + return res + else: + return res.get("results") + + def _write_concern_for_cmd(self, cmd, session): + raw_wc = cmd.get('writeConcern') + if raw_wc is not None: + return WriteConcern(**raw_wc) + else: + return self._write_concern_for(session) + + def __find_and_modify(self, filter, projection, sort, upsert=None, + return_document=ReturnDocument.BEFORE, + array_filters=None, session=None, **kwargs): + """Internal findAndModify helper.""" + + common.validate_is_mapping("filter", filter) + if not isinstance(return_document, bool): + raise ValueError("return_document must be " + "ReturnDocument.BEFORE or ReturnDocument.AFTER") + collation = validate_collation_or_none(kwargs.pop('collation', None)) + cmd = SON([("findAndModify", self.__name), + ("query", filter), + ("new", return_document)]) + cmd.update(kwargs) + if projection is not None: + cmd["fields"] = helpers._fields_list_to_dict(projection, + "projection") + if sort is not None: + cmd["sort"] = helpers._index_document(sort) + if upsert is not None: + common.validate_boolean("upsert", upsert) + cmd["upsert"] = upsert + + write_concern = self._write_concern_for_cmd(cmd, session) + + def _find_and_modify(session, sock_info, retryable_write): + if array_filters is not None: + if sock_info.max_wire_version < 6: + raise ConfigurationError( + 'Must be connected to MongoDB 3.6+ to use ' + 'arrayFilters.') + if not write_concern.acknowledged: + raise ConfigurationError( + 'arrayFilters is unsupported for unacknowledged ' + 'writes.') + cmd["arrayFilters"] = array_filters + if (sock_info.max_wire_version >= 4 and + not write_concern.is_server_default): + cmd['writeConcern'] = write_concern.document + out = self._command(sock_info, cmd, + read_preference=ReadPreference.PRIMARY, + write_concern=write_concern, + allowable_errors=[_NO_OBJ_ERROR], + collation=collation, session=session, + retryable_write=retryable_write, + user_fields=_FIND_AND_MODIFY_DOC_FIELDS) + _check_write_command_response(out) + + return out.get("value") + + return self.__database.client._retryable_write( + write_concern.acknowledged, _find_and_modify, session) + + def find_one_and_delete(self, filter, + projection=None, sort=None, session=None, **kwargs): + """Finds a single document and deletes it, returning the document. + + >>> db.test.count_documents({'x': 1}) + 2 + >>> db.test.find_one_and_delete({'x': 1}) + {u'x': 1, u'_id': ObjectId('54f4e12bfba5220aa4d6dee8')} + >>> db.test.count_documents({'x': 1}) + 1 + + If multiple documents match *filter*, a *sort* can be applied. + + >>> for doc in db.test.find({'x': 1}): + ... print(doc) + ... + {u'x': 1, u'_id': 0} + {u'x': 1, u'_id': 1} + {u'x': 1, u'_id': 2} + >>> db.test.find_one_and_delete( + ... {'x': 1}, sort=[('_id', pymongo.DESCENDING)]) + {u'x': 1, u'_id': 2} + + The *projection* option can be used to limit the fields returned. + + >>> db.test.find_one_and_delete({'x': 1}, projection={'_id': False}) + {u'x': 1} + + :Parameters: + - `filter`: A query that matches the document to delete. + - `projection` (optional): a list of field names that should be + returned in the result document or a mapping specifying the fields + to include or exclude. If `projection` is a list "_id" will + always be returned. Use a mapping to exclude fields from + the result (e.g. projection={'_id': False}). + - `sort` (optional): a list of (key, direction) pairs + specifying the sort order for the query. If multiple documents + match the query, they are sorted and the first is deleted. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): additional command arguments can be passed + as keyword arguments (for example maxTimeMS can be used with + recent server versions). + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.2 + Respects write concern. + + .. warning:: Starting in PyMongo 3.2, this command uses the + :class:`~pymongo.write_concern.WriteConcern` of this + :class:`~pymongo.collection.Collection` when connected to MongoDB >= + 3.2. Note that using an elevated write concern with this command may + be slower compared to using the default write concern. + + .. versionchanged:: 3.4 + Added the `collation` option. + .. versionadded:: 3.0 + + """ + kwargs['remove'] = True + return self.__find_and_modify(filter, projection, sort, + session=session, **kwargs) + + def find_one_and_replace(self, filter, replacement, + projection=None, sort=None, upsert=False, + return_document=ReturnDocument.BEFORE, + session=None, **kwargs): + """Finds a single document and replaces it, returning either the + original or the replaced document. + + The :meth:`find_one_and_replace` method differs from + :meth:`find_one_and_update` by replacing the document matched by + *filter*, rather than modifying the existing document. + + >>> for doc in db.test.find({}): + ... print(doc) + ... + {u'x': 1, u'_id': 0} + {u'x': 1, u'_id': 1} + {u'x': 1, u'_id': 2} + >>> db.test.find_one_and_replace({'x': 1}, {'y': 1}) + {u'x': 1, u'_id': 0} + >>> for doc in db.test.find({}): + ... print(doc) + ... + {u'y': 1, u'_id': 0} + {u'x': 1, u'_id': 1} + {u'x': 1, u'_id': 2} + + :Parameters: + - `filter`: A query that matches the document to replace. + - `replacement`: The replacement document. + - `projection` (optional): A list of field names that should be + returned in the result document or a mapping specifying the fields + to include or exclude. If `projection` is a list "_id" will + always be returned. Use a mapping to exclude fields from + the result (e.g. projection={'_id': False}). + - `sort` (optional): a list of (key, direction) pairs + specifying the sort order for the query. If multiple documents + match the query, they are sorted and the first is replaced. + - `upsert` (optional): When ``True``, inserts a new document if no + document matches the query. Defaults to ``False``. + - `return_document`: If + :attr:`ReturnDocument.BEFORE` (the default), + returns the original document before it was replaced, or ``None`` + if no document matches. If + :attr:`ReturnDocument.AFTER`, returns the replaced + or inserted document. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): additional command arguments can be passed + as keyword arguments (for example maxTimeMS can be used with + recent server versions). + + .. versionchanged:: 3.6 + Added ``session`` parameter. + .. versionchanged:: 3.4 + Added the `collation` option. + .. versionchanged:: 3.2 + Respects write concern. + + .. warning:: Starting in PyMongo 3.2, this command uses the + :class:`~pymongo.write_concern.WriteConcern` of this + :class:`~pymongo.collection.Collection` when connected to MongoDB >= + 3.2. Note that using an elevated write concern with this command may + be slower compared to using the default write concern. + + .. versionadded:: 3.0 + """ + common.validate_ok_for_replace(replacement) + kwargs['update'] = replacement + return self.__find_and_modify(filter, projection, + sort, upsert, return_document, + session=session, **kwargs) + + def find_one_and_update(self, filter, update, + projection=None, sort=None, upsert=False, + return_document=ReturnDocument.BEFORE, + array_filters=None, session=None, **kwargs): + """Finds a single document and updates it, returning either the + original or the updated document. + + >>> db.test.find_one_and_update( + ... {'_id': 665}, {'$inc': {'count': 1}, '$set': {'done': True}}) + {u'_id': 665, u'done': False, u'count': 25}} + + Returns ``None`` if no document matches the filter. + + >>> db.test.find_one_and_update( + ... {'_exists': False}, {'$inc': {'count': 1}}) + + When the filter matches, by default :meth:`find_one_and_update` + returns the original version of the document before the update was + applied. To return the updated (or inserted in the case of + *upsert*) version of the document instead, use the *return_document* + option. + + >>> from pymongo import ReturnDocument + >>> db.example.find_one_and_update( + ... {'_id': 'userid'}, + ... {'$inc': {'seq': 1}}, + ... return_document=ReturnDocument.AFTER) + {u'_id': u'userid', u'seq': 1} + + You can limit the fields returned with the *projection* option. + + >>> db.example.find_one_and_update( + ... {'_id': 'userid'}, + ... {'$inc': {'seq': 1}}, + ... projection={'seq': True, '_id': False}, + ... return_document=ReturnDocument.AFTER) + {u'seq': 2} + + The *upsert* option can be used to create the document if it doesn't + already exist. + + >>> db.example.delete_many({}).deleted_count + 1 + >>> db.example.find_one_and_update( + ... {'_id': 'userid'}, + ... {'$inc': {'seq': 1}}, + ... projection={'seq': True, '_id': False}, + ... upsert=True, + ... return_document=ReturnDocument.AFTER) + {u'seq': 1} + + If multiple documents match *filter*, a *sort* can be applied. + + >>> for doc in db.test.find({'done': True}): + ... print(doc) + ... + {u'_id': 665, u'done': True, u'result': {u'count': 26}} + {u'_id': 701, u'done': True, u'result': {u'count': 17}} + >>> db.test.find_one_and_update( + ... {'done': True}, + ... {'$set': {'final': True}}, + ... sort=[('_id', pymongo.DESCENDING)]) + {u'_id': 701, u'done': True, u'result': {u'count': 17}} + + :Parameters: + - `filter`: A query that matches the document to update. + - `update`: The update operations to apply. + - `projection` (optional): A list of field names that should be + returned in the result document or a mapping specifying the fields + to include or exclude. If `projection` is a list "_id" will + always be returned. Use a dict to exclude fields from + the result (e.g. projection={'_id': False}). + - `sort` (optional): a list of (key, direction) pairs + specifying the sort order for the query. If multiple documents + match the query, they are sorted and the first is updated. + - `upsert` (optional): When ``True``, inserts a new document if no + document matches the query. Defaults to ``False``. + - `return_document`: If + :attr:`ReturnDocument.BEFORE` (the default), + returns the original document before it was updated. If + :attr:`ReturnDocument.AFTER`, returns the updated + or inserted document. + - `array_filters` (optional): A list of filters specifying which + array elements an update should apply. Requires MongoDB 3.6+. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): additional command arguments can be passed + as keyword arguments (for example maxTimeMS can be used with + recent server versions). + + .. versionchanged:: 3.6 + Added the `array_filters` and `session` options. + .. versionchanged:: 3.4 + Added the `collation` option. + .. versionchanged:: 3.2 + Respects write concern. + + .. warning:: Starting in PyMongo 3.2, this command uses the + :class:`~pymongo.write_concern.WriteConcern` of this + :class:`~pymongo.collection.Collection` when connected to MongoDB >= + 3.2. Note that using an elevated write concern with this command may + be slower compared to using the default write concern. + + .. versionadded:: 3.0 + """ + common.validate_ok_for_update(update) + common.validate_list_or_none('array_filters', array_filters) + kwargs['update'] = update + return self.__find_and_modify(filter, projection, + sort, upsert, return_document, + array_filters, session=session, **kwargs) + + def save(self, to_save, manipulate=True, check_keys=True, **kwargs): + """Save a document in this collection. + + **DEPRECATED** - Use :meth:`insert_one` or :meth:`replace_one` instead. + + .. versionchanged:: 3.0 + Removed the `safe` parameter. Pass ``w=0`` for unacknowledged write + operations. + """ + warnings.warn("save is deprecated. Use insert_one or replace_one " + "instead", DeprecationWarning, stacklevel=2) + common.validate_is_document_type("to_save", to_save) + + write_concern = None + collation = validate_collation_or_none(kwargs.pop('collation', None)) + if kwargs: + write_concern = WriteConcern(**kwargs) + + if not (isinstance(to_save, RawBSONDocument) or "_id" in to_save): + return self._insert( + to_save, True, check_keys, manipulate, write_concern) + else: + self._update_retryable( + {"_id": to_save["_id"]}, to_save, True, + check_keys, False, manipulate, write_concern, + collation=collation) + return to_save.get("_id") + + def insert(self, doc_or_docs, manipulate=True, + check_keys=True, continue_on_error=False, **kwargs): + """Insert a document(s) into this collection. + + **DEPRECATED** - Use :meth:`insert_one` or :meth:`insert_many` instead. + + .. versionchanged:: 3.0 + Removed the `safe` parameter. Pass ``w=0`` for unacknowledged write + operations. + """ + warnings.warn("insert is deprecated. Use insert_one or insert_many " + "instead.", DeprecationWarning, stacklevel=2) + write_concern = None + if kwargs: + write_concern = WriteConcern(**kwargs) + return self._insert(doc_or_docs, not continue_on_error, + check_keys, manipulate, write_concern) + + def update(self, spec, document, upsert=False, manipulate=False, + multi=False, check_keys=True, **kwargs): + """Update a document(s) in this collection. + + **DEPRECATED** - Use :meth:`replace_one`, :meth:`update_one`, or + :meth:`update_many` instead. + + .. versionchanged:: 3.0 + Removed the `safe` parameter. Pass ``w=0`` for unacknowledged write + operations. + """ + warnings.warn("update is deprecated. Use replace_one, update_one or " + "update_many instead.", DeprecationWarning, stacklevel=2) + common.validate_is_mapping("spec", spec) + common.validate_is_mapping("document", document) + if document: + # If a top level key begins with '$' this is a modify operation + # and we should skip key validation. It doesn't matter which key + # we check here. Passing a document with a mix of top level keys + # starting with and without a '$' is invalid and the server will + # raise an appropriate exception. + first = next(iter(document)) + if first.startswith('$'): + check_keys = False + + write_concern = None + collation = validate_collation_or_none(kwargs.pop('collation', None)) + if kwargs: + write_concern = WriteConcern(**kwargs) + return self._update_retryable( + spec, document, upsert, check_keys, multi, manipulate, + write_concern, collation=collation) + + def remove(self, spec_or_id=None, multi=True, **kwargs): + """Remove a document(s) from this collection. + + **DEPRECATED** - Use :meth:`delete_one` or :meth:`delete_many` instead. + + .. versionchanged:: 3.0 + Removed the `safe` parameter. Pass ``w=0`` for unacknowledged write + operations. + """ + warnings.warn("remove is deprecated. Use delete_one or delete_many " + "instead.", DeprecationWarning, stacklevel=2) + if spec_or_id is None: + spec_or_id = {} + if not isinstance(spec_or_id, abc.Mapping): + spec_or_id = {"_id": spec_or_id} + write_concern = None + collation = validate_collation_or_none(kwargs.pop('collation', None)) + if kwargs: + write_concern = WriteConcern(**kwargs) + return self._delete_retryable( + spec_or_id, multi, write_concern, collation=collation) + + def find_and_modify(self, query={}, update=None, + upsert=False, sort=None, full_response=False, + manipulate=False, **kwargs): + """Update and return an object. + + **DEPRECATED** - Use :meth:`find_one_and_delete`, + :meth:`find_one_and_replace`, or :meth:`find_one_and_update` instead. + """ + warnings.warn("find_and_modify is deprecated, use find_one_and_delete" + ", find_one_and_replace, or find_one_and_update instead", + DeprecationWarning, stacklevel=2) + + if not update and not kwargs.get('remove', None): + raise ValueError("Must either update or remove") + + if update and kwargs.get('remove', None): + raise ValueError("Can't do both update and remove") + + # No need to include empty args + if query: + kwargs['query'] = query + if update: + kwargs['update'] = update + if upsert: + kwargs['upsert'] = upsert + if sort: + # Accept a list of tuples to match Cursor's sort parameter. + if isinstance(sort, list): + kwargs['sort'] = helpers._index_document(sort) + # Accept OrderedDict, SON, and dict with len == 1 so we + # don't break existing code already using find_and_modify. + elif (isinstance(sort, ORDERED_TYPES) or + isinstance(sort, dict) and len(sort) == 1): + warnings.warn("Passing mapping types for `sort` is deprecated," + " use a list of (key, direction) pairs instead", + DeprecationWarning, stacklevel=2) + kwargs['sort'] = sort + else: + raise TypeError("sort must be a list of (key, direction) " + "pairs, a dict of len 1, or an instance of " + "SON or OrderedDict") + + fields = kwargs.pop("fields", None) + if fields is not None: + kwargs["fields"] = helpers._fields_list_to_dict(fields, "fields") + + collation = validate_collation_or_none(kwargs.pop('collation', None)) + + cmd = SON([("findAndModify", self.__name)]) + cmd.update(kwargs) + + write_concern = self._write_concern_for_cmd(cmd, None) + + def _find_and_modify(session, sock_info, retryable_write): + if (sock_info.max_wire_version >= 4 and + not write_concern.is_server_default): + cmd['writeConcern'] = write_concern.document + result = self._command( + sock_info, cmd, read_preference=ReadPreference.PRIMARY, + allowable_errors=[_NO_OBJ_ERROR], collation=collation, + session=session, retryable_write=retryable_write, + user_fields=_FIND_AND_MODIFY_DOC_FIELDS) + + _check_write_command_response(result) + return result + + out = self.__database.client._retryable_write( + write_concern.acknowledged, _find_and_modify, None) + + if not out['ok']: + if out["errmsg"] == _NO_OBJ_ERROR: + return None + else: + # Should never get here b/c of allowable_errors + raise ValueError("Unexpected Error: %s" % (out,)) + + if full_response: + return out + else: + document = out.get('value') + if manipulate: + document = self.__database._fix_outgoing(document, self) + return document + + def __iter__(self): + return self + + def __next__(self): + raise TypeError("'Collection' object is not iterable") + + next = __next__ + + def __call__(self, *args, **kwargs): + """This is only here so that some API misusages are easier to debug. + """ + if "." not in self.__name: + raise TypeError("'Collection' object is not callable. If you " + "meant to call the '%s' method on a 'Database' " + "object it is failing because no such method " + "exists." % + self.__name) + raise TypeError("'Collection' object is not callable. If you meant to " + "call the '%s' method on a 'Collection' object it is " + "failing because no such method exists." % + self.__name.split(".")[-1]) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/command_cursor.py b/backend/venv/lib/python3.7/site-packages/pymongo/command_cursor.py new file mode 100644 index 000000000..9dc407ee5 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/command_cursor.py @@ -0,0 +1,345 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""CommandCursor class to iterate over command results.""" + +import datetime + +from collections import deque + +from bson.py3compat import integer_types +from pymongo import helpers +from pymongo.errors import (AutoReconnect, + InvalidOperation, + NotMasterError, + OperationFailure) +from pymongo.message import (_convert_exception, + _CursorAddress, + _GetMore, + _RawBatchGetMore) + + +class CommandCursor(object): + """A cursor / iterator over command cursors.""" + _getmore_class = _GetMore + + def __init__(self, collection, cursor_info, address, retrieved=0, + batch_size=0, max_await_time_ms=None, session=None, + explicit_session=False): + """Create a new command cursor. + + The parameter 'retrieved' is unused. + """ + self.__collection = collection + self.__id = cursor_info['id'] + self.__address = address + self.__data = deque(cursor_info['firstBatch']) + self.__batch_size = batch_size + self.__max_await_time_ms = max_await_time_ms + self.__session = session + self.__explicit_session = explicit_session + self.__killed = (self.__id == 0) + if self.__killed: + self.__end_session(True) + + if "ns" in cursor_info: + self.__ns = cursor_info["ns"] + else: + self.__ns = collection.full_name + + self.batch_size(batch_size) + + if (not isinstance(max_await_time_ms, integer_types) + and max_await_time_ms is not None): + raise TypeError("max_await_time_ms must be an integer or None") + + def __del__(self): + if self.__id and not self.__killed: + self.__die() + + def __die(self, synchronous=False): + """Closes this cursor. + """ + already_killed = self.__killed + self.__killed = True + if self.__id and not already_killed: + address = _CursorAddress( + self.__address, self.__collection.full_name) + if synchronous: + self.__collection.database.client._close_cursor_now( + self.__id, address, session=self.__session) + else: + # The cursor will be closed later in a different session. + self.__collection.database.client._close_cursor( + self.__id, address) + self.__end_session(synchronous) + + def __end_session(self, synchronous): + if self.__session and not self.__explicit_session: + self.__session._end_session(lock=synchronous) + self.__session = None + + def close(self): + """Explicitly close / kill this cursor. + """ + self.__die(True) + + def batch_size(self, batch_size): + """Limits the number of documents returned in one batch. Each batch + requires a round trip to the server. It can be adjusted to optimize + performance and limit data transfer. + + .. note:: batch_size can not override MongoDB's internal limits on the + amount of data it will return to the client in a single batch (i.e + if you set batch size to 1,000,000,000, MongoDB will currently only + return 4-16MB of results per batch). + + Raises :exc:`TypeError` if `batch_size` is not an integer. + Raises :exc:`ValueError` if `batch_size` is less than ``0``. + + :Parameters: + - `batch_size`: The size of each batch of results requested. + """ + if not isinstance(batch_size, integer_types): + raise TypeError("batch_size must be an integer") + if batch_size < 0: + raise ValueError("batch_size must be >= 0") + + self.__batch_size = batch_size == 1 and 2 or batch_size + return self + + def __send_message(self, operation): + """Send a getmore message and handle the response. + """ + def kill(): + self.__killed = True + self.__end_session(True) + + client = self.__collection.database.client + listeners = client._event_listeners + publish = listeners.enabled_for_commands + start = datetime.datetime.now() + + def duration(): return datetime.datetime.now() - start + + try: + response = client._send_message_with_response( + operation, address=self.__address) + except AutoReconnect: + # Don't try to send kill cursors on another socket + # or to another server. It can cause a _pinValue + # assertion on some server releases if we get here + # due to a socket timeout. + kill() + raise + + rqst_id = response.request_id + from_command = response.from_command + reply = response.data + + try: + user_fields = None + legacy_response = True + if from_command: + user_fields = {'cursor': {'nextBatch': 1}} + legacy_response = False + docs = self._unpack_response( + reply, self.__id, self.__collection.codec_options, + legacy_response=legacy_response, user_fields=user_fields) + if from_command: + first = docs[0] + client._receive_cluster_time(first, self.__session) + helpers._check_command_response(first) + + except OperationFailure as exc: + kill() + + if publish: + listeners.publish_command_failure( + duration(), exc.details, "getMore", rqst_id, self.__address) + + raise + except NotMasterError as exc: + # Don't send kill cursors to another server after a "not master" + # error. It's completely pointless. + kill() + + if publish: + listeners.publish_command_failure( + duration(), exc.details, "getMore", rqst_id, self.__address) + + client._reset_server_and_request_check(self.__address) + raise + except Exception as exc: + if publish: + listeners.publish_command_failure( + duration(), _convert_exception(exc), "getMore", rqst_id, + self.__address) + raise + + if from_command: + cursor = docs[0]['cursor'] + documents = cursor['nextBatch'] + self.__id = cursor['id'] + if publish: + listeners.publish_command_success( + duration(), docs[0], "getMore", rqst_id, + self.__address) + else: + documents = docs + self.__id = reply.cursor_id + + if publish: + # Must publish in getMore command response format. + res = {"cursor": {"id": self.__id, + "ns": self.__collection.full_name, + "nextBatch": documents}, + "ok": 1} + listeners.publish_command_success( + duration(), res, "getMore", rqst_id, self.__address) + + if self.__id == 0: + kill() + self.__data = deque(documents) + + def _unpack_response(self, response, cursor_id, codec_options, + user_fields=None, legacy_response=False): + return response.unpack_response(cursor_id, codec_options, user_fields, + legacy_response) + + def _refresh(self): + """Refreshes the cursor with more data from the server. + + Returns the length of self.__data after refresh. Will exit early if + self.__data is already non-empty. Raises OperationFailure when the + cursor cannot be refreshed due to an error on the query. + """ + if len(self.__data) or self.__killed: + return len(self.__data) + + if self.__id: # Get More + dbname, collname = self.__ns.split('.', 1) + read_pref = self.__collection._read_preference_for(self.session) + self.__send_message( + self._getmore_class(dbname, + collname, + self.__batch_size, + self.__id, + self.__collection.codec_options, + read_pref, + self.__session, + self.__collection.database.client, + self.__max_await_time_ms)) + else: # Cursor id is zero nothing else to return + self.__killed = True + self.__end_session(True) + + return len(self.__data) + + @property + def alive(self): + """Does this cursor have the potential to return more data? + + Even if :attr:`alive` is ``True``, :meth:`next` can raise + :exc:`StopIteration`. Best to use a for loop:: + + for doc in collection.aggregate(pipeline): + print(doc) + + .. note:: :attr:`alive` can be True while iterating a cursor from + a failed server. In this case :attr:`alive` will return False after + :meth:`next` fails to retrieve the next batch of results from the + server. + """ + return bool(len(self.__data) or (not self.__killed)) + + @property + def cursor_id(self): + """Returns the id of the cursor.""" + return self.__id + + @property + def address(self): + """The (host, port) of the server used, or None. + + .. versionadded:: 3.0 + """ + return self.__address + + @property + def session(self): + """The cursor's :class:`~pymongo.client_session.ClientSession`, or None. + + .. versionadded:: 3.6 + """ + if self.__explicit_session: + return self.__session + + def __iter__(self): + return self + + def next(self): + """Advance the cursor.""" + # Block until a document is returnable. + while self.alive: + doc = self._try_next(True) + if doc is not None: + return doc + + raise StopIteration + + __next__ = next + + def _try_next(self, get_more_allowed): + """Advance the cursor blocking for at most one getMore command.""" + if not len(self.__data) and not self.__killed and get_more_allowed: + self._refresh() + if len(self.__data): + coll = self.__collection + return coll.database._fix_outgoing(self.__data.popleft(), coll) + else: + return None + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + +class RawBatchCommandCursor(CommandCursor): + _getmore_class = _RawBatchGetMore + + def __init__(self, collection, cursor_info, address, retrieved=0, + batch_size=0, max_await_time_ms=None, session=None, + explicit_session=False): + """Create a new cursor / iterator over raw batches of BSON data. + + Should not be called directly by application developers - + see :meth:`~pymongo.collection.Collection.aggregate_raw_batches` + instead. + + .. mongodoc:: cursors + """ + assert not cursor_info.get('firstBatch') + super(RawBatchCommandCursor, self).__init__( + collection, cursor_info, address, retrieved, batch_size, + max_await_time_ms, session, explicit_session) + + def _unpack_response(self, response, cursor_id, codec_options, + user_fields=None, legacy_response=False): + return response.raw_response(cursor_id) + + def __getitem__(self, index): + raise InvalidOperation("Cannot call __getitem__ on RawBatchCursor") diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/common.py b/backend/venv/lib/python3.7/site-packages/pymongo/common.py new file mode 100644 index 000000000..291c8b4dc --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/common.py @@ -0,0 +1,733 @@ +# Copyright 2011-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +"""Functions and classes common to multiple pymongo modules.""" + +import datetime +import warnings + +from bson import SON +from bson.binary import (STANDARD, PYTHON_LEGACY, + JAVA_LEGACY, CSHARP_LEGACY) +from bson.codec_options import CodecOptions, TypeRegistry +from bson.py3compat import abc, integer_types, iteritems, string_type +from bson.raw_bson import RawBSONDocument +from pymongo.auth import MECHANISMS +from pymongo.compression_support import (validate_compressors, + validate_zlib_compression_level) +from pymongo.driver_info import DriverInfo +from pymongo.errors import ConfigurationError +from pymongo.monitoring import _validate_event_listeners +from pymongo.read_concern import ReadConcern +from pymongo.read_preferences import _MONGOS_MODES, _ServerMode +from pymongo.ssl_support import validate_cert_reqs +from pymongo.write_concern import DEFAULT_WRITE_CONCERN, WriteConcern + +try: + from collections import OrderedDict + ORDERED_TYPES = (SON, OrderedDict) +except ImportError: + ORDERED_TYPES = (SON,) + + +# Defaults until we connect to a server and get updated limits. +MAX_BSON_SIZE = 16 * (1024 ** 2) +MAX_MESSAGE_SIZE = 2 * MAX_BSON_SIZE +MIN_WIRE_VERSION = 0 +MAX_WIRE_VERSION = 0 +MAX_WRITE_BATCH_SIZE = 1000 + +# What this version of PyMongo supports. +MIN_SUPPORTED_SERVER_VERSION = "2.6" +MIN_SUPPORTED_WIRE_VERSION = 2 +MAX_SUPPORTED_WIRE_VERSION = 5 + +# Frequency to call ismaster on servers, in seconds. +HEARTBEAT_FREQUENCY = 10 + +# Frequency to process kill-cursors, in seconds. See MongoClient.close_cursor. +KILL_CURSOR_FREQUENCY = 1 + +# Frequency to process events queue, in seconds. +EVENTS_QUEUE_FREQUENCY = 1 + +# How long to wait, in seconds, for a suitable server to be found before +# aborting an operation. For example, if the client attempts an insert +# during a replica set election, SERVER_SELECTION_TIMEOUT governs the +# longest it is willing to wait for a new primary to be found. +SERVER_SELECTION_TIMEOUT = 30 + +# Spec requires at least 500ms between ismaster calls. +MIN_HEARTBEAT_INTERVAL = 0.5 + +# Default connectTimeout in seconds. +CONNECT_TIMEOUT = 20.0 + +# Default value for maxPoolSize. +MAX_POOL_SIZE = 100 + +# Default value for minPoolSize. +MIN_POOL_SIZE = 0 + +# Default value for maxIdleTimeMS. +MAX_IDLE_TIME_MS = None + +# Default value for localThresholdMS. +LOCAL_THRESHOLD_MS = 15 + +# Default value for retryWrites. +RETRY_WRITES = False + +# mongod/s 2.6 and above return code 59 when a command doesn't exist. +COMMAND_NOT_FOUND_CODES = (59,) + +# Error codes to ignore if GridFS calls createIndex on a secondary +UNAUTHORIZED_CODES = (13, 16547, 16548) + +# Maximum number of sessions to send in a single endSessions command. +# From the driver sessions spec. +_MAX_END_SESSIONS = 10000 + + +def partition_node(node): + """Split a host:port string into (host, int(port)) pair.""" + host = node + port = 27017 + idx = node.rfind(':') + if idx != -1: + host, port = node[:idx], int(node[idx + 1:]) + if host.startswith('['): + host = host[1:-1] + return host, port + + +def clean_node(node): + """Split and normalize a node name from an ismaster response.""" + host, port = partition_node(node) + + # Normalize hostname to lowercase, since DNS is case-insensitive: + # http://tools.ietf.org/html/rfc4343 + # This prevents useless rediscovery if "foo.com" is in the seed list but + # "FOO.com" is in the ismaster response. + return host.lower(), port + + +def raise_config_error(key, dummy): + """Raise ConfigurationError with the given key name.""" + raise ConfigurationError("Unknown option %s" % (key,)) + + +# Mapping of URI uuid representation options to valid subtypes. +_UUID_REPRESENTATIONS = { + 'standard': STANDARD, + 'pythonLegacy': PYTHON_LEGACY, + 'javaLegacy': JAVA_LEGACY, + 'csharpLegacy': CSHARP_LEGACY +} + + +def validate_boolean(option, value): + """Validates that 'value' is True or False.""" + if isinstance(value, bool): + return value + raise TypeError("%s must be True or False" % (option,)) + + +def validate_boolean_or_string(option, value): + """Validates that value is True, False, 'true', or 'false'.""" + if isinstance(value, string_type): + if value not in ('true', 'false'): + raise ValueError("The value of %s must be " + "'true' or 'false'" % (option,)) + return value == 'true' + return validate_boolean(option, value) + + +def validate_integer(option, value): + """Validates that 'value' is an integer (or basestring representation). + """ + if isinstance(value, integer_types): + return value + elif isinstance(value, string_type): + try: + return int(value) + except ValueError: + raise ValueError("The value of %s must be " + "an integer" % (option,)) + raise TypeError("Wrong type for %s, value must be an integer" % (option,)) + + +def validate_positive_integer(option, value): + """Validate that 'value' is a positive integer, which does not include 0. + """ + val = validate_integer(option, value) + if val <= 0: + raise ValueError("The value of %s must be " + "a positive integer" % (option,)) + return val + + +def validate_non_negative_integer(option, value): + """Validate that 'value' is a positive integer or 0. + """ + val = validate_integer(option, value) + if val < 0: + raise ValueError("The value of %s must be " + "a non negative integer" % (option,)) + return val + + +def validate_readable(option, value): + """Validates that 'value' is file-like and readable. + """ + if value is None: + return value + # First make sure its a string py3.3 open(True, 'r') succeeds + # Used in ssl cert checking due to poor ssl module error reporting + value = validate_string(option, value) + open(value, 'r').close() + return value + + +def validate_positive_integer_or_none(option, value): + """Validate that 'value' is a positive integer or None. + """ + if value is None: + return value + return validate_positive_integer(option, value) + + +def validate_non_negative_integer_or_none(option, value): + """Validate that 'value' is a positive integer or 0 or None. + """ + if value is None: + return value + return validate_non_negative_integer(option, value) + + +def validate_string(option, value): + """Validates that 'value' is an instance of `basestring` for Python 2 + or `str` for Python 3. + """ + if isinstance(value, string_type): + return value + raise TypeError("Wrong type for %s, value must be " + "an instance of %s" % (option, string_type.__name__)) + + +def validate_string_or_none(option, value): + """Validates that 'value' is an instance of `basestring` or `None`. + """ + if value is None: + return value + return validate_string(option, value) + + +def validate_int_or_basestring(option, value): + """Validates that 'value' is an integer or string. + """ + if isinstance(value, integer_types): + return value + elif isinstance(value, string_type): + try: + return int(value) + except ValueError: + return value + raise TypeError("Wrong type for %s, value must be an " + "integer or a string" % (option,)) + + +def validate_non_negative_int_or_basestring(option, value): + """Validates that 'value' is an integer or string. + """ + if isinstance(value, integer_types): + return value + elif isinstance(value, string_type): + try: + val = int(value) + except ValueError: + return value + return validate_non_negative_integer(option, val) + raise TypeError("Wrong type for %s, value must be an " + "non negative integer or a string" % (option,)) + + +def validate_positive_float(option, value): + """Validates that 'value' is a float, or can be converted to one, and is + positive. + """ + errmsg = "%s must be an integer or float" % (option,) + try: + value = float(value) + except ValueError: + raise ValueError(errmsg) + except TypeError: + raise TypeError(errmsg) + + # float('inf') doesn't work in 2.4 or 2.5 on Windows, so just cap floats at + # one billion - this is a reasonable approximation for infinity + if not 0 < value < 1e9: + raise ValueError("%s must be greater than 0 and " + "less than one billion" % (option,)) + return value + + +def validate_positive_float_or_zero(option, value): + """Validates that 'value' is 0 or a positive float, or can be converted to + 0 or a positive float. + """ + if value == 0 or value == "0": + return 0 + return validate_positive_float(option, value) + + +def validate_timeout_or_none(option, value): + """Validates a timeout specified in milliseconds returning + a value in floating point seconds. + """ + if value is None: + return value + return validate_positive_float(option, value) / 1000.0 + + +def validate_timeout_or_zero(option, value): + """Validates a timeout specified in milliseconds returning + a value in floating point seconds for the case where None is an error + and 0 is valid. Setting the timeout to nothing in the URI string is a + config error. + """ + if value is None: + raise ConfigurationError("%s cannot be None" % (option, )) + if value == 0 or value == "0": + return 0 + return validate_positive_float(option, value) / 1000.0 + + +def validate_max_staleness(option, value): + """Validates maxStalenessSeconds according to the Max Staleness Spec.""" + if value == -1 or value == "-1": + # Default: No maximum staleness. + return -1 + return validate_positive_integer(option, value) + + +def validate_read_preference(dummy, value): + """Validate a read preference. + """ + if not isinstance(value, _ServerMode): + raise TypeError("%r is not a read preference." % (value,)) + return value + + +def validate_read_preference_mode(dummy, value): + """Validate read preference mode for a MongoReplicaSetClient. + + .. versionchanged:: 3.5 + Returns the original ``value`` instead of the validated read preference + mode. + """ + if value not in _MONGOS_MODES: + raise ValueError("%s is not a valid read preference" % (value,)) + return value + + +def validate_auth_mechanism(option, value): + """Validate the authMechanism URI option. + """ + # CRAM-MD5 is for server testing only. Undocumented, + # unsupported, may be removed at any time. You have + # been warned. + if value not in MECHANISMS and value != 'CRAM-MD5': + raise ValueError("%s must be in %s" % (option, tuple(MECHANISMS))) + return value + + +def validate_uuid_representation(dummy, value): + """Validate the uuid representation option selected in the URI. + """ + try: + return _UUID_REPRESENTATIONS[value] + except KeyError: + raise ValueError("%s is an invalid UUID representation. " + "Must be one of " + "%s" % (value, tuple(_UUID_REPRESENTATIONS))) + + +def validate_read_preference_tags(name, value): + """Parse readPreferenceTags if passed as a client kwarg. + """ + if not isinstance(value, list): + value = [value] + + tag_sets = [] + for tag_set in value: + if tag_set == '': + tag_sets.append({}) + continue + try: + tag_sets.append(dict([tag.split(":") + for tag in tag_set.split(",")])) + except Exception: + raise ValueError("%r not a valid " + "value for %s" % (tag_set, name)) + return tag_sets + + +_MECHANISM_PROPS = frozenset(['SERVICE_NAME', + 'CANONICALIZE_HOST_NAME', + 'SERVICE_REALM']) + + +def validate_auth_mechanism_properties(option, value): + """Validate authMechanismProperties.""" + value = validate_string(option, value) + props = {} + for opt in value.split(','): + try: + key, val = opt.split(':') + except ValueError: + raise ValueError("auth mechanism properties must be " + "key:value pairs like SERVICE_NAME:" + "mongodb, not %s." % (opt,)) + if key not in _MECHANISM_PROPS: + raise ValueError("%s is not a supported auth " + "mechanism property. Must be one of " + "%s." % (key, tuple(_MECHANISM_PROPS))) + if key == 'CANONICALIZE_HOST_NAME': + props[key] = validate_boolean_or_string(key, val) + else: + props[key] = val + + return props + + +def validate_document_class(option, value): + """Validate the document_class option.""" + if not issubclass(value, (abc.MutableMapping, RawBSONDocument)): + raise TypeError("%s must be dict, bson.son.SON, " + "bson.raw_bson.RawBSONDocument, or a " + "sublass of collections.MutableMapping" % (option,)) + return value + + +def validate_type_registry(option, value): + """Validate the type_registry option.""" + if value is not None and not isinstance(value, TypeRegistry): + raise TypeError("%s must be an instance of %s" % ( + option, TypeRegistry)) + return value + + +def validate_list(option, value): + """Validates that 'value' is a list.""" + if not isinstance(value, list): + raise TypeError("%s must be a list" % (option,)) + return value + + +def validate_list_or_none(option, value): + """Validates that 'value' is a list or None.""" + if value is None: + return value + return validate_list(option, value) + + +def validate_is_mapping(option, value): + """Validate the type of method arguments that expect a document.""" + if not isinstance(value, abc.Mapping): + raise TypeError("%s must be an instance of dict, bson.son.SON, or " + "other type that inherits from " + "collections.Mapping" % (option,)) + + +def validate_is_document_type(option, value): + """Validate the type of method arguments that expect a MongoDB document.""" + if not isinstance(value, (abc.MutableMapping, RawBSONDocument)): + raise TypeError("%s must be an instance of dict, bson.son.SON, " + "bson.raw_bson.RawBSONDocument, or " + "a type that inherits from " + "collections.MutableMapping" % (option,)) + + +def validate_appname_or_none(option, value): + """Validate the appname option.""" + if value is None: + return value + validate_string(option, value) + # We need length in bytes, so encode utf8 first. + if len(value.encode('utf-8')) > 128: + raise ValueError("%s must be <= 128 bytes" % (option,)) + return value + + +def validate_driver_or_none(option, value): + """Validate the driver keyword arg.""" + if value is None: + return value + if not isinstance(value, DriverInfo): + raise TypeError("%s must be an instance of DriverInfo" % (option,)) + return value + + +def validate_is_callable_or_none(option, value): + """Validates that 'value' is a callable.""" + if value is None: + return value + if not callable(value): + raise ValueError("%s must be a callable" % (option,)) + return value + + +def validate_ok_for_replace(replacement): + """Validate a replacement document.""" + validate_is_mapping("replacement", replacement) + # Replacement can be {} + if replacement and not isinstance(replacement, RawBSONDocument): + first = next(iter(replacement)) + if first.startswith('$'): + raise ValueError('replacement can not include $ operators') + + +def validate_ok_for_update(update): + """Validate an update document.""" + validate_is_mapping("update", update) + # Update can not be {} + if not update: + raise ValueError('update only works with $ operators') + first = next(iter(update)) + if not first.startswith('$'): + raise ValueError('update only works with $ operators') + + +_UNICODE_DECODE_ERROR_HANDLERS = frozenset(['strict', 'replace', 'ignore']) + + +def validate_unicode_decode_error_handler(dummy, value): + """Validate the Unicode decode error handler option of CodecOptions. + """ + if value not in _UNICODE_DECODE_ERROR_HANDLERS: + raise ValueError("%s is an invalid Unicode decode error handler. " + "Must be one of " + "%s" % (value, tuple(_UNICODE_DECODE_ERROR_HANDLERS))) + return value + + +def validate_tzinfo(dummy, value): + """Validate the tzinfo option + """ + if value is not None and not isinstance(value, datetime.tzinfo): + raise TypeError("%s must be an instance of datetime.tzinfo" % value) + return value + + +# journal is an alias for j, +# wtimeoutms is an alias for wtimeout, +URI_VALIDATORS = { + 'replicaset': validate_string_or_none, + 'w': validate_non_negative_int_or_basestring, + 'wtimeout': validate_non_negative_integer, + 'wtimeoutms': validate_non_negative_integer, + 'fsync': validate_boolean_or_string, + 'j': validate_boolean_or_string, + 'journal': validate_boolean_or_string, + 'maxpoolsize': validate_positive_integer_or_none, + 'socketkeepalive': validate_boolean_or_string, + 'waitqueuemultiple': validate_non_negative_integer_or_none, + 'ssl': validate_boolean_or_string, + 'ssl_keyfile': validate_readable, + 'ssl_certfile': validate_readable, + 'ssl_pem_passphrase': validate_string_or_none, + 'ssl_cert_reqs': validate_cert_reqs, + 'ssl_ca_certs': validate_readable, + 'ssl_match_hostname': validate_boolean_or_string, + 'ssl_crlfile': validate_readable, + 'readconcernlevel': validate_string_or_none, + 'readpreference': validate_read_preference_mode, + 'readpreferencetags': validate_read_preference_tags, + 'localthresholdms': validate_positive_float_or_zero, + 'authmechanism': validate_auth_mechanism, + 'authsource': validate_string, + 'authmechanismproperties': validate_auth_mechanism_properties, + 'tz_aware': validate_boolean_or_string, + 'uuidrepresentation': validate_uuid_representation, + 'connect': validate_boolean_or_string, + 'minpoolsize': validate_non_negative_integer, + 'appname': validate_appname_or_none, + 'driver': validate_driver_or_none, + 'unicode_decode_error_handler': validate_unicode_decode_error_handler, + 'retrywrites': validate_boolean_or_string, + 'compressors': validate_compressors, + 'zlibcompressionlevel': validate_zlib_compression_level, +} + +TIMEOUT_VALIDATORS = { + 'connecttimeoutms': validate_timeout_or_none, + 'sockettimeoutms': validate_timeout_or_none, + 'waitqueuetimeoutms': validate_timeout_or_none, + 'serverselectiontimeoutms': validate_timeout_or_zero, + 'heartbeatfrequencyms': validate_timeout_or_none, + 'maxidletimems': validate_timeout_or_none, + 'maxstalenessseconds': validate_max_staleness, +} + +KW_VALIDATORS = { + 'document_class': validate_document_class, + 'type_registry': validate_type_registry, + 'read_preference': validate_read_preference, + 'event_listeners': _validate_event_listeners, + 'tzinfo': validate_tzinfo, + 'username': validate_string_or_none, + 'password': validate_string_or_none, + 'server_selector': validate_is_callable_or_none, +} + +URI_VALIDATORS.update(TIMEOUT_VALIDATORS) +VALIDATORS = URI_VALIDATORS.copy() +VALIDATORS.update(KW_VALIDATORS) + + +_AUTH_OPTIONS = frozenset(['authmechanismproperties']) + + +def validate_auth_option(option, value): + """Validate optional authentication parameters. + """ + lower, value = validate(option, value) + if lower not in _AUTH_OPTIONS: + raise ConfigurationError('Unknown ' + 'authentication option: %s' % (option,)) + return lower, value + + +def validate(option, value): + """Generic validation function. + """ + lower = option.lower() + validator = VALIDATORS.get(lower, raise_config_error) + value = validator(option, value) + return lower, value + + +def get_validated_options(options, warn=True): + """Validate each entry in options and raise a warning if it is not valid. + Returns a copy of options with invalid entries removed + """ + validated_options = {} + for opt, value in iteritems(options): + lower = opt.lower() + try: + validator = URI_VALIDATORS.get(lower, raise_config_error) + value = validator(opt, value) + except (ValueError, ConfigurationError) as exc: + if warn: + warnings.warn(str(exc)) + else: + raise + else: + validated_options[lower] = value + return validated_options + + +WRITE_CONCERN_OPTIONS = frozenset([ + 'w', + 'wtimeout', + 'wtimeoutms', + 'fsync', + 'j', + 'journal' +]) + + +class BaseObject(object): + """A base class that provides attributes and methods common + to multiple pymongo classes. + + SHOULD NOT BE USED BY DEVELOPERS EXTERNAL TO MONGODB. + """ + + def __init__(self, codec_options, read_preference, write_concern, + read_concern): + + if not isinstance(codec_options, CodecOptions): + raise TypeError("codec_options must be an instance of " + "bson.codec_options.CodecOptions") + self.__codec_options = codec_options + + if not isinstance(read_preference, _ServerMode): + raise TypeError("%r is not valid for read_preference. See " + "pymongo.read_preferences for valid " + "options." % (read_preference,)) + self.__read_preference = read_preference + + if not isinstance(write_concern, WriteConcern): + raise TypeError("write_concern must be an instance of " + "pymongo.write_concern.WriteConcern") + self.__write_concern = write_concern + + if not isinstance(read_concern, ReadConcern): + raise TypeError("read_concern must be an instance of " + "pymongo.read_concern.ReadConcern") + self.__read_concern = read_concern + + @property + def codec_options(self): + """Read only access to the :class:`~bson.codec_options.CodecOptions` + of this instance. + """ + return self.__codec_options + + @property + def write_concern(self): + """Read only access to the :class:`~pymongo.write_concern.WriteConcern` + of this instance. + + .. versionchanged:: 3.0 + The :attr:`write_concern` attribute is now read only. + """ + return self.__write_concern + + def _write_concern_for(self, session): + """Read only access to the write concern of this instance or session. + """ + # Override this operation's write concern with the transaction's. + if session and session._in_transaction: + return DEFAULT_WRITE_CONCERN + return self.write_concern + + @property + def read_preference(self): + """Read only access to the read preference of this instance. + + .. versionchanged:: 3.0 + The :attr:`read_preference` attribute is now read only. + """ + return self.__read_preference + + def _read_preference_for(self, session): + """Read only access to the read preference of this instance or session. + """ + # Override this operation's read preference with the transaction's. + if session: + return session._txn_read_preference() or self.__read_preference + return self.__read_preference + + @property + def read_concern(self): + """Read only access to the :class:`~pymongo.read_concern.ReadConcern` + of this instance. + + .. versionadded:: 3.2 + """ + return self.__read_concern diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/compression_support.py b/backend/venv/lib/python3.7/site-packages/pymongo/compression_support.py new file mode 100644 index 000000000..a4987df08 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/compression_support.py @@ -0,0 +1,124 @@ +# Copyright 2018 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import warnings + +try: + import snappy + _HAVE_SNAPPY = True +except ImportError: + # python-snappy isn't available. + _HAVE_SNAPPY = False + +try: + import zlib + _HAVE_ZLIB = True +except ImportError: + # Python built without zlib support. + _HAVE_ZLIB = False + +from pymongo.monitoring import _SENSITIVE_COMMANDS + +_SUPPORTED_COMPRESSORS = set(["snappy", "zlib"]) +_NO_COMPRESSION = set(['ismaster']) +_NO_COMPRESSION.update(_SENSITIVE_COMMANDS) + + +def validate_compressors(dummy, value): + compressors = value.split(",") + for compressor in compressors[:]: + if compressor not in _SUPPORTED_COMPRESSORS: + compressors.remove(compressor) + warnings.warn("Unsupported compressor: %s" % (compressor,)) + elif compressor == "snappy" and not _HAVE_SNAPPY: + compressors.remove(compressor) + warnings.warn( + "Wire protocol compression with snappy is not available. " + "You must install the python-snappy module for snappy support.") + elif compressor == "zlib" and not _HAVE_ZLIB: + compressors.remove(compressor) + warnings.warn( + "Wire protocol compression with zlib is not available. " + "The zlib module is not available.") + return compressors + + +def validate_zlib_compression_level(option, value): + try: + level = int(value) + except: + raise TypeError("%s must be an integer, not %r." % (option, value)) + if level < -1 or level > 9: + raise ValueError( + "%s must be between -1 and 9, not %d." % (option, level)) + return level + + +class CompressionSettings(object): + def __init__(self, compressors, zlib_compression_level): + self.compressors = compressors + self.zlib_compression_level = zlib_compression_level + + def get_compression_context(self, compressors): + if compressors: + chosen = compressors[0] + if chosen == "snappy": + return SnappyContext() + elif chosen == "zlib": + return ZlibContext(self.zlib_compression_level) + + +def _zlib_no_compress(data): + """Compress data with zlib level 0.""" + cobj = zlib.compressobj(0) + return b"".join([cobj.compress(data), cobj.flush()]) + + +class SnappyContext(object): + compressor_id = 1 + + @staticmethod + def compress(data): + return snappy.compress(data) + + +class ZlibContext(object): + compressor_id = 2 + + def __init__(self, level): + # Jython zlib.compress doesn't support -1 + if level == -1: + self.compress = zlib.compress + # Jython zlib.compress also doesn't support 0 + elif level == 0: + self.compress = _zlib_no_compress + else: + self.compress = lambda data: zlib.compress(data, level) + + +def decompress(data, compressor_id): + if compressor_id == SnappyContext.compressor_id: + # python-snappy doesn't support the buffer interface. + # https://github.com/andrix/python-snappy/issues/65 + # This only matters when data is a memoryview since + # id(bytes(data)) == id(data) when data is a bytes. + # NOTE: bytes(memoryview) returns the memoryview repr + # in Python 2.7. The right thing to do in 2.7 is call + # memoryview.tobytes(), but we currently only use + # memoryview in Python 3.x. + return snappy.uncompress(bytes(data)) + elif compressor_id == ZlibContext.compressor_id: + return zlib.decompress(data) + else: + raise ValueError("Unknown compressorId %d" % (compressor_id,)) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/cursor.py b/backend/venv/lib/python3.7/site-packages/pymongo/cursor.py new file mode 100644 index 000000000..5f804d7b2 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/cursor.py @@ -0,0 +1,1327 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Cursor class to iterate over Mongo query results.""" + +import copy +import datetime +import warnings + +from collections import deque + +from bson import RE_TYPE +from bson.code import Code +from bson.py3compat import (iteritems, + integer_types, + string_type) +from bson.son import SON +from pymongo import helpers +from pymongo.common import validate_boolean, validate_is_mapping +from pymongo.collation import validate_collation_or_none +from pymongo.errors import (AutoReconnect, + ConnectionFailure, + InvalidOperation, + NotMasterError, + OperationFailure) +from pymongo.message import (_convert_exception, + _CursorAddress, + _GetMore, + _RawBatchGetMore, + _Query, + _RawBatchQuery) + + +_QUERY_OPTIONS = { + "tailable_cursor": 2, + "slave_okay": 4, + "oplog_replay": 8, + "no_timeout": 16, + "await_data": 32, + "exhaust": 64, + "partial": 128} +_CURSOR_DOC_FIELDS = {'cursor': {'firstBatch': 1, 'nextBatch': 1}} + + +class CursorType(object): + NON_TAILABLE = 0 + """The standard cursor type.""" + + TAILABLE = _QUERY_OPTIONS["tailable_cursor"] + """The tailable cursor type. + + Tailable cursors are only for use with capped collections. They are not + closed when the last data is retrieved but are kept open and the cursor + location marks the final document position. If more data is received + iteration of the cursor will continue from the last document received. + """ + + TAILABLE_AWAIT = TAILABLE | _QUERY_OPTIONS["await_data"] + """A tailable cursor with the await option set. + + Creates a tailable cursor that will wait for a few seconds after returning + the full result set so that it can capture and return additional data added + during the query. + """ + + EXHAUST = _QUERY_OPTIONS["exhaust"] + """An exhaust cursor. + + MongoDB will stream batched results to the client without waiting for the + client to request each batch, reducing latency. + """ + + +# This has to be an old style class due to +# http://bugs.jython.org/issue1057 +class _SocketManager: + """Used with exhaust cursors to ensure the socket is returned. + """ + def __init__(self, sock, pool): + self.sock = sock + self.pool = pool + self.__closed = False + + def __del__(self): + self.close() + + def close(self): + """Return this instance's socket to the connection pool. + """ + if not self.__closed: + self.__closed = True + self.pool.return_socket(self.sock) + self.sock, self.pool = None, None + + +class Cursor(object): + """A cursor / iterator over Mongo query results. + """ + _query_class = _Query + _getmore_class = _GetMore + + def __init__(self, collection, filter=None, projection=None, skip=0, + limit=0, no_cursor_timeout=False, + cursor_type=CursorType.NON_TAILABLE, + sort=None, allow_partial_results=False, oplog_replay=False, + modifiers=None, batch_size=0, manipulate=True, + collation=None, hint=None, max_scan=None, max_time_ms=None, + max=None, min=None, return_key=False, show_record_id=False, + snapshot=False, comment=None, session=None): + """Create a new cursor. + + Should not be called directly by application developers - see + :meth:`~pymongo.collection.Collection.find` instead. + + .. mongodoc:: cursors + """ + # Initialize all attributes used in __del__ before possibly raising + # an error to avoid attribute errors during garbage collection. + self.__id = None + self.__exhaust = False + self.__exhaust_mgr = None + self.__killed = False + + if session: + self.__session = session + self.__explicit_session = True + else: + self.__session = None + self.__explicit_session = False + + spec = filter + if spec is None: + spec = {} + + validate_is_mapping("filter", spec) + if not isinstance(skip, int): + raise TypeError("skip must be an instance of int") + if not isinstance(limit, int): + raise TypeError("limit must be an instance of int") + validate_boolean("no_cursor_timeout", no_cursor_timeout) + if cursor_type not in (CursorType.NON_TAILABLE, CursorType.TAILABLE, + CursorType.TAILABLE_AWAIT, CursorType.EXHAUST): + raise ValueError("not a valid value for cursor_type") + validate_boolean("allow_partial_results", allow_partial_results) + validate_boolean("oplog_replay", oplog_replay) + if modifiers is not None: + warnings.warn("the 'modifiers' parameter is deprecated", + DeprecationWarning, stacklevel=2) + validate_is_mapping("modifiers", modifiers) + if not isinstance(batch_size, integer_types): + raise TypeError("batch_size must be an integer") + if batch_size < 0: + raise ValueError("batch_size must be >= 0") + + if projection is not None: + if not projection: + projection = {"_id": 1} + projection = helpers._fields_list_to_dict(projection, "projection") + + self.__collection = collection + self.__spec = spec + self.__projection = projection + self.__skip = skip + self.__limit = limit + self.__batch_size = batch_size + self.__modifiers = modifiers and modifiers.copy() or {} + self.__ordering = sort and helpers._index_document(sort) or None + self.__max_scan = max_scan + self.__explain = False + self.__comment = comment + self.__max_time_ms = max_time_ms + self.__max_await_time_ms = None + self.__max = max + self.__min = min + self.__manipulate = manipulate + self.__collation = validate_collation_or_none(collation) + self.__return_key = return_key + self.__show_record_id = show_record_id + self.__snapshot = snapshot + self.__set_hint(hint) + + # Exhaust cursor support + if cursor_type == CursorType.EXHAUST: + if self.__collection.database.client.is_mongos: + raise InvalidOperation('Exhaust cursors are ' + 'not supported by mongos') + if limit: + raise InvalidOperation("Can't use limit and exhaust together.") + self.__exhaust = True + + # This is ugly. People want to be able to do cursor[5:5] and + # get an empty result set (old behavior was an + # exception). It's hard to do that right, though, because the + # server uses limit(0) to mean 'no limit'. So we set __empty + # in that case and check for it when iterating. We also unset + # it anytime we change __limit. + self.__empty = False + + self.__data = deque() + self.__address = None + self.__retrieved = 0 + + self.__codec_options = collection.codec_options + # Read preference is set when the initial find is sent. + self.__read_preference = None + self.__read_concern = collection.read_concern + + self.__query_flags = cursor_type + if no_cursor_timeout: + self.__query_flags |= _QUERY_OPTIONS["no_timeout"] + if allow_partial_results: + self.__query_flags |= _QUERY_OPTIONS["partial"] + if oplog_replay: + self.__query_flags |= _QUERY_OPTIONS["oplog_replay"] + + @property + def collection(self): + """The :class:`~pymongo.collection.Collection` that this + :class:`Cursor` is iterating. + """ + return self.__collection + + @property + def retrieved(self): + """The number of documents retrieved so far. + """ + return self.__retrieved + + def __del__(self): + self.__die() + + def rewind(self): + """Rewind this cursor to its unevaluated state. + + Reset this cursor if it has been partially or completely evaluated. + Any options that are present on the cursor will remain in effect. + Future iterating performed on this cursor will cause new queries to + be sent to the server, even if the resultant data has already been + retrieved by this cursor. + """ + self.__data = deque() + self.__id = None + self.__address = None + self.__retrieved = 0 + self.__killed = False + + return self + + def clone(self): + """Get a clone of this cursor. + + Returns a new Cursor instance with options matching those that have + been set on the current instance. The clone will be completely + unevaluated, even if the current instance has been partially or + completely evaluated. + """ + return self._clone(True) + + def _clone(self, deepcopy=True, base=None): + """Internal clone helper.""" + if not base: + if self.__explicit_session: + base = self._clone_base(self.__session) + else: + base = self._clone_base(None) + + values_to_clone = ("spec", "projection", "skip", "limit", + "max_time_ms", "max_await_time_ms", "comment", + "max", "min", "ordering", "explain", "hint", + "batch_size", "max_scan", "manipulate", + "query_flags", "modifiers", "collation") + data = dict((k, v) for k, v in iteritems(self.__dict__) + if k.startswith('_Cursor__') and k[9:] in values_to_clone) + if deepcopy: + data = self._deepcopy(data) + base.__dict__.update(data) + return base + + def _clone_base(self, session): + """Creates an empty Cursor object for information to be copied into. + """ + return self.__class__(self.__collection, session=session) + + def __die(self, synchronous=False): + """Closes this cursor. + """ + already_killed = self.__killed + self.__killed = True + if self.__id and not already_killed: + if self.__exhaust and self.__exhaust_mgr: + # If this is an exhaust cursor and we haven't completely + # exhausted the result set we *must* close the socket + # to stop the server from sending more data. + self.__exhaust_mgr.sock.close() + else: + address = _CursorAddress( + self.__address, self.__collection.full_name) + if synchronous: + self.__collection.database.client._close_cursor_now( + self.__id, address, session=self.__session) + else: + # The cursor will be closed later in a different session. + self.__collection.database.client._close_cursor( + self.__id, address) + if self.__exhaust and self.__exhaust_mgr: + self.__exhaust_mgr.close() + if self.__session and not self.__explicit_session: + self.__session._end_session(lock=synchronous) + self.__session = None + + def close(self): + """Explicitly close / kill this cursor. + """ + self.__die(True) + + def __query_spec(self): + """Get the spec to use for a query. + """ + operators = self.__modifiers.copy() + if self.__ordering: + operators["$orderby"] = self.__ordering + if self.__explain: + operators["$explain"] = True + if self.__hint: + operators["$hint"] = self.__hint + if self.__comment: + operators["$comment"] = self.__comment + if self.__max_scan: + operators["$maxScan"] = self.__max_scan + if self.__max_time_ms is not None: + operators["$maxTimeMS"] = self.__max_time_ms + if self.__max: + operators["$max"] = self.__max + if self.__min: + operators["$min"] = self.__min + if self.__return_key: + operators["$returnKey"] = self.__return_key + if self.__show_record_id: + # This is upgraded to showRecordId for MongoDB 3.2+ "find" command. + operators["$showDiskLoc"] = self.__show_record_id + if self.__snapshot: + operators["$snapshot"] = self.__snapshot + + if operators: + # Make a shallow copy so we can cleanly rewind or clone. + spec = self.__spec.copy() + + # White-listed commands must be wrapped in $query. + if "$query" not in spec: + # $query has to come first + spec = SON([("$query", spec)]) + + if not isinstance(spec, SON): + # Ensure the spec is SON. As order is important this will + # ensure its set before merging in any extra operators. + spec = SON(spec) + + spec.update(operators) + return spec + # Have to wrap with $query if "query" is the first key. + # We can't just use $query anytime "query" is a key as + # that breaks commands like count and find_and_modify. + # Checking spec.keys()[0] covers the case that the spec + # was passed as an instance of SON or OrderedDict. + elif ("query" in self.__spec and + (len(self.__spec) == 1 or + next(iter(self.__spec)) == "query")): + return SON({"$query": self.__spec}) + + return self.__spec + + def __check_okay_to_chain(self): + """Check if it is okay to chain more options onto this cursor. + """ + if self.__retrieved or self.__id is not None: + raise InvalidOperation("cannot set options after executing query") + + def add_option(self, mask): + """Set arbitrary query flags using a bitmask. + + To set the tailable flag: + cursor.add_option(2) + """ + if not isinstance(mask, int): + raise TypeError("mask must be an int") + self.__check_okay_to_chain() + + if mask & _QUERY_OPTIONS["exhaust"]: + if self.__limit: + raise InvalidOperation("Can't use limit and exhaust together.") + if self.__collection.database.client.is_mongos: + raise InvalidOperation('Exhaust cursors are ' + 'not supported by mongos') + self.__exhaust = True + + self.__query_flags |= mask + return self + + def remove_option(self, mask): + """Unset arbitrary query flags using a bitmask. + + To unset the tailable flag: + cursor.remove_option(2) + """ + if not isinstance(mask, int): + raise TypeError("mask must be an int") + self.__check_okay_to_chain() + + if mask & _QUERY_OPTIONS["exhaust"]: + self.__exhaust = False + + self.__query_flags &= ~mask + return self + + def limit(self, limit): + """Limits the number of results to be returned by this cursor. + + Raises :exc:`TypeError` if `limit` is not an integer. Raises + :exc:`~pymongo.errors.InvalidOperation` if this :class:`Cursor` + has already been used. The last `limit` applied to this cursor + takes precedence. A limit of ``0`` is equivalent to no limit. + + :Parameters: + - `limit`: the number of results to return + + .. mongodoc:: limit + """ + if not isinstance(limit, integer_types): + raise TypeError("limit must be an integer") + if self.__exhaust: + raise InvalidOperation("Can't use limit and exhaust together.") + self.__check_okay_to_chain() + + self.__empty = False + self.__limit = limit + return self + + def batch_size(self, batch_size): + """Limits the number of documents returned in one batch. Each batch + requires a round trip to the server. It can be adjusted to optimize + performance and limit data transfer. + + .. note:: batch_size can not override MongoDB's internal limits on the + amount of data it will return to the client in a single batch (i.e + if you set batch size to 1,000,000,000, MongoDB will currently only + return 4-16MB of results per batch). + + Raises :exc:`TypeError` if `batch_size` is not an integer. + Raises :exc:`ValueError` if `batch_size` is less than ``0``. + Raises :exc:`~pymongo.errors.InvalidOperation` if this + :class:`Cursor` has already been used. The last `batch_size` + applied to this cursor takes precedence. + + :Parameters: + - `batch_size`: The size of each batch of results requested. + """ + if not isinstance(batch_size, integer_types): + raise TypeError("batch_size must be an integer") + if batch_size < 0: + raise ValueError("batch_size must be >= 0") + self.__check_okay_to_chain() + + self.__batch_size = batch_size + return self + + def skip(self, skip): + """Skips the first `skip` results of this cursor. + + Raises :exc:`TypeError` if `skip` is not an integer. Raises + :exc:`ValueError` if `skip` is less than ``0``. Raises + :exc:`~pymongo.errors.InvalidOperation` if this :class:`Cursor` has + already been used. The last `skip` applied to this cursor takes + precedence. + + :Parameters: + - `skip`: the number of results to skip + """ + if not isinstance(skip, integer_types): + raise TypeError("skip must be an integer") + if skip < 0: + raise ValueError("skip must be >= 0") + self.__check_okay_to_chain() + + self.__skip = skip + return self + + def max_time_ms(self, max_time_ms): + """Specifies a time limit for a query operation. If the specified + time is exceeded, the operation will be aborted and + :exc:`~pymongo.errors.ExecutionTimeout` is raised. If `max_time_ms` + is ``None`` no limit is applied. + + Raises :exc:`TypeError` if `max_time_ms` is not an integer or ``None``. + Raises :exc:`~pymongo.errors.InvalidOperation` if this :class:`Cursor` + has already been used. + + :Parameters: + - `max_time_ms`: the time limit after which the operation is aborted + """ + if (not isinstance(max_time_ms, integer_types) + and max_time_ms is not None): + raise TypeError("max_time_ms must be an integer or None") + self.__check_okay_to_chain() + + self.__max_time_ms = max_time_ms + return self + + def max_await_time_ms(self, max_await_time_ms): + """Specifies a time limit for a getMore operation on a + :attr:`~pymongo.cursor.CursorType.TAILABLE_AWAIT` cursor. For all other + types of cursor max_await_time_ms is ignored. + + Raises :exc:`TypeError` if `max_await_time_ms` is not an integer or + ``None``. Raises :exc:`~pymongo.errors.InvalidOperation` if this + :class:`Cursor` has already been used. + + .. note:: `max_await_time_ms` requires server version **>= 3.2** + + :Parameters: + - `max_await_time_ms`: the time limit after which the operation is + aborted + + .. versionadded:: 3.2 + """ + if (not isinstance(max_await_time_ms, integer_types) + and max_await_time_ms is not None): + raise TypeError("max_await_time_ms must be an integer or None") + self.__check_okay_to_chain() + + # Ignore max_await_time_ms if not tailable or await_data is False. + if self.__query_flags & CursorType.TAILABLE_AWAIT: + self.__max_await_time_ms = max_await_time_ms + + return self + + def __getitem__(self, index): + """Get a single document or a slice of documents from this cursor. + + Raises :class:`~pymongo.errors.InvalidOperation` if this + cursor has already been used. + + To get a single document use an integral index, e.g.:: + + >>> db.test.find()[50] + + An :class:`IndexError` will be raised if the index is negative + or greater than the amount of documents in this cursor. Any + limit previously applied to this cursor will be ignored. + + To get a slice of documents use a slice index, e.g.:: + + >>> db.test.find()[20:25] + + This will return this cursor with a limit of ``5`` and skip of + ``20`` applied. Using a slice index will override any prior + limits or skips applied to this cursor (including those + applied through previous calls to this method). Raises + :class:`IndexError` when the slice has a step, a negative + start value, or a stop value less than or equal to the start + value. + + :Parameters: + - `index`: An integer or slice index to be applied to this cursor + """ + self.__check_okay_to_chain() + self.__empty = False + if isinstance(index, slice): + if index.step is not None: + raise IndexError("Cursor instances do not support slice steps") + + skip = 0 + if index.start is not None: + if index.start < 0: + raise IndexError("Cursor instances do not support " + "negative indices") + skip = index.start + + if index.stop is not None: + limit = index.stop - skip + if limit < 0: + raise IndexError("stop index must be greater than start " + "index for slice %r" % index) + if limit == 0: + self.__empty = True + else: + limit = 0 + + self.__skip = skip + self.__limit = limit + return self + + if isinstance(index, integer_types): + if index < 0: + raise IndexError("Cursor instances do not support negative " + "indices") + clone = self.clone() + clone.skip(index + self.__skip) + clone.limit(-1) # use a hard limit + clone.__query_flags &= ~CursorType.TAILABLE_AWAIT # PYTHON-1371 + for doc in clone: + return doc + raise IndexError("no such item for Cursor instance") + raise TypeError("index %r cannot be applied to Cursor " + "instances" % index) + + def max_scan(self, max_scan): + """**DEPRECATED** - Limit the number of documents to scan when + performing the query. + + Raises :class:`~pymongo.errors.InvalidOperation` if this + cursor has already been used. Only the last :meth:`max_scan` + applied to this cursor has any effect. + + :Parameters: + - `max_scan`: the maximum number of documents to scan + + .. versionchanged:: 3.7 + Deprecated :meth:`max_scan`. Support for this option is deprecated in + MongoDB 4.0. Use :meth:`max_time_ms` instead to limit server side + execution time. + """ + self.__check_okay_to_chain() + self.__max_scan = max_scan + return self + + def max(self, spec): + """Adds ``max`` operator that specifies upper bound for specific index. + + When using ``max``, :meth:`~hint` should also be configured to ensure + the query uses the expected index and starting in MongoDB 4.2 + :meth:`~hint` will be required. + + :Parameters: + - `spec`: a list of field, limit pairs specifying the exclusive + upper bound for all keys of a specific index in order. + + .. versionchanged:: 3.8 + Deprecated cursors that use ``max`` without a :meth:`~hint`. + + .. versionadded:: 2.7 + """ + if not isinstance(spec, (list, tuple)): + raise TypeError("spec must be an instance of list or tuple") + + self.__check_okay_to_chain() + self.__max = SON(spec) + return self + + def min(self, spec): + """Adds ``min`` operator that specifies lower bound for specific index. + + When using ``min``, :meth:`~hint` should also be configured to ensure + the query uses the expected index and starting in MongoDB 4.2 + :meth:`~hint` will be required. + + :Parameters: + - `spec`: a list of field, limit pairs specifying the inclusive + lower bound for all keys of a specific index in order. + + .. versionchanged:: 3.8 + Deprecated cursors that use ``min`` without a :meth:`~hint`. + + .. versionadded:: 2.7 + """ + if not isinstance(spec, (list, tuple)): + raise TypeError("spec must be an instance of list or tuple") + + self.__check_okay_to_chain() + self.__min = SON(spec) + return self + + def sort(self, key_or_list, direction=None): + """Sorts this cursor's results. + + Pass a field name and a direction, either + :data:`~pymongo.ASCENDING` or :data:`~pymongo.DESCENDING`:: + + for doc in collection.find().sort('field', pymongo.ASCENDING): + print(doc) + + To sort by multiple fields, pass a list of (key, direction) pairs:: + + for doc in collection.find().sort([ + ('field1', pymongo.ASCENDING), + ('field2', pymongo.DESCENDING)]): + print(doc) + + Beginning with MongoDB version 2.6, text search results can be + sorted by relevance:: + + cursor = db.test.find( + {'$text': {'$search': 'some words'}}, + {'score': {'$meta': 'textScore'}}) + + # Sort by 'score' field. + cursor.sort([('score', {'$meta': 'textScore'})]) + + for doc in cursor: + print(doc) + + Raises :class:`~pymongo.errors.InvalidOperation` if this cursor has + already been used. Only the last :meth:`sort` applied to this + cursor has any effect. + + :Parameters: + - `key_or_list`: a single key or a list of (key, direction) + pairs specifying the keys to sort on + - `direction` (optional): only used if `key_or_list` is a single + key, if not given :data:`~pymongo.ASCENDING` is assumed + """ + self.__check_okay_to_chain() + keys = helpers._index_list(key_or_list, direction) + self.__ordering = helpers._index_document(keys) + return self + + def count(self, with_limit_and_skip=False): + """**DEPRECATED** - Get the size of the results set for this query. + + The :meth:`count` method is deprecated and **not** supported in a + transaction. Please use + :meth:`~pymongo.collection.Collection.count_documents` instead. + + Returns the number of documents in the results set for this query. Does + not take :meth:`limit` and :meth:`skip` into account by default - set + `with_limit_and_skip` to ``True`` if that is the desired behavior. + Raises :class:`~pymongo.errors.OperationFailure` on a database error. + + When used with MongoDB >= 2.6, :meth:`~count` uses any :meth:`~hint` + applied to the query. In the following example the hint is passed to + the count command: + + collection.find({'field': 'value'}).hint('field_1').count() + + The :meth:`count` method obeys the + :attr:`~pymongo.collection.Collection.read_preference` of the + :class:`~pymongo.collection.Collection` instance on which + :meth:`~pymongo.collection.Collection.find` was called. + + :Parameters: + - `with_limit_and_skip` (optional): take any :meth:`limit` or + :meth:`skip` that has been applied to this cursor into account when + getting the count + + .. note:: The `with_limit_and_skip` parameter requires server + version **>= 1.1.4-** + + .. versionchanged:: 3.7 + Deprecated. + + .. versionchanged:: 2.8 + The :meth:`~count` method now supports :meth:`~hint`. + """ + warnings.warn("count is deprecated. Use Collection.count_documents " + "instead.", DeprecationWarning, stacklevel=2) + validate_boolean("with_limit_and_skip", with_limit_and_skip) + cmd = SON([("count", self.__collection.name), + ("query", self.__spec)]) + if self.__max_time_ms is not None: + cmd["maxTimeMS"] = self.__max_time_ms + if self.__comment: + cmd["comment"] = self.__comment + + if self.__hint is not None: + cmd["hint"] = self.__hint + + if with_limit_and_skip: + if self.__limit: + cmd["limit"] = self.__limit + if self.__skip: + cmd["skip"] = self.__skip + + return self.__collection._count( + cmd, self.__collation, session=self.__session) + + def distinct(self, key): + """Get a list of distinct values for `key` among all documents + in the result set of this query. + + Raises :class:`TypeError` if `key` is not an instance of + :class:`basestring` (:class:`str` in python 3). + + The :meth:`distinct` method obeys the + :attr:`~pymongo.collection.Collection.read_preference` of the + :class:`~pymongo.collection.Collection` instance on which + :meth:`~pymongo.collection.Collection.find` was called. + + :Parameters: + - `key`: name of key for which we want to get the distinct values + + .. seealso:: :meth:`pymongo.collection.Collection.distinct` + """ + options = {} + if self.__spec: + options["query"] = self.__spec + if self.__max_time_ms is not None: + options['maxTimeMS'] = self.__max_time_ms + if self.__comment: + options['comment'] = self.__comment + if self.__collation is not None: + options['collation'] = self.__collation + + return self.__collection.distinct( + key, session=self.__session, **options) + + def explain(self): + """Returns an explain plan record for this cursor. + + .. note:: Starting with MongoDB 3.2 :meth:`explain` uses + the default verbosity mode of the `explain command + `_, + ``allPlansExecution``. To use a different verbosity use + :meth:`~pymongo.database.Database.command` to run the explain + command directly. + + .. mongodoc:: explain + """ + c = self.clone() + c.__explain = True + + # always use a hard limit for explains + if c.__limit: + c.__limit = -abs(c.__limit) + return next(c) + + def __set_hint(self, index): + if index is None: + self.__hint = None + return + + if isinstance(index, string_type): + self.__hint = index + else: + self.__hint = helpers._index_document(index) + + def hint(self, index): + """Adds a 'hint', telling Mongo the proper index to use for the query. + + Judicious use of hints can greatly improve query + performance. When doing a query on multiple fields (at least + one of which is indexed) pass the indexed field as a hint to + the query. Raises :class:`~pymongo.errors.OperationFailure` if the + provided hint requires an index that does not exist on this collection, + and raises :class:`~pymongo.errors.InvalidOperation` if this cursor has + already been used. + + `index` should be an index as passed to + :meth:`~pymongo.collection.Collection.create_index` + (e.g. ``[('field', ASCENDING)]``) or the name of the index. + If `index` is ``None`` any existing hint for this query is + cleared. The last hint applied to this cursor takes precedence + over all others. + + :Parameters: + - `index`: index to hint on (as an index specifier) + + .. versionchanged:: 2.8 + The :meth:`~hint` method accepts the name of the index. + """ + self.__check_okay_to_chain() + self.__set_hint(index) + return self + + def comment(self, comment): + """Adds a 'comment' to the cursor. + + http://docs.mongodb.org/manual/reference/operator/comment/ + + :Parameters: + - `comment`: A string to attach to the query to help interpret and + trace the operation in the server logs and in profile data. + + .. versionadded:: 2.7 + """ + self.__check_okay_to_chain() + self.__comment = comment + return self + + def where(self, code): + """Adds a $where clause to this query. + + The `code` argument must be an instance of :class:`basestring` + (:class:`str` in python 3) or :class:`~bson.code.Code` + containing a JavaScript expression. This expression will be + evaluated for each document scanned. Only those documents + for which the expression evaluates to *true* will be returned + as results. The keyword *this* refers to the object currently + being scanned. + + Raises :class:`TypeError` if `code` is not an instance of + :class:`basestring` (:class:`str` in python 3). Raises + :class:`~pymongo.errors.InvalidOperation` if this + :class:`Cursor` has already been used. Only the last call to + :meth:`where` applied to a :class:`Cursor` has any effect. + + :Parameters: + - `code`: JavaScript expression to use as a filter + """ + self.__check_okay_to_chain() + if not isinstance(code, Code): + code = Code(code) + + self.__spec["$where"] = code + return self + + def collation(self, collation): + """Adds a :class:`~pymongo.collation.Collation` to this query. + + This option is only supported on MongoDB 3.4 and above. + + Raises :exc:`TypeError` if `collation` is not an instance of + :class:`~pymongo.collation.Collation` or a ``dict``. Raises + :exc:`~pymongo.errors.InvalidOperation` if this :class:`Cursor` has + already been used. Only the last collation applied to this cursor has + any effect. + + :Parameters: + - `collation`: An instance of :class:`~pymongo.collation.Collation`. + """ + self.__check_okay_to_chain() + self.__collation = validate_collation_or_none(collation) + return self + + def __send_message(self, operation): + """Send a query or getmore operation and handles the response. + + If operation is ``None`` this is an exhaust cursor, which reads + the next result batch off the exhaust socket instead of + sending getMore messages to the server. + + Can raise ConnectionFailure. + """ + client = self.__collection.database.client + listeners = client._event_listeners + publish = listeners.enabled_for_commands + from_command = False + start = datetime.datetime.now() + + def duration(): return datetime.datetime.now() - start + + if operation: + try: + response = client._send_message_with_response( + operation, exhaust=self.__exhaust, address=self.__address) + self.__address = response.address + if self.__exhaust: + # 'response' is an ExhaustResponse. + self.__exhaust_mgr = _SocketManager(response.socket_info, + response.pool) + + cmd_name = operation.name + reply = response.data + rqst_id = response.request_id + from_command = response.from_command + except AutoReconnect: + # Don't try to send kill cursors on another socket + # or to another server. It can cause a _pinValue + # assertion on some server releases if we get here + # due to a socket timeout. + self.__killed = True + self.__die() + raise + else: + # Exhaust cursor - no getMore message. + rqst_id = 0 + cmd_name = 'getMore' + if publish: + # Fake a getMore command. + cmd = SON([('getMore', self.__id), + ('collection', self.__collection.name)]) + if self.__batch_size: + cmd['batchSize'] = self.__batch_size + if self.__max_time_ms: + cmd['maxTimeMS'] = self.__max_time_ms + listeners.publish_command_start( + cmd, self.__collection.database.name, 0, self.__address) + try: + reply = self.__exhaust_mgr.sock.receive_message(None) + except Exception as exc: + if publish: + listeners.publish_command_failure( + duration(), _convert_exception(exc), cmd_name, rqst_id, + self.__address) + if isinstance(exc, ConnectionFailure): + self.__die() + raise + + try: + user_fields = None + legacy_response = True + if from_command: + user_fields = _CURSOR_DOC_FIELDS + legacy_response = False + docs = self._unpack_response( + reply, self.__id, self.__collection.codec_options, + legacy_response=legacy_response, user_fields=user_fields) + if from_command: + first = docs[0] + client._receive_cluster_time(first, self.__session) + helpers._check_command_response(first) + except OperationFailure as exc: + self.__killed = True + + # Make sure exhaust socket is returned immediately, if necessary. + self.__die() + + if publish: + listeners.publish_command_failure( + duration(), exc.details, cmd_name, rqst_id, self.__address) + + # If this is a tailable cursor the error is likely + # due to capped collection roll over. Setting + # self.__killed to True ensures Cursor.alive will be + # False. No need to re-raise. + if self.__query_flags & _QUERY_OPTIONS["tailable_cursor"]: + return + raise + except NotMasterError as exc: + # Don't send kill cursors to another server after a "not master" + # error. It's completely pointless. + self.__killed = True + + # Make sure exhaust socket is returned immediately, if necessary. + self.__die() + + if publish: + listeners.publish_command_failure( + duration(), exc.details, cmd_name, rqst_id, self.__address) + + client._reset_server_and_request_check(self.__address) + raise + except Exception as exc: + if publish: + listeners.publish_command_failure( + duration(), _convert_exception(exc), cmd_name, rqst_id, + self.__address) + raise + + if publish: + # Must publish in find / getMore / explain command response format. + if from_command: + res = docs[0] + elif cmd_name == "explain": + res = docs[0] if docs else {} + else: + res = {"cursor": {"id": reply.cursor_id, + "ns": self.__collection.full_name}, + "ok": 1} + if cmd_name == "find": + res["cursor"]["firstBatch"] = docs + else: + res["cursor"]["nextBatch"] = docs + listeners.publish_command_success( + duration(), res, cmd_name, rqst_id, self.__address) + + if from_command: + if cmd_name != "explain": + cursor = docs[0]['cursor'] + self.__id = cursor['id'] + if cmd_name == 'find': + documents = cursor['firstBatch'] + else: + documents = cursor['nextBatch'] + self.__data = deque(documents) + self.__retrieved += len(documents) + else: + self.__id = 0 + self.__data = deque(docs) + self.__retrieved += len(docs) + else: + self.__id = reply.cursor_id + self.__data = deque(docs) + self.__retrieved += reply.number_returned + + if self.__id == 0: + self.__killed = True + # Don't wait for garbage collection to call __del__, return the + # socket and the session to the pool now. + self.__die() + + if self.__limit and self.__id and self.__limit <= self.__retrieved: + self.__die() + + def _unpack_response(self, response, cursor_id, codec_options, + user_fields=None, legacy_response=False): + return response.unpack_response(cursor_id, codec_options, user_fields, + legacy_response) + + def _read_preference(self): + if self.__read_preference is None: + # Save the read preference for getMore commands. + self.__read_preference = self.__collection._read_preference_for( + self.session) + return self.__read_preference + + def _refresh(self): + """Refreshes the cursor with more data from Mongo. + + Returns the length of self.__data after refresh. Will exit early if + self.__data is already non-empty. Raises OperationFailure when the + cursor cannot be refreshed due to an error on the query. + """ + if len(self.__data) or self.__killed: + return len(self.__data) + + if not self.__session: + self.__session = self.__collection.database.client._ensure_session() + + if self.__id is None: # Query + if (self.__min or self.__max) and not self.__hint: + warnings.warn("using a min/max query operator without " + "specifying a Cursor.hint is deprecated. A " + "hint will be required when using min/max in " + "PyMongo 4.0", + DeprecationWarning, stacklevel=3) + q = self._query_class(self.__query_flags, + self.__collection.database.name, + self.__collection.name, + self.__skip, + self.__query_spec(), + self.__projection, + self.__codec_options, + self._read_preference(), + self.__limit, + self.__batch_size, + self.__read_concern, + self.__collation, + self.__session, + self.__collection.database.client) + self.__send_message(q) + elif self.__id: # Get More + if self.__limit: + limit = self.__limit - self.__retrieved + if self.__batch_size: + limit = min(limit, self.__batch_size) + else: + limit = self.__batch_size + + # Exhaust cursors don't send getMore messages. + if self.__exhaust: + self.__send_message(None) + else: + g = self._getmore_class(self.__collection.database.name, + self.__collection.name, + limit, + self.__id, + self.__codec_options, + self._read_preference(), + self.__session, + self.__collection.database.client, + self.__max_await_time_ms) + self.__send_message(g) + + return len(self.__data) + + @property + def alive(self): + """Does this cursor have the potential to return more data? + + This is mostly useful with `tailable cursors + `_ + since they will stop iterating even though they *may* return more + results in the future. + + With regular cursors, simply use a for loop instead of :attr:`alive`:: + + for doc in collection.find(): + print(doc) + + .. note:: Even if :attr:`alive` is True, :meth:`next` can raise + :exc:`StopIteration`. :attr:`alive` can also be True while iterating + a cursor from a failed server. In this case :attr:`alive` will + return False after :meth:`next` fails to retrieve the next batch + of results from the server. + """ + return bool(len(self.__data) or (not self.__killed)) + + @property + def cursor_id(self): + """Returns the id of the cursor + + Useful if you need to manage cursor ids and want to handle killing + cursors manually using + :meth:`~pymongo.mongo_client.MongoClient.kill_cursors` + + .. versionadded:: 2.2 + """ + return self.__id + + @property + def address(self): + """The (host, port) of the server used, or None. + + .. versionchanged:: 3.0 + Renamed from "conn_id". + """ + return self.__address + + @property + def session(self): + """The cursor's :class:`~pymongo.client_session.ClientSession`, or None. + + .. versionadded:: 3.6 + """ + if self.__explicit_session: + return self.__session + + def __iter__(self): + return self + + def next(self): + """Advance the cursor.""" + if self.__empty: + raise StopIteration + if len(self.__data) or self._refresh(): + if self.__manipulate: + _db = self.__collection.database + return _db._fix_outgoing(self.__data.popleft(), + self.__collection) + else: + return self.__data.popleft() + else: + raise StopIteration + + __next__ = next + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + def __copy__(self): + """Support function for `copy.copy()`. + + .. versionadded:: 2.4 + """ + return self._clone(deepcopy=False) + + def __deepcopy__(self, memo): + """Support function for `copy.deepcopy()`. + + .. versionadded:: 2.4 + """ + return self._clone(deepcopy=True) + + def _deepcopy(self, x, memo=None): + """Deepcopy helper for the data dictionary or list. + + Regular expressions cannot be deep copied but as they are immutable we + don't have to copy them when cloning. + """ + if not hasattr(x, 'items'): + y, is_list, iterator = [], True, enumerate(x) + else: + y, is_list, iterator = {}, False, iteritems(x) + + if memo is None: + memo = {} + val_id = id(x) + if val_id in memo: + return memo.get(val_id) + memo[val_id] = y + + for key, value in iterator: + if isinstance(value, (dict, list)) and not isinstance(value, SON): + value = self._deepcopy(value, memo) + elif not isinstance(value, RE_TYPE): + value = copy.deepcopy(value, memo) + + if is_list: + y.append(value) + else: + if not isinstance(key, RE_TYPE): + key = copy.deepcopy(key, memo) + y[key] = value + return y + + +class RawBatchCursor(Cursor): + """A cursor / iterator over raw batches of BSON data from a query result.""" + + _query_class = _RawBatchQuery + _getmore_class = _RawBatchGetMore + + def __init__(self, *args, **kwargs): + """Create a new cursor / iterator over raw batches of BSON data. + + Should not be called directly by application developers - + see :meth:`~pymongo.collection.Collection.find_raw_batches` + instead. + + .. mongodoc:: cursors + """ + manipulate = kwargs.get('manipulate') + kwargs['manipulate'] = False + super(RawBatchCursor, self).__init__(*args, **kwargs) + + # Throw only after cursor's initialized, to prevent errors in __del__. + if manipulate: + raise InvalidOperation( + "Cannot use RawBatchCursor with manipulate=True") + + def _unpack_response(self, response, cursor_id, codec_options, + user_fields=None, legacy_response=False): + return response.raw_response(cursor_id) + + def explain(self): + """Returns an explain plan record for this cursor. + + .. mongodoc:: explain + """ + clone = self._clone(deepcopy=True, base=Cursor(self.collection)) + return clone.explain() + + def __getitem__(self, index): + raise InvalidOperation("Cannot call __getitem__ on RawBatchCursor") diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/cursor_manager.py b/backend/venv/lib/python3.7/site-packages/pymongo/cursor_manager.py new file mode 100644 index 000000000..c05cf301e --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/cursor_manager.py @@ -0,0 +1,65 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""DEPRECATED - A manager to handle when cursors are killed after they are +closed. + +New cursor managers should be defined as subclasses of CursorManager and can be +installed on a client by calling +:meth:`~pymongo.mongo_client.MongoClient.set_cursor_manager`. + +.. versionchanged:: 3.3 + Deprecated, for real this time. + +.. versionchanged:: 3.0 + Undeprecated. :meth:`~pymongo.cursor_manager.CursorManager.close` now + requires an `address` argument. The ``BatchCursorManager`` class is removed. +""" + +import warnings +import weakref +from bson.py3compat import integer_types + + +class CursorManager(object): + """DEPRECATED - The cursor manager base class.""" + + def __init__(self, client): + """Instantiate the manager. + + :Parameters: + - `client`: a MongoClient + """ + warnings.warn( + "Cursor managers are deprecated.", + DeprecationWarning, + stacklevel=2) + self.__client = weakref.ref(client) + + def close(self, cursor_id, address): + """Kill a cursor. + + Raises TypeError if cursor_id is not an instance of (int, long). + + :Parameters: + - `cursor_id`: cursor id to close + - `address`: the cursor's server's (host, port) pair + + .. versionchanged:: 3.0 + Now requires an `address` argument. + """ + if not isinstance(cursor_id, integer_types): + raise TypeError("cursor_id must be an integer") + + self.__client().kill_cursors([cursor_id], address) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/database.py b/backend/venv/lib/python3.7/site-packages/pymongo/database.py new file mode 100644 index 000000000..c4f7f47f8 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/database.py @@ -0,0 +1,1465 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Database level operations.""" + +import warnings + +from bson.code import Code +from bson.codec_options import DEFAULT_CODEC_OPTIONS +from bson.dbref import DBRef +from bson.py3compat import iteritems, string_type, _unicode +from bson.son import SON +from pymongo import auth, common +from pymongo.change_stream import DatabaseChangeStream +from pymongo.collection import Collection +from pymongo.command_cursor import CommandCursor +from pymongo.errors import (CollectionInvalid, + ConfigurationError, + InvalidName, + OperationFailure) +from pymongo.message import _first_batch +from pymongo.read_preferences import ReadPreference +from pymongo.son_manipulator import SONManipulator +from pymongo.write_concern import DEFAULT_WRITE_CONCERN + + +_INDEX_REGEX = {"name": {"$regex": r"^(?!.*\$)"}} +_SYSTEM_FILTER = {"filter": {"name": {"$regex": r"^(?!system\.)"}}} + + +def _check_name(name): + """Check if a database name is valid. + """ + if not name: + raise InvalidName("database name cannot be the empty string") + + for invalid_char in [' ', '.', '$', '/', '\\', '\x00', '"']: + if invalid_char in name: + raise InvalidName("database names cannot contain the " + "character %r" % invalid_char) + + +class Database(common.BaseObject): + """A Mongo database. + """ + + def __init__(self, client, name, codec_options=None, read_preference=None, + write_concern=None, read_concern=None): + """Get a database by client and name. + + Raises :class:`TypeError` if `name` is not an instance of + :class:`basestring` (:class:`str` in python 3). Raises + :class:`~pymongo.errors.InvalidName` if `name` is not a valid + database name. + + :Parameters: + - `client`: A :class:`~pymongo.mongo_client.MongoClient` instance. + - `name`: The database name. + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. If ``None`` (the + default) client.codec_options is used. + - `read_preference` (optional): The read preference to use. If + ``None`` (the default) client.read_preference is used. + - `write_concern` (optional): An instance of + :class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the + default) client.write_concern is used. + - `read_concern` (optional): An instance of + :class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the + default) client.read_concern is used. + + .. mongodoc:: databases + + .. versionchanged:: 3.2 + Added the read_concern option. + + .. versionchanged:: 3.0 + Added the codec_options, read_preference, and write_concern options. + :class:`~pymongo.database.Database` no longer returns an instance + of :class:`~pymongo.collection.Collection` for attribute names + with leading underscores. You must use dict-style lookups instead:: + + db['__my_collection__'] + + Not: + + db.__my_collection__ + """ + super(Database, self).__init__( + codec_options or client.codec_options, + read_preference or client.read_preference, + write_concern or client.write_concern, + read_concern or client.read_concern) + + if not isinstance(name, string_type): + raise TypeError("name must be an instance " + "of %s" % (string_type.__name__,)) + + if name != '$external': + _check_name(name) + + self.__name = _unicode(name) + self.__client = client + + self.__incoming_manipulators = [] + self.__incoming_copying_manipulators = [] + self.__outgoing_manipulators = [] + self.__outgoing_copying_manipulators = [] + + def add_son_manipulator(self, manipulator): + """Add a new son manipulator to this database. + + **DEPRECATED** - `add_son_manipulator` is deprecated. + + .. versionchanged:: 3.0 + Deprecated add_son_manipulator. + """ + warnings.warn("add_son_manipulator is deprecated", + DeprecationWarning, stacklevel=2) + base = SONManipulator() + def method_overwritten(instance, method): + """Test if this method has been overridden.""" + return (getattr( + instance, method).__func__ != getattr(base, method).__func__) + + if manipulator.will_copy(): + if method_overwritten(manipulator, "transform_incoming"): + self.__incoming_copying_manipulators.insert(0, manipulator) + if method_overwritten(manipulator, "transform_outgoing"): + self.__outgoing_copying_manipulators.insert(0, manipulator) + else: + if method_overwritten(manipulator, "transform_incoming"): + self.__incoming_manipulators.insert(0, manipulator) + if method_overwritten(manipulator, "transform_outgoing"): + self.__outgoing_manipulators.insert(0, manipulator) + + @property + def system_js(self): + """**DEPRECATED**: :class:`SystemJS` helper for this :class:`Database`. + + See the documentation for :class:`SystemJS` for more details. + """ + return SystemJS(self) + + @property + def client(self): + """The client instance for this :class:`Database`.""" + return self.__client + + @property + def name(self): + """The name of this :class:`Database`.""" + return self.__name + + @property + def incoming_manipulators(self): + """**DEPRECATED**: All incoming SON manipulators. + + .. versionchanged:: 3.5 + Deprecated. + + .. versionadded:: 2.0 + """ + warnings.warn("Database.incoming_manipulators() is deprecated", + DeprecationWarning, stacklevel=2) + + return [manipulator.__class__.__name__ + for manipulator in self.__incoming_manipulators] + + @property + def incoming_copying_manipulators(self): + """**DEPRECATED**: All incoming SON copying manipulators. + + .. versionchanged:: 3.5 + Deprecated. + + .. versionadded:: 2.0 + """ + warnings.warn("Database.incoming_copying_manipulators() is deprecated", + DeprecationWarning, stacklevel=2) + + return [manipulator.__class__.__name__ + for manipulator in self.__incoming_copying_manipulators] + + @property + def outgoing_manipulators(self): + """**DEPRECATED**: All outgoing SON manipulators. + + .. versionchanged:: 3.5 + Deprecated. + + .. versionadded:: 2.0 + """ + warnings.warn("Database.outgoing_manipulators() is deprecated", + DeprecationWarning, stacklevel=2) + + return [manipulator.__class__.__name__ + for manipulator in self.__outgoing_manipulators] + + @property + def outgoing_copying_manipulators(self): + """**DEPRECATED**: All outgoing SON copying manipulators. + + .. versionchanged:: 3.5 + Deprecated. + + .. versionadded:: 2.0 + """ + warnings.warn("Database.outgoing_copying_manipulators() is deprecated", + DeprecationWarning, stacklevel=2) + + return [manipulator.__class__.__name__ + for manipulator in self.__outgoing_copying_manipulators] + + def with_options(self, codec_options=None, read_preference=None, + write_concern=None, read_concern=None): + """Get a clone of this database changing the specified settings. + + >>> db1.read_preference + Primary() + >>> from pymongo import ReadPreference + >>> db2 = db1.with_options(read_preference=ReadPreference.SECONDARY) + >>> db1.read_preference + Primary() + >>> db2.read_preference + Secondary(tag_sets=None) + + :Parameters: + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. If ``None`` (the + default) the :attr:`codec_options` of this :class:`Collection` + is used. + - `read_preference` (optional): The read preference to use. If + ``None`` (the default) the :attr:`read_preference` of this + :class:`Collection` is used. See :mod:`~pymongo.read_preferences` + for options. + - `write_concern` (optional): An instance of + :class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the + default) the :attr:`write_concern` of this :class:`Collection` + is used. + - `read_concern` (optional): An instance of + :class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the + default) the :attr:`read_concern` of this :class:`Collection` + is used. + + .. versionadded:: 3.8 + """ + return Database(self.client, + self.__name, + codec_options or self.codec_options, + read_preference or self.read_preference, + write_concern or self.write_concern, + read_concern or self.read_concern) + + def __eq__(self, other): + if isinstance(other, Database): + return (self.__client == other.client and + self.__name == other.name) + return NotImplemented + + def __ne__(self, other): + return not self == other + + def __repr__(self): + return "Database(%r, %r)" % (self.__client, self.__name) + + def __getattr__(self, name): + """Get a collection of this database by name. + + Raises InvalidName if an invalid collection name is used. + + :Parameters: + - `name`: the name of the collection to get + """ + if name.startswith('_'): + raise AttributeError( + "Database has no attribute %r. To access the %s" + " collection, use database[%r]." % (name, name, name)) + return self.__getitem__(name) + + def __getitem__(self, name): + """Get a collection of this database by name. + + Raises InvalidName if an invalid collection name is used. + + :Parameters: + - `name`: the name of the collection to get + """ + return Collection(self, name) + + def get_collection(self, name, codec_options=None, read_preference=None, + write_concern=None, read_concern=None): + """Get a :class:`~pymongo.collection.Collection` with the given name + and options. + + Useful for creating a :class:`~pymongo.collection.Collection` with + different codec options, read preference, and/or write concern from + this :class:`Database`. + + >>> db.read_preference + Primary() + >>> coll1 = db.test + >>> coll1.read_preference + Primary() + >>> from pymongo import ReadPreference + >>> coll2 = db.get_collection( + ... 'test', read_preference=ReadPreference.SECONDARY) + >>> coll2.read_preference + Secondary(tag_sets=None) + + :Parameters: + - `name`: The name of the collection - a string. + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. If ``None`` (the + default) the :attr:`codec_options` of this :class:`Database` is + used. + - `read_preference` (optional): The read preference to use. If + ``None`` (the default) the :attr:`read_preference` of this + :class:`Database` is used. See :mod:`~pymongo.read_preferences` + for options. + - `write_concern` (optional): An instance of + :class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the + default) the :attr:`write_concern` of this :class:`Database` is + used. + - `read_concern` (optional): An instance of + :class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the + default) the :attr:`read_concern` of this :class:`Database` is + used. + """ + return Collection( + self, name, False, codec_options, read_preference, + write_concern, read_concern) + + def create_collection(self, name, codec_options=None, + read_preference=None, write_concern=None, + read_concern=None, session=None, **kwargs): + """Create a new :class:`~pymongo.collection.Collection` in this + database. + + Normally collection creation is automatic. This method should + only be used to specify options on + creation. :class:`~pymongo.errors.CollectionInvalid` will be + raised if the collection already exists. + + Options should be passed as keyword arguments to this method. Supported + options vary with MongoDB release. Some examples include: + + - "size": desired initial size for the collection (in + bytes). For capped collections this size is the max + size of the collection. + - "capped": if True, this is a capped collection + - "max": maximum number of objects if capped (optional) + + See the MongoDB documentation for a full list of supported options by + server version. + + :Parameters: + - `name`: the name of the collection to create + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. If ``None`` (the + default) the :attr:`codec_options` of this :class:`Database` is + used. + - `read_preference` (optional): The read preference to use. If + ``None`` (the default) the :attr:`read_preference` of this + :class:`Database` is used. + - `write_concern` (optional): An instance of + :class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the + default) the :attr:`write_concern` of this :class:`Database` is + used. + - `read_concern` (optional): An instance of + :class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the + default) the :attr:`read_concern` of this :class:`Database` is + used. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): additional keyword arguments will + be passed as options for the create collection command + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.4 + Added the collation option. + + .. versionchanged:: 3.0 + Added the codec_options, read_preference, and write_concern options. + + .. versionchanged:: 2.2 + Removed deprecated argument: options + """ + with self.__client._tmp_session(session) as s: + if name in self.list_collection_names( + filter={"name": name}, session=s): + raise CollectionInvalid("collection %s already exists" % name) + + return Collection(self, name, True, codec_options, + read_preference, write_concern, + read_concern, session=s, **kwargs) + + def _apply_incoming_manipulators(self, son, collection): + """Apply incoming manipulators to `son`.""" + for manipulator in self.__incoming_manipulators: + son = manipulator.transform_incoming(son, collection) + return son + + def _apply_incoming_copying_manipulators(self, son, collection): + """Apply incoming copying manipulators to `son`.""" + for manipulator in self.__incoming_copying_manipulators: + son = manipulator.transform_incoming(son, collection) + return son + + def _fix_incoming(self, son, collection): + """Apply manipulators to an incoming SON object before it gets stored. + + :Parameters: + - `son`: the son object going into the database + - `collection`: the collection the son object is being saved in + """ + son = self._apply_incoming_manipulators(son, collection) + son = self._apply_incoming_copying_manipulators(son, collection) + return son + + def _fix_outgoing(self, son, collection): + """Apply manipulators to a SON object as it comes out of the database. + + :Parameters: + - `son`: the son object coming out of the database + - `collection`: the collection the son object was saved in + """ + for manipulator in reversed(self.__outgoing_manipulators): + son = manipulator.transform_outgoing(son, collection) + for manipulator in reversed(self.__outgoing_copying_manipulators): + son = manipulator.transform_outgoing(son, collection) + return son + + def watch(self, pipeline=None, full_document='default', resume_after=None, + max_await_time_ms=None, batch_size=None, collation=None, + start_at_operation_time=None, session=None): + """Watch changes on this database. + + Performs an aggregation with an implicit initial ``$changeStream`` + stage and returns a + :class:`~pymongo.change_stream.DatabaseChangeStream` cursor which + iterates over changes on all collections in this database. + + Introduced in MongoDB 4.0. + + .. code-block:: python + + with db.watch() as stream: + for change in stream: + print(change) + + The :class:`~pymongo.change_stream.DatabaseChangeStream` iterable + blocks until the next change document is returned or an error is + raised. If the + :meth:`~pymongo.change_stream.DatabaseChangeStream.next` method + encounters a network error when retrieving a batch from the server, + it will automatically attempt to recreate the cursor such that no + change events are missed. Any error encountered during the resume + attempt indicates there may be an outage and will be raised. + + .. code-block:: python + + try: + with db.watch( + [{'$match': {'operationType': 'insert'}}]) as stream: + for insert_change in stream: + print(insert_change) + except pymongo.errors.PyMongoError: + # The ChangeStream encountered an unrecoverable error or the + # resume attempt failed to recreate the cursor. + logging.error('...') + + For a precise description of the resume process see the + `change streams specification`_. + + :Parameters: + - `pipeline` (optional): A list of aggregation pipeline stages to + append to an initial ``$changeStream`` stage. Not all + pipeline stages are valid after a ``$changeStream`` stage, see the + MongoDB documentation on change streams for the supported stages. + - `full_document` (optional): The fullDocument to pass as an option + to the ``$changeStream`` stage. Allowed values: 'default', + 'updateLookup'. Defaults to 'default'. + When set to 'updateLookup', the change notification for partial + updates will include both a delta describing the changes to the + document, as well as a copy of the entire document that was + changed from some time after the change occurred. + - `resume_after` (optional): The logical starting point for this + change stream. + - `max_await_time_ms` (optional): The maximum time in milliseconds + for the server to wait for changes before responding to a getMore + operation. + - `batch_size` (optional): The maximum number of documents to return + per batch. + - `collation` (optional): The :class:`~pymongo.collation.Collation` + to use for the aggregation. + - `start_at_operation_time` (optional): If provided, the resulting + change stream will only return changes that occurred at or after + the specified :class:`~bson.timestamp.Timestamp`. Requires + MongoDB >= 4.0. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + :Returns: + A :class:`~pymongo.change_stream.DatabaseChangeStream` cursor. + + .. versionadded:: 3.7 + + .. mongodoc:: changeStreams + + .. _change streams specification: + https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst + """ + return DatabaseChangeStream( + self, pipeline, full_document, resume_after, max_await_time_ms, + batch_size, collation, start_at_operation_time, session + ) + + def _command(self, sock_info, command, slave_ok=False, value=1, check=True, + allowable_errors=None, read_preference=ReadPreference.PRIMARY, + codec_options=DEFAULT_CODEC_OPTIONS, + write_concern=None, + parse_write_concern_error=False, session=None, **kwargs): + """Internal command helper.""" + if isinstance(command, string_type): + command = SON([(command, value)]) + + command.update(kwargs) + with self.__client._tmp_session(session) as s: + return sock_info.command( + self.__name, + command, + slave_ok, + read_preference, + codec_options, + check, + allowable_errors, + write_concern=write_concern, + parse_write_concern_error=parse_write_concern_error, + session=s, + client=self.__client) + + def command(self, command, value=1, check=True, + allowable_errors=None, read_preference=None, + codec_options=DEFAULT_CODEC_OPTIONS, session=None, **kwargs): + """Issue a MongoDB command. + + Send command `command` to the database and return the + response. If `command` is an instance of :class:`basestring` + (:class:`str` in python 3) then the command {`command`: `value`} + will be sent. Otherwise, `command` must be an instance of + :class:`dict` and will be sent as is. + + Any additional keyword arguments will be added to the final + command document before it is sent. + + For example, a command like ``{buildinfo: 1}`` can be sent + using: + + >>> db.command("buildinfo") + + For a command where the value matters, like ``{collstats: + collection_name}`` we can do: + + >>> db.command("collstats", collection_name) + + For commands that take additional arguments we can use + kwargs. So ``{filemd5: object_id, root: file_root}`` becomes: + + >>> db.command("filemd5", object_id, root=file_root) + + :Parameters: + - `command`: document representing the command to be issued, + or the name of the command (for simple commands only). + + .. note:: the order of keys in the `command` document is + significant (the "verb" must come first), so commands + which require multiple keys (e.g. `findandmodify`) + should use an instance of :class:`~bson.son.SON` or + a string and kwargs instead of a Python `dict`. + + - `value` (optional): value to use for the command verb when + `command` is passed as a string + - `check` (optional): check the response for errors, raising + :class:`~pymongo.errors.OperationFailure` if there are any + - `allowable_errors`: if `check` is ``True``, error messages + in this list will be ignored by error-checking + - `read_preference` (optional): The read preference for this + operation. See :mod:`~pymongo.read_preferences` for options. + If the provided `session` is in a transaction, defaults to the + read preference configured for the transaction. + Otherwise, defaults to + :attr:`~pymongo.read_preferences.ReadPreference.PRIMARY`. + - `codec_options`: A :class:`~bson.codec_options.CodecOptions` + instance. + - `session` (optional): A + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): additional keyword arguments will + be added to the command document before it is sent + + .. note:: :meth:`command` does **not** obey this Database's + :attr:`read_preference` or :attr:`codec_options`. You must use the + `read_preference` and `codec_options` parameters instead. + + .. note:: :meth:`command` does **not** apply any custom TypeDecoders + when decoding the command response. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.0 + Removed the `as_class`, `fields`, `uuid_subtype`, `tag_sets`, + and `secondary_acceptable_latency_ms` option. + Removed `compile_re` option: PyMongo now always represents BSON + regular expressions as :class:`~bson.regex.Regex` objects. Use + :meth:`~bson.regex.Regex.try_compile` to attempt to convert from a + BSON regular expression to a Python regular expression object. + Added the `codec_options` parameter. + + .. versionchanged:: 2.7 + Added `compile_re` option. If set to False, PyMongo represented BSON + regular expressions as :class:`~bson.regex.Regex` objects instead of + attempting to compile BSON regular expressions as Python native + regular expressions, thus preventing errors for some incompatible + patterns, see `PYTHON-500`_. + + .. versionchanged:: 2.3 + Added `tag_sets` and `secondary_acceptable_latency_ms` options. + .. versionchanged:: 2.2 + Added support for `as_class` - the class you want to use for + the resulting documents + + .. _PYTHON-500: https://jira.mongodb.org/browse/PYTHON-500 + + .. mongodoc:: commands + """ + if read_preference is None: + read_preference = ((session and session._txn_read_preference()) + or ReadPreference.PRIMARY) + with self.__client._socket_for_reads( + read_preference) as (sock_info, slave_ok): + return self._command(sock_info, command, slave_ok, value, + check, allowable_errors, read_preference, + codec_options, session=session, **kwargs) + + def _list_collections(self, sock_info, slave_okay, session, + read_preference, **kwargs): + """Internal listCollections helper.""" + + coll = self.get_collection( + "$cmd", read_preference=read_preference) + if sock_info.max_wire_version > 2: + cmd = SON([("listCollections", 1), + ("cursor", {})]) + cmd.update(kwargs) + with self.__client._tmp_session( + session, close=False) as tmp_session: + cursor = self._command( + sock_info, cmd, slave_okay, + read_preference=read_preference, + session=tmp_session)["cursor"] + return CommandCursor( + coll, + cursor, + sock_info.address, + session=tmp_session, + explicit_session=session is not None) + else: + match = _INDEX_REGEX + if "filter" in kwargs: + match = {"$and": [_INDEX_REGEX, kwargs["filter"]]} + dblen = len(self.name.encode("utf8") + b".") + pipeline = [ + {"$project": {"name": {"$substr": ["$name", dblen, -1]}, + "options": 1}}, + {"$match": match} + ] + cmd = SON([("aggregate", "system.namespaces"), + ("pipeline", pipeline), + ("cursor", kwargs.get("cursor", {}))]) + cursor = self._command(sock_info, cmd, slave_okay)["cursor"] + return CommandCursor(coll, cursor, sock_info.address) + + def list_collections(self, session=None, filter=None, **kwargs): + """Get a cursor over the collectons of this database. + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `filter` (optional): A query document to filter the list of + collections returned from the listCollections command. + - `**kwargs` (optional): Optional parameters of the + `listCollections command + `_ + can be passed as keyword arguments to this method. The supported + options differ by server version. + + :Returns: + An instance of :class:`~pymongo.command_cursor.CommandCursor`. + + .. versionadded:: 3.6 + """ + if filter is not None: + kwargs['filter'] = filter + read_pref = ((session and session._txn_read_preference()) + or ReadPreference.PRIMARY) + with self.__client._socket_for_reads( + read_pref) as (sock_info, slave_okay): + return self._list_collections( + sock_info, slave_okay, session, read_preference=read_pref, + **kwargs) + + def list_collection_names(self, session=None, filter=None, **kwargs): + """Get a list of all the collection names in this database. + + For example, to list all non-system collections:: + + filter = {"name": {"$regex": r"^(?!system\.)"}} + db.list_collection_names(filter=filter) + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `filter` (optional): A query document to filter the list of + collections returned from the listCollections command. + - `**kwargs` (optional): Optional parameters of the + `listCollections command + `_ + can be passed as keyword arguments to this method. The supported + options differ by server version. + + .. versionchanged:: 3.8 + Added the ``filter`` and ``**kwargs`` parameters. + + .. versionadded:: 3.6 + """ + if filter is None: + kwargs["nameOnly"] = True + else: + # The enumerate collections spec states that "drivers MUST NOT set + # nameOnly if a filter specifies any keys other than name." + common.validate_is_mapping("filter", filter) + kwargs["filter"] = filter + if not filter or (len(filter) == 1 and "name" in filter): + kwargs["nameOnly"] = True + + return [result["name"] + for result in self.list_collections(session=session, **kwargs)] + + def collection_names(self, include_system_collections=True, + session=None): + """**DEPRECATED**: Get a list of all the collection names in this + database. + + :Parameters: + - `include_system_collections` (optional): if ``False`` list + will not include system collections (e.g ``system.indexes``) + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. versionchanged:: 3.7 + Deprecated. Use :meth:`list_collection_names` instead. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + warnings.warn("collection_names is deprecated. Use " + "list_collection_names instead.", + DeprecationWarning, stacklevel=2) + kws = {} if include_system_collections else _SYSTEM_FILTER + return [result["name"] + for result in self.list_collections(session=session, + nameOnly=True, **kws)] + + def drop_collection(self, name_or_collection, session=None): + """Drop a collection. + + :Parameters: + - `name_or_collection`: the name of a collection to drop or the + collection object itself + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. note:: The :attr:`~pymongo.database.Database.write_concern` of + this database is automatically applied to this operation when using + MongoDB >= 3.4. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.4 + Apply this database's write concern automatically to this operation + when connected to MongoDB >= 3.4. + + """ + name = name_or_collection + if isinstance(name, Collection): + name = name.name + + if not isinstance(name, string_type): + raise TypeError("name_or_collection must be an " + "instance of %s" % (string_type.__name__,)) + + self.__client._purge_index(self.__name, name) + + with self.__client._socket_for_writes() as sock_info: + return self._command( + sock_info, 'drop', value=_unicode(name), + allowable_errors=['ns not found'], + write_concern=self._write_concern_for(session), + parse_write_concern_error=True, + session=session) + + def validate_collection(self, name_or_collection, + scandata=False, full=False, session=None): + """Validate a collection. + + Returns a dict of validation info. Raises CollectionInvalid if + validation fails. + + :Parameters: + - `name_or_collection`: A Collection object or the name of a + collection to validate. + - `scandata`: Do extra checks beyond checking the overall + structure of the collection. + - `full`: Have the server do a more thorough scan of the + collection. Use with `scandata` for a thorough scan + of the structure of the collection and the individual + documents. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + name = name_or_collection + if isinstance(name, Collection): + name = name.name + + if not isinstance(name, string_type): + raise TypeError("name_or_collection must be an instance of " + "%s or Collection" % (string_type.__name__,)) + + result = self.command("validate", _unicode(name), + scandata=scandata, full=full, session=session) + + valid = True + # Pre 1.9 results + if "result" in result: + info = result["result"] + if info.find("exception") != -1 or info.find("corrupt") != -1: + raise CollectionInvalid("%s invalid: %s" % (name, info)) + # Sharded results + elif "raw" in result: + for _, res in iteritems(result["raw"]): + if "result" in res: + info = res["result"] + if (info.find("exception") != -1 or + info.find("corrupt") != -1): + raise CollectionInvalid("%s invalid: " + "%s" % (name, info)) + elif not res.get("valid", False): + valid = False + break + # Post 1.9 non-sharded results. + elif not result.get("valid", False): + valid = False + + if not valid: + raise CollectionInvalid("%s invalid: %r" % (name, result)) + + return result + + def current_op(self, include_all=False, session=None): + """Get information on operations currently running. + + :Parameters: + - `include_all` (optional): if ``True`` also list currently + idle operations in the result + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + cmd = SON([("currentOp", 1), ("$all", include_all)]) + with self.__client._socket_for_writes() as sock_info: + if sock_info.max_wire_version >= 4: + return self.__client.admin._command( + sock_info, cmd, codec_options=self.codec_options, + session=session) + else: + spec = {"$all": True} if include_all else {} + return _first_batch(sock_info, "admin", "$cmd.sys.inprog", + spec, -1, True, self.codec_options, + ReadPreference.PRIMARY, cmd, + self.client._event_listeners) + + def profiling_level(self, session=None): + """Get the database's current profiling level. + + Returns one of (:data:`~pymongo.OFF`, + :data:`~pymongo.SLOW_ONLY`, :data:`~pymongo.ALL`). + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. mongodoc:: profiling + """ + result = self.command("profile", -1, session=session) + + assert result["was"] >= 0 and result["was"] <= 2 + return result["was"] + + def set_profiling_level(self, level, slow_ms=None, session=None): + """Set the database's profiling level. + + :Parameters: + - `level`: Specifies a profiling level, see list of possible values + below. + - `slow_ms`: Optionally modify the threshold for the profile to + consider a query or operation. Even if the profiler is off queries + slower than the `slow_ms` level will get written to the logs. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + Possible `level` values: + + +----------------------------+------------------------------------+ + | Level | Setting | + +============================+====================================+ + | :data:`~pymongo.OFF` | Off. No profiling. | + +----------------------------+------------------------------------+ + | :data:`~pymongo.SLOW_ONLY` | On. Only includes slow operations. | + +----------------------------+------------------------------------+ + | :data:`~pymongo.ALL` | On. Includes all operations. | + +----------------------------+------------------------------------+ + + Raises :class:`ValueError` if level is not one of + (:data:`~pymongo.OFF`, :data:`~pymongo.SLOW_ONLY`, + :data:`~pymongo.ALL`). + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. mongodoc:: profiling + """ + if not isinstance(level, int) or level < 0 or level > 2: + raise ValueError("level must be one of (OFF, SLOW_ONLY, ALL)") + + if slow_ms is not None and not isinstance(slow_ms, int): + raise TypeError("slow_ms must be an integer") + + if slow_ms is not None: + self.command("profile", level, slowms=slow_ms, session=session) + else: + self.command("profile", level, session=session) + + def profiling_info(self, session=None): + """Returns a list containing current profiling information. + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. mongodoc:: profiling + """ + return list(self["system.profile"].find(session=session)) + + def error(self): + """**DEPRECATED**: Get the error if one occurred on the last operation. + + This method is obsolete: all MongoDB write operations (insert, update, + remove, and so on) use the write concern ``w=1`` and report their + errors by default. + + .. versionchanged:: 2.8 + Deprecated. + """ + warnings.warn("Database.error() is deprecated", + DeprecationWarning, stacklevel=2) + + error = self.command("getlasterror") + error_msg = error.get("err", "") + if error_msg is None: + return None + if error_msg.startswith("not master"): + # Reset primary server and request check, if another thread isn't + # doing so already. + primary = self.__client.primary + if primary: + self.__client._reset_server_and_request_check(primary) + return error + + def last_status(self): + """**DEPRECATED**: Get status information from the last operation. + + This method is obsolete: all MongoDB write operations (insert, update, + remove, and so on) use the write concern ``w=1`` and report their + errors by default. + + Returns a SON object with status information. + + .. versionchanged:: 2.8 + Deprecated. + """ + warnings.warn("last_status() is deprecated", + DeprecationWarning, stacklevel=2) + + return self.command("getlasterror") + + def previous_error(self): + """**DEPRECATED**: Get the most recent error on this database. + + This method is obsolete: all MongoDB write operations (insert, update, + remove, and so on) use the write concern ``w=1`` and report their + errors by default. + + Only returns errors that have occurred since the last call to + :meth:`reset_error_history`. Returns None if no such errors have + occurred. + + .. versionchanged:: 2.8 + Deprecated. + """ + warnings.warn("previous_error() is deprecated", + DeprecationWarning, stacklevel=2) + + error = self.command("getpreverror") + if error.get("err", 0) is None: + return None + return error + + def reset_error_history(self): + """**DEPRECATED**: Reset the error history of this database. + + This method is obsolete: all MongoDB write operations (insert, update, + remove, and so on) use the write concern ``w=1`` and report their + errors by default. + + Calls to :meth:`previous_error` will only return errors that have + occurred since the most recent call to this method. + + .. versionchanged:: 2.8 + Deprecated. + """ + warnings.warn("reset_error_history() is deprecated", + DeprecationWarning, stacklevel=2) + + self.command("reseterror") + + def __iter__(self): + return self + + def __next__(self): + raise TypeError("'Database' object is not iterable") + + next = __next__ + + def _default_role(self, read_only): + """Return the default user role for this database.""" + if self.name == "admin": + if read_only: + return "readAnyDatabase" + else: + return "root" + else: + if read_only: + return "read" + else: + return "dbOwner" + + def _create_or_update_user( + self, create, name, password, read_only, session=None, **kwargs): + """Use a command to create (if create=True) or modify a user. + """ + opts = {} + if read_only or (create and "roles" not in kwargs): + warnings.warn("Creating a user with the read_only option " + "or without roles is deprecated in MongoDB " + ">= 2.6", DeprecationWarning) + + opts["roles"] = [self._default_role(read_only)] + + if read_only: + warnings.warn("The read_only option is deprecated in MongoDB " + ">= 2.6, use 'roles' instead", DeprecationWarning) + + if password is not None: + if "digestPassword" in kwargs: + raise ConfigurationError("The digestPassword option is not " + "supported via add_user. Please use " + "db.command('createUser', ...) " + "instead for this option.") + opts["pwd"] = password + + # Don't send {} as writeConcern. + if self.write_concern.acknowledged and self.write_concern.document: + opts["writeConcern"] = self.write_concern.document + opts.update(kwargs) + + if create: + command_name = "createUser" + else: + command_name = "updateUser" + + self.command(command_name, name, session=session, **opts) + + def add_user(self, name, password=None, read_only=None, session=None, + **kwargs): + """**DEPRECATED**: Create user `name` with password `password`. + + Add a new user with permissions for this :class:`Database`. + + .. note:: Will change the password if user `name` already exists. + + .. note:: add_user is deprecated and will be removed in PyMongo + 4.0. Starting with MongoDB 2.6 user management is handled with four + database commands, createUser_, usersInfo_, updateUser_, and + dropUser_. + + To create a user:: + + db.command("createUser", "admin", pwd="password", roles=["root"]) + + To create a read-only user:: + + db.command("createUser", "user", pwd="password", roles=["read"]) + + To change a password:: + + db.command("updateUser", "user", pwd="newpassword") + + Or change roles:: + + db.command("updateUser", "user", roles=["readWrite"]) + + .. _createUser: https://docs.mongodb.com/manual/reference/command/createUser/ + .. _usersInfo: https://docs.mongodb.com/manual/reference/command/usersInfo/ + .. _updateUser: https://docs.mongodb.com/manual/reference/command/updateUser/ + .. _dropUser: https://docs.mongodb.com/manual/reference/command/createUser/ + + .. warning:: Never create or modify users over an insecure network without + the use of TLS. See :doc:`/examples/tls` for more information. + + :Parameters: + - `name`: the name of the user to create + - `password` (optional): the password of the user to create. Can not + be used with the ``userSource`` argument. + - `read_only` (optional): if ``True`` the user will be read only + - `**kwargs` (optional): optional fields for the user document + (e.g. ``userSource``, ``otherDBRoles``, or ``roles``). See + ``_ + for more information. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. versionchanged:: 3.7 + Added support for SCRAM-SHA-256 users with MongoDB 4.0 and later. + + .. versionchanged:: 3.6 + Added ``session`` parameter. Deprecated add_user. + + .. versionchanged:: 2.5 + Added kwargs support for optional fields introduced in MongoDB 2.4 + + .. versionchanged:: 2.2 + Added support for read only users + """ + warnings.warn("add_user is deprecated and will be removed in PyMongo " + "4.0. Use db.command with createUser or updateUser " + "instead", DeprecationWarning, stacklevel=2) + if not isinstance(name, string_type): + raise TypeError("name must be an " + "instance of %s" % (string_type.__name__,)) + if password is not None: + if not isinstance(password, string_type): + raise TypeError("password must be an " + "instance of %s" % (string_type.__name__,)) + if len(password) == 0: + raise ValueError("password can't be empty") + if read_only is not None: + read_only = common.validate_boolean('read_only', read_only) + if 'roles' in kwargs: + raise ConfigurationError("Can not use " + "read_only and roles together") + + try: + uinfo = self.command("usersInfo", name, session=session) + # Create the user if not found in uinfo, otherwise update one. + self._create_or_update_user( + (not uinfo["users"]), name, password, read_only, + session=session, **kwargs) + except OperationFailure as exc: + # Unauthorized. Attempt to create the user in case of + # localhost exception. + if exc.code == 13: + self._create_or_update_user( + True, name, password, read_only, session=session, **kwargs) + else: + raise + + def remove_user(self, name, session=None): + """**DEPRECATED**: Remove user `name` from this :class:`Database`. + + User `name` will no longer have permissions to access this + :class:`Database`. + + .. note:: remove_user is deprecated and will be removed in PyMongo + 4.0. Use the dropUser command instead:: + + db.command("dropUser", "user") + + :Parameters: + - `name`: the name of the user to remove + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. Deprecated remove_user. + """ + warnings.warn("remove_user is deprecated and will be removed in " + "PyMongo 4.0. Use db.command with dropUser " + "instead", DeprecationWarning, stacklevel=2) + cmd = SON([("dropUser", name)]) + # Don't send {} as writeConcern. + if self.write_concern.acknowledged and self.write_concern.document: + cmd["writeConcern"] = self.write_concern.document + self.command(cmd, session=session) + + def authenticate(self, name=None, password=None, + source=None, mechanism='DEFAULT', **kwargs): + """**DEPRECATED**: Authenticate to use this database. + + .. warning:: Starting in MongoDB 3.6, calling :meth:`authenticate` + invalidates all existing cursors. It may also leave logical sessions + open on the server for up to 30 minutes until they time out. + + Authentication lasts for the life of the underlying client + instance, or until :meth:`logout` is called. + + Raises :class:`TypeError` if (required) `name`, (optional) `password`, + or (optional) `source` is not an instance of :class:`basestring` + (:class:`str` in python 3). + + .. note:: + - This method authenticates the current connection, and + will also cause all new :class:`~socket.socket` connections + in the underlying client instance to be authenticated automatically. + + - Authenticating more than once on the same database with different + credentials is not supported. You must call :meth:`logout` before + authenticating with new credentials. + + - When sharing a client instance between multiple threads, all + threads will share the authentication. If you need different + authentication profiles for different purposes you must use + distinct client instances. + + :Parameters: + - `name`: the name of the user to authenticate. Optional when + `mechanism` is MONGODB-X509 and the MongoDB server version is + >= 3.4. + - `password` (optional): the password of the user to authenticate. + Not used with GSSAPI or MONGODB-X509 authentication. + - `source` (optional): the database to authenticate on. If not + specified the current database is used. + - `mechanism` (optional): See :data:`~pymongo.auth.MECHANISMS` for + options. If no mechanism is specified, PyMongo automatically uses + MONGODB-CR when connected to a pre-3.0 version of MongoDB, + SCRAM-SHA-1 when connected to MongoDB 3.0 through 3.6, and + negotiates the mechanism to use (SCRAM-SHA-1 or SCRAM-SHA-256) when + connected to MongoDB 4.0+. + - `authMechanismProperties` (optional): Used to specify + authentication mechanism specific options. To specify the service + name for GSSAPI authentication pass + authMechanismProperties='SERVICE_NAME:' + + .. versionchanged:: 3.7 + Added support for SCRAM-SHA-256 with MongoDB 4.0 and later. + + .. versionchanged:: 3.5 + Deprecated. Authenticating multiple users conflicts with support for + logical sessions in MongoDB 3.6. To authenticate as multiple users, + create multiple instances of MongoClient. + + .. versionadded:: 2.8 + Use SCRAM-SHA-1 with MongoDB 3.0 and later. + + .. versionchanged:: 2.5 + Added the `source` and `mechanism` parameters. :meth:`authenticate` + now raises a subclass of :class:`~pymongo.errors.PyMongoError` if + authentication fails due to invalid credentials or configuration + issues. + + .. mongodoc:: authenticate + """ + if name is not None and not isinstance(name, string_type): + raise TypeError("name must be an " + "instance of %s" % (string_type.__name__,)) + if password is not None and not isinstance(password, string_type): + raise TypeError("password must be an " + "instance of %s" % (string_type.__name__,)) + if source is not None and not isinstance(source, string_type): + raise TypeError("source must be an " + "instance of %s" % (string_type.__name__,)) + common.validate_auth_mechanism('mechanism', mechanism) + + validated_options = {} + for option, value in iteritems(kwargs): + normalized, val = common.validate_auth_option(option, value) + validated_options[normalized] = val + + credentials = auth._build_credentials_tuple( + mechanism, + source, + name, + password, + validated_options, + self.name) + + self.client._cache_credentials( + self.name, + credentials, + connect=True) + + return True + + def logout(self): + """**DEPRECATED**: Deauthorize use of this database. + + .. warning:: Starting in MongoDB 3.6, calling :meth:`logout` + invalidates all existing cursors. It may also leave logical sessions + open on the server for up to 30 minutes until they time out. + """ + warnings.warn("Database.logout() is deprecated", + DeprecationWarning, stacklevel=2) + + # Sockets will be deauthenticated as they are used. + self.client._purge_credentials(self.name) + + def dereference(self, dbref, session=None, **kwargs): + """Dereference a :class:`~bson.dbref.DBRef`, getting the + document it points to. + + Raises :class:`TypeError` if `dbref` is not an instance of + :class:`~bson.dbref.DBRef`. Returns a document, or ``None`` if + the reference does not point to a valid document. Raises + :class:`ValueError` if `dbref` has a database specified that + is different from the current database. + + :Parameters: + - `dbref`: the reference + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): any additional keyword arguments + are the same as the arguments to + :meth:`~pymongo.collection.Collection.find`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + if not isinstance(dbref, DBRef): + raise TypeError("cannot dereference a %s" % type(dbref)) + if dbref.database is not None and dbref.database != self.__name: + raise ValueError("trying to dereference a DBRef that points to " + "another database (%r not %r)" % (dbref.database, + self.__name)) + return self[dbref.collection].find_one( + {"_id": dbref.id}, session=session, **kwargs) + + def eval(self, code, *args): + """**DEPRECATED**: Evaluate a JavaScript expression in MongoDB. + + :Parameters: + - `code`: string representation of JavaScript code to be + evaluated + - `args` (optional): additional positional arguments are + passed to the `code` being evaluated + + .. warning:: the eval command is deprecated in MongoDB 3.0 and + will be removed in a future server version. + """ + warnings.warn("Database.eval() is deprecated", + DeprecationWarning, stacklevel=2) + + if not isinstance(code, Code): + code = Code(code) + + result = self.command("$eval", code, args=args) + return result.get("retval", None) + + def __call__(self, *args, **kwargs): + """This is only here so that some API misusages are easier to debug. + """ + raise TypeError("'Database' object is not callable. If you meant to " + "call the '%s' method on a '%s' object it is " + "failing because no such method exists." % ( + self.__name, self.__client.__class__.__name__)) + + +class SystemJS(object): + """**DEPRECATED**: Helper class for dealing with stored JavaScript. + """ + + def __init__(self, database): + """**DEPRECATED**: Get a system js helper for the database `database`. + + SystemJS will be removed in PyMongo 4.0. + """ + warnings.warn("SystemJS is deprecated", + DeprecationWarning, stacklevel=2) + + if not database.write_concern.acknowledged: + database = database.client.get_database( + database.name, write_concern=DEFAULT_WRITE_CONCERN) + # can't just assign it since we've overridden __setattr__ + object.__setattr__(self, "_db", database) + + def __setattr__(self, name, code): + self._db.system.js.replace_one( + {"_id": name}, {"_id": name, "value": Code(code)}, True) + + def __setitem__(self, name, code): + self.__setattr__(name, code) + + def __delattr__(self, name): + self._db.system.js.delete_one({"_id": name}) + + def __delitem__(self, name): + self.__delattr__(name) + + def __getattr__(self, name): + return lambda *args: self._db.eval(Code("function() { " + "return this[name].apply(" + "this, arguments); }", + scope={'name': name}), *args) + + def __getitem__(self, name): + return self.__getattr__(name) + + def list(self): + """Get a list of the names of the functions stored in this database.""" + return [x["_id"] for x in self._db.system.js.find(projection=["_id"])] diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/driver_info.py b/backend/venv/lib/python3.7/site-packages/pymongo/driver_info.py new file mode 100644 index 000000000..1f5235aca --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/driver_info.py @@ -0,0 +1,39 @@ +# Copyright 2018-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Advanced options for MongoDB drivers implemented on top of PyMongo.""" + +from collections import namedtuple + +from bson.py3compat import string_type + + +class DriverInfo(namedtuple('DriverInfo', ['name', 'version', 'platform'])): + """Info about a driver wrapping PyMongo. + + The MongoDB server logs PyMongo's name, version, and platform whenever + PyMongo establishes a connection. A driver implemented on top of PyMongo + can add its own info to this log message. Initialize with three strings + like 'MyDriver', '1.2.3', 'some platform info'. Any of these strings may be + None to accept PyMongo's default. + """ + def __new__(cls, name=None, version=None, platform=None): + self = super(DriverInfo, cls).__new__(cls, name, version, platform) + for name, value in self._asdict().items(): + if value is not None and not isinstance(value, string_type): + raise TypeError("Wrong type for DriverInfo %s option, value " + "must be an instance of %s" % ( + name, string_type.__name__)) + + return self diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/errors.py b/backend/venv/lib/python3.7/site-packages/pymongo/errors.py new file mode 100644 index 000000000..74e646f68 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/errors.py @@ -0,0 +1,249 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Exceptions raised by PyMongo.""" + +import sys + +from bson.errors import * + +try: + from ssl import CertificateError +except ImportError: + from pymongo.ssl_match_hostname import CertificateError + + +class PyMongoError(Exception): + """Base class for all PyMongo exceptions.""" + def __init__(self, message='', error_labels=None): + super(PyMongoError, self).__init__(message) + self._message = message + self._error_labels = set(error_labels or []) + + def has_error_label(self, label): + """Return True if this error contains the given label. + + .. versionadded:: 3.7 + """ + return label in self._error_labels + + def _add_error_label(self, label): + """Add the given label to this error.""" + self._error_labels.add(label) + + def _remove_error_label(self, label): + """Remove the given label from this error.""" + self._error_labels.remove(label) + + def __str__(self): + if sys.version_info[0] == 2 and isinstance(self._message, unicode): + return self._message.encode('utf-8', errors='replace') + return str(self._message) + + +class ProtocolError(PyMongoError): + """Raised for failures related to the wire protocol.""" + + +class ConnectionFailure(PyMongoError): + """Raised when a connection to the database cannot be made or is lost.""" + def __init__(self, message='', error_labels=None): + if error_labels is None: + # Connection errors are transient errors by default. + error_labels = ("TransientTransactionError",) + super(ConnectionFailure, self).__init__( + message, error_labels=error_labels) + + +class AutoReconnect(ConnectionFailure): + """Raised when a connection to the database is lost and an attempt to + auto-reconnect will be made. + + In order to auto-reconnect you must handle this exception, recognizing that + the operation which caused it has not necessarily succeeded. Future + operations will attempt to open a new connection to the database (and + will continue to raise this exception until the first successful + connection is made). + + Subclass of :exc:`~pymongo.errors.ConnectionFailure`. + """ + def __init__(self, message='', errors=None): + super(AutoReconnect, self).__init__(message) + self.errors = self.details = errors or [] + + +class NetworkTimeout(AutoReconnect): + """An operation on an open connection exceeded socketTimeoutMS. + + The remaining connections in the pool stay open. In the case of a write + operation, you cannot know whether it succeeded or failed. + + Subclass of :exc:`~pymongo.errors.AutoReconnect`. + """ + + +class NotMasterError(AutoReconnect): + """The server responded "not master" or "node is recovering". + + These errors result from a query, write, or command. The operation failed + because the client thought it was using the primary but the primary has + stepped down, or the client thought it was using a healthy secondary but + the secondary is stale and trying to recover. + + The client launches a refresh operation on a background thread, to update + its view of the server as soon as possible after throwing this exception. + + Subclass of :exc:`~pymongo.errors.AutoReconnect`. + """ + + +class ServerSelectionTimeoutError(AutoReconnect): + """Thrown when no MongoDB server is available for an operation + + If there is no suitable server for an operation PyMongo tries for + ``serverSelectionTimeoutMS`` (default 30 seconds) to find one, then + throws this exception. For example, it is thrown after attempting an + operation when PyMongo cannot connect to any server, or if you attempt + an insert into a replica set that has no primary and does not elect one + within the timeout window, or if you attempt to query with a Read + Preference that the replica set cannot satisfy. + """ + + +class ConfigurationError(PyMongoError): + """Raised when something is incorrectly configured. + """ + + +class OperationFailure(PyMongoError): + """Raised when a database operation fails. + + .. versionadded:: 2.7 + The :attr:`details` attribute. + """ + + def __init__(self, error, code=None, details=None): + error_labels = None + if details is not None: + error_labels = details.get('errorLabels') + super(OperationFailure, self).__init__( + error, error_labels=error_labels) + self.__code = code + self.__details = details + + @property + def code(self): + """The error code returned by the server, if any. + """ + return self.__code + + @property + def details(self): + """The complete error document returned by the server. + + Depending on the error that occurred, the error document + may include useful information beyond just the error + message. When connected to a mongos the error document + may contain one or more subdocuments if errors occurred + on multiple shards. + """ + return self.__details + + +class CursorNotFound(OperationFailure): + """Raised while iterating query results if the cursor is + invalidated on the server. + + .. versionadded:: 2.7 + """ + + +class ExecutionTimeout(OperationFailure): + """Raised when a database operation times out, exceeding the $maxTimeMS + set in the query or command option. + + .. note:: Requires server version **>= 2.6.0** + + .. versionadded:: 2.7 + """ + + +class WriteConcernError(OperationFailure): + """Base exception type for errors raised due to write concern. + + .. versionadded:: 3.0 + """ + + +class WriteError(OperationFailure): + """Base exception type for errors raised during write operations. + + .. versionadded:: 3.0 + """ + + +class WTimeoutError(WriteConcernError): + """Raised when a database operation times out (i.e. wtimeout expires) + before replication completes. + + With newer versions of MongoDB the `details` attribute may include + write concern fields like 'n', 'updatedExisting', or 'writtenTo'. + + .. versionadded:: 2.7 + """ + + +class DuplicateKeyError(WriteError): + """Raised when an insert or update fails due to a duplicate key error.""" + + +class BulkWriteError(OperationFailure): + """Exception class for bulk write errors. + + .. versionadded:: 2.7 + """ + def __init__(self, results): + super(BulkWriteError, self).__init__( + "batch op errors occurred", 65, results) + + +class InvalidOperation(PyMongoError): + """Raised when a client attempts to perform an invalid operation.""" + + +class InvalidName(PyMongoError): + """Raised when an invalid name is used.""" + + +class CollectionInvalid(PyMongoError): + """Raised when collection validation fails.""" + + +class InvalidURI(ConfigurationError): + """Raised when trying to parse an invalid mongodb URI.""" + + +class ExceededMaxWaiters(Exception): + """Raised when a thread tries to get a connection from a pool and + ``maxPoolSize * waitQueueMultiple`` threads are already waiting. + + .. versionadded:: 2.6 + """ + pass + + +class DocumentTooLarge(InvalidDocument): + """Raised when an encoded document is too large for the connected server. + """ + pass diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/helpers.py b/backend/venv/lib/python3.7/site-packages/pymongo/helpers.py new file mode 100644 index 000000000..347c40dc7 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/helpers.py @@ -0,0 +1,272 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bits and pieces used by the driver that don't really fit elsewhere.""" + +import sys +import traceback + +from bson.py3compat import abc, iteritems, itervalues, string_type +from bson.son import SON +from pymongo import ASCENDING +from pymongo.errors import (CursorNotFound, + DuplicateKeyError, + ExecutionTimeout, + NotMasterError, + OperationFailure, + WriteError, + WriteConcernError, + WTimeoutError) + +# From the Server Discovery and Monitoring spec, the "not master" error codes +# are combined with the "node is recovering" error codes. +_NOT_MASTER_CODES = frozenset([ + 10107, # NotMaster + 13435, # NotMasterNoSlaveOk + 11600, # InterruptedAtShutdown + 11602, # InterruptedDueToReplStateChange + 13436, # NotMasterOrSecondary + 189, # PrimarySteppedDown + 91, # ShutdownInProgress +]) +# From the retryable writes spec. +_RETRYABLE_ERROR_CODES = _NOT_MASTER_CODES | frozenset([ + 7, # HostNotFound + 6, # HostUnreachable + 89, # NetworkTimeout + 9001, # SocketException +]) +_UUNDER = u"_" + + +def _gen_index_name(keys): + """Generate an index name from the set of fields it is over.""" + return _UUNDER.join(["%s_%s" % item for item in keys]) + + +def _index_list(key_or_list, direction=None): + """Helper to generate a list of (key, direction) pairs. + + Takes such a list, or a single key, or a single key and direction. + """ + if direction is not None: + return [(key_or_list, direction)] + else: + if isinstance(key_or_list, string_type): + return [(key_or_list, ASCENDING)] + elif not isinstance(key_or_list, (list, tuple)): + raise TypeError("if no direction is specified, " + "key_or_list must be an instance of list") + return key_or_list + + +def _index_document(index_list): + """Helper to generate an index specifying document. + + Takes a list of (key, direction) pairs. + """ + if isinstance(index_list, abc.Mapping): + raise TypeError("passing a dict to sort/create_index/hint is not " + "allowed - use a list of tuples instead. did you " + "mean %r?" % list(iteritems(index_list))) + elif not isinstance(index_list, (list, tuple)): + raise TypeError("must use a list of (key, direction) pairs, " + "not: " + repr(index_list)) + if not len(index_list): + raise ValueError("key_or_list must not be the empty list") + + index = SON() + for (key, value) in index_list: + if not isinstance(key, string_type): + raise TypeError("first item in each key pair must be a string") + if not isinstance(value, (string_type, int, abc.Mapping)): + raise TypeError("second item in each key pair must be 1, -1, " + "'2d', 'geoHaystack', or another valid MongoDB " + "index specifier.") + index[key] = value + return index + + +def _check_command_response(response, msg=None, allowable_errors=None, + parse_write_concern_error=False): + """Check the response to a command for errors. + """ + if "ok" not in response: + # Server didn't recognize our message as a command. + raise OperationFailure(response.get("$err"), + response.get("code"), + response) + + if parse_write_concern_error and 'writeConcernError' in response: + _raise_write_concern_error(response['writeConcernError']) + + if not response["ok"]: + + details = response + # Mongos returns the error details in a 'raw' object + # for some errors. + if "raw" in response: + for shard in itervalues(response["raw"]): + # Grab the first non-empty raw error from a shard. + if shard.get("errmsg") and not shard.get("ok"): + details = shard + break + + errmsg = details["errmsg"] + if allowable_errors is None or errmsg not in allowable_errors: + + code = details.get("code") + # Server is "not master" or "recovering" + if code in _NOT_MASTER_CODES: + raise NotMasterError(errmsg, response) + elif ("not master" in errmsg + or "node is recovering" in errmsg): + raise NotMasterError(errmsg, response) + + # Server assertion failures + if errmsg == "db assertion failure": + errmsg = ("db assertion failure, assertion: '%s'" % + details.get("assertion", "")) + raise OperationFailure(errmsg, + details.get("assertionCode"), + response) + + # Other errors + # findAndModify with upsert can raise duplicate key error + if code in (11000, 11001, 12582): + raise DuplicateKeyError(errmsg, code, response) + elif code == 50: + raise ExecutionTimeout(errmsg, code, response) + elif code == 43: + raise CursorNotFound(errmsg, code, response) + + msg = msg or "%s" + raise OperationFailure(msg % errmsg, code, response) + + +def _check_gle_response(result): + """Return getlasterror response as a dict, or raise OperationFailure.""" + # Did getlasterror itself fail? + _check_command_response(result) + + if result.get("wtimeout", False): + # MongoDB versions before 1.8.0 return the error message in an "errmsg" + # field. If "errmsg" exists "err" will also exist set to None, so we + # have to check for "errmsg" first. + raise WTimeoutError(result.get("errmsg", result.get("err")), + result.get("code"), + result) + + error_msg = result.get("err", "") + if error_msg is None: + return result + + if error_msg.startswith("not master"): + raise NotMasterError(error_msg, result) + + details = result + + # mongos returns the error code in an error object for some errors. + if "errObjects" in result: + for errobj in result["errObjects"]: + if errobj.get("err") == error_msg: + details = errobj + break + + code = details.get("code") + if code in (11000, 11001, 12582): + raise DuplicateKeyError(details["err"], code, result) + raise OperationFailure(details["err"], code, result) + + +def _raise_last_write_error(write_errors): + # If the last batch had multiple errors only report + # the last error to emulate continue_on_error. + error = write_errors[-1] + if error.get("code") == 11000: + raise DuplicateKeyError(error.get("errmsg"), 11000, error) + raise WriteError(error.get("errmsg"), error.get("code"), error) + + +def _raise_write_concern_error(error): + if "errInfo" in error and error["errInfo"].get('wtimeout'): + # Make sure we raise WTimeoutError + raise WTimeoutError( + error.get("errmsg"), error.get("code"), error) + raise WriteConcernError( + error.get("errmsg"), error.get("code"), error) + + +def _check_write_command_response(result): + """Backward compatibility helper for write command error handling. + """ + # Prefer write errors over write concern errors + write_errors = result.get("writeErrors") + if write_errors: + _raise_last_write_error(write_errors) + + error = result.get("writeConcernError") + if error: + _raise_write_concern_error(error) + + +def _raise_last_error(bulk_write_result): + """Backward compatibility helper for insert error handling. + """ + # Prefer write errors over write concern errors + write_errors = bulk_write_result.get("writeErrors") + if write_errors: + _raise_last_write_error(write_errors) + + _raise_write_concern_error(bulk_write_result["writeConcernErrors"][-1]) + + +def _fields_list_to_dict(fields, option_name): + """Takes a sequence of field names and returns a matching dictionary. + + ["a", "b"] becomes {"a": 1, "b": 1} + + and + + ["a.b.c", "d", "a.c"] becomes {"a.b.c": 1, "d": 1, "a.c": 1} + """ + if isinstance(fields, abc.Mapping): + return fields + + if isinstance(fields, (abc.Sequence, abc.Set)): + if not all(isinstance(field, string_type) for field in fields): + raise TypeError("%s must be a list of key names, each an " + "instance of %s" % (option_name, + string_type.__name__)) + return dict.fromkeys(fields, 1) + + raise TypeError("%s must be a mapping or " + "list of key names" % (option_name,)) + + +def _handle_exception(): + """Print exceptions raised by subscribers to stderr.""" + # Heavily influenced by logging.Handler.handleError. + + # See note here: + # https://docs.python.org/3.4/library/sys.html#sys.__stderr__ + if sys.stderr: + einfo = sys.exc_info() + try: + traceback.print_exception(einfo[0], einfo[1], einfo[2], + None, sys.stderr) + except IOError: + pass + finally: + del einfo diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/ismaster.py b/backend/venv/lib/python3.7/site-packages/pymongo/ismaster.py new file mode 100644 index 000000000..e723ff0a9 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/ismaster.py @@ -0,0 +1,158 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Parse a response to the 'ismaster' command.""" + +import itertools + +from bson.py3compat import imap +from pymongo import common +from pymongo.server_type import SERVER_TYPE + + +def _get_server_type(doc): + """Determine the server type from an ismaster response.""" + if not doc.get('ok'): + return SERVER_TYPE.Unknown + + if doc.get('isreplicaset'): + return SERVER_TYPE.RSGhost + elif doc.get('setName'): + if doc.get('hidden'): + return SERVER_TYPE.RSOther + elif doc.get('ismaster'): + return SERVER_TYPE.RSPrimary + elif doc.get('secondary'): + return SERVER_TYPE.RSSecondary + elif doc.get('arbiterOnly'): + return SERVER_TYPE.RSArbiter + else: + return SERVER_TYPE.RSOther + elif doc.get('msg') == 'isdbgrid': + return SERVER_TYPE.Mongos + else: + return SERVER_TYPE.Standalone + + +class IsMaster(object): + __slots__ = ('_doc', '_server_type', '_is_writable', '_is_readable') + + def __init__(self, doc): + """Parse an ismaster response from the server.""" + self._server_type = _get_server_type(doc) + self._doc = doc + self._is_writable = self._server_type in ( + SERVER_TYPE.RSPrimary, + SERVER_TYPE.Standalone, + SERVER_TYPE.Mongos) + + self._is_readable = ( + self.server_type == SERVER_TYPE.RSSecondary + or self._is_writable) + + @property + def document(self): + """The complete ismaster command response document. + + .. versionadded:: 3.4 + """ + return self._doc.copy() + + @property + def server_type(self): + return self._server_type + + @property + def all_hosts(self): + """List of hosts, passives, and arbiters known to this server.""" + return set(imap(common.clean_node, itertools.chain( + self._doc.get('hosts', []), + self._doc.get('passives', []), + self._doc.get('arbiters', [])))) + + @property + def tags(self): + """Replica set member tags or empty dict.""" + return self._doc.get('tags', {}) + + @property + def primary(self): + """This server's opinion about who the primary is, or None.""" + if self._doc.get('primary'): + return common.partition_node(self._doc['primary']) + else: + return None + + @property + def replica_set_name(self): + """Replica set name or None.""" + return self._doc.get('setName') + + @property + def max_bson_size(self): + return self._doc.get('maxBsonObjectSize', common.MAX_BSON_SIZE) + + @property + def max_message_size(self): + return self._doc.get('maxMessageSizeBytes', 2 * self.max_bson_size) + + @property + def max_write_batch_size(self): + return self._doc.get('maxWriteBatchSize', common.MAX_WRITE_BATCH_SIZE) + + @property + def min_wire_version(self): + return self._doc.get('minWireVersion', common.MIN_WIRE_VERSION) + + @property + def max_wire_version(self): + return self._doc.get('maxWireVersion', common.MAX_WIRE_VERSION) + + @property + def set_version(self): + return self._doc.get('setVersion') + + @property + def election_id(self): + return self._doc.get('electionId') + + @property + def cluster_time(self): + return self._doc.get('$clusterTime') + + @property + def logical_session_timeout_minutes(self): + return self._doc.get('logicalSessionTimeoutMinutes') + + @property + def is_writable(self): + return self._is_writable + + @property + def is_readable(self): + return self._is_readable + + @property + def me(self): + me = self._doc.get('me') + if me: + return common.clean_node(me) + + @property + def last_write_date(self): + return self._doc.get('lastWrite', {}).get('lastWriteDate') + + @property + def compressors(self): + return self._doc.get('compression') diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/max_staleness_selectors.py b/backend/venv/lib/python3.7/site-packages/pymongo/max_staleness_selectors.py new file mode 100644 index 000000000..6bc2fe723 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/max_staleness_selectors.py @@ -0,0 +1,116 @@ +# Copyright 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Criteria to select ServerDescriptions based on maxStalenessSeconds. + +The Max Staleness Spec says: When there is a known primary P, +a secondary S's staleness is estimated with this formula: + + (S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + + heartbeatFrequencyMS + +When there is no known primary, a secondary S's staleness is estimated with: + + SMax.lastWriteDate - S.lastWriteDate + heartbeatFrequencyMS + +where "SMax" is the secondary with the greatest lastWriteDate. +""" + +from pymongo.errors import ConfigurationError +from pymongo.server_type import SERVER_TYPE + + +# Constant defined in Max Staleness Spec: An idle primary writes a no-op every +# 10 seconds to refresh secondaries' lastWriteDate values. +IDLE_WRITE_PERIOD = 10 +SMALLEST_MAX_STALENESS = 90 + + +def _validate_max_staleness(max_staleness, + heartbeat_frequency): + # We checked for max staleness -1 before this, it must be positive here. + if max_staleness < heartbeat_frequency + IDLE_WRITE_PERIOD: + raise ConfigurationError( + "maxStalenessSeconds must be at least heartbeatFrequencyMS +" + " %d seconds. maxStalenessSeconds is set to %d," + " heartbeatFrequencyMS is set to %d." % ( + IDLE_WRITE_PERIOD, max_staleness, heartbeat_frequency * 1000)) + + if max_staleness < SMALLEST_MAX_STALENESS: + raise ConfigurationError( + "maxStalenessSeconds must be at least %d. " + "maxStalenessSeconds is set to %d." % ( + SMALLEST_MAX_STALENESS, max_staleness)) + + +def _with_primary(max_staleness, selection): + """Apply max_staleness, in seconds, to a Selection with a known primary.""" + primary = selection.primary + sds = [] + + for s in selection.server_descriptions: + if s.server_type == SERVER_TYPE.RSSecondary: + # See max-staleness.rst for explanation of this formula. + staleness = ( + (s.last_update_time - s.last_write_date) - + (primary.last_update_time - primary.last_write_date) + + selection.heartbeat_frequency) + + if staleness <= max_staleness: + sds.append(s) + else: + sds.append(s) + + return selection.with_server_descriptions(sds) + + +def _no_primary(max_staleness, selection): + """Apply max_staleness, in seconds, to a Selection with no known primary.""" + # Secondary that's replicated the most recent writes. + smax = selection.secondary_with_max_last_write_date() + if not smax: + # No secondaries and no primary, short-circuit out of here. + return selection.with_server_descriptions([]) + + sds = [] + + for s in selection.server_descriptions: + if s.server_type == SERVER_TYPE.RSSecondary: + # See max-staleness.rst for explanation of this formula. + staleness = (smax.last_write_date - + s.last_write_date + + selection.heartbeat_frequency) + + if staleness <= max_staleness: + sds.append(s) + else: + sds.append(s) + + return selection.with_server_descriptions(sds) + + +def select(max_staleness, selection): + """Apply max_staleness, in seconds, to a Selection.""" + if max_staleness == -1: + return selection + + # Server Selection Spec: If the TopologyType is ReplicaSetWithPrimary or + # ReplicaSetNoPrimary, a client MUST raise an error if maxStaleness < + # heartbeatFrequency + IDLE_WRITE_PERIOD, or if maxStaleness < 90. + _validate_max_staleness(max_staleness, selection.heartbeat_frequency) + + if selection.primary: + return _with_primary(max_staleness, selection) + else: + return _no_primary(max_staleness, selection) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/message.py b/backend/venv/lib/python3.7/site-packages/pymongo/message.py new file mode 100644 index 000000000..29ec24fa0 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/message.py @@ -0,0 +1,1555 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for creating `messages +`_ to be sent to +MongoDB. + +.. note:: This module is for internal use and is generally not needed by + application developers. +""" + +import datetime +import random +import struct + +import bson +from bson import (CodecOptions, + _dict_to_bson, + _make_c_string) +from bson.codec_options import DEFAULT_CODEC_OPTIONS +from bson.py3compat import b, StringIO +from bson.son import SON + +try: + from pymongo import _cmessage + _use_c = True +except ImportError: + _use_c = False +from pymongo.errors import (ConfigurationError, + CursorNotFound, + DocumentTooLarge, + ExecutionTimeout, + InvalidOperation, + NotMasterError, + OperationFailure, + ProtocolError) +from pymongo.read_concern import DEFAULT_READ_CONCERN +from pymongo.read_preferences import ReadPreference + + +MAX_INT32 = 2147483647 +MIN_INT32 = -2147483648 + +# Overhead allowed for encoded command documents. +_COMMAND_OVERHEAD = 16382 + +_INSERT = 0 +_UPDATE = 1 +_DELETE = 2 + +_EMPTY = b'' +_BSONOBJ = b'\x03' +_ZERO_8 = b'\x00' +_ZERO_16 = b'\x00\x00' +_ZERO_32 = b'\x00\x00\x00\x00' +_ZERO_64 = b'\x00\x00\x00\x00\x00\x00\x00\x00' +_SKIPLIM = b'\x00\x00\x00\x00\xff\xff\xff\xff' +_OP_MAP = { + _INSERT: b'\x04documents\x00\x00\x00\x00\x00', + _UPDATE: b'\x04updates\x00\x00\x00\x00\x00', + _DELETE: b'\x04deletes\x00\x00\x00\x00\x00', +} +_FIELD_MAP = { + 'insert': 'documents', + 'update': 'updates', + 'delete': 'deletes' +} + +_UJOIN = u"%s.%s" + +_UNICODE_REPLACE_CODEC_OPTIONS = CodecOptions( + unicode_decode_error_handler='replace') + + +def _randint(): + """Generate a pseudo random 32 bit integer.""" + return random.randint(MIN_INT32, MAX_INT32) + + +def _maybe_add_read_preference(spec, read_preference): + """Add $readPreference to spec when appropriate.""" + mode = read_preference.mode + tag_sets = read_preference.tag_sets + max_staleness = read_preference.max_staleness + # Only add $readPreference if it's something other than primary to avoid + # problems with mongos versions that don't support read preferences. Also, + # for maximum backwards compatibility, don't add $readPreference for + # secondaryPreferred unless tags or maxStalenessSeconds are in use (setting + # the slaveOkay bit has the same effect). + if mode and ( + mode != ReadPreference.SECONDARY_PREFERRED.mode + or tag_sets != [{}] + or max_staleness != -1): + + if "$query" not in spec: + spec = SON([("$query", spec)]) + spec["$readPreference"] = read_preference.document + return spec + + +def _convert_exception(exception): + """Convert an Exception into a failure document for publishing.""" + return {'errmsg': str(exception), + 'errtype': exception.__class__.__name__} + + +def _convert_write_result(operation, command, result): + """Convert a legacy write result to write commmand format.""" + + # Based on _merge_legacy from bulk.py + affected = result.get("n", 0) + res = {"ok": 1, "n": affected} + errmsg = result.get("errmsg", result.get("err", "")) + if errmsg: + # The write was successful on at least the primary so don't return. + if result.get("wtimeout"): + res["writeConcernError"] = {"errmsg": errmsg, + "code": 64, + "errInfo": {"wtimeout": True}} + else: + # The write failed. + error = {"index": 0, + "code": result.get("code", 8), + "errmsg": errmsg} + if "errInfo" in result: + error["errInfo"] = result["errInfo"] + res["writeErrors"] = [error] + return res + if operation == "insert": + # GLE result for insert is always 0 in most MongoDB versions. + res["n"] = len(command['documents']) + elif operation == "update": + if "upserted" in result: + res["upserted"] = [{"index": 0, "_id": result["upserted"]}] + # Versions of MongoDB before 2.6 don't return the _id for an + # upsert if _id is not an ObjectId. + elif result.get("updatedExisting") is False and affected == 1: + # If _id is in both the update document *and* the query spec + # the update document _id takes precedence. + update = command['updates'][0] + _id = update["u"].get("_id", update["q"].get("_id")) + res["upserted"] = [{"index": 0, "_id": _id}] + return res + + +_OPTIONS = SON([ + ('tailable', 2), + ('oplogReplay', 8), + ('noCursorTimeout', 16), + ('awaitData', 32), + ('allowPartialResults', 128)]) + + +_MODIFIERS = SON([ + ('$query', 'filter'), + ('$orderby', 'sort'), + ('$hint', 'hint'), + ('$comment', 'comment'), + ('$maxScan', 'maxScan'), + ('$maxTimeMS', 'maxTimeMS'), + ('$max', 'max'), + ('$min', 'min'), + ('$returnKey', 'returnKey'), + ('$showRecordId', 'showRecordId'), + ('$showDiskLoc', 'showRecordId'), # <= MongoDb 3.0 + ('$snapshot', 'snapshot')]) + + +def _gen_find_command(coll, spec, projection, skip, limit, batch_size, options, + read_concern, collation=None, session=None): + """Generate a find command document.""" + cmd = SON([('find', coll)]) + if '$query' in spec: + cmd.update([(_MODIFIERS[key], val) if key in _MODIFIERS else (key, val) + for key, val in spec.items()]) + if '$explain' in cmd: + cmd.pop('$explain') + if '$readPreference' in cmd: + cmd.pop('$readPreference') + else: + cmd['filter'] = spec + + if projection: + cmd['projection'] = projection + if skip: + cmd['skip'] = skip + if limit: + cmd['limit'] = abs(limit) + if limit < 0: + cmd['singleBatch'] = True + if batch_size: + cmd['batchSize'] = batch_size + if read_concern.level and not (session and session._in_transaction): + cmd['readConcern'] = read_concern.document + if collation: + cmd['collation'] = collation + if options: + cmd.update([(opt, True) + for opt, val in _OPTIONS.items() + if options & val]) + return cmd + + +def _gen_get_more_command(cursor_id, coll, batch_size, max_await_time_ms): + """Generate a getMore command document.""" + cmd = SON([('getMore', cursor_id), + ('collection', coll)]) + if batch_size: + cmd['batchSize'] = batch_size + if max_await_time_ms is not None: + cmd['maxTimeMS'] = max_await_time_ms + return cmd + + +class _Query(object): + """A query operation.""" + + __slots__ = ('flags', 'db', 'coll', 'ntoskip', 'spec', + 'fields', 'codec_options', 'read_preference', 'limit', + 'batch_size', 'name', 'read_concern', 'collation', + 'session', 'client', '_as_command') + + def __init__(self, flags, db, coll, ntoskip, spec, fields, + codec_options, read_preference, limit, + batch_size, read_concern, collation, session, client): + self.flags = flags + self.db = db + self.coll = coll + self.ntoskip = ntoskip + self.spec = spec + self.fields = fields + self.codec_options = codec_options + self.read_preference = read_preference + self.read_concern = read_concern + self.limit = limit + self.batch_size = batch_size + self.collation = collation + self.session = session + self.client = client + self.name = 'find' + self._as_command = None + + def use_command(self, sock_info, exhaust): + use_find_cmd = False + if sock_info.max_wire_version >= 4: + if not exhaust: + use_find_cmd = True + elif not self.read_concern.ok_for_legacy: + raise ConfigurationError( + 'read concern level of %s is not valid ' + 'with a max wire version of %d.' + % (self.read_concern.level, + sock_info.max_wire_version)) + + if sock_info.max_wire_version < 5 and self.collation is not None: + raise ConfigurationError( + 'Specifying a collation is unsupported with a max wire ' + 'version of %d.' % (sock_info.max_wire_version,)) + + sock_info.validate_session(self.client, self.session) + + return use_find_cmd + + def as_command(self, sock_info): + """Return a find command document for this query.""" + # We use the command twice: on the wire and for command monitoring. + # Generate it once, for speed and to avoid repeating side-effects. + if self._as_command is not None: + return self._as_command + + explain = '$explain' in self.spec + cmd = _gen_find_command( + self.coll, self.spec, self.fields, self.ntoskip, + self.limit, self.batch_size, self.flags, self.read_concern, + self.collation, self.session) + if explain: + self.name = 'explain' + cmd = SON([('explain', cmd)]) + session = self.session + if session: + session._apply_to(cmd, False, self.read_preference, sock_info) + # Explain does not support readConcern. + if (not explain and session.options.causal_consistency + and session.operation_time is not None + and not session._in_transaction): + cmd.setdefault( + 'readConcern', {})[ + 'afterClusterTime'] = session.operation_time + sock_info.send_cluster_time(cmd, session, self.client) + self._as_command = cmd, self.db + return self._as_command + + def get_message(self, set_slave_ok, sock_info, use_cmd=False): + """Get a query message, possibly setting the slaveOk bit.""" + if set_slave_ok: + # Set the slaveOk bit. + flags = self.flags | 4 + else: + flags = self.flags + + ns = _UJOIN % (self.db, self.coll) + spec = self.spec + + if use_cmd: + spec = self.as_command(sock_info)[0] + if sock_info.op_msg_enabled: + request_id, msg, size, _ = _op_msg( + 0, spec, self.db, self.read_preference, + set_slave_ok, False, self.codec_options, + ctx=sock_info.compression_context) + return request_id, msg, size + ns = _UJOIN % (self.db, "$cmd") + ntoreturn = -1 # All DB commands return 1 document + else: + # OP_QUERY treats ntoreturn of -1 and 1 the same, return + # one document and close the cursor. We have to use 2 for + # batch size if 1 is specified. + ntoreturn = self.batch_size == 1 and 2 or self.batch_size + if self.limit: + if ntoreturn: + ntoreturn = min(self.limit, ntoreturn) + else: + ntoreturn = self.limit + + if sock_info.is_mongos: + spec = _maybe_add_read_preference(spec, + self.read_preference) + + return query(flags, ns, self.ntoskip, ntoreturn, + spec, None if use_cmd else self.fields, + self.codec_options, ctx=sock_info.compression_context) + + +class _GetMore(object): + """A getmore operation.""" + + __slots__ = ('db', 'coll', 'ntoreturn', 'cursor_id', 'max_await_time_ms', + 'codec_options', 'read_preference', 'session', 'client', + '_as_command') + + name = 'getMore' + + def __init__(self, db, coll, ntoreturn, cursor_id, codec_options, + read_preference, session, client, max_await_time_ms=None): + self.db = db + self.coll = coll + self.ntoreturn = ntoreturn + self.cursor_id = cursor_id + self.codec_options = codec_options + self.read_preference = read_preference + self.session = session + self.client = client + self.max_await_time_ms = max_await_time_ms + self._as_command = None + + def use_command(self, sock_info, exhaust): + sock_info.validate_session(self.client, self.session) + return sock_info.max_wire_version >= 4 and not exhaust + + def as_command(self, sock_info): + """Return a getMore command document for this query.""" + # See _Query.as_command for an explanation of this caching. + if self._as_command is not None: + return self._as_command + + cmd = _gen_get_more_command(self.cursor_id, self.coll, + self.ntoreturn, + self.max_await_time_ms) + + if self.session: + self.session._apply_to(cmd, False, self.read_preference, sock_info) + sock_info.send_cluster_time(cmd, self.session, self.client) + self._as_command = cmd, self.db + return self._as_command + + def get_message(self, dummy0, sock_info, use_cmd=False): + """Get a getmore message.""" + + ns = _UJOIN % (self.db, self.coll) + ctx = sock_info.compression_context + + if use_cmd: + spec = self.as_command(sock_info)[0] + if sock_info.op_msg_enabled: + request_id, msg, size, _ = _op_msg( + 0, spec, self.db, ReadPreference.PRIMARY, + False, False, self.codec_options, + ctx=sock_info.compression_context) + return request_id, msg, size + ns = _UJOIN % (self.db, "$cmd") + return query(0, ns, 0, -1, spec, None, self.codec_options, ctx=ctx) + + return get_more(ns, self.ntoreturn, self.cursor_id, ctx) + + +# TODO: Use OP_MSG once the server is able to respond with document streams. +class _RawBatchQuery(_Query): + def use_command(self, socket_info, exhaust): + # Compatibility checks. + super(_RawBatchQuery, self).use_command(socket_info, exhaust) + + return False + + def get_message(self, set_slave_ok, sock_info, use_cmd=False): + # Always pass False for use_cmd. + return super(_RawBatchQuery, self).get_message( + set_slave_ok, sock_info, False) + + +class _RawBatchGetMore(_GetMore): + def use_command(self, socket_info, exhaust): + return False + + def get_message(self, set_slave_ok, sock_info, use_cmd=False): + # Always pass False for use_cmd. + return super(_RawBatchGetMore, self).get_message( + set_slave_ok, sock_info, False) + + +class _CursorAddress(tuple): + """The server address (host, port) of a cursor, with namespace property.""" + + def __new__(cls, address, namespace): + self = tuple.__new__(cls, address) + self.__namespace = namespace + return self + + @property + def namespace(self): + """The namespace this cursor.""" + return self.__namespace + + def __hash__(self): + # Two _CursorAddress instances with different namespaces + # must not hash the same. + return (self + (self.__namespace,)).__hash__() + + def __eq__(self, other): + if isinstance(other, _CursorAddress): + return (tuple(self) == tuple(other) + and self.namespace == other.namespace) + return NotImplemented + + def __ne__(self, other): + return not self == other + + +_pack_compression_header = struct.Struct(" ctx.max_bson_size) + + message_length += encoded_length + if message_length < ctx.max_message_size and not too_large: + data.write(encoded) + to_send.append(doc) + has_docs = True + continue + + if has_docs: + # We have enough data, send this message. + try: + if compress: + rid, msg = None, data.getvalue() + else: + rid, msg = _insert_message(data.getvalue(), send_safe) + ctx.legacy_bulk_insert( + rid, msg, 0, send_safe, to_send, compress) + # Exception type could be OperationFailure or a subtype + # (e.g. DuplicateKeyError) + except OperationFailure as exc: + # Like it says, continue on error... + if continue_on_error: + # Store exception details to re-raise after the final batch. + last_error = exc + # With unacknowledged writes just return at the first error. + elif not safe: + return + # With acknowledged writes raise immediately. + else: + raise + + if too_large: + _raise_document_too_large( + "insert", encoded_length, ctx.max_bson_size) + + message_length = begin_loc + encoded_length + data.seek(begin_loc) + data.truncate() + data.write(encoded) + to_send = [doc] + + if not has_docs: + raise InvalidOperation("cannot do an empty bulk insert") + + if compress: + request_id, msg = None, data.getvalue() + else: + request_id, msg = _insert_message(data.getvalue(), safe) + ctx.legacy_bulk_insert(request_id, msg, 0, safe, to_send, compress) + + # Re-raise any exception stored due to continue_on_error + if last_error is not None: + raise last_error +if _use_c: + _do_batched_insert = _cmessage._do_batched_insert + +# OP_MSG ------------------------------------------------------------- + + +_OP_MSG_MAP = { + _INSERT: b'documents\x00', + _UPDATE: b'updates\x00', + _DELETE: b'deletes\x00', +} + + +def _batched_op_msg_impl( + operation, command, docs, check_keys, ack, opts, ctx, buf): + """Create a batched OP_MSG write.""" + max_bson_size = ctx.max_bson_size + max_write_batch_size = ctx.max_write_batch_size + max_message_size = ctx.max_message_size + + flags = b"\x00\x00\x00\x00" if ack else b"\x02\x00\x00\x00" + # Flags + buf.write(flags) + + # Type 0 Section + buf.write(b"\x00") + buf.write(_dict_to_bson(command, False, opts)) + + # Type 1 Section + buf.write(b"\x01") + size_location = buf.tell() + # Save space for size + buf.write(b"\x00\x00\x00\x00") + try: + buf.write(_OP_MSG_MAP[operation]) + except KeyError: + raise InvalidOperation('Unknown command') + + if operation in (_UPDATE, _DELETE): + check_keys = False + + to_send = [] + idx = 0 + for doc in docs: + # Encode the current operation + value = _dict_to_bson(doc, check_keys, opts) + doc_length = len(value) + new_message_size = buf.tell() + doc_length + # Does first document exceed max_message_size? + doc_too_large = (idx == 0 and (new_message_size > max_message_size)) + # When OP_MSG is used unacknowleged we have to check + # document size client side or applications won't be notified. + # Otherwise we let the server deal with documents that are too large + # since ordered=False causes those documents to be skipped instead of + # halting the bulk write operation. + unacked_doc_too_large = (not ack and (doc_length > max_bson_size)) + if doc_too_large or unacked_doc_too_large: + write_op = list(_FIELD_MAP.keys())[operation] + _raise_document_too_large( + write_op, len(value), max_bson_size) + # We have enough data, return this batch. + if new_message_size > max_message_size: + break + buf.write(value) + to_send.append(doc) + idx += 1 + # We have enough documents, return this batch. + if idx == max_write_batch_size: + break + + # Write type 1 section size + length = buf.tell() + buf.seek(size_location) + buf.write(_pack_int(length - size_location)) + + return to_send, length + + +def _encode_batched_op_msg( + operation, command, docs, check_keys, ack, opts, ctx): + """Encode the next batched insert, update, or delete operation + as OP_MSG. + """ + buf = StringIO() + + to_send, _ = _batched_op_msg_impl( + operation, command, docs, check_keys, ack, opts, ctx, buf) + return buf.getvalue(), to_send +if _use_c: + _encode_batched_op_msg = _cmessage._encode_batched_op_msg + + +def _batched_op_msg_compressed( + operation, command, docs, check_keys, ack, opts, ctx): + """Create the next batched insert, update, or delete operation + with OP_MSG, compressed. + """ + data, to_send = _encode_batched_op_msg( + operation, command, docs, check_keys, ack, opts, ctx) + + request_id, msg = _compress( + 2013, + data, + ctx.sock_info.compression_context) + return request_id, msg, to_send + + +def _batched_op_msg( + operation, command, docs, check_keys, ack, opts, ctx): + """OP_MSG implementation entry point.""" + buf = StringIO() + + # Save space for message length and request id + buf.write(_ZERO_64) + # responseTo, opCode + buf.write(b"\x00\x00\x00\x00\xdd\x07\x00\x00") + + to_send, length = _batched_op_msg_impl( + operation, command, docs, check_keys, ack, opts, ctx, buf) + + # Header - request id and message length + buf.seek(4) + request_id = _randint() + buf.write(_pack_int(request_id)) + buf.seek(0) + buf.write(_pack_int(length)) + + return request_id, buf.getvalue(), to_send +if _use_c: + _batched_op_msg = _cmessage._batched_op_msg + + +def _do_batched_op_msg( + namespace, operation, command, docs, check_keys, opts, ctx): + """Create the next batched insert, update, or delete operation + using OP_MSG. + """ + command['$db'] = namespace.split('.', 1)[0] + if 'writeConcern' in command: + ack = bool(command['writeConcern'].get('w', 1)) + else: + ack = True + if ctx.sock_info.compression_context: + return _batched_op_msg_compressed( + operation, command, docs, check_keys, ack, opts, ctx) + return _batched_op_msg( + operation, command, docs, check_keys, ack, opts, ctx) + + +# End OP_MSG ----------------------------------------------------- + + +def _batched_write_command_compressed( + namespace, operation, command, docs, check_keys, opts, ctx): + """Create the next batched insert, update, or delete command, compressed. + """ + data, to_send = _encode_batched_write_command( + namespace, operation, command, docs, check_keys, opts, ctx) + + request_id, msg = _compress( + 2004, + data, + ctx.sock_info.compression_context) + return request_id, msg, to_send + + +def _encode_batched_write_command( + namespace, operation, command, docs, check_keys, opts, ctx): + """Encode the next batched insert, update, or delete command. + """ + buf = StringIO() + + to_send, _ = _batched_write_command_impl( + namespace, operation, command, docs, check_keys, opts, ctx, buf) + return buf.getvalue(), to_send +if _use_c: + _encode_batched_write_command = _cmessage._encode_batched_write_command + + +def _batched_write_command( + namespace, operation, command, docs, check_keys, opts, ctx): + """Create the next batched insert, update, or delete command. + """ + buf = StringIO() + + # Save space for message length and request id + buf.write(_ZERO_64) + # responseTo, opCode + buf.write(b"\x00\x00\x00\x00\xd4\x07\x00\x00") + + # Write OP_QUERY write command + to_send, length = _batched_write_command_impl( + namespace, operation, command, docs, check_keys, opts, ctx, buf) + + # Header - request id and message length + buf.seek(4) + request_id = _randint() + buf.write(_pack_int(request_id)) + buf.seek(0) + buf.write(_pack_int(length)) + + return request_id, buf.getvalue(), to_send +if _use_c: + _batched_write_command = _cmessage._batched_write_command + + +def _do_batched_write_command( + namespace, operation, command, docs, check_keys, opts, ctx): + """Batched write commands entry point.""" + if ctx.sock_info.compression_context: + return _batched_write_command_compressed( + namespace, operation, command, docs, check_keys, opts, ctx) + return _batched_write_command( + namespace, operation, command, docs, check_keys, opts, ctx) + + +def _do_bulk_write_command( + namespace, operation, command, docs, check_keys, opts, ctx): + """Bulk write commands entry point.""" + if ctx.sock_info.max_wire_version > 5: + return _do_batched_op_msg( + namespace, operation, command, docs, check_keys, opts, ctx) + return _do_batched_write_command( + namespace, operation, command, docs, check_keys, opts, ctx) + + +def _batched_write_command_impl( + namespace, operation, command, docs, check_keys, opts, ctx, buf): + """Create a batched OP_QUERY write command.""" + max_bson_size = ctx.max_bson_size + max_write_batch_size = ctx.max_write_batch_size + # Max BSON object size + 16k - 2 bytes for ending NUL bytes. + # Server guarantees there is enough room: SERVER-10643. + max_cmd_size = max_bson_size + _COMMAND_OVERHEAD + + # No options + buf.write(_ZERO_32) + # Namespace as C string + buf.write(b(namespace)) + buf.write(_ZERO_8) + # Skip: 0, Limit: -1 + buf.write(_SKIPLIM) + + # Where to write command document length + command_start = buf.tell() + buf.write(bson.BSON.encode(command)) + + # Start of payload + buf.seek(-1, 2) + # Work around some Jython weirdness. + buf.truncate() + try: + buf.write(_OP_MAP[operation]) + except KeyError: + raise InvalidOperation('Unknown command') + + if operation in (_UPDATE, _DELETE): + check_keys = False + + # Where to write list document length + list_start = buf.tell() - 4 + to_send = [] + idx = 0 + for doc in docs: + # Encode the current operation + key = b(str(idx)) + value = bson.BSON.encode(doc, check_keys, opts) + # Is there enough room to add this document? max_cmd_size accounts for + # the two trailing null bytes. + enough_data = (buf.tell() + len(key) + len(value)) >= max_cmd_size + enough_documents = (idx >= max_write_batch_size) + if enough_data or enough_documents: + if not idx: + write_op = list(_FIELD_MAP.keys())[operation] + _raise_document_too_large( + write_op, len(value), max_bson_size) + break + buf.write(_BSONOBJ) + buf.write(key) + buf.write(_ZERO_8) + buf.write(value) + to_send.append(doc) + idx += 1 + + # Finalize the current OP_QUERY message. + # Close list and command documents + buf.write(_ZERO_16) + + # Write document lengths and request id + length = buf.tell() + buf.seek(list_start) + buf.write(_pack_int(length - list_start - 1)) + buf.seek(command_start) + buf.write(_pack_int(length - command_start)) + + return to_send, length + + +class _OpReply(object): + """A MongoDB OP_REPLY response message.""" + + __slots__ = ("flags", "cursor_id", "number_returned", "documents") + + UNPACK_FROM = struct.Struct("1 section") + + # Convert Python 3 memoryview to bytes. Note we should call + # memoryview.tobytes() if we start using memoryview in Python 2.7. + payload_document = bytes(msg[5:]) + return cls(flags, payload_document) + + +_UNPACK_REPLY = { + _OpReply.OP_CODE: _OpReply.unpack, + _OpMsg.OP_CODE: _OpMsg.unpack, +} + + +def _first_batch(sock_info, db, coll, query, ntoreturn, + slave_ok, codec_options, read_preference, cmd, listeners): + """Simple query helper for retrieving a first (and possibly only) batch.""" + query = _Query( + 0, db, coll, 0, query, None, codec_options, + read_preference, ntoreturn, 0, DEFAULT_READ_CONCERN, None, None, + None) + + name = next(iter(cmd)) + publish = listeners.enabled_for_commands + if publish: + start = datetime.datetime.now() + + request_id, msg, max_doc_size = query.get_message(slave_ok, sock_info) + + if publish: + encoding_duration = datetime.datetime.now() - start + listeners.publish_command_start( + cmd, db, request_id, sock_info.address) + start = datetime.datetime.now() + + sock_info.send_message(msg, max_doc_size) + reply = sock_info.receive_message(request_id) + try: + docs = reply.unpack_response(None, codec_options) + except Exception as exc: + if publish: + duration = (datetime.datetime.now() - start) + encoding_duration + if isinstance(exc, (NotMasterError, OperationFailure)): + failure = exc.details + else: + failure = _convert_exception(exc) + listeners.publish_command_failure( + duration, failure, name, request_id, sock_info.address) + raise + # listIndexes + if 'cursor' in cmd: + result = { + u'cursor': { + u'firstBatch': docs, + u'id': reply.cursor_id, + u'ns': u'%s.%s' % (db, coll) + }, + u'ok': 1.0 + } + # fsyncUnlock, currentOp + else: + result = docs[0] if docs else {} + result[u'ok'] = 1.0 + if publish: + duration = (datetime.datetime.now() - start) + encoding_duration + listeners.publish_command_success( + duration, result, name, request_id, sock_info.address) + + return result diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/mongo_client.py b/backend/venv/lib/python3.7/site-packages/pymongo/mongo_client.py new file mode 100644 index 000000000..bbb2624c0 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/mongo_client.py @@ -0,0 +1,1950 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Tools for connecting to MongoDB. + +.. seealso:: :doc:`/examples/high_availability` for examples of connecting + to replica sets or sets of mongos servers. + +To get a :class:`~pymongo.database.Database` instance from a +:class:`MongoClient` use either dictionary-style or attribute-style +access: + +.. doctest:: + + >>> from pymongo import MongoClient + >>> c = MongoClient() + >>> c.test_database + Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), u'test_database') + >>> c['test-database'] + Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), u'test-database') +""" + +import contextlib +import datetime +import threading +import warnings +import weakref + +from collections import defaultdict + +from bson.codec_options import DEFAULT_CODEC_OPTIONS, TypeRegistry +from bson.py3compat import (integer_types, + string_type) +from bson.son import SON +from pymongo import (common, + database, + helpers, + message, + periodic_executor, + uri_parser, + client_session) +from pymongo.change_stream import ClusterChangeStream +from pymongo.client_options import ClientOptions +from pymongo.command_cursor import CommandCursor +from pymongo.cursor_manager import CursorManager +from pymongo.errors import (AutoReconnect, + BulkWriteError, + ConfigurationError, + ConnectionFailure, + InvalidOperation, + NetworkTimeout, + NotMasterError, + OperationFailure, + PyMongoError, + ServerSelectionTimeoutError) +from pymongo.read_preferences import ReadPreference +from pymongo.server_selectors import (writable_preferred_server_selector, + writable_server_selector) +from pymongo.server_type import SERVER_TYPE +from pymongo.topology import Topology +from pymongo.topology_description import TOPOLOGY_TYPE +from pymongo.settings import TopologySettings +from pymongo.write_concern import DEFAULT_WRITE_CONCERN + + +class MongoClient(common.BaseObject): + """ + A client-side representation of a MongoDB cluster. + + Instances can represent either a standalone MongoDB server, a replica + set, or a sharded cluster. Instances of this class are responsible for + maintaining up-to-date state of the cluster, and possibly cache + resources related to this, including background threads for monitoring, + and connection pools. + """ + HOST = "localhost" + PORT = 27017 + # Define order to retrieve options from ClientOptions for __repr__. + # No host/port; these are retrieved from TopologySettings. + _constructor_args = ('document_class', 'tz_aware', 'connect') + + def __init__( + self, + host=None, + port=None, + document_class=dict, + tz_aware=None, + connect=None, + type_registry=None, + **kwargs): + """Client for a MongoDB instance, a replica set, or a set of mongoses. + + The client object is thread-safe and has connection-pooling built in. + If an operation fails because of a network error, + :class:`~pymongo.errors.ConnectionFailure` is raised and the client + reconnects in the background. Application code should handle this + exception (recognizing that the operation failed) and then continue to + execute. + + The `host` parameter can be a full `mongodb URI + `_, in addition to + a simple hostname. It can also be a list of hostnames or + URIs. Any port specified in the host string(s) will override + the `port` parameter. If multiple mongodb URIs containing + database or auth information are passed, the last database, + username, and password present will be used. For username and + passwords reserved characters like ':', '/', '+' and '@' must be + percent encoded following RFC 2396:: + + try: + # Python 3.x + from urllib.parse import quote_plus + except ImportError: + # Python 2.x + from urllib import quote_plus + + uri = "mongodb://%s:%s@%s" % ( + quote_plus(user), quote_plus(password), host) + client = MongoClient(uri) + + Unix domain sockets are also supported. The socket path must be percent + encoded in the URI:: + + uri = "mongodb://%s:%s@%s" % ( + quote_plus(user), quote_plus(password), quote_plus(socket_path)) + client = MongoClient(uri) + + But not when passed as a simple hostname:: + + client = MongoClient('/tmp/mongodb-27017.sock') + + Starting with version 3.6, PyMongo supports mongodb+srv:// URIs. The + URI must include one, and only one, hostname. The hostname will be + resolved to one or more DNS `SRV records + `_ which will be used + as the seed list for connecting to the MongoDB deployment. When using + SRV URIs, the `authSource` and `replicaSet` configuration options can + be specified using `TXT records + `_. See the + `Initial DNS Seedlist Discovery spec + `_ + for more details. Note that the use of SRV URIs implicitly enables + TLS support. Pass ssl=false in the URI to override. + + .. note:: MongoClient creation will block waiting for answers from + DNS when mongodb+srv:// URIs are used. + + .. note:: Starting with version 3.0 the :class:`MongoClient` + constructor no longer blocks while connecting to the server or + servers, and it no longer raises + :class:`~pymongo.errors.ConnectionFailure` if they are + unavailable, nor :class:`~pymongo.errors.ConfigurationError` + if the user's credentials are wrong. Instead, the constructor + returns immediately and launches the connection process on + background threads. You can check if the server is available + like this:: + + from pymongo.errors import ConnectionFailure + client = MongoClient() + try: + # The ismaster command is cheap and does not require auth. + client.admin.command('ismaster') + except ConnectionFailure: + print("Server not available") + + .. warning:: When using PyMongo in a multiprocessing context, please + read :ref:`multiprocessing` first. + + :Parameters: + - `host` (optional): hostname or IP address or Unix domain socket + path of a single mongod or mongos instance to connect to, or a + mongodb URI, or a list of hostnames / mongodb URIs. If `host` is + an IPv6 literal it must be enclosed in '[' and ']' characters + following the RFC2732 URL syntax (e.g. '[::1]' for localhost). + Multihomed and round robin DNS addresses are **not** supported. + - `port` (optional): port number on which to connect + - `document_class` (optional): default class to use for + documents returned from queries on this client + - `type_registry` (optional): instance of + :class:`~bson.codec_options.TypeRegistry` to enable encoding + and decoding of custom types. + - `tz_aware` (optional): if ``True``, + :class:`~datetime.datetime` instances returned as values + in a document by this :class:`MongoClient` will be timezone + aware (otherwise they will be naive) + - `connect` (optional): if ``True`` (the default), immediately + begin connecting to MongoDB in the background. Otherwise connect + on the first operation. + + | **Other optional parameters can be passed as keyword arguments:** + + - `maxPoolSize` (optional): The maximum allowable number of + concurrent connections to each connected server. Requests to a + server will block if there are `maxPoolSize` outstanding + connections to the requested server. Defaults to 100. Cannot be 0. + - `minPoolSize` (optional): The minimum required number of concurrent + connections that the pool will maintain to each connected server. + Default is 0. + - `maxIdleTimeMS` (optional): The maximum number of milliseconds that + a connection can remain idle in the pool before being removed and + replaced. Defaults to `None` (no limit). + - `socketTimeoutMS`: (integer or None) Controls how long (in + milliseconds) the driver will wait for a response after sending an + ordinary (non-monitoring) database operation before concluding that + a network error has occurred. Defaults to ``None`` (no timeout). + - `connectTimeoutMS`: (integer or None) Controls how long (in + milliseconds) the driver will wait during server monitoring when + connecting a new socket to a server before concluding the server + is unavailable. Defaults to ``20000`` (20 seconds). + - `server_selector`: (callable or None) Optional, user-provided + function that augments server selection rules. The function should + accept as an argument a list of + :class:`~pymongo.server_description.ServerDescription` objects and + return a list of server descriptions that should be considered + suitable for the desired operation. + - `serverSelectionTimeoutMS`: (integer) Controls how long (in + milliseconds) the driver will wait to find an available, + appropriate server to carry out a database operation; while it is + waiting, multiple server monitoring operations may be carried out, + each controlled by `connectTimeoutMS`. Defaults to ``30000`` (30 + seconds). + - `waitQueueTimeoutMS`: (integer or None) How long (in milliseconds) + a thread will wait for a socket from the pool if the pool has no + free sockets. Defaults to ``None`` (no timeout). + - `waitQueueMultiple`: (integer or None) Multiplied by maxPoolSize + to give the number of threads allowed to wait for a socket at one + time. Defaults to ``None`` (no limit). + - `heartbeatFrequencyMS`: (optional) The number of milliseconds + between periodic server checks, or None to accept the default + frequency of 10 seconds. + - `appname`: (string or None) The name of the application that + created this MongoClient instance. MongoDB 3.4 and newer will + print this value in the server log upon establishing each + connection. It is also recorded in the slow query log and + profile collections. + - `driver`: (pair or None) A driver implemented on top of PyMongo can + pass a :class:`~pymongo.driver_info.DriverInfo` to add its name, + version, and platform to the message printed in the server log when + establishing a connection. + - `event_listeners`: a list or tuple of event listeners. See + :mod:`~pymongo.monitoring` for details. + - `retryWrites`: (boolean) Whether supported write operations + executed within this MongoClient will be retried once after a + network error on MongoDB 3.6+. Defaults to ``False``. + The supported write operations are: + + - :meth:`~pymongo.collection.Collection.bulk_write`, as long as + :class:`~pymongo.operations.UpdateMany` or + :class:`~pymongo.operations.DeleteMany` are not included. + - :meth:`~pymongo.collection.Collection.delete_one` + - :meth:`~pymongo.collection.Collection.insert_one` + - :meth:`~pymongo.collection.Collection.insert_many` + - :meth:`~pymongo.collection.Collection.replace_one` + - :meth:`~pymongo.collection.Collection.update_one` + - :meth:`~pymongo.collection.Collection.find_one_and_delete` + - :meth:`~pymongo.collection.Collection.find_one_and_replace` + - :meth:`~pymongo.collection.Collection.find_one_and_update` + + Unsupported write operations include, but are not limited to, + :meth:`~pymongo.collection.Collection.aggregate` using the ``$out`` + pipeline operator and any operation with an unacknowledged write + concern (e.g. {w: 0})). See + https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.rst + - `socketKeepAlive`: (boolean) **DEPRECATED** Whether to send + periodic keep-alive packets on connected sockets. Defaults to + ``True``. Disabling it is not recommended, see + https://docs.mongodb.com/manual/faq/diagnostics/#does-tcp-keepalive-time-affect-mongodb-deployments", + - `compressors`: Comma separated list of compressors for wire + protocol compression. The list is used to negotiate a compressor + with the server. Currently supported options are "snappy" and + "zlib". Support for snappy requires the + `python-snappy `_ package. + zlib support requires the Python standard library zlib module. + By default no compression is used. Compression support must also be + enabled on the server. MongoDB 3.4+ supports snappy compression. + MongoDB 3.6+ supports snappy and zlib. + - `zlibCompressionLevel`: (int) The zlib compression level to use + when zlib is used as the wire protocol compressor. Supported values + are -1 through 9. -1 tells the zlib library to use its default + compression level (usually 6). 0 means no compression. 1 is best + speed. 9 is best compression. Defaults to -1. + - `uuidRepresentation`: The BSON representation to use when encoding + from and decoding to instances of :class:`~uuid.UUID`. Valid + values are `pythonLegacy` (the default), `javaLegacy`, + `csharpLegacy` and `standard`. New applications should consider + setting this to `standard` for cross language compatibility. + + | **Write Concern options:** + | (Only set if passed. No default values.) + + - `w`: (integer or string) If this is a replica set, write operations + will block until they have been replicated to the specified number + or tagged set of servers. `w=` always includes the replica set + primary (e.g. w=3 means write to the primary and wait until + replicated to **two** secondaries). Passing w=0 **disables write + acknowledgement** and all other write concern options. + - `wtimeout`: (integer) Used in conjunction with `w`. Specify a value + in milliseconds to control how long to wait for write propagation + to complete. If replication does not complete in the given + timeframe, a timeout exception is raised. + - `j`: If ``True`` block until write operations have been committed + to the journal. Cannot be used in combination with `fsync`. Prior + to MongoDB 2.6 this option was ignored if the server was running + without journaling. Starting with MongoDB 2.6 write operations will + fail with an exception if this option is used when the server is + running without journaling. + - `fsync`: If ``True`` and the server is running without journaling, + blocks until the server has synced all data files to disk. If the + server is running with journaling, this acts the same as the `j` + option, blocking until write operations have been committed to the + journal. Cannot be used in combination with `j`. + + | **Replica set keyword arguments for connecting with a replica set + - either directly or via a mongos:** + + - `replicaSet`: (string or None) The name of the replica set to + connect to. The driver will verify that all servers it connects to + match this name. Implies that the hosts specified are a seed list + and the driver should attempt to find all members of the set. + Defaults to ``None``. + + | **Read Preference:** + + - `readPreference`: The replica set read preference for this client. + One of ``primary``, ``primaryPreferred``, ``secondary``, + ``secondaryPreferred``, or ``nearest``. Defaults to ``primary``. + - `readPreferenceTags`: Specifies a tag set as a comma-separated list + of colon-separated key-value pairs. For example ``dc:ny,rack:1``. + Defaults to ``None``. + - `maxStalenessSeconds`: (integer) The maximum estimated + length of time a replica set secondary can fall behind the primary + in replication before it will no longer be selected for operations. + Defaults to ``-1``, meaning no maximum. If maxStalenessSeconds + is set, it must be a positive integer greater than or equal to + 90 seconds. + + .. seealso:: :doc:`/examples/server_selection` + + | **Authentication:** + + - `username`: A string. + - `password`: A string. + + Although username and password must be percent-escaped in a MongoDB + URI, they must not be percent-escaped when passed as parameters. In + this example, both the space and slash special characters are passed + as-is:: + + MongoClient(username="user name", password="pass/word") + + - `authSource`: The database to authenticate on. Defaults to the + database specified in the URI, if provided, or to "admin". + - `authMechanism`: See :data:`~pymongo.auth.MECHANISMS` for options. + If no mechanism is specified, PyMongo automatically uses MONGODB-CR + when connected to a pre-3.0 version of MongoDB, SCRAM-SHA-1 when + connected to MongoDB 3.0 through 3.6, and negotiates the mechanism + to use (SCRAM-SHA-1 or SCRAM-SHA-256) when connected to MongoDB + 4.0+. + - `authMechanismProperties`: Used to specify authentication mechanism + specific options. To specify the service name for GSSAPI + authentication pass authMechanismProperties='SERVICE_NAME:' + + .. seealso:: :doc:`/examples/authentication` + + | **SSL configuration:** + + - `ssl`: If ``True``, create the connection to the server using SSL. + Defaults to ``False``. + - `ssl_certfile`: The certificate file used to identify the local + connection against mongod. Implies ``ssl=True``. Defaults to + ``None``. + - `ssl_keyfile`: The private keyfile used to identify the local + connection against mongod. If included with the ``certfile`` then + only the ``ssl_certfile`` is needed. Implies ``ssl=True``. + Defaults to ``None``. + - `ssl_pem_passphrase`: The password or passphrase for decrypting + the private key in ``ssl_certfile`` or ``ssl_keyfile``. Only + necessary if the private key is encrypted. Only supported by python + 2.7.9+ (pypy 2.5.1+) and 3.3+. Defaults to ``None``. + - `ssl_cert_reqs`: Specifies whether a certificate is required from + the other side of the connection, and whether it will be validated + if provided. It must be one of the three values ``ssl.CERT_NONE`` + (certificates ignored), ``ssl.CERT_REQUIRED`` (certificates + required and validated), or ``ssl.CERT_OPTIONAL`` (the same as + CERT_REQUIRED, unless the server was configured to use anonymous + ciphers). If the value of this parameter is not ``ssl.CERT_NONE`` + and a value is not provided for ``ssl_ca_certs`` PyMongo will + attempt to load system provided CA certificates. If the python + version in use does not support loading system CA certificates + then the ``ssl_ca_certs`` parameter must point to a file of CA + certificates. Implies ``ssl=True``. Defaults to + ``ssl.CERT_REQUIRED`` if not provided and ``ssl=True``. + - `ssl_ca_certs`: The ca_certs file contains a set of concatenated + "certification authority" certificates, which are used to validate + certificates passed from the other end of the connection. + Implies ``ssl=True``. Defaults to ``None``. + - `ssl_crlfile`: The path to a PEM or DER formatted certificate + revocation list. Only supported by python 2.7.9+ (pypy 2.5.1+) + and 3.4+. Defaults to ``None``. + - `ssl_match_hostname`: If ``True`` (the default), and + `ssl_cert_reqs` is not ``ssl.CERT_NONE``, enables hostname + verification using the :func:`~ssl.match_hostname` function from + python's :mod:`~ssl` module. Think very carefully before setting + this to ``False`` as that could make your application vulnerable to + man-in-the-middle attacks. + + | **Read Concern options:** + | (If not set explicitly, this will use the server default) + + - `readConcernLevel`: (string) The read concern level specifies the + level of isolation for read operations. For example, a read + operation using a read concern level of ``majority`` will only + return data that has been written to a majority of nodes. If the + level is left unspecified, the server default will be used. + + .. mongodoc:: connections + + .. versionchanged:: 3.8 + Added the ``server_selector`` keyword argument. + Added the ``type_registry`` keyword argument. + + .. versionchanged:: 3.7 + Added the ``driver`` keyword argument. + + .. versionchanged:: 3.6 + Added support for mongodb+srv:// URIs. + Added the ``retryWrites`` keyword argument and URI option. + + .. versionchanged:: 3.5 + Add ``username`` and ``password`` options. Document the + ``authSource``, ``authMechanism``, and ``authMechanismProperties `` + options. + Deprecated the `socketKeepAlive` keyword argument and URI option. + `socketKeepAlive` now defaults to ``True``. + + .. versionchanged:: 3.0 + :class:`~pymongo.mongo_client.MongoClient` is now the one and only + client class for a standalone server, mongos, or replica set. + It includes the functionality that had been split into + :class:`~pymongo.mongo_client.MongoReplicaSetClient`: it can connect + to a replica set, discover all its members, and monitor the set for + stepdowns, elections, and reconfigs. + + The :class:`~pymongo.mongo_client.MongoClient` constructor no + longer blocks while connecting to the server or servers, and it no + longer raises :class:`~pymongo.errors.ConnectionFailure` if they + are unavailable, nor :class:`~pymongo.errors.ConfigurationError` + if the user's credentials are wrong. Instead, the constructor + returns immediately and launches the connection process on + background threads. + + Therefore the ``alive`` method is removed since it no longer + provides meaningful information; even if the client is disconnected, + it may discover a server in time to fulfill the next operation. + + In PyMongo 2.x, :class:`~pymongo.MongoClient` accepted a list of + standalone MongoDB servers and used the first it could connect to:: + + MongoClient(['host1.com:27017', 'host2.com:27017']) + + A list of multiple standalones is no longer supported; if multiple + servers are listed they must be members of the same replica set, or + mongoses in the same sharded cluster. + + The behavior for a list of mongoses is changed from "high + availability" to "load balancing". Before, the client connected to + the lowest-latency mongos in the list, and used it until a network + error prompted it to re-evaluate all mongoses' latencies and + reconnect to one of them. In PyMongo 3, the client monitors its + network latency to all the mongoses continuously, and distributes + operations evenly among those with the lowest latency. See + :ref:`mongos-load-balancing` for more information. + + The ``connect`` option is added. + + The ``start_request``, ``in_request``, and ``end_request`` methods + are removed, as well as the ``auto_start_request`` option. + + The ``copy_database`` method is removed, see the + :doc:`copy_database examples ` for alternatives. + + The :meth:`MongoClient.disconnect` method is removed; it was a + synonym for :meth:`~pymongo.MongoClient.close`. + + :class:`~pymongo.mongo_client.MongoClient` no longer returns an + instance of :class:`~pymongo.database.Database` for attribute names + with leading underscores. You must use dict-style lookups instead:: + + client['__my_database__'] + + Not:: + + client.__my_database__ + """ + if host is None: + host = self.HOST + if isinstance(host, string_type): + host = [host] + if port is None: + port = self.PORT + if not isinstance(port, int): + raise TypeError("port must be an instance of int") + + seeds = set() + username = None + password = None + dbase = None + opts = {} + for entity in host: + if "://" in entity: + res = uri_parser.parse_uri(entity, port, warn=True) + seeds.update(res["nodelist"]) + username = res["username"] or username + password = res["password"] or password + dbase = res["database"] or dbase + opts = res["options"] + else: + seeds.update(uri_parser.split_hosts(entity, port)) + if not seeds: + raise ConfigurationError("need to specify at least one host") + + # _pool_class, _monitor_class, and _condition_class are for deep + # customization of PyMongo, e.g. Motor. + pool_class = kwargs.pop('_pool_class', None) + monitor_class = kwargs.pop('_monitor_class', None) + condition_class = kwargs.pop('_condition_class', None) + + keyword_opts = kwargs + keyword_opts['document_class'] = document_class + if type_registry is not None: + keyword_opts['type_registry'] = type_registry + if tz_aware is None: + tz_aware = opts.get('tz_aware', False) + if connect is None: + connect = opts.get('connect', True) + keyword_opts['tz_aware'] = tz_aware + keyword_opts['connect'] = connect + # Validate all keyword options. + keyword_opts = dict(common.validate(k, v) + for k, v in keyword_opts.items()) + opts.update(keyword_opts) + # Username and password passed as kwargs override user info in URI. + username = opts.get("username", username) + password = opts.get("password", password) + if 'socketkeepalive' in opts: + warnings.warn( + "The socketKeepAlive option is deprecated. It now" + "defaults to true and disabling it is not recommended, see " + "https://docs.mongodb.com/manual/faq/diagnostics/" + "#does-tcp-keepalive-time-affect-mongodb-deployments", + DeprecationWarning, stacklevel=2) + self.__options = options = ClientOptions( + username, password, dbase, opts) + + self.__default_database_name = dbase + self.__lock = threading.Lock() + self.__cursor_manager = None + self.__kill_cursors_queue = [] + + self._event_listeners = options.pool_options.event_listeners + + # Cache of existing indexes used by ensure_index ops. + self.__index_cache = {} + self.__index_cache_lock = threading.Lock() + + super(MongoClient, self).__init__(options.codec_options, + options.read_preference, + options.write_concern, + options.read_concern) + + self.__all_credentials = {} + creds = options.credentials + if creds: + self._cache_credentials(creds.source, creds) + + self._topology_settings = TopologySettings( + seeds=seeds, + replica_set_name=options.replica_set_name, + pool_class=pool_class, + pool_options=options.pool_options, + monitor_class=monitor_class, + condition_class=condition_class, + local_threshold_ms=options.local_threshold_ms, + server_selection_timeout=options.server_selection_timeout, + server_selector=options.server_selector, + heartbeat_frequency=options.heartbeat_frequency) + + self._topology = Topology(self._topology_settings) + if connect: + self._topology.open() + + def target(): + client = self_ref() + if client is None: + return False # Stop the executor. + MongoClient._process_periodic_tasks(client) + return True + + executor = periodic_executor.PeriodicExecutor( + interval=common.KILL_CURSOR_FREQUENCY, + min_interval=0.5, + target=target, + name="pymongo_kill_cursors_thread") + + # We strongly reference the executor and it weakly references us via + # this closure. When the client is freed, stop the executor soon. + self_ref = weakref.ref(self, executor.close) + self._kill_cursors_executor = executor + executor.open() + + def _cache_credentials(self, source, credentials, connect=False): + """Save a set of authentication credentials. + + The credentials are used to login a socket whenever one is created. + If `connect` is True, verify the credentials on the server first. + """ + # Don't let other threads affect this call's data. + all_credentials = self.__all_credentials.copy() + + if source in all_credentials: + # Nothing to do if we already have these credentials. + if credentials == all_credentials[source]: + return + raise OperationFailure('Another user is already authenticated ' + 'to this database. You must logout first.') + + if connect: + server = self._get_topology().select_server( + writable_preferred_server_selector) + + # get_socket() logs out of the database if logged in with old + # credentials, and logs in with new ones. + with server.get_socket(all_credentials) as sock_info: + sock_info.authenticate(credentials) + + # If several threads run _cache_credentials at once, last one wins. + self.__all_credentials[source] = credentials + + def _purge_credentials(self, source): + """Purge credentials from the authentication cache.""" + self.__all_credentials.pop(source, None) + + def _cached(self, dbname, coll, index): + """Test if `index` is cached.""" + cache = self.__index_cache + now = datetime.datetime.utcnow() + with self.__index_cache_lock: + return (dbname in cache and + coll in cache[dbname] and + index in cache[dbname][coll] and + now < cache[dbname][coll][index]) + + def _cache_index(self, dbname, collection, index, cache_for): + """Add an index to the index cache for ensure_index operations.""" + now = datetime.datetime.utcnow() + expire = datetime.timedelta(seconds=cache_for) + now + + with self.__index_cache_lock: + if dbname not in self.__index_cache: + self.__index_cache[dbname] = {} + self.__index_cache[dbname][collection] = {} + self.__index_cache[dbname][collection][index] = expire + + elif collection not in self.__index_cache[dbname]: + self.__index_cache[dbname][collection] = {} + self.__index_cache[dbname][collection][index] = expire + + else: + self.__index_cache[dbname][collection][index] = expire + + def _purge_index(self, database_name, + collection_name=None, index_name=None): + """Purge an index from the index cache. + + If `index_name` is None purge an entire collection. + + If `collection_name` is None purge an entire database. + """ + with self.__index_cache_lock: + if not database_name in self.__index_cache: + return + + if collection_name is None: + del self.__index_cache[database_name] + return + + if not collection_name in self.__index_cache[database_name]: + return + + if index_name is None: + del self.__index_cache[database_name][collection_name] + return + + if index_name in self.__index_cache[database_name][collection_name]: + del self.__index_cache[database_name][collection_name][index_name] + + def _server_property(self, attr_name): + """An attribute of the current server's description. + + If the client is not connected, this will block until a connection is + established or raise ServerSelectionTimeoutError if no server is + available. + + Not threadsafe if used multiple times in a single method, since + the server may change. In such cases, store a local reference to a + ServerDescription first, then use its properties. + """ + server = self._topology.select_server( + writable_server_selector) + + return getattr(server.description, attr_name) + + def watch(self, pipeline=None, full_document='default', resume_after=None, + max_await_time_ms=None, batch_size=None, collation=None, + start_at_operation_time=None, session=None): + """Watch changes on this cluster. + + Performs an aggregation with an implicit initial ``$changeStream`` + stage and returns a + :class:`~pymongo.change_stream.ClusterChangeStream` cursor which + iterates over changes on all databases on this cluster. + + Introduced in MongoDB 4.0. + + .. code-block:: python + + with client.watch() as stream: + for change in stream: + print(change) + + The :class:`~pymongo.change_stream.ClusterChangeStream` iterable + blocks until the next change document is returned or an error is + raised. If the + :meth:`~pymongo.change_stream.ClusterChangeStream.next` method + encounters a network error when retrieving a batch from the server, + it will automatically attempt to recreate the cursor such that no + change events are missed. Any error encountered during the resume + attempt indicates there may be an outage and will be raised. + + .. code-block:: python + + try: + with client.watch( + [{'$match': {'operationType': 'insert'}}]) as stream: + for insert_change in stream: + print(insert_change) + except pymongo.errors.PyMongoError: + # The ChangeStream encountered an unrecoverable error or the + # resume attempt failed to recreate the cursor. + logging.error('...') + + For a precise description of the resume process see the + `change streams specification`_. + + :Parameters: + - `pipeline` (optional): A list of aggregation pipeline stages to + append to an initial ``$changeStream`` stage. Not all + pipeline stages are valid after a ``$changeStream`` stage, see the + MongoDB documentation on change streams for the supported stages. + - `full_document` (optional): The fullDocument to pass as an option + to the ``$changeStream`` stage. Allowed values: 'default', + 'updateLookup'. Defaults to 'default'. + When set to 'updateLookup', the change notification for partial + updates will include both a delta describing the changes to the + document, as well as a copy of the entire document that was + changed from some time after the change occurred. + - `resume_after` (optional): The logical starting point for this + change stream. + - `max_await_time_ms` (optional): The maximum time in milliseconds + for the server to wait for changes before responding to a getMore + operation. + - `batch_size` (optional): The maximum number of documents to return + per batch. + - `collation` (optional): The :class:`~pymongo.collation.Collation` + to use for the aggregation. + - `start_at_operation_time` (optional): If provided, the resulting + change stream will only return changes that occurred at or after + the specified :class:`~bson.timestamp.Timestamp`. Requires + MongoDB >= 4.0. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + :Returns: + A :class:`~pymongo.change_stream.ClusterChangeStream` cursor. + + .. versionadded:: 3.7 + + .. mongodoc:: changeStreams + + .. _change streams specification: + https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst + """ + return ClusterChangeStream( + self.admin, pipeline, full_document, resume_after, max_await_time_ms, + batch_size, collation, start_at_operation_time, session + ) + + @property + def event_listeners(self): + """The event listeners registered for this client. + + See :mod:`~pymongo.monitoring` for details. + """ + return self._event_listeners.event_listeners + + @property + def address(self): + """(host, port) of the current standalone, primary, or mongos, or None. + + Accessing :attr:`address` raises :exc:`~.errors.InvalidOperation` if + the client is load-balancing among mongoses, since there is no single + address. Use :attr:`nodes` instead. + + If the client is not connected, this will block until a connection is + established or raise ServerSelectionTimeoutError if no server is + available. + + .. versionadded:: 3.0 + """ + topology_type = self._topology._description.topology_type + if topology_type == TOPOLOGY_TYPE.Sharded: + raise InvalidOperation( + 'Cannot use "address" property when load balancing among' + ' mongoses, use "nodes" instead.') + if topology_type not in (TOPOLOGY_TYPE.ReplicaSetWithPrimary, + TOPOLOGY_TYPE.Single): + return None + return self._server_property('address') + + @property + def primary(self): + """The (host, port) of the current primary of the replica set. + + Returns ``None`` if this client is not connected to a replica set, + there is no primary, or this client was created without the + `replicaSet` option. + + .. versionadded:: 3.0 + MongoClient gained this property in version 3.0 when + MongoReplicaSetClient's functionality was merged in. + """ + return self._topology.get_primary() + + @property + def secondaries(self): + """The secondary members known to this client. + + A sequence of (host, port) pairs. Empty if this client is not + connected to a replica set, there are no visible secondaries, or this + client was created without the `replicaSet` option. + + .. versionadded:: 3.0 + MongoClient gained this property in version 3.0 when + MongoReplicaSetClient's functionality was merged in. + """ + return self._topology.get_secondaries() + + @property + def arbiters(self): + """Arbiters in the replica set. + + A sequence of (host, port) pairs. Empty if this client is not + connected to a replica set, there are no arbiters, or this client was + created without the `replicaSet` option. + """ + return self._topology.get_arbiters() + + @property + def is_primary(self): + """If this client is connected to a server that can accept writes. + + True if the current server is a standalone, mongos, or the primary of + a replica set. If the client is not connected, this will block until a + connection is established or raise ServerSelectionTimeoutError if no + server is available. + """ + return self._server_property('is_writable') + + @property + def is_mongos(self): + """If this client is connected to mongos. If the client is not + connected, this will block until a connection is established or raise + ServerSelectionTimeoutError if no server is available.. + """ + return self._server_property('server_type') == SERVER_TYPE.Mongos + + @property + def max_pool_size(self): + """The maximum allowable number of concurrent connections to each + connected server. Requests to a server will block if there are + `maxPoolSize` outstanding connections to the requested server. + Defaults to 100. Cannot be 0. + + When a server's pool has reached `max_pool_size`, operations for that + server block waiting for a socket to be returned to the pool. If + ``waitQueueTimeoutMS`` is set, a blocked operation will raise + :exc:`~pymongo.errors.ConnectionFailure` after a timeout. + By default ``waitQueueTimeoutMS`` is not set. + """ + return self.__options.pool_options.max_pool_size + + @property + def min_pool_size(self): + """The minimum required number of concurrent connections that the pool + will maintain to each connected server. Default is 0. + """ + return self.__options.pool_options.min_pool_size + + @property + def max_idle_time_ms(self): + """The maximum number of milliseconds that a connection can remain + idle in the pool before being removed and replaced. Defaults to + `None` (no limit). + """ + seconds = self.__options.pool_options.max_idle_time_seconds + if seconds is None: + return None + return 1000 * seconds + + @property + def nodes(self): + """Set of all currently connected servers. + + .. warning:: When connected to a replica set the value of :attr:`nodes` + can change over time as :class:`MongoClient`'s view of the replica + set changes. :attr:`nodes` can also be an empty set when + :class:`MongoClient` is first instantiated and hasn't yet connected + to any servers, or a network partition causes it to lose connection + to all servers. + """ + description = self._topology.description + return frozenset(s.address for s in description.known_servers) + + @property + def max_bson_size(self): + """The largest BSON object the connected server accepts in bytes. + + If the client is not connected, this will block until a connection is + established or raise ServerSelectionTimeoutError if no server is + available. + """ + return self._server_property('max_bson_size') + + @property + def max_message_size(self): + """The largest message the connected server accepts in bytes. + + If the client is not connected, this will block until a connection is + established or raise ServerSelectionTimeoutError if no server is + available. + """ + return self._server_property('max_message_size') + + @property + def max_write_batch_size(self): + """The maxWriteBatchSize reported by the server. + + If the client is not connected, this will block until a connection is + established or raise ServerSelectionTimeoutError if no server is + available. + + Returns a default value when connected to server versions prior to + MongoDB 2.6. + """ + return self._server_property('max_write_batch_size') + + @property + def local_threshold_ms(self): + """The local threshold for this instance.""" + return self.__options.local_threshold_ms + + @property + def server_selection_timeout(self): + """The server selection timeout for this instance in seconds.""" + return self.__options.server_selection_timeout + + @property + def retry_writes(self): + """If this instance should retry supported write operations.""" + return self.__options.retry_writes + + def _is_writable(self): + """Attempt to connect to a writable server, or return False. + """ + topology = self._get_topology() # Starts monitors if necessary. + try: + svr = topology.select_server(writable_server_selector) + + # When directly connected to a secondary, arbiter, etc., + # select_server returns it, whatever the selector. Check + # again if the server is writable. + return svr.description.is_writable + except ConnectionFailure: + return False + + def _end_sessions(self, session_ids): + """Send endSessions command(s) with the given session ids.""" + try: + # Use SocketInfo.command directly to avoid implicitly creating + # another session. + with self._socket_for_reads( + ReadPreference.PRIMARY_PREFERRED) as (sock_info, slave_ok): + if not sock_info.supports_sessions: + return + + for i in range(0, len(session_ids), common._MAX_END_SESSIONS): + spec = SON([('endSessions', + session_ids[i:i + common._MAX_END_SESSIONS])]) + sock_info.command( + 'admin', spec, slave_ok=slave_ok, client=self) + except PyMongoError: + # Drivers MUST ignore any errors returned by the endSessions + # command. + pass + + def close(self): + """Cleanup client resources and disconnect from MongoDB. + + On MongoDB >= 3.6, end all server sessions created by this client by + sending one or more endSessions commands. + + Close all sockets in the connection pools and stop the monitor threads. + If this instance is used again it will be automatically re-opened and + the threads restarted. + + .. versionchanged:: 3.6 + End all server sessions created by this client. + """ + session_ids = self._topology.pop_all_sessions() + if session_ids: + self._end_sessions(session_ids) + # Stop the periodic task thread and then run _process_periodic_tasks + # to send pending killCursor requests before closing the topology. + self._kill_cursors_executor.close() + self._process_periodic_tasks() + self._topology.close() + + def set_cursor_manager(self, manager_class): + """DEPRECATED - Set this client's cursor manager. + + Raises :class:`TypeError` if `manager_class` is not a subclass of + :class:`~pymongo.cursor_manager.CursorManager`. A cursor manager + handles closing cursors. Different managers can implement different + policies in terms of when to actually kill a cursor that has + been closed. + + :Parameters: + - `manager_class`: cursor manager to use + + .. versionchanged:: 3.3 + Deprecated, for real this time. + + .. versionchanged:: 3.0 + Undeprecated. + """ + warnings.warn( + "set_cursor_manager is Deprecated", + DeprecationWarning, + stacklevel=2) + manager = manager_class(self) + if not isinstance(manager, CursorManager): + raise TypeError("manager_class must be a subclass of " + "CursorManager") + + self.__cursor_manager = manager + + def _get_topology(self): + """Get the internal :class:`~pymongo.topology.Topology` object. + + If this client was created with "connect=False", calling _get_topology + launches the connection process in the background. + """ + self._topology.open() + with self.__lock: + self._kill_cursors_executor.open() + return self._topology + + @contextlib.contextmanager + def _get_socket(self, server): + try: + with server.get_socket(self.__all_credentials) as sock_info: + yield sock_info + except NetworkTimeout: + # The socket has been closed. Don't reset the server. + # Server Discovery And Monitoring Spec: "When an application + # operation fails because of any network error besides a socket + # timeout...." + raise + except NotMasterError: + # "When the client sees a "not master" error it MUST replace the + # server's description with type Unknown. It MUST request an + # immediate check of the server." + self._reset_server_and_request_check(server.description.address) + raise + except ConnectionFailure: + # "Client MUST replace the server's description with type Unknown + # ... MUST NOT request an immediate check of the server." + self.__reset_server(server.description.address) + raise + except OperationFailure as exc: + if exc.code in helpers._RETRYABLE_ERROR_CODES: + # Do not request an immediate check since the server is likely + # shutting down. + self.__reset_server(server.description.address) + raise + + def _socket_for_writes(self): + server = self._get_topology().select_server(writable_server_selector) + return self._get_socket(server) + + @contextlib.contextmanager + def _socket_for_reads(self, read_preference): + assert read_preference is not None, "read_preference must not be None" + # Get a socket for a server matching the read preference, and yield + # sock_info, slave_ok. Server Selection Spec: "slaveOK must be sent to + # mongods with topology type Single. If the server type is Mongos, + # follow the rules for passing read preference to mongos, even for + # topology type Single." + # Thread safe: if the type is single it cannot change. + topology = self._get_topology() + single = topology.description.topology_type == TOPOLOGY_TYPE.Single + server = topology.select_server(read_preference) + + with self._get_socket(server) as sock_info: + slave_ok = (single and not sock_info.is_mongos) or ( + read_preference != ReadPreference.PRIMARY) + yield sock_info, slave_ok + + def _send_message_with_response(self, operation, exhaust=False, + address=None): + """Send a message to MongoDB and return a Response. + + :Parameters: + - `operation`: a _Query or _GetMore object. + - `read_preference` (optional): A ReadPreference. + - `exhaust` (optional): If True, the socket used stays checked out. + It is returned along with its Pool in the Response. + - `address` (optional): Optional address when sending a message + to a specific server, used for getMore. + """ + topology = self._get_topology() + if address: + server = topology.select_server_by_address(address) + if not server: + raise AutoReconnect('server %s:%d no longer available' + % address) + else: + server = topology.select_server(operation.read_preference) + + # If this is a direct connection to a mongod, *always* set the slaveOk + # bit. See bullet point 2 in server-selection.rst#topology-type-single. + set_slave_ok = ( + topology.description.topology_type == TOPOLOGY_TYPE.Single + and server.description.server_type != SERVER_TYPE.Mongos) or ( + operation.read_preference != ReadPreference.PRIMARY) + + return self._reset_on_error( + server, + server.send_message_with_response, + operation, + set_slave_ok, + self.__all_credentials, + self._event_listeners, + exhaust) + + def _reset_on_error(self, server, func, *args, **kwargs): + """Execute an operation. Reset the server on network error. + + Returns fn()'s return value on success. On error, clears the server's + pool and marks the server Unknown. + + Re-raises any exception thrown by fn(). + """ + try: + return func(*args, **kwargs) + except NetworkTimeout: + # The socket has been closed. Don't reset the server. + raise + except ConnectionFailure: + self.__reset_server(server.description.address) + raise + + def _retry_with_session(self, retryable, func, session, bulk): + """Execute an operation with at most one consecutive retries + + Returns func()'s return value on success. On error retries the same + command once. + + Re-raises any exception thrown by func(). + """ + retryable = (retryable and self.retry_writes + and session and not session._in_transaction) + last_error = None + retrying = False + + def is_retrying(): + return bulk.retrying if bulk else retrying + # Increment the transaction id up front to ensure any retry attempt + # will use the proper txnNumber, even if server or socket selection + # fails before the command can be sent. + if retryable: + session._start_retryable_write() + if bulk: + bulk.started_retryable_write = True + + while True: + try: + server = self._get_topology().select_server( + writable_server_selector) + supports_session = ( + session is not None and + server.description.retryable_writes_supported) + with self._get_socket(server) as sock_info: + if retryable and not supports_session: + if is_retrying(): + # A retry is not possible because this server does + # not support sessions raise the last error. + raise last_error + retryable = False + return func(session, sock_info, retryable) + except ServerSelectionTimeoutError: + if is_retrying(): + # The application may think the write was never attempted + # if we raise ServerSelectionTimeoutError on the retry + # attempt. Raise the original exception instead. + raise last_error + # A ServerSelectionTimeoutError error indicates that there may + # be a persistent outage. Attempting to retry in this case will + # most likely be a waste of time. + raise + except ConnectionFailure as exc: + if not retryable or is_retrying(): + raise + if bulk: + bulk.retrying = True + else: + retrying = True + last_error = exc + except BulkWriteError as exc: + if not retryable or is_retrying(): + raise + # Check the last writeConcernError to determine if this + # BulkWriteError is retryable. + wces = exc.details['writeConcernErrors'] + wce = wces[-1] if wces else {} + if wce.get('code', 0) not in helpers._RETRYABLE_ERROR_CODES: + raise + if bulk: + bulk.retrying = True + else: + retrying = True + last_error = exc + except OperationFailure as exc: + if not retryable or is_retrying(): + raise + if exc.code not in helpers._RETRYABLE_ERROR_CODES: + raise + if bulk: + bulk.retrying = True + else: + retrying = True + last_error = exc + + def _retryable_write(self, retryable, func, session): + """Internal retryable write helper.""" + with self._tmp_session(session) as s: + return self._retry_with_session(retryable, func, s, None) + + def __reset_server(self, address): + """Clear our connection pool for a server and mark it Unknown.""" + self._topology.reset_server(address) + + def _reset_server_and_request_check(self, address): + """Clear our pool for a server, mark it Unknown, and check it soon.""" + self._topology.reset_server_and_request_check(address) + + def __eq__(self, other): + if isinstance(other, self.__class__): + return self.address == other.address + return NotImplemented + + def __ne__(self, other): + return not self == other + + def _repr_helper(self): + def option_repr(option, value): + """Fix options whose __repr__ isn't usable in a constructor.""" + if option == 'document_class': + if value is dict: + return 'document_class=dict' + else: + return 'document_class=%s.%s' % (value.__module__, + value.__name__) + if option in common.TIMEOUT_VALIDATORS and value is not None: + return "%s=%s" % (option, int(value * 1000)) + + return '%s=%r' % (option, value) + + # Host first... + options = ['host=%r' % [ + '%s:%d' % (host, port) if port is not None else host + for host, port in self._topology_settings.seeds]] + # ... then everything in self._constructor_args... + options.extend( + option_repr(key, self.__options._options[key]) + for key in self._constructor_args) + # ... then everything else. + options.extend( + option_repr(key, self.__options._options[key]) + for key in self.__options._options + if key not in set(self._constructor_args) + and key != 'username' and key != 'password') + return ', '.join(options) + + def __repr__(self): + return ("MongoClient(%s)" % (self._repr_helper(),)) + + def __getattr__(self, name): + """Get a database by name. + + Raises :class:`~pymongo.errors.InvalidName` if an invalid + database name is used. + + :Parameters: + - `name`: the name of the database to get + """ + if name.startswith('_'): + raise AttributeError( + "MongoClient has no attribute %r. To access the %s" + " database, use client[%r]." % (name, name, name)) + return self.__getitem__(name) + + def __getitem__(self, name): + """Get a database by name. + + Raises :class:`~pymongo.errors.InvalidName` if an invalid + database name is used. + + :Parameters: + - `name`: the name of the database to get + """ + return database.Database(self, name) + + def close_cursor(self, cursor_id, address=None): + """DEPRECATED - Send a kill cursors message soon with the given id. + + Raises :class:`TypeError` if `cursor_id` is not an instance of + ``(int, long)``. What closing the cursor actually means + depends on this client's cursor manager. + + This method may be called from a :class:`~pymongo.cursor.Cursor` + destructor during garbage collection, so it isn't safe to take a + lock or do network I/O. Instead, we schedule the cursor to be closed + soon on a background thread. + + :Parameters: + - `cursor_id`: id of cursor to close + - `address` (optional): (host, port) pair of the cursor's server. + If it is not provided, the client attempts to close the cursor on + the primary or standalone, or a mongos server. + + .. versionchanged:: 3.7 + Deprecated. + + .. versionchanged:: 3.0 + Added ``address`` parameter. + """ + warnings.warn( + "close_cursor is deprecated.", + DeprecationWarning, + stacklevel=2) + if not isinstance(cursor_id, integer_types): + raise TypeError("cursor_id must be an instance of (int, long)") + + self._close_cursor(cursor_id, address) + + def _close_cursor(self, cursor_id, address): + """Send a kill cursors message with the given id. + + What closing the cursor actually means depends on this client's + cursor manager. If there is none, the cursor is closed asynchronously + on a background thread. + """ + if self.__cursor_manager is not None: + self.__cursor_manager.close(cursor_id, address) + else: + self.__kill_cursors_queue.append((address, [cursor_id])) + + def _close_cursor_now(self, cursor_id, address=None, session=None): + """Send a kill cursors message with the given id. + + What closing the cursor actually means depends on this client's + cursor manager. If there is none, the cursor is closed synchronously + on the current thread. + """ + if not isinstance(cursor_id, integer_types): + raise TypeError("cursor_id must be an instance of (int, long)") + + if self.__cursor_manager is not None: + self.__cursor_manager.close(cursor_id, address) + else: + try: + self._kill_cursors( + [cursor_id], address, self._get_topology(), session) + except PyMongoError: + # Make another attempt to kill the cursor later. + self.__kill_cursors_queue.append((address, [cursor_id])) + + def kill_cursors(self, cursor_ids, address=None): + """DEPRECATED - Send a kill cursors message soon with the given ids. + + Raises :class:`TypeError` if `cursor_ids` is not an instance of + ``list``. + + :Parameters: + - `cursor_ids`: list of cursor ids to kill + - `address` (optional): (host, port) pair of the cursor's server. + If it is not provided, the client attempts to close the cursor on + the primary or standalone, or a mongos server. + + .. versionchanged:: 3.3 + Deprecated. + + .. versionchanged:: 3.0 + Now accepts an `address` argument. Schedules the cursors to be + closed on a background thread instead of sending the message + immediately. + """ + warnings.warn( + "kill_cursors is deprecated.", + DeprecationWarning, + stacklevel=2) + + if not isinstance(cursor_ids, list): + raise TypeError("cursor_ids must be a list") + + # "Atomic", needs no lock. + self.__kill_cursors_queue.append((address, cursor_ids)) + + def _kill_cursors(self, cursor_ids, address, topology, session): + """Send a kill cursors message with the given ids.""" + listeners = self._event_listeners + publish = listeners.enabled_for_commands + if address: + # address could be a tuple or _CursorAddress, but + # select_server_by_address needs (host, port). + server = topology.select_server_by_address(tuple(address)) + else: + # Application called close_cursor() with no address. + server = topology.select_server(writable_server_selector) + + try: + namespace = address.namespace + db, coll = namespace.split('.', 1) + except AttributeError: + namespace = None + db = coll = "OP_KILL_CURSORS" + + spec = SON([('killCursors', coll), ('cursors', cursor_ids)]) + with server.get_socket(self.__all_credentials) as sock_info: + if sock_info.max_wire_version >= 4 and namespace is not None: + sock_info.command(db, spec, session=session, client=self) + else: + if publish: + start = datetime.datetime.now() + request_id, msg = message.kill_cursors(cursor_ids) + if publish: + duration = datetime.datetime.now() - start + # Here and below, address could be a tuple or + # _CursorAddress. We always want to publish a + # tuple to match the rest of the monitoring + # API. + listeners.publish_command_start( + spec, db, request_id, tuple(address)) + start = datetime.datetime.now() + + try: + sock_info.send_message(msg, 0) + except Exception as exc: + if publish: + dur = ((datetime.datetime.now() - start) + duration) + listeners.publish_command_failure( + dur, message._convert_exception(exc), + 'killCursors', request_id, + tuple(address)) + raise + + if publish: + duration = ((datetime.datetime.now() - start) + duration) + # OP_KILL_CURSORS returns no reply, fake one. + reply = {'cursorsUnknown': cursor_ids, 'ok': 1} + listeners.publish_command_success( + duration, reply, 'killCursors', request_id, + tuple(address)) + + # This method is run periodically by a background thread. + def _process_periodic_tasks(self): + """Process any pending kill cursors requests and + maintain connection pool parameters.""" + address_to_cursor_ids = defaultdict(list) + + # Other threads or the GC may append to the queue concurrently. + while True: + try: + address, cursor_ids = self.__kill_cursors_queue.pop() + except IndexError: + break + + address_to_cursor_ids[address].extend(cursor_ids) + + # Don't re-open topology if it's closed and there's no pending cursors. + if address_to_cursor_ids: + topology = self._get_topology() + for address, cursor_ids in address_to_cursor_ids.items(): + try: + self._kill_cursors( + cursor_ids, address, topology, session=None) + except Exception: + helpers._handle_exception() + try: + self._topology.update_pool() + except Exception: + helpers._handle_exception() + + def _is_mongos_non_blocking(self): + return self._topology.is_mongos_non_blocking() + + def __start_session(self, implicit, **kwargs): + # Driver Sessions Spec: "If startSession is called when multiple users + # are authenticated drivers MUST raise an error with the error message + # 'Cannot call startSession when multiple users are authenticated.'" + authset = set(self.__all_credentials.values()) + if len(authset) > 1: + raise InvalidOperation("Cannot call start_session when" + " multiple users are authenticated") + + # Raises ConfigurationError if sessions are not supported. + server_session = self._get_server_session() + opts = client_session.SessionOptions(**kwargs) + return client_session.ClientSession( + self, server_session, opts, authset, implicit) + + def start_session(self, + causal_consistency=True, + default_transaction_options=None): + """Start a logical session. + + This method takes the same parameters as + :class:`~pymongo.client_session.SessionOptions`. See the + :mod:`~pymongo.client_session` module for details and examples. + + Requires MongoDB 3.6. It is an error to call :meth:`start_session` + if this client has been authenticated to multiple databases using the + deprecated method :meth:`~pymongo.database.Database.authenticate`. + + A :class:`~pymongo.client_session.ClientSession` may only be used with + the MongoClient that started it. + + :Returns: + An instance of :class:`~pymongo.client_session.ClientSession`. + + .. versionadded:: 3.6 + """ + return self.__start_session( + False, + causal_consistency=causal_consistency, + default_transaction_options=default_transaction_options) + + def _get_server_session(self): + """Internal: start or resume a _ServerSession.""" + return self._topology.get_server_session() + + def _return_server_session(self, server_session, lock): + """Internal: return a _ServerSession to the pool.""" + return self._topology.return_server_session(server_session, lock) + + def _ensure_session(self, session=None): + """If provided session is None, lend a temporary session.""" + if session: + return session + + try: + # Don't make implicit sessions causally consistent. Applications + # should always opt-in. + return self.__start_session(True, causal_consistency=False) + except (ConfigurationError, InvalidOperation): + # Sessions not supported, or multiple users authenticated. + return None + + @contextlib.contextmanager + def _tmp_session(self, session, close=True): + """If provided session is None, lend a temporary session.""" + if session: + # Don't call end_session. + yield session + return + + s = self._ensure_session(session) + if s and close: + with s: + # Call end_session when we exit this scope. + yield s + elif s: + try: + # Only call end_session on error. + yield s + except Exception: + s.end_session() + raise + else: + yield None + + def _send_cluster_time(self, command, session): + topology_time = self._topology.max_cluster_time() + session_time = session.cluster_time if session else None + if topology_time and session_time: + if topology_time['clusterTime'] > session_time['clusterTime']: + cluster_time = topology_time + else: + cluster_time = session_time + else: + cluster_time = topology_time or session_time + if cluster_time: + command['$clusterTime'] = cluster_time + + def _receive_cluster_time(self, reply, session): + cluster_time = reply.get('$clusterTime') + self._topology.receive_cluster_time(cluster_time) + if session is not None: + session._advance_cluster_time(cluster_time) + session._advance_operation_time(reply.get("operationTime")) + + def server_info(self, session=None): + """Get information about the MongoDB server we're connected to. + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + return self.admin.command("buildinfo", + read_preference=ReadPreference.PRIMARY, + session=session) + + def list_databases(self, session=None, **kwargs): + """Get a cursor over the databases of the connected server. + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + - `**kwargs` (optional): Optional parameters of the + `listDatabases command + `_ + can be passed as keyword arguments to this method. The supported + options differ by server version. + + :Returns: + An instance of :class:`~pymongo.command_cursor.CommandCursor`. + + .. versionadded:: 3.6 + """ + cmd = SON([("listDatabases", 1)]) + cmd.update(kwargs) + admin = self._database_default_options("admin") + res = admin.command(cmd, session=session) + # listDatabases doesn't return a cursor (yet). Fake one. + cursor = { + "id": 0, + "firstBatch": res["databases"], + "ns": "admin.$cmd", + } + return CommandCursor(admin["$cmd"], cursor, None) + + def list_database_names(self, session=None): + """Get a list of the names of all databases on the connected server. + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. versionadded:: 3.6 + """ + return [doc["name"] + for doc in self.list_databases(session, nameOnly=True)] + + def database_names(self, session=None): + """**DEPRECATED**: Get a list of the names of all databases on the + connected server. + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. versionchanged:: 3.7 + Deprecated. Use :meth:`list_database_names` instead. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + warnings.warn("database_names is deprecated. Use list_database_names " + "instead.", DeprecationWarning, stacklevel=2) + return self.list_database_names(session) + + def drop_database(self, name_or_database, session=None): + """Drop a database. + + Raises :class:`TypeError` if `name_or_database` is not an instance of + :class:`basestring` (:class:`str` in python 3) or + :class:`~pymongo.database.Database`. + + :Parameters: + - `name_or_database`: the name of a database to drop, or a + :class:`~pymongo.database.Database` instance representing the + database to drop + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. note:: The :attr:`~pymongo.mongo_client.MongoClient.write_concern` of + this client is automatically applied to this operation when using + MongoDB >= 3.4. + + .. versionchanged:: 3.4 + Apply this client's write concern automatically to this operation + when connected to MongoDB >= 3.4. + + """ + name = name_or_database + if isinstance(name, database.Database): + name = name.name + + if not isinstance(name, string_type): + raise TypeError("name_or_database must be an instance " + "of %s or a Database" % (string_type.__name__,)) + + self._purge_index(name) + with self._socket_for_writes() as sock_info: + self[name]._command( + sock_info, + "dropDatabase", + read_preference=ReadPreference.PRIMARY, + write_concern=self._write_concern_for(session), + parse_write_concern_error=True, + session=session) + + def get_default_database(self, default=None, codec_options=None, + read_preference=None, write_concern=None, read_concern=None): + """Get the database named in the MongoDB connection URI. + + >>> uri = 'mongodb://host/my_database' + >>> client = MongoClient(uri) + >>> db = client.get_default_database() + >>> assert db.name == 'my_database' + >>> db = client.get_database() + >>> assert db.name == 'my_database' + + Useful in scripts where you want to choose which database to use + based only on the URI in a configuration file. + + :Parameters: + - `default` (optional): the database name to use if no database name + was provided in the URI. + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. If ``None`` (the + default) the :attr:`codec_options` of this :class:`MongoClient` is + used. + - `read_preference` (optional): The read preference to use. If + ``None`` (the default) the :attr:`read_preference` of this + :class:`MongoClient` is used. See :mod:`~pymongo.read_preferences` + for options. + - `write_concern` (optional): An instance of + :class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the + default) the :attr:`write_concern` of this :class:`MongoClient` is + used. + - `read_concern` (optional): An instance of + :class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the + default) the :attr:`read_concern` of this :class:`MongoClient` is + used. + + .. versionchanged:: 3.8 + Undeprecated. Added the ``default``, ``codec_options``, + ``read_preference``, ``write_concern`` and ``read_concern`` + parameters. + + .. versionchanged:: 3.5 + Deprecated, use :meth:`get_database` instead. + """ + if self.__default_database_name is None and default is None: + raise ConfigurationError( + 'No default database name defined or provided.') + + return database.Database( + self, self.__default_database_name or default, codec_options, + read_preference, write_concern, read_concern) + + def get_database(self, name=None, codec_options=None, read_preference=None, + write_concern=None, read_concern=None): + """Get a :class:`~pymongo.database.Database` with the given name and + options. + + Useful for creating a :class:`~pymongo.database.Database` with + different codec options, read preference, and/or write concern from + this :class:`MongoClient`. + + >>> client.read_preference + Primary() + >>> db1 = client.test + >>> db1.read_preference + Primary() + >>> from pymongo import ReadPreference + >>> db2 = client.get_database( + ... 'test', read_preference=ReadPreference.SECONDARY) + >>> db2.read_preference + Secondary(tag_sets=None) + + :Parameters: + - `name` (optional): The name of the database - a string. If ``None`` + (the default) the database named in the MongoDB connection URI is + returned. + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. If ``None`` (the + default) the :attr:`codec_options` of this :class:`MongoClient` is + used. + - `read_preference` (optional): The read preference to use. If + ``None`` (the default) the :attr:`read_preference` of this + :class:`MongoClient` is used. See :mod:`~pymongo.read_preferences` + for options. + - `write_concern` (optional): An instance of + :class:`~pymongo.write_concern.WriteConcern`. If ``None`` (the + default) the :attr:`write_concern` of this :class:`MongoClient` is + used. + - `read_concern` (optional): An instance of + :class:`~pymongo.read_concern.ReadConcern`. If ``None`` (the + default) the :attr:`read_concern` of this :class:`MongoClient` is + used. + + .. versionchanged:: 3.5 + The `name` parameter is now optional, defaulting to the database + named in the MongoDB connection URI. + """ + if name is None: + if self.__default_database_name is None: + raise ConfigurationError('No default database defined') + name = self.__default_database_name + + return database.Database( + self, name, codec_options, read_preference, + write_concern, read_concern) + + def _database_default_options(self, name): + """Get a Database instance with the default settings.""" + return self.get_database( + name, codec_options=DEFAULT_CODEC_OPTIONS, + read_preference=ReadPreference.PRIMARY, + write_concern=DEFAULT_WRITE_CONCERN) + + @property + def is_locked(self): + """Is this server locked? While locked, all write operations + are blocked, although read operations may still be allowed. + Use :meth:`unlock` to unlock. + """ + ops = self._database_default_options('admin').current_op() + return bool(ops.get('fsyncLock', 0)) + + def fsync(self, **kwargs): + """Flush all pending writes to datafiles. + + Optional parameters can be passed as keyword arguments: + - `lock`: If True lock the server to disallow writes. + - `async`: If True don't block while synchronizing. + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. note:: Starting with Python 3.7 `async` is a reserved keyword. + The async option to the fsync command can be passed using a + dictionary instead:: + + options = {'async': True} + client.fsync(**options) + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. warning:: `async` and `lock` can not be used together. + + .. warning:: MongoDB does not support the `async` option + on Windows and will raise an exception on that + platform. + """ + self.admin.command("fsync", + read_preference=ReadPreference.PRIMARY, **kwargs) + + def unlock(self, session=None): + """Unlock a previously locked server. + + :Parameters: + - `session` (optional): a + :class:`~pymongo.client_session.ClientSession`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + cmd = SON([("fsyncUnlock", 1)]) + with self._socket_for_writes() as sock_info: + if sock_info.max_wire_version >= 4: + try: + with self._tmp_session(session) as s: + sock_info.command( + "admin", cmd, session=s, client=self) + except OperationFailure as exc: + # Ignore "DB not locked" to replicate old behavior + if exc.code != 125: + raise + else: + message._first_batch(sock_info, "admin", "$cmd.sys.unlock", + {}, -1, True, self.codec_options, + ReadPreference.PRIMARY, cmd, + self._event_listeners) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + def __iter__(self): + return self + + def __next__(self): + raise TypeError("'MongoClient' object is not iterable") + + next = __next__ diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/mongo_replica_set_client.py b/backend/venv/lib/python3.7/site-packages/pymongo/mongo_replica_set_client.py new file mode 100644 index 000000000..c9436c24e --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/mongo_replica_set_client.py @@ -0,0 +1,48 @@ +# Copyright 2011-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Deprecated. See :doc:`/examples/high_availability`.""" + +import warnings + +from pymongo import mongo_client + + +class MongoReplicaSetClient(mongo_client.MongoClient): + """Deprecated alias for :class:`~pymongo.mongo_client.MongoClient`. + + :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient` + will be removed in a future version of PyMongo. + + .. versionchanged:: 3.0 + :class:`~pymongo.mongo_client.MongoClient` is now the one and only + client class for a standalone server, mongos, or replica set. + It includes the functionality that had been split into + :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`: it + can connect to a replica set, discover all its members, and monitor + the set for stepdowns, elections, and reconfigs. + + The ``refresh`` method is removed from + :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`, + as are the ``seeds`` and ``hosts`` properties. + """ + def __init__(self, *args, **kwargs): + warnings.warn('MongoReplicaSetClient is deprecated, use MongoClient' + ' to connect to a replica set', + DeprecationWarning, stacklevel=2) + + super(MongoReplicaSetClient, self).__init__(*args, **kwargs) + + def __repr__(self): + return "MongoReplicaSetClient(%s)" % (self._repr_helper(),) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/monitor.py b/backend/venv/lib/python3.7/site-packages/pymongo/monitor.py new file mode 100644 index 000000000..4c0f3182e --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/monitor.py @@ -0,0 +1,184 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Class to monitor a MongoDB server on a background thread.""" + +import weakref + +from pymongo import common, periodic_executor +from pymongo.errors import OperationFailure +from pymongo.server_type import SERVER_TYPE +from pymongo.monotonic import time as _time +from pymongo.read_preferences import MovingAverage +from pymongo.server_description import ServerDescription + + +class Monitor(object): + def __init__( + self, + server_description, + topology, + pool, + topology_settings): + """Class to monitor a MongoDB server on a background thread. + + Pass an initial ServerDescription, a Topology, a Pool, and + TopologySettings. + + The Topology is weakly referenced. The Pool must be exclusive to this + Monitor. + """ + self._server_description = server_description + self._pool = pool + self._settings = topology_settings + self._avg_round_trip_time = MovingAverage() + self._listeners = self._settings._pool_options.event_listeners + pub = self._listeners is not None + self._publish = pub and self._listeners.enabled_for_server_heartbeat + + # We strongly reference the executor and it weakly references us via + # this closure. When the monitor is freed, stop the executor soon. + def target(): + monitor = self_ref() + if monitor is None: + return False # Stop the executor. + Monitor._run(monitor) + return True + + executor = periodic_executor.PeriodicExecutor( + interval=self._settings.heartbeat_frequency, + min_interval=common.MIN_HEARTBEAT_INTERVAL, + target=target, + name="pymongo_server_monitor_thread") + + self._executor = executor + + # Avoid cycles. When self or topology is freed, stop executor soon. + self_ref = weakref.ref(self, executor.close) + self._topology = weakref.proxy(topology, executor.close) + + def open(self): + """Start monitoring, or restart after a fork. + + Multiple calls have no effect. + """ + self._executor.open() + + def close(self): + """Close and stop monitoring. + + open() restarts the monitor after closing. + """ + self._executor.close() + + # Increment the pool_id and maybe close the socket. If the executor + # thread has the socket checked out, it will be closed when checked in. + self._pool.reset() + + def join(self, timeout=None): + self._executor.join(timeout) + + def request_check(self): + """If the monitor is sleeping, wake and check the server soon.""" + self._executor.wake() + + def _run(self): + try: + self._server_description = self._check_with_retry() + self._topology.on_change(self._server_description) + except ReferenceError: + # Topology was garbage-collected. + self.close() + + def _check_with_retry(self): + """Call ismaster once or twice. Reset server's pool on error. + + Returns a ServerDescription. + """ + # According to the spec, if an ismaster call fails we reset the + # server's pool. If a server was once connected, change its type + # to Unknown only after retrying once. + address = self._server_description.address + retry = True + if self._server_description.server_type == SERVER_TYPE.Unknown: + retry = False + + start = _time() + try: + return self._check_once() + except ReferenceError: + raise + except Exception as error: + error_time = _time() - start + if self._publish: + self._listeners.publish_server_heartbeat_failed( + address, error_time, error) + self._topology.reset_pool(address) + default = ServerDescription(address, error=error) + if not retry: + self._avg_round_trip_time.reset() + # Server type defaults to Unknown. + return default + + # Try a second and final time. If it fails return original error. + # Always send metadata: this is a new connection. + start = _time() + try: + return self._check_once() + except ReferenceError: + raise + except Exception as error: + error_time = _time() - start + if self._publish: + self._listeners.publish_server_heartbeat_failed( + address, error_time, error) + self._avg_round_trip_time.reset() + return default + + def _check_once(self): + """A single attempt to call ismaster. + + Returns a ServerDescription, or raises an exception. + """ + address = self._server_description.address + if self._publish: + self._listeners.publish_server_heartbeat_started(address) + with self._pool.get_socket({}) as sock_info: + response, round_trip_time = self._check_with_socket(sock_info) + self._avg_round_trip_time.add_sample(round_trip_time) + sd = ServerDescription( + address=address, + ismaster=response, + round_trip_time=self._avg_round_trip_time.get()) + if self._publish: + self._listeners.publish_server_heartbeat_succeeded( + address, round_trip_time, response) + + return sd + + def _check_with_socket(self, sock_info): + """Return (IsMaster, round_trip_time). + + Can raise ConnectionFailure or OperationFailure. + """ + start = _time() + try: + return (sock_info.ismaster(self._pool.opts.metadata, + self._topology.max_cluster_time()), + _time() - start) + except OperationFailure as exc: + # Update max cluster time even when isMaster fails. + self._topology.receive_cluster_time( + exc.details.get('$clusterTime')) + raise diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/monitoring.py b/backend/venv/lib/python3.7/site-packages/pymongo/monitoring.py new file mode 100644 index 000000000..92b838cbe --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/monitoring.py @@ -0,0 +1,931 @@ +# Copyright 2015-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Tools to monitor driver events. + +.. versionadded:: 3.1 + +Use :func:`register` to register global listeners for specific events. +Listeners must inherit from one of the abstract classes below and implement +the correct functions for that class. + +For example, a simple command logger might be implemented like this:: + + import logging + + from pymongo import monitoring + + class CommandLogger(monitoring.CommandListener): + + def started(self, event): + logging.info("Command {0.command_name} with request id " + "{0.request_id} started on server " + "{0.connection_id}".format(event)) + + def succeeded(self, event): + logging.info("Command {0.command_name} with request id " + "{0.request_id} on server {0.connection_id} " + "succeeded in {0.duration_micros} " + "microseconds".format(event)) + + def failed(self, event): + logging.info("Command {0.command_name} with request id " + "{0.request_id} on server {0.connection_id} " + "failed in {0.duration_micros} " + "microseconds".format(event)) + + monitoring.register(CommandLogger()) + +Server discovery and monitoring events are also available. For example:: + + class ServerLogger(monitoring.ServerListener): + + def opened(self, event): + logging.info("Server {0.server_address} added to topology " + "{0.topology_id}".format(event)) + + def description_changed(self, event): + previous_server_type = event.previous_description.server_type + new_server_type = event.new_description.server_type + if new_server_type != previous_server_type: + # server_type_name was added in PyMongo 3.4 + logging.info( + "Server {0.server_address} changed type from " + "{0.previous_description.server_type_name} to " + "{0.new_description.server_type_name}".format(event)) + + def closed(self, event): + logging.warning("Server {0.server_address} removed from topology " + "{0.topology_id}".format(event)) + + + class HeartbeatLogger(monitoring.ServerHeartbeatListener): + + def started(self, event): + logging.info("Heartbeat sent to server " + "{0.connection_id}".format(event)) + + def succeeded(self, event): + # The reply.document attribute was added in PyMongo 3.4. + logging.info("Heartbeat to server {0.connection_id} " + "succeeded with reply " + "{0.reply.document}".format(event)) + + def failed(self, event): + logging.warning("Heartbeat to server {0.connection_id} " + "failed with error {0.reply}".format(event)) + + class TopologyLogger(monitoring.TopologyListener): + + def opened(self, event): + logging.info("Topology with id {0.topology_id} " + "opened".format(event)) + + def description_changed(self, event): + logging.info("Topology description updated for " + "topology id {0.topology_id}".format(event)) + previous_topology_type = event.previous_description.topology_type + new_topology_type = event.new_description.topology_type + if new_topology_type != previous_topology_type: + # topology_type_name was added in PyMongo 3.4 + logging.info( + "Topology {0.topology_id} changed type from " + "{0.previous_description.topology_type_name} to " + "{0.new_description.topology_type_name}".format(event)) + # The has_writable_server and has_readable_server methods + # were added in PyMongo 3.4. + if not event.new_description.has_writable_server(): + logging.warning("No writable servers available.") + if not event.new_description.has_readable_server(): + logging.warning("No readable servers available.") + + def closed(self, event): + logging.info("Topology with id {0.topology_id} " + "closed".format(event)) + + +Event listeners can also be registered per instance of +:class:`~pymongo.mongo_client.MongoClient`:: + + client = MongoClient(event_listeners=[CommandLogger()]) + +Note that previously registered global listeners are automatically included +when configuring per client event listeners. Registering a new global listener +will not add that listener to existing client instances. + +.. note:: Events are delivered **synchronously**. Application threads block + waiting for event handlers (e.g. :meth:`~CommandListener.started`) to + return. Care must be taken to ensure that your event handlers are efficient + enough to not adversely affect overall application performance. + +.. warning:: The command documents published through this API are *not* copies. + If you intend to modify them in any way you must copy them in your event + handler first. +""" + +import sys +import traceback + +from collections import namedtuple + +from bson.py3compat import abc +from pymongo.helpers import _handle_exception + +_Listeners = namedtuple('Listeners', + ('command_listeners', 'server_listeners', + 'server_heartbeat_listeners', 'topology_listeners')) + +_LISTENERS = _Listeners([], [], [], []) + + +class _EventListener(object): + """Abstract base class for all event listeners.""" + + +class CommandListener(_EventListener): + """Abstract base class for command listeners. + Handles `CommandStartedEvent`, `CommandSucceededEvent`, + and `CommandFailedEvent`.""" + + def started(self, event): + """Abstract method to handle a `CommandStartedEvent`. + + :Parameters: + - `event`: An instance of :class:`CommandStartedEvent`. + """ + raise NotImplementedError + + def succeeded(self, event): + """Abstract method to handle a `CommandSucceededEvent`. + + :Parameters: + - `event`: An instance of :class:`CommandSucceededEvent`. + """ + raise NotImplementedError + + def failed(self, event): + """Abstract method to handle a `CommandFailedEvent`. + + :Parameters: + - `event`: An instance of :class:`CommandFailedEvent`. + """ + raise NotImplementedError + + +class ServerHeartbeatListener(_EventListener): + """Abstract base class for server heartbeat listeners. + Handles `ServerHeartbeatStartedEvent`, `ServerHeartbeatSucceededEvent`, + and `ServerHeartbeatFailedEvent`. + + .. versionadded:: 3.3 + """ + + def started(self, event): + """Abstract method to handle a `ServerHeartbeatStartedEvent`. + + :Parameters: + - `event`: An instance of :class:`ServerHeartbeatStartedEvent`. + """ + raise NotImplementedError + + def succeeded(self, event): + """Abstract method to handle a `ServerHeartbeatSucceededEvent`. + + :Parameters: + - `event`: An instance of :class:`ServerHeartbeatSucceededEvent`. + """ + raise NotImplementedError + + def failed(self, event): + """Abstract method to handle a `ServerHeartbeatFailedEvent`. + + :Parameters: + - `event`: An instance of :class:`ServerHeartbeatFailedEvent`. + """ + raise NotImplementedError + + +class TopologyListener(_EventListener): + """Abstract base class for topology monitoring listeners. + Handles `TopologyOpenedEvent`, `TopologyDescriptionChangedEvent`, and + `TopologyClosedEvent`. + + .. versionadded:: 3.3 + """ + + def opened(self, event): + """Abstract method to handle a `TopologyOpenedEvent`. + + :Parameters: + - `event`: An instance of :class:`TopologyOpenedEvent`. + """ + raise NotImplementedError + + def description_changed(self, event): + """Abstract method to handle a `TopologyDescriptionChangedEvent`. + + :Parameters: + - `event`: An instance of :class:`TopologyDescriptionChangedEvent`. + """ + raise NotImplementedError + + def closed(self, event): + """Abstract method to handle a `TopologyClosedEvent`. + + :Parameters: + - `event`: An instance of :class:`TopologyClosedEvent`. + """ + raise NotImplementedError + + +class ServerListener(_EventListener): + """Abstract base class for server listeners. + Handles `ServerOpeningEvent`, `ServerDescriptionChangedEvent`, and + `ServerClosedEvent`. + + .. versionadded:: 3.3 + """ + + def opened(self, event): + """Abstract method to handle a `ServerOpeningEvent`. + + :Parameters: + - `event`: An instance of :class:`ServerOpeningEvent`. + """ + raise NotImplementedError + + def description_changed(self, event): + """Abstract method to handle a `ServerDescriptionChangedEvent`. + + :Parameters: + - `event`: An instance of :class:`ServerDescriptionChangedEvent`. + """ + raise NotImplementedError + + def closed(self, event): + """Abstract method to handle a `ServerClosedEvent`. + + :Parameters: + - `event`: An instance of :class:`ServerClosedEvent`. + """ + raise NotImplementedError + + +def _to_micros(dur): + """Convert duration 'dur' to microseconds.""" + return int(dur.total_seconds() * 10e5) + + +def _validate_event_listeners(option, listeners): + """Validate event listeners""" + if not isinstance(listeners, abc.Sequence): + raise TypeError("%s must be a list or tuple" % (option,)) + for listener in listeners: + if not isinstance(listener, _EventListener): + raise TypeError("Listeners for %s must be either a " + "CommandListener, ServerHeartbeatListener, " + "ServerListener, or TopologyListener." % (option,)) + return listeners + + +def register(listener): + """Register a global event listener. + + :Parameters: + - `listener`: A subclasses of :class:`CommandListener`, + :class:`ServerHeartbeatListener`, :class:`ServerListener`, or + :class:`TopologyListener`. + """ + if not isinstance(listener, _EventListener): + raise TypeError("Listeners for %s must be either a " + "CommandListener, ServerHeartbeatListener, " + "ServerListener, or TopologyListener." % (listener,)) + if isinstance(listener, CommandListener): + _LISTENERS.command_listeners.append(listener) + if isinstance(listener, ServerHeartbeatListener): + _LISTENERS.server_heartbeat_listeners.append(listener) + if isinstance(listener, ServerListener): + _LISTENERS.server_listeners.append(listener) + if isinstance(listener, TopologyListener): + _LISTENERS.topology_listeners.append(listener) + + +# Note - to avoid bugs from forgetting which if these is all lowercase and +# which are camelCase, and at the same time avoid having to add a test for +# every command, use all lowercase here and test against command_name.lower(). +_SENSITIVE_COMMANDS = set( + ["authenticate", "saslstart", "saslcontinue", "getnonce", "createuser", + "updateuser", "copydbgetnonce", "copydbsaslstart", "copydb"]) + + +class _CommandEvent(object): + """Base class for command events.""" + + __slots__ = ("__cmd_name", "__rqst_id", "__conn_id", "__op_id") + + def __init__(self, command_name, request_id, connection_id, operation_id): + self.__cmd_name = command_name + self.__rqst_id = request_id + self.__conn_id = connection_id + self.__op_id = operation_id + + @property + def command_name(self): + """The command name.""" + return self.__cmd_name + + @property + def request_id(self): + """The request id for this operation.""" + return self.__rqst_id + + @property + def connection_id(self): + """The address (host, port) of the server this command was sent to.""" + return self.__conn_id + + @property + def operation_id(self): + """An id for this series of events or None.""" + return self.__op_id + + +class CommandStartedEvent(_CommandEvent): + """Event published when a command starts. + + :Parameters: + - `command`: The command document. + - `database_name`: The name of the database this command was run against. + - `request_id`: The request id for this operation. + - `connection_id`: The address (host, port) of the server this command + was sent to. + - `operation_id`: An optional identifier for a series of related events. + """ + __slots__ = ("__cmd", "__db") + + def __init__(self, command, database_name, *args): + if not command: + raise ValueError("%r is not a valid command" % (command,)) + # Command name must be first key. + command_name = next(iter(command)) + super(CommandStartedEvent, self).__init__(command_name, *args) + if command_name.lower() in _SENSITIVE_COMMANDS: + self.__cmd = {} + else: + self.__cmd = command + self.__db = database_name + + @property + def command(self): + """The command document.""" + return self.__cmd + + @property + def database_name(self): + """The name of the database this command was run against.""" + return self.__db + + +class CommandSucceededEvent(_CommandEvent): + """Event published when a command succeeds. + + :Parameters: + - `duration`: The command duration as a datetime.timedelta. + - `reply`: The server reply document. + - `command_name`: The command name. + - `request_id`: The request id for this operation. + - `connection_id`: The address (host, port) of the server this command + was sent to. + - `operation_id`: An optional identifier for a series of related events. + """ + __slots__ = ("__duration_micros", "__reply") + + def __init__(self, duration, reply, command_name, + request_id, connection_id, operation_id): + super(CommandSucceededEvent, self).__init__( + command_name, request_id, connection_id, operation_id) + self.__duration_micros = _to_micros(duration) + if command_name.lower() in _SENSITIVE_COMMANDS: + self.__reply = {} + else: + self.__reply = reply + + @property + def duration_micros(self): + """The duration of this operation in microseconds.""" + return self.__duration_micros + + @property + def reply(self): + """The server failure document for this operation.""" + return self.__reply + + +class CommandFailedEvent(_CommandEvent): + """Event published when a command fails. + + :Parameters: + - `duration`: The command duration as a datetime.timedelta. + - `failure`: The server reply document. + - `command_name`: The command name. + - `request_id`: The request id for this operation. + - `connection_id`: The address (host, port) of the server this command + was sent to. + - `operation_id`: An optional identifier for a series of related events. + """ + __slots__ = ("__duration_micros", "__failure") + + def __init__(self, duration, failure, *args): + super(CommandFailedEvent, self).__init__(*args) + self.__duration_micros = _to_micros(duration) + self.__failure = failure + + @property + def duration_micros(self): + """The duration of this operation in microseconds.""" + return self.__duration_micros + + @property + def failure(self): + """The server failure document for this operation.""" + return self.__failure + + +class _ServerEvent(object): + """Base class for server events.""" + + __slots__ = ("__server_address", "__topology_id") + + def __init__(self, server_address, topology_id): + self.__server_address = server_address + self.__topology_id = topology_id + + @property + def server_address(self): + """The address (host/port pair) of the server""" + return self.__server_address + + @property + def topology_id(self): + """A unique identifier for the topology this server is a part of.""" + return self.__topology_id + + +class ServerDescriptionChangedEvent(_ServerEvent): + """Published when server description changes. + + .. versionadded:: 3.3 + """ + + __slots__ = ('__previous_description', '__new_description') + + def __init__(self, previous_description, new_description, *args): + super(ServerDescriptionChangedEvent, self).__init__(*args) + self.__previous_description = previous_description + self.__new_description = new_description + + @property + def previous_description(self): + """The previous + :class:`~pymongo.server_description.ServerDescription`.""" + return self.__previous_description + + @property + def new_description(self): + """The new + :class:`~pymongo.server_description.ServerDescription`.""" + return self.__new_description + + +class ServerOpeningEvent(_ServerEvent): + """Published when server is initialized. + + .. versionadded:: 3.3 + """ + + __slots__ = () + + +class ServerClosedEvent(_ServerEvent): + """Published when server is closed. + + .. versionadded:: 3.3 + """ + + __slots__ = () + + +class TopologyEvent(object): + """Base class for topology description events.""" + + __slots__ = ('__topology_id') + + def __init__(self, topology_id): + self.__topology_id = topology_id + + @property + def topology_id(self): + """A unique identifier for the topology this server is a part of.""" + return self.__topology_id + + +class TopologyDescriptionChangedEvent(TopologyEvent): + """Published when the topology description changes. + + .. versionadded:: 3.3 + """ + + __slots__ = ('__previous_description', '__new_description') + + def __init__(self, previous_description, new_description, *args): + super(TopologyDescriptionChangedEvent, self).__init__(*args) + self.__previous_description = previous_description + self.__new_description = new_description + + @property + def previous_description(self): + """The previous + :class:`~pymongo.topology_description.TopologyDescription`.""" + return self.__previous_description + + @property + def new_description(self): + """The new + :class:`~pymongo.topology_description.TopologyDescription`.""" + return self.__new_description + + +class TopologyOpenedEvent(TopologyEvent): + """Published when the topology is initialized. + + .. versionadded:: 3.3 + """ + + __slots__ = () + + +class TopologyClosedEvent(TopologyEvent): + """Published when the topology is closed. + + .. versionadded:: 3.3 + """ + + __slots__ = () + + +class _ServerHeartbeatEvent(object): + """Base class for server heartbeat events.""" + + __slots__ = ('__connection_id') + + def __init__(self, connection_id): + self.__connection_id = connection_id + + @property + def connection_id(self): + """The address (host, port) of the server this heartbeat was sent + to.""" + return self.__connection_id + + +class ServerHeartbeatStartedEvent(_ServerHeartbeatEvent): + """Published when a heartbeat is started. + + .. versionadded:: 3.3 + """ + + __slots__ = () + + +class ServerHeartbeatSucceededEvent(_ServerHeartbeatEvent): + """Fired when the server heartbeat succeeds. + + .. versionadded:: 3.3 + """ + + __slots__ = ('__duration', '__reply') + + def __init__(self, duration, reply, *args): + super(ServerHeartbeatSucceededEvent, self).__init__(*args) + self.__duration = duration + self.__reply = reply + + @property + def duration(self): + """The duration of this heartbeat in microseconds.""" + return self.__duration + + @property + def reply(self): + """An instance of :class:`~pymongo.ismaster.IsMaster`.""" + return self.__reply + + +class ServerHeartbeatFailedEvent(_ServerHeartbeatEvent): + """Fired when the server heartbeat fails, either with an "ok: 0" + or a socket exception. + + .. versionadded:: 3.3 + """ + + __slots__ = ('__duration', '__reply') + + def __init__(self, duration, reply, *args): + super(ServerHeartbeatFailedEvent, self).__init__(*args) + self.__duration = duration + self.__reply = reply + + @property + def duration(self): + """The duration of this heartbeat in microseconds.""" + return self.__duration + + @property + def reply(self): + """A subclass of :exc:`Exception`.""" + return self.__reply + + +class _EventListeners(object): + """Configure event listeners for a client instance. + + Any event listeners registered globally are included by default. + + :Parameters: + - `listeners`: A list of event listeners. + """ + def __init__(self, listeners): + self.__command_listeners = _LISTENERS.command_listeners[:] + self.__server_listeners = _LISTENERS.server_listeners[:] + lst = _LISTENERS.server_heartbeat_listeners + self.__server_heartbeat_listeners = lst[:] + self.__topology_listeners = _LISTENERS.topology_listeners[:] + if listeners is not None: + for lst in listeners: + if isinstance(lst, CommandListener): + self.__command_listeners.append(lst) + if isinstance(lst, ServerListener): + self.__server_listeners.append(lst) + if isinstance(lst, ServerHeartbeatListener): + self.__server_heartbeat_listeners.append(lst) + if isinstance(lst, TopologyListener): + self.__topology_listeners.append(lst) + self.__enabled_for_commands = bool(self.__command_listeners) + self.__enabled_for_server = bool(self.__server_listeners) + self.__enabled_for_server_heartbeat = bool( + self.__server_heartbeat_listeners) + self.__enabled_for_topology = bool(self.__topology_listeners) + + @property + def enabled_for_commands(self): + """Are any CommandListener instances registered?""" + return self.__enabled_for_commands + + @property + def enabled_for_server(self): + """Are any ServerListener instances registered?""" + return self.__enabled_for_server + + @property + def enabled_for_server_heartbeat(self): + """Are any ServerHeartbeatListener instances registered?""" + return self.__enabled_for_server_heartbeat + + @property + def enabled_for_topology(self): + """Are any TopologyListener instances registered?""" + return self.__enabled_for_topology + + def event_listeners(self): + """List of registered event listeners.""" + return (self.__command_listeners[:], + self.__server_heartbeat_listeners[:], + self.__server_listeners[:], + self.__topology_listeners[:]) + + def publish_command_start(self, command, database_name, + request_id, connection_id, op_id=None): + """Publish a CommandStartedEvent to all command listeners. + + :Parameters: + - `command`: The command document. + - `database_name`: The name of the database this command was run + against. + - `request_id`: The request id for this operation. + - `connection_id`: The address (host, port) of the server this + command was sent to. + - `op_id`: The (optional) operation id for this operation. + """ + if op_id is None: + op_id = request_id + event = CommandStartedEvent( + command, database_name, request_id, connection_id, op_id) + for subscriber in self.__command_listeners: + try: + subscriber.started(event) + except Exception: + _handle_exception() + + def publish_command_success(self, duration, reply, command_name, + request_id, connection_id, op_id=None): + """Publish a CommandSucceededEvent to all command listeners. + + :Parameters: + - `duration`: The command duration as a datetime.timedelta. + - `reply`: The server reply document. + - `command_name`: The command name. + - `request_id`: The request id for this operation. + - `connection_id`: The address (host, port) of the server this + command was sent to. + - `op_id`: The (optional) operation id for this operation. + """ + if op_id is None: + op_id = request_id + event = CommandSucceededEvent( + duration, reply, command_name, request_id, connection_id, op_id) + for subscriber in self.__command_listeners: + try: + subscriber.succeeded(event) + except Exception: + _handle_exception() + + def publish_command_failure(self, duration, failure, command_name, + request_id, connection_id, op_id=None): + """Publish a CommandFailedEvent to all command listeners. + + :Parameters: + - `duration`: The command duration as a datetime.timedelta. + - `failure`: The server reply document or failure description + document. + - `command_name`: The command name. + - `request_id`: The request id for this operation. + - `connection_id`: The address (host, port) of the server this + command was sent to. + - `op_id`: The (optional) operation id for this operation. + """ + if op_id is None: + op_id = request_id + event = CommandFailedEvent( + duration, failure, command_name, request_id, connection_id, op_id) + for subscriber in self.__command_listeners: + try: + subscriber.failed(event) + except Exception: + _handle_exception() + + def publish_server_heartbeat_started(self, connection_id): + """Publish a ServerHeartbeatStartedEvent to all server heartbeat + listeners. + + :Parameters: + - `connection_id`: The address (host/port pair) of the connection. + """ + event = ServerHeartbeatStartedEvent(connection_id) + for subscriber in self.__server_heartbeat_listeners: + try: + subscriber.started(event) + except Exception: + _handle_exception() + + def publish_server_heartbeat_succeeded(self, connection_id, duration, + reply): + """Publish a ServerHeartbeatSucceededEvent to all server heartbeat + listeners. + + :Parameters: + - `connection_id`: The address (host/port pair) of the connection. + - `duration`: The execution time of the event in the highest possible + resolution for the platform. + - `reply`: The command reply. + """ + event = ServerHeartbeatSucceededEvent(duration, reply, connection_id) + for subscriber in self.__server_heartbeat_listeners: + try: + subscriber.succeeded(event) + except Exception: + _handle_exception() + + def publish_server_heartbeat_failed(self, connection_id, duration, reply): + """Publish a ServerHeartbeatFailedEvent to all server heartbeat + listeners. + + :Parameters: + - `connection_id`: The address (host/port pair) of the connection. + - `duration`: The execution time of the event in the highest possible + resolution for the platform. + - `reply`: The command reply. + """ + event = ServerHeartbeatFailedEvent(duration, reply, connection_id) + for subscriber in self.__server_heartbeat_listeners: + try: + subscriber.failed(event) + except Exception: + _handle_exception() + + def publish_server_opened(self, server_address, topology_id): + """Publish a ServerOpeningEvent to all server listeners. + + :Parameters: + - `server_address`: The address (host/port pair) of the server. + - `topology_id`: A unique identifier for the topology this server + is a part of. + """ + event = ServerOpeningEvent(server_address, topology_id) + for subscriber in self.__server_listeners: + try: + subscriber.opened(event) + except Exception: + _handle_exception() + + def publish_server_closed(self, server_address, topology_id): + """Publish a ServerClosedEvent to all server listeners. + + :Parameters: + - `server_address`: The address (host/port pair) of the server. + - `topology_id`: A unique identifier for the topology this server + is a part of. + """ + event = ServerClosedEvent(server_address, topology_id) + for subscriber in self.__server_listeners: + try: + subscriber.closed(event) + except Exception: + _handle_exception() + + def publish_server_description_changed(self, previous_description, + new_description, server_address, + topology_id): + """Publish a ServerDescriptionChangedEvent to all server listeners. + + :Parameters: + - `previous_description`: The previous server description. + - `server_address`: The address (host/port pair) of the server. + - `new_description`: The new server description. + - `topology_id`: A unique identifier for the topology this server + is a part of. + """ + event = ServerDescriptionChangedEvent(previous_description, + new_description, server_address, + topology_id) + for subscriber in self.__server_listeners: + try: + subscriber.description_changed(event) + except Exception: + _handle_exception() + + def publish_topology_opened(self, topology_id): + """Publish a TopologyOpenedEvent to all topology listeners. + + :Parameters: + - `topology_id`: A unique identifier for the topology this server + is a part of. + """ + event = TopologyOpenedEvent(topology_id) + for subscriber in self.__topology_listeners: + try: + subscriber.opened(event) + except Exception: + _handle_exception() + + def publish_topology_closed(self, topology_id): + """Publish a TopologyClosedEvent to all topology listeners. + + :Parameters: + - `topology_id`: A unique identifier for the topology this server + is a part of. + """ + event = TopologyClosedEvent(topology_id) + for subscriber in self.__topology_listeners: + try: + subscriber.closed(event) + except Exception: + _handle_exception() + + def publish_topology_description_changed(self, previous_description, + new_description, topology_id): + """Publish a TopologyDescriptionChangedEvent to all topology listeners. + + :Parameters: + - `previous_description`: The previous topology description. + - `new_description`: The new topology description. + - `topology_id`: A unique identifier for the topology this server + is a part of. + """ + event = TopologyDescriptionChangedEvent(previous_description, + new_description, topology_id) + for subscriber in self.__topology_listeners: + try: + subscriber.description_changed(event) + except Exception: + _handle_exception() diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/monotonic.py b/backend/venv/lib/python3.7/site-packages/pymongo/monotonic.py new file mode 100644 index 000000000..3be25b8b1 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/monotonic.py @@ -0,0 +1,38 @@ +# Copyright 2014-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Time. Monotonic if possible. +""" + +from __future__ import absolute_import + +__all__ = ['time'] + +try: + # Patches standard time module. + # From https://pypi.python.org/pypi/Monotime. + import monotime +except ImportError: + pass + +try: + # From https://pypi.python.org/pypi/monotonic. + from monotonic import monotonic as time +except ImportError: + try: + # Monotime or Python 3. + from time import monotonic as time + except ImportError: + # Not monotonic. + from time import time diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/network.py b/backend/venv/lib/python3.7/site-packages/pymongo/network.py new file mode 100644 index 000000000..c9ee2b13b --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/network.py @@ -0,0 +1,305 @@ +# Copyright 2015-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Internal network layer helper methods.""" + +import datetime +import errno +import select +import struct +import threading + +_HAS_POLL = True +_EVENT_MASK = 0 +try: + from select import poll + _EVENT_MASK = ( + select.POLLIN | select.POLLPRI | select.POLLERR | select.POLLHUP) +except ImportError: + _HAS_POLL = False + +try: + from select import error as _SELECT_ERROR +except ImportError: + _SELECT_ERROR = OSError + +from bson.py3compat import PY3 + +from pymongo import helpers, message +from pymongo.common import MAX_MESSAGE_SIZE +from pymongo.compression_support import decompress, _NO_COMPRESSION +from pymongo.errors import (AutoReconnect, + NotMasterError, + OperationFailure, + ProtocolError) +from pymongo.message import _UNPACK_REPLY + + +_UNPACK_HEADER = struct.Struct(" max_bson_size): + message._raise_document_too_large(name, size, max_bson_size) + else: + request_id, msg, size = message.query( + flags, ns, 0, -1, spec, None, codec_options, check_keys, + compression_ctx) + + if (max_bson_size is not None + and size > max_bson_size + message._COMMAND_OVERHEAD): + message._raise_document_too_large( + name, size, max_bson_size + message._COMMAND_OVERHEAD) + + if publish: + encoding_duration = datetime.datetime.now() - start + listeners.publish_command_start(orig, dbname, request_id, address) + start = datetime.datetime.now() + + try: + sock.sendall(msg) + if use_op_msg and unacknowledged: + # Unacknowledged, fake a successful command response. + response_doc = {"ok": 1} + else: + reply = receive_message(sock, request_id) + unpacked_docs = reply.unpack_response( + codec_options=codec_options, user_fields=user_fields) + + response_doc = unpacked_docs[0] + if client: + client._receive_cluster_time(response_doc, session) + if check: + helpers._check_command_response( + response_doc, None, allowable_errors, + parse_write_concern_error=parse_write_concern_error) + except Exception as exc: + if publish: + duration = (datetime.datetime.now() - start) + encoding_duration + if isinstance(exc, (NotMasterError, OperationFailure)): + failure = exc.details + else: + failure = message._convert_exception(exc) + listeners.publish_command_failure( + duration, failure, name, request_id, address) + raise + if publish: + duration = (datetime.datetime.now() - start) + encoding_duration + listeners.publish_command_success( + duration, response_doc, name, request_id, address) + return response_doc + +_UNPACK_COMPRESSION_HEADER = struct.Struct(" max_message_size: + raise ProtocolError("Message length (%r) is larger than server max " + "message size (%r)" % (length, max_message_size)) + if op_code == 2012: + op_code, _, compressor_id = _UNPACK_COMPRESSION_HEADER( + _receive_data_on_socket(sock, 9)) + data = decompress( + _receive_data_on_socket(sock, length - 25), compressor_id) + else: + data = _receive_data_on_socket(sock, length - 16) + + try: + unpack_reply = _UNPACK_REPLY[op_code] + except KeyError: + raise ProtocolError("Got opcode %r but expected " + "%r" % (op_code, _UNPACK_REPLY.keys())) + return unpack_reply(data) + + +# memoryview was introduced in Python 2.7 but we only use it on Python 3 +# because before 2.7.4 the struct module did not support memoryview: +# https://bugs.python.org/issue10212. +# In Jython, using slice assignment on a memoryview results in a +# NullPointerException. +if not PY3: + def _receive_data_on_socket(sock, length): + buf = bytearray(length) + i = 0 + while length: + try: + chunk = sock.recv(length) + except (IOError, OSError) as exc: + if _errno_from_exception(exc) == errno.EINTR: + continue + raise + if chunk == b"": + raise AutoReconnect("connection closed") + + buf[i:i + len(chunk)] = chunk + i += len(chunk) + length -= len(chunk) + + return bytes(buf) +else: + def _receive_data_on_socket(sock, length): + buf = bytearray(length) + mv = memoryview(buf) + bytes_read = 0 + while bytes_read < length: + try: + chunk_length = sock.recv_into(mv[bytes_read:]) + except (IOError, OSError) as exc: + if _errno_from_exception(exc) == errno.EINTR: + continue + raise + if chunk_length == 0: + raise AutoReconnect("connection closed") + + bytes_read += chunk_length + + return mv + + +def _errno_from_exception(exc): + if hasattr(exc, 'errno'): + return exc.errno + elif exc.args: + return exc.args[0] + else: + return None + + +class SocketChecker(object): + + def __init__(self): + if _HAS_POLL: + self._lock = threading.Lock() + self._poller = poll() + else: + self._lock = None + self._poller = None + + def socket_closed(self, sock): + """Return True if we know socket has been closed, False otherwise. + """ + while True: + try: + if self._poller: + with self._lock: + self._poller.register(sock, _EVENT_MASK) + try: + rd = self._poller.poll(0) + finally: + self._poller.unregister(sock) + else: + rd, _, _ = select.select([sock], [], [], 0) + except (RuntimeError, KeyError): + # RuntimeError is raised during a concurrent poll. KeyError + # is raised by unregister if the socket is not in the poller. + # These errors should not be possible since we protect the + # poller with a mutex. + raise + except ValueError: + # ValueError is raised by register/unregister/select if the + # socket file descriptor is negative or outside the range for + # select (> 1023). + return True + except (_SELECT_ERROR, IOError) as exc: + if _errno_from_exception(exc) in (errno.EINTR, errno.EAGAIN): + continue + return True + except Exception: + # Any other exceptions should be attributed to a closed + # or invalid socket. + return True + return len(rd) > 0 diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/operations.py b/backend/venv/lib/python3.7/site-packages/pymongo/operations.py new file mode 100644 index 000000000..acec05e1f --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/operations.py @@ -0,0 +1,370 @@ +# Copyright 2015-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Operation class definitions.""" + +from pymongo.common import validate_boolean, validate_is_mapping, validate_list +from pymongo.collation import validate_collation_or_none +from pymongo.helpers import _gen_index_name, _index_document, _index_list + + +class InsertOne(object): + """Represents an insert_one operation.""" + + __slots__ = ("_doc",) + + def __init__(self, document): + """Create an InsertOne instance. + + For use with :meth:`~pymongo.collection.Collection.bulk_write`. + + :Parameters: + - `document`: The document to insert. If the document is missing an + _id field one will be added. + """ + self._doc = document + + def _add_to_bulk(self, bulkobj): + """Add this operation to the _Bulk instance `bulkobj`.""" + bulkobj.add_insert(self._doc) + + def __repr__(self): + return "InsertOne(%r)" % (self._doc,) + + def __eq__(self, other): + if type(other) == type(self): + return other._doc == self._doc + return NotImplemented + + def __ne__(self, other): + return not self == other + + +class DeleteOne(object): + """Represents a delete_one operation.""" + + __slots__ = ("_filter", "_collation") + + def __init__(self, filter, collation=None): + """Create a DeleteOne instance. + + For use with :meth:`~pymongo.collection.Collection.bulk_write`. + + :Parameters: + - `filter`: A query that matches the document to delete. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only + supported on MongoDB 3.4 and above. + + .. versionchanged:: 3.5 + Added the `collation` option. + """ + if filter is not None: + validate_is_mapping("filter", filter) + self._filter = filter + self._collation = collation + + def _add_to_bulk(self, bulkobj): + """Add this operation to the _Bulk instance `bulkobj`.""" + bulkobj.add_delete(self._filter, 1, collation=self._collation) + + def __repr__(self): + return "DeleteOne(%r, %r)" % (self._filter, self._collation) + + def __eq__(self, other): + if type(other) == type(self): + return ((other._filter, other._collation) == + (self._filter, self._collation)) + return NotImplemented + + def __ne__(self, other): + return not self == other + + +class DeleteMany(object): + """Represents a delete_many operation.""" + + __slots__ = ("_filter", "_collation") + + def __init__(self, filter, collation=None): + """Create a DeleteMany instance. + + For use with :meth:`~pymongo.collection.Collection.bulk_write`. + + :Parameters: + - `filter`: A query that matches the documents to delete. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only + supported on MongoDB 3.4 and above. + + .. versionchanged:: 3.5 + Added the `collation` option. + """ + if filter is not None: + validate_is_mapping("filter", filter) + self._filter = filter + self._collation = collation + + def _add_to_bulk(self, bulkobj): + """Add this operation to the _Bulk instance `bulkobj`.""" + bulkobj.add_delete(self._filter, 0, collation=self._collation) + + def __repr__(self): + return "DeleteMany(%r, %r)" % (self._filter, self._collation) + + def __eq__(self, other): + if type(other) == type(self): + return ((other._filter, other._collation) == + (self._filter, self._collation)) + return NotImplemented + + def __ne__(self, other): + return not self == other + + +class ReplaceOne(object): + """Represents a replace_one operation.""" + + __slots__ = ("_filter", "_doc", "_upsert", "_collation") + + def __init__(self, filter, replacement, upsert=False, collation=None): + """Create a ReplaceOne instance. + + For use with :meth:`~pymongo.collection.Collection.bulk_write`. + + :Parameters: + - `filter`: A query that matches the document to replace. + - `replacement`: The new document. + - `upsert` (optional): If ``True``, perform an insert if no documents + match the filter. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only + supported on MongoDB 3.4 and above. + + .. versionchanged:: 3.5 + Added the `collation` option. + """ + if filter is not None: + validate_is_mapping("filter", filter) + if upsert is not None: + validate_boolean("upsert", upsert) + self._filter = filter + self._doc = replacement + self._upsert = upsert + self._collation = collation + + def _add_to_bulk(self, bulkobj): + """Add this operation to the _Bulk instance `bulkobj`.""" + bulkobj.add_replace(self._filter, self._doc, self._upsert, + collation=self._collation) + + def __eq__(self, other): + if type(other) == type(self): + return ( + (other._filter, other._doc, other._upsert, other._collation) == + (self._filter, self._doc, self._upsert, self._collation)) + return NotImplemented + + def __ne__(self, other): + return not self == other + + def __repr__(self): + return "%s(%r, %r, %r, %r)" % ( + self.__class__.__name__, self._filter, self._doc, self._upsert, + self._collation) + + +class _UpdateOp(object): + """Private base class for update operations.""" + + __slots__ = ("_filter", "_doc", "_upsert", "_collation", "_array_filters") + + def __init__(self, filter, doc, upsert, collation, array_filters): + if filter is not None: + validate_is_mapping("filter", filter) + if upsert is not None: + validate_boolean("upsert", upsert) + if array_filters is not None: + validate_list("array_filters", array_filters) + self._filter = filter + self._doc = doc + self._upsert = upsert + self._collation = collation + self._array_filters = array_filters + + def __eq__(self, other): + if type(other) == type(self): + return ( + (other._filter, other._doc, other._upsert, other._collation, + other._array_filters) == + (self._filter, self._doc, self._upsert, self._collation, + self._array_filters)) + return NotImplemented + + def __ne__(self, other): + return not self == other + + def __repr__(self): + return "%s(%r, %r, %r, %r, %r)" % ( + self.__class__.__name__, self._filter, self._doc, self._upsert, + self._collation, self._array_filters) + + +class UpdateOne(_UpdateOp): + """Represents an update_one operation.""" + + __slots__ = () + + def __init__(self, filter, update, upsert=False, collation=None, + array_filters=None): + """Represents an update_one operation. + + For use with :meth:`~pymongo.collection.Collection.bulk_write`. + + :Parameters: + - `filter`: A query that matches the document to update. + - `update`: The modifications to apply. + - `upsert` (optional): If ``True``, perform an insert if no documents + match the filter. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only + supported on MongoDB 3.4 and above. + - `array_filters` (optional): A list of filters specifying which + array elements an update should apply. Requires MongoDB 3.6+. + + .. versionchanged:: 3.6 + Added the `array_filters` option. + .. versionchanged:: 3.5 + Added the `collation` option. + """ + super(UpdateOne, self).__init__(filter, update, upsert, collation, + array_filters) + + def _add_to_bulk(self, bulkobj): + """Add this operation to the _Bulk instance `bulkobj`.""" + bulkobj.add_update(self._filter, self._doc, False, self._upsert, + collation=self._collation, + array_filters=self._array_filters) + + +class UpdateMany(_UpdateOp): + """Represents an update_many operation.""" + + __slots__ = () + + def __init__(self, filter, update, upsert=False, collation=None, + array_filters=None): + """Create an UpdateMany instance. + + For use with :meth:`~pymongo.collection.Collection.bulk_write`. + + :Parameters: + - `filter`: A query that matches the documents to update. + - `update`: The modifications to apply. + - `upsert` (optional): If ``True``, perform an insert if no documents + match the filter. + - `collation` (optional): An instance of + :class:`~pymongo.collation.Collation`. This option is only + supported on MongoDB 3.4 and above. + - `array_filters` (optional): A list of filters specifying which + array elements an update should apply. Requires MongoDB 3.6+. + + .. versionchanged:: 3.6 + Added the `array_filters` option. + .. versionchanged:: 3.5 + Added the `collation` option. + """ + super(UpdateMany, self).__init__(filter, update, upsert, collation, + array_filters) + + def _add_to_bulk(self, bulkobj): + """Add this operation to the _Bulk instance `bulkobj`.""" + bulkobj.add_update(self._filter, self._doc, True, self._upsert, + collation=self._collation, + array_filters=self._array_filters) + + +class IndexModel(object): + """Represents an index to create.""" + + __slots__ = ("__document",) + + def __init__(self, keys, **kwargs): + """Create an Index instance. + + For use with :meth:`~pymongo.collection.Collection.create_indexes`. + + Takes either a single key or a list of (key, direction) pairs. + The key(s) must be an instance of :class:`basestring` + (:class:`str` in python 3), and the direction(s) must be one of + (:data:`~pymongo.ASCENDING`, :data:`~pymongo.DESCENDING`, + :data:`~pymongo.GEO2D`, :data:`~pymongo.GEOHAYSTACK`, + :data:`~pymongo.GEOSPHERE`, :data:`~pymongo.HASHED`, + :data:`~pymongo.TEXT`). + + Valid options include, but are not limited to: + + - `name`: custom name to use for this index - if none is + given, a name will be generated. + - `unique`: if ``True`` creates a uniqueness constraint on the index. + - `background`: if ``True`` this index should be created in the + background. + - `sparse`: if ``True``, omit from the index any documents that lack + the indexed field. + - `bucketSize`: for use with geoHaystack indexes. + Number of documents to group together within a certain proximity + to a given longitude and latitude. + - `min`: minimum value for keys in a :data:`~pymongo.GEO2D` + index. + - `max`: maximum value for keys in a :data:`~pymongo.GEO2D` + index. + - `expireAfterSeconds`: Used to create an expiring (TTL) + collection. MongoDB will automatically delete documents from + this collection after seconds. The indexed field must + be a UTC datetime or the data will not expire. + - `partialFilterExpression`: A document that specifies a filter for + a partial index. + - `collation`: An instance of :class:`~pymongo.collation.Collation` + that specifies the collation to use in MongoDB >= 3.4. + + See the MongoDB documentation for a full list of supported options by + server version. + + .. note:: `partialFilterExpression` requires server version **>= 3.2** + + :Parameters: + - `keys`: a single key or a list of (key, direction) + pairs specifying the index to create + - `**kwargs` (optional): any additional index creation + options (see the above list) should be passed as keyword + arguments + + .. versionchanged:: 3.2 + Added partialFilterExpression to support partial indexes. + """ + keys = _index_list(keys) + if "name" not in kwargs: + kwargs["name"] = _gen_index_name(keys) + kwargs["key"] = _index_document(keys) + collation = validate_collation_or_none(kwargs.pop('collation', None)) + self.__document = kwargs + if collation is not None: + self.__document['collation'] = collation + + @property + def document(self): + """An index document suitable for passing to the createIndexes + command. + """ + return self.__document diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/periodic_executor.py b/backend/venv/lib/python3.7/site-packages/pymongo/periodic_executor.py new file mode 100644 index 000000000..2a325446e --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/periodic_executor.py @@ -0,0 +1,174 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Run a target function on a background thread.""" + +import atexit +import threading +import time +import weakref + +from pymongo.monotonic import time as _time + + +class PeriodicExecutor(object): + def __init__(self, interval, min_interval, target, name=None): + """"Run a target function periodically on a background thread. + + If the target's return value is false, the executor stops. + + :Parameters: + - `interval`: Seconds between calls to `target`. + - `min_interval`: Minimum seconds between calls if `wake` is + called very often. + - `target`: A function. + - `name`: A name to give the underlying thread. + """ + # threading.Event and its internal condition variable are expensive + # in Python 2, see PYTHON-983. Use a boolean to know when to wake. + # The executor's design is constrained by several Python issues, see + # "periodic_executor.rst" in this repository. + self._event = False + self._interval = interval + self._min_interval = min_interval + self._target = target + self._stopped = False + self._thread = None + self._name = name + + self._thread_will_exit = False + self._lock = threading.Lock() + + def open(self): + """Start. Multiple calls have no effect. + + Not safe to call from multiple threads at once. + """ + with self._lock: + if self._thread_will_exit: + # If the background thread has read self._stopped as True + # there is a chance that it has not yet exited. The call to + # join should not block indefinitely because there is no + # other work done outside the while loop in self._run. + try: + self._thread.join() + except ReferenceError: + # Thread terminated. + pass + self._thread_will_exit = False + self._stopped = False + started = False + try: + started = self._thread and self._thread.is_alive() + except ReferenceError: + # Thread terminated. + pass + + if not started: + thread = threading.Thread(target=self._run, name=self._name) + thread.daemon = True + self._thread = weakref.proxy(thread) + _register_executor(self) + thread.start() + + def close(self, dummy=None): + """Stop. To restart, call open(). + + The dummy parameter allows an executor's close method to be a weakref + callback; see monitor.py. + """ + self._stopped = True + + def join(self, timeout=None): + if self._thread is not None: + try: + self._thread.join(timeout) + except (ReferenceError, RuntimeError): + # Thread already terminated, or not yet started. + pass + + def wake(self): + """Execute the target function soon.""" + self._event = True + + def __should_stop(self): + with self._lock: + if self._stopped: + self._thread_will_exit = True + return True + return False + + def _run(self): + while not self.__should_stop(): + try: + if not self._target(): + self._stopped = True + break + except: + with self._lock: + self._stopped = True + self._thread_will_exit = True + + raise + + deadline = _time() + self._interval + + while not self._stopped and _time() < deadline: + time.sleep(self._min_interval) + if self._event: + break # Early wake. + + self._event = False + + +# _EXECUTORS has a weakref to each running PeriodicExecutor. Once started, +# an executor is kept alive by a strong reference from its thread and perhaps +# from other objects. When the thread dies and all other referrers are freed, +# the executor is freed and removed from _EXECUTORS. If any threads are +# running when the interpreter begins to shut down, we try to halt and join +# them to avoid spurious errors. +_EXECUTORS = set() + + +def _register_executor(executor): + ref = weakref.ref(executor, _on_executor_deleted) + _EXECUTORS.add(ref) + + +def _on_executor_deleted(ref): + _EXECUTORS.remove(ref) + + +def _shutdown_executors(): + if _EXECUTORS is None: + return + + # Copy the set. Stopping threads has the side effect of removing executors. + executors = list(_EXECUTORS) + + # First signal all executors to close... + for ref in executors: + executor = ref() + if executor: + executor.close() + + # ...then try to join them. + for ref in executors: + executor = ref() + if executor: + executor.join(1) + + executor = None + +atexit.register(_shutdown_executors) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/pool.py b/backend/venv/lib/python3.7/site-packages/pymongo/pool.py new file mode 100644 index 000000000..15d98fd09 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/pool.py @@ -0,0 +1,1113 @@ +# Copyright 2011-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +import contextlib +import copy +import os +import platform +import socket +import sys +import threading +import collections + +try: + import ssl + from ssl import SSLError + _HAVE_SNI = getattr(ssl, 'HAS_SNI', False) +except ImportError: + _HAVE_SNI = False + class SSLError(socket.error): + pass + +try: + from ssl import CertificateError as _SSLCertificateError +except ImportError: + class _SSLCertificateError(ValueError): + pass + + +from bson import DEFAULT_CODEC_OPTIONS +from bson.py3compat import imap, itervalues, _unicode, integer_types +from bson.son import SON +from pymongo import auth, helpers, thread_util, __version__ +from pymongo.client_session import _validate_session_write_concern +from pymongo.common import (MAX_BSON_SIZE, + MAX_MESSAGE_SIZE, + MAX_WIRE_VERSION, + MAX_WRITE_BATCH_SIZE, + ORDERED_TYPES) +from pymongo.errors import (AutoReconnect, + ConnectionFailure, + ConfigurationError, + InvalidOperation, + DocumentTooLarge, + NetworkTimeout, + NotMasterError, + OperationFailure) +from pymongo.ismaster import IsMaster +from pymongo.monotonic import time as _time +from pymongo.network import (command, + receive_message, + SocketChecker) +from pymongo.read_preferences import ReadPreference +from pymongo.server_type import SERVER_TYPE +# Always use our backport so we always have support for IP address matching +from pymongo.ssl_match_hostname import match_hostname, CertificateError + +# For SNI support. According to RFC6066, section 3, IPv4 and IPv6 literals are +# not permitted for SNI hostname. +try: + from ipaddress import ip_address + def is_ip_address(address): + try: + ip_address(_unicode(address)) + return True + except (ValueError, UnicodeError): + return False +except ImportError: + if hasattr(socket, 'inet_pton') and socket.has_ipv6: + # Most *nix, recent Windows + def is_ip_address(address): + try: + # inet_pton rejects IPv4 literals with leading zeros + # (e.g. 192.168.0.01), inet_aton does not, and we + # can connect to them without issue. Use inet_aton. + socket.inet_aton(address) + return True + except socket.error: + try: + socket.inet_pton(socket.AF_INET6, address) + return True + except socket.error: + return False + else: + # No inet_pton + def is_ip_address(address): + try: + socket.inet_aton(address) + return True + except socket.error: + if ':' in address: + # ':' is not a valid character for a hostname. If we get + # here a few things have to be true: + # - We're on a recent version of python 2.7 (2.7.9+). + # Older 2.7 versions don't support SNI. + # - We're on Windows XP or some unusual Unix that doesn't + # have inet_pton. + # - The application is using IPv6 literals with TLS, which + # is pretty unusual. + return True + return False + +try: + from fcntl import fcntl, F_GETFD, F_SETFD, FD_CLOEXEC + def _set_non_inheritable_non_atomic(fd): + """Set the close-on-exec flag on the given file descriptor.""" + flags = fcntl(fd, F_GETFD) + fcntl(fd, F_SETFD, flags | FD_CLOEXEC) +except ImportError: + # Windows, various platforms we don't claim to support + # (Jython, IronPython, ...), systems that don't provide + # everything we need from fcntl, etc. + def _set_non_inheritable_non_atomic(dummy): + """Dummy function for platforms that don't provide fcntl.""" + pass + +_MAX_TCP_KEEPIDLE = 300 +_MAX_TCP_KEEPINTVL = 10 +_MAX_TCP_KEEPCNT = 9 + +if sys.platform == 'win32': + try: + import _winreg as winreg + except ImportError: + import winreg + + try: + with winreg.OpenKey( + winreg.HKEY_LOCAL_MACHINE, + r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters") as key: + _DEFAULT_TCP_IDLE_MS, _ = winreg.QueryValueEx(key, "KeepAliveTime") + _DEFAULT_TCP_INTERVAL_MS, _ = winreg.QueryValueEx( + key, "KeepAliveInterval") + # Make sure these are integers. + if not isinstance(_DEFAULT_TCP_IDLE_MS, integer_types): + raise ValueError + if not isinstance(_DEFAULT_TCP_INTERVAL_MS, integer_types): + raise ValueError + except (OSError, ValueError): + # We could not check the default values so do not attempt to override. + def _set_keepalive_times(dummy): + pass + else: + def _set_keepalive_times(sock): + idle_ms = min(_DEFAULT_TCP_IDLE_MS, _MAX_TCP_KEEPIDLE * 1000) + interval_ms = min(_DEFAULT_TCP_INTERVAL_MS, + _MAX_TCP_KEEPINTVL * 1000) + if (idle_ms < _DEFAULT_TCP_IDLE_MS or + interval_ms < _DEFAULT_TCP_INTERVAL_MS): + sock.ioctl(socket.SIO_KEEPALIVE_VALS, + (1, idle_ms, interval_ms)) +else: + def _set_tcp_option(sock, tcp_option, max_value): + if hasattr(socket, tcp_option): + sockopt = getattr(socket, tcp_option) + try: + # PYTHON-1350 - NetBSD doesn't implement getsockopt for + # TCP_KEEPIDLE and friends. Don't attempt to set the + # values there. + default = sock.getsockopt(socket.IPPROTO_TCP, sockopt) + if default > max_value: + sock.setsockopt(socket.IPPROTO_TCP, sockopt, max_value) + except socket.error: + pass + + def _set_keepalive_times(sock): + _set_tcp_option(sock, 'TCP_KEEPIDLE', _MAX_TCP_KEEPIDLE) + _set_tcp_option(sock, 'TCP_KEEPINTVL', _MAX_TCP_KEEPINTVL) + _set_tcp_option(sock, 'TCP_KEEPCNT', _MAX_TCP_KEEPCNT) + +_METADATA = SON([ + ('driver', SON([('name', 'PyMongo'), ('version', __version__)])), +]) + +if sys.platform.startswith('linux'): + # platform.linux_distribution was deprecated in Python 3.5. + if sys.version_info[:2] < (3, 5): + # Distro name and version (e.g. Ubuntu 16.04 xenial) + _name = ' '.join([part for part in + platform.linux_distribution() if part]) + else: + _name = platform.system() + _METADATA['os'] = SON([ + ('type', platform.system()), + ('name', _name), + ('architecture', platform.machine()), + # Kernel version (e.g. 4.4.0-17-generic). + ('version', platform.release()) + ]) +elif sys.platform == 'darwin': + _METADATA['os'] = SON([ + ('type', platform.system()), + ('name', platform.system()), + ('architecture', platform.machine()), + # (mac|i|tv)OS(X) version (e.g. 10.11.6) instead of darwin + # kernel version. + ('version', platform.mac_ver()[0]) + ]) +elif sys.platform == 'win32': + _METADATA['os'] = SON([ + ('type', platform.system()), + # "Windows XP", "Windows 7", "Windows 10", etc. + ('name', ' '.join((platform.system(), platform.release()))), + ('architecture', platform.machine()), + # Windows patch level (e.g. 5.1.2600-SP3) + ('version', '-'.join(platform.win32_ver()[1:3])) + ]) +elif sys.platform.startswith('java'): + _name, _ver, _arch = platform.java_ver()[-1] + _METADATA['os'] = SON([ + # Linux, Windows 7, Mac OS X, etc. + ('type', _name), + ('name', _name), + # x86, x86_64, AMD64, etc. + ('architecture', _arch), + # Linux kernel version, OSX version, etc. + ('version', _ver) + ]) +else: + # Get potential alias (e.g. SunOS 5.11 becomes Solaris 2.11) + _aliased = platform.system_alias( + platform.system(), platform.release(), platform.version()) + _METADATA['os'] = SON([ + ('type', platform.system()), + ('name', ' '.join([part for part in _aliased[:2] if part])), + ('architecture', platform.machine()), + ('version', _aliased[2]) + ]) + +if platform.python_implementation().startswith('PyPy'): + _METADATA['platform'] = ' '.join( + (platform.python_implementation(), + '.'.join(imap(str, sys.pypy_version_info)), + '(Python %s)' % '.'.join(imap(str, sys.version_info)))) +elif sys.platform.startswith('java'): + _METADATA['platform'] = ' '.join( + (platform.python_implementation(), + '.'.join(imap(str, sys.version_info)), + '(%s)' % ' '.join((platform.system(), platform.release())))) +else: + _METADATA['platform'] = ' '.join( + (platform.python_implementation(), + '.'.join(imap(str, sys.version_info)))) + + +# If the first getaddrinfo call of this interpreter's life is on a thread, +# while the main thread holds the import lock, getaddrinfo deadlocks trying +# to import the IDNA codec. Import it here, where presumably we're on the +# main thread, to avoid the deadlock. See PYTHON-607. +u'foo'.encode('idna') + + +def _raise_connection_failure(address, error, msg_prefix=None): + """Convert a socket.error to ConnectionFailure and raise it.""" + host, port = address + # If connecting to a Unix socket, port will be None. + if port is not None: + msg = '%s:%d: %s' % (host, port, error) + else: + msg = '%s: %s' % (host, error) + if msg_prefix: + msg = msg_prefix + msg + if isinstance(error, socket.timeout): + raise NetworkTimeout(msg) + elif isinstance(error, SSLError) and 'timed out' in str(error): + # CPython 2.7 and PyPy 2.x do not distinguish network + # timeouts from other SSLErrors (https://bugs.python.org/issue10272). + # Luckily, we can work around this limitation because the phrase + # 'timed out' appears in all the timeout related SSLErrors raised + # on the above platforms. + raise NetworkTimeout(msg) + else: + raise AutoReconnect(msg) + + +class PoolOptions(object): + + __slots__ = ('__max_pool_size', '__min_pool_size', + '__max_idle_time_seconds', + '__connect_timeout', '__socket_timeout', + '__wait_queue_timeout', '__wait_queue_multiple', + '__ssl_context', '__ssl_match_hostname', '__socket_keepalive', + '__event_listeners', '__appname', '__driver', '__metadata', + '__compression_settings') + + def __init__(self, max_pool_size=100, min_pool_size=0, + max_idle_time_seconds=None, connect_timeout=None, + socket_timeout=None, wait_queue_timeout=None, + wait_queue_multiple=None, ssl_context=None, + ssl_match_hostname=True, socket_keepalive=True, + event_listeners=None, appname=None, driver=None, + compression_settings=None): + + self.__max_pool_size = max_pool_size + self.__min_pool_size = min_pool_size + self.__max_idle_time_seconds = max_idle_time_seconds + self.__connect_timeout = connect_timeout + self.__socket_timeout = socket_timeout + self.__wait_queue_timeout = wait_queue_timeout + self.__wait_queue_multiple = wait_queue_multiple + self.__ssl_context = ssl_context + self.__ssl_match_hostname = ssl_match_hostname + self.__socket_keepalive = socket_keepalive + self.__event_listeners = event_listeners + self.__appname = appname + self.__driver = driver + self.__compression_settings = compression_settings + self.__metadata = copy.deepcopy(_METADATA) + if appname: + self.__metadata['application'] = {'name': appname} + + # Combine the "driver" MongoClient option with PyMongo's info, like: + # { + # 'driver': { + # 'name': 'PyMongo|MyDriver', + # 'version': '3.7.0|1.2.3', + # }, + # 'platform': 'CPython 3.6.0|MyPlatform' + # } + if driver: + if driver.name: + self.__metadata['driver']['name'] = "%s|%s" % ( + _METADATA['driver']['name'], driver.name) + if driver.version: + self.__metadata['driver']['version'] = "%s|%s" % ( + _METADATA['driver']['version'], driver.version) + if driver.platform: + self.__metadata['platform'] = "%s|%s" % ( + _METADATA['platform'], driver.platform) + + @property + def max_pool_size(self): + """The maximum allowable number of concurrent connections to each + connected server. Requests to a server will block if there are + `maxPoolSize` outstanding connections to the requested server. + Defaults to 100. Cannot be 0. + + When a server's pool has reached `max_pool_size`, operations for that + server block waiting for a socket to be returned to the pool. If + ``waitQueueTimeoutMS`` is set, a blocked operation will raise + :exc:`~pymongo.errors.ConnectionFailure` after a timeout. + By default ``waitQueueTimeoutMS`` is not set. + """ + return self.__max_pool_size + + @property + def min_pool_size(self): + """The minimum required number of concurrent connections that the pool + will maintain to each connected server. Default is 0. + """ + return self.__min_pool_size + + @property + def max_idle_time_seconds(self): + """The maximum number of seconds that a connection can remain + idle in the pool before being removed and replaced. Defaults to + `None` (no limit). + """ + return self.__max_idle_time_seconds + + @property + def connect_timeout(self): + """How long a connection can take to be opened before timing out. + """ + return self.__connect_timeout + + @property + def socket_timeout(self): + """How long a send or receive on a socket can take before timing out. + """ + return self.__socket_timeout + + @property + def wait_queue_timeout(self): + """How long a thread will wait for a socket from the pool if the pool + has no free sockets. + """ + return self.__wait_queue_timeout + + @property + def wait_queue_multiple(self): + """Multiplied by max_pool_size to give the number of threads allowed + to wait for a socket at one time. + """ + return self.__wait_queue_multiple + + @property + def ssl_context(self): + """An SSLContext instance or None. + """ + return self.__ssl_context + + @property + def ssl_match_hostname(self): + """Call ssl.match_hostname if cert_reqs is not ssl.CERT_NONE. + """ + return self.__ssl_match_hostname + + @property + def socket_keepalive(self): + """Whether to send periodic messages to determine if a connection + is closed. + """ + return self.__socket_keepalive + + @property + def event_listeners(self): + """An instance of pymongo.monitoring._EventListeners. + """ + return self.__event_listeners + + @property + def appname(self): + """The application name, for sending with ismaster in server handshake. + """ + return self.__appname + + @property + def driver(self): + """Driver name and version, for sending with ismaster in handshake. + """ + return self.__driver + + @property + def compression_settings(self): + return self.__compression_settings + + @property + def metadata(self): + """A dict of metadata about the application, driver, os, and platform. + """ + return self.__metadata.copy() + + +class SocketInfo(object): + """Store a socket with some metadata. + + :Parameters: + - `sock`: a raw socket object + - `pool`: a Pool instance + - `address`: the server's (host, port) + """ + def __init__(self, sock, pool, address): + self.sock = sock + self.address = address + self.authset = set() + self.closed = False + self.last_checkin_time = _time() + self.performed_handshake = False + self.is_writable = False + self.max_wire_version = MAX_WIRE_VERSION + self.max_bson_size = MAX_BSON_SIZE + self.max_message_size = MAX_MESSAGE_SIZE + self.max_write_batch_size = MAX_WRITE_BATCH_SIZE + self.supports_sessions = False + self.is_mongos = False + self.op_msg_enabled = False + self.listeners = pool.opts.event_listeners + self.compression_settings = pool.opts.compression_settings + self.compression_context = None + + # The pool's pool_id changes with each reset() so we can close sockets + # created before the last reset. + self.pool_id = pool.pool_id + + def ismaster(self, metadata, cluster_time): + cmd = SON([('ismaster', 1)]) + if not self.performed_handshake: + cmd['client'] = metadata + if self.compression_settings: + cmd['compression'] = self.compression_settings.compressors + + if self.max_wire_version >= 6 and cluster_time is not None: + cmd['$clusterTime'] = cluster_time + + ismaster = IsMaster(self.command('admin', cmd, publish_events=False)) + self.is_writable = ismaster.is_writable + self.max_wire_version = ismaster.max_wire_version + self.max_bson_size = ismaster.max_bson_size + self.max_message_size = ismaster.max_message_size + self.max_write_batch_size = ismaster.max_write_batch_size + self.supports_sessions = ( + ismaster.logical_session_timeout_minutes is not None) + self.is_mongos = ismaster.server_type == SERVER_TYPE.Mongos + if not self.performed_handshake and self.compression_settings: + ctx = self.compression_settings.get_compression_context( + ismaster.compressors) + self.compression_context = ctx + + self.performed_handshake = True + self.op_msg_enabled = ismaster.max_wire_version >= 6 + return ismaster + + def command(self, dbname, spec, slave_ok=False, + read_preference=ReadPreference.PRIMARY, + codec_options=DEFAULT_CODEC_OPTIONS, check=True, + allowable_errors=None, check_keys=False, + read_concern=None, + write_concern=None, + parse_write_concern_error=False, + collation=None, + session=None, + client=None, + retryable_write=False, + publish_events=True, + user_fields=None): + """Execute a command or raise an error. + + :Parameters: + - `dbname`: name of the database on which to run the command + - `spec`: a command document as a dict, SON, or mapping object + - `slave_ok`: whether to set the SlaveOkay wire protocol bit + - `read_preference`: a read preference + - `codec_options`: a CodecOptions instance + - `check`: raise OperationFailure if there are errors + - `allowable_errors`: errors to ignore if `check` is True + - `check_keys`: if True, check `spec` for invalid keys + - `read_concern`: The read concern for this command. + - `write_concern`: The write concern for this command. + - `parse_write_concern_error`: Whether to parse the + ``writeConcernError`` field in the command response. + - `collation`: The collation for this command. + - `session`: optional ClientSession instance. + - `client`: optional MongoClient for gossipping $clusterTime. + - `retryable_write`: True if this command is a retryable write. + - `publish_events`: Should we publish events for this command? + - `user_fields` (optional): Response fields that should be decoded + using the TypeDecoders from codec_options, passed to + bson._decode_all_selective. + """ + self.validate_session(client, session) + session = _validate_session_write_concern(session, write_concern) + + # Ensure command name remains in first place. + if not isinstance(spec, ORDERED_TYPES): + spec = SON(spec) + + if (read_concern and self.max_wire_version < 4 + and not read_concern.ok_for_legacy): + raise ConfigurationError( + 'read concern level of %s is not valid ' + 'with a max wire version of %d.' + % (read_concern.level, self.max_wire_version)) + if not (write_concern is None or write_concern.acknowledged or + collation is None): + raise ConfigurationError( + 'Collation is unsupported for unacknowledged writes.') + if (self.max_wire_version >= 5 and + write_concern and + not write_concern.is_server_default): + spec['writeConcern'] = write_concern.document + elif self.max_wire_version < 5 and collation is not None: + raise ConfigurationError( + 'Must be connected to MongoDB 3.4+ to use a collation.') + + if session: + session._apply_to(spec, retryable_write, read_preference, self) + self.send_cluster_time(spec, session, client) + listeners = self.listeners if publish_events else None + unacknowledged = write_concern and not write_concern.acknowledged + if self.op_msg_enabled: + self._raise_if_not_writable(unacknowledged) + try: + return command(self.sock, dbname, spec, slave_ok, + self.is_mongos, read_preference, codec_options, + session, client, check, allowable_errors, + self.address, check_keys, listeners, + self.max_bson_size, read_concern, + parse_write_concern_error=parse_write_concern_error, + collation=collation, + compression_ctx=self.compression_context, + use_op_msg=self.op_msg_enabled, + unacknowledged=unacknowledged, + user_fields=user_fields) + except OperationFailure: + raise + # Catch socket.error, KeyboardInterrupt, etc. and close ourselves. + except BaseException as error: + self._raise_connection_failure(error) + + def send_message(self, message, max_doc_size): + """Send a raw BSON message or raise ConnectionFailure. + + If a network exception is raised, the socket is closed. + """ + if (self.max_bson_size is not None + and max_doc_size > self.max_bson_size): + raise DocumentTooLarge( + "BSON document too large (%d bytes) - the connected server " + "supports BSON document sizes up to %d bytes." % + (max_doc_size, self.max_bson_size)) + + try: + self.sock.sendall(message) + except BaseException as error: + self._raise_connection_failure(error) + + def receive_message(self, request_id): + """Receive a raw BSON message or raise ConnectionFailure. + + If any exception is raised, the socket is closed. + """ + try: + return receive_message(self.sock, request_id, + self.max_message_size) + except BaseException as error: + self._raise_connection_failure(error) + + def _raise_if_not_writable(self, unacknowledged): + """Raise NotMasterError on unacknowledged write if this socket is not + writable. + """ + if unacknowledged and not self.is_writable: + # Write won't succeed, bail as if we'd received a not master error. + raise NotMasterError("not master") + + def legacy_write(self, request_id, msg, max_doc_size, with_last_error): + """Send OP_INSERT, etc., optionally returning response as a dict. + + Can raise ConnectionFailure or OperationFailure. + + :Parameters: + - `request_id`: an int. + - `msg`: bytes, an OP_INSERT, OP_UPDATE, or OP_DELETE message, + perhaps with a getlasterror command appended. + - `max_doc_size`: size in bytes of the largest document in `msg`. + - `with_last_error`: True if a getlasterror command is appended. + """ + self._raise_if_not_writable(not with_last_error) + + self.send_message(msg, max_doc_size) + if with_last_error: + reply = self.receive_message(request_id) + return helpers._check_gle_response(reply.command_response()) + + def write_command(self, request_id, msg): + """Send "insert" etc. command, returning response as a dict. + + Can raise ConnectionFailure or OperationFailure. + + :Parameters: + - `request_id`: an int. + - `msg`: bytes, the command message. + """ + self.send_message(msg, 0) + reply = self.receive_message(request_id) + result = reply.command_response() + + # Raises NotMasterError or OperationFailure. + helpers._check_command_response(result) + return result + + def check_auth(self, all_credentials): + """Update this socket's authentication. + + Log in or out to bring this socket's credentials up to date with + those provided. Can raise ConnectionFailure or OperationFailure. + + :Parameters: + - `all_credentials`: dict, maps auth source to MongoCredential. + """ + if all_credentials or self.authset: + cached = set(itervalues(all_credentials)) + authset = self.authset.copy() + + # Logout any credentials that no longer exist in the cache. + for credentials in authset - cached: + auth.logout(credentials.source, self) + self.authset.discard(credentials) + + for credentials in cached - authset: + auth.authenticate(credentials, self) + self.authset.add(credentials) + + def authenticate(self, credentials): + """Log in to the server and store these credentials in `authset`. + + Can raise ConnectionFailure or OperationFailure. + + :Parameters: + - `credentials`: A MongoCredential. + """ + auth.authenticate(credentials, self) + self.authset.add(credentials) + + def validate_session(self, client, session): + """Validate this session before use with client. + + Raises error if this session is logged in as a different user or + the client is not the one that created the session. + """ + if session: + if session._client is not client: + raise InvalidOperation( + 'Can only use session with the MongoClient that' + ' started it') + if session._authset != self.authset: + raise InvalidOperation( + 'Cannot use session after authenticating with different' + ' credentials') + + def close(self): + self.closed = True + # Avoid exceptions on interpreter shutdown. + try: + self.sock.close() + except Exception: + pass + + def send_cluster_time(self, command, session, client): + """Add cluster time for MongoDB >= 3.6.""" + if self.max_wire_version >= 6 and client: + client._send_cluster_time(command, session) + + def update_last_checkin_time(self): + self.last_checkin_time = _time() + + def idle_time_seconds(self): + """Seconds since this socket was last checked into its pool.""" + return _time() - self.last_checkin_time + + def _raise_connection_failure(self, error): + # Catch *all* exceptions from socket methods and close the socket. In + # regular Python, socket operations only raise socket.error, even if + # the underlying cause was a Ctrl-C: a signal raised during socket.recv + # is expressed as an EINTR error from poll. See internal_select_ex() in + # socketmodule.c. All error codes from poll become socket.error at + # first. Eventually in PyEval_EvalFrameEx the interpreter checks for + # signals and throws KeyboardInterrupt into the current frame on the + # main thread. + # + # But in Gevent and Eventlet, the polling mechanism (epoll, kqueue, + # ...) is called in Python code, which experiences the signal as a + # KeyboardInterrupt from the start, rather than as an initial + # socket.error, so we catch that, close the socket, and reraise it. + self.close() + if isinstance(error, socket.error): + _raise_connection_failure(self.address, error) + else: + raise error + + def __eq__(self, other): + return self.sock == other.sock + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.sock) + + def __repr__(self): + return "SocketInfo(%s)%s at %s" % ( + repr(self.sock), + self.closed and " CLOSED" or "", + id(self) + ) + + +def _create_connection(address, options): + """Given (host, port) and PoolOptions, connect and return a socket object. + + Can raise socket.error. + + This is a modified version of create_connection from CPython >= 2.7. + """ + host, port = address + + # Check if dealing with a unix domain socket + if host.endswith('.sock'): + if not hasattr(socket, "AF_UNIX"): + raise ConnectionFailure("UNIX-sockets are not supported " + "on this system") + sock = socket.socket(socket.AF_UNIX) + # SOCK_CLOEXEC not supported for Unix sockets. + _set_non_inheritable_non_atomic(sock.fileno()) + try: + sock.connect(host) + return sock + except socket.error: + sock.close() + raise + + # Don't try IPv6 if we don't support it. Also skip it if host + # is 'localhost' (::1 is fine). Avoids slow connect issues + # like PYTHON-356. + family = socket.AF_INET + if socket.has_ipv6 and host != 'localhost': + family = socket.AF_UNSPEC + + err = None + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, dummy, sa = res + # SOCK_CLOEXEC was new in CPython 3.2, and only available on a limited + # number of platforms (newer Linux and *BSD). Starting with CPython 3.4 + # all file descriptors are created non-inheritable. See PEP 446. + try: + sock = socket.socket( + af, socktype | getattr(socket, 'SOCK_CLOEXEC', 0), proto) + except socket.error: + # Can SOCK_CLOEXEC be defined even if the kernel doesn't support + # it? + sock = socket.socket(af, socktype, proto) + # Fallback when SOCK_CLOEXEC isn't available. + _set_non_inheritable_non_atomic(sock.fileno()) + try: + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + sock.settimeout(options.connect_timeout) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, + options.socket_keepalive) + if options.socket_keepalive: + _set_keepalive_times(sock) + sock.connect(sa) + return sock + except socket.error as e: + err = e + sock.close() + + if err is not None: + raise err + else: + # This likely means we tried to connect to an IPv6 only + # host with an OS/kernel or Python interpreter that doesn't + # support IPv6. The test case is Jython2.5.1 which doesn't + # support IPv6 at all. + raise socket.error('getaddrinfo failed') + + +_PY37PLUS = sys.version_info[:2] >= (3, 7) + + +def _configured_socket(address, options): + """Given (host, port) and PoolOptions, return a configured socket. + + Can raise socket.error, ConnectionFailure, or CertificateError. + + Sets socket's SSL and timeout options. + """ + sock = _create_connection(address, options) + ssl_context = options.ssl_context + + if ssl_context is not None: + host = address[0] + try: + # According to RFC6066, section 3, IPv4 and IPv6 literals are + # not permitted for SNI hostname. + # Previous to Python 3.7 wrap_socket would blindly pass + # IP addresses as SNI hostname. + # https://bugs.python.org/issue32185 + # We have to pass hostname / ip address to wrap_socket + # to use SSLContext.check_hostname. + if _HAVE_SNI and (not is_ip_address(host) or _PY37PLUS): + sock = ssl_context.wrap_socket(sock, server_hostname=host) + else: + sock = ssl_context.wrap_socket(sock) + except _SSLCertificateError: + sock.close() + # Raise CertificateError directly like we do after match_hostname + # below. + raise + except IOError as exc: + sock.close() + # We raise AutoReconnect for transient and permanent SSL handshake + # failures alike. Permanent handshake failures, like protocol + # mismatch, will be turned into ServerSelectionTimeoutErrors later. + _raise_connection_failure(address, exc, "SSL handshake failed: ") + if (ssl_context.verify_mode and not + getattr(ssl_context, "check_hostname", False) and + options.ssl_match_hostname): + try: + match_hostname(sock.getpeercert(), hostname=host) + except CertificateError: + sock.close() + raise + + sock.settimeout(options.socket_timeout) + return sock + + +# Do *not* explicitly inherit from object or Jython won't call __del__ +# http://bugs.jython.org/issue1057 +class Pool: + def __init__(self, address, options, handshake=True): + """ + :Parameters: + - `address`: a (hostname, port) tuple + - `options`: a PoolOptions instance + - `handshake`: whether to call ismaster for each new SocketInfo + """ + # Check a socket's health with socket_closed() every once in a while. + # Can override for testing: 0 to always check, None to never check. + self._check_interval_seconds = 1 + # LIFO pool. Sockets are ordered on idle time. Sockets claimed + # and returned to pool from the left side. Stale sockets removed + # from the right side. + self.sockets = collections.deque() + self.lock = threading.Lock() + self.active_sockets = 0 + + # Keep track of resets, so we notice sockets created before the most + # recent reset and close them. + self.pool_id = 0 + self.pid = os.getpid() + self.address = address + self.opts = options + self.handshake = handshake + + if (self.opts.wait_queue_multiple is None or + self.opts.max_pool_size is None): + max_waiters = None + else: + max_waiters = ( + self.opts.max_pool_size * self.opts.wait_queue_multiple) + + self._socket_semaphore = thread_util.create_semaphore( + self.opts.max_pool_size, max_waiters) + self.socket_checker = SocketChecker() + + def reset(self): + with self.lock: + self.pool_id += 1 + self.pid = os.getpid() + sockets, self.sockets = self.sockets, collections.deque() + self.active_sockets = 0 + + for sock_info in sockets: + sock_info.close() + + def remove_stale_sockets(self): + """Removes stale sockets then adds new ones if pool is too small.""" + if self.opts.max_idle_time_seconds is not None: + with self.lock: + while (self.sockets and + self.sockets[-1].idle_time_seconds() > self.opts.max_idle_time_seconds): + sock_info = self.sockets.pop() + sock_info.close() + while True: + with self.lock: + if (len(self.sockets) + self.active_sockets >= + self.opts.min_pool_size): + # There are enough sockets in the pool. + break + + # We must acquire the semaphore to respect max_pool_size. + if not self._socket_semaphore.acquire(False): + break + try: + sock_info = self.connect() + with self.lock: + self.sockets.appendleft(sock_info) + finally: + self._socket_semaphore.release() + + def connect(self): + """Connect to Mongo and return a new SocketInfo. + + Can raise ConnectionFailure or CertificateError. + + Note that the pool does not keep a reference to the socket -- you + must call return_socket() when you're done with it. + """ + sock = None + try: + sock = _configured_socket(self.address, self.opts) + except socket.error as error: + if sock is not None: + sock.close() + _raise_connection_failure(self.address, error) + + sock_info = SocketInfo(sock, self, self.address) + if self.handshake: + sock_info.ismaster(self.opts.metadata, None) + return sock_info + + @contextlib.contextmanager + def get_socket(self, all_credentials, checkout=False): + """Get a socket from the pool. Use with a "with" statement. + + Returns a :class:`SocketInfo` object wrapping a connected + :class:`socket.socket`. + + This method should always be used in a with-statement:: + + with pool.get_socket(credentials, checkout) as socket_info: + socket_info.send_message(msg) + data = socket_info.receive_message(op_code, request_id) + + The socket is logged in or out as needed to match ``all_credentials`` + using the correct authentication mechanism for the server's wire + protocol version. + + Can raise ConnectionFailure or OperationFailure. + + :Parameters: + - `all_credentials`: dict, maps auth source to MongoCredential. + - `checkout` (optional): keep socket checked out. + """ + # First get a socket, then attempt authentication. Simplifies + # semaphore management in the face of network errors during auth. + sock_info = self._get_socket_no_auth() + try: + sock_info.check_auth(all_credentials) + yield sock_info + except: + # Exception in caller. Decrement semaphore. + self.return_socket(sock_info) + raise + else: + if not checkout: + self.return_socket(sock_info) + + def _get_socket_no_auth(self): + """Get or create a SocketInfo. Can raise ConnectionFailure.""" + # We use the pid here to avoid issues with fork / multiprocessing. + # See test.test_client:TestClient.test_fork for an example of + # what could go wrong otherwise + if self.pid != os.getpid(): + self.reset() + + # Get a free socket or create one. + if not self._socket_semaphore.acquire( + True, self.opts.wait_queue_timeout): + self._raise_wait_queue_timeout() + with self.lock: + self.active_sockets += 1 + + # We've now acquired the semaphore and must release it on error. + try: + try: + # set.pop() isn't atomic in Jython less than 2.7, see + # http://bugs.jython.org/issue1854 + with self.lock: + # Can raise ConnectionFailure. + sock_info = self.sockets.popleft() + except IndexError: + # Can raise ConnectionFailure or CertificateError. + sock_info = self.connect() + else: + # Can raise ConnectionFailure. + sock_info = self._check(sock_info) + except Exception: + self._socket_semaphore.release() + with self.lock: + self.active_sockets -= 1 + raise + + return sock_info + + def return_socket(self, sock_info): + """Return the socket to the pool, or if it's closed discard it.""" + if self.pid != os.getpid(): + self.reset() + else: + if sock_info.pool_id != self.pool_id: + sock_info.close() + elif not sock_info.closed: + sock_info.update_last_checkin_time() + with self.lock: + self.sockets.appendleft(sock_info) + + self._socket_semaphore.release() + with self.lock: + self.active_sockets -= 1 + + def _check(self, sock_info): + """This side-effecty function checks if this socket has been idle for + for longer than the max idle time, or if the socket has been closed by + some external network error, and if so, attempts to create a new + socket. If this connection attempt fails we raise the + ConnectionFailure. + + Checking sockets lets us avoid seeing *some* + :class:`~pymongo.errors.AutoReconnect` exceptions on server + hiccups, etc. We only check if the socket was closed by an external + error if it has been > 1 second since the socket was checked into the + pool, to keep performance reasonable - we can't avoid AutoReconnects + completely anyway. + """ + idle_time_seconds = sock_info.idle_time_seconds() + # If socket is idle, open a new one. + if (self.opts.max_idle_time_seconds is not None and + idle_time_seconds > self.opts.max_idle_time_seconds): + sock_info.close() + return self.connect() + + if (self._check_interval_seconds is not None and ( + 0 == self._check_interval_seconds or + idle_time_seconds > self._check_interval_seconds)): + if self.socket_checker.socket_closed(sock_info.sock): + sock_info.close() + return self.connect() + + return sock_info + + def _raise_wait_queue_timeout(self): + raise ConnectionFailure( + 'Timed out waiting for socket from pool with max_size %r and' + ' wait_queue_timeout %r' % ( + self.opts.max_pool_size, self.opts.wait_queue_timeout)) + + def __del__(self): + # Avoid ResourceWarnings in Python 3 + for sock_info in self.sockets: + sock_info.close() diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/read_concern.py b/backend/venv/lib/python3.7/site-packages/pymongo/read_concern.py new file mode 100644 index 000000000..3ba8c854a --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/read_concern.py @@ -0,0 +1,76 @@ +# Copyright 2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License", +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for working with read concerns.""" + +from bson.py3compat import string_type + + +class ReadConcern(object): + """ReadConcern + + :Parameters: + - `level`: (string) The read concern level specifies the level of + isolation for read operations. For example, a read operation using a + read concern level of ``majority`` will only return data that has been + written to a majority of nodes. If the level is left unspecified, the + server default will be used. + + .. versionadded:: 3.2 + + """ + + def __init__(self, level=None): + if level is None or isinstance(level, string_type): + self.__level = level + else: + raise TypeError( + 'level must be a string or None.') + + @property + def level(self): + """The read concern level.""" + return self.__level + + @property + def ok_for_legacy(self): + """Return ``True`` if this read concern is compatible with + old wire protocol versions.""" + return self.level is None or self.level == 'local' + + @property + def document(self): + """The document representation of this read concern. + + .. note:: + :class:`ReadConcern` is immutable. Mutating the value of + :attr:`document` does not mutate this :class:`ReadConcern`. + """ + doc = {} + if self.__level: + doc['level'] = self.level + return doc + + def __eq__(self, other): + if isinstance(other, ReadConcern): + return self.document == other.document + return NotImplemented + + def __repr__(self): + if self.level: + return 'ReadConcern(%s)' % self.level + return 'ReadConcern()' + + +DEFAULT_READ_CONCERN = ReadConcern() diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/read_preferences.py b/backend/venv/lib/python3.7/site-packages/pymongo/read_preferences.py new file mode 100644 index 000000000..f4425acaa --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/read_preferences.py @@ -0,0 +1,471 @@ +# Copyright 2012-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License", +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utilities for choosing which member of a replica set to read from.""" + +from bson.py3compat import abc, integer_types +from pymongo import max_staleness_selectors +from pymongo.errors import ConfigurationError +from pymongo.server_selectors import (member_with_tags_server_selector, + secondary_with_tags_server_selector) + + +_PRIMARY = 0 +_PRIMARY_PREFERRED = 1 +_SECONDARY = 2 +_SECONDARY_PREFERRED = 3 +_NEAREST = 4 + + +_MONGOS_MODES = ( + 'primary', + 'primaryPreferred', + 'secondary', + 'secondaryPreferred', + 'nearest', +) + + +def _validate_tag_sets(tag_sets): + """Validate tag sets for a MongoReplicaSetClient. + """ + if tag_sets is None: + return tag_sets + + if not isinstance(tag_sets, list): + raise TypeError(( + "Tag sets %r invalid, must be a list") % (tag_sets,)) + if len(tag_sets) == 0: + raise ValueError(( + "Tag sets %r invalid, must be None or contain at least one set of" + " tags") % (tag_sets,)) + + for tags in tag_sets: + if not isinstance(tags, abc.Mapping): + raise TypeError( + "Tag set %r invalid, must be an instance of dict, " + "bson.son.SON or other type that inherits from " + "collection.Mapping" % (tags,)) + + return tag_sets + + +def _invalid_max_staleness_msg(max_staleness): + return ("maxStalenessSeconds must be a positive integer, not %s" % + max_staleness) + + +# Some duplication with common.py to avoid import cycle. +def _validate_max_staleness(max_staleness): + """Validate max_staleness.""" + if max_staleness == -1: + return -1 + + if not isinstance(max_staleness, integer_types): + raise TypeError(_invalid_max_staleness_msg(max_staleness)) + + if max_staleness <= 0: + raise ValueError(_invalid_max_staleness_msg(max_staleness)) + + return max_staleness + + +class _ServerMode(object): + """Base class for all read preferences. + """ + + __slots__ = ("__mongos_mode", "__mode", "__tag_sets", "__max_staleness") + + def __init__(self, mode, tag_sets=None, max_staleness=-1): + self.__mongos_mode = _MONGOS_MODES[mode] + self.__mode = mode + self.__tag_sets = _validate_tag_sets(tag_sets) + self.__max_staleness = _validate_max_staleness(max_staleness) + + @property + def name(self): + """The name of this read preference. + """ + return self.__class__.__name__ + + @property + def mongos_mode(self): + """The mongos mode of this read preference. + """ + return self.__mongos_mode + + @property + def document(self): + """Read preference as a document. + """ + doc = {'mode': self.__mongos_mode} + if self.__tag_sets not in (None, [{}]): + doc['tags'] = self.__tag_sets + if self.__max_staleness != -1: + doc['maxStalenessSeconds'] = self.__max_staleness + return doc + + @property + def mode(self): + """The mode of this read preference instance. + """ + return self.__mode + + @property + def tag_sets(self): + """Set ``tag_sets`` to a list of dictionaries like [{'dc': 'ny'}] to + read only from members whose ``dc`` tag has the value ``"ny"``. + To specify a priority-order for tag sets, provide a list of + tag sets: ``[{'dc': 'ny'}, {'dc': 'la'}, {}]``. A final, empty tag + set, ``{}``, means "read from any member that matches the mode, + ignoring tags." MongoReplicaSetClient tries each set of tags in turn + until it finds a set of tags with at least one matching member. + + .. seealso:: `Data-Center Awareness + `_ + """ + return list(self.__tag_sets) if self.__tag_sets else [{}] + + @property + def max_staleness(self): + """The maximum estimated length of time (in seconds) a replica set + secondary can fall behind the primary in replication before it will + no longer be selected for operations, or -1 for no maximum.""" + return self.__max_staleness + + @property + def min_wire_version(self): + """The wire protocol version the server must support. + + Some read preferences impose version requirements on all servers (e.g. + maxStalenessSeconds requires MongoDB 3.4 / maxWireVersion 5). + + All servers' maxWireVersion must be at least this read preference's + `min_wire_version`, or the driver raises + :exc:`~pymongo.errors.ConfigurationError`. + """ + return 0 if self.__max_staleness == -1 else 5 + + def __repr__(self): + return "%s(tag_sets=%r, max_staleness=%r)" % ( + self.name, self.__tag_sets, self.__max_staleness) + + def __eq__(self, other): + if isinstance(other, _ServerMode): + return (self.mode == other.mode and + self.tag_sets == other.tag_sets and + self.max_staleness == other.max_staleness) + return NotImplemented + + def __ne__(self, other): + return not self == other + + def __getstate__(self): + """Return value of object for pickling. + + Needed explicitly because __slots__() defined. + """ + return {'mode': self.__mode, + 'tag_sets': self.__tag_sets, + 'max_staleness': self.__max_staleness} + + def __setstate__(self, value): + """Restore from pickling.""" + self.__mode = value['mode'] + self.__mongos_mode = _MONGOS_MODES[self.__mode] + self.__tag_sets = _validate_tag_sets(value['tag_sets']) + self.__max_staleness = _validate_max_staleness(value['max_staleness']) + + +class Primary(_ServerMode): + """Primary read preference. + + * When directly connected to one mongod queries are allowed if the server + is standalone or a replica set primary. + * When connected to a mongos queries are sent to the primary of a shard. + * When connected to a replica set queries are sent to the primary of + the replica set. + """ + + __slots__ = () + + def __init__(self): + super(Primary, self).__init__(_PRIMARY) + + def __call__(self, selection): + """Apply this read preference to a Selection.""" + return selection.primary_selection + + def __repr__(self): + return "Primary()" + + def __eq__(self, other): + if isinstance(other, _ServerMode): + return other.mode == _PRIMARY + return NotImplemented + + +class PrimaryPreferred(_ServerMode): + """PrimaryPreferred read preference. + + * When directly connected to one mongod queries are allowed to standalone + servers, to a replica set primary, or to replica set secondaries. + * When connected to a mongos queries are sent to the primary of a shard if + available, otherwise a shard secondary. + * When connected to a replica set queries are sent to the primary if + available, otherwise a secondary. + + :Parameters: + - `tag_sets`: The :attr:`~tag_sets` to use if the primary is not + available. + - `max_staleness`: (integer, in seconds) The maximum estimated + length of time a replica set secondary can fall behind the primary in + replication before it will no longer be selected for operations. + Default -1, meaning no maximum. If it is set, it must be at least + 90 seconds. + """ + + __slots__ = () + + def __init__(self, tag_sets=None, max_staleness=-1): + super(PrimaryPreferred, self).__init__(_PRIMARY_PREFERRED, + tag_sets, + max_staleness) + + def __call__(self, selection): + """Apply this read preference to Selection.""" + if selection.primary: + return selection.primary_selection + else: + return secondary_with_tags_server_selector( + self.tag_sets, + max_staleness_selectors.select( + self.max_staleness, selection)) + + +class Secondary(_ServerMode): + """Secondary read preference. + + * When directly connected to one mongod queries are allowed to standalone + servers, to a replica set primary, or to replica set secondaries. + * When connected to a mongos queries are distributed among shard + secondaries. An error is raised if no secondaries are available. + * When connected to a replica set queries are distributed among + secondaries. An error is raised if no secondaries are available. + + :Parameters: + - `tag_sets`: The :attr:`~tag_sets` for this read preference. + - `max_staleness`: (integer, in seconds) The maximum estimated + length of time a replica set secondary can fall behind the primary in + replication before it will no longer be selected for operations. + Default -1, meaning no maximum. If it is set, it must be at least + 90 seconds. + """ + + __slots__ = () + + def __init__(self, tag_sets=None, max_staleness=-1): + super(Secondary, self).__init__(_SECONDARY, tag_sets, max_staleness) + + def __call__(self, selection): + """Apply this read preference to Selection.""" + return secondary_with_tags_server_selector( + self.tag_sets, + max_staleness_selectors.select( + self.max_staleness, selection)) + + +class SecondaryPreferred(_ServerMode): + """SecondaryPreferred read preference. + + * When directly connected to one mongod queries are allowed to standalone + servers, to a replica set primary, or to replica set secondaries. + * When connected to a mongos queries are distributed among shard + secondaries, or the shard primary if no secondary is available. + * When connected to a replica set queries are distributed among + secondaries, or the primary if no secondary is available. + + :Parameters: + - `tag_sets`: The :attr:`~tag_sets` for this read preference. + - `max_staleness`: (integer, in seconds) The maximum estimated + length of time a replica set secondary can fall behind the primary in + replication before it will no longer be selected for operations. + Default -1, meaning no maximum. If it is set, it must be at least + 90 seconds. + """ + + __slots__ = () + + def __init__(self, tag_sets=None, max_staleness=-1): + super(SecondaryPreferred, self).__init__(_SECONDARY_PREFERRED, + tag_sets, + max_staleness) + + def __call__(self, selection): + """Apply this read preference to Selection.""" + secondaries = secondary_with_tags_server_selector( + self.tag_sets, + max_staleness_selectors.select( + self.max_staleness, selection)) + + if secondaries: + return secondaries + else: + return selection.primary_selection + + +class Nearest(_ServerMode): + """Nearest read preference. + + * When directly connected to one mongod queries are allowed to standalone + servers, to a replica set primary, or to replica set secondaries. + * When connected to a mongos queries are distributed among all members of + a shard. + * When connected to a replica set queries are distributed among all + members. + + :Parameters: + - `tag_sets`: The :attr:`~tag_sets` for this read preference. + - `max_staleness`: (integer, in seconds) The maximum estimated + length of time a replica set secondary can fall behind the primary in + replication before it will no longer be selected for operations. + Default -1, meaning no maximum. If it is set, it must be at least + 90 seconds. + """ + + __slots__ = () + + def __init__(self, tag_sets=None, max_staleness=-1): + super(Nearest, self).__init__(_NEAREST, tag_sets, max_staleness) + + def __call__(self, selection): + """Apply this read preference to Selection.""" + return member_with_tags_server_selector( + self.tag_sets, + max_staleness_selectors.select( + self.max_staleness, selection)) + + +_ALL_READ_PREFERENCES = (Primary, PrimaryPreferred, + Secondary, SecondaryPreferred, Nearest) + + +def make_read_preference(mode, tag_sets, max_staleness=-1): + if mode == _PRIMARY: + if tag_sets not in (None, [{}]): + raise ConfigurationError("Read preference primary " + "cannot be combined with tags") + if max_staleness != -1: + raise ConfigurationError("Read preference primary cannot be " + "combined with maxStalenessSeconds") + return Primary() + return _ALL_READ_PREFERENCES[mode](tag_sets, max_staleness) + + +_MODES = ( + 'PRIMARY', + 'PRIMARY_PREFERRED', + 'SECONDARY', + 'SECONDARY_PREFERRED', + 'NEAREST', +) + + +class ReadPreference(object): + """An enum that defines the read preference modes supported by PyMongo. + + See :doc:`/examples/high_availability` for code examples. + + A read preference is used in three cases: + + :class:`~pymongo.mongo_client.MongoClient` connected to a single mongod: + + - ``PRIMARY``: Queries are allowed if the server is standalone or a replica + set primary. + - All other modes allow queries to standalone servers, to a replica set + primary, or to replica set secondaries. + + :class:`~pymongo.mongo_client.MongoClient` initialized with the + ``replicaSet`` option: + + - ``PRIMARY``: Read from the primary. This is the default, and provides the + strongest consistency. If no primary is available, raise + :class:`~pymongo.errors.AutoReconnect`. + + - ``PRIMARY_PREFERRED``: Read from the primary if available, or if there is + none, read from a secondary. + + - ``SECONDARY``: Read from a secondary. If no secondary is available, + raise :class:`~pymongo.errors.AutoReconnect`. + + - ``SECONDARY_PREFERRED``: Read from a secondary if available, otherwise + from the primary. + + - ``NEAREST``: Read from any member. + + :class:`~pymongo.mongo_client.MongoClient` connected to a mongos, with a + sharded cluster of replica sets: + + - ``PRIMARY``: Read from the primary of the shard, or raise + :class:`~pymongo.errors.OperationFailure` if there is none. + This is the default. + + - ``PRIMARY_PREFERRED``: Read from the primary of the shard, or if there is + none, read from a secondary of the shard. + + - ``SECONDARY``: Read from a secondary of the shard, or raise + :class:`~pymongo.errors.OperationFailure` if there is none. + + - ``SECONDARY_PREFERRED``: Read from a secondary of the shard if available, + otherwise from the shard primary. + + - ``NEAREST``: Read from any shard member. + """ + PRIMARY = Primary() + PRIMARY_PREFERRED = PrimaryPreferred() + SECONDARY = Secondary() + SECONDARY_PREFERRED = SecondaryPreferred() + NEAREST = Nearest() + + +def read_pref_mode_from_name(name): + """Get the read preference mode from mongos/uri name. + """ + return _MONGOS_MODES.index(name) + + +class MovingAverage(object): + """Tracks an exponentially-weighted moving average.""" + def __init__(self): + self.average = None + + def add_sample(self, sample): + if sample < 0: + # Likely system time change while waiting for ismaster response + # and not using time.monotonic. Ignore it, the next one will + # probably be valid. + return + if self.average is None: + self.average = sample + else: + # The Server Selection Spec requires an exponentially weighted + # average with alpha = 0.2. + self.average = 0.8 * self.average + 0.2 * sample + + def get(self): + """Get the calculated average, or None if no samples yet.""" + return self.average + + def reset(self): + self.average = None diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/response.py b/backend/venv/lib/python3.7/site-packages/pymongo/response.py new file mode 100644 index 000000000..a91490ed3 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/response.py @@ -0,0 +1,101 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Represent a response from the server.""" + + +class Response(object): + __slots__ = ('_data', '_address', '_request_id', '_duration', + '_from_command') + + def __init__(self, data, address, request_id, duration, from_command): + """Represent a response from the server. + + :Parameters: + - `data`: A network response message. + - `address`: (host, port) of the source server. + - `request_id`: The request id of this operation. + - `duration`: The duration of the operation. + - `from_command`: if the response is the result of a db command. + """ + self._data = data + self._address = address + self._request_id = request_id + self._duration = duration + self._from_command = from_command + + @property + def data(self): + """Server response's raw BSON bytes.""" + return self._data + + @property + def address(self): + """(host, port) of the source server.""" + return self._address + + @property + def request_id(self): + """The request id of this operation.""" + return self._request_id + + @property + def duration(self): + """The duration of the operation.""" + return self._duration + + @property + def from_command(self): + """If the response is a result from a db command.""" + return self._from_command + + +class ExhaustResponse(Response): + __slots__ = ('_socket_info', '_pool') + + def __init__(self, data, address, socket_info, pool, request_id, duration, + from_command): + """Represent a response to an exhaust cursor's initial query. + + :Parameters: + - `data`: A network response message. + - `address`: (host, port) of the source server. + - `socket_info`: The SocketInfo used for the initial query. + - `pool`: The Pool from which the SocketInfo came. + - `request_id`: The request id of this operation. + - `duration`: The duration of the operation. + - `from_command`: If the response is the result of a db command. + """ + super(ExhaustResponse, self).__init__(data, + address, + request_id, + duration, + from_command) + self._socket_info = socket_info + self._pool = pool + + @property + def socket_info(self): + """The SocketInfo used for the initial query. + + The server will send batches on this socket, without waiting for + getMores from the client, until the result set is exhausted or there + is an error. + """ + return self._socket_info + + @property + def pool(self): + """The Pool from which the SocketInfo came.""" + return self._pool diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/results.py b/backend/venv/lib/python3.7/site-packages/pymongo/results.py new file mode 100644 index 000000000..a5025e9f4 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/results.py @@ -0,0 +1,226 @@ +# Copyright 2015-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Result class definitions.""" + +from pymongo.errors import InvalidOperation + + +class _WriteResult(object): + """Base class for write result classes.""" + + __slots__ = ("__acknowledged",) + + def __init__(self, acknowledged): + self.__acknowledged = acknowledged + + def _raise_if_unacknowledged(self, property_name): + """Raise an exception on property access if unacknowledged.""" + if not self.__acknowledged: + raise InvalidOperation("A value for %s is not available when " + "the write is unacknowledged. Check the " + "acknowledged attribute to avoid this " + "error." % (property_name,)) + + @property + def acknowledged(self): + """Is this the result of an acknowledged write operation? + + The :attr:`acknowledged` attribute will be ``False`` when using + ``WriteConcern(w=0)``, otherwise ``True``. + + .. note:: + If the :attr:`acknowledged` attribute is ``False`` all other + attibutes of this class will raise + :class:`~pymongo.errors.InvalidOperation` when accessed. Values for + other attributes cannot be determined if the write operation was + unacknowledged. + + .. seealso:: + :class:`~pymongo.write_concern.WriteConcern` + """ + return self.__acknowledged + + +class InsertOneResult(_WriteResult): + """The return type for :meth:`~pymongo.collection.Collection.insert_one`. + """ + + __slots__ = ("__inserted_id", "__acknowledged") + + def __init__(self, inserted_id, acknowledged): + self.__inserted_id = inserted_id + super(InsertOneResult, self).__init__(acknowledged) + + @property + def inserted_id(self): + """The inserted document's _id.""" + return self.__inserted_id + + +class InsertManyResult(_WriteResult): + """The return type for :meth:`~pymongo.collection.Collection.insert_many`. + """ + + __slots__ = ("__inserted_ids", "__acknowledged") + + def __init__(self, inserted_ids, acknowledged): + self.__inserted_ids = inserted_ids + super(InsertManyResult, self).__init__(acknowledged) + + @property + def inserted_ids(self): + """A list of _ids of the inserted documents, in the order provided. + + .. note:: If ``False`` is passed for the `ordered` parameter to + :meth:`~pymongo.collection.Collection.insert_many` the server + may have inserted the documents in a different order than what + is presented here. + """ + return self.__inserted_ids + + +class UpdateResult(_WriteResult): + """The return type for :meth:`~pymongo.collection.Collection.update_one`, + :meth:`~pymongo.collection.Collection.update_many`, and + :meth:`~pymongo.collection.Collection.replace_one`. + """ + + __slots__ = ("__raw_result", "__acknowledged") + + def __init__(self, raw_result, acknowledged): + self.__raw_result = raw_result + super(UpdateResult, self).__init__(acknowledged) + + @property + def raw_result(self): + """The raw result document returned by the server.""" + return self.__raw_result + + @property + def matched_count(self): + """The number of documents matched for this update.""" + self._raise_if_unacknowledged("matched_count") + if self.upserted_id is not None: + return 0 + return self.__raw_result.get("n", 0) + + @property + def modified_count(self): + """The number of documents modified. + + .. note:: modified_count is only reported by MongoDB 2.6 and later. + When connected to an earlier server version, or in certain mixed + version sharding configurations, this attribute will be set to + ``None``. + """ + self._raise_if_unacknowledged("modified_count") + return self.__raw_result.get("nModified") + + @property + def upserted_id(self): + """The _id of the inserted document if an upsert took place. Otherwise + ``None``. + """ + self._raise_if_unacknowledged("upserted_id") + return self.__raw_result.get("upserted") + + +class DeleteResult(_WriteResult): + """The return type for :meth:`~pymongo.collection.Collection.delete_one` + and :meth:`~pymongo.collection.Collection.delete_many`""" + + __slots__ = ("__raw_result", "__acknowledged") + + def __init__(self, raw_result, acknowledged): + self.__raw_result = raw_result + super(DeleteResult, self).__init__(acknowledged) + + @property + def raw_result(self): + """The raw result document returned by the server.""" + return self.__raw_result + + @property + def deleted_count(self): + """The number of documents deleted.""" + self._raise_if_unacknowledged("deleted_count") + return self.__raw_result.get("n", 0) + + +class BulkWriteResult(_WriteResult): + """An object wrapper for bulk API write results.""" + + __slots__ = ("__bulk_api_result", "__acknowledged") + + def __init__(self, bulk_api_result, acknowledged): + """Create a BulkWriteResult instance. + + :Parameters: + - `bulk_api_result`: A result dict from the bulk API + - `acknowledged`: Was this write result acknowledged? If ``False`` + then all properties of this object will raise + :exc:`~pymongo.errors.InvalidOperation`. + """ + self.__bulk_api_result = bulk_api_result + super(BulkWriteResult, self).__init__(acknowledged) + + @property + def bulk_api_result(self): + """The raw bulk API result.""" + return self.__bulk_api_result + + @property + def inserted_count(self): + """The number of documents inserted.""" + self._raise_if_unacknowledged("inserted_count") + return self.__bulk_api_result.get("nInserted") + + @property + def matched_count(self): + """The number of documents matched for an update.""" + self._raise_if_unacknowledged("matched_count") + return self.__bulk_api_result.get("nMatched") + + @property + def modified_count(self): + """The number of documents modified. + + .. note:: modified_count is only reported by MongoDB 2.6 and later. + When connected to an earlier server version, or in certain mixed + version sharding configurations, this attribute will be set to + ``None``. + """ + self._raise_if_unacknowledged("modified_count") + return self.__bulk_api_result.get("nModified") + + @property + def deleted_count(self): + """The number of documents deleted.""" + self._raise_if_unacknowledged("deleted_count") + return self.__bulk_api_result.get("nRemoved") + + @property + def upserted_count(self): + """The number of documents upserted.""" + self._raise_if_unacknowledged("upserted_count") + return self.__bulk_api_result.get("nUpserted") + + @property + def upserted_ids(self): + """A map of operation index to the _id of the upserted document.""" + self._raise_if_unacknowledged("upserted_ids") + if self.__bulk_api_result: + return dict((upsert["index"], upsert["_id"]) + for upsert in self.bulk_api_result["upserted"]) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/saslprep.py b/backend/venv/lib/python3.7/site-packages/pymongo/saslprep.py new file mode 100644 index 000000000..baa1a4066 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/saslprep.py @@ -0,0 +1,108 @@ +# Copyright 2016-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""An implementation of RFC4013 SASLprep.""" + +from bson.py3compat import text_type as _text_type + +try: + import stringprep +except ImportError: + HAVE_STRINGPREP = False + def saslprep(data): + """SASLprep dummy""" + if isinstance(data, _text_type): + raise TypeError( + "The stringprep module is not available. Usernames and " + "passwords must be ASCII strings.") + return data +else: + HAVE_STRINGPREP = True + import unicodedata + # RFC4013 section 2.3 prohibited output. + _PROHIBITED = ( + # A strict reading of RFC 4013 requires table c12 here, but + # characters from it are mapped to SPACE in the Map step. Can + # normalization reintroduce them somehow? + stringprep.in_table_c12, + stringprep.in_table_c21_c22, + stringprep.in_table_c3, + stringprep.in_table_c4, + stringprep.in_table_c5, + stringprep.in_table_c6, + stringprep.in_table_c7, + stringprep.in_table_c8, + stringprep.in_table_c9) + + def saslprep(data, prohibit_unassigned_code_points=True): + """An implementation of RFC4013 SASLprep. + + :Parameters: + - `data`: The string to SASLprep. Unicode strings + (python 2.x unicode, 3.x str) are supported. Byte strings + (python 2.x str, 3.x bytes) are ignored. + - `prohibit_unassigned_code_points`: True / False. RFC 3454 + and RFCs for various SASL mechanisms distinguish between + `queries` (unassigned code points allowed) and + `stored strings` (unassigned code points prohibited). Defaults + to ``True`` (unassigned code points are prohibited). + + :Returns: + The SASLprep'ed version of `data`. + """ + if not isinstance(data, _text_type): + return data + + if prohibit_unassigned_code_points: + prohibited = _PROHIBITED + (stringprep.in_table_a1,) + else: + prohibited = _PROHIBITED + + # RFC3454 section 2, step 1 - Map + # RFC4013 section 2.1 mappings + # Map Non-ASCII space characters to SPACE (U+0020). Map + # commonly mapped to nothing characters to, well, nothing. + in_table_c12 = stringprep.in_table_c12 + in_table_b1 = stringprep.in_table_b1 + data = u"".join( + [u"\u0020" if in_table_c12(elt) else elt + for elt in data if not in_table_b1(elt)]) + + # RFC3454 section 2, step 2 - Normalize + # RFC4013 section 2.2 normalization + data = unicodedata.ucd_3_2_0.normalize('NFKC', data) + + in_table_d1 = stringprep.in_table_d1 + if in_table_d1(data[0]): + if not in_table_d1(data[-1]): + # RFC3454, Section 6, #3. If a string contains any + # RandALCat character, the first and last characters + # MUST be RandALCat characters. + raise ValueError("SASLprep: failed bidirectional check") + # RFC3454, Section 6, #2. If a string contains any RandALCat + # character, it MUST NOT contain any LCat character. + prohibited = prohibited + (stringprep.in_table_d2,) + else: + # RFC3454, Section 6, #3. Following the logic of #3, if + # the first character is not a RandALCat, no other character + # can be either. + prohibited = prohibited + (in_table_d1,) + + # RFC3454 section 2, step 3 and 4 - Prohibit and check bidi + for char in data: + if any(in_table(char) for in_table in prohibited): + raise ValueError( + "SASLprep: failed prohibited character check") + + return data diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/server.py b/backend/venv/lib/python3.7/site-packages/pymongo/server.py new file mode 100644 index 000000000..8c7162387 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/server.py @@ -0,0 +1,169 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Communicate with one MongoDB server in a topology.""" + +import contextlib + +from datetime import datetime + +from pymongo.message import _convert_exception +from pymongo.response import Response, ExhaustResponse +from pymongo.server_type import SERVER_TYPE + + +class Server(object): + def __init__(self, server_description, pool, monitor, topology_id=None, + listeners=None, events=None): + """Represent one MongoDB server.""" + self._description = server_description + self._pool = pool + self._monitor = monitor + self._topology_id = topology_id + self._publish = listeners is not None and listeners.enabled_for_server + self._listener = listeners + self._events = None + if self._publish: + self._events = events() + + def open(self): + """Start monitoring, or restart after a fork. + + Multiple calls have no effect. + """ + self._monitor.open() + + def reset(self): + """Clear the connection pool.""" + self.pool.reset() + + def close(self): + """Clear the connection pool and stop the monitor. + + Reconnect with open(). + """ + if self._publish: + self._events.put((self._listener.publish_server_closed, + (self._description.address, self._topology_id))) + self._monitor.close() + self._pool.reset() + + def request_check(self): + """Check the server's state soon.""" + self._monitor.request_check() + + def send_message_with_response( + self, + operation, + set_slave_okay, + all_credentials, + listeners, + exhaust=False): + """Send a message to MongoDB and return a Response object. + + Can raise ConnectionFailure. + + :Parameters: + - `operation`: A _Query or _GetMore object. + - `set_slave_okay`: Pass to operation.get_message. + - `all_credentials`: dict, maps auth source to MongoCredential. + - `listeners`: Instance of _EventListeners or None. + - `exhaust` (optional): If True, the socket used stays checked out. + It is returned along with its Pool in the Response. + """ + with self.get_socket(all_credentials, exhaust) as sock_info: + + duration = None + publish = listeners.enabled_for_commands + if publish: + start = datetime.now() + + use_find_cmd = operation.use_command(sock_info, exhaust) + message = operation.get_message( + set_slave_okay, sock_info, use_find_cmd) + request_id, data, max_doc_size = self._split_message(message) + + if publish: + encoding_duration = datetime.now() - start + cmd, dbn = operation.as_command(sock_info) + listeners.publish_command_start( + cmd, dbn, request_id, sock_info.address) + start = datetime.now() + + try: + sock_info.send_message(data, max_doc_size) + reply = sock_info.receive_message(request_id) + except Exception as exc: + if publish: + duration = (datetime.now() - start) + encoding_duration + failure = _convert_exception(exc) + listeners.publish_command_failure( + duration, failure, next(iter(cmd)), request_id, + sock_info.address) + raise + + if publish: + duration = (datetime.now() - start) + encoding_duration + + if exhaust: + return ExhaustResponse( + data=reply, + address=self._description.address, + socket_info=sock_info, + pool=self._pool, + duration=duration, + request_id=request_id, + from_command=use_find_cmd) + else: + return Response( + data=reply, + address=self._description.address, + duration=duration, + request_id=request_id, + from_command=use_find_cmd) + + def get_socket(self, all_credentials, checkout=False): + return self.pool.get_socket(all_credentials, checkout) + + @property + def description(self): + return self._description + + @description.setter + def description(self, server_description): + assert server_description.address == self._description.address + self._description = server_description + + @property + def pool(self): + return self._pool + + def _split_message(self, message): + """Return request_id, data, max_doc_size. + + :Parameters: + - `message`: (request_id, data, max_doc_size) or (request_id, data) + """ + if len(message) == 3: + return message + else: + # get_more and kill_cursors messages don't include BSON documents. + request_id, data = message + return request_id, data, 0 + + def __str__(self): + d = self._description + return '' % ( + d.address[0], d.address[1], + SERVER_TYPE._fields[d.server_type]) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/server_description.py b/backend/venv/lib/python3.7/site-packages/pymongo/server_description.py new file mode 100644 index 000000000..b77785549 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/server_description.py @@ -0,0 +1,202 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Represent one server the driver is connected to.""" + +from bson import EPOCH_NAIVE +from pymongo.server_type import SERVER_TYPE +from pymongo.ismaster import IsMaster +from pymongo.monotonic import time as _time + + +class ServerDescription(object): + """Immutable representation of one server. + + :Parameters: + - `address`: A (host, port) pair + - `ismaster`: Optional IsMaster instance + - `round_trip_time`: Optional float + - `error`: Optional, the last error attempting to connect to the server + """ + + __slots__ = ( + '_address', '_server_type', '_all_hosts', '_tags', '_replica_set_name', + '_primary', '_max_bson_size', '_max_message_size', + '_max_write_batch_size', '_min_wire_version', '_max_wire_version', + '_round_trip_time', '_me', '_is_writable', '_is_readable', + '_ls_timeout_minutes', '_error', '_set_version', '_election_id', + '_cluster_time', '_last_write_date', '_last_update_time') + + def __init__( + self, + address, + ismaster=None, + round_trip_time=None, + error=None): + self._address = address + if not ismaster: + ismaster = IsMaster({}) + + self._server_type = ismaster.server_type + self._all_hosts = ismaster.all_hosts + self._tags = ismaster.tags + self._replica_set_name = ismaster.replica_set_name + self._primary = ismaster.primary + self._max_bson_size = ismaster.max_bson_size + self._max_message_size = ismaster.max_message_size + self._max_write_batch_size = ismaster.max_write_batch_size + self._min_wire_version = ismaster.min_wire_version + self._max_wire_version = ismaster.max_wire_version + self._set_version = ismaster.set_version + self._election_id = ismaster.election_id + self._cluster_time = ismaster.cluster_time + self._is_writable = ismaster.is_writable + self._is_readable = ismaster.is_readable + self._ls_timeout_minutes = ismaster.logical_session_timeout_minutes + self._round_trip_time = round_trip_time + self._me = ismaster.me + self._last_update_time = _time() + self._error = error + + if ismaster.last_write_date: + # Convert from datetime to seconds. + delta = ismaster.last_write_date - EPOCH_NAIVE + self._last_write_date = delta.total_seconds() + else: + self._last_write_date = None + + @property + def address(self): + """The address (host, port) of this server.""" + return self._address + + @property + def server_type(self): + """The type of this server.""" + return self._server_type + + @property + def server_type_name(self): + """The server type as a human readable string. + + .. versionadded:: 3.4 + """ + return SERVER_TYPE._fields[self._server_type] + + @property + def all_hosts(self): + """List of hosts, passives, and arbiters known to this server.""" + return self._all_hosts + + @property + def tags(self): + return self._tags + + @property + def replica_set_name(self): + """Replica set name or None.""" + return self._replica_set_name + + @property + def primary(self): + """This server's opinion about who the primary is, or None.""" + return self._primary + + @property + def max_bson_size(self): + return self._max_bson_size + + @property + def max_message_size(self): + return self._max_message_size + + @property + def max_write_batch_size(self): + return self._max_write_batch_size + + @property + def min_wire_version(self): + return self._min_wire_version + + @property + def max_wire_version(self): + return self._max_wire_version + + @property + def set_version(self): + return self._set_version + + @property + def election_id(self): + return self._election_id + + @property + def cluster_time(self): + return self._cluster_time + + @property + def election_tuple(self): + return self._set_version, self._election_id + + @property + def me(self): + return self._me + + @property + def logical_session_timeout_minutes(self): + return self._ls_timeout_minutes + + @property + def last_write_date(self): + return self._last_write_date + + @property + def last_update_time(self): + return self._last_update_time + + @property + def round_trip_time(self): + """The current average latency or None.""" + # This override is for unittesting only! + if self._address in self._host_to_round_trip_time: + return self._host_to_round_trip_time[self._address] + + return self._round_trip_time + + @property + def error(self): + """The last error attempting to connect to the server, or None.""" + return self._error + + @property + def is_writable(self): + return self._is_writable + + @property + def is_readable(self): + return self._is_readable + + @property + def is_server_type_known(self): + return self.server_type != SERVER_TYPE.Unknown + + @property + def retryable_writes_supported(self): + """Checks if this server supports retryable writes.""" + return ( + self._ls_timeout_minutes is not None and + self._server_type in (SERVER_TYPE.Mongos, SERVER_TYPE.RSPrimary)) + + # For unittesting only. Use under no circumstances! + _host_to_round_trip_time = {} diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/server_selectors.py b/backend/venv/lib/python3.7/site-packages/pymongo/server_selectors.py new file mode 100644 index 000000000..01f13065c --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/server_selectors.py @@ -0,0 +1,156 @@ +# Copyright 2014-2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Criteria to select some ServerDescriptions from a TopologyDescription.""" + +from pymongo.server_type import SERVER_TYPE + + +class Selection(object): + """Input or output of a server selector function.""" + + @classmethod + def from_topology_description(cls, topology_description): + known_servers = topology_description.known_servers + primary = None + for sd in known_servers: + if sd.server_type == SERVER_TYPE.RSPrimary: + primary = sd + break + + return Selection(topology_description, + topology_description.known_servers, + topology_description.common_wire_version, + primary) + + def __init__(self, + topology_description, + server_descriptions, + common_wire_version, + primary): + self.topology_description = topology_description + self.server_descriptions = server_descriptions + self.primary = primary + self.common_wire_version = common_wire_version + + def with_server_descriptions(self, server_descriptions): + return Selection(self.topology_description, + server_descriptions, + self.common_wire_version, + self.primary) + + def secondary_with_max_last_write_date(self): + secondaries = secondary_server_selector(self) + if secondaries.server_descriptions: + return max(secondaries.server_descriptions, + key=lambda sd: sd.last_write_date) + + @property + def primary_selection(self): + primaries = [self.primary] if self.primary else [] + return self.with_server_descriptions(primaries) + + @property + def heartbeat_frequency(self): + return self.topology_description.heartbeat_frequency + + @property + def topology_type(self): + return self.topology_description.topology_type + + def __bool__(self): + return bool(self.server_descriptions) + + __nonzero__ = __bool__ # Python 2. + + def __getitem__(self, item): + return self.server_descriptions[item] + + +def any_server_selector(selection): + return selection + + +def readable_server_selector(selection): + return selection.with_server_descriptions( + [s for s in selection.server_descriptions if s.is_readable]) + + +def writable_server_selector(selection): + return selection.with_server_descriptions( + [s for s in selection.server_descriptions if s.is_writable]) + + +def secondary_server_selector(selection): + return selection.with_server_descriptions( + [s for s in selection.server_descriptions + if s.server_type == SERVER_TYPE.RSSecondary]) + + +def arbiter_server_selector(selection): + return selection.with_server_descriptions( + [s for s in selection.server_descriptions + if s.server_type == SERVER_TYPE.RSArbiter]) + + +def writable_preferred_server_selector(selection): + """Like PrimaryPreferred but doesn't use tags or latency.""" + return (writable_server_selector(selection) or + secondary_server_selector(selection)) + + +def apply_single_tag_set(tag_set, selection): + """All servers matching one tag set. + + A tag set is a dict. A server matches if its tags are a superset: + A server tagged {'a': '1', 'b': '2'} matches the tag set {'a': '1'}. + + The empty tag set {} matches any server. + """ + def tags_match(server_tags): + for key, value in tag_set.items(): + if key not in server_tags or server_tags[key] != value: + return False + + return True + + return selection.with_server_descriptions( + [s for s in selection.server_descriptions if tags_match(s.tags)]) + + +def apply_tag_sets(tag_sets, selection): + """All servers match a list of tag sets. + + tag_sets is a list of dicts. The empty tag set {} matches any server, + and may be provided at the end of the list as a fallback. So + [{'a': 'value'}, {}] expresses a preference for servers tagged + {'a': 'value'}, but accepts any server if none matches the first + preference. + """ + for tag_set in tag_sets: + with_tag_set = apply_single_tag_set(tag_set, selection) + if with_tag_set: + return with_tag_set + + return selection.with_server_descriptions([]) + + +def secondary_with_tags_server_selector(tag_sets, selection): + """All near-enough secondaries matching the tag sets.""" + return apply_tag_sets(tag_sets, secondary_server_selector(selection)) + + +def member_with_tags_server_selector(tag_sets, selection): + """All near-enough members matching the tag sets.""" + return apply_tag_sets(tag_sets, readable_server_selector(selection)) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/server_type.py b/backend/venv/lib/python3.7/site-packages/pymongo/server_type.py new file mode 100644 index 000000000..c231aa04c --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/server_type.py @@ -0,0 +1,23 @@ +# Copyright 2014-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Type codes for MongoDB servers.""" + +from collections import namedtuple + + +SERVER_TYPE = namedtuple('ServerType', + ['Unknown', 'Mongos', 'RSPrimary', 'RSSecondary', + 'RSArbiter', 'RSOther', 'RSGhost', + 'Standalone'])(*range(8)) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/settings.py b/backend/venv/lib/python3.7/site-packages/pymongo/settings.py new file mode 100644 index 000000000..a03bbc98a --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/settings.py @@ -0,0 +1,123 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Represent MongoClient's configuration.""" + +import threading + +from bson.objectid import ObjectId +from pymongo import common, monitor, pool +from pymongo.common import LOCAL_THRESHOLD_MS, SERVER_SELECTION_TIMEOUT +from pymongo.errors import ConfigurationError +from pymongo.topology_description import TOPOLOGY_TYPE +from pymongo.pool import PoolOptions +from pymongo.server_description import ServerDescription + + +class TopologySettings(object): + def __init__(self, + seeds=None, + replica_set_name=None, + pool_class=None, + pool_options=None, + monitor_class=None, + condition_class=None, + local_threshold_ms=LOCAL_THRESHOLD_MS, + server_selection_timeout=SERVER_SELECTION_TIMEOUT, + heartbeat_frequency=common.HEARTBEAT_FREQUENCY, + server_selector=None): + """Represent MongoClient's configuration. + + Take a list of (host, port) pairs and optional replica set name. + """ + if heartbeat_frequency < common.MIN_HEARTBEAT_INTERVAL: + raise ConfigurationError( + "heartbeatFrequencyMS cannot be less than %d" % ( + common.MIN_HEARTBEAT_INTERVAL * 1000,)) + + self._seeds = seeds or [('localhost', 27017)] + self._replica_set_name = replica_set_name + self._pool_class = pool_class or pool.Pool + self._pool_options = pool_options or PoolOptions() + self._monitor_class = monitor_class or monitor.Monitor + self._condition_class = condition_class or threading.Condition + self._local_threshold_ms = local_threshold_ms + self._server_selection_timeout = server_selection_timeout + self._server_selector = server_selector + self._heartbeat_frequency = heartbeat_frequency + self._direct = (len(self._seeds) == 1 and not replica_set_name) + self._topology_id = ObjectId() + + @property + def seeds(self): + """List of server addresses.""" + return self._seeds + + @property + def replica_set_name(self): + return self._replica_set_name + + @property + def pool_class(self): + return self._pool_class + + @property + def pool_options(self): + return self._pool_options + + @property + def monitor_class(self): + return self._monitor_class + + @property + def condition_class(self): + return self._condition_class + + @property + def local_threshold_ms(self): + return self._local_threshold_ms + + @property + def server_selection_timeout(self): + return self._server_selection_timeout + + @property + def server_selector(self): + return self._server_selector + + @property + def heartbeat_frequency(self): + return self._heartbeat_frequency + + @property + def direct(self): + """Connect directly to a single server, or use a set of servers? + + True if there is one seed and no replica_set_name. + """ + return self._direct + + def get_topology_type(self): + if self.direct: + return TOPOLOGY_TYPE.Single + elif self.replica_set_name is not None: + return TOPOLOGY_TYPE.ReplicaSetNoPrimary + else: + return TOPOLOGY_TYPE.Unknown + + def get_server_descriptions(self): + """Initial dict of (address, ServerDescription) for all seeds.""" + return dict([ + (address, ServerDescription(address)) + for address in self.seeds]) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/son_manipulator.py b/backend/venv/lib/python3.7/site-packages/pymongo/son_manipulator.py new file mode 100644 index 000000000..f470d6f33 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/son_manipulator.py @@ -0,0 +1,191 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""**DEPRECATED**: Manipulators that can edit SON objects as they enter and exit +a database. + +The :class:`~pymongo.son_manipulator.SONManipulator` API has limitations as a +technique for transforming your data. Instead, it is more flexible and +straightforward to transform outgoing documents in your own code before passing +them to PyMongo, and transform incoming documents after receiving them from +PyMongo. SON Manipulators will be removed from PyMongo in 4.0. + +PyMongo does **not** apply SON manipulators to documents passed to +the modern methods :meth:`~pymongo.collection.Collection.bulk_write`, +:meth:`~pymongo.collection.Collection.insert_one`, +:meth:`~pymongo.collection.Collection.insert_many`, +:meth:`~pymongo.collection.Collection.update_one`, or +:meth:`~pymongo.collection.Collection.update_many`. SON manipulators are +**not** applied to documents returned by the modern methods +:meth:`~pymongo.collection.Collection.find_one_and_delete`, +:meth:`~pymongo.collection.Collection.find_one_and_replace`, and +:meth:`~pymongo.collection.Collection.find_one_and_update`. +""" + +from bson.dbref import DBRef +from bson.objectid import ObjectId +from bson.py3compat import abc +from bson.son import SON + + +class SONManipulator(object): + """A base son manipulator. + + This manipulator just saves and restores objects without changing them. + """ + + def will_copy(self): + """Will this SON manipulator make a copy of the incoming document? + + Derived classes that do need to make a copy should override this + method, returning True instead of False. All non-copying manipulators + will be applied first (so that the user's document will be updated + appropriately), followed by copying manipulators. + """ + return False + + def transform_incoming(self, son, collection): + """Manipulate an incoming SON object. + + :Parameters: + - `son`: the SON object to be inserted into the database + - `collection`: the collection the object is being inserted into + """ + if self.will_copy(): + return SON(son) + return son + + def transform_outgoing(self, son, collection): + """Manipulate an outgoing SON object. + + :Parameters: + - `son`: the SON object being retrieved from the database + - `collection`: the collection this object was stored in + """ + if self.will_copy(): + return SON(son) + return son + + +class ObjectIdInjector(SONManipulator): + """A son manipulator that adds the _id field if it is missing. + + .. versionchanged:: 2.7 + ObjectIdInjector is no longer used by PyMongo, but remains in this + module for backwards compatibility. + """ + + def transform_incoming(self, son, collection): + """Add an _id field if it is missing. + """ + if not "_id" in son: + son["_id"] = ObjectId() + return son + + +# This is now handled during BSON encoding (for performance reasons), +# but I'm keeping this here as a reference for those implementing new +# SONManipulators. +class ObjectIdShuffler(SONManipulator): + """A son manipulator that moves _id to the first position. + """ + + def will_copy(self): + """We need to copy to be sure that we are dealing with SON, not a dict. + """ + return True + + def transform_incoming(self, son, collection): + """Move _id to the front if it's there. + """ + if not "_id" in son: + return son + transformed = SON({"_id": son["_id"]}) + transformed.update(son) + return transformed + + +class NamespaceInjector(SONManipulator): + """A son manipulator that adds the _ns field. + """ + + def transform_incoming(self, son, collection): + """Add the _ns field to the incoming object + """ + son["_ns"] = collection.name + return son + + +class AutoReference(SONManipulator): + """Transparently reference and de-reference already saved embedded objects. + + This manipulator should probably only be used when the NamespaceInjector is + also being used, otherwise it doesn't make too much sense - documents can + only be auto-referenced if they have an *_ns* field. + + NOTE: this will behave poorly if you have a circular reference. + + TODO: this only works for documents that are in the same database. To fix + this we'll need to add a DatabaseInjector that adds *_db* and then make + use of the optional *database* support for DBRefs. + """ + + def __init__(self, db): + self.database = db + + def will_copy(self): + """We need to copy so the user's document doesn't get transformed refs. + """ + return True + + def transform_incoming(self, son, collection): + """Replace embedded documents with DBRefs. + """ + + def transform_value(value): + if isinstance(value, abc.MutableMapping): + if "_id" in value and "_ns" in value: + return DBRef(value["_ns"], transform_value(value["_id"])) + else: + return transform_dict(SON(value)) + elif isinstance(value, list): + return [transform_value(v) for v in value] + return value + + def transform_dict(object): + for (key, value) in object.items(): + object[key] = transform_value(value) + return object + + return transform_dict(SON(son)) + + def transform_outgoing(self, son, collection): + """Replace DBRefs with embedded documents. + """ + + def transform_value(value): + if isinstance(value, DBRef): + return self.database.dereference(value) + elif isinstance(value, list): + return [transform_value(v) for v in value] + elif isinstance(value, abc.MutableMapping): + return transform_dict(SON(value)) + return value + + def transform_dict(object): + for (key, value) in object.items(): + object[key] = transform_value(value) + return object + + return transform_dict(SON(son)) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/ssl_context.py b/backend/venv/lib/python3.7/site-packages/pymongo/ssl_context.py new file mode 100644 index 000000000..6afb2d2d5 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/ssl_context.py @@ -0,0 +1,96 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""A fake SSLContext implementation.""" + +try: + import ssl +except ImportError: + pass + + +class SSLContext(object): + """A fake SSLContext. + + This implements an API similar to ssl.SSLContext from python 3.2 + but does not implement methods or properties that would be + incompatible with ssl.wrap_socket from python 2.7 < 2.7.9. + + You must pass protocol which must be one of the PROTOCOL_* constants + defined in the ssl module. ssl.PROTOCOL_SSLv23 is recommended for maximum + interoperability. + """ + + __slots__ = ('_cafile', '_certfile', + '_keyfile', '_protocol', '_verify_mode') + + def __init__(self, protocol): + self._cafile = None + self._certfile = None + self._keyfile = None + self._protocol = protocol + self._verify_mode = ssl.CERT_NONE + + @property + def protocol(self): + """The protocol version chosen when constructing the context. + This attribute is read-only. + """ + return self._protocol + + def __get_verify_mode(self): + """Whether to try to verify other peers' certificates and how to + behave if verification fails. This attribute must be one of + ssl.CERT_NONE, ssl.CERT_OPTIONAL or ssl.CERT_REQUIRED. + """ + return self._verify_mode + + def __set_verify_mode(self, value): + """Setter for verify_mode.""" + self._verify_mode = value + + verify_mode = property(__get_verify_mode, __set_verify_mode) + + def load_cert_chain(self, certfile, keyfile=None): + """Load a private key and the corresponding certificate. The certfile + string must be the path to a single file in PEM format containing the + certificate as well as any number of CA certificates needed to + establish the certificate's authenticity. The keyfile string, if + present, must point to a file containing the private key. Otherwise + the private key will be taken from certfile as well. + """ + self._certfile = certfile + self._keyfile = keyfile + + def load_verify_locations(self, cafile=None, dummy=None): + """Load a set of "certification authority"(CA) certificates used to + validate other peers' certificates when `~verify_mode` is other than + ssl.CERT_NONE. + """ + self._cafile = cafile + + def wrap_socket(self, sock, server_side=False, + do_handshake_on_connect=True, + suppress_ragged_eofs=True, dummy=None): + """Wrap an existing Python socket sock and return an ssl.SSLSocket + object. + """ + return ssl.wrap_socket(sock, keyfile=self._keyfile, + certfile=self._certfile, + server_side=server_side, + cert_reqs=self._verify_mode, + ssl_version=self._protocol, + ca_certs=self._cafile, + do_handshake_on_connect=do_handshake_on_connect, + suppress_ragged_eofs=suppress_ragged_eofs) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/ssl_match_hostname.py b/backend/venv/lib/python3.7/site-packages/pymongo/ssl_match_hostname.py new file mode 100644 index 000000000..49e3dd657 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/ssl_match_hostname.py @@ -0,0 +1,135 @@ +# Backport of the match_hostname logic from python 3.5, with small +# changes to support IP address matching on python 2.7 and 3.4. + +import re +import sys + +try: + # Python 3.4+, or the ipaddress module from pypi. + from ipaddress import ip_address +except ImportError: + ip_address = lambda address: None + +# ipaddress.ip_address requires unicode +if sys.version_info[0] < 3: + _unicode = unicode +else: + _unicode = lambda value: value + + +class CertificateError(ValueError): + pass + + +def _dnsname_match(dn, hostname, max_wildcards=1): + """Matching according to RFC 6125, section 6.4.3 + + http://tools.ietf.org/html/rfc6125#section-6.4.3 + """ + pats = [] + if not dn: + return False + + parts = dn.split(r'.') + leftmost = parts[0] + remainder = parts[1:] + + wildcards = leftmost.count('*') + if wildcards > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survey of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn)) + + # speed up common case w/o wildcards + if not wildcards: + return dn.lower() == hostname.lower() + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in which + # the wildcard character comprises a label other than the left-most label. + if leftmost == '*': + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append('[^.]+') + elif leftmost.startswith('xn--') or hostname.startswith('xn--'): + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + pats.append(re.escape(leftmost)) + else: + # Otherwise, '*' matches any dotless string, e.g. www* + pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) + + # add the remaining fragments, ignore any wildcards + for frag in remainder: + pats.append(re.escape(frag)) + + pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + return pat.match(hostname) + + +def _ipaddress_match(ipname, host_ip): + """Exact matching of IP addresses. + + RFC 6125 explicitly doesn't define an algorithm for this + (section 1.7.2 - "Out of Scope"). + """ + # OpenSSL may add a trailing newline to a subjectAltName's IP address + ip = ip_address(_unicode(ipname).rstrip()) + return ip == host_ip + + +def match_hostname(cert, hostname): + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 + rules are followed. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError("empty or no certificate, match_hostname needs a " + "SSL socket or SSL context with either " + "CERT_OPTIONAL or CERT_REQUIRED") + try: + host_ip = ip_address(_unicode(hostname)) + except (ValueError, UnicodeError): + # Not an IP address (common case) + host_ip = None + dnsnames = [] + san = cert.get('subjectAltName', ()) + for key, value in san: + if key == 'DNS': + if host_ip is None and _dnsname_match(value, hostname): + return + dnsnames.append(value) + elif key == 'IP Address': + if host_ip is not None and _ipaddress_match(value, host_ip): + return + dnsnames.append(value) + if not dnsnames: + # The subject is only checked when there is no dNSName entry + # in subjectAltName + for sub in cert.get('subject', ()): + for key, value in sub: + # XXX according to RFC 2818, the most specific Common Name + # must be used. + if key == 'commonName': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if len(dnsnames) > 1: + raise CertificateError("hostname %r " + "doesn't match either of %s" + % (hostname, ', '.join(map(repr, dnsnames)))) + elif len(dnsnames) == 1: + raise CertificateError("hostname %r " + "doesn't match %r" + % (hostname, dnsnames[0])) + else: + raise CertificateError("no appropriate commonName or " + "subjectAltName fields were found") diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/ssl_support.py b/backend/venv/lib/python3.7/site-packages/pymongo/ssl_support.py new file mode 100644 index 000000000..181c8b109 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/ssl_support.py @@ -0,0 +1,191 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Support for SSL in PyMongo.""" + +import atexit +import sys +import threading + +HAVE_SSL = True +try: + import ssl +except ImportError: + HAVE_SSL = False + +HAVE_CERTIFI = False +try: + import certifi + HAVE_CERTIFI = True +except ImportError: + pass + +HAVE_WINCERTSTORE = False +try: + from wincertstore import CertFile + HAVE_WINCERTSTORE = True +except ImportError: + pass + +from bson.py3compat import string_type +from pymongo.errors import ConfigurationError + +_WINCERTSLOCK = threading.Lock() +_WINCERTS = None + +_PY37PLUS = sys.version_info[:2] >= (3, 7) + +if HAVE_SSL: + try: + # Python 2.7.9+, PyPy 2.5.1+, etc. + from ssl import SSLContext + except ImportError: + from pymongo.ssl_context import SSLContext + + def validate_cert_reqs(option, value): + """Validate the cert reqs are valid. It must be None or one of the + three values ``ssl.CERT_NONE``, ``ssl.CERT_OPTIONAL`` or + ``ssl.CERT_REQUIRED``. + """ + if value is None: + return value + elif isinstance(value, string_type) and hasattr(ssl, value): + value = getattr(ssl, value) + + if value in (ssl.CERT_NONE, ssl.CERT_OPTIONAL, ssl.CERT_REQUIRED): + return value + raise ValueError("The value of %s must be one of: " + "`ssl.CERT_NONE`, `ssl.CERT_OPTIONAL` or " + "`ssl.CERT_REQUIRED`" % (option,)) + + def _load_wincerts(): + """Set _WINCERTS to an instance of wincertstore.Certfile.""" + global _WINCERTS + + certfile = CertFile() + certfile.addstore("CA") + certfile.addstore("ROOT") + atexit.register(certfile.close) + + _WINCERTS = certfile + + # XXX: Possible future work. + # - OCSP? Not supported by python at all. + # http://bugs.python.org/issue17123 + # - Adding an ssl_context keyword argument to MongoClient? This might + # be useful for sites that have unusual requirements rather than + # trying to expose every SSLContext option through a keyword/uri + # parameter. + def get_ssl_context(*args): + """Create and return an SSLContext object.""" + (certfile, + keyfile, + passphrase, + ca_certs, + cert_reqs, + crlfile, + match_hostname) = args + verify_mode = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs + # Note PROTOCOL_SSLv23 is about the most misleading name imaginable. + # This configures the server and client to negotiate the + # highest protocol version they both support. A very good thing. + # PROTOCOL_TLS_CLIENT was added in CPython 3.6, deprecating + # PROTOCOL_SSLv23. + ctx = SSLContext( + getattr(ssl, "PROTOCOL_TLS_CLIENT", ssl.PROTOCOL_SSLv23)) + # SSLContext.check_hostname was added in CPython 2.7.9 and 3.4. + # PROTOCOL_TLS_CLIENT (added in Python 3.6) enables it by default. + if hasattr(ctx, "check_hostname"): + if _PY37PLUS and verify_mode != ssl.CERT_NONE: + # Python 3.7 uses OpenSSL's hostname matching implementation + # making it the obvious version to start using this with. + # Python 3.6 might have been a good version, but it suffers + # from https://bugs.python.org/issue32185. + # We'll use our bundled match_hostname for older Python + # versions, which also supports IP address matching + # with Python < 3.5. + ctx.check_hostname = match_hostname + else: + ctx.check_hostname = False + if hasattr(ctx, "options"): + # Explicitly disable SSLv2, SSLv3 and TLS compression. Note that + # up to date versions of MongoDB 2.4 and above already disable + # SSLv2 and SSLv3, python disables SSLv2 by default in >= 2.7.7 + # and >= 3.3.4 and SSLv3 in >= 3.4.3. There is no way for us to do + # any of this explicitly for python 2.7 before 2.7.9. + ctx.options |= getattr(ssl, "OP_NO_SSLv2", 0) + ctx.options |= getattr(ssl, "OP_NO_SSLv3", 0) + # OpenSSL >= 1.0.0 + ctx.options |= getattr(ssl, "OP_NO_COMPRESSION", 0) + # Python 3.7+ with OpenSSL >= 1.1.0h + ctx.options |= getattr(ssl, "OP_NO_RENEGOTIATION", 0) + if certfile is not None: + try: + if passphrase is not None: + vi = sys.version_info + # Since python just added a new parameter to an existing method + # this seems to be about the best we can do. + if (vi[0] == 2 and vi < (2, 7, 9) or + vi[0] == 3 and vi < (3, 3)): + raise ConfigurationError( + "Support for ssl_pem_passphrase requires " + "python 2.7.9+ (pypy 2.5.1+) or 3.3+") + ctx.load_cert_chain(certfile, keyfile, passphrase) + else: + ctx.load_cert_chain(certfile, keyfile) + except ssl.SSLError as exc: + raise ConfigurationError( + "Private key doesn't match certificate: %s" % (exc,)) + if crlfile is not None: + if not hasattr(ctx, "verify_flags"): + raise ConfigurationError( + "Support for ssl_crlfile requires " + "python 2.7.9+ (pypy 2.5.1+) or 3.4+") + # Match the server's behavior. + ctx.verify_flags = ssl.VERIFY_CRL_CHECK_LEAF + ctx.load_verify_locations(crlfile) + if ca_certs is not None: + ctx.load_verify_locations(ca_certs) + elif cert_reqs != ssl.CERT_NONE: + # CPython >= 2.7.9 or >= 3.4.0, pypy >= 2.5.1 + if hasattr(ctx, "load_default_certs"): + ctx.load_default_certs() + # Python >= 3.2.0, useless on Windows. + elif (sys.platform != "win32" and + hasattr(ctx, "set_default_verify_paths")): + ctx.set_default_verify_paths() + elif sys.platform == "win32" and HAVE_WINCERTSTORE: + with _WINCERTSLOCK: + if _WINCERTS is None: + _load_wincerts() + ctx.load_verify_locations(_WINCERTS.name) + elif HAVE_CERTIFI: + ctx.load_verify_locations(certifi.where()) + else: + raise ConfigurationError( + "`ssl_cert_reqs` is not ssl.CERT_NONE and no system " + "CA certificates could be loaded. `ssl_ca_certs` is " + "required.") + ctx.verify_mode = verify_mode + return ctx +else: + def validate_cert_reqs(option, dummy): + """No ssl module, raise ConfigurationError.""" + raise ConfigurationError("The value of %s is set but can't be " + "validated. The ssl module is not available" + % (option,)) + + def get_ssl_context(*dummy): + """No ssl module, raise ConfigurationError.""" + raise ConfigurationError("The ssl module is not available.") diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/thread_util.py b/backend/venv/lib/python3.7/site-packages/pymongo/thread_util.py new file mode 100644 index 000000000..3869ec322 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/thread_util.py @@ -0,0 +1,131 @@ +# Copyright 2012-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utilities for multi-threading support.""" + +import threading +try: + from time import monotonic as _time +except ImportError: + from time import time as _time + +from pymongo.monotonic import time as _time +from pymongo.errors import ExceededMaxWaiters + + +### Begin backport from CPython 3.2 for timeout support for Semaphore.acquire +class Semaphore: + + # After Tim Peters' semaphore class, but not quite the same (no maximum) + + def __init__(self, value=1): + if value < 0: + raise ValueError("semaphore initial value must be >= 0") + self._cond = threading.Condition(threading.Lock()) + self._value = value + + def acquire(self, blocking=True, timeout=None): + if not blocking and timeout is not None: + raise ValueError("can't specify timeout for non-blocking acquire") + rc = False + endtime = None + self._cond.acquire() + while self._value == 0: + if not blocking: + break + if timeout is not None: + if endtime is None: + endtime = _time() + timeout + else: + timeout = endtime - _time() + if timeout <= 0: + break + self._cond.wait(timeout) + else: + self._value = self._value - 1 + rc = True + self._cond.release() + return rc + + __enter__ = acquire + + def release(self): + self._cond.acquire() + self._value = self._value + 1 + self._cond.notify() + self._cond.release() + + def __exit__(self, t, v, tb): + self.release() + + @property + def counter(self): + return self._value + + +class BoundedSemaphore(Semaphore): + """Semaphore that checks that # releases is <= # acquires""" + def __init__(self, value=1): + Semaphore.__init__(self, value) + self._initial_value = value + + def release(self): + if self._value >= self._initial_value: + raise ValueError("Semaphore released too many times") + return Semaphore.release(self) +### End backport from CPython 3.2 + + +class DummySemaphore(object): + def __init__(self, value=None): + pass + + def acquire(self, blocking=True, timeout=None): + return True + + def release(self): + pass + + +class MaxWaitersBoundedSemaphore(object): + def __init__(self, semaphore_class, value=1, max_waiters=1): + self.waiter_semaphore = semaphore_class(max_waiters) + self.semaphore = semaphore_class(value) + + def acquire(self, blocking=True, timeout=None): + if not self.waiter_semaphore.acquire(False): + raise ExceededMaxWaiters() + try: + return self.semaphore.acquire(blocking, timeout) + finally: + self.waiter_semaphore.release() + + def __getattr__(self, name): + return getattr(self.semaphore, name) + + +class MaxWaitersBoundedSemaphoreThread(MaxWaitersBoundedSemaphore): + def __init__(self, value=1, max_waiters=1): + MaxWaitersBoundedSemaphore.__init__( + self, BoundedSemaphore, value, max_waiters) + + +def create_semaphore(max_size, max_waiters): + if max_size is None: + return DummySemaphore() + else: + if max_waiters is None: + return BoundedSemaphore(max_size) + else: + return MaxWaitersBoundedSemaphoreThread(max_size, max_waiters) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/topology.py b/backend/venv/lib/python3.7/site-packages/pymongo/topology.py new file mode 100644 index 000000000..60f4fa7c8 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/topology.py @@ -0,0 +1,633 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Internal class to monitor a topology of one or more servers.""" + +import os +import random +import threading +import warnings +import weakref + +from bson.py3compat import itervalues, PY3 +if PY3: + import queue as Queue +else: + import Queue + +from pymongo import common +from pymongo import periodic_executor +from pymongo.pool import PoolOptions +from pymongo.topology_description import (updated_topology_description, + SERVER_TYPE, + TOPOLOGY_TYPE, + TopologyDescription) +from pymongo.errors import ServerSelectionTimeoutError, ConfigurationError +from pymongo.monotonic import time as _time +from pymongo.server import Server +from pymongo.server_selectors import (any_server_selector, + arbiter_server_selector, + secondary_server_selector, + readable_server_selector, + writable_server_selector, + Selection) +from pymongo.client_session import _ServerSessionPool + + +def process_events_queue(queue_ref): + q = queue_ref() + if not q: + return False # Cancel PeriodicExecutor. + + while True: + try: + event = q.get_nowait() + except Queue.Empty: + break + else: + fn, args = event + fn(*args) + + return True # Continue PeriodicExecutor. + + +class Topology(object): + """Monitor a topology of one or more servers.""" + def __init__(self, topology_settings): + self._topology_id = topology_settings._topology_id + self._listeners = topology_settings._pool_options.event_listeners + pub = self._listeners is not None + self._publish_server = pub and self._listeners.enabled_for_server + self._publish_tp = pub and self._listeners.enabled_for_topology + + # Create events queue if there are publishers. + self._events = None + self._events_thread = None + + if self._publish_server or self._publish_tp: + self._events = Queue.Queue(maxsize=100) + + if self._publish_tp: + self._events.put((self._listeners.publish_topology_opened, + (self._topology_id,))) + self._settings = topology_settings + topology_description = TopologyDescription( + topology_settings.get_topology_type(), + topology_settings.get_server_descriptions(), + topology_settings.replica_set_name, + None, + None, + topology_settings) + + self._description = topology_description + if self._publish_tp: + initial_td = TopologyDescription(TOPOLOGY_TYPE.Unknown, {}, None, + None, None, self._settings) + self._events.put(( + self._listeners.publish_topology_description_changed, + (initial_td, self._description, self._topology_id))) + + for seed in topology_settings.seeds: + if self._publish_server: + self._events.put((self._listeners.publish_server_opened, + (seed, self._topology_id))) + + # Store the seed list to help diagnose errors in _error_message(). + self._seed_addresses = list(topology_description.server_descriptions()) + self._opened = False + self._lock = threading.Lock() + self._condition = self._settings.condition_class(self._lock) + self._servers = {} + self._pid = None + self._max_cluster_time = None + self._session_pool = _ServerSessionPool() + + if self._publish_server or self._publish_tp: + def target(): + return process_events_queue(weak) + + executor = periodic_executor.PeriodicExecutor( + interval=common.EVENTS_QUEUE_FREQUENCY, + min_interval=0.5, + target=target, + name="pymongo_events_thread") + + # We strongly reference the executor and it weakly references + # the queue via this closure. When the topology is freed, stop + # the executor soon. + weak = weakref.ref(self._events) + self.__events_executor = executor + executor.open() + + def open(self): + """Start monitoring, or restart after a fork. + + No effect if called multiple times. + + .. warning:: Topology is shared among multiple threads and is protected + by mutual exclusion. Using Topology from a process other than the one + that initialized it will emit a warning and may result in deadlock. To + prevent this from happening, MongoClient must be created after any + forking. + + """ + if self._pid is None: + self._pid = os.getpid() + else: + if os.getpid() != self._pid: + warnings.warn( + "MongoClient opened before fork. Create MongoClient only " + "after forking. See PyMongo's documentation for details: " + "http://api.mongodb.org/python/current/faq.html#" + "is-pymongo-fork-safe") + + with self._lock: + self._ensure_opened() + + def select_servers(self, + selector, + server_selection_timeout=None, + address=None): + """Return a list of Servers matching selector, or time out. + + :Parameters: + - `selector`: function that takes a list of Servers and returns + a subset of them. + - `server_selection_timeout` (optional): maximum seconds to wait. + If not provided, the default value common.SERVER_SELECTION_TIMEOUT + is used. + - `address`: optional server address to select. + + Calls self.open() if needed. + + Raises exc:`ServerSelectionTimeoutError` after + `server_selection_timeout` if no matching servers are found. + """ + if server_selection_timeout is None: + server_timeout = self._settings.server_selection_timeout + else: + server_timeout = server_selection_timeout + + with self._lock: + server_descriptions = self._select_servers_loop( + selector, server_timeout, address) + + return [self.get_server_by_address(sd.address) + for sd in server_descriptions] + + def _select_servers_loop(self, selector, timeout, address): + """select_servers() guts. Hold the lock when calling this.""" + now = _time() + end_time = now + timeout + server_descriptions = self._description.apply_selector( + selector, address, custom_selector=self._settings.server_selector) + + while not server_descriptions: + # No suitable servers. + if timeout == 0 or now > end_time: + raise ServerSelectionTimeoutError( + self._error_message(selector)) + + self._ensure_opened() + self._request_check_all() + + # Release the lock and wait for the topology description to + # change, or for a timeout. We won't miss any changes that + # came after our most recent apply_selector call, since we've + # held the lock until now. + self._condition.wait(common.MIN_HEARTBEAT_INTERVAL) + self._description.check_compatible() + now = _time() + server_descriptions = self._description.apply_selector( + selector, address, + custom_selector=self._settings.server_selector) + + self._description.check_compatible() + return server_descriptions + + def select_server(self, + selector, + server_selection_timeout=None, + address=None): + """Like select_servers, but choose a random server if several match.""" + return random.choice(self.select_servers(selector, + server_selection_timeout, + address)) + + def select_server_by_address(self, address, + server_selection_timeout=None): + """Return a Server for "address", reconnecting if necessary. + + If the server's type is not known, request an immediate check of all + servers. Time out after "server_selection_timeout" if the server + cannot be reached. + + :Parameters: + - `address`: A (host, port) pair. + - `server_selection_timeout` (optional): maximum seconds to wait. + If not provided, the default value + common.SERVER_SELECTION_TIMEOUT is used. + + Calls self.open() if needed. + + Raises exc:`ServerSelectionTimeoutError` after + `server_selection_timeout` if no matching servers are found. + """ + return self.select_server(any_server_selector, + server_selection_timeout, + address) + + def _process_change(self, server_description): + """Process a new ServerDescription on an opened topology. + + Hold the lock when calling this. + """ + td_old = self._description + if self._publish_server: + old_server_description = td_old._server_descriptions[ + server_description.address] + self._events.put(( + self._listeners.publish_server_description_changed, + (old_server_description, server_description, + server_description.address, self._topology_id))) + + self._description = updated_topology_description( + self._description, server_description) + + self._update_servers() + self._receive_cluster_time_no_lock(server_description.cluster_time) + + if self._publish_tp: + self._events.put(( + self._listeners.publish_topology_description_changed, + (td_old, self._description, self._topology_id))) + + # Wake waiters in select_servers(). + self._condition.notify_all() + + def on_change(self, server_description): + """Process a new ServerDescription after an ismaster call completes.""" + # We do no I/O holding the lock. + with self._lock: + # Monitors may continue working on ismaster calls for some time + # after a call to Topology.close, so this method may be called at + # any time. Ensure the topology is open before processing the + # change. + # Any monitored server was definitely in the topology description + # once. Check if it's still in the description or if some state- + # change removed it. E.g., we got a host list from the primary + # that didn't include this server. + if (self._opened and + self._description.has_server(server_description.address)): + self._process_change(server_description) + + def get_server_by_address(self, address): + """Get a Server or None. + + Returns the current version of the server immediately, even if it's + Unknown or absent from the topology. Only use this in unittests. + In driver code, use select_server_by_address, since then you're + assured a recent view of the server's type and wire protocol version. + """ + return self._servers.get(address) + + def has_server(self, address): + return address in self._servers + + def get_primary(self): + """Return primary's address or None.""" + # Implemented here in Topology instead of MongoClient, so it can lock. + with self._lock: + topology_type = self._description.topology_type + if topology_type != TOPOLOGY_TYPE.ReplicaSetWithPrimary: + return None + + return writable_server_selector(self._new_selection())[0].address + + def _get_replica_set_members(self, selector): + """Return set of replica set member addresses.""" + # Implemented here in Topology instead of MongoClient, so it can lock. + with self._lock: + topology_type = self._description.topology_type + if topology_type not in (TOPOLOGY_TYPE.ReplicaSetWithPrimary, + TOPOLOGY_TYPE.ReplicaSetNoPrimary): + return set() + + return set([sd.address for sd in selector(self._new_selection())]) + + def get_secondaries(self): + """Return set of secondary addresses.""" + return self._get_replica_set_members(secondary_server_selector) + + def get_arbiters(self): + """Return set of arbiter addresses.""" + return self._get_replica_set_members(arbiter_server_selector) + + def max_cluster_time(self): + """Return a document, the highest seen $clusterTime.""" + return self._max_cluster_time + + def _receive_cluster_time_no_lock(self, cluster_time): + # Driver Sessions Spec: "Whenever a driver receives a cluster time from + # a server it MUST compare it to the current highest seen cluster time + # for the deployment. If the new cluster time is higher than the + # highest seen cluster time it MUST become the new highest seen cluster + # time. Two cluster times are compared using only the BsonTimestamp + # value of the clusterTime embedded field." + if cluster_time: + # ">" uses bson.timestamp.Timestamp's comparison operator. + if (not self._max_cluster_time + or cluster_time['clusterTime'] > + self._max_cluster_time['clusterTime']): + self._max_cluster_time = cluster_time + + def receive_cluster_time(self, cluster_time): + with self._lock: + self._receive_cluster_time_no_lock(cluster_time) + + def request_check_all(self, wait_time=5): + """Wake all monitors, wait for at least one to check its server.""" + with self._lock: + self._request_check_all() + self._condition.wait(wait_time) + + def reset_pool(self, address): + with self._lock: + server = self._servers.get(address) + if server: + server.pool.reset() + + def reset_server(self, address): + """Clear our pool for a server and mark it Unknown. + + Do *not* request an immediate check. + """ + with self._lock: + self._reset_server(address) + + def reset_server_and_request_check(self, address): + """Clear our pool for a server, mark it Unknown, and check it soon.""" + with self._lock: + self._reset_server(address) + self._request_check(address) + + def update_pool(self): + # Remove any stale sockets and add new sockets if pool is too small. + with self._lock: + for server in self._servers.values(): + server._pool.remove_stale_sockets() + + def close(self): + """Clear pools and terminate monitors. Topology reopens on demand.""" + with self._lock: + for server in self._servers.values(): + server.close() + + # Mark all servers Unknown. + self._description = self._description.reset() + self._update_servers() + self._opened = False + + # Publish only after releasing the lock. + if self._publish_tp: + self._events.put((self._listeners.publish_topology_closed, + (self._topology_id,))) + if self._publish_server or self._publish_tp: + self.__events_executor.close() + + @property + def description(self): + return self._description + + def pop_all_sessions(self): + """Pop all session ids from the pool.""" + with self._lock: + return self._session_pool.pop_all() + + def get_server_session(self): + """Start or resume a server session, or raise ConfigurationError.""" + with self._lock: + session_timeout = self._description.logical_session_timeout_minutes + if session_timeout is None: + # Maybe we need an initial scan? Can raise ServerSelectionError. + if self._description.topology_type == TOPOLOGY_TYPE.Single: + if not self._description.has_known_servers: + self._select_servers_loop( + any_server_selector, + self._settings.server_selection_timeout, + None) + elif not self._description.readable_servers: + self._select_servers_loop( + readable_server_selector, + self._settings.server_selection_timeout, + None) + + session_timeout = self._description.logical_session_timeout_minutes + if session_timeout is None: + raise ConfigurationError( + "Sessions are not supported by this MongoDB deployment") + + return self._session_pool.get_server_session(session_timeout) + + def return_server_session(self, server_session, lock): + if lock: + with self._lock: + session_timeout = \ + self._description.logical_session_timeout_minutes + if session_timeout is not None: + self._session_pool.return_server_session(server_session, + session_timeout) + else: + # Called from a __del__ method, can't use a lock. + self._session_pool.return_server_session_no_lock(server_session) + + def is_mongos_non_blocking(self): + """Return if we are connected to a Mongos without blocking. + + If the state is unknown, return False. + """ + with self._lock: + if not self._opened: + return False + if self._description.topology_type == TOPOLOGY_TYPE.Sharded: + return True + server_descriptions = self._description.apply_selector( + writable_server_selector, None) + if not server_descriptions: + return False + return server_descriptions[0].server_type == SERVER_TYPE.Mongos + + def _new_selection(self): + """A Selection object, initially including all known servers. + + Hold the lock when calling this. + """ + return Selection.from_topology_description(self._description) + + def _ensure_opened(self): + """Start monitors, or restart after a fork. + + Hold the lock when calling this. + """ + if not self._opened: + self._opened = True + self._update_servers() + + # Start or restart the events publishing thread. + if self._publish_tp or self._publish_server: + self.__events_executor.open() + + # Ensure that the monitors are open. + for server in itervalues(self._servers): + server.open() + + def _reset_server(self, address): + """Clear our pool for a server and mark it Unknown. + + Hold the lock when calling this. Does *not* request an immediate check. + """ + server = self._servers.get(address) + + # "server" is None if another thread removed it from the topology. + if server: + server.reset() + + # Mark this server Unknown. + self._description = self._description.reset_server(address) + self._update_servers() + + def _request_check(self, address): + """Wake one monitor. Hold the lock when calling this.""" + server = self._servers.get(address) + + # "server" is None if another thread removed it from the topology. + if server: + server.request_check() + + def _request_check_all(self): + """Wake all monitors. Hold the lock when calling this.""" + for server in self._servers.values(): + server.request_check() + + def _update_servers(self): + """Sync our Servers from TopologyDescription.server_descriptions. + + Hold the lock while calling this. + """ + for address, sd in self._description.server_descriptions().items(): + if address not in self._servers: + monitor = self._settings.monitor_class( + server_description=sd, + topology=self, + pool=self._create_pool_for_monitor(address), + topology_settings=self._settings) + + weak = None + if self._publish_server: + weak = weakref.ref(self._events) + server = Server( + server_description=sd, + pool=self._create_pool_for_server(address), + monitor=monitor, + topology_id=self._topology_id, + listeners=self._listeners, + events=weak) + + self._servers[address] = server + server.open() + else: + self._servers[address].description = sd + + for address, server in list(self._servers.items()): + if not self._description.has_server(address): + server.close() + self._servers.pop(address) + + def _create_pool_for_server(self, address): + return self._settings.pool_class(address, self._settings.pool_options) + + def _create_pool_for_monitor(self, address): + options = self._settings.pool_options + + # According to the Server Discovery And Monitoring Spec, monitors use + # connect_timeout for both connect_timeout and socket_timeout. The + # pool only has one socket so maxPoolSize and so on aren't needed. + monitor_pool_options = PoolOptions( + connect_timeout=options.connect_timeout, + socket_timeout=options.connect_timeout, + ssl_context=options.ssl_context, + ssl_match_hostname=options.ssl_match_hostname, + event_listeners=options.event_listeners, + appname=options.appname, + driver=options.driver) + + return self._settings.pool_class(address, monitor_pool_options, + handshake=False) + + def _error_message(self, selector): + """Format an error message if server selection fails. + + Hold the lock when calling this. + """ + is_replica_set = self._description.topology_type in ( + TOPOLOGY_TYPE.ReplicaSetWithPrimary, + TOPOLOGY_TYPE.ReplicaSetNoPrimary) + + if is_replica_set: + server_plural = 'replica set members' + elif self._description.topology_type == TOPOLOGY_TYPE.Sharded: + server_plural = 'mongoses' + else: + server_plural = 'servers' + + if self._description.known_servers: + # We've connected, but no servers match the selector. + if selector is writable_server_selector: + if is_replica_set: + return 'No primary available for writes' + else: + return 'No %s available for writes' % server_plural + else: + return 'No %s match selector "%s"' % (server_plural, selector) + else: + addresses = list(self._description.server_descriptions()) + servers = list(self._description.server_descriptions().values()) + if not servers: + if is_replica_set: + # We removed all servers because of the wrong setName? + return 'No %s available for replica set name "%s"' % ( + server_plural, self._settings.replica_set_name) + else: + return 'No %s available' % server_plural + + # 1 or more servers, all Unknown. Are they unknown for one reason? + error = servers[0].error + same = all(server.error == error for server in servers[1:]) + if same: + if error is None: + # We're still discovering. + return 'No %s found yet' % server_plural + + if (is_replica_set and not + set(addresses).intersection(self._seed_addresses)): + # We replaced our seeds with new hosts but can't reach any. + return ( + 'Could not reach any servers in %s. Replica set is' + ' configured with internal hostnames or IPs?' % + addresses) + + return str(error) + else: + return ','.join(str(server.error) for server in servers + if server.error) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/topology_description.py b/backend/venv/lib/python3.7/site-packages/pymongo/topology_description.py new file mode 100644 index 000000000..eca761520 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/topology_description.py @@ -0,0 +1,545 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Represent a deployment of MongoDB servers.""" + +from collections import namedtuple + +from pymongo import common +from pymongo.errors import ConfigurationError +from pymongo.read_preferences import ReadPreference +from pymongo.server_description import ServerDescription +from pymongo.server_selectors import Selection +from pymongo.server_type import SERVER_TYPE + + +TOPOLOGY_TYPE = namedtuple('TopologyType', ['Single', 'ReplicaSetNoPrimary', + 'ReplicaSetWithPrimary', 'Sharded', + 'Unknown'])(*range(5)) + + +class TopologyDescription(object): + def __init__(self, + topology_type, + server_descriptions, + replica_set_name, + max_set_version, + max_election_id, + topology_settings): + """Representation of a deployment of MongoDB servers. + + :Parameters: + - `topology_type`: initial type + - `server_descriptions`: dict of (address, ServerDescription) for + all seeds + - `replica_set_name`: replica set name or None + - `max_set_version`: greatest setVersion seen from a primary, or None + - `max_election_id`: greatest electionId seen from a primary, or None + - `topology_settings`: a TopologySettings + """ + self._topology_type = topology_type + self._replica_set_name = replica_set_name + self._server_descriptions = server_descriptions + self._max_set_version = max_set_version + self._max_election_id = max_election_id + + # The heartbeat_frequency is used in staleness estimates. + self._topology_settings = topology_settings + + # Is PyMongo compatible with all servers' wire protocols? + self._incompatible_err = None + + for s in self._server_descriptions.values(): + if not s.is_server_type_known: + continue + + # s.min/max_wire_version is the server's wire protocol. + # MIN/MAX_SUPPORTED_WIRE_VERSION is what PyMongo supports. + server_too_new = ( + # Server too new. + s.min_wire_version is not None + and s.min_wire_version > common.MAX_SUPPORTED_WIRE_VERSION) + + server_too_old = ( + # Server too old. + s.max_wire_version is not None + and s.max_wire_version < common.MIN_SUPPORTED_WIRE_VERSION) + + if server_too_new: + self._incompatible_err = ( + "Server at %s:%d requires wire version %d, but this " + "version of PyMongo only supports up to %d." + % (s.address[0], s.address[1], + s.min_wire_version, common.MAX_SUPPORTED_WIRE_VERSION)) + + elif server_too_old: + self._incompatible_err = ( + "Server at %s:%d reports wire version %d, but this " + "version of PyMongo requires at least %d (MongoDB %s)." + % (s.address[0], s.address[1], + s.max_wire_version, + common.MIN_SUPPORTED_WIRE_VERSION, + common.MIN_SUPPORTED_SERVER_VERSION)) + + break + + # Server Discovery And Monitoring Spec: Whenever a client updates the + # TopologyDescription from an ismaster response, it MUST set + # TopologyDescription.logicalSessionTimeoutMinutes to the smallest + # logicalSessionTimeoutMinutes value among ServerDescriptions of all + # data-bearing server types. If any have a null + # logicalSessionTimeoutMinutes, then + # TopologyDescription.logicalSessionTimeoutMinutes MUST be set to null. + readable_servers = self.readable_servers + if not readable_servers: + self._ls_timeout_minutes = None + elif any(s.logical_session_timeout_minutes is None + for s in readable_servers): + self._ls_timeout_minutes = None + else: + self._ls_timeout_minutes = min(s.logical_session_timeout_minutes + for s in readable_servers) + + def check_compatible(self): + """Raise ConfigurationError if any server is incompatible. + + A server is incompatible if its wire protocol version range does not + overlap with PyMongo's. + """ + if self._incompatible_err: + raise ConfigurationError(self._incompatible_err) + + def has_server(self, address): + return address in self._server_descriptions + + def reset_server(self, address): + """A copy of this description, with one server marked Unknown.""" + return updated_topology_description(self, ServerDescription(address)) + + def reset(self): + """A copy of this description, with all servers marked Unknown.""" + if self._topology_type == TOPOLOGY_TYPE.ReplicaSetWithPrimary: + topology_type = TOPOLOGY_TYPE.ReplicaSetNoPrimary + else: + topology_type = self._topology_type + + # The default ServerDescription's type is Unknown. + sds = dict((address, ServerDescription(address)) + for address in self._server_descriptions) + + return TopologyDescription( + topology_type, + sds, + self._replica_set_name, + self._max_set_version, + self._max_election_id, + self._topology_settings) + + def server_descriptions(self): + """Dict of (address, + :class:`~pymongo.server_description.ServerDescription`).""" + return self._server_descriptions.copy() + + @property + def topology_type(self): + """The type of this topology.""" + return self._topology_type + + @property + def topology_type_name(self): + """The topology type as a human readable string. + + .. versionadded:: 3.4 + """ + return TOPOLOGY_TYPE._fields[self._topology_type] + + @property + def replica_set_name(self): + """The replica set name.""" + return self._replica_set_name + + @property + def max_set_version(self): + """Greatest setVersion seen from a primary, or None.""" + return self._max_set_version + + @property + def max_election_id(self): + """Greatest electionId seen from a primary, or None.""" + return self._max_election_id + + @property + def logical_session_timeout_minutes(self): + """Minimum logical session timeout, or None.""" + return self._ls_timeout_minutes + + @property + def known_servers(self): + """List of Servers of types besides Unknown.""" + return [s for s in self._server_descriptions.values() + if s.is_server_type_known] + + @property + def has_known_servers(self): + """Whether there are any Servers of types besides Unknown.""" + return any(s for s in self._server_descriptions.values() + if s.is_server_type_known) + + @property + def readable_servers(self): + """List of readable Servers.""" + return [s for s in self._server_descriptions.values() if s.is_readable] + + @property + def common_wire_version(self): + """Minimum of all servers' max wire versions, or None.""" + servers = self.known_servers + if servers: + return min(s.max_wire_version for s in self.known_servers) + + return None + + @property + def heartbeat_frequency(self): + return self._topology_settings.heartbeat_frequency + + def apply_selector(self, selector, address, custom_selector=None): + + def apply_local_threshold(selection): + if not selection: + return [] + + settings = self._topology_settings + + # Round trip time in seconds. + fastest = min( + s.round_trip_time for s in selection.server_descriptions) + threshold = settings.local_threshold_ms / 1000.0 + return [s for s in selection.server_descriptions + if (s.round_trip_time - fastest) <= threshold] + + if getattr(selector, 'min_wire_version', 0): + common_wv = self.common_wire_version + if common_wv and common_wv < selector.min_wire_version: + raise ConfigurationError( + "%s requires min wire version %d, but topology's min" + " wire version is %d" % (selector, + selector.min_wire_version, + common_wv)) + + if self.topology_type == TOPOLOGY_TYPE.Single: + # Ignore selectors for standalone. + return self.known_servers + elif address: + # Ignore selectors when explicit address is requested. + description = self.server_descriptions().get(address) + return [description] if description else [] + elif self.topology_type == TOPOLOGY_TYPE.Sharded: + # Ignore read preference. + selection = Selection.from_topology_description(self) + else: + selection = selector(Selection.from_topology_description(self)) + + # Apply custom selector followed by localThresholdMS. + if custom_selector is not None and selection: + selection = selection.with_server_descriptions( + custom_selector(selection.server_descriptions)) + return apply_local_threshold(selection) + + def has_readable_server(self, read_preference=ReadPreference.PRIMARY): + """Does this topology have any readable servers available matching the + given read preference? + + :Parameters: + - `read_preference`: an instance of a read preference from + :mod:`~pymongo.read_preferences`. Defaults to + :attr:`~pymongo.read_preferences.ReadPreference.PRIMARY`. + + .. note:: When connected directly to a single server this method + always returns ``True``. + + .. versionadded:: 3.4 + """ + common.validate_read_preference("read_preference", read_preference) + return any(self.apply_selector(read_preference, None)) + + def has_writable_server(self): + """Does this topology have a writable server available? + + .. note:: When connected directly to a single server this method + always returns ``True``. + + .. versionadded:: 3.4 + """ + return self.has_readable_server(ReadPreference.PRIMARY) + + +# If topology type is Unknown and we receive an ismaster response, what should +# the new topology type be? +_SERVER_TYPE_TO_TOPOLOGY_TYPE = { + SERVER_TYPE.Mongos: TOPOLOGY_TYPE.Sharded, + SERVER_TYPE.RSPrimary: TOPOLOGY_TYPE.ReplicaSetWithPrimary, + SERVER_TYPE.RSSecondary: TOPOLOGY_TYPE.ReplicaSetNoPrimary, + SERVER_TYPE.RSArbiter: TOPOLOGY_TYPE.ReplicaSetNoPrimary, + SERVER_TYPE.RSOther: TOPOLOGY_TYPE.ReplicaSetNoPrimary, +} + + +def updated_topology_description(topology_description, server_description): + """Return an updated copy of a TopologyDescription. + + :Parameters: + - `topology_description`: the current TopologyDescription + - `server_description`: a new ServerDescription that resulted from + an ismaster call + + Called after attempting (successfully or not) to call ismaster on the + server at server_description.address. Does not modify topology_description. + """ + address = server_description.address + + # These values will be updated, if necessary, to form the new + # TopologyDescription. + topology_type = topology_description.topology_type + set_name = topology_description.replica_set_name + max_set_version = topology_description.max_set_version + max_election_id = topology_description.max_election_id + server_type = server_description.server_type + + # Don't mutate the original dict of server descriptions; copy it. + sds = topology_description.server_descriptions() + + # Replace this server's description with the new one. + sds[address] = server_description + + if topology_type == TOPOLOGY_TYPE.Single: + # Single type never changes. + return TopologyDescription( + TOPOLOGY_TYPE.Single, + sds, + set_name, + max_set_version, + max_election_id, + topology_description._topology_settings) + + if topology_type == TOPOLOGY_TYPE.Unknown: + if server_type == SERVER_TYPE.Standalone: + sds.pop(address) + + elif server_type not in (SERVER_TYPE.Unknown, SERVER_TYPE.RSGhost): + topology_type = _SERVER_TYPE_TO_TOPOLOGY_TYPE[server_type] + + if topology_type == TOPOLOGY_TYPE.Sharded: + if server_type not in (SERVER_TYPE.Mongos, SERVER_TYPE.Unknown): + sds.pop(address) + + elif topology_type == TOPOLOGY_TYPE.ReplicaSetNoPrimary: + if server_type in (SERVER_TYPE.Standalone, SERVER_TYPE.Mongos): + sds.pop(address) + + elif server_type == SERVER_TYPE.RSPrimary: + (topology_type, + set_name, + max_set_version, + max_election_id) = _update_rs_from_primary(sds, + set_name, + server_description, + max_set_version, + max_election_id) + + elif server_type in ( + SERVER_TYPE.RSSecondary, + SERVER_TYPE.RSArbiter, + SERVER_TYPE.RSOther): + topology_type, set_name = _update_rs_no_primary_from_member( + sds, set_name, server_description) + + elif topology_type == TOPOLOGY_TYPE.ReplicaSetWithPrimary: + if server_type in (SERVER_TYPE.Standalone, SERVER_TYPE.Mongos): + sds.pop(address) + topology_type = _check_has_primary(sds) + + elif server_type == SERVER_TYPE.RSPrimary: + (topology_type, + set_name, + max_set_version, + max_election_id) = _update_rs_from_primary(sds, + set_name, + server_description, + max_set_version, + max_election_id) + + elif server_type in ( + SERVER_TYPE.RSSecondary, + SERVER_TYPE.RSArbiter, + SERVER_TYPE.RSOther): + topology_type = _update_rs_with_primary_from_member( + sds, set_name, server_description) + + else: + # Server type is Unknown or RSGhost: did we just lose the primary? + topology_type = _check_has_primary(sds) + + # Return updated copy. + return TopologyDescription(topology_type, + sds, + set_name, + max_set_version, + max_election_id, + topology_description._topology_settings) + + +def _update_rs_from_primary( + sds, + replica_set_name, + server_description, + max_set_version, + max_election_id): + """Update topology description from a primary's ismaster response. + + Pass in a dict of ServerDescriptions, current replica set name, the + ServerDescription we are processing, and the TopologyDescription's + max_set_version and max_election_id if any. + + Returns (new topology type, new replica_set_name, new max_set_version, + new max_election_id). + """ + if replica_set_name is None: + replica_set_name = server_description.replica_set_name + + elif replica_set_name != server_description.replica_set_name: + # We found a primary but it doesn't have the replica_set_name + # provided by the user. + sds.pop(server_description.address) + return (_check_has_primary(sds), + replica_set_name, + max_set_version, + max_election_id) + + max_election_tuple = max_set_version, max_election_id + if None not in server_description.election_tuple: + if (None not in max_election_tuple and + max_election_tuple > server_description.election_tuple): + + # Stale primary, set to type Unknown. + address = server_description.address + sds[address] = ServerDescription(address) + return (_check_has_primary(sds), + replica_set_name, + max_set_version, + max_election_id) + + max_election_id = server_description.election_id + + if (server_description.set_version is not None and + (max_set_version is None or + server_description.set_version > max_set_version)): + + max_set_version = server_description.set_version + + # We've heard from the primary. Is it the same primary as before? + for server in sds.values(): + if (server.server_type is SERVER_TYPE.RSPrimary + and server.address != server_description.address): + + # Reset old primary's type to Unknown. + sds[server.address] = ServerDescription(server.address) + + # There can be only one prior primary. + break + + # Discover new hosts from this primary's response. + for new_address in server_description.all_hosts: + if new_address not in sds: + sds[new_address] = ServerDescription(new_address) + + # Remove hosts not in the response. + for addr in set(sds) - server_description.all_hosts: + sds.pop(addr) + + # If the host list differs from the seed list, we may not have a primary + # after all. + return (_check_has_primary(sds), + replica_set_name, + max_set_version, + max_election_id) + + +def _update_rs_with_primary_from_member( + sds, + replica_set_name, + server_description): + """RS with known primary. Process a response from a non-primary. + + Pass in a dict of ServerDescriptions, current replica set name, and the + ServerDescription we are processing. + + Returns new topology type. + """ + assert replica_set_name is not None + + if replica_set_name != server_description.replica_set_name: + sds.pop(server_description.address) + elif (server_description.me and + server_description.address != server_description.me): + sds.pop(server_description.address) + + # Had this member been the primary? + return _check_has_primary(sds) + + +def _update_rs_no_primary_from_member( + sds, + replica_set_name, + server_description): + """RS without known primary. Update from a non-primary's response. + + Pass in a dict of ServerDescriptions, current replica set name, and the + ServerDescription we are processing. + + Returns (new topology type, new replica_set_name). + """ + topology_type = TOPOLOGY_TYPE.ReplicaSetNoPrimary + if replica_set_name is None: + replica_set_name = server_description.replica_set_name + + elif replica_set_name != server_description.replica_set_name: + sds.pop(server_description.address) + return topology_type, replica_set_name + + # This isn't the primary's response, so don't remove any servers + # it doesn't report. Only add new servers. + for address in server_description.all_hosts: + if address not in sds: + sds[address] = ServerDescription(address) + + if (server_description.me and + server_description.address != server_description.me): + sds.pop(server_description.address) + + return topology_type, replica_set_name + + +def _check_has_primary(sds): + """Current topology type is ReplicaSetWithPrimary. Is primary still known? + + Pass in a dict of ServerDescriptions. + + Returns new topology type. + """ + for s in sds.values(): + if s.server_type == SERVER_TYPE.RSPrimary: + return TOPOLOGY_TYPE.ReplicaSetWithPrimary + else: + return TOPOLOGY_TYPE.ReplicaSetNoPrimary diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/uri_parser.py b/backend/venv/lib/python3.7/site-packages/pymongo/uri_parser.py new file mode 100644 index 000000000..8ee5fbb11 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/uri_parser.py @@ -0,0 +1,429 @@ +# Copyright 2011-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +"""Tools to parse and validate a MongoDB URI.""" +import re +import warnings + +try: + from dns import resolver + _HAVE_DNSPYTHON = True +except ImportError: + _HAVE_DNSPYTHON = False + +from bson.py3compat import PY3, string_type + +if PY3: + from urllib.parse import unquote_plus +else: + from urllib import unquote_plus + +from pymongo.common import get_validated_options +from pymongo.errors import ConfigurationError, InvalidURI + + +SCHEME = 'mongodb://' +SCHEME_LEN = len(SCHEME) +SRV_SCHEME = 'mongodb+srv://' +SRV_SCHEME_LEN = len(SRV_SCHEME) +DEFAULT_PORT = 27017 + + +def parse_userinfo(userinfo): + """Validates the format of user information in a MongoDB URI. + Reserved characters like ':', '/', '+' and '@' must be escaped + following RFC 3986. + + Returns a 2-tuple containing the unescaped username followed + by the unescaped password. + + :Paramaters: + - `userinfo`: A string of the form : + + .. versionchanged:: 2.2 + Now uses `urllib.unquote_plus` so `+` characters must be escaped. + """ + if '@' in userinfo or userinfo.count(':') > 1: + if PY3: + quote_fn = "urllib.parse.quote_plus" + else: + quote_fn = "urllib.quote_plus" + raise InvalidURI("Username and password must be escaped according to " + "RFC 3986, use %s()." % quote_fn) + user, _, passwd = userinfo.partition(":") + # No password is expected with GSSAPI authentication. + if not user: + raise InvalidURI("The empty string is not valid username.") + return unquote_plus(user), unquote_plus(passwd) + + +def parse_ipv6_literal_host(entity, default_port): + """Validates an IPv6 literal host:port string. + + Returns a 2-tuple of IPv6 literal followed by port where + port is default_port if it wasn't specified in entity. + + :Parameters: + - `entity`: A string that represents an IPv6 literal enclosed + in braces (e.g. '[::1]' or '[::1]:27017'). + - `default_port`: The port number to use when one wasn't + specified in entity. + """ + if entity.find(']') == -1: + raise ValueError("an IPv6 address literal must be " + "enclosed in '[' and ']' according " + "to RFC 2732.") + i = entity.find(']:') + if i == -1: + return entity[1:-1], default_port + return entity[1: i], entity[i + 2:] + + +def parse_host(entity, default_port=DEFAULT_PORT): + """Validates a host string + + Returns a 2-tuple of host followed by port where port is default_port + if it wasn't specified in the string. + + :Parameters: + - `entity`: A host or host:port string where host could be a + hostname or IP address. + - `default_port`: The port number to use when one wasn't + specified in entity. + """ + host = entity + port = default_port + if entity[0] == '[': + host, port = parse_ipv6_literal_host(entity, default_port) + elif entity.endswith(".sock"): + return entity, default_port + elif entity.find(':') != -1: + if entity.count(':') > 1: + raise ValueError("Reserved characters such as ':' must be " + "escaped according RFC 2396. An IPv6 " + "address literal must be enclosed in '[' " + "and ']' according to RFC 2732.") + host, port = host.split(':', 1) + if isinstance(port, string_type): + if not port.isdigit() or int(port) > 65535 or int(port) <= 0: + raise ValueError("Port must be an integer between 0 and 65535: %s" + % (port,)) + port = int(port) + + # Normalize hostname to lowercase, since DNS is case-insensitive: + # http://tools.ietf.org/html/rfc4343 + # This prevents useless rediscovery if "foo.com" is in the seed list but + # "FOO.com" is in the ismaster response. + return host.lower(), port + + +def validate_options(opts, warn=False): + """Validates and normalizes options passed in a MongoDB URI. + + Returns a new dictionary of validated and normalized options. If warn is + False then errors will be thrown for invalid options, otherwise they will + be ignored and a warning will be issued. + + :Parameters: + - `opts`: A dict of MongoDB URI options. + - `warn` (optional): If ``True`` then warnigns will be logged and + invalid options will be ignored. Otherwise invalid options will + cause errors. + """ + return get_validated_options(opts, warn) + + +def _parse_options(opts, delim): + """Helper method for split_options which creates the options dict. + Also handles the creation of a list for the URI tag_sets/ + readpreferencetags portion.""" + options = {} + for opt in opts.split(delim): + key, val = opt.split("=") + if key.lower() == 'readpreferencetags': + options.setdefault('readpreferencetags', []).append(val) + else: + # str(option) to ensure that a unicode URI results in plain 'str' + # option names. 'normalized' is then suitable to be passed as + # kwargs in all Python versions. + if str(key) in options: + warnings.warn("Duplicate URI option %s" % (str(key),)) + options[str(key)] = unquote_plus(val) + + # Special case for deprecated options + if "wtimeout" in options: + if "wtimeoutMS" in options: + options.pop("wtimeout") + warnings.warn("Option wtimeout is deprecated, use 'wtimeoutMS'" + " instead") + + return options + + +def split_options(opts, validate=True, warn=False): + """Takes the options portion of a MongoDB URI, validates each option + and returns the options in a dictionary. + + :Parameters: + - `opt`: A string representing MongoDB URI options. + - `validate`: If ``True`` (the default), validate and normalize all + options. + """ + and_idx = opts.find("&") + semi_idx = opts.find(";") + try: + if and_idx >= 0 and semi_idx >= 0: + raise InvalidURI("Can not mix '&' and ';' for option separators.") + elif and_idx >= 0: + options = _parse_options(opts, "&") + elif semi_idx >= 0: + options = _parse_options(opts, ";") + elif opts.find("=") != -1: + options = _parse_options(opts, None) + else: + raise ValueError + except ValueError: + raise InvalidURI("MongoDB URI options are key=value pairs.") + + if validate: + return validate_options(options, warn) + return options + + +def split_hosts(hosts, default_port=DEFAULT_PORT): + """Takes a string of the form host1[:port],host2[:port]... and + splits it into (host, port) tuples. If [:port] isn't present the + default_port is used. + + Returns a set of 2-tuples containing the host name (or IP) followed by + port number. + + :Parameters: + - `hosts`: A string of the form host1[:port],host2[:port],... + - `default_port`: The port number to use when one wasn't specified + for a host. + """ + nodes = [] + for entity in hosts.split(','): + if not entity: + raise ConfigurationError("Empty host " + "(or extra comma in host list).") + port = default_port + # Unix socket entities don't have ports + if entity.endswith('.sock'): + port = None + nodes.append(parse_host(entity, port)) + return nodes + + +# Prohibited characters in database name. DB names also can't have ".", but for +# backward-compat we allow "db.collection" in URI. +_BAD_DB_CHARS = re.compile('[' + re.escape(r'/ "$') + ']') + + +if PY3: + # dnspython can return bytes or str from various parts + # of its API depending on version. We always want str. + def maybe_decode(text): + if isinstance(text, bytes): + return text.decode() + return text +else: + def maybe_decode(text): + return text + + +_ALLOWED_TXT_OPTS = frozenset( + ['authsource', 'authSource', 'replicaset', 'replicaSet']) + + +def _get_dns_srv_hosts(hostname): + try: + results = resolver.query('_mongodb._tcp.' + hostname, 'SRV') + except Exception as exc: + raise ConfigurationError(str(exc)) + return [(maybe_decode(res.target.to_text(omit_final_dot=True)), res.port) + for res in results] + + +def _get_dns_txt_options(hostname): + try: + results = resolver.query(hostname, 'TXT') + except (resolver.NoAnswer, resolver.NXDOMAIN): + # No TXT records + return None + except Exception as exc: + raise ConfigurationError(str(exc)) + if len(results) > 1: + raise ConfigurationError('Only one TXT record is supported') + return ( + b'&'.join([b''.join(res.strings) for res in results])).decode('utf-8') + + +def parse_uri(uri, default_port=DEFAULT_PORT, validate=True, warn=False): + """Parse and validate a MongoDB URI. + + Returns a dict of the form:: + + { + 'nodelist': , + 'username': or None, + 'password': or None, + 'database': or None, + 'collection': or None, + 'options': + } + + If the URI scheme is "mongodb+srv://" DNS SRV and TXT lookups will be done + to build nodelist and options. + + :Parameters: + - `uri`: The MongoDB URI to parse. + - `default_port`: The port number to use when one wasn't specified + for a host in the URI. + - `validate`: If ``True`` (the default), validate and normalize all + options. + - `warn` (optional): When validating, if ``True`` then will warn + the user then ignore any invalid options or values. If ``False``, + validation will error when options are unsupported or values are + invalid. + + .. versionchanged:: 3.6 + Added support for mongodb+srv:// URIs + + .. versionchanged:: 3.5 + Return the original value of the ``readPreference`` MongoDB URI option + instead of the validated read preference mode. + + .. versionchanged:: 3.1 + ``warn`` added so invalid options can be ignored. + """ + if uri.startswith(SCHEME): + is_srv = False + scheme_free = uri[SCHEME_LEN:] + elif uri.startswith(SRV_SCHEME): + if not _HAVE_DNSPYTHON: + raise ConfigurationError('The "dnspython" module must be ' + 'installed to use mongodb+srv:// URIs') + is_srv = True + scheme_free = uri[SRV_SCHEME_LEN:] + else: + raise InvalidURI("Invalid URI scheme: URI must " + "begin with '%s' or '%s'" % (SCHEME, SRV_SCHEME)) + + if not scheme_free: + raise InvalidURI("Must provide at least one hostname or IP.") + + user = None + passwd = None + dbase = None + collection = None + options = {} + + host_part, _, path_part = scheme_free.partition('/') + if not host_part: + host_part = path_part + path_part = "" + + if not path_part and '?' in host_part: + raise InvalidURI("A '/' is required between " + "the host list and any options.") + + if '@' in host_part: + userinfo, _, hosts = host_part.rpartition('@') + user, passwd = parse_userinfo(userinfo) + else: + hosts = host_part + + if '/' in hosts: + raise InvalidURI("Any '/' in a unix domain socket must be" + " percent-encoded: %s" % host_part) + + hosts = unquote_plus(hosts) + + if is_srv: + nodes = split_hosts(hosts, default_port=None) + if len(nodes) != 1: + raise InvalidURI( + "%s URIs must include one, " + "and only one, hostname" % (SRV_SCHEME,)) + fqdn, port = nodes[0] + if port is not None: + raise InvalidURI( + "%s URIs must not include a port number" % (SRV_SCHEME,)) + nodes = _get_dns_srv_hosts(fqdn) + + try: + plist = fqdn.split(".")[1:] + except Exception: + raise ConfigurationError("Invalid URI host") + slen = len(plist) + if slen < 2: + raise ConfigurationError("Invalid URI host") + for node in nodes: + try: + nlist = node[0].split(".")[1:][-slen:] + except Exception: + raise ConfigurationError("Invalid SRV host") + if plist != nlist: + raise ConfigurationError("Invalid SRV host") + + dns_options = _get_dns_txt_options(fqdn) + if dns_options: + options = split_options(dns_options, validate, warn) + if set(options) - _ALLOWED_TXT_OPTS: + raise ConfigurationError( + "Only authSource and replicaSet are supported from DNS") + options["ssl"] = True if validate else 'true' + else: + nodes = split_hosts(hosts, default_port=default_port) + + if path_part: + if path_part[0] == '?': + opts = unquote_plus(path_part[1:]) + else: + dbase, _, opts = map(unquote_plus, path_part.partition('?')) + if '.' in dbase: + dbase, collection = dbase.split('.', 1) + + if _BAD_DB_CHARS.search(dbase): + raise InvalidURI('Bad database name "%s"' % dbase) + + if opts: + options.update(split_options(opts, validate, warn)) + + if dbase is not None: + dbase = unquote_plus(dbase) + if collection is not None: + collection = unquote_plus(collection) + + return { + 'nodelist': nodes, + 'username': user, + 'password': passwd, + 'database': dbase, + 'collection': collection, + 'options': options + } + + +if __name__ == '__main__': + import pprint + import sys + try: + pprint.pprint(parse_uri(sys.argv[1])) + except InvalidURI as exc: + print(exc) + sys.exit(0) diff --git a/backend/venv/lib/python3.7/site-packages/pymongo/write_concern.py b/backend/venv/lib/python3.7/site-packages/pymongo/write_concern.py new file mode 100644 index 000000000..14ad63de8 --- /dev/null +++ b/backend/venv/lib/python3.7/site-packages/pymongo/write_concern.py @@ -0,0 +1,126 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for working with write concerns.""" + +from bson.py3compat import integer_types, string_type +from pymongo.errors import ConfigurationError + + +class WriteConcern(object): + """WriteConcern + + :Parameters: + - `w`: (integer or string) Used with replication, write operations + will block until they have been replicated to the specified number + or tagged set of servers. `w=` always includes the replica + set primary (e.g. w=3 means write to the primary and wait until + replicated to **two** secondaries). **w=0 disables acknowledgement + of write operations and can not be used with other write concern + options.** + - `wtimeout`: (integer) Used in conjunction with `w`. Specify a value + in milliseconds to control how long to wait for write propagation + to complete. If replication does not complete in the given + timeframe, a timeout exception is raised. + - `j`: If ``True`` block until write operations have been committed + to the journal. Cannot be used in combination with `fsync`. Prior + to MongoDB 2.6 this option was ignored if the server was running + without journaling. Starting with MongoDB 2.6 write operations will + fail with an exception if this option is used when the server is + running without journaling. + - `fsync`: If ``True`` and the server is running without journaling, + blocks until the server has synced all data files to disk. If the + server is running with journaling, this acts the same as the `j` + option, blocking until write operations have been committed to the + journal. Cannot be used in combination with `j`. + """ + + __slots__ = ("__document", "__acknowledged", "__server_default") + + def __init__(self, w=None, wtimeout=None, j=None, fsync=None): + self.__document = {} + self.__acknowledged = True + + if wtimeout is not None: + if not isinstance(wtimeout, integer_types): + raise TypeError("wtimeout must be an integer") + if wtimeout < 0: + raise ValueError("wtimeout cannot be less than 0") + self.__document["wtimeout"] = wtimeout + + if j is not None: + if not isinstance(j, bool): + raise TypeError("j must be True or False") + self.__document["j"] = j + + if fsync is not None: + if not isinstance(fsync, bool): + raise TypeError("fsync must be True or False") + if j and fsync: + raise ConfigurationError("Can't set both j " + "and fsync at the same time") + self.__document["fsync"] = fsync + + if w == 0 and j is True: + raise ConfigurationError("Cannot set w to 0 and j to True") + + if w is not None: + if isinstance(w, integer_types): + if w < 0: + raise ValueError("w cannot be less than 0") + self.__acknowledged = w > 0 + elif not isinstance(w, string_type): + raise TypeError("w must be an integer or string") + self.__document["w"] = w + + self.__server_default = not self.__document + + @property + def is_server_default(self): + """Does this WriteConcern match the server default.""" + return self.__server_default + + @property + def document(self): + """The document representation of this write concern. + + .. note:: + :class:`WriteConcern` is immutable. Mutating the value of + :attr:`document` does not mutate this :class:`WriteConcern`. + """ + return self.__document.copy() + + @property + def acknowledged(self): + """If ``True`` write operations will wait for acknowledgement before + returning. + """ + return self.__acknowledged + + def __repr__(self): + return ("WriteConcern(%s)" % ( + ", ".join("%s=%s" % kvt for kvt in self.__document.items()),)) + + def __eq__(self, other): + if isinstance(other, WriteConcern): + return self.__document == other.document + return NotImplemented + + def __ne__(self, other): + if isinstance(other, WriteConcern): + return self.__document != other.document + return NotImplemented + + +DEFAULT_WRITE_CONCERN = WriteConcern() diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__init__.py b/backend/venv/lib/python3.7/site-packages/werkzeug/__init__.py index aa13bb576..e460e758a 100644 --- a/backend/venv/lib/python3.7/site-packages/werkzeug/__init__.py +++ b/backend/venv/lib/python3.7/site-packages/werkzeug/__init__.py @@ -11,16 +11,13 @@ library. - :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. + :copyright: 2007 Pallets + :license: BSD-3-Clause """ -from types import ModuleType import sys +from types import ModuleType -from werkzeug._compat import iteritems - -__version__ = '0.12.2' - +__version__ = "0.15.4" # This import magic raises concerns quite often which is why the implementation # and motivation is explained here in detail now. @@ -35,80 +32,153 @@ __version__ = '0.12.2' # werkzeug package when imported from within. Attribute access to the werkzeug # module will then lazily import from the modules that implement the objects. - # import mapping to objects in other modules all_by_module = { - 'werkzeug.debug': ['DebuggedApplication'], - 'werkzeug.local': ['Local', 'LocalManager', 'LocalProxy', 'LocalStack', - 'release_local'], - 'werkzeug.serving': ['run_simple'], - 'werkzeug.test': ['Client', 'EnvironBuilder', 'create_environ', - 'run_wsgi_app'], - 'werkzeug.testapp': ['test_app'], - 'werkzeug.exceptions': ['abort', 'Aborter'], - 'werkzeug.urls': ['url_decode', 'url_encode', 'url_quote', - 'url_quote_plus', 'url_unquote', 'url_unquote_plus', - 'url_fix', 'Href', 'iri_to_uri', 'uri_to_iri'], - 'werkzeug.formparser': ['parse_form_data'], - 'werkzeug.utils': ['escape', 'environ_property', 'append_slash_redirect', - 'redirect', 'cached_property', 'import_string', - 'dump_cookie', 'parse_cookie', 'unescape', - 'format_string', 'find_modules', 'header_property', - 'html', 'xhtml', 'HTMLBuilder', 'validate_arguments', - 'ArgumentValidationError', 'bind_arguments', - 'secure_filename'], - 'werkzeug.wsgi': ['get_current_url', 'get_host', 'pop_path_info', - 'peek_path_info', 'SharedDataMiddleware', - 'DispatcherMiddleware', 'ClosingIterator', 'FileWrapper', - 'make_line_iter', 'LimitedStream', 'responder', - 'wrap_file', 'extract_path_info'], - 'werkzeug.datastructures': ['MultiDict', 'CombinedMultiDict', 'Headers', - 'EnvironHeaders', 'ImmutableList', - 'ImmutableDict', 'ImmutableMultiDict', - 'TypeConversionDict', - 'ImmutableTypeConversionDict', 'Accept', - 'MIMEAccept', 'CharsetAccept', - 'LanguageAccept', 'RequestCacheControl', - 'ResponseCacheControl', 'ETags', 'HeaderSet', - 'WWWAuthenticate', 'Authorization', - 'FileMultiDict', 'CallbackDict', 'FileStorage', - 'OrderedMultiDict', 'ImmutableOrderedMultiDict' - ], - 'werkzeug.useragents': ['UserAgent'], - 'werkzeug.http': ['parse_etags', 'parse_date', 'http_date', 'cookie_date', - 'parse_cache_control_header', 'is_resource_modified', - 'parse_accept_header', 'parse_set_header', 'quote_etag', - 'unquote_etag', 'generate_etag', 'dump_header', - 'parse_list_header', 'parse_dict_header', - 'parse_authorization_header', - 'parse_www_authenticate_header', 'remove_entity_headers', - 'is_entity_header', 'remove_hop_by_hop_headers', - 'parse_options_header', 'dump_options_header', - 'is_hop_by_hop_header', 'unquote_header_value', - 'quote_header_value', 'HTTP_STATUS_CODES'], - 'werkzeug.wrappers': ['BaseResponse', 'BaseRequest', 'Request', 'Response', - 'AcceptMixin', 'ETagRequestMixin', - 'ETagResponseMixin', 'ResponseStreamMixin', - 'CommonResponseDescriptorsMixin', 'UserAgentMixin', - 'AuthorizationMixin', 'WWWAuthenticateMixin', - 'CommonRequestDescriptorsMixin'], - 'werkzeug.security': ['generate_password_hash', 'check_password_hash'], + "werkzeug.debug": ["DebuggedApplication"], + "werkzeug.local": [ + "Local", + "LocalManager", + "LocalProxy", + "LocalStack", + "release_local", + ], + "werkzeug.serving": ["run_simple"], + "werkzeug.test": ["Client", "EnvironBuilder", "create_environ", "run_wsgi_app"], + "werkzeug.testapp": ["test_app"], + "werkzeug.exceptions": ["abort", "Aborter"], + "werkzeug.urls": [ + "url_decode", + "url_encode", + "url_quote", + "url_quote_plus", + "url_unquote", + "url_unquote_plus", + "url_fix", + "Href", + "iri_to_uri", + "uri_to_iri", + ], + "werkzeug.formparser": ["parse_form_data"], + "werkzeug.utils": [ + "escape", + "environ_property", + "append_slash_redirect", + "redirect", + "cached_property", + "import_string", + "dump_cookie", + "parse_cookie", + "unescape", + "format_string", + "find_modules", + "header_property", + "html", + "xhtml", + "HTMLBuilder", + "validate_arguments", + "ArgumentValidationError", + "bind_arguments", + "secure_filename", + ], + "werkzeug.wsgi": [ + "get_current_url", + "get_host", + "pop_path_info", + "peek_path_info", + "ClosingIterator", + "FileWrapper", + "make_line_iter", + "LimitedStream", + "responder", + "wrap_file", + "extract_path_info", + ], + "werkzeug.datastructures": [ + "MultiDict", + "CombinedMultiDict", + "Headers", + "EnvironHeaders", + "ImmutableList", + "ImmutableDict", + "ImmutableMultiDict", + "TypeConversionDict", + "ImmutableTypeConversionDict", + "Accept", + "MIMEAccept", + "CharsetAccept", + "LanguageAccept", + "RequestCacheControl", + "ResponseCacheControl", + "ETags", + "HeaderSet", + "WWWAuthenticate", + "Authorization", + "FileMultiDict", + "CallbackDict", + "FileStorage", + "OrderedMultiDict", + "ImmutableOrderedMultiDict", + ], + "werkzeug.useragents": ["UserAgent"], + "werkzeug.http": [ + "parse_etags", + "parse_date", + "http_date", + "cookie_date", + "parse_cache_control_header", + "is_resource_modified", + "parse_accept_header", + "parse_set_header", + "quote_etag", + "unquote_etag", + "generate_etag", + "dump_header", + "parse_list_header", + "parse_dict_header", + "parse_authorization_header", + "parse_www_authenticate_header", + "remove_entity_headers", + "is_entity_header", + "remove_hop_by_hop_headers", + "parse_options_header", + "dump_options_header", + "is_hop_by_hop_header", + "unquote_header_value", + "quote_header_value", + "HTTP_STATUS_CODES", + ], + "werkzeug.wrappers": [ + "BaseResponse", + "BaseRequest", + "Request", + "Response", + "AcceptMixin", + "ETagRequestMixin", + "ETagResponseMixin", + "ResponseStreamMixin", + "CommonResponseDescriptorsMixin", + "UserAgentMixin", + "AuthorizationMixin", + "WWWAuthenticateMixin", + "CommonRequestDescriptorsMixin", + ], + "werkzeug.middleware.dispatcher": ["DispatcherMiddleware"], + "werkzeug.middleware.shared_data": ["SharedDataMiddleware"], + "werkzeug.security": ["generate_password_hash", "check_password_hash"], # the undocumented easteregg ;-) - 'werkzeug._internal': ['_easteregg'] + "werkzeug._internal": ["_easteregg"], } # modules that should be imported when accessed as attributes of werkzeug -attribute_modules = frozenset(['exceptions', 'routing', 'script']) - +attribute_modules = frozenset(["exceptions", "routing"]) object_origins = {} -for module, items in iteritems(all_by_module): +for module, items in all_by_module.items(): for item in items: object_origins[item] = module class module(ModuleType): - """Automatically import objects from the modules.""" def __getattr__(self, name): @@ -118,34 +188,46 @@ class module(ModuleType): setattr(self, extra_name, getattr(module, extra_name)) return getattr(module, name) elif name in attribute_modules: - __import__('werkzeug.' + name) + __import__("werkzeug." + name) return ModuleType.__getattribute__(self, name) def __dir__(self): """Just show what we want to show.""" result = list(new_module.__all__) - result.extend(('__file__', '__path__', '__doc__', '__all__', - '__docformat__', '__name__', '__path__', - '__package__', '__version__')) + result.extend( + ( + "__file__", + "__doc__", + "__all__", + "__docformat__", + "__name__", + "__path__", + "__package__", + "__version__", + ) + ) return result + # keep a reference to this module so that it's not garbage collected -old_module = sys.modules['werkzeug'] +old_module = sys.modules["werkzeug"] # setup the new module and patch it into the dict of loaded modules -new_module = sys.modules['werkzeug'] = module('werkzeug') -new_module.__dict__.update({ - '__file__': __file__, - '__package__': 'werkzeug', - '__path__': __path__, - '__doc__': __doc__, - '__version__': __version__, - '__all__': tuple(object_origins) + tuple(attribute_modules), - '__docformat__': 'restructuredtext en' -}) +new_module = sys.modules["werkzeug"] = module("werkzeug") +new_module.__dict__.update( + { + "__file__": __file__, + "__package__": "werkzeug", + "__path__": __path__, + "__doc__": __doc__, + "__version__": __version__, + "__all__": tuple(object_origins) + tuple(attribute_modules), + "__docformat__": "restructuredtext en", + } +) # Due to bootstrapping issues we need to import exceptions here. # Don't ask :-( -__import__('werkzeug.exceptions') +__import__("werkzeug.exceptions") diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/__init__.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/__init__.cpython-37.pyc index 8db997a53c28666929ec432ed3aaa056d57aa966..d01da224a99da5706a61de530da708d243722876 100644 GIT binary patch delta 1364 zcma)6&u<$=6rNeH?e*H;IF9qfN!z$-($)zvY1$G9X;lSFm5^E?r8Lq)wOZwyZnmr) zXVxZ(`C+IN2%Lyk+;V7-oO-a3DlV1EKY^_N16;Uqs}M8mHW4UD%xd4f_q{joz3xc2kCt41tkQ;B%n|{YErRBJf#vbBa}cjRYV<%7~;^7fbK+* z6m|+}80d3UWQ5g^tfOE6ISgV5!x%vxqZq>}N5^U5KZ9`;a269dhe=G~JTBlOikQX> z4xS_m&Hyf99+zeY4orq!i)!fk20(udo0TcQ`rF1+EmDRn+_9vgKrPxk0~WAhyAwLp{$VNclLfK zr>xQP{M_6^;RDSp1nN#o@Y0FrvEOl^LCMT^;;Wgc!S2~8euXLWrepP33W!JCS^|f`zZ@GZSwrtMK+{(JxP`2y#F&gY97il(l zmV9~R%ogLHG+DzbRgMmoaFnSzev;o0b6OBdgI`ihoxf7w(_Bt;DaN&3=eAX~E9@u2 z$Ad%TeT^;HZnfsl2Y(nd^l32K_b{P#HOsp1dMtR-H))KBlM)c!uUl5|qVLVtq=aH3 z8LOhG@YA8mncwj0d|VhkQIK%lvfy&ddM*rK6M`r@Rh5|DG%De|O0NQL7h^murCV0d zV_DslWo{G2$=&k{06_^ecqONClNVv^_2;`L)fXrhv)^IY&WGc`UT-4~=d zFGZN_qSPcCJ{7#oDk>GDq_HF9wWp;B-p7Hj;gfbX^8eA3B74}ss=2Q^{O<$Ob8_j>BbZ5E Xx+)mctYDdoWD2^P{RdZr>hZq;pq(pA delta 1270 zcmZuvO>Y}T7@pZ(Z|wC)96NF1(2q@;l(rQv<;jR`O`2lg=w6@3}$f_hd*RZ zEZSKt;bmOFD|i*JVHp>(f>o^HbzHKOhfmKXu!U>*(4N4yO#_Y2cThte4Lgrp;PBw1 ziO;Zy7WUD`=eUh8>?FQKM@;jT=zHf;+8Sa}+(j4nY_&_Rm_sb1Rb&x1)TQg>`t-+& z6}J+t97_lKIr2EevN&L)j4+*zF{4Wb#=3Nz-~qmQH1vd0!g6@n6ztWje%W!o zqG5_>#dRvI=CKv?^5*)W;JtU=yt1)*eFi2ob>m{^VW zb+5vmhTrQa9@5dA=wunJ<@0i>R<5$22%idCsduBz>h5BB(OC$7OT9#S@K5S)Hr9`p zN;{6rN+oHDZo9#q;hET^(BQKIg5L1??H7g7*M%ahp`h^7q05=qbbUT0nITNe@KC7) zhnGsXh2!Z0h+#N?r zXQhwHrAoajPJ^pbD3r`gR&0mY+w#(Vev*(3;o~wd2kb|}TMQOPPITs_FOCyV#TN&w zHybF6YYwNCgNnw@hRgVbEPl?8`_Z+*aa~FahWEVyjV5SRNhn%Yqfx4fr5~X#EF<)# zrHiclEIkMeh)PHwl!>@9kdX|HKFi7MKwK&aPcgKVs?m&SFsi1<4J{)W%JE83L;nv^ C9WEvS diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/_compat.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/_compat.cpython-37.pyc index 421bbf3d7acba855b4df3de7dacf81981164e917..2926b7a906a9fd7bd7ed57f7196f3157aefcabdc 100644 GIT binary patch delta 1102 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 delta 3789 zcmZu!X>c4z6`r1JXIHz@>aZ?rrL`r?9@|+<%55AUiIezH}@SAM_`AQa)fURfqe zW~;w`{a*KbeY}4C_HU&Ryv+}1GARYV@BXFdZ0miB@>gu!{tO`YLJ6j{;P#4tw%Gq8gM>|-q6Njx^BCL-!v)lsNrCfOLw8F?@KkWov z1Cr_jDo?we4UA0n(;mQ$wD)<{8GKx!>uKLLh4!(b1YHG`!!$?xuPM%EIzaPK zZ=oCLMyPE%NQa=_3d1(hVHh?nhi#*q=@#H`r#9UR^<8uu-41nu-bD*g@1Q&A2-G8V zl#W3?O7Eq2(|fKFXN;;R6#Krp_fvWJE-^0tOJ?g^igtc%Wb~f=B;$h?*JQ6qgRbU0ON1WnbwZs}dG9X|j7>oTw|7Jo_ZV{M; zod|&x1#N<4MP0j|DFE}yGndZL4TGD8MNS_c@_{4QxMk1LUDIrK_miJ_=Wqvym8Jxy%!BmFtE8;0?2aojj$)hg#ly-|Neyl&X!Q{vag!ObYO3Gb1e zR-qTDIOV#W(OQwY?ryQ$e0+30%5$ro8j7)r-FX+lgHS>pz)~%ZcZqir9pV?}oArKR z!G>zVP7z1v2ofhvB(91&x7iIRAK=rBn`hT;Wp4k6*=&kP&6nYB@km)r0BtQ10v#5UOUe}kc&T{< zIZ;!j%qt7(JXuUHDd)(%`l>pwy#NwQz+d&b;qjo9heHgcf8uHKs#it_Za8W=D8ca- zCODp|V@bXN9&BgCcvG$`)_D*_v31_7(lX;=Y?RqWRu>uTKVzGG7@LU-3q!92?&h}BOi1@~IWq~B_)YU;=Kau-KOok(T9U-?1xdDJPDWJQO*O-_o8SIij1f> zQ)YHD*3Z?dMLr>(&kj8CIEoJ-9F%>)xL1*Dvvqz5`M4%Nj_?G6oMYCNlXOGBRP~}7 zXAX$MG4ZeLhE7XW!L^!6R!sp~YFpkS{Os~rYlgI?q1!E7>MdM{o#cb!SX)HTPm%mo^FTW~CdyZUK9%v`TY%D+wcZ@tHFWl;acnGR_ zgiu2x8V!nTPtI~(zv@S>y9_qEOhdTKc1gT@+sv`A{qO}?z6j4@Yu&wUz6>+hvb}vShhW$?rUL0f9AE1O7 zE)=2?lg#yttL^K=Xm_WKM|=PTJdbb@LC)(kQu7FPgsTWL?Ap(!&zWld<053DSp*G4PpL)p5dlrz~IQ`rto_X@4aJ3dT_=q z2Enw?+zYiJVt>NX*h7Kk=uk`*To9U zJH=0;{3(P-#LMe#7cU+!B0LQM4*z+mV&cc7%0G+jXRw_J+=>?!&&P=iD+HN}$@JR8 zYPmhaeMJoR?Y-wEQ1P+-K${^t6eU-PjJ)KEdUL~w|A%oXQILL7i-{b6EIg?3_MmV8^sy_qM*6LA+!gP5?8{%gdnh=Ay>e^f{f20 ztl99k+j<(2!>`Pg+r=Mq-Bt?1WSa2T#D8)x_GM5NDa5)@tKQ%7kPEgYBO6bM8~yfv zjC8R+6+p6}$0iLbkT%2^GNX`VEQ@ud*BVd56Icu=b5ZTE=o}azZ;Sf|-mRlO{szM9 zIM{@E2AL248Z=hzqV($s-$1y5fV1FlBD@6vk@A~Je+xkd)NdoTW&_f2(^lv3AS?|W zPMpiHC+~=-^3zSyvv9%ujpcvl$H*zX01az%KtU=YZLCq+4*oLDZf|X4JC366HW;Hw z7siJuD=C%mo8s!m-;#_tHn?pc&fnHzqcnSwagM={e+OZW32Sn$@u82c+kKc=q0$GS!7>UW{HRJ!C+J#S2F{sS!E};fYH%tX$28>>=4BbkSJ>qoX?nXM* zE3OxE5dBK~Q7eWfb zAZeh_pk4WIg*0oOc&XdPS4W5HczAItoGJTJ*{`w?wEq+pe;?sJ1l$X-FS%%<@%*LS zRps8QYSq%y16j4Xm;W3G!MyL5@edvY7Mqw~R7?Cgh~mU0NQyZ-SLe7FTtmPZ7AGy7=?hruxgM z!-Tw(6(gCV@;($|M#*;~oJF{RfZ?1!i|`V{YY5W2-$Cjo0&dy=IgWHed5qSsNFt QJ7Tq3LsmQdx~$g!0m_7cW&i*H diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/_reloader.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/_reloader.cpython-37.pyc index 590c8e3a181bd059fb270d1862ea80e5f9ac370a..ec8f684b8f38eb455be51868b738fba851c26d1a 100644 GIT binary patch literal 9800 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 delta 4043 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 delta 29567 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 delta 6361 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 diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/filesystem.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/filesystem.cpython-37.pyc index aa3038f7a6f0ba7261dc80a14529fd3a2878784d..99ecf99b2dee9cce534bd559620e5663c9cde5cf 100644 GIT binary patch delta 159 zcmcaE*eb~D#LLUY00h#uQn98Jd21MFPwXmIH!?6VR|rVV$w@6K=28FxtDMZ_)V$(U zD+Q-u7hPjr=bXgS;?&7mjA@LwH(z5+X5>gUPt43K$TXVl$ZW^Ry19XwiIGuhax+Uh y3kwqi!{*N{D$J5vK#Ph+K-TeaFbXiTKp`8`WFwAg{EQqR$RftV!7Ied!VCbIY9X}% delta 259 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 P+N(gcfqx`jk=*|S!;7iI diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/http.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/http.cpython-37.pyc index 5d33533e1bcde55c4b3f56b9011dd98752983027..1f497b39027fdbbf5d637578a57f98c9fe5e80bf 100644 GIT binary patch delta 12191 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 delta 4521 zcmai1Yit}>72dl$-W{*K-t{AK){dP`;@8@a6MNT*?bwbVaU7>zZbI;>D=bn4+xp&^V$6mS5d}BVJ*MWa;|0#I><9oi~jZbi(I${o$sZO26 z82)$Z&ZH~mV#JelDOnOL5$`3sJL!pel5$LDj->!f-Fj)#8!KauGmg{|$)S~B_VU}z zK|dk)=)PpxsywtRU(UON6IXif zT(0_X>=WZE99O7*9Q(z%T9c6zP%DvBNt|0OR--ykIl@&}KlX<3Z$aaiuWGsZERZTRixRP9#IqFLEk_JSoFBQ&yBjDv?!` z{V?X9?j$R-B$3sUMws!cg08SC7+{o&G63(mlg*@JmSlK(amfZ66Rs=D5KMcf+Xb{M zlg@BI+SsFW8AYS;foEYY{z*C1Dtt((cv9QwqD)cdaY3?KjOQ@CCV#n>u(_CfJ$Wg~ z&!GW2OY3W_Q|Fbaa173sUK`BkiY&>9Q!>NORZC)64Ndf_YI@%54Agi}pROjYtwe%E zXcoMY2>IYUi4-vkx#T_+MDQ^JNMt5CWl464$1Tg;xALy{XRbCF z#na8L_$J(MqDhf0LXCfVyU^TZCE%vNmc0NE{O9%v4Hu^X^OGUFwdCv|ZgFB|5DL_? z>(CeY#kOMUh^p~m{OQZN%rf{Z`+IBGVeDDMSc{nSMH+A6iy=Ti9A=`A*{z<7}msl&)mA>!=-id$r+dKtQ@U!v8TxJ#1n?}$$#(VI7jSS(3#DL?H)wz51j5g=j?CnBJ+Bsx3}%j+@wa1Mzl*djq~x({y7O z0q-W#gv9hm7hOTw9`m8OD4Y=u<)3taim-#*9w*P^-;ABI`_9CLF#xkrMY~Ar}BAxm(JZKo6 zB1fxb#7FR*j}j4FXPzM-gtb2zzpN?AgsSS=LY!-#qH4ZQr3&ZDj>bo5kG*kdX9yHzXPh@w1- zM!3%TmH-dig1bd-#9B0<%x<-tRy^T2V=^S#+d9OC6L{2Vc?aRy?iI5dK5MULo$%ZC zuQZcdzBSwG@=28U!E(p#UZFWb>g2?fD0OQU(@pGRPReL_1j!*7?_3f-HVFlnfKFgV zIRby|ytYU9cp42iQO7ePmrBf~RZUTj!<{{gO=Pi&GVS~XuI>!9c3o?x^sI$zmrpua zX5sy=^BtnhT$i;azOfUd3LR61mZh^?QO?83y=O#O@@G-DAp@%?T3+>eR1L!8y;DJJ znoN06k0&pv@nIFF_RV&SMW^1vW8}?cXkXk2TP`9n=$51?7vZCQpYOC<*%;u-x@wSs zSa)jUp?wa0>2Pms7kn74fvTQ5JB1{S9_oc$Pry!?f)@|8!!LV+W$We#cYC5xAF)Nt z2m>QhZ!s8&MBz7)It%X`$ZLt(f*1nidejQ?RJ0NN_6m1Up%*+u0gKD`2OAMJ zY=`(N8bskILtE^GuL(5&uodmm&=AZIS6Mu?4-dh6!_{^m_XJ48AuGtI!^3c81F!E8 zg^61ROZ|6;{B~ep#Y}I3ijfgJ;n&a;rH>Hy*eSe@yJ*%<@b%RSFNZUuwk7ePpzz~S z%PPaQ(Fl}|*&2PbV02;3YP5H(6&phX{Cdo`qkOwy*feg7{6<0H+PD?@*mxfK@wjb; zy(v-@J{+;j!gp7b>x27;4-DFLq3_|{+Q5c^o$5sRm^gxqm)!jIq?!6jHIz)W2E=3@^1(!cH5a{!H*t8=x5U5jG43LfnMqd7f)pO4qWS?pWLd(WARAiVp&D z+o4Y}PFHG9Hz-c7*Y)8-On6cz(`ruFhKXg+B}Bq2ZaMw}B~BJ#6?l0gy3Hlm23i6$ IfthmlUvWxmc>n+a diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/posixemulation.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/posixemulation.cpython-37.pyc index 028da9640a30355ccdf86aee7e2b6ada47dad4a0..c5b2988b4ab6b95819121fda39e074d3da59bc58 100644 GIT binary patch delta 860 zcmah{&ui2`6wXYtNj6RPciY|Ww!2u@vV~%)&_c0$Xe|`I2o=QYZ><{xX(O+#Y~2M+&^Gzglz=tlDwJDzG0eloAo0xc>*GjbRnHc9im3EtN_C4Y2U zMSFN@ZFO~{_0SJPmL^V1t<5kPuqa`ht?m7LD|c3QLjN#f;+wvC+Q^hqP3j3BNS!9B z%y5?SV~P38j~XwDu5sUH8-5UtgEjHqXlJcHXOTZ*OQL61U5s+rKn`*duZY*?8M4Hu zOj$g$mQ6S{J|*5+)dwz6Pl0s%J7Ju#JwKu$>tuKiDtV~j)aM1riU6g5PzGiz=&C1K z7Qd}>1EarTB2#_eMg|5C2IjUnGj81mc?Vy1zuvE+>IC{>1sW1xjm7_Jz(enfrf&a0 z74gg7+LhMGKT3jEOqz#L7(@p$yL&WX<1~n)j&bQiA|~}Xq}SavRM+R?MYi1r)@ezr z->31Q-{%V|=XHP8UYYm|WTUa)u|EGQ zzxZ;b`-em!sW+0P5w-W#yB0wNJ83ITV%9JjvNAN?9%*OWIV(>zq64=VDYQqeV&5xS z0&JVgRc6se)l-^XV7+vHT? zqh#VJ7O-X%BSFYy!C*4~9lxh^+?{!QzBR{hCaQU5(XriaVlNwl5D1f)%IA`ou7beC zEC~?>;OqwQQUEZRNnt(B@kuO)X8)0+1fwDAMwsoN(?!V!qn_>3%iK+rGzBRrj#Q)} zR^Ug8Z%E_Mq&8Wx=s>5B+_5r{&NsW_y9%3@NFz68sq&Xb~HOORE4-$v~aG z4#5;7WzqFX$Dzy6zTk!c=uCfO4QVl$jyPj1ERM#+5i1#01YyUqmQN!Yt63TS1%f-u AdH?_b diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/routing.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/routing.cpython-37.pyc index feb4e25dd85da62c11ea0cb01a58a839a947e6a1..b1c2c07fbb37aa657d6f0640cf88776f0ac1f6e3 100644 GIT binary patch delta 19027 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 delta 14371 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 diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/script.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/script.cpython-37.pyc deleted file mode 100644 index 439e67b4cc7b4f47f934cf70db4b6822eb208136..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10438 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 diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/security.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/security.cpython-37.pyc index bacd6323cea6b64cef7b9d5a0402ccb6e5a66ed1..e32f46d672f4ce51b10210b87dd32613f726fec3 100644 GIT binary patch delta 2334 zcmZ8iO>7%Q6yDiguh)*facn2eUlS)Jtt^^Sh5ynvqAjH$2&JJ27QuG2Gl|`<*VfF0 zCecO~xDs5DfR#W(LP{hKAcO?u#DN(iriGp^D>7*Eh4dgQj|PSRmI0=z~? z=@{@SI!=!QpQguXjZVSJd+0R12Q)KuhRyJE=3!_y zH=7;c#+8@nf#ta$tujw;bQp|slC8^wfi2tKvb7cuJ03mu55@qyiP<}ID%laI0sU8VEa`JNS;mVl^yXZa>wnfDrO zeHQIZAX=$J_&D$&gClsd`|kzd;%OkUDQrr%OsU0y!pbnyDO84_zFJ z#5AwK+DG9PWguF`B&Kc?8(xbzx=DCt_pI|Xsi=HYG$SGXC}7-%m3%O%m1+wXiWo3u zv^lM36!8^V=3}TK>xyg~a;OTzUtyk*+zVPAH+iFUoSfMGtdu3=PHN*FGT;NCj*}k- z&MYAwkSA8iqE*>Av`*QpEb@dO%5_*K@VhiT3E>Zd6c{*$2g8eVDW-pG$t&geyK#np z9TTxFEn4i6H;K#xN&soBQRf|VD;u4Q3I7GbmZYci?_;tJ#kYgN_oawx0JR<4+l3ts zR+nUP+X1Zg9eqy|XJcyx%c_6#yj%H#n8~^7rR2lv!8a(==c^)3AUD?(-d={Oq94dLKlJu(QcMll{BY!*YMNzU zbN_5za46=JZw9BjkWK(W8E77U1(MgH$ex7xJAh#6Vg4XM;t?QHdmD<24@rnJR?OSl zjx7>NdGCDKDAG#$6oj<1Xk_!~T`@j+CTv5UigPEix>nYr0PHX0 zUM8Oerp(3*2!(n1*~c#^+w-ufJ+E#m<0jisMoX;jxkJQLp9lU^*9)Ni1YVKeL=Z(s zcQO{?3&}r&3wosQJ~lLMKK~Hf&H>r9XT?nVhht&Nr(v#J95X%jC3ZEklt8 ze*&lPYLCg6qhkYKfpV{eMydo5yPBMJMt1)iE$i2?S6t#4V>n1AXs<<*KZbjpK!VWL zGp^OSg)#Zxcbaf++QVM=!@K=~fvO|}M=F&cK@`|pv_1zCrcC&?y4T%Vkq=?B$ezqzy* z-Do$XUtm@YC0|Wef(h{L5Plsr5Xt9&@hd1FQ(4cW?ZESR6_ry+@K)e2BY6>svfx64 zi;v?yq%5dCxcfL}HrNL37JO>jE0e|AX!jIaox?e^;|Dk0oQDjHh}Upx7RkwUK5W8P zE{^dOaIjDc*5NOq21`F};(GwxL7rp&@T*9!A-Rs^f1Hb0bG(%NTbsRCFFWb}>I;5Bvv(P#G5h delta 3134 zcmZuzO>7)V6|U;;>FN3Zu@nEsX**7w$&VF#lVu4Ot0c~ngxw^H0|||yPJ60tw>{l6 zsjf+Et4D)y5GM}1ed7SO#2Ew@NF3M$2niu1IB}>85(i+ptbijNc&{cKl8BzES5>cG zRad?DzOVX^OTWKbzFI2f2|Pdh^Vp-mp7^jlN-umkccV{Prj-v1PJt3enb|6aC8wm? z*;YBMIF+#KRKuE63+qlj9CC)jVP`lz;v4}R%Q?z&&WN97`8~Qh>Wum0zQzhn+tZy1 zKetDmN&grtveI+n)6J>P<4W4=+A`Urd(@d`<8`kv;TdO0%0hGArm9brdd-f1?%Mxk!7F*XkM8FqplV^d&pmK|p{XwI=|HUsr} zrmc|1?CwAEHayRKzP0Oj*XQFjprU`e*{~NcEL^l#cWklY+Yeq0+xLAhJZHzgZ(sS! z{Tp}gt=MaAZij8|+sqeU(2D2PdY4;4(~n~RvVC=Bc`)Jqo7b-2Ub+5%CVYZXxZR-A zToJt66pcqz7FTv+;fMFUh_yo)(zn|js%$Jg9(tWdR_3`Et^2On>G)E=`?W=x6aKas z^o?LQ=txVn-PIl8$Fdj;9z^SEFiuys_w~EKBI%E{$7Tgy%`54Me#`2H8w=h-a3Kx! zM&AZ~Y=AI`M)?plno!h(+6qRB2Q)>kc2`!^xIC}Ypu8YJP;dU+%TmcvP;s4H@pRDf?Qm@ zIH)hZt?IYl8T2nLEH*TmU56kD!DV*Ci#J-qYQu0cxLzzZ{!|*+l*U>&YVyIsVfVZa zLXNs?LB!k_)7&|<9PD8hkBK&f7O6!I_~lirNcr)@@9W>zXfd5L-|h2Buz7Rcd}now ztu4A6{`LYcgw7+CD&-BRW#O~Y&}%l)r}&hhSWuLIs}%j47=rF;d{}6SF-CeuluwL! zO6Z$L&k&gez|$jrXaI(KRG9p6V(gJ#Mr4x=!-jfrG^l^q!gM@pfWv7JGVPu2H5l<2mhRMG024wuLCOOrjSET8xtdVVM!K2l#!| zf7r_<*@TKplG~%8qz38bdxhEW0pBZ#lBkKg7)tV>(cnjW z`6s`HY_Rr+ll%dtz$PYy^=OSFBO5L_R8s_p9sgLUcch9Qk+yR#$BZijiompO!x z$GHb87OeZRXy}q|Lt=VONjF{(c%R6U+ll=yYXdw1x)HwE1VJEo@^p9lMZ{sl^GmIE z(`&_FoPTY`ClGPcI3n{w97F*3sOj?psANH9r;FJe^9j)K5fsSbvW{bJ|0QqS{e0P(J<*09j0{ve9( z`6XC_BbxY$^czL{N*0^y7vF1weCl5?6t&q!EBHu8k~~yd(dxU z+Tw%p#dWShi6?3L^YTai0bmaRmPwXnfNy^&;8IP$GkkI&JVX`vzMG1gT z69a53-!Xbtw46}>A7B-#Smkek3AklpH7d&c6-5i#1AV^=II*J9Bn!aLCAs}t0ydOD ztD=Sy3-|#q(Q5kB$}j1~bi4Xqy%t1Iy;i{N&=(tRW}oEgFRFGOlcG_7EgGLzN6ckV z^E+v=*0}#RG^GywEpzwbO1sW^n=6v!pGTw1IGh6(9V9J35B)}7nuFJ!59`jRaQag6p$swsL*bKsqY4t|PWO;xsNP3?~Uh4o%;-&OAwP_oZt*9aEV`wlH zF#(4)OhgD(GyTHQyIQAym<-JrD@ZdOuf$#JWIU_>4NZlUH$mF~{zVHHgj2xCyDu_B zfk9KwL3od1f(GHp3DW17oy#6#fl7_>dWs%x7iLmo>0`u6}c#~ zL55Mxtf-q8vHJmvuj6tCGAv)hKAx-35!?LBYN!>!uJG32=oN5OOT>PMw@`B&1p;LN zjCZj0ITT2lgPxq40Vf{u%m4rY diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/serving.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/serving.cpython-37.pyc index 66c8cef57dd63151b4ed4797d8cef04c50ee2064..af9cca95881a96256bebf8e78f0c7fec1b7a955c 100644 GIT binary patch delta 13903 zcma)j3v?XUdEUHsc6N5LSS;QTg3AX;E<}Rw7X(rSK~RJ(fiM7y^h)w#xpx38u-FCf z3`hc6cx{0Wp+r(;k5AhuIWC}+=6zx}Qe8W(<5TA}%}JHiJ#E}{oVdr4j~q6RQ`>Rt zDoxezzq1Q~5ZzNO=bt?fDJ~(Hj3|T@tfOIGo5@{W2Gi8c& z5b1C#EYe0kGFOwT5$RAqI#-*jmCIDw%*W>HQgtFDjC4E|7wHJn^{IMUTHGtTZAdlX zU6XH|Yf3etgv_EyH>a9q>AF-np-EQDtjT{OOY{>pT1Aa$zRfQsYPb2dKPnZJ8&Y{O zA_}&r+R?;8zlqmme#~#=Nr~06*gc8Gtd12mwZq!6BBk2>s+F3ZsZP{v+o+kUbg!g3 z{c^01#qUX}omRt4B9*XqSqiIPQBu3D-RP(MBG7wMdkQgh)xa9jRio%?&!TB*Gka5A zmB#nYnkfFojmEnwjW?zCTV_OhO)kETrZv`#rVm&K%KjPZCsW-8&3Y`=jn-ROD_U>0 z$77JvnuR^|Ma46RD#t!FFK=$Jj--o9zMS zI4bXDU8vh7277`HuzhSla!#@XEQzwGST}nN@6#xGkR3wFLsqls@YBgt%UXL z8FYLWs3YtsP)Dsj7GZkXC3c*hK!cC7lk5~4>SKfKEb9g49DAJg0dt<6W9RXH!n(ko zm{nd=_#awNtkB9UV^Z>R=JcC+u++ZK2KF8Db)?!In($goala)tTa zG532$w>;=JhlaWvFg_lC7}V2e6yOpb8yjCTWFB>YHuRfvi+kGajI~gK%}VvIW$x@8 zp~6CH$ch}3qjFT?9l&|oWRcIhUo(5lja09RKr?~O7DP&P+lh<`Jk@UL`@tswJbjWE z=O$Se>5V@c+XX!K5dceqSdvI2K^!Se#hPnOV*$JaOlQG+WeM`gU?Cua%*5h{z33$? zJH1etwb)Q$e!*Ef(_ct{L<>&BcDR+9OE}XRCowJPYA42dreIH6{Lo+_TV%PyRAPUn z_Wmplh5qPG&j22q=30_VQW|2WB$d+;p7>ChgwECB%Up1W>gj1dnaf*VAkB&ei@3om zBo!V*Z7(3Qw$O^mB2asePzoq7fC)86dmqk}oR1A#oKVpCyKA4Qr zD8u8TkEATR{Xl;p2Fu*cTOUUSu{P2N`>K|4zkjN!JdT2kyGzoHv?i}hYgm(}_aSNni&T?^rAu0T z@`?lDdutOA3B! zv-ByY4>dHDD5+<_;b;%3)6(*0ySk_sT$h%$Qdwi_Y>?}Y!8BUBTl#fLEMH()aQ%2v z{ShsZDVV9o6dyH9yPnS3Hf=0_F&&G~~ zsm0_LS~z@}SiFexflY@Ilp!9R;&9U74+}jnn8qg0rqg^M8s_^6&;)odfk#QiUC6kN zN61z~))XUPsD{Z2pT)EBry_L$kNrA;`<3w7`n{}%MWG5!-#ST!S(h7%?2`Ank41LX zkdBsM_(ZU&_PZa6{G+|Zn_I>pBu&&7S|klkV&{s5&%2SD-Z0IHy0c%D-H+7lD;Ka) zm(^8iMzc?`z^ati9KadP(N`2cLMf(CDugdcPOv1;fKQh-7A#R6!wJopcq=nu0VB*P zq3lczo+t}BwIy&6pga?!H?XK(eXmMub?apbulPD38mVV`tK{gFwAm`H$VfM^@Gmwj z2ktVIY+Q$Cd;Z3A%Cf#3Ea_`akWbAl!fMumDFu;6w@4$8)y@WCBkg0clHM#KS1$!- zb%Ci{Ybgav=&KaGDGCSavroG94RCs3t(C>8k+n87-VT9R57R^Dj5U-3tg%mWI`D3y z!Ono{A>!H--U92-wx z8y!joNBgf+!u?+DhqWc8#&l1|_Lr9eFDxH?VL8=Arbc>vw10T)^5AHCaCqSArJ>;` zygwHYA&!NoT8 zbUQn3&7oAmazJ71dfG}}xY`fQHhvM96l}YE+RfGNKfQG=#WF_bGP73ND%{TTV&P2` zwn>FbvZ+MW2BF*|a#VG{TGzf$F%&~!OvoWqjl)itf8x*ZMevg)2`*il}(E-{U^itVjREYK=3 zeO9r(lFEX!l6V`G$3m-W8qb=}Ocq{OV3bEtE69H+a5aGDx+L&X!eeJdL{2SW*v~;K zvJR`psVPZLbOpQ2KClx!Wl5y#sHm{N>Rhj3mo9d|per}lALCDg z+@5x2u>aD7KR`06Ja`+g+!2sy+%pG?g@ToJAOpQXHebYEyWn;=UOmElP>3I-x;3^{ zU}k%ny#0psdf$^sc|yUyog`c#%_iI65jlB zwpeDwF}wuuV?6d500}IhLrp1oqF@6kCVWQ$4Orsz1xrKG#XBY##ZVgLh$=623mNg< z=D7Rgrn^s(MaXRc*f(Uv@J|4EhIKa!i*vFF>N)DV&(o_ zTj##lP#eaeycl(0Zxg>S%doDevELO(ozW&0*w~lcy7pcDXMof3wIsuKlcl9LSZ%=> z1JW5xvA|V{sm-=(1=N=Nr9-MyWijOg>E7IegA>Kj0~S)w#}Bv_%ZhnI)7o3gw!zD zij*4f21wQN%pGhr;q?pu<1S%qM4X_C=l>YEhs*`hf#ANlzjA4~o7Li3_m6k}4Yb1V zCLX{3Jhc{^=S2s>k9<{4ZtgHox5xvwu!Cvp{F4A)a1QD?GX=Rf&9+dG)+bVYLqsJ%m9rs(i2Fe#u(t+mzZj@G3-h?HB7u^aE8Y#t2Fm+6V8baF9U~C?v zl%K~mDkWq+oDLxlhS1nHBPhT>M`ID$AEDGJfiVCvG|2VsYnnpd9a>Wgk&xImu@^PG zb$4y|Q^#qsP?g&HDU*8^S)Zf^sHh|_?IeyZ&le#(NySmmdjqH0<%-irR)hx1q9iauTr>g8U~A_Wg1IN*(kp z!(UVQ3~X2gO{QcPctu&(3a|Q^q`mn)kr!l!$Z?Pp0`_%*H7nSURmz8v^OnegH809v zLQV~^e<-lg3bvmqg88|@!D>Xq|M_}M-vaUqrn7g-Ys+U9rc&YLdT~(q(T82;A+ZfafL1+1b_6_&n zbUk`R}nfHp!k;vJUk*VOm!<8`JcMa?<+qaq*BE5sRqhpB(R;_9HchLRG6xW z08eukVKD#ZPia@s=T~W_#K<&L`Yknq^P9>Pn-YH%+n^;8j7|!`k@)JAv@G9UC3c^Y z;kL~$D~lZn41sAs1-3P-df7b>ielCVBuiSp@^Ob>;h{L)|| z1x{UAD=05kb)38wz+Mu{VNxNh@m3s-n$-PT5RnZ;L?{N8c?Nq|gntwGd(uKY34ufK z1;xk``JC+}ij#?1Yw<8*QwvsNK9l2i0?q`CLvSO-Gb&b0k6GSmXM8$mCngsP!pclQ z{NBzn%TA0eI@85M;@IKi3HUMai*h$}`JA(u$WW7rg;^}&6cMVlHd-P@K%HO^6)myR z-$rvirxWO|P%Jy5*?cZja1wWN&h$nrqn8H~$IqNTnHcj8-^7WYqdiB@B<@V-veTmB z9OjdlTgW@PdCXhP6*DOEYHu4&VSG1127gK{ffCgCu~SD+CCYwtA|U5io-F1?bQPl) zZ6Bh&x6xFUaEXV>bq8u0V{#UFA(2VISIbTl1;ko#gaJ81T(*(|~^rpP#3l+J=M_)nIT&RRu<9 zREsGQ&D4xwi()EEdxX~7?l>o9oN5dscug8ifMnyg5Ey?PS)Ku2fWUATIeF-Oh5#8X z!pP%fH~DttuTYAt2j6BOLBvUca+00A{AGf_Lx9Aa82lEIwmGXl)XAWd9m$VtP6iAzRhw%=V zl(ia&3IvPRq9tXfmg(y-v`Z=+QJq8zQgsL|1$}G@kuiuA!~OFEk2U2;(IcENojG>$ zR5H$gA3gI9_d;@qPAYNcy1=%$|1-Jk#y0WY3|$8y1QDHK(+D$?$xX87U0Tq6 zXaV+$qN^f|2vuVux*#G6faD&+RRk`g@UOo)-@U5#x1hrClG)wUWgi|GKG$VK3%w28 zlGc;u4j?3)r`)*-rfdb8Q+fV%08hi>7I?`$+jAvGHT>Inl&SlB*>_g|BMsm`5%?1VdZ|aBNDl!tX`_9ys8tY zQLd-Y-g$(H!he8*&(pLbNKky;|KTX4IXyP^)Iibw)Tx%?M&xZdFC@9)hs|l6qKz%& z5>p}PhQAB&G&N4PhM^*2uA2e=uKQP~e%}BZdyRj`{qpJd^50Tv5_5cU+ zIr2bxeYL0FO-~8)r0Y@M_j^ z;70B#JQcX1FjEdVDqL0-zRUsO;fexM2v}RXHtK7)iHZKp>7n7l@e>mh2NN4#r|1ic zRUobn58arUNHB{Og0KV%Lb&C zUc61z6P#U3PE;HZ|2vf5aD6BmMXM;NmO=ujToSy97B~X%>zT5y_|D*Bo1?732_m<^ zQE>90%mmg0%WB0LQf4UR@l@3vD#;{s%Qqm3nF{kzc#6ru(lZqbXJCcp^NWdF3piPv z%)t=wSv^r1zke)Tu@&Yswk<3$LEv7p-+Cwf@;x_m1vb&Mq!M+B1AM(svq}cNaxgQA z)BN0BUyoA&#S25BQYYlBc;T_D10PP0jgJoYKkbEwMn*=jj$ciW4~%%mR3;}vDcH$G zb^abz)&*G?Jr!2j_7#YbrlwI*>n{`CV@;?dK^MfpxSHIgP*{*_vfFz0ttbW2bv%Ob z=iP6feY{+P&q&cB@;4Do3DMgzuo#FM&6sQ9QV6_^^jd^c6p)4blS-kIwyu)W!x>~P z{5dHd6X%b0;$S?ER@Alnb=>d4Ns5$iC9nJ42g;fVhGA}3;1_eI1J4dT zoh5T!TQ*Dj%+8X@aADvTY)5p97Sf3V`m3jeh$aO6Bh1TOasO9udwG{I76jjV(T5m( z(GQ9xKu7*Q0S#(cgo^p!)8`um$o)h-@`I)b z+KL+`{s5?;r%bX`2nU>b!A$6~7j6EKTHhueY4mRppOGmgnPhU1B9BvWQzg?Qs<_>K zopOUa)Hi;ZOsr}&iAMPU_9Xw*eYG3ul>b?CfX2#ix+i_dhO%PD1Py|+_*J+ew z2$8ug^m7oq5kIpBCUVFK@qq09(i8UBPwUu=vJCvFLK@lwA^WW&X|tzR^z=jOiKLC+ z6K|E>nG5fgNlT&+02;f81G2lgOp(h&g%ofNK&<&Th7EzQvvBhu|8Rj&+T>eKakEFU zC@JNNaizhu!b!if)OHy+mU8Yp{VgBsK(*n^!xVje)HJWt5Cvt$j5dLwVmm6oM{|o- z=T@PhBy-BU?B2Ne_c+#gX5d_z#y5-|ASO*j5hcX*G1qO=G<=F8?z9sny^ZtC4{X;* zP1_6mwzfFeucztnmL$v}N|8(osW%8y#t+d{p#c|=;~b{@(yx?Lb=L<+Kb#u8_GEhW z+Hm^m{-I%nfB1q0rhS75GDYAfflY#JuJ}4K&QQ0Y43rF~?{QDdEWiq`B^3ThHBlsp z#Fl*%b9AItTvAz4?6`AC@sBD?8b8Po{>iIz+A?^$gsTCiz>4ycREqG+$kFnFIo-WA z*jR=*zh}Ip+^?L5-q-F2?(6r1_l?OALqPMMv0PVDp(1WL!4=$*wEI>y5#0QYW7wZ@ zLd>MIM8{l}R}o`gjyqvWmEzEX^-cu(F78BT==MNEsbMXObS)leIidF&*6PZodZ7^G zC0s**u6h^6>RCkK=n7`sp^8v;xYy7$)68mc>ff-|; zgB0#oi3@YS+4yiUX=0*cX2L8*;ED{g&8-bu7Bc&ZI&m6@!QU;OP6Rc&8tCLHPVk~FlBo)qbWz|y#b(Nm8r-c2AQ(~iFs_m%c1;mjy??Nl7rnwwL_!F+U)q~g)4lF2+pW*wc zi*=NCfAmaUqfkBMe))DPnIrstx8eGcwD9(NsPGsyty8R}P~1XNopu8PKFw67NEJ6( z0Jr^dX0kCWpB~rG#`K5hbR>!Kz6Su z+oEN$7SNanDdYcl{lH==$uo(pc8&y2KP})zG%Gzr_iAvuhqHG9m1w-9LDzh4(+xz+ zz;Cp`_-W+A`Y|{l)+qH4md_BxB^KvtMN79;Ib{_ zh9}+615V{%^BqAZ^cFdSOm*v1O95d_yRW2vS?+aDJbS470%nh3wSSo=$gIf>P7ruM zuuIAzzGNIa;Bxc$K=bKWp&NGVhjyU?pwJj%y`y1!n zi_dirJV8YqBJ2qQ;{XV);;P(&Bcg$xCSrwsR7~uf%{D`Pkg(_6&p+2#{u0&R>|Qkc z65%$j8!!+eA9INA|8H8WVSGx&BZya$MMXc@wp&EN_Ahdc{L4AGsDoN$i$xR#I5UW9 zn#_mYW6$p{6B`M8G;;a!xbLO#y%;K-QOZG9vfmG|E4j)B{U^xqg&R?Tp98=xRR1K& z_X~-&I0d*l8R6Kgq^)=%ljkJEJriLtxFopwWS9M0F&eJ1+5YJ%xiR9(ofq+A9{wf7 zRG9t~MUVf%BUDKEv~AR2WK7(=850($7p?v;2LIa+FzUsdGYA-o|AxT7NYzQ;d*$f3 z@FgG$pU&MKD)@1z9kf=RROlxJgy{L_loI0TDCNFPpRWRVwcGCjg+?=XhOW{e#AQC= z&3keB9)0M)RZw8NEb(trOYac)Hi35uyiQ$lkcWO?6~fa0yP$U*-}q8u5J4h^Yl21y%!C_Y zh8ol8q#5BJRg>cZU=&S_pNy9=yF*QM+4H6sDvVrZTOlmbA2{ zQduwRXudo0!A61xXMrzo|h+iK-FCJ<&3OdujB z&?bj!Pq*V8Eq0VT)17ih8j4+|?sRu)L3%-{C*4z8m|iIHvErgqGMy|fPA`_RU920J)o8qc^`LQ&oa!33oh@XGP_vdL*<#SH1H}@y6ckJC1Za%( zdSI5Z<-jbrTkZA0YyhU0tpH|)-F8T#HnKa}Dz+LOZDMQKT6A<9+rc)ljlgVXo7io@ zY+;+(7QDCGx3jGy+T$AEZf~8G=iINP?#h<0{#5&n(Q?7&Bjfhi&^jl_^P}#Wa59+` z-w57TTbWBGH*MIsIeGR{(jB&wC+mdC0Xth-lXPr5x$DHhfuqO!lY!3y*;_R|6aer;e&hj_Vw?*MT74Vm#0)Ob|LQ$XB;=%;Myg-?DlyE zdLI$5TfeDV;^ELCYpgW9F}pFpL3}l|O5HBr3LRWUBEqd3L5MyLfNgjj2#sQ?+z{u& zPpUDoFtRWnBZ9d`A#pgesP`TsmJ+9GYFv$}F`XxXOKD#Cz`($}{Jo{p7(uFUW14%{#W&e8CyY zXY6vW!t&*z49k{tHpUN)@nU8)%Rv#L;#m1y_gKZX(OL7svTO5lw&+i9FXt65-i&mG zbquzX27f}_*0f{G64t>SqI*YY?=6I z)8arBEVEpErRn|4OmsJIp|nOX=E!ZyU~8Owr93RIB(_JIQA6W9KUBn561}z2mCTsX zU8AZN)Jb*1V1cSWr4$0LSqS2-6)Xuu%&clCh4DmK(2Z7&Nfl5nG)$^f%5-c-0c<3Q zHgRTkDsEG~9O_hvrjTIa4<{x97w<;7dAemrxmP)Tbi>=V33DP?HK$u=l<77WVbK|2 zswQg~3Gy$oSk-J-@ExoMs^$*m+;5*$Flamf7r+i5Rs*bYhf)n-GveaGmi-Aj=9J3k z_9P@e+0vp`#7iyrcc--Psi@}!_IduBr>I3j+}Zk{Fjpy+vSr4*M5%q-Dcy0o z*U0U2W47bY9*GwmwzCY!;|1~%EWE}>M{Q0_H(Jc*Y|nIZ!w@*nEZc6el1o`~QC?)W zA8;J+6$?5x*z?ycPd{49j@aPW^Lbt=7cnDe2Y{l6wW!{rhII|1WZ-AwH(Svlhu+<6hk$AFedF=t;(o|t-G-X1cFnrrGrA-q*>XT~K zn1LB}1Ct84W?fYSzx4X-0vfPLHAwYfo!|_yNMPEknlq}ZoO4eq%%}#yK+m$kl$Hq* zJu{i4XO^saxh9cR6`oP3BNmnw-<9|XV8q9x6rF*Fb{ndS8=KS~R~-Wji^_#*g~sW4 zRiR<0sN6)9OaU)8^o{pi;5gTnspMH3>e5a^fpRy)ZD+Jnb~f6Q?f5F3%6LrjQrTTw zw97;8@PHTIQz@72oD0GA0=Z%Zvb;>(z2N9JFZ@8ZIA%+2;x#ySnay@+dwRa?db(q~ z>4>cGxyuU_E3hZxPZ#Wqdf~j2FJm6iUc%}*899j2`5}OmE-F0>_RJF+s(A?DBY2z* z0E!v~!?b{1TEHH0{7k%~V33$(8!CZe#H&5OceoSe{CoK=Y&g^!4`lQAu`863<77$aE^X z5ws}dLwL!%YHimIHM7scT)Su8N~P%V5;f_eCORyN7lJtBY@jSs0behXM0%<%3B8cZ zA?;_gxe>VyDRbU}nV zfQ|*n1}RoG-2kNHGw~kphD>*h?=Egr9}z!a+}4zaG;0MNk1?ph3*9G{Em^#AC9o#G zigl4y3c-TK)Dg=WbVHMhGyI6+3vZQb!u*%SuPoV9YoRro!%%`no1epn+&9jtl#s^3r!K^qNlYJ$_$$?}`1H<0twuM|Qz$FkC)n5A5qp zMZ~dX-91{x@lUjZfjwg+Uo3hiIBzu1Ak`mOcIRrjB;Cmm-MpL$wD>nXzWWo1X>sw^ zvZq(kqU$^1Z>nU}o3?`XSP@@e{u})%6#BqcTk!TQ4x>}dm+f>T&t8D`kT!`B{B5XD zH%ND87SG3lyTO{uQ9|Oy-gY%0UhDko7X-7iUx6NC0j*D$$2HouZAKjDrL=2-iG<#Bou!xJLMZA{U zx`K8X4WIB|a)CFU#$BjdBo3`wQ(KQxj+`+17r+z5vj>m!DgYP^*3ea}dP3ZNS0BR1)8dy5QmtE9Yz@C-ZP#s%)sNNuqs;aDl zOD7xLi<&5~Pe^P-9lNehA4AR4vZhfE9>1bvkhsJ&&0(s_v_a8X6%(s!Gl9B2Z0VCb z@2TQbtB;xQ!%F$DiRhX|@j!akpOHLxkI1a)ICLKMv$|4pis9tPZO*8fY|FEph>_((&P|->g`l*3HMCt+*1r9~2mR)< zU0wf}u;`_%%Rh{&o0~_dd7ZQGqDg5WQq=K@7OvH1N*(yl1N8ZPZzf;uW=z{%f50N`h1izcB{#f|(4f_)5+fQp67pNUkp=?3ZDnbWZ<94uy$ zIj%QWJuOkdLjJgBlcbSe)6s`i}AD6J_XO$F*5<*O)t8fire#JkoC$2iNxJ zhsrj~E0`{f`fzsBmaVA-{~gSfC&bL=9`ghhw~Ehgevc`!G4bG*#qk@N&TAJ5jUsMO@ zoeWt=FD|13GP2A*Pi=;1;>3+gg4{RA3#Mv8ZWf}vP~*>DJ+|FcI}+5`LX0vh$1q`$ zFXmnTlqlcXUi)RLG`U@>Ak>t;*VDl8PZIbe0!N4-UaVx9wD0w}DHZk?_$Ng2X#&^R zla9#s)Ln-^MqN50msQ7niaOMD?nPRkHS0Wz=#E9&W2#uYW10HG)#E$PYrCi^m(;}g z(lYVq33ua3^5=m22yI0e1tmgfMw`K(6QO$!sh<@C_k72Xu|NO68+%p;K5gF3WS&vQ z13S+o{+wvOK;Vl2Wbecmb}nwcX~@r^?$fGxYiFVMWupBH06(s*2kcOPFG}16o*m89 zJx|%K9N;C?%{oH{)q3X0g6+m-0MQDN{I zkn%4Ppn1wZ=IxnW3NaM_EWjU9=Y+C!u)P@$@Xv}PyI-25;{>)6 z7$R^A081dPoDNZFKw8k@lJutCs)FY8G!Aj10v;uA7KtD!S~w`G&q*~))p&F~d+<05 z0hn5=DkCQEkY1lQsi6eVuOB{OL5IT7$L~IFyJNiUBzNu09PHaWusMnBS-rd!88nC= z<$aQsa`M8kjabgDB*DiNqM^9%O?A6x&64B93mDXQdH)6_FSu*J-z~KCGlVckE8Ly8 zp;9=T9J`4AGWpS2@9C@8k1dt}OY*NPV&A@JV#K6$m~wq_VeP-OHXW?ORdubZHmMG3 zh@5rOl2s)WLgGY$Hq`(lFvv(M7WDmweKJ~32eNFqk{dvJ4IwUN z{W4CT*{NE$v^UrwY;LUX$YlnzrF`*{e-4YNCenX&&wZKxf#Z939pTNu_$)4!{Rn-I z5unJ!Kc)zrbP}y{wq*0aB{Zorem4PPUN7K|jUu>tu11&_2(%O0tdBfR9|5U(d=c6V z1oR#qo*cpNl!m6rRt8G`9Ei}<23a};Z@nm`pTA74ZjuWU(QB&Gp*VM-M1lcB3mAs} zGGKwk#||!31xER* z@$TJO$3EG=|Dc~-6|IN&Y^M;@GqbszJ?hSOtV2jSxoe128#yGv=iNhPaZlwWf zQ~5UWt;4B*CdL1Wz}%k5l2nVYq2dh^-WC+d6_q~?>9-YdcS2Txzov)> zk2tNj98~nSjo~J(tj5-x#4^fAx$|I zx+TYjz?eA%JW#pcriPOFiShXB1m@Ny$ENk)PGg5rptzdSKFA<+?ZZt#UcCQk$LSxU zqHkXxMVhx-*EUr90yo$7+?M?SwVSAaLdmsZyEtKe(H9NJex{~G(1(e(Pj%5u%L zCz4FA9!q_O5K>sBTv^E3QkIqRA1UJD`&$nE!ex39-$RnRWhYICPEau^+@r*a?<7UF zu*!d|h->$MY(0M`jrTABc#d45xln)J7G7VcC@kwbIY1rbLC5?jfCHY99fNm5ULXBy z#Y3B}<8d~CkE#am!cCf1R7&bZBNTMw6f)kE+G9#J$~ViqHm#(Ko&Bvfr0^fL9@8!> zmt~grvT-?Z*}NRQYz-ob&w`Iy6HQeeioDk~3IU`=E#y2xBZLW0;f@k|{9SI)d6#Rk zFl9L1(3CobTgr)q8=ekV0Sggi=$os}(@~Tg@Wfa|nw#e7#woQng~So1L*00_8A(!& zzY5YO)*x{~45PUTV47!eD?n3VF(mJCAKAL#g4{5r_Rnc+wK?*S7M1JD{$)|lCGRC+K$?37tj}vQ> zpQgy9Az2YbGzg>rF85sRb?V6CC-9nMbuV(bQW+T=l__aoCVxx>GUG?O5_6)4Y2D%H zQz0)h??CuZiSlOzq*zL^{Skfs2LUZx@vAgXTBQPWkiZYImejJA zDvsDm^PQ;S4~w#Wae>?@uMv51_~~%o@^1EQ(H`J$fkeay+tZ!t*o}Ey{$Hp{H(WDqbNF8O%8H&v^XAyHTfA!>q_^bb>c=zCjqXqg#)T;1%P=a%SFs)qi%wis~ zX_@~sRm%`5aeY8oCJL93356f0q+0!blDngbuAx&kxhdB-Djmh?x%r$7`NF+dPx9l)8u;ocND9nNy75T1^(PN^3|(^1wTK9L_6s|vrXHi%aWYt?4)^Fk}` zEujdswS3%KyVhq49u+G`Hkn_eO-YGUBL}xiA@Z4nwk}Gb0U*`nS(Q;&=5yz<_?U~o z2H{`h4~HpTPP4Lm#SAcgVoj3uiKeV~F?cOon6Vx>!qvMLWPJ z2}rU1S1NsofQ(&VM9DKNXA6kai9~-=AoY7XKBq5m+x!5or)BuT zJE&Lkbq(s<%@xufGY$YX!u zoQgytuB<8K598439oM6}71mHqw~@%UX(lm_7AF?Mg-=|K;;Jj5Cu+D1*26vFC00u~ i5pN5}tmeetL@*J9=2OE7D@1Sq7p^G*D`+hbtN#PO0l^CZ diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/test.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/test.cpython-37.pyc index a544f6478ab1a763260c28508ec9ee9f1af17290..4f924293b4373ed3d839d988641972870779f01f 100644 GIT binary patch delta 15130 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 T)><0NcrE%2HiiV=ptb)$JxhYa delta 11266 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 diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/testapp.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/testapp.cpython-37.pyc index 037cae820b371b1e6e1788e6168f78bbd244fe84..613eb28a5e1aaf05a78fd19301ad5a5ee6195fe9 100644 GIT binary patch delta 1203 zcmZuw&2Jk;6yF(-oz2?2ah$|yoWwCCDoxv@X=xiJl&DTj(^Qa}R7hy0YPIo<9INY( z&a4w!A^8B}R)sJZ;6f`90xldPdf^{{OD{QqmJ`QHz~9jKHll*6GkR}+@568B{ru+B z8((bLtF~-yf)vXLCQ^W#*;x`({xQ@08Rz{P0LNkxDRl~wTU5Y zK^{8Xd_;IZw;oV;fT!Io&v5%=!yV)fAAmE*vwRTFAu-Hzcc}apstpAr&Agk33VA-v zN5l}%^U((ks?%Ern>}&=w}Yb}dlwcK7E4t>2t=r?60GH*Q4=jCmP@O*Ds!*Ttp$Ep z3F|4gusKU2wkQ40QvwzkDWPh9M*vE%`bunxyIr9|Ff+%d>a?MhUjzcIt%YFyW+m8Z zm)Gj$o9}Ft%WqZ6hgWIIA}iX@JSOUJ)c!e568h+bAW6M6woeB2uVXh%vFJBioyG+{ zUYH@D>8pk6(qR03IRJ-5e5`VahCqD@q9`bX%vuuZk66oZ3Ype_7v2z<5+!PTJYtPd zG-Vp>7s^C7M5L$gDX-&)JNuBK98di^(PaH1IzGtSr6?Gokt{Zw5WbEp`gXp$x87hjYQ^^z;AAIf8~4q)#T@6 zFG-QFz?Xi~)#4tp^e@H7gz4VYDKewKo0=Ip1F^VuIgIEmsyY4V)ah&I0Y*&J>q^c8 zbQ1vOC5)d#(@||13hs&4ZbPNVv?)pJj$OX)rM0HZXTgvzT&wb=b(c(Z`2vgv9 zI$uMgs)8~I1-=~ME=L%#z_LgVgpr8^10Eefl$-{P@f1W*(07Va_>Bph#T!wU%4<-w zMvcS2N=-`ov^!m>Uk6uYZi(ZWi!)}KTmmn26#6ps_`jXwS5h%&xIL10cDw)Li z2C5XQw=vqkefc66wKkB4eaI`QaJb?=JrKQ&*a9P;46UP mX|{Pc5Gy#_>Le%@vuKhG1;I@zVbhG7RhBS^%}Zt;W6uBwQ3P@T delta 1327 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 delta 10002 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 QLjuJNGpl;H+L^Zg2YC=ALjV8( delta 590 zcmZuu&1(}u6yG;HnT#=;Zj#cb2W1YTMrvq^pu~De+TvFm#WV;Jl{7o~>Tb5oZmAWd zU_A(WDRc2q@DJ!ga8bM{c(RE8137xqTQ8n|%4FZOPYDS(m z?`%Kb-#p#Qv+1egIkvpP!Y*gGxmbC~*E(#8H@!&~aL&s0r7JgX)mYmX%<~0j4iB3> zHz*{Ss*>Al@j<{#tWujhY*@HDJ72BM|EJ-H{zSE<@vx#@USC*{`BK zdkfOLdL&g~Kmw9Lki3_<53l8iOc&nBQ`r#S$(`(u{r+zb0Rb#4qhhnI#e;py7!gF{ z)YK0np%0jcVX7W*QSR_TxY&Ep8|35SqB6vB1w-O5_jxN6NGm$uNU#4nPhnj&S68_R d#5HXm`xj0qI;1n6FRSB;q~09^HSN0X{083ej06Ax diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/utils.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/utils.cpython-37.pyc index 9c6864717440c2fac899b418f4bf9c709bf2ff21..5f384bf7c8ffcbba4f9536e3c4c1e9890cde0a9a 100644 GIT binary patch delta 9514 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 S-W10I9^3WJ#NK3g?=rhr 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 diff --git a/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/wrappers.cpython-37.pyc b/backend/venv/lib/python3.7/site-packages/werkzeug/__pycache__/wrappers.cpython-37.pyc deleted file mode 100644 index e212eefd170c409d80570b573c02194b8712d142..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75013 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 y+NbYIY}C_<@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]' -_cookie_re = re.compile(b""" - (?P[^=]+) - \s*=\s* - (?P - "(?:[^\\\\"]|\\\\.)*" | - (?:.*?) - ) + _cookie_quoting_map[int_to_byte(_i)] = ("\\%03o" % _i).encode("latin1") + +_octal_re = re.compile(br"\\[0-3][0-7][0-7]") +_quote_re = re.compile(br"[\\].") +_legal_cookie_chars_re = br"[\w\d!#%&\'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]" +_cookie_re = re.compile( + br""" + (?P[^=;]+) + (?:\s*=\s* + (?P + "(?:[^\\"]|\\.)*" | + (?:.*?) + ) + )? \s*; -""", flags=re.VERBOSE) +""", + flags=re.VERBOSE, +) class _Missing(object): - def __repr__(self): - return 'no value' + return "no value" def __reduce__(self): - return '_missing' + return "_missing" + _missing = _Missing() def _get_environ(obj): - env = getattr(obj, 'environ', obj) - assert isinstance(env, dict), \ - '%r is not a WSGI environment (has to be a dict)' % type(obj).__name__ + env = getattr(obj, "environ", obj) + assert isinstance(env, dict), ( + "%r is not a WSGI environment (has to be a dict)" % type(obj).__name__ + ) return env +def _has_level_handler(logger): + """Check if there is a handler in the logging chain that will handle + the given logger's effective level. + """ + level = logger.getEffectiveLevel() + current = logger + + while current: + if any(handler.level <= level for handler in current.handlers): + return True + + if not current.propagate: + break + + current = current.parent + + return False + + def _log(type, message, *args, **kwargs): - """Log into the internal werkzeug logger.""" + """Log a message to the 'werkzeug' logger. + + The logger is created the first time it is needed. If there is no + level set, it is set to :data:`logging.INFO`. If there is no handler + for the logger's effective level, a :class:`logging.StreamHandler` + is added. + """ global _logger + if _logger is None: - import logging - _logger = logging.getLogger('werkzeug') - # Only set up a default log handler if the - # end-user application didn't set anything up. - if not logging.root.handlers and _logger.level == logging.NOTSET: + _logger = logging.getLogger("werkzeug") + + if _logger.level == logging.NOTSET: _logger.setLevel(logging.INFO) - handler = logging.StreamHandler() - _logger.addHandler(handler) + + if not _has_level_handler(_logger): + _logger.addHandler(logging.StreamHandler()) + getattr(_logger, type)(message.rstrip(), *args, **kwargs) def _parse_signature(func): """Return a signature object for the function.""" - if hasattr(func, 'im_func'): + if hasattr(func, "im_func"): func = func.im_func # if we have a cached validator for this function, return it @@ -98,7 +133,7 @@ def _parse_signature(func): return parse # inspect the function signature and collect all the information - if hasattr(inspect, 'getfullargspec'): + if hasattr(inspect, "getfullargspec"): tup = inspect.getfullargspec(func) else: tup = inspect.getargspec(func) @@ -108,8 +143,9 @@ def _parse_signature(func): arguments = [] for idx, name in enumerate(positional): if isinstance(name, list): - raise TypeError('cannot parse functions that unpack tuples ' - 'in the function signature') + raise TypeError( + "cannot parse functions that unpack tuples in the function signature" + ) try: default = defaults[idx - arg_count] except IndexError: @@ -149,8 +185,17 @@ def _parse_signature(func): extra.update(kwargs) kwargs = {} - return new_args, kwargs, missing, extra, extra_positional, \ - arguments, vararg_var, kwarg_var + return ( + new_args, + kwargs, + missing, + extra, + extra_positional, + arguments, + vararg_var, + kwarg_var, + ) + _signature_cache[func] = parse return parse @@ -172,12 +217,19 @@ def _date_to_unix(arg): class _DictAccessorProperty(object): - """Baseclass for `environ_property` and `header_property`.""" + read_only = False - def __init__(self, name, default=None, load_func=None, dump_func=None, - read_only=None, doc=None): + def __init__( + self, + name, + default=None, + load_func=None, + dump_func=None, + read_only=None, + doc=None, + ): self.name = name self.default = default self.load_func = load_func @@ -202,21 +254,18 @@ class _DictAccessorProperty(object): def __set__(self, obj, value): if self.read_only: - raise AttributeError('read only property') + raise AttributeError("read only property") if self.dump_func is not None: value = self.dump_func(value) self.lookup(obj)[self.name] = value def __delete__(self, obj): if self.read_only: - raise AttributeError('read only property') + raise AttributeError("read only property") self.lookup(obj).pop(self.name, None) def __repr__(self): - return '<%s %s>' % ( - self.__class__.__name__, - self.name - ) + return "<%s %s>" % (self.__class__.__name__, self.name) def _cookie_quote(b): @@ -262,11 +311,11 @@ def _cookie_unquote(b): k = q_match.start(0) if q_match and (not o_match or k < j): _push(b[i:k]) - _push(b[k + 1:k + 2]) + _push(b[k + 1 : k + 2]) i = k + 2 else: _push(b[i:j]) - rv.append(int(b[j + 1:j + 4], 8)) + rv.append(int(b[j + 1 : j + 4], 8)) i = j + 4 return bytes(rv) @@ -278,12 +327,12 @@ def _cookie_parse_impl(b): n = len(b) while i < n: - match = _cookie_re.search(b + b';', i) + match = _cookie_re.search(b + b";", i) if not match: break - key = match.group('key').strip() - value = match.group('val') + key = match.group("key").strip() + value = match.group("val") or b"" i = match.end(0) # Ignore parameters. We have no interest in them. @@ -294,20 +343,20 @@ def _cookie_parse_impl(b): def _encode_idna(domain): # If we're given bytes, make sure they fit into ASCII if not isinstance(domain, text_type): - domain.decode('ascii') + domain.decode("ascii") return domain # Otherwise check if it's already ascii, then return try: - return domain.encode('ascii') + return domain.encode("ascii") except UnicodeError: pass # Otherwise encode each part separately - parts = domain.split('.') + parts = domain.split(".") for idx, part in enumerate(parts): - parts[idx] = part.encode('idna') - return b'.'.join(parts) + parts[idx] = part.encode("idna") + return b".".join(parts) def _decode_idna(domain): @@ -316,47 +365,54 @@ def _decode_idna(domain): # unicode error, then we already have a decoded idna domain if isinstance(domain, text_type): try: - domain = domain.encode('ascii') + domain = domain.encode("ascii") except UnicodeError: return domain # Decode each part separately. If a part fails, try to # decode it with ascii and silently ignore errors. This makes # most sense because the idna codec does not have error handling - parts = domain.split(b'.') + parts = domain.split(b".") for idx, part in enumerate(parts): try: - parts[idx] = part.decode('idna') + parts[idx] = part.decode("idna") except UnicodeError: - parts[idx] = part.decode('ascii', 'ignore') + parts[idx] = part.decode("ascii", "ignore") - return '.'.join(parts) + return ".".join(parts) def _make_cookie_domain(domain): if domain is None: return None domain = _encode_idna(domain) - if b':' in domain: - domain = domain.split(b':', 1)[0] - if b'.' in domain: + if b":" in domain: + domain = domain.split(b":", 1)[0] + if b"." in domain: return domain raise ValueError( - 'Setting \'domain\' for a cookie on a server running locally (ex: ' - 'localhost) is not supported by complying browsers. You should ' - 'have something like: \'127.0.0.1 localhost dev.localhost\' on ' - 'your hosts file and then point your server to run on ' - '\'dev.localhost\' and also set \'domain\' for \'dev.localhost\'' + "Setting 'domain' for a cookie on a server running locally (ex: " + "localhost) is not supported by complying browsers. You should " + "have something like: '127.0.0.1 localhost dev.localhost' on " + "your hosts file and then point your server to run on " + "'dev.localhost' and also set 'domain' for 'dev.localhost'" ) def _easteregg(app=None): """Like the name says. But who knows how it works?""" + def bzzzzzzz(gyver): import base64 import zlib - return zlib.decompress(base64.b64decode(gyver)).decode('ascii') - gyver = u'\n'.join([x + (77 - len(x)) * u' ' for x in bzzzzzzz(b''' + + return zlib.decompress(base64.b64decode(gyver)).decode("ascii") + + gyver = u"\n".join( + [ + x + (77 - len(x)) * u" " + for x in bzzzzzzz( + b""" eJyFlzuOJDkMRP06xRjymKgDJCDQStBYT8BCgK4gTwfQ2fcFs2a2FzvZk+hvlcRvRJD148efHt9m 9Xz94dRY5hGt1nrYcXx7us9qlcP9HHNh28rz8dZj+q4rynVFFPdlY4zH873NKCexrDM6zxxRymzz 4QIxzK4bth1PV7+uHn6WXZ5C4ka/+prFzx3zWLMHAVZb8RRUxtFXI5DTQ2n3Hi2sNI+HK43AOWSY @@ -387,16 +443,22 @@ p1qXK3Du2mnr5INXmT/78KI12n11EFBkJHHp0wJyLe9MvPNUGYsf+170maayRoy2lURGHAIapSpQ krEDuNoJCHNlZYhKpvw4mspVWxqo415n8cD62N9+EfHrAvqQnINStetek7RY2Urv8nxsnGaZfRr/ nhXbJ6m/yl1LzYqscDZA9QHLNbdaSTTr+kFg3bC0iYbX/eQy0Bv3h4B50/SGYzKAXkCeOLI3bcAt mj2Z/FM1vQWgDynsRwNvrWnJHlespkrp8+vO1jNaibm+PhqXPPv30YwDZ6jApe3wUjFQobghvW9p -7f2zLkGNv8b191cD/3vs9Q833z8t''').splitlines()]) +7f2zLkGNv8b191cD/3vs9Q833z8t""" + ).splitlines() + ] + ) def easteregged(environ, start_response): def injecting_start_response(status, headers, exc_info=None): - headers.append(('X-Powered-By', 'Werkzeug')) + headers.append(("X-Powered-By", "Werkzeug")) return start_response(status, headers, exc_info) - if app is not None and environ.get('QUERY_STRING') != 'macgybarchakku': + + if app is not None and environ.get("QUERY_STRING") != "macgybarchakku": return app(environ, injecting_start_response) - injecting_start_response('200 OK', [('Content-Type', 'text/html')]) - return [(u''' + injecting_start_response("200 OK", [("Content-Type", "text/html")]) + return [ + ( + u""" @@ -414,5 +476,9 @@ mj2Z/FM1vQWgDynsRwNvrWnJHlespkrp8+vO1jNaibm+PhqXPPv30YwDZ6jApe3wUjFQobghvW9p