├── hello_world ├── __init__.py ├── requirements.txt └── app.py ├── layer └── python │ ├── idna │ ├── py.typed │ ├── package_data.py │ ├── __pycache__ │ │ ├── core.cpython-311.pyc │ │ ├── codec.cpython-311.pyc │ │ ├── compat.cpython-311.pyc │ │ ├── __init__.cpython-311.pyc │ │ ├── idnadata.cpython-311.pyc │ │ ├── intranges.cpython-311.pyc │ │ ├── uts46data.cpython-311.pyc │ │ └── package_data.cpython-311.pyc │ ├── compat.py │ ├── __init__.py │ ├── intranges.py │ └── codec.py │ ├── certifi │ ├── py.typed │ ├── __init__.py │ ├── __pycache__ │ │ ├── core.cpython-311.pyc │ │ ├── __init__.cpython-311.pyc │ │ └── __main__.cpython-311.pyc │ ├── __main__.py │ └── core.py │ ├── charset_normalizer │ ├── py.typed │ ├── cli │ │ ├── __init__.py │ │ └── __pycache__ │ │ │ ├── __init__.cpython-311.pyc │ │ │ └── normalizer.cpython-311.pyc │ ├── version.py │ ├── md.cpython-311-darwin.so │ ├── __pycache__ │ │ ├── api.cpython-311.pyc │ │ ├── cd.cpython-311.pyc │ │ ├── md.cpython-311.pyc │ │ ├── utils.cpython-311.pyc │ │ ├── __init__.cpython-311.pyc │ │ ├── constant.cpython-311.pyc │ │ ├── legacy.cpython-311.pyc │ │ ├── models.cpython-311.pyc │ │ └── version.cpython-311.pyc │ ├── md__mypyc.cpython-311-darwin.so │ ├── assets │ │ └── __pycache__ │ │ │ └── __init__.cpython-311.pyc │ ├── __init__.py │ └── legacy.py │ ├── urllib3 │ ├── contrib │ │ ├── __init__.py │ │ ├── _securetransport │ │ │ ├── __init__.py │ │ │ └── __pycache__ │ │ │ │ ├── __init__.cpython-311.pyc │ │ │ │ ├── bindings.cpython-311.pyc │ │ │ │ └── low_level.cpython-311.pyc │ │ ├── __pycache__ │ │ │ ├── socks.cpython-311.pyc │ │ │ ├── __init__.cpython-311.pyc │ │ │ ├── pyopenssl.cpython-311.pyc │ │ │ └── securetransport.cpython-311.pyc │ │ └── socks.py │ ├── py.typed │ ├── _version.py │ ├── __pycache__ │ │ ├── fields.cpython-311.pyc │ │ ├── __init__.cpython-311.pyc │ │ ├── _version.cpython-311.pyc │ │ ├── filepost.cpython-311.pyc │ │ ├── response.cpython-311.pyc │ │ ├── connection.cpython-311.pyc │ │ ├── exceptions.cpython-311.pyc │ │ ├── poolmanager.cpython-311.pyc │ │ ├── _collections.cpython-311.pyc │ │ ├── connectionpool.cpython-311.pyc │ │ ├── _base_connection.cpython-311.pyc │ │ └── _request_methods.cpython-311.pyc │ ├── util │ │ ├── __pycache__ │ │ │ ├── ssl_.cpython-311.pyc │ │ │ ├── url.cpython-311.pyc │ │ │ ├── util.cpython-311.pyc │ │ │ ├── wait.cpython-311.pyc │ │ │ ├── proxy.cpython-311.pyc │ │ │ ├── retry.cpython-311.pyc │ │ │ ├── __init__.cpython-311.pyc │ │ │ ├── request.cpython-311.pyc │ │ │ ├── response.cpython-311.pyc │ │ │ ├── timeout.cpython-311.pyc │ │ │ ├── connection.cpython-311.pyc │ │ │ ├── ssltransport.cpython-311.pyc │ │ │ └── ssl_match_hostname.cpython-311.pyc │ │ ├── __init__.py │ │ ├── util.py │ │ ├── proxy.py │ │ ├── response.py │ │ ├── wait.py │ │ ├── connection.py │ │ ├── ssl_match_hostname.py │ │ ├── request.py │ │ └── ssltransport.py │ ├── filepost.py │ ├── __init__.py │ ├── _base_connection.py │ └── _request_methods.py │ ├── requests-2.31.0.dist-info │ ├── REQUESTED │ ├── INSTALLER │ ├── top_level.txt │ ├── WHEEL │ ├── RECORD │ └── METADATA │ ├── idna-3.4.dist-info │ ├── INSTALLER │ ├── WHEEL │ ├── RECORD │ └── LICENSE.md │ ├── certifi-2023.5.7.dist-info │ ├── INSTALLER │ ├── top_level.txt │ ├── WHEEL │ ├── RECORD │ ├── LICENSE │ └── METADATA │ ├── urllib3-2.0.2.dist-info │ ├── INSTALLER │ ├── WHEEL │ ├── licenses │ │ └── LICENSE.txt │ ├── RECORD │ └── METADATA │ ├── charset_normalizer-3.1.0.dist-info │ ├── INSTALLER │ ├── top_level.txt │ ├── entry_points.txt │ ├── WHEEL │ ├── LICENSE │ └── RECORD │ ├── requests │ ├── __pycache__ │ │ ├── api.cpython-311.pyc │ │ ├── auth.cpython-311.pyc │ │ ├── certs.cpython-311.pyc │ │ ├── help.cpython-311.pyc │ │ ├── hooks.cpython-311.pyc │ │ ├── utils.cpython-311.pyc │ │ ├── __init__.cpython-311.pyc │ │ ├── adapters.cpython-311.pyc │ │ ├── compat.cpython-311.pyc │ │ ├── cookies.cpython-311.pyc │ │ ├── models.cpython-311.pyc │ │ ├── packages.cpython-311.pyc │ │ ├── sessions.cpython-311.pyc │ │ ├── exceptions.cpython-311.pyc │ │ ├── structures.cpython-311.pyc │ │ ├── __version__.cpython-311.pyc │ │ ├── status_codes.cpython-311.pyc │ │ └── _internal_utils.cpython-311.pyc │ ├── __version__.py │ ├── certs.py │ ├── hooks.py │ ├── packages.py │ ├── _internal_utils.py │ ├── compat.py │ ├── structures.py │ ├── exceptions.py │ ├── help.py │ ├── status_codes.py │ ├── __init__.py │ └── api.py │ └── bin │ └── normalizer ├── variables.tf ├── output.tf ├── .gitignore ├── terraform.tf ├── .circleci └── config.yml ├── .terraform.lock.hcl ├── main.tf └── README.md /hello_world/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /layer/python/idna/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /layer/python/certifi/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /hello_world/requirements.txt: -------------------------------------------------------------------------------- 1 | requests -------------------------------------------------------------------------------- /layer/python/charset_normalizer/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /layer/python/urllib3/contrib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /layer/python/charset_normalizer/cli/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /layer/python/requests-2.31.0.dist-info/REQUESTED: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /layer/python/idna-3.4.dist-info/INSTALLER: -------------------------------------------------------------------------------- 1 | pip 2 | -------------------------------------------------------------------------------- /layer/python/certifi-2023.5.7.dist-info/INSTALLER: -------------------------------------------------------------------------------- 1 | pip 2 | -------------------------------------------------------------------------------- /layer/python/requests-2.31.0.dist-info/INSTALLER: -------------------------------------------------------------------------------- 1 | pip 2 | -------------------------------------------------------------------------------- /layer/python/urllib3-2.0.2.dist-info/INSTALLER: -------------------------------------------------------------------------------- 1 | pip 2 | -------------------------------------------------------------------------------- /layer/python/urllib3/contrib/_securetransport/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /layer/python/idna/package_data.py: -------------------------------------------------------------------------------- 1 | __version__ = '3.4' 2 | 3 | -------------------------------------------------------------------------------- /layer/python/certifi-2023.5.7.dist-info/top_level.txt: -------------------------------------------------------------------------------- 1 | certifi 2 | -------------------------------------------------------------------------------- /layer/python/charset_normalizer-3.1.0.dist-info/INSTALLER: -------------------------------------------------------------------------------- 1 | pip 2 | -------------------------------------------------------------------------------- /layer/python/requests-2.31.0.dist-info/top_level.txt: -------------------------------------------------------------------------------- 1 | requests 2 | -------------------------------------------------------------------------------- /layer/python/charset_normalizer-3.1.0.dist-info/top_level.txt: -------------------------------------------------------------------------------- 1 | charset_normalizer 2 | -------------------------------------------------------------------------------- /layer/python/urllib3/py.typed: -------------------------------------------------------------------------------- 1 | # Instruct type checkers to look for inline type annotations in this package. 2 | # See PEP 561. 3 | -------------------------------------------------------------------------------- /layer/python/idna-3.4.dist-info/WHEEL: -------------------------------------------------------------------------------- 1 | Wheel-Version: 1.0 2 | Generator: flit 3.7.1 3 | Root-Is-Purelib: true 4 | Tag: py3-none-any 5 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | description = "AWS region" 3 | type = string 4 | default = "us-east-1" 5 | } 6 | -------------------------------------------------------------------------------- /layer/python/certifi/__init__.py: -------------------------------------------------------------------------------- 1 | from .core import contents, where 2 | 3 | __all__ = ["contents", "where"] 4 | __version__ = "2023.05.07" 5 | -------------------------------------------------------------------------------- /layer/python/charset_normalizer-3.1.0.dist-info/entry_points.txt: -------------------------------------------------------------------------------- 1 | [console_scripts] 2 | normalizer = charset_normalizer.cli.normalizer:cli_detect 3 | -------------------------------------------------------------------------------- /layer/python/charset_normalizer/version.py: -------------------------------------------------------------------------------- 1 | """ 2 | Expose version 3 | """ 4 | 5 | __version__ = "3.1.0" 6 | VERSION = __version__.split(".") 7 | -------------------------------------------------------------------------------- /layer/python/urllib3-2.0.2.dist-info/WHEEL: -------------------------------------------------------------------------------- 1 | Wheel-Version: 1.0 2 | Generator: hatchling 1.14.1 3 | Root-Is-Purelib: true 4 | Tag: py3-none-any 5 | -------------------------------------------------------------------------------- /layer/python/urllib3/_version.py: -------------------------------------------------------------------------------- 1 | # This file is protected via CODEOWNERS 2 | from __future__ import annotations 3 | 4 | __version__ = "2.0.2" 5 | -------------------------------------------------------------------------------- /layer/python/certifi-2023.5.7.dist-info/WHEEL: -------------------------------------------------------------------------------- 1 | Wheel-Version: 1.0 2 | Generator: bdist_wheel (0.37.0) 3 | Root-Is-Purelib: true 4 | Tag: py3-none-any 5 | 6 | -------------------------------------------------------------------------------- /layer/python/requests-2.31.0.dist-info/WHEEL: -------------------------------------------------------------------------------- 1 | Wheel-Version: 1.0 2 | Generator: bdist_wheel (0.40.0) 3 | Root-Is-Purelib: true 4 | Tag: py3-none-any 5 | 6 | -------------------------------------------------------------------------------- /layer/python/idna/__pycache__/core.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/idna/__pycache__/core.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/certifi/__pycache__/core.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/certifi/__pycache__/core.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/idna/__pycache__/codec.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/idna/__pycache__/codec.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/idna/__pycache__/compat.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/idna/__pycache__/compat.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/api.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/api.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/idna/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/idna/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/idna/__pycache__/idnadata.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/idna/__pycache__/idnadata.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/idna/__pycache__/intranges.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/idna/__pycache__/intranges.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/idna/__pycache__/uts46data.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/idna/__pycache__/uts46data.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/auth.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/auth.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/certs.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/certs.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/help.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/help.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/hooks.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/hooks.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/utils.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/utils.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/__pycache__/fields.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/__pycache__/fields.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/certifi/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/certifi/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/certifi/__pycache__/__main__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/certifi/__pycache__/__main__.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer/md.cpython-311-darwin.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/charset_normalizer/md.cpython-311-darwin.so -------------------------------------------------------------------------------- /layer/python/idna/__pycache__/package_data.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/idna/__pycache__/package_data.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/adapters.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/adapters.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/compat.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/compat.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/cookies.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/cookies.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/models.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/models.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/packages.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/packages.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/sessions.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/sessions.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/__pycache__/_version.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/__pycache__/_version.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/__pycache__/filepost.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/__pycache__/filepost.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/__pycache__/response.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/__pycache__/response.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/util/__pycache__/ssl_.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/util/__pycache__/ssl_.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/util/__pycache__/url.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/util/__pycache__/url.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/util/__pycache__/util.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/util/__pycache__/util.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/util/__pycache__/wait.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/util/__pycache__/wait.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer-3.1.0.dist-info/WHEEL: -------------------------------------------------------------------------------- 1 | Wheel-Version: 1.0 2 | Generator: bdist_wheel (0.38.4) 3 | Root-Is-Purelib: false 4 | Tag: cp311-cp311-macosx_10_9_x86_64 5 | 6 | -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/exceptions.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/exceptions.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/structures.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/structures.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/__pycache__/connection.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/__pycache__/connection.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/__pycache__/exceptions.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/__pycache__/exceptions.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/__pycache__/poolmanager.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/__pycache__/poolmanager.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/util/__pycache__/proxy.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/util/__pycache__/proxy.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/util/__pycache__/retry.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/util/__pycache__/retry.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer/__pycache__/api.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/charset_normalizer/__pycache__/api.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer/__pycache__/cd.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/charset_normalizer/__pycache__/cd.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer/__pycache__/md.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/charset_normalizer/__pycache__/md.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer/md__mypyc.cpython-311-darwin.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/charset_normalizer/md__mypyc.cpython-311-darwin.so -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/__version__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/__version__.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/status_codes.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/status_codes.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/__pycache__/_collections.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/__pycache__/_collections.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/__pycache__/connectionpool.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/__pycache__/connectionpool.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/contrib/__pycache__/socks.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/contrib/__pycache__/socks.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/util/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/util/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/util/__pycache__/request.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/util/__pycache__/request.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/util/__pycache__/response.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/util/__pycache__/response.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/util/__pycache__/timeout.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/util/__pycache__/timeout.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer/__pycache__/utils.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/charset_normalizer/__pycache__/utils.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/requests/__pycache__/_internal_utils.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/requests/__pycache__/_internal_utils.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/__pycache__/_base_connection.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/__pycache__/_base_connection.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/__pycache__/_request_methods.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/__pycache__/_request_methods.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/contrib/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/contrib/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/util/__pycache__/connection.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/util/__pycache__/connection.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/charset_normalizer/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer/__pycache__/constant.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/charset_normalizer/__pycache__/constant.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer/__pycache__/legacy.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/charset_normalizer/__pycache__/legacy.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer/__pycache__/models.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/charset_normalizer/__pycache__/models.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer/__pycache__/version.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/charset_normalizer/__pycache__/version.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/contrib/__pycache__/pyopenssl.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/contrib/__pycache__/pyopenssl.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/util/__pycache__/ssltransport.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/util/__pycache__/ssltransport.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer/cli/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/charset_normalizer/cli/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/contrib/__pycache__/securetransport.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/contrib/__pycache__/securetransport.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/util/__pycache__/ssl_match_hostname.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/util/__pycache__/ssl_match_hostname.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer/assets/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/charset_normalizer/assets/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/charset_normalizer/cli/__pycache__/normalizer.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/charset_normalizer/cli/__pycache__/normalizer.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorgetovar/serverless-terraform/HEAD/layer/python/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-311.pyc -------------------------------------------------------------------------------- /layer/python/bin/normalizer: -------------------------------------------------------------------------------- 1 | #!/usr/local/opt/python@3.11/bin/python3.11 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from charset_normalizer.cli.normalizer import cli_detect 6 | if __name__ == '__main__': 7 | sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) 8 | sys.exit(cli_detect()) 9 | -------------------------------------------------------------------------------- /layer/python/certifi/__main__.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | from certifi import contents, where 4 | 5 | parser = argparse.ArgumentParser() 6 | parser.add_argument("-c", "--contents", action="store_true") 7 | args = parser.parse_args() 8 | 9 | if args.contents: 10 | print(contents()) 11 | else: 12 | print(where()) 13 | -------------------------------------------------------------------------------- /layer/python/idna/compat.py: -------------------------------------------------------------------------------- 1 | from .core import * 2 | from .codec import * 3 | from typing import Any, Union 4 | 5 | def ToASCII(label: str) -> bytes: 6 | return encode(label) 7 | 8 | def ToUnicode(label: Union[bytes, bytearray]) -> str: 9 | return decode(label) 10 | 11 | def nameprep(s: Any) -> None: 12 | raise NotImplementedError('IDNA 2008 does not utilise nameprep protocol') 13 | 14 | -------------------------------------------------------------------------------- /output.tf: -------------------------------------------------------------------------------- 1 | output "lambda_bucket_name" { 2 | description = "Name of the S3 bucket" 3 | value = aws_s3_bucket.lambda_bucket.id 4 | } 5 | 6 | output "function_name" { 7 | description = "Name of the Lambda function." 8 | value = aws_lambda_function.hello_world.function_name 9 | } 10 | 11 | output "base_url" { 12 | description = "Base URL for API Gateway stage." 13 | value = aws_apigatewayv2_stage.lambda.invoke_url 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Terraform-specific files 2 | .terraform/ 3 | terraform.tfstate 4 | terraform.tfstate.backup 5 | *.tfvars 6 | 7 | 8 | hello_world.zip 9 | response.json 10 | .layer 11 | layer.zip 12 | 13 | # AWS credentials file 14 | .aws/credentials 15 | 16 | .idea/ 17 | 18 | # AWS CLI configuration file 19 | .aws/config 20 | 21 | # AWS SAM (Serverless Application Model) build artifacts 22 | .sam/ 23 | 24 | # Logs and other generated files 25 | *.log 26 | *.log.* 27 | *.swp 28 | .DS_Store 29 | 30 | -------------------------------------------------------------------------------- /layer/python/requests/__version__.py: -------------------------------------------------------------------------------- 1 | # .-. .-. .-. . . .-. .-. .-. .-. 2 | # |( |- |.| | | |- `-. | `-. 3 | # ' ' `-' `-`.`-' `-' `-' ' `-' 4 | 5 | __title__ = "requests" 6 | __description__ = "Python HTTP for Humans." 7 | __url__ = "https://requests.readthedocs.io" 8 | __version__ = "2.31.0" 9 | __build__ = 0x023100 10 | __author__ = "Kenneth Reitz" 11 | __author_email__ = "me@kennethreitz.org" 12 | __license__ = "Apache 2.0" 13 | __copyright__ = "Copyright Kenneth Reitz" 14 | __cake__ = "\u2728 \U0001f370 \u2728" 15 | -------------------------------------------------------------------------------- /layer/python/requests/certs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | requests.certs 5 | ~~~~~~~~~~~~~~ 6 | 7 | This module returns the preferred default CA certificate bundle. There is 8 | only one — the one from the certifi package. 9 | 10 | If you are packaging Requests, e.g., for a Linux distribution or a managed 11 | environment, you can change the definition of where() to return a separately 12 | packaged CA bundle. 13 | """ 14 | from certifi import where 15 | 16 | if __name__ == "__main__": 17 | print(where()) 18 | -------------------------------------------------------------------------------- /hello_world/app.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import requests 4 | 5 | def lambda_handler(event, context): 6 | 7 | response = requests.get("https://test-api.k6.io/public/crocodiles/") 8 | if response.status_code == 200: 9 | data = response.json() 10 | random_info = data 11 | else: 12 | random_info = "No data!" 13 | 14 | return { 15 | "statusCode": 200, 16 | "body": json.dumps({ 17 | "message": "We are going to create a DevOps as a service powered by IA, ChatGPT and GH Copilot", 18 | "random_info": random_info 19 | 20 | }), 21 | } 22 | -------------------------------------------------------------------------------- /terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | 3 | # Set the following environment variables, which Terraform will access in your build environment to configure both the AWS provider and the Terraform Cloud integration for your project: 4 | # Set AWS_ACCESS_KEY_I 5 | # Set AWS_SECRET_ACCESS_KEY 6 | # Set TF_CLOUD_ORGANIZATION 7 | # Set TF_WORKSPACE 8 | # Set TF_TOKEN_app_terraform_io 9 | cloud { 10 | organization = "jorgetovar" 11 | 12 | workspaces { 13 | name = "serverless-terraform" 14 | } 15 | } 16 | 17 | required_providers { 18 | aws = { 19 | source = "hashicorp/aws" 20 | version = "~> 4.4.0" 21 | } 22 | null = { 23 | source = "hashicorp/null" 24 | version = "~> 3.1.0" 25 | } 26 | } 27 | required_version = "~> 1.2" 28 | } -------------------------------------------------------------------------------- /layer/python/requests/hooks.py: -------------------------------------------------------------------------------- 1 | """ 2 | requests.hooks 3 | ~~~~~~~~~~~~~~ 4 | 5 | This module provides the capabilities for the Requests hooks system. 6 | 7 | Available hooks: 8 | 9 | ``response``: 10 | The response generated from a Request. 11 | """ 12 | HOOKS = ["response"] 13 | 14 | 15 | def default_hooks(): 16 | return {event: [] for event in HOOKS} 17 | 18 | 19 | # TODO: response is the only one 20 | 21 | 22 | def dispatch_hook(key, hooks, hook_data, **kwargs): 23 | """Dispatches a hook dictionary on a given piece of data.""" 24 | hooks = hooks or {} 25 | hooks = hooks.get(key) 26 | if hooks: 27 | if hasattr(hooks, "__call__"): 28 | hooks = [hooks] 29 | for hook in hooks: 30 | _hook_data = hook(hook_data, **kwargs) 31 | if _hook_data is not None: 32 | hook_data = _hook_data 33 | return hook_data 34 | -------------------------------------------------------------------------------- /layer/python/idna/__init__.py: -------------------------------------------------------------------------------- 1 | from .package_data import __version__ 2 | from .core import ( 3 | IDNABidiError, 4 | IDNAError, 5 | InvalidCodepoint, 6 | InvalidCodepointContext, 7 | alabel, 8 | check_bidi, 9 | check_hyphen_ok, 10 | check_initial_combiner, 11 | check_label, 12 | check_nfc, 13 | decode, 14 | encode, 15 | ulabel, 16 | uts46_remap, 17 | valid_contextj, 18 | valid_contexto, 19 | valid_label_length, 20 | valid_string_length, 21 | ) 22 | from .intranges import intranges_contain 23 | 24 | __all__ = [ 25 | "IDNABidiError", 26 | "IDNAError", 27 | "InvalidCodepoint", 28 | "InvalidCodepointContext", 29 | "alabel", 30 | "check_bidi", 31 | "check_hyphen_ok", 32 | "check_initial_combiner", 33 | "check_label", 34 | "check_nfc", 35 | "decode", 36 | "encode", 37 | "intranges_contain", 38 | "ulabel", 39 | "uts46_remap", 40 | "valid_contextj", 41 | "valid_contexto", 42 | "valid_label_length", 43 | "valid_string_length", 44 | ] 45 | -------------------------------------------------------------------------------- /layer/python/certifi-2023.5.7.dist-info/RECORD: -------------------------------------------------------------------------------- 1 | certifi-2023.5.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 2 | certifi-2023.5.7.dist-info/LICENSE,sha256=oC9sY4-fuE0G93ZMOrCF2K9-2luTwWbaVDEkeQd8b7A,1052 3 | certifi-2023.5.7.dist-info/METADATA,sha256=fpDUR-Vqju0gWBVplvFKNISMUn1UqTzr6698ttTzSLo,2190 4 | certifi-2023.5.7.dist-info/RECORD,, 5 | certifi-2023.5.7.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 6 | certifi-2023.5.7.dist-info/top_level.txt,sha256=KMu4vUCfsjLrkPbSNdgdekS-pVJzBAJFO__nI8NF6-U,8 7 | certifi/__init__.py,sha256=q5ePznlfOw-XYIOV6RTnh45yS9haN-Nb1d__4QXc3g0,94 8 | certifi/__main__.py,sha256=xBBoj905TUWBLRGANOcf7oi6e-3dMP4cEoG9OyMs11g,243 9 | certifi/__pycache__/__init__.cpython-311.pyc,, 10 | certifi/__pycache__/__main__.cpython-311.pyc,, 11 | certifi/__pycache__/core.cpython-311.pyc,, 12 | certifi/cacert.pem,sha256=swFTXcpJHZgU6ij6oyCsehnQ9dlCN5lvoKO1qTZDJRQ,278952 13 | certifi/core.py,sha256=lhewz0zFb2b4ULsQurElmloYwQoecjWzPqY67P8T7iM,4219 14 | certifi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 15 | -------------------------------------------------------------------------------- /layer/python/requests/packages.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | try: 4 | import chardet 5 | except ImportError: 6 | import warnings 7 | 8 | import charset_normalizer as chardet 9 | 10 | warnings.filterwarnings("ignore", "Trying to detect", module="charset_normalizer") 11 | 12 | # This code exists for backwards compatibility reasons. 13 | # I don't like it either. Just look the other way. :) 14 | 15 | for package in ("urllib3", "idna"): 16 | locals()[package] = __import__(package) 17 | # This traversal is apparently necessary such that the identities are 18 | # preserved (requests.packages.urllib3.* is urllib3.*) 19 | for mod in list(sys.modules): 20 | if mod == package or mod.startswith(f"{package}."): 21 | sys.modules[f"requests.packages.{mod}"] = sys.modules[mod] 22 | 23 | target = chardet.__name__ 24 | for mod in list(sys.modules): 25 | if mod == target or mod.startswith(f"{target}."): 26 | target = target.replace(target, "chardet") 27 | sys.modules[f"requests.packages.{target}"] = sys.modules[mod] 28 | # Kinda cool, though, right? 29 | -------------------------------------------------------------------------------- /layer/python/certifi-2023.5.7.dist-info/LICENSE: -------------------------------------------------------------------------------- 1 | This package contains a modified version of ca-bundle.crt: 2 | 3 | ca-bundle.crt -- Bundle of CA Root Certificates 4 | 5 | Certificate data from Mozilla as of: Thu Nov 3 19:04:19 2011# 6 | This is a bundle of X.509 certificates of public Certificate Authorities 7 | (CA). These were automatically extracted from Mozilla's root certificates 8 | file (certdata.txt). This file can be found in the mozilla source tree: 9 | https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt 10 | It contains the certificates in PEM format and therefore 11 | can be directly used with curl / libcurl / php_curl, or with 12 | an Apache+mod_ssl webserver for SSL client authentication. 13 | Just configure this file as the SSLCACertificateFile.# 14 | 15 | ***** BEGIN LICENSE BLOCK ***** 16 | This Source Code Form is subject to the terms of the Mozilla Public License, 17 | v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain 18 | one at http://mozilla.org/MPL/2.0/. 19 | 20 | ***** END LICENSE BLOCK ***** 21 | @(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $ 22 | -------------------------------------------------------------------------------- /layer/python/charset_normalizer-3.1.0.dist-info/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 TAHRI Ahmed R. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /layer/python/urllib3-2.0.2.dist-info/licenses/LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2008-2020 Andrey Petrov and contributors (see CONTRIBUTORS.txt) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /layer/python/urllib3/util/__init__.py: -------------------------------------------------------------------------------- 1 | # For backwards compatibility, provide imports that used to be here. 2 | from __future__ import annotations 3 | 4 | from .connection import is_connection_dropped 5 | from .request import SKIP_HEADER, SKIPPABLE_HEADERS, make_headers 6 | from .response import is_fp_closed 7 | from .retry import Retry 8 | from .ssl_ import ( 9 | ALPN_PROTOCOLS, 10 | IS_PYOPENSSL, 11 | IS_SECURETRANSPORT, 12 | SSLContext, 13 | assert_fingerprint, 14 | create_urllib3_context, 15 | resolve_cert_reqs, 16 | resolve_ssl_version, 17 | ssl_wrap_socket, 18 | ) 19 | from .timeout import Timeout 20 | from .url import Url, parse_url 21 | from .wait import wait_for_read, wait_for_write 22 | 23 | __all__ = ( 24 | "IS_PYOPENSSL", 25 | "IS_SECURETRANSPORT", 26 | "SSLContext", 27 | "ALPN_PROTOCOLS", 28 | "Retry", 29 | "Timeout", 30 | "Url", 31 | "assert_fingerprint", 32 | "create_urllib3_context", 33 | "is_connection_dropped", 34 | "is_fp_closed", 35 | "parse_url", 36 | "make_headers", 37 | "resolve_cert_reqs", 38 | "resolve_ssl_version", 39 | "ssl_wrap_socket", 40 | "wait_for_read", 41 | "wait_for_write", 42 | "SKIP_HEADER", 43 | "SKIPPABLE_HEADERS", 44 | ) 45 | -------------------------------------------------------------------------------- /layer/python/urllib3/util/util.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import typing 4 | from types import TracebackType 5 | 6 | 7 | def to_bytes( 8 | x: str | bytes, encoding: str | None = None, errors: str | None = None 9 | ) -> bytes: 10 | if isinstance(x, bytes): 11 | return x 12 | elif not isinstance(x, str): 13 | raise TypeError(f"not expecting type {type(x).__name__}") 14 | if encoding or errors: 15 | return x.encode(encoding or "utf-8", errors=errors or "strict") 16 | return x.encode() 17 | 18 | 19 | def to_str( 20 | x: str | bytes, encoding: str | None = None, errors: str | None = None 21 | ) -> str: 22 | if isinstance(x, str): 23 | return x 24 | elif not isinstance(x, bytes): 25 | raise TypeError(f"not expecting type {type(x).__name__}") 26 | if encoding or errors: 27 | return x.decode(encoding or "utf-8", errors=errors or "strict") 28 | return x.decode() 29 | 30 | 31 | def reraise( 32 | tp: type[BaseException] | None, 33 | value: BaseException, 34 | tb: TracebackType | None = None, 35 | ) -> typing.NoReturn: 36 | try: 37 | if value.__traceback__ is not tb: 38 | raise value.with_traceback(tb) 39 | raise value 40 | finally: 41 | value = None # type: ignore[assignment] 42 | tb = None 43 | -------------------------------------------------------------------------------- /layer/python/urllib3/util/proxy.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import typing 4 | 5 | from .url import Url 6 | 7 | if typing.TYPE_CHECKING: 8 | from ..connection import ProxyConfig 9 | 10 | 11 | def connection_requires_http_tunnel( 12 | proxy_url: Url | None = None, 13 | proxy_config: ProxyConfig | None = None, 14 | destination_scheme: str | None = None, 15 | ) -> bool: 16 | """ 17 | Returns True if the connection requires an HTTP CONNECT through the proxy. 18 | 19 | :param URL proxy_url: 20 | URL of the proxy. 21 | :param ProxyConfig proxy_config: 22 | Proxy configuration from poolmanager.py 23 | :param str destination_scheme: 24 | The scheme of the destination. (i.e https, http, etc) 25 | """ 26 | # If we're not using a proxy, no way to use a tunnel. 27 | if proxy_url is None: 28 | return False 29 | 30 | # HTTP destinations never require tunneling, we always forward. 31 | if destination_scheme == "http": 32 | return False 33 | 34 | # Support for forwarding with HTTPS proxies and HTTPS destinations. 35 | if ( 36 | proxy_url.scheme == "https" 37 | and proxy_config 38 | and proxy_config.use_forwarding_for_https 39 | ): 40 | return False 41 | 42 | # Otherwise always use a tunnel. 43 | return True 44 | -------------------------------------------------------------------------------- /layer/python/idna-3.4.dist-info/RECORD: -------------------------------------------------------------------------------- 1 | idna-3.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 2 | idna-3.4.dist-info/LICENSE.md,sha256=otbk2UC9JNvnuWRc3hmpeSzFHbeuDVrNMBrIYMqj6DY,1523 3 | idna-3.4.dist-info/METADATA,sha256=8aLSf9MFS7oB26pZh2hprg7eJp0UJSc-3rpf_evp4DA,9830 4 | idna-3.4.dist-info/RECORD,, 5 | idna-3.4.dist-info/WHEEL,sha256=4TfKIB_xu-04bc2iKz6_zFt-gEFEEDU_31HGhqzOCE8,81 6 | idna/__init__.py,sha256=KJQN1eQBr8iIK5SKrJ47lXvxG0BJ7Lm38W4zT0v_8lk,849 7 | idna/__pycache__/__init__.cpython-311.pyc,, 8 | idna/__pycache__/codec.cpython-311.pyc,, 9 | idna/__pycache__/compat.cpython-311.pyc,, 10 | idna/__pycache__/core.cpython-311.pyc,, 11 | idna/__pycache__/idnadata.cpython-311.pyc,, 12 | idna/__pycache__/intranges.cpython-311.pyc,, 13 | idna/__pycache__/package_data.cpython-311.pyc,, 14 | idna/__pycache__/uts46data.cpython-311.pyc,, 15 | idna/codec.py,sha256=6ly5odKfqrytKT9_7UrlGklHnf1DSK2r9C6cSM4sa28,3374 16 | idna/compat.py,sha256=0_sOEUMT4CVw9doD3vyRhX80X19PwqFoUBs7gWsFME4,321 17 | idna/core.py,sha256=1JxchwKzkxBSn7R_oCE12oBu3eVux0VzdxolmIad24M,12950 18 | idna/idnadata.py,sha256=xUjqKqiJV8Ho_XzBpAtv5JFoVPSupK-SUXvtjygUHqw,44375 19 | idna/intranges.py,sha256=YBr4fRYuWH7kTKS2tXlFjM24ZF1Pdvcir-aywniInqg,1881 20 | idna/package_data.py,sha256=C_jHJzmX8PI4xq0jpzmcTMxpb5lDsq4o5VyxQzlVrZE,21 21 | idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 22 | idna/uts46data.py,sha256=zvjZU24s58_uAS850Mcd0NnD0X7_gCMAMjzWNIeUJdc,206539 23 | -------------------------------------------------------------------------------- /layer/python/idna-3.4.dist-info/LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2013-2021, Kim Davies 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /layer/python/charset_normalizer/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Charset-Normalizer 4 | ~~~~~~~~~~~~~~ 5 | The Real First Universal Charset Detector. 6 | A library that helps you read text from an unknown charset encoding. 7 | Motivated by chardet, This package is trying to resolve the issue by taking a new approach. 8 | All IANA character set names for which the Python core library provides codecs are supported. 9 | 10 | Basic usage: 11 | >>> from charset_normalizer import from_bytes 12 | >>> results = from_bytes('Bсеки човек има право на образование. Oбразованието!'.encode('utf_8')) 13 | >>> best_guess = results.best() 14 | >>> str(best_guess) 15 | 'Bсеки човек има право на образование. Oбразованието!' 16 | 17 | Others methods and usages are available - see the full documentation 18 | at . 19 | :copyright: (c) 2021 by Ahmed TAHRI 20 | :license: MIT, see LICENSE for more details. 21 | """ 22 | import logging 23 | 24 | from .api import from_bytes, from_fp, from_path 25 | from .legacy import detect 26 | from .models import CharsetMatch, CharsetMatches 27 | from .utils import set_logging_handler 28 | from .version import VERSION, __version__ 29 | 30 | __all__ = ( 31 | "from_fp", 32 | "from_path", 33 | "from_bytes", 34 | "detect", 35 | "CharsetMatch", 36 | "CharsetMatches", 37 | "__version__", 38 | "VERSION", 39 | "set_logging_handler", 40 | ) 41 | 42 | # Attach a NullHandler to the top level logger by default 43 | # https://docs.python.org/3.3/howto/logging.html#configuring-logging-for-a-library 44 | 45 | logging.getLogger("charset_normalizer").addHandler(logging.NullHandler()) 46 | -------------------------------------------------------------------------------- /layer/python/requests/_internal_utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | requests._internal_utils 3 | ~~~~~~~~~~~~~~ 4 | 5 | Provides utility functions that are consumed internally by Requests 6 | which depend on extremely few external helpers (such as compat) 7 | """ 8 | import re 9 | 10 | from .compat import builtin_str 11 | 12 | _VALID_HEADER_NAME_RE_BYTE = re.compile(rb"^[^:\s][^:\r\n]*$") 13 | _VALID_HEADER_NAME_RE_STR = re.compile(r"^[^:\s][^:\r\n]*$") 14 | _VALID_HEADER_VALUE_RE_BYTE = re.compile(rb"^\S[^\r\n]*$|^$") 15 | _VALID_HEADER_VALUE_RE_STR = re.compile(r"^\S[^\r\n]*$|^$") 16 | 17 | _HEADER_VALIDATORS_STR = (_VALID_HEADER_NAME_RE_STR, _VALID_HEADER_VALUE_RE_STR) 18 | _HEADER_VALIDATORS_BYTE = (_VALID_HEADER_NAME_RE_BYTE, _VALID_HEADER_VALUE_RE_BYTE) 19 | HEADER_VALIDATORS = { 20 | bytes: _HEADER_VALIDATORS_BYTE, 21 | str: _HEADER_VALIDATORS_STR, 22 | } 23 | 24 | 25 | def to_native_string(string, encoding="ascii"): 26 | """Given a string object, regardless of type, returns a representation of 27 | that string in the native string type, encoding and decoding where 28 | necessary. This assumes ASCII unless told otherwise. 29 | """ 30 | if isinstance(string, builtin_str): 31 | out = string 32 | else: 33 | out = string.decode(encoding) 34 | 35 | return out 36 | 37 | 38 | def unicode_is_ascii(u_string): 39 | """Determine if unicode string only contains ASCII characters. 40 | 41 | :param str u_string: unicode string to check. Must be unicode 42 | and not Python 2 `str`. 43 | :rtype: bool 44 | """ 45 | assert isinstance(u_string, str) 46 | try: 47 | u_string.encode("ascii") 48 | return True 49 | except UnicodeEncodeError: 50 | return False 51 | -------------------------------------------------------------------------------- /layer/python/requests/compat.py: -------------------------------------------------------------------------------- 1 | """ 2 | requests.compat 3 | ~~~~~~~~~~~~~~~ 4 | 5 | This module previously handled import compatibility issues 6 | between Python 2 and Python 3. It remains for backwards 7 | compatibility until the next major version. 8 | """ 9 | 10 | try: 11 | import chardet 12 | except ImportError: 13 | import charset_normalizer as chardet 14 | 15 | import sys 16 | 17 | # ------- 18 | # Pythons 19 | # ------- 20 | 21 | # Syntax sugar. 22 | _ver = sys.version_info 23 | 24 | #: Python 2.x? 25 | is_py2 = _ver[0] == 2 26 | 27 | #: Python 3.x? 28 | is_py3 = _ver[0] == 3 29 | 30 | # json/simplejson module import resolution 31 | has_simplejson = False 32 | try: 33 | import simplejson as json 34 | 35 | has_simplejson = True 36 | except ImportError: 37 | import json 38 | 39 | if has_simplejson: 40 | from simplejson import JSONDecodeError 41 | else: 42 | from json import JSONDecodeError 43 | 44 | # Keep OrderedDict for backwards compatibility. 45 | from collections import OrderedDict 46 | from collections.abc import Callable, Mapping, MutableMapping 47 | from http import cookiejar as cookielib 48 | from http.cookies import Morsel 49 | from io import StringIO 50 | 51 | # -------------- 52 | # Legacy Imports 53 | # -------------- 54 | from urllib.parse import ( 55 | quote, 56 | quote_plus, 57 | unquote, 58 | unquote_plus, 59 | urldefrag, 60 | urlencode, 61 | urljoin, 62 | urlparse, 63 | urlsplit, 64 | urlunparse, 65 | ) 66 | from urllib.request import ( 67 | getproxies, 68 | getproxies_environment, 69 | parse_http_list, 70 | proxy_bypass, 71 | proxy_bypass_environment, 72 | ) 73 | 74 | builtin_str = str 75 | str = str 76 | bytes = bytes 77 | basestring = (str, bytes) 78 | numeric_types = (int, float) 79 | integer_types = (int,) 80 | -------------------------------------------------------------------------------- /layer/python/idna/intranges.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a list of integers, made up of (hopefully) a small number of long runs 3 | of consecutive integers, compute a representation of the form 4 | ((start1, end1), (start2, end2) ...). Then answer the question "was x present 5 | in the original list?" in time O(log(# runs)). 6 | """ 7 | 8 | import bisect 9 | from typing import List, Tuple 10 | 11 | def intranges_from_list(list_: List[int]) -> Tuple[int, ...]: 12 | """Represent a list of integers as a sequence of ranges: 13 | ((start_0, end_0), (start_1, end_1), ...), such that the original 14 | integers are exactly those x such that start_i <= x < end_i for some i. 15 | 16 | Ranges are encoded as single integers (start << 32 | end), not as tuples. 17 | """ 18 | 19 | sorted_list = sorted(list_) 20 | ranges = [] 21 | last_write = -1 22 | for i in range(len(sorted_list)): 23 | if i+1 < len(sorted_list): 24 | if sorted_list[i] == sorted_list[i+1]-1: 25 | continue 26 | current_range = sorted_list[last_write+1:i+1] 27 | ranges.append(_encode_range(current_range[0], current_range[-1] + 1)) 28 | last_write = i 29 | 30 | return tuple(ranges) 31 | 32 | def _encode_range(start: int, end: int) -> int: 33 | return (start << 32) | end 34 | 35 | def _decode_range(r: int) -> Tuple[int, int]: 36 | return (r >> 32), (r & ((1 << 32) - 1)) 37 | 38 | 39 | def intranges_contain(int_: int, ranges: Tuple[int, ...]) -> bool: 40 | """Determine if `int_` falls into one of the ranges in `ranges`.""" 41 | tuple_ = _encode_range(int_, 0) 42 | pos = bisect.bisect_left(ranges, tuple_) 43 | # we could be immediately ahead of a tuple (start, end) 44 | # with start < int_ <= end 45 | if pos > 0: 46 | left, right = _decode_range(ranges[pos-1]) 47 | if left <= int_ < right: 48 | return True 49 | # or we could be immediately behind a tuple (int_, end) 50 | if pos < len(ranges): 51 | left, _ = _decode_range(ranges[pos]) 52 | if left == int_: 53 | return True 54 | return False 55 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | jobs: 4 | plan-apply: 5 | working_directory: /tmp/project 6 | docker: 7 | - image: docker.mirror.hashicorp.services/hashicorp/terraform:light 8 | steps: 9 | - checkout 10 | - run: 11 | name: Terraform init & plan 12 | command: | 13 | terraform init -input=false 14 | terraform plan 15 | - persist_to_workspace: 16 | root: . 17 | paths: 18 | - . 19 | apply: 20 | docker: 21 | - image: docker.mirror.hashicorp.services/hashicorp/terraform:light 22 | steps: 23 | - attach_workspace: 24 | at: . 25 | - run: 26 | name: Terraform apply 27 | command: | 28 | terraform apply -auto-approve 29 | - persist_to_workspace: 30 | root: . 31 | paths: 32 | - . 33 | plan-destroy: 34 | docker: 35 | - image: docker.mirror.hashicorp.services/hashicorp/terraform:light 36 | steps: 37 | - attach_workspace: 38 | at: . 39 | - run: 40 | name: Terraform create destroy plan 41 | command: | 42 | terraform plan -destroy 43 | - persist_to_workspace: 44 | root: . 45 | paths: 46 | - . 47 | destroy: 48 | docker: 49 | - image: docker.mirror.hashicorp.services/hashicorp/terraform:light 50 | steps: 51 | - attach_workspace: 52 | at: . 53 | - run: 54 | name: Terraform destroy 55 | command: | 56 | terraform apply -auto-approve 57 | 58 | workflows: 59 | 60 | manage-infra: 61 | jobs: 62 | - plan-apply: 63 | filters: 64 | branches: 65 | only: 66 | - main 67 | - hold-apply: 68 | type: approval 69 | requires: 70 | - plan-apply 71 | - apply: 72 | requires: 73 | - hold-apply 74 | - plan-destroy: 75 | filters: 76 | branches: 77 | only: 78 | - main 79 | - hold-destroy: 80 | type: approval 81 | requires: 82 | - plan-destroy 83 | - destroy: 84 | requires: 85 | - hold-destroy -------------------------------------------------------------------------------- /layer/python/charset_normalizer/legacy.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict, Optional, Union 2 | from warnings import warn 3 | 4 | from .api import from_bytes 5 | from .constant import CHARDET_CORRESPONDENCE 6 | 7 | 8 | def detect( 9 | byte_str: bytes, should_rename_legacy: bool = False, **kwargs: Any 10 | ) -> Dict[str, Optional[Union[str, float]]]: 11 | """ 12 | chardet legacy method 13 | Detect the encoding of the given byte string. It should be mostly backward-compatible. 14 | Encoding name will match Chardet own writing whenever possible. (Not on encoding name unsupported by it) 15 | This function is deprecated and should be used to migrate your project easily, consult the documentation for 16 | further information. Not planned for removal. 17 | 18 | :param byte_str: The byte sequence to examine. 19 | :param should_rename_legacy: Should we rename legacy encodings 20 | to their more modern equivalents? 21 | """ 22 | if len(kwargs): 23 | warn( 24 | f"charset-normalizer disregard arguments '{','.join(list(kwargs.keys()))}' in legacy function detect()" 25 | ) 26 | 27 | if not isinstance(byte_str, (bytearray, bytes)): 28 | raise TypeError( # pragma: nocover 29 | "Expected object of type bytes or bytearray, got: " 30 | "{0}".format(type(byte_str)) 31 | ) 32 | 33 | if isinstance(byte_str, bytearray): 34 | byte_str = bytes(byte_str) 35 | 36 | r = from_bytes(byte_str).best() 37 | 38 | encoding = r.encoding if r is not None else None 39 | language = r.language if r is not None and r.language != "Unknown" else "" 40 | confidence = 1.0 - r.chaos if r is not None else None 41 | 42 | # Note: CharsetNormalizer does not return 'UTF-8-SIG' as the sig get stripped in the detection/normalization process 43 | # but chardet does return 'utf-8-sig' and it is a valid codec name. 44 | if r is not None and encoding == "utf_8" and r.bom: 45 | encoding += "_sig" 46 | 47 | if should_rename_legacy is False and encoding in CHARDET_CORRESPONDENCE: 48 | encoding = CHARDET_CORRESPONDENCE[encoding] 49 | 50 | return { 51 | "encoding": encoding, 52 | "language": language, 53 | "confidence": confidence, 54 | } 55 | -------------------------------------------------------------------------------- /layer/python/certifi-2023.5.7.dist-info/METADATA: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: certifi 3 | Version: 2023.5.7 4 | Summary: Python package for providing Mozilla's CA Bundle. 5 | Home-page: https://github.com/certifi/python-certifi 6 | Author: Kenneth Reitz 7 | Author-email: me@kennethreitz.com 8 | License: MPL-2.0 9 | Project-URL: Source, https://github.com/certifi/python-certifi 10 | Platform: UNKNOWN 11 | Classifier: Development Status :: 5 - Production/Stable 12 | Classifier: Intended Audience :: Developers 13 | Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) 14 | Classifier: Natural Language :: English 15 | Classifier: Programming Language :: Python 16 | Classifier: Programming Language :: Python :: 3 17 | Classifier: Programming Language :: Python :: 3 :: Only 18 | Classifier: Programming Language :: Python :: 3.6 19 | Classifier: Programming Language :: Python :: 3.7 20 | Classifier: Programming Language :: Python :: 3.8 21 | Classifier: Programming Language :: Python :: 3.9 22 | Classifier: Programming Language :: Python :: 3.10 23 | Classifier: Programming Language :: Python :: 3.11 24 | Requires-Python: >=3.6 25 | License-File: LICENSE 26 | 27 | Certifi: Python SSL Certificates 28 | ================================ 29 | 30 | Certifi provides Mozilla's carefully curated collection of Root Certificates for 31 | validating the trustworthiness of SSL certificates while verifying the identity 32 | of TLS hosts. It has been extracted from the `Requests`_ project. 33 | 34 | Installation 35 | ------------ 36 | 37 | ``certifi`` is available on PyPI. Simply install it with ``pip``:: 38 | 39 | $ pip install certifi 40 | 41 | Usage 42 | ----- 43 | 44 | To reference the installed certificate authority (CA) bundle, you can use the 45 | built-in function:: 46 | 47 | >>> import certifi 48 | 49 | >>> certifi.where() 50 | '/usr/local/lib/python3.7/site-packages/certifi/cacert.pem' 51 | 52 | Or from the command line:: 53 | 54 | $ python -m certifi 55 | /usr/local/lib/python3.7/site-packages/certifi/cacert.pem 56 | 57 | Enjoy! 58 | 59 | .. _`Requests`: https://requests.readthedocs.io/en/master/ 60 | 61 | Addition/Removal of Certificates 62 | -------------------------------- 63 | 64 | Certifi does not support any addition/removal or other modification of the 65 | CA trust store content. This project is intended to provide a reliable and 66 | highly portable root of trust to python deployments. Look to upstream projects 67 | for methods to use alternate trust. 68 | 69 | 70 | -------------------------------------------------------------------------------- /layer/python/urllib3/filepost.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import binascii 4 | import codecs 5 | import os 6 | import typing 7 | from io import BytesIO 8 | 9 | from .fields import _TYPE_FIELD_VALUE_TUPLE, RequestField 10 | 11 | writer = codecs.lookup("utf-8")[3] 12 | 13 | _TYPE_FIELDS_SEQUENCE = typing.Sequence[ 14 | typing.Union[typing.Tuple[str, _TYPE_FIELD_VALUE_TUPLE], RequestField] 15 | ] 16 | _TYPE_FIELDS = typing.Union[ 17 | _TYPE_FIELDS_SEQUENCE, 18 | typing.Mapping[str, _TYPE_FIELD_VALUE_TUPLE], 19 | ] 20 | 21 | 22 | def choose_boundary() -> str: 23 | """ 24 | Our embarrassingly-simple replacement for mimetools.choose_boundary. 25 | """ 26 | return binascii.hexlify(os.urandom(16)).decode() 27 | 28 | 29 | def iter_field_objects(fields: _TYPE_FIELDS) -> typing.Iterable[RequestField]: 30 | """ 31 | Iterate over fields. 32 | 33 | Supports list of (k, v) tuples and dicts, and lists of 34 | :class:`~urllib3.fields.RequestField`. 35 | 36 | """ 37 | iterable: typing.Iterable[RequestField | tuple[str, _TYPE_FIELD_VALUE_TUPLE]] 38 | 39 | if isinstance(fields, typing.Mapping): 40 | iterable = fields.items() 41 | else: 42 | iterable = fields 43 | 44 | for field in iterable: 45 | if isinstance(field, RequestField): 46 | yield field 47 | else: 48 | yield RequestField.from_tuples(*field) 49 | 50 | 51 | def encode_multipart_formdata( 52 | fields: _TYPE_FIELDS, boundary: str | None = None 53 | ) -> tuple[bytes, str]: 54 | """ 55 | Encode a dictionary of ``fields`` using the multipart/form-data MIME format. 56 | 57 | :param fields: 58 | Dictionary of fields or list of (key, :class:`~urllib3.fields.RequestField`). 59 | Values are processed by :func:`urllib3.fields.RequestField.from_tuples`. 60 | 61 | :param boundary: 62 | If not specified, then a random boundary will be generated using 63 | :func:`urllib3.filepost.choose_boundary`. 64 | """ 65 | body = BytesIO() 66 | if boundary is None: 67 | boundary = choose_boundary() 68 | 69 | for field in iter_field_objects(fields): 70 | body.write(f"--{boundary}\r\n".encode("latin-1")) 71 | 72 | writer(body).write(field.render_headers()) 73 | data = field.data 74 | 75 | if isinstance(data, int): 76 | data = str(data) # Backwards compatibility 77 | 78 | if isinstance(data, str): 79 | writer(body).write(data) 80 | else: 81 | body.write(data) 82 | 83 | body.write(b"\r\n") 84 | 85 | body.write(f"--{boundary}--\r\n".encode("latin-1")) 86 | 87 | content_type = f"multipart/form-data; boundary={boundary}" 88 | 89 | return body.getvalue(), content_type 90 | -------------------------------------------------------------------------------- /layer/python/charset_normalizer-3.1.0.dist-info/RECORD: -------------------------------------------------------------------------------- 1 | ../../bin/normalizer,sha256=AtqGUAjKtrQXUg2BvaV1cxbF3bqy0G7ae5jxko-vphw,269 2 | charset_normalizer-3.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 3 | charset_normalizer-3.1.0.dist-info/LICENSE,sha256=6zGgxaT7Cbik4yBV0lweX5w1iidS_vPNcgIT0cz-4kE,1070 4 | charset_normalizer-3.1.0.dist-info/METADATA,sha256=8lfcrrmtfEq--eZqh8FJzEjptLCEoGXySKruxIms44I,30983 5 | charset_normalizer-3.1.0.dist-info/RECORD,, 6 | charset_normalizer-3.1.0.dist-info/WHEEL,sha256=cU6VF8zMujJUYVnyrnmNyXlIvfRQGwjK6RJnNnUKSGE,111 7 | charset_normalizer-3.1.0.dist-info/entry_points.txt,sha256=uYo8aIGLWv8YgWfSna5HnfY_En4pkF1w4bgawNAXzP0,76 8 | charset_normalizer-3.1.0.dist-info/top_level.txt,sha256=7ASyzePr8_xuZWJsnqJjIBtyV8vhEo0wBCv1MPRRi3Q,19 9 | charset_normalizer/__init__.py,sha256=aAb_F9Zb23pb4NO6TfIfqLXLvf1PjnLBBOuPvQwPA18,1549 10 | charset_normalizer/__pycache__/__init__.cpython-311.pyc,, 11 | charset_normalizer/__pycache__/api.cpython-311.pyc,, 12 | charset_normalizer/__pycache__/cd.cpython-311.pyc,, 13 | charset_normalizer/__pycache__/constant.cpython-311.pyc,, 14 | charset_normalizer/__pycache__/legacy.cpython-311.pyc,, 15 | charset_normalizer/__pycache__/md.cpython-311.pyc,, 16 | charset_normalizer/__pycache__/models.cpython-311.pyc,, 17 | charset_normalizer/__pycache__/utils.cpython-311.pyc,, 18 | charset_normalizer/__pycache__/version.cpython-311.pyc,, 19 | charset_normalizer/api.py,sha256=Vh44rFXztkxCjW7gF2waq8TyRL3mXKX8RwNGB99bhb4,18624 20 | charset_normalizer/assets/__init__.py,sha256=wpRfujN7GJuEE5wHHo3wEDVoJ5ovzRIxsImyimCBfGU,20069 21 | charset_normalizer/assets/__pycache__/__init__.cpython-311.pyc,, 22 | charset_normalizer/cd.py,sha256=mZuiTSKq4XpweSDD2H4T4R3Axtaa-QS0tpEWdpMuAzQ,12554 23 | charset_normalizer/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 24 | charset_normalizer/cli/__pycache__/__init__.cpython-311.pyc,, 25 | charset_normalizer/cli/__pycache__/normalizer.cpython-311.pyc,, 26 | charset_normalizer/cli/normalizer.py,sha256=2F-xURZJzo063Ye-2RLJ2wcmURpbKeAzKwpiws65dAs,9744 27 | charset_normalizer/constant.py,sha256=PmCeoKXqq3ZbCtCUpKHwwFBIv9DXMT_an1yd24q28mA,19101 28 | charset_normalizer/legacy.py,sha256=T-QuVMsMeDiQEk8WSszMrzVJg_14AMeSkmHdRYhdl1k,2071 29 | charset_normalizer/md.cpython-311-darwin.so,sha256=9j8oRMrzLFeoNCGldbnWfNhUuxWPvj6qxddN07zMEBY,33744 30 | charset_normalizer/md.py,sha256=MXPKP_oLbsubulEL_1rxcYKSd5FeEfyEfNNm5O6ADpc,18258 31 | charset_normalizer/md__mypyc.cpython-311-darwin.so,sha256=eiJGCVnXsoaiyd6hh9nDwCatjCcp7XUf0eKEKqMD95w,245296 32 | charset_normalizer/models.py,sha256=mC11wo84l00u2o03TRNX7M5ItBAbPUKKXgJSFxA35GY,11492 33 | charset_normalizer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 34 | charset_normalizer/utils.py,sha256=tKLpquPYQdaRdFRwBo5gPOi06ov8UCJy5X1Pti0Q78U,11544 35 | charset_normalizer/version.py,sha256=bekbdpF_D3BtF-PhbPnA9PNaZaI8kKIgl3LTCD5FmYk,79 36 | -------------------------------------------------------------------------------- /layer/python/requests-2.31.0.dist-info/RECORD: -------------------------------------------------------------------------------- 1 | requests-2.31.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 2 | requests-2.31.0.dist-info/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142 3 | requests-2.31.0.dist-info/METADATA,sha256=eCPokOnbb0FROLrfl0R5EpDvdufsb9CaN4noJH__54I,4634 4 | requests-2.31.0.dist-info/RECORD,, 5 | requests-2.31.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 6 | requests-2.31.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92 7 | requests-2.31.0.dist-info/top_level.txt,sha256=fMSVmHfb5rbGOo6xv-O_tUX6j-WyixssE-SnwcDRxNQ,9 8 | requests/__init__.py,sha256=LvmKhjIz8mHaKXthC2Mv5ykZ1d92voyf3oJpd-VuAig,4963 9 | requests/__pycache__/__init__.cpython-311.pyc,, 10 | requests/__pycache__/__version__.cpython-311.pyc,, 11 | requests/__pycache__/_internal_utils.cpython-311.pyc,, 12 | requests/__pycache__/adapters.cpython-311.pyc,, 13 | requests/__pycache__/api.cpython-311.pyc,, 14 | requests/__pycache__/auth.cpython-311.pyc,, 15 | requests/__pycache__/certs.cpython-311.pyc,, 16 | requests/__pycache__/compat.cpython-311.pyc,, 17 | requests/__pycache__/cookies.cpython-311.pyc,, 18 | requests/__pycache__/exceptions.cpython-311.pyc,, 19 | requests/__pycache__/help.cpython-311.pyc,, 20 | requests/__pycache__/hooks.cpython-311.pyc,, 21 | requests/__pycache__/models.cpython-311.pyc,, 22 | requests/__pycache__/packages.cpython-311.pyc,, 23 | requests/__pycache__/sessions.cpython-311.pyc,, 24 | requests/__pycache__/status_codes.cpython-311.pyc,, 25 | requests/__pycache__/structures.cpython-311.pyc,, 26 | requests/__pycache__/utils.cpython-311.pyc,, 27 | requests/__version__.py,sha256=ssI3Ezt7PaxgkOW45GhtwPUclo_SO_ygtIm4A74IOfw,435 28 | requests/_internal_utils.py,sha256=nMQymr4hs32TqVo5AbCrmcJEhvPUh7xXlluyqwslLiQ,1495 29 | requests/adapters.py,sha256=v_FmjU5KZ76k-YttShZYB5RprIzhhL8Y3zgW9p4eBQ8,19553 30 | requests/api.py,sha256=q61xcXq4tmiImrvcSVLTbFyCiD2F-L_-hWKGbz4y8vg,6449 31 | requests/auth.py,sha256=h-HLlVx9j8rKV5hfSAycP2ApOSglTz77R0tz7qCbbEE,10187 32 | requests/certs.py,sha256=Z9Sb410Anv6jUFTyss0jFFhU6xst8ctELqfy8Ev23gw,429 33 | requests/compat.py,sha256=yxntVOSEHGMrn7FNr_32EEam1ZNAdPRdSE13_yaHzTk,1451 34 | requests/cookies.py,sha256=kD3kNEcCj-mxbtf5fJsSaT86eGoEYpD3X0CSgpzl7BM,18560 35 | requests/exceptions.py,sha256=DhveFBclVjTRxhRduVpO-GbMYMID2gmjdLfNEqNpI_U,3811 36 | requests/help.py,sha256=gPX5d_H7Xd88aDABejhqGgl9B1VFRTt5BmiYvL3PzIQ,3875 37 | requests/hooks.py,sha256=CiuysiHA39V5UfcCBXFIx83IrDpuwfN9RcTUgv28ftQ,733 38 | requests/models.py,sha256=-DlKi0or8gFAM6VzutobXvvBW_2wrJuOF5NfndTIddA,35223 39 | requests/packages.py,sha256=DXgv-FJIczZITmv0vEBAhWj4W-5CGCIN_ksvgR17Dvs,957 40 | requests/sessions.py,sha256=-LvTzrPtetSTrR3buxu4XhdgMrJFLB1q5D7P--L2Xhw,30373 41 | requests/status_codes.py,sha256=FvHmT5uH-_uimtRz5hH9VCbt7VV-Nei2J9upbej6j8g,4235 42 | requests/structures.py,sha256=-IbmhVz06S-5aPSZuUthZ6-6D9XOjRuTXHOabY041XM,2912 43 | requests/utils.py,sha256=6sx2X3cIVA8BgWOg8odxFy-_lbWDFETU8HI4fU4Rmqw,33448 44 | -------------------------------------------------------------------------------- /layer/python/requests/structures.py: -------------------------------------------------------------------------------- 1 | """ 2 | requests.structures 3 | ~~~~~~~~~~~~~~~~~~~ 4 | 5 | Data structures that power Requests. 6 | """ 7 | 8 | from collections import OrderedDict 9 | 10 | from .compat import Mapping, MutableMapping 11 | 12 | 13 | class CaseInsensitiveDict(MutableMapping): 14 | """A case-insensitive ``dict``-like object. 15 | 16 | Implements all methods and operations of 17 | ``MutableMapping`` as well as dict's ``copy``. Also 18 | provides ``lower_items``. 19 | 20 | All keys are expected to be strings. The structure remembers the 21 | case of the last key to be set, and ``iter(instance)``, 22 | ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()`` 23 | will contain case-sensitive keys. However, querying and contains 24 | testing is case insensitive:: 25 | 26 | cid = CaseInsensitiveDict() 27 | cid['Accept'] = 'application/json' 28 | cid['aCCEPT'] == 'application/json' # True 29 | list(cid) == ['Accept'] # True 30 | 31 | For example, ``headers['content-encoding']`` will return the 32 | value of a ``'Content-Encoding'`` response header, regardless 33 | of how the header name was originally stored. 34 | 35 | If the constructor, ``.update``, or equality comparison 36 | operations are given keys that have equal ``.lower()``s, the 37 | behavior is undefined. 38 | """ 39 | 40 | def __init__(self, data=None, **kwargs): 41 | self._store = OrderedDict() 42 | if data is None: 43 | data = {} 44 | self.update(data, **kwargs) 45 | 46 | def __setitem__(self, key, value): 47 | # Use the lowercased key for lookups, but store the actual 48 | # key alongside the value. 49 | self._store[key.lower()] = (key, value) 50 | 51 | def __getitem__(self, key): 52 | return self._store[key.lower()][1] 53 | 54 | def __delitem__(self, key): 55 | del self._store[key.lower()] 56 | 57 | def __iter__(self): 58 | return (casedkey for casedkey, mappedvalue in self._store.values()) 59 | 60 | def __len__(self): 61 | return len(self._store) 62 | 63 | def lower_items(self): 64 | """Like iteritems(), but with all lowercase keys.""" 65 | return ((lowerkey, keyval[1]) for (lowerkey, keyval) in self._store.items()) 66 | 67 | def __eq__(self, other): 68 | if isinstance(other, Mapping): 69 | other = CaseInsensitiveDict(other) 70 | else: 71 | return NotImplemented 72 | # Compare insensitively 73 | return dict(self.lower_items()) == dict(other.lower_items()) 74 | 75 | # Copy is required 76 | def copy(self): 77 | return CaseInsensitiveDict(self._store.values()) 78 | 79 | def __repr__(self): 80 | return str(dict(self.items())) 81 | 82 | 83 | class LookupDict(dict): 84 | """Dictionary lookup object.""" 85 | 86 | def __init__(self, name=None): 87 | self.name = name 88 | super().__init__() 89 | 90 | def __repr__(self): 91 | return f"" 92 | 93 | def __getitem__(self, key): 94 | # We allow fall-through here, so values default to None 95 | 96 | return self.__dict__.get(key, None) 97 | 98 | def get(self, key, default=None): 99 | return self.__dict__.get(key, default) 100 | -------------------------------------------------------------------------------- /layer/python/urllib3/util/response.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import http.client as httplib 4 | from email.errors import MultipartInvariantViolationDefect, StartBoundaryNotFoundDefect 5 | 6 | from ..exceptions import HeaderParsingError 7 | 8 | 9 | def is_fp_closed(obj: object) -> bool: 10 | """ 11 | Checks whether a given file-like object is closed. 12 | 13 | :param obj: 14 | The file-like object to check. 15 | """ 16 | 17 | try: 18 | # Check `isclosed()` first, in case Python3 doesn't set `closed`. 19 | # GH Issue #928 20 | return obj.isclosed() # type: ignore[no-any-return, attr-defined] 21 | except AttributeError: 22 | pass 23 | 24 | try: 25 | # Check via the official file-like-object way. 26 | return obj.closed # type: ignore[no-any-return, attr-defined] 27 | except AttributeError: 28 | pass 29 | 30 | try: 31 | # Check if the object is a container for another file-like object that 32 | # gets released on exhaustion (e.g. HTTPResponse). 33 | return obj.fp is None # type: ignore[attr-defined] 34 | except AttributeError: 35 | pass 36 | 37 | raise ValueError("Unable to determine whether fp is closed.") 38 | 39 | 40 | def assert_header_parsing(headers: httplib.HTTPMessage) -> None: 41 | """ 42 | Asserts whether all headers have been successfully parsed. 43 | Extracts encountered errors from the result of parsing headers. 44 | 45 | Only works on Python 3. 46 | 47 | :param http.client.HTTPMessage headers: Headers to verify. 48 | 49 | :raises urllib3.exceptions.HeaderParsingError: 50 | If parsing errors are found. 51 | """ 52 | 53 | # This will fail silently if we pass in the wrong kind of parameter. 54 | # To make debugging easier add an explicit check. 55 | if not isinstance(headers, httplib.HTTPMessage): 56 | raise TypeError(f"expected httplib.Message, got {type(headers)}.") 57 | 58 | unparsed_data = None 59 | 60 | # get_payload is actually email.message.Message.get_payload; 61 | # we're only interested in the result if it's not a multipart message 62 | if not headers.is_multipart(): 63 | payload = headers.get_payload() 64 | 65 | if isinstance(payload, (bytes, str)): 66 | unparsed_data = payload 67 | 68 | # httplib is assuming a response body is available 69 | # when parsing headers even when httplib only sends 70 | # header data to parse_headers() This results in 71 | # defects on multipart responses in particular. 72 | # See: https://github.com/urllib3/urllib3/issues/800 73 | 74 | # So we ignore the following defects: 75 | # - StartBoundaryNotFoundDefect: 76 | # The claimed start boundary was never found. 77 | # - MultipartInvariantViolationDefect: 78 | # A message claimed to be a multipart but no subparts were found. 79 | defects = [ 80 | defect 81 | for defect in headers.defects 82 | if not isinstance( 83 | defect, (StartBoundaryNotFoundDefect, MultipartInvariantViolationDefect) 84 | ) 85 | ] 86 | 87 | if defects or unparsed_data: 88 | raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data) 89 | 90 | 91 | def is_response_to_head(response: httplib.HTTPResponse) -> bool: 92 | """ 93 | Checks whether the request of a response has been a HEAD-request. 94 | 95 | :param http.client.HTTPResponse response: 96 | Response to check if the originating request 97 | used 'HEAD' as a method. 98 | """ 99 | # FIXME: Can we do this somehow without accessing private httplib _method? 100 | method_str = response._method # type: str # type: ignore[attr-defined] 101 | return method_str.upper() == "HEAD" 102 | -------------------------------------------------------------------------------- /layer/python/idna/codec.py: -------------------------------------------------------------------------------- 1 | from .core import encode, decode, alabel, ulabel, IDNAError 2 | import codecs 3 | import re 4 | from typing import Tuple, Optional 5 | 6 | _unicode_dots_re = re.compile('[\u002e\u3002\uff0e\uff61]') 7 | 8 | class Codec(codecs.Codec): 9 | 10 | def encode(self, data: str, errors: str = 'strict') -> Tuple[bytes, int]: 11 | if errors != 'strict': 12 | raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) 13 | 14 | if not data: 15 | return b"", 0 16 | 17 | return encode(data), len(data) 18 | 19 | def decode(self, data: bytes, errors: str = 'strict') -> Tuple[str, int]: 20 | if errors != 'strict': 21 | raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) 22 | 23 | if not data: 24 | return '', 0 25 | 26 | return decode(data), len(data) 27 | 28 | class IncrementalEncoder(codecs.BufferedIncrementalEncoder): 29 | def _buffer_encode(self, data: str, errors: str, final: bool) -> Tuple[str, int]: # type: ignore 30 | if errors != 'strict': 31 | raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) 32 | 33 | if not data: 34 | return "", 0 35 | 36 | labels = _unicode_dots_re.split(data) 37 | trailing_dot = '' 38 | if labels: 39 | if not labels[-1]: 40 | trailing_dot = '.' 41 | del labels[-1] 42 | elif not final: 43 | # Keep potentially unfinished label until the next call 44 | del labels[-1] 45 | if labels: 46 | trailing_dot = '.' 47 | 48 | result = [] 49 | size = 0 50 | for label in labels: 51 | result.append(alabel(label)) 52 | if size: 53 | size += 1 54 | size += len(label) 55 | 56 | # Join with U+002E 57 | result_str = '.'.join(result) + trailing_dot # type: ignore 58 | size += len(trailing_dot) 59 | return result_str, size 60 | 61 | class IncrementalDecoder(codecs.BufferedIncrementalDecoder): 62 | def _buffer_decode(self, data: str, errors: str, final: bool) -> Tuple[str, int]: # type: ignore 63 | if errors != 'strict': 64 | raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) 65 | 66 | if not data: 67 | return ('', 0) 68 | 69 | labels = _unicode_dots_re.split(data) 70 | trailing_dot = '' 71 | if labels: 72 | if not labels[-1]: 73 | trailing_dot = '.' 74 | del labels[-1] 75 | elif not final: 76 | # Keep potentially unfinished label until the next call 77 | del labels[-1] 78 | if labels: 79 | trailing_dot = '.' 80 | 81 | result = [] 82 | size = 0 83 | for label in labels: 84 | result.append(ulabel(label)) 85 | if size: 86 | size += 1 87 | size += len(label) 88 | 89 | result_str = '.'.join(result) + trailing_dot 90 | size += len(trailing_dot) 91 | return (result_str, size) 92 | 93 | 94 | class StreamWriter(Codec, codecs.StreamWriter): 95 | pass 96 | 97 | 98 | class StreamReader(Codec, codecs.StreamReader): 99 | pass 100 | 101 | 102 | def getregentry() -> codecs.CodecInfo: 103 | # Compatibility as a search_function for codecs.register() 104 | return codecs.CodecInfo( 105 | name='idna', 106 | encode=Codec().encode, # type: ignore 107 | decode=Codec().decode, # type: ignore 108 | incrementalencoder=IncrementalEncoder, 109 | incrementaldecoder=IncrementalDecoder, 110 | streamwriter=StreamWriter, 111 | streamreader=StreamReader, 112 | ) 113 | -------------------------------------------------------------------------------- /.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/archive" { 5 | version = "2.3.0" 6 | hashes = [ 7 | "h1:pTPG9Kf1Qg2aPsZLXDa6OvLqsEXaMrKnp0Z4Q/TIBPA=", 8 | "zh:0869128d13abe12b297b0cd13b8767f10d6bf047f5afc4215615aabc39c2eb4f", 9 | "zh:481ed837d63ba3aa45dd8736da83e911e3509dee0e7961bf5c00ed2644f807b3", 10 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 11 | "zh:9f08fe2977e2166849be24fb9f394e4d2697414d463f7996fd0d7beb4e19a29c", 12 | "zh:9fe566deeafd460d27999ca0bbfd85426a5fcfcb40007b23884deb76da127b6f", 13 | "zh:a1bd9a60925d9769e0da322e4523330ee86af9dc2e770cba1d0247a999ef29cb", 14 | "zh:bb4094c8149f74308b22a87e1ac19bcccca76e8ef021b571074d9bccf1c0c6f0", 15 | "zh:c8984c9def239041ce41ec8e19bbd76a49e74ed2024ff736dad60429dee89bcc", 16 | "zh:ea4bb5ae73db1de3a586e62f39106f5e56770804a55aa5e6b4f642df973e0e75", 17 | "zh:f44a9d596ecc3a8c5653f56ba0cd202ad93b49f76767f4608daf7260b813289e", 18 | "zh:f5c5e6cc9f7f070020ab7d95fcc9ed8e20d5cf219978295a71236e22cbb6d508", 19 | "zh:fd2273f51dcc8f43403bf1e425ba9db08a57c3ddcba5ad7a51742ccde21ca611", 20 | ] 21 | } 22 | 23 | provider "registry.terraform.io/hashicorp/aws" { 24 | version = "4.4.0" 25 | constraints = "~> 4.4.0" 26 | hashes = [ 27 | "h1:e9Lg+N7g//WXWDGPPbisC1w34HFIDZrCNkJFbP+z5Rk=", 28 | "zh:087e8e1b9c3d2c9d547181aa88f75fd42d9800eea6d37c0276b1208c427113ff", 29 | "zh:25c3deac14f06a7da5d4d8b56dd5e25a24b5c3bb6bb7a585145d7df1a6e5bc3f", 30 | "zh:5bd23fc03cd51eca3f1e4e4414624dcc4f075eca5cf5aabf06b54b4edded5c50", 31 | "zh:8399507975a422a84b93b24c07db34cc9342f54aa693eace1b451c6b1ab54b87", 32 | "zh:9618bed0832433fee57579d4a001479b08e2092d0c08539edb897f57f6ea0114", 33 | "zh:b0b9060bc367c5fb6175c7ae59382fd6107ab0c0bad6e40cd3205127d8e6717d", 34 | "zh:b160122057659cceb72f78a86483f71d59742502dad23b770dc4248b8e94edd4", 35 | "zh:cb927f4622ef9bf439b867aef760c948839e1cec2ddb8bdba7abfc5183124360", 36 | "zh:e37ce5054a5838eda190f286a62eeb7146087863e38b1a205aa0eb12a5e765b9", 37 | "zh:e38856fd703b2f6e08a35cbe5ddab9a734c9608d2372411bfa6ef1b05ffeb758", 38 | "zh:f342e638d9672d969ed3946b9f0650cf327690b35e0812b2ddae97bd32c2d946", 39 | ] 40 | } 41 | 42 | provider "registry.terraform.io/hashicorp/null" { 43 | version = "3.1.1" 44 | constraints = "~> 3.1.0" 45 | hashes = [ 46 | "h1:Pctug/s/2Hg5FJqjYcTM0kPyx3AoYK1MpRWO0T9V2ns=", 47 | "zh:063466f41f1d9fd0dd93722840c1314f046d8760b1812fa67c34de0afcba5597", 48 | "zh:08c058e367de6debdad35fc24d97131c7cf75103baec8279aba3506a08b53faf", 49 | "zh:73ce6dff935150d6ddc6ac4a10071e02647d10175c173cfe5dca81f3d13d8afe", 50 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 51 | "zh:8fdd792a626413502e68c195f2097352bdc6a0df694f7df350ed784741eb587e", 52 | "zh:976bbaf268cb497400fd5b3c774d218f3933271864345f18deebe4dcbfcd6afa", 53 | "zh:b21b78ca581f98f4cdb7a366b03ae9db23a73dfa7df12c533d7c19b68e9e72e5", 54 | "zh:b7fc0c1615dbdb1d6fd4abb9c7dc7da286631f7ca2299fb9cd4664258ccfbff4", 55 | "zh:d1efc942b2c44345e0c29bc976594cb7278c38cfb8897b344669eafbc3cddf46", 56 | "zh:e356c245b3cd9d4789bab010893566acace682d7db877e52d40fc4ca34a50924", 57 | "zh:ea98802ba92fcfa8cf12cbce2e9e7ebe999afbf8ed47fa45fc847a098d89468b", 58 | "zh:eff8872458806499889f6927b5d954560f3d74bf20b6043409edf94d26cd906f", 59 | ] 60 | } 61 | 62 | provider "registry.terraform.io/hashicorp/random" { 63 | version = "3.5.1" 64 | hashes = [ 65 | "h1:sZ7MTSD4FLekNN2wSNFGpM+5slfvpm5A/NLVZiB7CO0=", 66 | "zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64", 67 | "zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d", 68 | "zh:4d2b219d09abf3b1bb4df93d399ed156cadd61f44ad3baf5cf2954df2fba0831", 69 | "zh:6130bdde527587bbe2dcaa7150363e96dbc5250ea20154176d82bc69df5d4ce3", 70 | "zh:6cc326cd4000f724d3086ee05587e7710f032f94fc9af35e96a386a1c6f2214f", 71 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 72 | "zh:b6d88e1d28cf2dfa24e9fdcc3efc77adcdc1c3c3b5c7ce503a423efbdd6de57b", 73 | "zh:ba74c592622ecbcef9dc2a4d81ed321c4e44cddf7da799faa324da9bf52a22b2", 74 | "zh:c7c5cde98fe4ef1143bd1b3ec5dc04baf0d4cc3ca2c5c7d40d17c0e9b2076865", 75 | "zh:dac4bad52c940cd0dfc27893507c1e92393846b024c5a9db159a93c534a3da03", 76 | "zh:de8febe2a2acd9ac454b844a4106ed295ae9520ef54dc8ed2faf29f12716b602", 77 | "zh:eab0d0495e7e711cca367f7d4df6e322e6c562fc52151ec931176115b83ed014", 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /layer/python/requests/exceptions.py: -------------------------------------------------------------------------------- 1 | """ 2 | requests.exceptions 3 | ~~~~~~~~~~~~~~~~~~~ 4 | 5 | This module contains the set of Requests' exceptions. 6 | """ 7 | from urllib3.exceptions import HTTPError as BaseHTTPError 8 | 9 | from .compat import JSONDecodeError as CompatJSONDecodeError 10 | 11 | 12 | class RequestException(IOError): 13 | """There was an ambiguous exception that occurred while handling your 14 | request. 15 | """ 16 | 17 | def __init__(self, *args, **kwargs): 18 | """Initialize RequestException with `request` and `response` objects.""" 19 | response = kwargs.pop("response", None) 20 | self.response = response 21 | self.request = kwargs.pop("request", None) 22 | if response is not None and not self.request and hasattr(response, "request"): 23 | self.request = self.response.request 24 | super().__init__(*args, **kwargs) 25 | 26 | 27 | class InvalidJSONError(RequestException): 28 | """A JSON error occurred.""" 29 | 30 | 31 | class JSONDecodeError(InvalidJSONError, CompatJSONDecodeError): 32 | """Couldn't decode the text into json""" 33 | 34 | def __init__(self, *args, **kwargs): 35 | """ 36 | Construct the JSONDecodeError instance first with all 37 | args. Then use it's args to construct the IOError so that 38 | the json specific args aren't used as IOError specific args 39 | and the error message from JSONDecodeError is preserved. 40 | """ 41 | CompatJSONDecodeError.__init__(self, *args) 42 | InvalidJSONError.__init__(self, *self.args, **kwargs) 43 | 44 | 45 | class HTTPError(RequestException): 46 | """An HTTP error occurred.""" 47 | 48 | 49 | class ConnectionError(RequestException): 50 | """A Connection error occurred.""" 51 | 52 | 53 | class ProxyError(ConnectionError): 54 | """A proxy error occurred.""" 55 | 56 | 57 | class SSLError(ConnectionError): 58 | """An SSL error occurred.""" 59 | 60 | 61 | class Timeout(RequestException): 62 | """The request timed out. 63 | 64 | Catching this error will catch both 65 | :exc:`~requests.exceptions.ConnectTimeout` and 66 | :exc:`~requests.exceptions.ReadTimeout` errors. 67 | """ 68 | 69 | 70 | class ConnectTimeout(ConnectionError, Timeout): 71 | """The request timed out while trying to connect to the remote server. 72 | 73 | Requests that produced this error are safe to retry. 74 | """ 75 | 76 | 77 | class ReadTimeout(Timeout): 78 | """The server did not send any data in the allotted amount of time.""" 79 | 80 | 81 | class URLRequired(RequestException): 82 | """A valid URL is required to make a request.""" 83 | 84 | 85 | class TooManyRedirects(RequestException): 86 | """Too many redirects.""" 87 | 88 | 89 | class MissingSchema(RequestException, ValueError): 90 | """The URL scheme (e.g. http or https) is missing.""" 91 | 92 | 93 | class InvalidSchema(RequestException, ValueError): 94 | """The URL scheme provided is either invalid or unsupported.""" 95 | 96 | 97 | class InvalidURL(RequestException, ValueError): 98 | """The URL provided was somehow invalid.""" 99 | 100 | 101 | class InvalidHeader(RequestException, ValueError): 102 | """The header value provided was somehow invalid.""" 103 | 104 | 105 | class InvalidProxyURL(InvalidURL): 106 | """The proxy URL provided is invalid.""" 107 | 108 | 109 | class ChunkedEncodingError(RequestException): 110 | """The server declared chunked encoding but sent an invalid chunk.""" 111 | 112 | 113 | class ContentDecodingError(RequestException, BaseHTTPError): 114 | """Failed to decode response content.""" 115 | 116 | 117 | class StreamConsumedError(RequestException, TypeError): 118 | """The content for this response was already consumed.""" 119 | 120 | 121 | class RetryError(RequestException): 122 | """Custom retries logic failed""" 123 | 124 | 125 | class UnrewindableBodyError(RequestException): 126 | """Requests encountered an error when trying to rewind a body.""" 127 | 128 | 129 | # Warnings 130 | 131 | 132 | class RequestsWarning(Warning): 133 | """Base warning for Requests.""" 134 | 135 | 136 | class FileModeWarning(RequestsWarning, DeprecationWarning): 137 | """A file was opened in text mode, but Requests determined its binary length.""" 138 | 139 | 140 | class RequestsDependencyWarning(RequestsWarning): 141 | """An imported dependency doesn't match the expected version range.""" 142 | -------------------------------------------------------------------------------- /layer/python/requests/help.py: -------------------------------------------------------------------------------- 1 | """Module containing bug report helper(s).""" 2 | 3 | import json 4 | import platform 5 | import ssl 6 | import sys 7 | 8 | import idna 9 | import urllib3 10 | 11 | from . import __version__ as requests_version 12 | 13 | try: 14 | import charset_normalizer 15 | except ImportError: 16 | charset_normalizer = None 17 | 18 | try: 19 | import chardet 20 | except ImportError: 21 | chardet = None 22 | 23 | try: 24 | from urllib3.contrib import pyopenssl 25 | except ImportError: 26 | pyopenssl = None 27 | OpenSSL = None 28 | cryptography = None 29 | else: 30 | import cryptography 31 | import OpenSSL 32 | 33 | 34 | def _implementation(): 35 | """Return a dict with the Python implementation and version. 36 | 37 | Provide both the name and the version of the Python implementation 38 | currently running. For example, on CPython 3.10.3 it will return 39 | {'name': 'CPython', 'version': '3.10.3'}. 40 | 41 | This function works best on CPython and PyPy: in particular, it probably 42 | doesn't work for Jython or IronPython. Future investigation should be done 43 | to work out the correct shape of the code for those platforms. 44 | """ 45 | implementation = platform.python_implementation() 46 | 47 | if implementation == "CPython": 48 | implementation_version = platform.python_version() 49 | elif implementation == "PyPy": 50 | implementation_version = "{}.{}.{}".format( 51 | sys.pypy_version_info.major, 52 | sys.pypy_version_info.minor, 53 | sys.pypy_version_info.micro, 54 | ) 55 | if sys.pypy_version_info.releaselevel != "final": 56 | implementation_version = "".join( 57 | [implementation_version, sys.pypy_version_info.releaselevel] 58 | ) 59 | elif implementation == "Jython": 60 | implementation_version = platform.python_version() # Complete Guess 61 | elif implementation == "IronPython": 62 | implementation_version = platform.python_version() # Complete Guess 63 | else: 64 | implementation_version = "Unknown" 65 | 66 | return {"name": implementation, "version": implementation_version} 67 | 68 | 69 | def info(): 70 | """Generate information for a bug report.""" 71 | try: 72 | platform_info = { 73 | "system": platform.system(), 74 | "release": platform.release(), 75 | } 76 | except OSError: 77 | platform_info = { 78 | "system": "Unknown", 79 | "release": "Unknown", 80 | } 81 | 82 | implementation_info = _implementation() 83 | urllib3_info = {"version": urllib3.__version__} 84 | charset_normalizer_info = {"version": None} 85 | chardet_info = {"version": None} 86 | if charset_normalizer: 87 | charset_normalizer_info = {"version": charset_normalizer.__version__} 88 | if chardet: 89 | chardet_info = {"version": chardet.__version__} 90 | 91 | pyopenssl_info = { 92 | "version": None, 93 | "openssl_version": "", 94 | } 95 | if OpenSSL: 96 | pyopenssl_info = { 97 | "version": OpenSSL.__version__, 98 | "openssl_version": f"{OpenSSL.SSL.OPENSSL_VERSION_NUMBER:x}", 99 | } 100 | cryptography_info = { 101 | "version": getattr(cryptography, "__version__", ""), 102 | } 103 | idna_info = { 104 | "version": getattr(idna, "__version__", ""), 105 | } 106 | 107 | system_ssl = ssl.OPENSSL_VERSION_NUMBER 108 | system_ssl_info = {"version": f"{system_ssl:x}" if system_ssl is not None else ""} 109 | 110 | return { 111 | "platform": platform_info, 112 | "implementation": implementation_info, 113 | "system_ssl": system_ssl_info, 114 | "using_pyopenssl": pyopenssl is not None, 115 | "using_charset_normalizer": chardet is None, 116 | "pyOpenSSL": pyopenssl_info, 117 | "urllib3": urllib3_info, 118 | "chardet": chardet_info, 119 | "charset_normalizer": charset_normalizer_info, 120 | "cryptography": cryptography_info, 121 | "idna": idna_info, 122 | "requests": { 123 | "version": requests_version, 124 | }, 125 | } 126 | 127 | 128 | def main(): 129 | """Pretty-print the bug information as JSON.""" 130 | print(json.dumps(info(), sort_keys=True, indent=2)) 131 | 132 | 133 | if __name__ == "__main__": 134 | main() 135 | -------------------------------------------------------------------------------- /layer/python/certifi/core.py: -------------------------------------------------------------------------------- 1 | """ 2 | certifi.py 3 | ~~~~~~~~~~ 4 | 5 | This module returns the installation location of cacert.pem or its contents. 6 | """ 7 | import sys 8 | 9 | 10 | if sys.version_info >= (3, 11): 11 | 12 | from importlib.resources import as_file, files 13 | 14 | _CACERT_CTX = None 15 | _CACERT_PATH = None 16 | 17 | def where() -> str: 18 | # This is slightly terrible, but we want to delay extracting the file 19 | # in cases where we're inside of a zipimport situation until someone 20 | # actually calls where(), but we don't want to re-extract the file 21 | # on every call of where(), so we'll do it once then store it in a 22 | # global variable. 23 | global _CACERT_CTX 24 | global _CACERT_PATH 25 | if _CACERT_PATH is None: 26 | # This is slightly janky, the importlib.resources API wants you to 27 | # manage the cleanup of this file, so it doesn't actually return a 28 | # path, it returns a context manager that will give you the path 29 | # when you enter it and will do any cleanup when you leave it. In 30 | # the common case of not needing a temporary file, it will just 31 | # return the file system location and the __exit__() is a no-op. 32 | # 33 | # We also have to hold onto the actual context manager, because 34 | # it will do the cleanup whenever it gets garbage collected, so 35 | # we will also store that at the global level as well. 36 | _CACERT_CTX = as_file(files("certifi").joinpath("cacert.pem")) 37 | _CACERT_PATH = str(_CACERT_CTX.__enter__()) 38 | 39 | return _CACERT_PATH 40 | 41 | def contents() -> str: 42 | return files("certifi").joinpath("cacert.pem").read_text(encoding="ascii") 43 | 44 | elif sys.version_info >= (3, 7): 45 | 46 | from importlib.resources import path as get_path, read_text 47 | 48 | _CACERT_CTX = None 49 | _CACERT_PATH = None 50 | 51 | def where() -> str: 52 | # This is slightly terrible, but we want to delay extracting the 53 | # file in cases where we're inside of a zipimport situation until 54 | # someone actually calls where(), but we don't want to re-extract 55 | # the file on every call of where(), so we'll do it once then store 56 | # it in a global variable. 57 | global _CACERT_CTX 58 | global _CACERT_PATH 59 | if _CACERT_PATH is None: 60 | # This is slightly janky, the importlib.resources API wants you 61 | # to manage the cleanup of this file, so it doesn't actually 62 | # return a path, it returns a context manager that will give 63 | # you the path when you enter it and will do any cleanup when 64 | # you leave it. In the common case of not needing a temporary 65 | # file, it will just return the file system location and the 66 | # __exit__() is a no-op. 67 | # 68 | # We also have to hold onto the actual context manager, because 69 | # it will do the cleanup whenever it gets garbage collected, so 70 | # we will also store that at the global level as well. 71 | _CACERT_CTX = get_path("certifi", "cacert.pem") 72 | _CACERT_PATH = str(_CACERT_CTX.__enter__()) 73 | 74 | return _CACERT_PATH 75 | 76 | def contents() -> str: 77 | return read_text("certifi", "cacert.pem", encoding="ascii") 78 | 79 | else: 80 | import os 81 | import types 82 | from typing import Union 83 | 84 | Package = Union[types.ModuleType, str] 85 | Resource = Union[str, "os.PathLike"] 86 | 87 | # This fallback will work for Python versions prior to 3.7 that lack the 88 | # importlib.resources module but relies on the existing `where` function 89 | # so won't address issues with environments like PyOxidizer that don't set 90 | # __file__ on modules. 91 | def read_text( 92 | package: Package, 93 | resource: Resource, 94 | encoding: str = 'utf-8', 95 | errors: str = 'strict' 96 | ) -> str: 97 | with open(where(), encoding=encoding) as data: 98 | return data.read() 99 | 100 | # If we don't have importlib.resources, then we will just do the old logic 101 | # of assuming we're on the filesystem and munge the path directly. 102 | def where() -> str: 103 | f = os.path.dirname(__file__) 104 | 105 | return os.path.join(f, "cacert.pem") 106 | 107 | def contents() -> str: 108 | return read_text("certifi", "cacert.pem", encoding="ascii") 109 | -------------------------------------------------------------------------------- /layer/python/requests/status_codes.py: -------------------------------------------------------------------------------- 1 | r""" 2 | The ``codes`` object defines a mapping from common names for HTTP statuses 3 | to their numerical codes, accessible either as attributes or as dictionary 4 | items. 5 | 6 | Example:: 7 | 8 | >>> import requests 9 | >>> requests.codes['temporary_redirect'] 10 | 307 11 | >>> requests.codes.teapot 12 | 418 13 | >>> requests.codes['\o/'] 14 | 200 15 | 16 | Some codes have multiple names, and both upper- and lower-case versions of 17 | the names are allowed. For example, ``codes.ok``, ``codes.OK``, and 18 | ``codes.okay`` all correspond to the HTTP status code 200. 19 | """ 20 | 21 | from .structures import LookupDict 22 | 23 | _codes = { 24 | # Informational. 25 | 100: ("continue",), 26 | 101: ("switching_protocols",), 27 | 102: ("processing",), 28 | 103: ("checkpoint",), 29 | 122: ("uri_too_long", "request_uri_too_long"), 30 | 200: ("ok", "okay", "all_ok", "all_okay", "all_good", "\\o/", "✓"), 31 | 201: ("created",), 32 | 202: ("accepted",), 33 | 203: ("non_authoritative_info", "non_authoritative_information"), 34 | 204: ("no_content",), 35 | 205: ("reset_content", "reset"), 36 | 206: ("partial_content", "partial"), 37 | 207: ("multi_status", "multiple_status", "multi_stati", "multiple_stati"), 38 | 208: ("already_reported",), 39 | 226: ("im_used",), 40 | # Redirection. 41 | 300: ("multiple_choices",), 42 | 301: ("moved_permanently", "moved", "\\o-"), 43 | 302: ("found",), 44 | 303: ("see_other", "other"), 45 | 304: ("not_modified",), 46 | 305: ("use_proxy",), 47 | 306: ("switch_proxy",), 48 | 307: ("temporary_redirect", "temporary_moved", "temporary"), 49 | 308: ( 50 | "permanent_redirect", 51 | "resume_incomplete", 52 | "resume", 53 | ), # "resume" and "resume_incomplete" to be removed in 3.0 54 | # Client Error. 55 | 400: ("bad_request", "bad"), 56 | 401: ("unauthorized",), 57 | 402: ("payment_required", "payment"), 58 | 403: ("forbidden",), 59 | 404: ("not_found", "-o-"), 60 | 405: ("method_not_allowed", "not_allowed"), 61 | 406: ("not_acceptable",), 62 | 407: ("proxy_authentication_required", "proxy_auth", "proxy_authentication"), 63 | 408: ("request_timeout", "timeout"), 64 | 409: ("conflict",), 65 | 410: ("gone",), 66 | 411: ("length_required",), 67 | 412: ("precondition_failed", "precondition"), 68 | 413: ("request_entity_too_large",), 69 | 414: ("request_uri_too_large",), 70 | 415: ("unsupported_media_type", "unsupported_media", "media_type"), 71 | 416: ( 72 | "requested_range_not_satisfiable", 73 | "requested_range", 74 | "range_not_satisfiable", 75 | ), 76 | 417: ("expectation_failed",), 77 | 418: ("im_a_teapot", "teapot", "i_am_a_teapot"), 78 | 421: ("misdirected_request",), 79 | 422: ("unprocessable_entity", "unprocessable"), 80 | 423: ("locked",), 81 | 424: ("failed_dependency", "dependency"), 82 | 425: ("unordered_collection", "unordered"), 83 | 426: ("upgrade_required", "upgrade"), 84 | 428: ("precondition_required", "precondition"), 85 | 429: ("too_many_requests", "too_many"), 86 | 431: ("header_fields_too_large", "fields_too_large"), 87 | 444: ("no_response", "none"), 88 | 449: ("retry_with", "retry"), 89 | 450: ("blocked_by_windows_parental_controls", "parental_controls"), 90 | 451: ("unavailable_for_legal_reasons", "legal_reasons"), 91 | 499: ("client_closed_request",), 92 | # Server Error. 93 | 500: ("internal_server_error", "server_error", "/o\\", "✗"), 94 | 501: ("not_implemented",), 95 | 502: ("bad_gateway",), 96 | 503: ("service_unavailable", "unavailable"), 97 | 504: ("gateway_timeout",), 98 | 505: ("http_version_not_supported", "http_version"), 99 | 506: ("variant_also_negotiates",), 100 | 507: ("insufficient_storage",), 101 | 509: ("bandwidth_limit_exceeded", "bandwidth"), 102 | 510: ("not_extended",), 103 | 511: ("network_authentication_required", "network_auth", "network_authentication"), 104 | } 105 | 106 | codes = LookupDict(name="status_codes") 107 | 108 | 109 | def _init(): 110 | for code, titles in _codes.items(): 111 | for title in titles: 112 | setattr(codes, title, code) 113 | if not title.startswith(("\\", "/")): 114 | setattr(codes, title.upper(), code) 115 | 116 | def doc(code): 117 | names = ", ".join(f"``{n}``" for n in _codes[code]) 118 | return "* %d: %s" % (code, names) 119 | 120 | global __doc__ 121 | __doc__ = ( 122 | __doc__ + "\n" + "\n".join(doc(code) for code in sorted(_codes)) 123 | if __doc__ is not None 124 | else None 125 | ) 126 | 127 | 128 | _init() 129 | -------------------------------------------------------------------------------- /layer/python/urllib3/util/wait.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import select 4 | import socket 5 | from functools import partial 6 | 7 | __all__ = ["wait_for_read", "wait_for_write"] 8 | 9 | 10 | # How should we wait on sockets? 11 | # 12 | # There are two types of APIs you can use for waiting on sockets: the fancy 13 | # modern stateful APIs like epoll/kqueue, and the older stateless APIs like 14 | # select/poll. The stateful APIs are more efficient when you have a lots of 15 | # sockets to keep track of, because you can set them up once and then use them 16 | # lots of times. But we only ever want to wait on a single socket at a time 17 | # and don't want to keep track of state, so the stateless APIs are actually 18 | # more efficient. So we want to use select() or poll(). 19 | # 20 | # Now, how do we choose between select() and poll()? On traditional Unixes, 21 | # select() has a strange calling convention that makes it slow, or fail 22 | # altogether, for high-numbered file descriptors. The point of poll() is to fix 23 | # that, so on Unixes, we prefer poll(). 24 | # 25 | # On Windows, there is no poll() (or at least Python doesn't provide a wrapper 26 | # for it), but that's OK, because on Windows, select() doesn't have this 27 | # strange calling convention; plain select() works fine. 28 | # 29 | # So: on Windows we use select(), and everywhere else we use poll(). We also 30 | # fall back to select() in case poll() is somehow broken or missing. 31 | 32 | 33 | def select_wait_for_socket( 34 | sock: socket.socket, 35 | read: bool = False, 36 | write: bool = False, 37 | timeout: float | None = None, 38 | ) -> bool: 39 | if not read and not write: 40 | raise RuntimeError("must specify at least one of read=True, write=True") 41 | rcheck = [] 42 | wcheck = [] 43 | if read: 44 | rcheck.append(sock) 45 | if write: 46 | wcheck.append(sock) 47 | # When doing a non-blocking connect, most systems signal success by 48 | # marking the socket writable. Windows, though, signals success by marked 49 | # it as "exceptional". We paper over the difference by checking the write 50 | # sockets for both conditions. (The stdlib selectors module does the same 51 | # thing.) 52 | fn = partial(select.select, rcheck, wcheck, wcheck) 53 | rready, wready, xready = fn(timeout) 54 | return bool(rready or wready or xready) 55 | 56 | 57 | def poll_wait_for_socket( 58 | sock: socket.socket, 59 | read: bool = False, 60 | write: bool = False, 61 | timeout: float | None = None, 62 | ) -> bool: 63 | if not read and not write: 64 | raise RuntimeError("must specify at least one of read=True, write=True") 65 | mask = 0 66 | if read: 67 | mask |= select.POLLIN 68 | if write: 69 | mask |= select.POLLOUT 70 | poll_obj = select.poll() 71 | poll_obj.register(sock, mask) 72 | 73 | # For some reason, poll() takes timeout in milliseconds 74 | def do_poll(t: float | None) -> list[tuple[int, int]]: 75 | if t is not None: 76 | t *= 1000 77 | return poll_obj.poll(t) 78 | 79 | return bool(do_poll(timeout)) 80 | 81 | 82 | def _have_working_poll() -> bool: 83 | # Apparently some systems have a select.poll that fails as soon as you try 84 | # to use it, either due to strange configuration or broken monkeypatching 85 | # from libraries like eventlet/greenlet. 86 | try: 87 | poll_obj = select.poll() 88 | poll_obj.poll(0) 89 | except (AttributeError, OSError): 90 | return False 91 | else: 92 | return True 93 | 94 | 95 | def wait_for_socket( 96 | sock: socket.socket, 97 | read: bool = False, 98 | write: bool = False, 99 | timeout: float | None = None, 100 | ) -> bool: 101 | # We delay choosing which implementation to use until the first time we're 102 | # called. We could do it at import time, but then we might make the wrong 103 | # decision if someone goes wild with monkeypatching select.poll after 104 | # we're imported. 105 | global wait_for_socket 106 | if _have_working_poll(): 107 | wait_for_socket = poll_wait_for_socket 108 | elif hasattr(select, "select"): 109 | wait_for_socket = select_wait_for_socket 110 | return wait_for_socket(sock, read, write, timeout) 111 | 112 | 113 | def wait_for_read(sock: socket.socket, timeout: float | None = None) -> bool: 114 | """Waits for reading to be available on a given socket. 115 | Returns True if the socket is readable, or False if the timeout expired. 116 | """ 117 | return wait_for_socket(sock, read=True, timeout=timeout) 118 | 119 | 120 | def wait_for_write(sock: socket.socket, timeout: float | None = None) -> bool: 121 | """Waits for writing to be available on a given socket. 122 | Returns True if the socket is readable, or False if the timeout expired. 123 | """ 124 | return wait_for_socket(sock, write=True, timeout=timeout) 125 | -------------------------------------------------------------------------------- /layer/python/urllib3-2.0.2.dist-info/RECORD: -------------------------------------------------------------------------------- 1 | urllib3-2.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 2 | urllib3-2.0.2.dist-info/METADATA,sha256=Pjt8oQAuTYAFhVvdW74cs-_bOLCNsYkQcysvdm02I0A,6591 3 | urllib3-2.0.2.dist-info/RECORD,, 4 | urllib3-2.0.2.dist-info/WHEEL,sha256=9MIigYJ7D5sOqAPqr0-o6tSMY_nQ7c6kvtvyeUB99YQ,87 5 | urllib3-2.0.2.dist-info/licenses/LICENSE.txt,sha256=w3vxhuJ8-dvpYZ5V7f486nswCRzrPaY8fay-Dm13kHs,1115 6 | urllib3/__init__.py,sha256=C8d5q6u2-WX4z8YpVSxYvLXOHYKNdwg83wmFt1Jn6VE,5028 7 | urllib3/__pycache__/__init__.cpython-311.pyc,, 8 | urllib3/__pycache__/_base_connection.cpython-311.pyc,, 9 | urllib3/__pycache__/_collections.cpython-311.pyc,, 10 | urllib3/__pycache__/_request_methods.cpython-311.pyc,, 11 | urllib3/__pycache__/_version.cpython-311.pyc,, 12 | urllib3/__pycache__/connection.cpython-311.pyc,, 13 | urllib3/__pycache__/connectionpool.cpython-311.pyc,, 14 | urllib3/__pycache__/exceptions.cpython-311.pyc,, 15 | urllib3/__pycache__/fields.cpython-311.pyc,, 16 | urllib3/__pycache__/filepost.cpython-311.pyc,, 17 | urllib3/__pycache__/poolmanager.cpython-311.pyc,, 18 | urllib3/__pycache__/response.cpython-311.pyc,, 19 | urllib3/_base_connection.py,sha256=Zx9RKhOpdcV6EuNZUYpwSy_i3Y-HgBQxl72rprYBD9I,5651 20 | urllib3/_collections.py,sha256=4v2Ahneo61XYuVZUJ5jmpr7F1Qe9_RimrcYUXgJRsxA,15561 21 | urllib3/_request_methods.py,sha256=rTM3FfErdUIVfuqGYJvrnI-HLvBePTLDWKdzosJoyx4,7756 22 | urllib3/_version.py,sha256=FFIgY9hhDK5ayByVAYo6yCpA1X8tcQqqQIX4bF43QDc,98 23 | urllib3/connection.py,sha256=mp220GyxEjgCK4P83hCcu0lzVd6g1Ul2uPe3DLcuSa4,33511 24 | urllib3/connectionpool.py,sha256=Sj7x6xkQHYe9I4xHBOBgkpeFQXog3nmmsLdtQJU3A4k,42961 25 | urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 26 | urllib3/contrib/__pycache__/__init__.cpython-311.pyc,, 27 | urllib3/contrib/__pycache__/pyopenssl.cpython-311.pyc,, 28 | urllib3/contrib/__pycache__/securetransport.cpython-311.pyc,, 29 | urllib3/contrib/__pycache__/socks.cpython-311.pyc,, 30 | urllib3/contrib/_securetransport/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 31 | urllib3/contrib/_securetransport/__pycache__/__init__.cpython-311.pyc,, 32 | urllib3/contrib/_securetransport/__pycache__/bindings.cpython-311.pyc,, 33 | urllib3/contrib/_securetransport/__pycache__/low_level.cpython-311.pyc,, 34 | urllib3/contrib/_securetransport/bindings.py,sha256=N8r6aifbJ-dNS5v-YTPsBl7d0R1GhTi6FiUOdZMGdoo,14452 35 | urllib3/contrib/_securetransport/low_level.py,sha256=14Dhp_jima5J824obfFX5oBORYiAnULtUJ_TB8CEscY,16220 36 | urllib3/contrib/pyopenssl.py,sha256=mezuGWCrkZqlESm2CbLLTmKTt16ky-wADega-HSeq9U,19437 37 | urllib3/contrib/securetransport.py,sha256=tDglxxJvySdRih4JsawEKHitPytWUBJ6glrDrje4yM8,34121 38 | urllib3/contrib/socks.py,sha256=xbqs-P-UHH5L5a_dKvxKetFyV9lKuxkcV9K9Oiyd-gI,7715 39 | urllib3/exceptions.py,sha256=_5S-pqOweY4kaxTaKgYJNPIno-ha0S-D1eD6BfpC_dY,9289 40 | urllib3/fields.py,sha256=XvSMfnSMqeOn9o-6Eb3Fl9MN2MNjiHsmEff_HR5jhEI,11026 41 | urllib3/filepost.py,sha256=-9qJT11cNGjO9dqnI20-oErZuTvNaM18xZZPCjZSbOE,2395 42 | urllib3/poolmanager.py,sha256=xWkMJ-5pVjrhPetDcSUKz9nUWTPvDFtE3tssHL_d9Fs,22160 43 | urllib3/py.typed,sha256=UaCuPFa3H8UAakbt-5G8SPacldTOGvJv18pPjUJ5gDY,93 44 | urllib3/response.py,sha256=qxERPINDB7XPJFlYFAPtiauip4WPdmWvGFnSACZgKBg,39802 45 | urllib3/util/__init__.py,sha256=WsFx_PdwI25do8AcdW-Xj3rvUrI3NsgeQULp6S0sPeU,1051 46 | urllib3/util/__pycache__/__init__.cpython-311.pyc,, 47 | urllib3/util/__pycache__/connection.cpython-311.pyc,, 48 | urllib3/util/__pycache__/proxy.cpython-311.pyc,, 49 | urllib3/util/__pycache__/request.cpython-311.pyc,, 50 | urllib3/util/__pycache__/response.cpython-311.pyc,, 51 | urllib3/util/__pycache__/retry.cpython-311.pyc,, 52 | urllib3/util/__pycache__/ssl_.cpython-311.pyc,, 53 | urllib3/util/__pycache__/ssl_match_hostname.cpython-311.pyc,, 54 | urllib3/util/__pycache__/ssltransport.cpython-311.pyc,, 55 | urllib3/util/__pycache__/timeout.cpython-311.pyc,, 56 | urllib3/util/__pycache__/url.cpython-311.pyc,, 57 | urllib3/util/__pycache__/util.cpython-311.pyc,, 58 | urllib3/util/__pycache__/wait.cpython-311.pyc,, 59 | urllib3/util/connection.py,sha256=QeUUEuNmhznpuKNPL-B0IVOkMdMCu8oJX62OC0Vpzug,4462 60 | urllib3/util/proxy.py,sha256=seP8-Q5B6bB0dMtwPj-YcZZQ30vHuLqRu-tI0JZ2fzs,1148 61 | urllib3/util/request.py,sha256=soce9c9lyFvuDtHEGIDV3lj2kgXZltNHIeszIkVWIlc,8111 62 | urllib3/util/response.py,sha256=vQE639uoEhj1vpjEdxu5lNIhJCSUZkd7pqllUI0BZOA,3374 63 | urllib3/util/retry.py,sha256=tq3pp7hsrinYUh0eGCm_jaO5QPhi1DDFtpyY0irpI_c,18375 64 | urllib3/util/ssl_.py,sha256=R8rR0Bv8_o9B3OQRTwtBZ_7MfF6zXXPrFUaI2zhso9E,18540 65 | urllib3/util/ssl_match_hostname.py,sha256=gaWqixoYtQ_GKO8fcRGFj3VXeMoqyxQQuUTPgWeiL_M,5812 66 | urllib3/util/ssltransport.py,sha256=jGmDxXI-nPBfMib-kjksI5TxUQyooYpekd0sjo1ibdg,9045 67 | urllib3/util/timeout.py,sha256=iXlm7hqG7ij7y27z23giTzsjyg3KIiVyjhsQsiWLDHA,10529 68 | urllib3/util/url.py,sha256=wHORhp80RAXyTlAIkTqLFzSrkU7J34ZDxX-tN65MBZk,15213 69 | urllib3/util/util.py,sha256=j3lbZK1jPyiwD34T8IgJzdWEZVT-4E-0vYIJi9UjeNA,1146 70 | urllib3/util/wait.py,sha256=_ph8IrUR3sqPqi0OopQgJUlH4wzkGeM5CiyA7XGGtmI,4423 71 | -------------------------------------------------------------------------------- /layer/python/urllib3/util/connection.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import socket 4 | import typing 5 | 6 | from ..exceptions import LocationParseError 7 | from .timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT 8 | 9 | _TYPE_SOCKET_OPTIONS = typing.Sequence[typing.Tuple[int, int, typing.Union[int, bytes]]] 10 | 11 | if typing.TYPE_CHECKING: 12 | from .._base_connection import BaseHTTPConnection 13 | 14 | 15 | def is_connection_dropped(conn: BaseHTTPConnection) -> bool: # Platform-specific 16 | """ 17 | Returns True if the connection is dropped and should be closed. 18 | :param conn: :class:`urllib3.connection.HTTPConnection` object. 19 | """ 20 | return not conn.is_connected 21 | 22 | 23 | # This function is copied from socket.py in the Python 2.7 standard 24 | # library test suite. Added to its signature is only `socket_options`. 25 | # One additional modification is that we avoid binding to IPv6 servers 26 | # discovered in DNS if the system doesn't have IPv6 functionality. 27 | def create_connection( 28 | address: tuple[str, int], 29 | timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 30 | source_address: tuple[str, int] | None = None, 31 | socket_options: _TYPE_SOCKET_OPTIONS | None = None, 32 | ) -> socket.socket: 33 | """Connect to *address* and return the socket object. 34 | 35 | Convenience function. Connect to *address* (a 2-tuple ``(host, 36 | port)``) and return the socket object. Passing the optional 37 | *timeout* parameter will set the timeout on the socket instance 38 | before attempting to connect. If no *timeout* is supplied, the 39 | global default timeout setting returned by :func:`socket.getdefaulttimeout` 40 | is used. If *source_address* is set it must be a tuple of (host, port) 41 | for the socket to bind as a source address before making the connection. 42 | An host of '' or port 0 tells the OS to use the default. 43 | """ 44 | 45 | host, port = address 46 | if host.startswith("["): 47 | host = host.strip("[]") 48 | err = None 49 | 50 | # Using the value from allowed_gai_family() in the context of getaddrinfo lets 51 | # us select whether to work with IPv4 DNS records, IPv6 records, or both. 52 | # The original create_connection function always returns all records. 53 | family = allowed_gai_family() 54 | 55 | try: 56 | host.encode("idna") 57 | except UnicodeError: 58 | raise LocationParseError(f"'{host}', label empty or too long") from None 59 | 60 | for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): 61 | af, socktype, proto, canonname, sa = res 62 | sock = None 63 | try: 64 | sock = socket.socket(af, socktype, proto) 65 | 66 | # If provided, set socket level options before connecting. 67 | _set_socket_options(sock, socket_options) 68 | 69 | if timeout is not _DEFAULT_TIMEOUT: 70 | sock.settimeout(timeout) 71 | if source_address: 72 | sock.bind(source_address) 73 | sock.connect(sa) 74 | # Break explicitly a reference cycle 75 | err = None 76 | return sock 77 | 78 | except OSError as _: 79 | err = _ 80 | if sock is not None: 81 | sock.close() 82 | 83 | if err is not None: 84 | try: 85 | raise err 86 | finally: 87 | # Break explicitly a reference cycle 88 | err = None 89 | else: 90 | raise OSError("getaddrinfo returns an empty list") 91 | 92 | 93 | def _set_socket_options( 94 | sock: socket.socket, options: _TYPE_SOCKET_OPTIONS | None 95 | ) -> None: 96 | if options is None: 97 | return 98 | 99 | for opt in options: 100 | sock.setsockopt(*opt) 101 | 102 | 103 | def allowed_gai_family() -> socket.AddressFamily: 104 | """This function is designed to work in the context of 105 | getaddrinfo, where family=socket.AF_UNSPEC is the default and 106 | will perform a DNS search for both IPv6 and IPv4 records.""" 107 | 108 | family = socket.AF_INET 109 | if HAS_IPV6: 110 | family = socket.AF_UNSPEC 111 | return family 112 | 113 | 114 | def _has_ipv6(host: str) -> bool: 115 | """Returns True if the system can bind an IPv6 address.""" 116 | sock = None 117 | has_ipv6 = False 118 | 119 | if socket.has_ipv6: 120 | # has_ipv6 returns true if cPython was compiled with IPv6 support. 121 | # It does not tell us if the system has IPv6 support enabled. To 122 | # determine that we must bind to an IPv6 address. 123 | # https://github.com/urllib3/urllib3/pull/611 124 | # https://bugs.python.org/issue658327 125 | try: 126 | sock = socket.socket(socket.AF_INET6) 127 | sock.bind((host, 0)) 128 | has_ipv6 = True 129 | except Exception: 130 | pass 131 | 132 | if sock: 133 | sock.close() 134 | return has_ipv6 135 | 136 | 137 | HAS_IPV6 = _has_ipv6("::1") 138 | -------------------------------------------------------------------------------- /layer/python/requests-2.31.0.dist-info/METADATA: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: requests 3 | Version: 2.31.0 4 | Summary: Python HTTP for Humans. 5 | Home-page: https://requests.readthedocs.io 6 | Author: Kenneth Reitz 7 | Author-email: me@kennethreitz.org 8 | License: Apache 2.0 9 | Project-URL: Documentation, https://requests.readthedocs.io 10 | Project-URL: Source, https://github.com/psf/requests 11 | Platform: UNKNOWN 12 | Classifier: Development Status :: 5 - Production/Stable 13 | Classifier: Environment :: Web Environment 14 | Classifier: Intended Audience :: Developers 15 | Classifier: License :: OSI Approved :: Apache Software License 16 | Classifier: Natural Language :: English 17 | Classifier: Operating System :: OS Independent 18 | Classifier: Programming Language :: Python 19 | Classifier: Programming Language :: Python :: 3 20 | Classifier: Programming Language :: Python :: 3.7 21 | Classifier: Programming Language :: Python :: 3.8 22 | Classifier: Programming Language :: Python :: 3.9 23 | Classifier: Programming Language :: Python :: 3.10 24 | Classifier: Programming Language :: Python :: 3.11 25 | Classifier: Programming Language :: Python :: 3 :: Only 26 | Classifier: Programming Language :: Python :: Implementation :: CPython 27 | Classifier: Programming Language :: Python :: Implementation :: PyPy 28 | Classifier: Topic :: Internet :: WWW/HTTP 29 | Classifier: Topic :: Software Development :: Libraries 30 | Requires-Python: >=3.7 31 | Description-Content-Type: text/markdown 32 | License-File: LICENSE 33 | Requires-Dist: charset-normalizer (<4,>=2) 34 | Requires-Dist: idna (<4,>=2.5) 35 | Requires-Dist: urllib3 (<3,>=1.21.1) 36 | Requires-Dist: certifi (>=2017.4.17) 37 | Provides-Extra: security 38 | Provides-Extra: socks 39 | Requires-Dist: PySocks (!=1.5.7,>=1.5.6) ; extra == 'socks' 40 | Provides-Extra: use_chardet_on_py3 41 | Requires-Dist: chardet (<6,>=3.0.2) ; extra == 'use_chardet_on_py3' 42 | 43 | # Requests 44 | 45 | **Requests** is a simple, yet elegant, HTTP library. 46 | 47 | ```python 48 | >>> import requests 49 | >>> r = requests.get('https://httpbin.org/basic-auth/user/pass', auth=('user', 'pass')) 50 | >>> r.status_code 51 | 200 52 | >>> r.headers['content-type'] 53 | 'application/json; charset=utf8' 54 | >>> r.encoding 55 | 'utf-8' 56 | >>> r.text 57 | '{"authenticated": true, ...' 58 | >>> r.json() 59 | {'authenticated': True, ...} 60 | ``` 61 | 62 | Requests allows you to send HTTP/1.1 requests extremely easily. There’s no need to manually add query strings to your URLs, or to form-encode your `PUT` & `POST` data — but nowadays, just use the `json` method! 63 | 64 | Requests is one of the most downloaded Python packages today, pulling in around `30M downloads / week`— according to GitHub, Requests is currently [depended upon](https://github.com/psf/requests/network/dependents?package_id=UGFja2FnZS01NzA4OTExNg%3D%3D) by `1,000,000+` repositories. You may certainly put your trust in this code. 65 | 66 | [![Downloads](https://pepy.tech/badge/requests/month)](https://pepy.tech/project/requests) 67 | [![Supported Versions](https://img.shields.io/pypi/pyversions/requests.svg)](https://pypi.org/project/requests) 68 | [![Contributors](https://img.shields.io/github/contributors/psf/requests.svg)](https://github.com/psf/requests/graphs/contributors) 69 | 70 | ## Installing Requests and Supported Versions 71 | 72 | Requests is available on PyPI: 73 | 74 | ```console 75 | $ python -m pip install requests 76 | ``` 77 | 78 | Requests officially supports Python 3.7+. 79 | 80 | ## Supported Features & Best–Practices 81 | 82 | Requests is ready for the demands of building robust and reliable HTTP–speaking applications, for the needs of today. 83 | 84 | - Keep-Alive & Connection Pooling 85 | - International Domains and URLs 86 | - Sessions with Cookie Persistence 87 | - Browser-style TLS/SSL Verification 88 | - Basic & Digest Authentication 89 | - Familiar `dict`–like Cookies 90 | - Automatic Content Decompression and Decoding 91 | - Multi-part File Uploads 92 | - SOCKS Proxy Support 93 | - Connection Timeouts 94 | - Streaming Downloads 95 | - Automatic honoring of `.netrc` 96 | - Chunked HTTP Requests 97 | 98 | ## API Reference and User Guide available on [Read the Docs](https://requests.readthedocs.io) 99 | 100 | [![Read the Docs](https://raw.githubusercontent.com/psf/requests/main/ext/ss.png)](https://requests.readthedocs.io) 101 | 102 | ## Cloning the repository 103 | 104 | When cloning the Requests repository, you may need to add the `-c 105 | fetch.fsck.badTimezone=ignore` flag to avoid an error about a bad commit (see 106 | [this issue](https://github.com/psf/requests/issues/2690) for more background): 107 | 108 | ```shell 109 | git clone -c fetch.fsck.badTimezone=ignore https://github.com/psf/requests.git 110 | ``` 111 | 112 | You can also apply this setting to your global Git config: 113 | 114 | ```shell 115 | git config --global fetch.fsck.badTimezone ignore 116 | ``` 117 | 118 | --- 119 | 120 | [![Kenneth Reitz](https://raw.githubusercontent.com/psf/requests/main/ext/kr.png)](https://kennethreitz.org) [![Python Software Foundation](https://raw.githubusercontent.com/psf/requests/main/ext/psf.png)](https://www.python.org/psf) 121 | 122 | 123 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.aws_region 3 | 4 | default_tags { 5 | tags = { 6 | created-by = "terraform" 7 | } 8 | } 9 | 10 | } 11 | 12 | resource "random_pet" "lambda_bucket_name" { 13 | prefix = "aws-community-builder" 14 | length = 2 15 | } 16 | 17 | resource "aws_s3_bucket" "lambda_bucket" { 18 | bucket = random_pet.lambda_bucket_name.id 19 | } 20 | 21 | resource "aws_s3_bucket_ownership_controls" "s3_bucket_acl_ownership" { 22 | bucket = aws_s3_bucket.lambda_bucket.id 23 | rule { 24 | object_ownership = "ObjectWriter" 25 | } 26 | } 27 | 28 | resource "aws_s3_bucket_acl" "bucket_acl" { 29 | bucket = aws_s3_bucket.lambda_bucket.id 30 | acl = "private" 31 | depends_on = [aws_s3_bucket_ownership_controls.s3_bucket_acl_ownership] 32 | 33 | } 34 | 35 | data "archive_file" "lambda_hello_world" { 36 | type = "zip" 37 | source_dir = "${path.module}/hello_world" 38 | output_path = "${path.module}/hello_world.zip" 39 | } 40 | 41 | resource "aws_s3_object" "lambda_hello_world" { 42 | bucket = aws_s3_bucket.lambda_bucket.id 43 | key = "hello_world.zip" 44 | source = data.archive_file.lambda_hello_world.output_path 45 | etag = filemd5(data.archive_file.lambda_hello_world.output_path) 46 | } 47 | 48 | resource "aws_lambda_function" "hello_world" { 49 | function_name = "HelloCommunityBuilders" 50 | s3_bucket = aws_s3_bucket.lambda_bucket.id 51 | s3_key = aws_s3_object.lambda_hello_world.key 52 | runtime = "python3.9" 53 | handler = "app.lambda_handler" 54 | source_code_hash = data.archive_file.lambda_hello_world.output_base64sha256 55 | role = aws_iam_role.lambda_exec.arn 56 | layers = [aws_lambda_layer_version.lambda_layer.arn] 57 | } 58 | 59 | resource "aws_cloudwatch_log_group" "hello_world" { 60 | name = "/aws/lambda/${aws_lambda_function.hello_world.function_name}" 61 | retention_in_days = 14 62 | } 63 | 64 | resource "aws_iam_role" "lambda_exec" { 65 | name = "serverless_lambda" 66 | 67 | assume_role_policy = jsonencode({ 68 | Version = "2012-10-17" 69 | Statement = [ 70 | { 71 | Action = "sts:AssumeRole" 72 | Effect = "Allow" 73 | Sid = "" 74 | Principal = { 75 | Service = "lambda.amazonaws.com" 76 | } 77 | } 78 | ] 79 | }) 80 | } 81 | 82 | resource "aws_iam_role_policy_attachment" "lambda_policy" { 83 | role = aws_iam_role.lambda_exec.name 84 | policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 85 | } 86 | 87 | resource "aws_apigatewayv2_api" "lambda" { 88 | name = "serverless_lambda_api" 89 | protocol_type = "HTTP" 90 | } 91 | 92 | 93 | resource "aws_apigatewayv2_stage" "lambda" { 94 | api_id = aws_apigatewayv2_api.lambda.id 95 | name = "stage" 96 | auto_deploy = true 97 | 98 | access_log_settings { 99 | destination_arn = aws_cloudwatch_log_group.api_log_group.arn 100 | 101 | format = jsonencode({ 102 | requestId = "$context.requestId" 103 | sourceIp = "$context.identity.sourceIp" 104 | requestTime = "$context.requestTime" 105 | protocol = "$context.protocol" 106 | httpMethod = "$context.httpMethod" 107 | resourcePath = "$context.resourcePath" 108 | routeKey = "$context.routeKey" 109 | status = "$context.status" 110 | responseLength = "$context.responseLength" 111 | integrationErrorMessage = "$context.integrationErrorMessage" 112 | } 113 | ) 114 | } 115 | } 116 | 117 | resource "aws_apigatewayv2_integration" "hello_world" { 118 | api_id = aws_apigatewayv2_api.lambda.id 119 | integration_uri = aws_lambda_function.hello_world.invoke_arn 120 | integration_type = "AWS_PROXY" 121 | integration_method = "POST" 122 | } 123 | 124 | resource "aws_apigatewayv2_route" "hello_world" { 125 | api_id = aws_apigatewayv2_api.lambda.id 126 | route_key = "GET /hello" 127 | target = "integrations/${aws_apigatewayv2_integration.hello_world.id}" 128 | } 129 | 130 | resource "aws_cloudwatch_log_group" "api_log_group" { 131 | name = "/aws/api_gw/${aws_apigatewayv2_api.lambda.name}" 132 | retention_in_days = 14 133 | } 134 | 135 | resource "aws_lambda_permission" "api_lambda_permission" { 136 | statement_id = "AllowExecutionFromAPIGateway" 137 | action = "lambda:InvokeFunction" 138 | function_name = aws_lambda_function.hello_world.function_name 139 | principal = "apigateway.amazonaws.com" 140 | source_arn = "${aws_apigatewayv2_api.lambda.execution_arn}/*/*" 141 | } 142 | 143 | 144 | data "archive_file" "layer" { 145 | type = "zip" 146 | source_dir = "${path.module}/layer" 147 | output_path = "${path.module}/layer.zip" 148 | } 149 | 150 | resource "aws_lambda_layer_version" "lambda_layer" { 151 | filename = data.archive_file.layer.output_path 152 | layer_name = "community-layer" 153 | source_code_hash = data.archive_file.layer.output_base64sha256 154 | compatible_runtimes = ["python3.9"] 155 | compatible_architectures = ["x86_64"] 156 | } 157 | 158 | resource "null_resource" "pip_install" { 159 | triggers = { 160 | shell_hash = sha256(file("${path.module}/hello_world/requirements.txt")) 161 | } 162 | 163 | provisioner "local-exec" { 164 | command = "python3 -m pip install -r ${path.module}/hello_world/requirements.txt -t ${path.module}/layer/python" 165 | } 166 | 167 | depends_on = [data.archive_file.layer] 168 | } 169 | 170 | 171 | -------------------------------------------------------------------------------- /layer/python/urllib3/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python HTTP library with thread-safe connection pooling, file post support, user friendly, and more 3 | """ 4 | 5 | from __future__ import annotations 6 | 7 | # Set default logging handler to avoid "No handler found" warnings. 8 | import logging 9 | import typing 10 | import warnings 11 | from logging import NullHandler 12 | 13 | from . import exceptions 14 | from ._base_connection import _TYPE_BODY 15 | from ._collections import HTTPHeaderDict 16 | from ._version import __version__ 17 | from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, connection_from_url 18 | from .filepost import _TYPE_FIELDS, encode_multipart_formdata 19 | from .poolmanager import PoolManager, ProxyManager, proxy_from_url 20 | from .response import BaseHTTPResponse, HTTPResponse 21 | from .util.request import make_headers 22 | from .util.retry import Retry 23 | from .util.timeout import Timeout 24 | 25 | # Ensure that Python is compiled with OpenSSL 1.1.1+ 26 | # If the 'ssl' module isn't available at all that's 27 | # fine, we only care if the module is available. 28 | try: 29 | import ssl 30 | except ImportError: 31 | pass 32 | else: 33 | # fmt: off 34 | if ( 35 | not ssl.OPENSSL_VERSION.startswith("OpenSSL ") 36 | or ssl.OPENSSL_VERSION_INFO < (1, 1, 1) 37 | ): # Defensive: 38 | raise ImportError( 39 | "urllib3 v2.0 only supports OpenSSL 1.1.1+, currently " 40 | f"the 'ssl' module is compiled with {ssl.OPENSSL_VERSION}. " 41 | "See: https://github.com/urllib3/urllib3/issues/2168" 42 | ) 43 | # fmt: on 44 | 45 | # === NOTE TO REPACKAGERS AND VENDORS === 46 | # Please delete this block, this logic is only 47 | # for urllib3 being distributed via PyPI. 48 | # See: https://github.com/urllib3/urllib3/issues/2680 49 | try: 50 | import urllib3_secure_extra # type: ignore # noqa: F401 51 | except ModuleNotFoundError: 52 | pass 53 | else: 54 | warnings.warn( 55 | "'urllib3[secure]' extra is deprecated and will be removed " 56 | "in urllib3 v2.1.0. Read more in this issue: " 57 | "https://github.com/urllib3/urllib3/issues/2680", 58 | category=DeprecationWarning, 59 | stacklevel=2, 60 | ) 61 | 62 | __author__ = "Andrey Petrov (andrey.petrov@shazow.net)" 63 | __license__ = "MIT" 64 | __version__ = __version__ 65 | 66 | __all__ = ( 67 | "HTTPConnectionPool", 68 | "HTTPHeaderDict", 69 | "HTTPSConnectionPool", 70 | "PoolManager", 71 | "ProxyManager", 72 | "HTTPResponse", 73 | "Retry", 74 | "Timeout", 75 | "add_stderr_logger", 76 | "connection_from_url", 77 | "disable_warnings", 78 | "encode_multipart_formdata", 79 | "make_headers", 80 | "proxy_from_url", 81 | "request", 82 | ) 83 | 84 | logging.getLogger(__name__).addHandler(NullHandler()) 85 | 86 | 87 | def add_stderr_logger( 88 | level: int = logging.DEBUG, 89 | ) -> logging.StreamHandler[typing.TextIO]: 90 | """ 91 | Helper for quickly adding a StreamHandler to the logger. Useful for 92 | debugging. 93 | 94 | Returns the handler after adding it. 95 | """ 96 | # This method needs to be in this __init__.py to get the __name__ correct 97 | # even if urllib3 is vendored within another package. 98 | logger = logging.getLogger(__name__) 99 | handler = logging.StreamHandler() 100 | handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) 101 | logger.addHandler(handler) 102 | logger.setLevel(level) 103 | logger.debug("Added a stderr logging handler to logger: %s", __name__) 104 | return handler 105 | 106 | 107 | # ... Clean up. 108 | del NullHandler 109 | 110 | 111 | # All warning filters *must* be appended unless you're really certain that they 112 | # shouldn't be: otherwise, it's very hard for users to use most Python 113 | # mechanisms to silence them. 114 | # SecurityWarning's always go off by default. 115 | warnings.simplefilter("always", exceptions.SecurityWarning, append=True) 116 | # InsecurePlatformWarning's don't vary between requests, so we keep it default. 117 | warnings.simplefilter("default", exceptions.InsecurePlatformWarning, append=True) 118 | 119 | 120 | def disable_warnings(category: type[Warning] = exceptions.HTTPWarning) -> None: 121 | """ 122 | Helper for quickly disabling all urllib3 warnings. 123 | """ 124 | warnings.simplefilter("ignore", category) 125 | 126 | 127 | _DEFAULT_POOL = PoolManager() 128 | 129 | 130 | def request( 131 | method: str, 132 | url: str, 133 | *, 134 | body: _TYPE_BODY | None = None, 135 | fields: _TYPE_FIELDS | None = None, 136 | headers: typing.Mapping[str, str] | None = None, 137 | preload_content: bool | None = True, 138 | decode_content: bool | None = True, 139 | redirect: bool | None = True, 140 | retries: Retry | bool | int | None = None, 141 | timeout: Timeout | float | int | None = 3, 142 | json: typing.Any | None = None, 143 | ) -> BaseHTTPResponse: 144 | """ 145 | A convenience, top-level request method. It uses a module-global ``PoolManager`` instance. 146 | Therefore, its side effects could be shared across dependencies relying on it. 147 | To avoid side effects create a new ``PoolManager`` instance and use it instead. 148 | The method does not accept low-level ``**urlopen_kw`` keyword arguments. 149 | """ 150 | 151 | return _DEFAULT_POOL.request( 152 | method, 153 | url, 154 | body=body, 155 | fields=fields, 156 | headers=headers, 157 | preload_content=preload_content, 158 | decode_content=decode_content, 159 | redirect=redirect, 160 | retries=retries, 161 | timeout=timeout, 162 | json=json, 163 | ) 164 | -------------------------------------------------------------------------------- /layer/python/requests/__init__.py: -------------------------------------------------------------------------------- 1 | # __ 2 | # /__) _ _ _ _ _/ _ 3 | # / ( (- (/ (/ (- _) / _) 4 | # / 5 | 6 | """ 7 | Requests HTTP Library 8 | ~~~~~~~~~~~~~~~~~~~~~ 9 | 10 | Requests is an HTTP library, written in Python, for human beings. 11 | Basic GET usage: 12 | 13 | >>> import requests 14 | >>> r = requests.get('https://www.python.org') 15 | >>> r.status_code 16 | 200 17 | >>> b'Python is a programming language' in r.content 18 | True 19 | 20 | ... or POST: 21 | 22 | >>> payload = dict(key1='value1', key2='value2') 23 | >>> r = requests.post('https://httpbin.org/post', data=payload) 24 | >>> print(r.text) 25 | { 26 | ... 27 | "form": { 28 | "key1": "value1", 29 | "key2": "value2" 30 | }, 31 | ... 32 | } 33 | 34 | The other HTTP methods are supported - see `requests.api`. Full documentation 35 | is at . 36 | 37 | :copyright: (c) 2017 by Kenneth Reitz. 38 | :license: Apache 2.0, see LICENSE for more details. 39 | """ 40 | 41 | import warnings 42 | 43 | import urllib3 44 | 45 | from .exceptions import RequestsDependencyWarning 46 | 47 | try: 48 | from charset_normalizer import __version__ as charset_normalizer_version 49 | except ImportError: 50 | charset_normalizer_version = None 51 | 52 | try: 53 | from chardet import __version__ as chardet_version 54 | except ImportError: 55 | chardet_version = None 56 | 57 | 58 | def check_compatibility(urllib3_version, chardet_version, charset_normalizer_version): 59 | urllib3_version = urllib3_version.split(".") 60 | assert urllib3_version != ["dev"] # Verify urllib3 isn't installed from git. 61 | 62 | # Sometimes, urllib3 only reports its version as 16.1. 63 | if len(urllib3_version) == 2: 64 | urllib3_version.append("0") 65 | 66 | # Check urllib3 for compatibility. 67 | major, minor, patch = urllib3_version # noqa: F811 68 | major, minor, patch = int(major), int(minor), int(patch) 69 | # urllib3 >= 1.21.1 70 | assert major >= 1 71 | if major == 1: 72 | assert minor >= 21 73 | 74 | # Check charset_normalizer for compatibility. 75 | if chardet_version: 76 | major, minor, patch = chardet_version.split(".")[:3] 77 | major, minor, patch = int(major), int(minor), int(patch) 78 | # chardet_version >= 3.0.2, < 6.0.0 79 | assert (3, 0, 2) <= (major, minor, patch) < (6, 0, 0) 80 | elif charset_normalizer_version: 81 | major, minor, patch = charset_normalizer_version.split(".")[:3] 82 | major, minor, patch = int(major), int(minor), int(patch) 83 | # charset_normalizer >= 2.0.0 < 4.0.0 84 | assert (2, 0, 0) <= (major, minor, patch) < (4, 0, 0) 85 | else: 86 | raise Exception("You need either charset_normalizer or chardet installed") 87 | 88 | 89 | def _check_cryptography(cryptography_version): 90 | # cryptography < 1.3.4 91 | try: 92 | cryptography_version = list(map(int, cryptography_version.split("."))) 93 | except ValueError: 94 | return 95 | 96 | if cryptography_version < [1, 3, 4]: 97 | warning = "Old version of cryptography ({}) may cause slowdown.".format( 98 | cryptography_version 99 | ) 100 | warnings.warn(warning, RequestsDependencyWarning) 101 | 102 | 103 | # Check imported dependencies for compatibility. 104 | try: 105 | check_compatibility( 106 | urllib3.__version__, chardet_version, charset_normalizer_version 107 | ) 108 | except (AssertionError, ValueError): 109 | warnings.warn( 110 | "urllib3 ({}) or chardet ({})/charset_normalizer ({}) doesn't match a supported " 111 | "version!".format( 112 | urllib3.__version__, chardet_version, charset_normalizer_version 113 | ), 114 | RequestsDependencyWarning, 115 | ) 116 | 117 | # Attempt to enable urllib3's fallback for SNI support 118 | # if the standard library doesn't support SNI or the 119 | # 'ssl' library isn't available. 120 | try: 121 | try: 122 | import ssl 123 | except ImportError: 124 | ssl = None 125 | 126 | if not getattr(ssl, "HAS_SNI", False): 127 | from urllib3.contrib import pyopenssl 128 | 129 | pyopenssl.inject_into_urllib3() 130 | 131 | # Check cryptography version 132 | from cryptography import __version__ as cryptography_version 133 | 134 | _check_cryptography(cryptography_version) 135 | except ImportError: 136 | pass 137 | 138 | # urllib3's DependencyWarnings should be silenced. 139 | from urllib3.exceptions import DependencyWarning 140 | 141 | warnings.simplefilter("ignore", DependencyWarning) 142 | 143 | # Set default logging handler to avoid "No handler found" warnings. 144 | import logging 145 | from logging import NullHandler 146 | 147 | from . import packages, utils 148 | from .__version__ import ( 149 | __author__, 150 | __author_email__, 151 | __build__, 152 | __cake__, 153 | __copyright__, 154 | __description__, 155 | __license__, 156 | __title__, 157 | __url__, 158 | __version__, 159 | ) 160 | from .api import delete, get, head, options, patch, post, put, request 161 | from .exceptions import ( 162 | ConnectionError, 163 | ConnectTimeout, 164 | FileModeWarning, 165 | HTTPError, 166 | JSONDecodeError, 167 | ReadTimeout, 168 | RequestException, 169 | Timeout, 170 | TooManyRedirects, 171 | URLRequired, 172 | ) 173 | from .models import PreparedRequest, Request, Response 174 | from .sessions import Session, session 175 | from .status_codes import codes 176 | 177 | logging.getLogger(__name__).addHandler(NullHandler()) 178 | 179 | # FileModeWarnings go off per the default. 180 | warnings.simplefilter("default", FileModeWarning, append=True) 181 | -------------------------------------------------------------------------------- /layer/python/urllib3/_base_connection.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import typing 4 | 5 | from .util.connection import _TYPE_SOCKET_OPTIONS 6 | from .util.timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT 7 | from .util.url import Url 8 | 9 | _TYPE_BODY = typing.Union[bytes, typing.IO[typing.Any], typing.Iterable[bytes], str] 10 | 11 | 12 | class ProxyConfig(typing.NamedTuple): 13 | ssl_context: ssl.SSLContext | None 14 | use_forwarding_for_https: bool 15 | assert_hostname: None | str | Literal[False] 16 | assert_fingerprint: str | None 17 | 18 | 19 | class _ResponseOptions(typing.NamedTuple): 20 | # TODO: Remove this in favor of a better 21 | # HTTP request/response lifecycle tracking. 22 | request_method: str 23 | request_url: str 24 | preload_content: bool 25 | decode_content: bool 26 | enforce_content_length: bool 27 | 28 | 29 | if typing.TYPE_CHECKING: 30 | import ssl 31 | 32 | from typing_extensions import Literal, Protocol 33 | 34 | from .response import BaseHTTPResponse 35 | 36 | class BaseHTTPConnection(Protocol): 37 | default_port: typing.ClassVar[int] 38 | default_socket_options: typing.ClassVar[_TYPE_SOCKET_OPTIONS] 39 | 40 | host: str 41 | port: int 42 | timeout: None | ( 43 | float 44 | ) # Instance doesn't store _DEFAULT_TIMEOUT, must be resolved. 45 | blocksize: int 46 | source_address: tuple[str, int] | None 47 | socket_options: _TYPE_SOCKET_OPTIONS | None 48 | 49 | proxy: Url | None 50 | proxy_config: ProxyConfig | None 51 | 52 | is_verified: bool 53 | proxy_is_verified: bool | None 54 | 55 | def __init__( 56 | self, 57 | host: str, 58 | port: int | None = None, 59 | *, 60 | timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 61 | source_address: tuple[str, int] | None = None, 62 | blocksize: int = 8192, 63 | socket_options: _TYPE_SOCKET_OPTIONS | None = ..., 64 | proxy: Url | None = None, 65 | proxy_config: ProxyConfig | None = None, 66 | ) -> None: 67 | ... 68 | 69 | def set_tunnel( 70 | self, 71 | host: str, 72 | port: int | None = None, 73 | headers: typing.Mapping[str, str] | None = None, 74 | scheme: str = "http", 75 | ) -> None: 76 | ... 77 | 78 | def connect(self) -> None: 79 | ... 80 | 81 | def request( 82 | self, 83 | method: str, 84 | url: str, 85 | body: _TYPE_BODY | None = None, 86 | headers: typing.Mapping[str, str] | None = None, 87 | # We know *at least* botocore is depending on the order of the 88 | # first 3 parameters so to be safe we only mark the later ones 89 | # as keyword-only to ensure we have space to extend. 90 | *, 91 | chunked: bool = False, 92 | preload_content: bool = True, 93 | decode_content: bool = True, 94 | enforce_content_length: bool = True, 95 | ) -> None: 96 | ... 97 | 98 | def getresponse(self) -> BaseHTTPResponse: 99 | ... 100 | 101 | def close(self) -> None: 102 | ... 103 | 104 | @property 105 | def is_closed(self) -> bool: 106 | """Whether the connection either is brand new or has been previously closed. 107 | If this property is True then both ``is_connected`` and ``has_connected_to_proxy`` 108 | properties must be False. 109 | """ 110 | 111 | @property 112 | def is_connected(self) -> bool: 113 | """Whether the connection is actively connected to any origin (proxy or target)""" 114 | 115 | @property 116 | def has_connected_to_proxy(self) -> bool: 117 | """Whether the connection has successfully connected to its proxy. 118 | This returns False if no proxy is in use. Used to determine whether 119 | errors are coming from the proxy layer or from tunnelling to the target origin. 120 | """ 121 | 122 | class BaseHTTPSConnection(BaseHTTPConnection, Protocol): 123 | default_port: typing.ClassVar[int] 124 | default_socket_options: typing.ClassVar[_TYPE_SOCKET_OPTIONS] 125 | 126 | # Certificate verification methods 127 | cert_reqs: int | str | None 128 | assert_hostname: None | str | Literal[False] 129 | assert_fingerprint: str | None 130 | ssl_context: ssl.SSLContext | None 131 | 132 | # Trusted CAs 133 | ca_certs: str | None 134 | ca_cert_dir: str | None 135 | ca_cert_data: None | str | bytes 136 | 137 | # TLS version 138 | ssl_minimum_version: int | None 139 | ssl_maximum_version: int | None 140 | ssl_version: int | str | None # Deprecated 141 | 142 | # Client certificates 143 | cert_file: str | None 144 | key_file: str | None 145 | key_password: str | None 146 | 147 | def __init__( 148 | self, 149 | host: str, 150 | port: int | None = None, 151 | *, 152 | timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 153 | source_address: tuple[str, int] | None = None, 154 | blocksize: int = 8192, 155 | socket_options: _TYPE_SOCKET_OPTIONS | None = ..., 156 | proxy: Url | None = None, 157 | proxy_config: ProxyConfig | None = None, 158 | cert_reqs: int | str | None = None, 159 | assert_hostname: None | str | Literal[False] = None, 160 | assert_fingerprint: str | None = None, 161 | server_hostname: str | None = None, 162 | ssl_context: ssl.SSLContext | None = None, 163 | ca_certs: str | None = None, 164 | ca_cert_dir: str | None = None, 165 | ca_cert_data: None | str | bytes = None, 166 | ssl_minimum_version: int | None = None, 167 | ssl_maximum_version: int | None = None, 168 | ssl_version: int | str | None = None, # Deprecated 169 | cert_file: str | None = None, 170 | key_file: str | None = None, 171 | key_password: str | None = None, 172 | ) -> None: 173 | ... 174 | -------------------------------------------------------------------------------- /layer/python/urllib3/util/ssl_match_hostname.py: -------------------------------------------------------------------------------- 1 | """The match_hostname() function from Python 3.5, essential when using SSL.""" 2 | 3 | # Note: This file is under the PSF license as the code comes from the python 4 | # stdlib. http://docs.python.org/3/license.html 5 | # It is modified to remove commonName support. 6 | 7 | from __future__ import annotations 8 | 9 | import ipaddress 10 | import re 11 | import typing 12 | from ipaddress import IPv4Address, IPv6Address 13 | 14 | if typing.TYPE_CHECKING: 15 | from .ssl_ import _TYPE_PEER_CERT_RET_DICT 16 | 17 | __version__ = "3.5.0.1" 18 | 19 | 20 | class CertificateError(ValueError): 21 | pass 22 | 23 | 24 | def _dnsname_match( 25 | dn: typing.Any, hostname: str, max_wildcards: int = 1 26 | ) -> typing.Match[str] | None | bool: 27 | """Matching according to RFC 6125, section 6.4.3 28 | 29 | http://tools.ietf.org/html/rfc6125#section-6.4.3 30 | """ 31 | pats = [] 32 | if not dn: 33 | return False 34 | 35 | # Ported from python3-syntax: 36 | # leftmost, *remainder = dn.split(r'.') 37 | parts = dn.split(r".") 38 | leftmost = parts[0] 39 | remainder = parts[1:] 40 | 41 | wildcards = leftmost.count("*") 42 | if wildcards > max_wildcards: 43 | # Issue #17980: avoid denials of service by refusing more 44 | # than one wildcard per fragment. A survey of established 45 | # policy among SSL implementations showed it to be a 46 | # reasonable choice. 47 | raise CertificateError( 48 | "too many wildcards in certificate DNS name: " + repr(dn) 49 | ) 50 | 51 | # speed up common case w/o wildcards 52 | if not wildcards: 53 | return bool(dn.lower() == hostname.lower()) 54 | 55 | # RFC 6125, section 6.4.3, subitem 1. 56 | # The client SHOULD NOT attempt to match a presented identifier in which 57 | # the wildcard character comprises a label other than the left-most label. 58 | if leftmost == "*": 59 | # When '*' is a fragment by itself, it matches a non-empty dotless 60 | # fragment. 61 | pats.append("[^.]+") 62 | elif leftmost.startswith("xn--") or hostname.startswith("xn--"): 63 | # RFC 6125, section 6.4.3, subitem 3. 64 | # The client SHOULD NOT attempt to match a presented identifier 65 | # where the wildcard character is embedded within an A-label or 66 | # U-label of an internationalized domain name. 67 | pats.append(re.escape(leftmost)) 68 | else: 69 | # Otherwise, '*' matches any dotless string, e.g. www* 70 | pats.append(re.escape(leftmost).replace(r"\*", "[^.]*")) 71 | 72 | # add the remaining fragments, ignore any wildcards 73 | for frag in remainder: 74 | pats.append(re.escape(frag)) 75 | 76 | pat = re.compile(r"\A" + r"\.".join(pats) + r"\Z", re.IGNORECASE) 77 | return pat.match(hostname) 78 | 79 | 80 | def _ipaddress_match(ipname: str, host_ip: IPv4Address | IPv6Address) -> bool: 81 | """Exact matching of IP addresses. 82 | 83 | RFC 9110 section 4.3.5: "A reference identity of IP-ID contains the decoded 84 | bytes of the IP address. An IP version 4 address is 4 octets, and an IP 85 | version 6 address is 16 octets. [...] A reference identity of type IP-ID 86 | matches if the address is identical to an iPAddress value of the 87 | subjectAltName extension of the certificate." 88 | """ 89 | # OpenSSL may add a trailing newline to a subjectAltName's IP address 90 | # Divergence from upstream: ipaddress can't handle byte str 91 | ip = ipaddress.ip_address(ipname.rstrip()) 92 | return bool(ip.packed == host_ip.packed) 93 | 94 | 95 | def match_hostname( 96 | cert: _TYPE_PEER_CERT_RET_DICT | None, 97 | hostname: str, 98 | hostname_checks_common_name: bool = False, 99 | ) -> None: 100 | """Verify that *cert* (in decoded format as returned by 101 | SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 102 | rules are followed, but IP addresses are not accepted for *hostname*. 103 | 104 | CertificateError is raised on failure. On success, the function 105 | returns nothing. 106 | """ 107 | if not cert: 108 | raise ValueError( 109 | "empty or no certificate, match_hostname needs a " 110 | "SSL socket or SSL context with either " 111 | "CERT_OPTIONAL or CERT_REQUIRED" 112 | ) 113 | try: 114 | # Divergence from upstream: ipaddress can't handle byte str 115 | # 116 | # The ipaddress module shipped with Python < 3.9 does not support 117 | # scoped IPv6 addresses so we unconditionally strip the Zone IDs for 118 | # now. Once we drop support for Python 3.9 we can remove this branch. 119 | if "%" in hostname: 120 | host_ip = ipaddress.ip_address(hostname[: hostname.rfind("%")]) 121 | else: 122 | host_ip = ipaddress.ip_address(hostname) 123 | 124 | except ValueError: 125 | # Not an IP address (common case) 126 | host_ip = None 127 | dnsnames = [] 128 | san: tuple[tuple[str, str], ...] = cert.get("subjectAltName", ()) 129 | key: str 130 | value: str 131 | for key, value in san: 132 | if key == "DNS": 133 | if host_ip is None and _dnsname_match(value, hostname): 134 | return 135 | dnsnames.append(value) 136 | elif key == "IP Address": 137 | if host_ip is not None and _ipaddress_match(value, host_ip): 138 | return 139 | dnsnames.append(value) 140 | 141 | # We only check 'commonName' if it's enabled and we're not verifying 142 | # an IP address. IP addresses aren't valid within 'commonName'. 143 | if hostname_checks_common_name and host_ip is None and not dnsnames: 144 | for sub in cert.get("subject", ()): 145 | for key, value in sub: 146 | if key == "commonName": 147 | if _dnsname_match(value, hostname): 148 | return 149 | dnsnames.append(value) 150 | 151 | if len(dnsnames) > 1: 152 | raise CertificateError( 153 | "hostname %r " 154 | "doesn't match either of %s" % (hostname, ", ".join(map(repr, dnsnames))) 155 | ) 156 | elif len(dnsnames) == 1: 157 | raise CertificateError(f"hostname {hostname!r} doesn't match {dnsnames[0]!r}") 158 | else: 159 | raise CertificateError("no appropriate subjectAltName fields were found") 160 | -------------------------------------------------------------------------------- /layer/python/requests/api.py: -------------------------------------------------------------------------------- 1 | """ 2 | requests.api 3 | ~~~~~~~~~~~~ 4 | 5 | This module implements the Requests API. 6 | 7 | :copyright: (c) 2012 by Kenneth Reitz. 8 | :license: Apache2, see LICENSE for more details. 9 | """ 10 | 11 | from . import sessions 12 | 13 | 14 | def request(method, url, **kwargs): 15 | """Constructs and sends a :class:`Request `. 16 | 17 | :param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``. 18 | :param url: URL for the new :class:`Request` object. 19 | :param params: (optional) Dictionary, list of tuples or bytes to send 20 | in the query string for the :class:`Request`. 21 | :param data: (optional) Dictionary, list of tuples, bytes, or file-like 22 | object to send in the body of the :class:`Request`. 23 | :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. 24 | :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. 25 | :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. 26 | :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. 27 | ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` 28 | or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string 29 | defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers 30 | to add for the file. 31 | :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. 32 | :param timeout: (optional) How many seconds to wait for the server to send data 33 | before giving up, as a float, or a :ref:`(connect timeout, read 34 | timeout) ` tuple. 35 | :type timeout: float or tuple 36 | :param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``. 37 | :type allow_redirects: bool 38 | :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. 39 | :param verify: (optional) Either a boolean, in which case it controls whether we verify 40 | the server's TLS certificate, or a string, in which case it must be a path 41 | to a CA bundle to use. Defaults to ``True``. 42 | :param stream: (optional) if ``False``, the response content will be immediately downloaded. 43 | :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. 44 | :return: :class:`Response ` object 45 | :rtype: requests.Response 46 | 47 | Usage:: 48 | 49 | >>> import requests 50 | >>> req = requests.request('GET', 'https://httpbin.org/get') 51 | >>> req 52 | 53 | """ 54 | 55 | # By using the 'with' statement we are sure the session is closed, thus we 56 | # avoid leaving sockets open which can trigger a ResourceWarning in some 57 | # cases, and look like a memory leak in others. 58 | with sessions.Session() as session: 59 | return session.request(method=method, url=url, **kwargs) 60 | 61 | 62 | def get(url, params=None, **kwargs): 63 | r"""Sends a GET request. 64 | 65 | :param url: URL for the new :class:`Request` object. 66 | :param params: (optional) Dictionary, list of tuples or bytes to send 67 | in the query string for the :class:`Request`. 68 | :param \*\*kwargs: Optional arguments that ``request`` takes. 69 | :return: :class:`Response ` object 70 | :rtype: requests.Response 71 | """ 72 | 73 | return request("get", url, params=params, **kwargs) 74 | 75 | 76 | def options(url, **kwargs): 77 | r"""Sends an OPTIONS request. 78 | 79 | :param url: URL for the new :class:`Request` object. 80 | :param \*\*kwargs: Optional arguments that ``request`` takes. 81 | :return: :class:`Response ` object 82 | :rtype: requests.Response 83 | """ 84 | 85 | return request("options", url, **kwargs) 86 | 87 | 88 | def head(url, **kwargs): 89 | r"""Sends a HEAD request. 90 | 91 | :param url: URL for the new :class:`Request` object. 92 | :param \*\*kwargs: Optional arguments that ``request`` takes. If 93 | `allow_redirects` is not provided, it will be set to `False` (as 94 | opposed to the default :meth:`request` behavior). 95 | :return: :class:`Response ` object 96 | :rtype: requests.Response 97 | """ 98 | 99 | kwargs.setdefault("allow_redirects", False) 100 | return request("head", url, **kwargs) 101 | 102 | 103 | def post(url, data=None, json=None, **kwargs): 104 | r"""Sends a POST request. 105 | 106 | :param url: URL for the new :class:`Request` object. 107 | :param data: (optional) Dictionary, list of tuples, bytes, or file-like 108 | object to send in the body of the :class:`Request`. 109 | :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. 110 | :param \*\*kwargs: Optional arguments that ``request`` takes. 111 | :return: :class:`Response ` object 112 | :rtype: requests.Response 113 | """ 114 | 115 | return request("post", url, data=data, json=json, **kwargs) 116 | 117 | 118 | def put(url, data=None, **kwargs): 119 | r"""Sends a PUT request. 120 | 121 | :param url: URL for the new :class:`Request` object. 122 | :param data: (optional) Dictionary, list of tuples, bytes, or file-like 123 | object to send in the body of the :class:`Request`. 124 | :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. 125 | :param \*\*kwargs: Optional arguments that ``request`` takes. 126 | :return: :class:`Response ` object 127 | :rtype: requests.Response 128 | """ 129 | 130 | return request("put", url, data=data, **kwargs) 131 | 132 | 133 | def patch(url, data=None, **kwargs): 134 | r"""Sends a PATCH request. 135 | 136 | :param url: URL for the new :class:`Request` object. 137 | :param data: (optional) Dictionary, list of tuples, bytes, or file-like 138 | object to send in the body of the :class:`Request`. 139 | :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. 140 | :param \*\*kwargs: Optional arguments that ``request`` takes. 141 | :return: :class:`Response ` object 142 | :rtype: requests.Response 143 | """ 144 | 145 | return request("patch", url, data=data, **kwargs) 146 | 147 | 148 | def delete(url, **kwargs): 149 | r"""Sends a DELETE request. 150 | 151 | :param url: URL for the new :class:`Request` object. 152 | :param \*\*kwargs: Optional arguments that ``request`` takes. 153 | :return: :class:`Response ` object 154 | :rtype: requests.Response 155 | """ 156 | 157 | return request("delete", url, **kwargs) 158 | -------------------------------------------------------------------------------- /layer/python/urllib3-2.0.2.dist-info/METADATA: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: urllib3 3 | Version: 2.0.2 4 | Summary: HTTP library with thread-safe connection pooling, file post, and more. 5 | Project-URL: Changelog, https://github.com/urllib3/urllib3/blob/main/CHANGES.rst 6 | Project-URL: Documentation, https://urllib3.readthedocs.io 7 | Project-URL: Code, https://github.com/urllib3/urllib3 8 | Project-URL: Issue tracker, https://github.com/urllib3/urllib3/issues 9 | Author-email: Andrey Petrov 10 | Maintainer-email: Seth Michael Larson , Quentin Pradet 11 | License-File: LICENSE.txt 12 | Keywords: filepost,http,httplib,https,pooling,ssl,threadsafe,urllib 13 | Classifier: Environment :: Web Environment 14 | Classifier: Intended Audience :: Developers 15 | Classifier: License :: OSI Approved :: MIT License 16 | Classifier: Operating System :: OS Independent 17 | Classifier: Programming Language :: Python 18 | Classifier: Programming Language :: Python :: 3 19 | Classifier: Programming Language :: Python :: 3 :: Only 20 | Classifier: Programming Language :: Python :: 3.7 21 | Classifier: Programming Language :: Python :: 3.8 22 | Classifier: Programming Language :: Python :: 3.9 23 | Classifier: Programming Language :: Python :: 3.10 24 | Classifier: Programming Language :: Python :: 3.11 25 | Classifier: Programming Language :: Python :: 3.12 26 | Classifier: Programming Language :: Python :: Implementation :: CPython 27 | Classifier: Programming Language :: Python :: Implementation :: PyPy 28 | Classifier: Topic :: Internet :: WWW/HTTP 29 | Classifier: Topic :: Software Development :: Libraries 30 | Requires-Python: >=3.7 31 | Provides-Extra: brotli 32 | Requires-Dist: brotli>=1.0.9; platform_python_implementation == 'CPython' and extra == 'brotli' 33 | Requires-Dist: brotlicffi>=0.8.0; platform_python_implementation != 'CPython' and extra == 'brotli' 34 | Provides-Extra: secure 35 | Requires-Dist: certifi; extra == 'secure' 36 | Requires-Dist: cryptography>=1.9; extra == 'secure' 37 | Requires-Dist: idna>=2.0.0; extra == 'secure' 38 | Requires-Dist: pyopenssl>=17.1.0; extra == 'secure' 39 | Requires-Dist: urllib3-secure-extra; extra == 'secure' 40 | Provides-Extra: socks 41 | Requires-Dist: pysocks!=1.5.7,<2.0,>=1.5.6; extra == 'socks' 42 | Provides-Extra: zstd 43 | Requires-Dist: zstandard>=0.18.0; extra == 'zstd' 44 | Description-Content-Type: text/markdown 45 | 46 |

47 | 48 | ![urllib3](https://github.com/urllib3/urllib3/raw/main/docs/_static/banner_github.svg) 49 | 50 |

51 | 52 |

53 | PyPI Version 54 | Python Versions 55 | Join our Discord 56 | Coverage Status 57 | Build Status on GitHub 58 | Documentation Status
59 | OpenSSF Scorecard 60 | SLSA 3 61 | CII Best Practices 62 |

63 | 64 | urllib3 is a powerful, *user-friendly* HTTP client for Python. Much of the 65 | Python ecosystem already uses urllib3 and you should too. 66 | urllib3 brings many critical features that are missing from the Python 67 | standard libraries: 68 | 69 | - Thread safety. 70 | - Connection pooling. 71 | - Client-side SSL/TLS verification. 72 | - File uploads with multipart encoding. 73 | - Helpers for retrying requests and dealing with HTTP redirects. 74 | - Support for gzip, deflate, brotli, and zstd encoding. 75 | - Proxy support for HTTP and SOCKS. 76 | - 100% test coverage. 77 | 78 | urllib3 is powerful and easy to use: 79 | 80 | ```python3 81 | >>> import urllib3 82 | >>> resp = urllib3.request("GET", "http://httpbin.org/robots.txt") 83 | >>> resp.status 84 | 200 85 | >>> resp.data 86 | b"User-agent: *\nDisallow: /deny\n" 87 | ``` 88 | 89 | ## Installing 90 | 91 | urllib3 can be installed with [pip](https://pip.pypa.io): 92 | 93 | ```bash 94 | $ python -m pip install urllib3 95 | ``` 96 | 97 | Alternatively, you can grab the latest source code from [GitHub](https://github.com/urllib3/urllib3): 98 | 99 | ```bash 100 | $ git clone https://github.com/urllib3/urllib3.git 101 | $ cd urllib3 102 | $ pip install . 103 | ``` 104 | 105 | 106 | ## Documentation 107 | 108 | urllib3 has usage and reference documentation at [urllib3.readthedocs.io](https://urllib3.readthedocs.io). 109 | 110 | 111 | ## Community 112 | 113 | urllib3 has a [community Discord channel](https://discord.gg/urllib3) for asking questions and 114 | collaborating with other contributors. Drop by and say hello 👋 115 | 116 | 117 | ## Contributing 118 | 119 | urllib3 happily accepts contributions. Please see our 120 | [contributing documentation](https://urllib3.readthedocs.io/en/latest/contributing.html) 121 | for some tips on getting started. 122 | 123 | 124 | ## Security Disclosures 125 | 126 | To report a security vulnerability, please use the 127 | [Tidelift security contact](https://tidelift.com/security). 128 | Tidelift will coordinate the fix and disclosure with maintainers. 129 | 130 | 131 | ## Maintainers 132 | 133 | - [@sethmlarson](https://github.com/sethmlarson) (Seth M. Larson) 134 | - [@pquentin](https://github.com/pquentin) (Quentin Pradet) 135 | - [@theacodes](https://github.com/theacodes) (Thea Flowers) 136 | - [@haikuginger](https://github.com/haikuginger) (Jess Shapiro) 137 | - [@lukasa](https://github.com/lukasa) (Cory Benfield) 138 | - [@sigmavirus24](https://github.com/sigmavirus24) (Ian Stapleton Cordasco) 139 | - [@shazow](https://github.com/shazow) (Andrey Petrov) 140 | 141 | 👋 142 | 143 | 144 | ## Sponsorship 145 | 146 | If your company benefits from this library, please consider [sponsoring its 147 | development](https://urllib3.readthedocs.io/en/latest/sponsors.html). 148 | 149 | 150 | ## For Enterprise 151 | 152 | Professional support for urllib3 is available as part of the [Tidelift 153 | Subscription][1]. Tidelift gives software development teams a single source for 154 | purchasing and maintaining their software, with professional grade assurances 155 | from the experts who know it best, while seamlessly integrating with existing 156 | tools. 157 | 158 | [1]: https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=readme 159 | -------------------------------------------------------------------------------- /layer/python/urllib3/_request_methods.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import json as _json 4 | import typing 5 | from urllib.parse import urlencode 6 | 7 | from ._base_connection import _TYPE_BODY 8 | from ._collections import HTTPHeaderDict 9 | from .filepost import _TYPE_FIELDS, encode_multipart_formdata 10 | from .response import BaseHTTPResponse 11 | 12 | __all__ = ["RequestMethods"] 13 | 14 | _TYPE_ENCODE_URL_FIELDS = typing.Union[ 15 | typing.Sequence[typing.Tuple[str, typing.Union[str, bytes]]], 16 | typing.Mapping[str, typing.Union[str, bytes]], 17 | ] 18 | 19 | 20 | class RequestMethods: 21 | """ 22 | Convenience mixin for classes who implement a :meth:`urlopen` method, such 23 | as :class:`urllib3.HTTPConnectionPool` and 24 | :class:`urllib3.PoolManager`. 25 | 26 | Provides behavior for making common types of HTTP request methods and 27 | decides which type of request field encoding to use. 28 | 29 | Specifically, 30 | 31 | :meth:`.request_encode_url` is for sending requests whose fields are 32 | encoded in the URL (such as GET, HEAD, DELETE). 33 | 34 | :meth:`.request_encode_body` is for sending requests whose fields are 35 | encoded in the *body* of the request using multipart or www-form-urlencoded 36 | (such as for POST, PUT, PATCH). 37 | 38 | :meth:`.request` is for making any kind of request, it will look up the 39 | appropriate encoding format and use one of the above two methods to make 40 | the request. 41 | 42 | Initializer parameters: 43 | 44 | :param headers: 45 | Headers to include with all requests, unless other headers are given 46 | explicitly. 47 | """ 48 | 49 | _encode_url_methods = {"DELETE", "GET", "HEAD", "OPTIONS"} 50 | 51 | def __init__(self, headers: typing.Mapping[str, str] | None = None) -> None: 52 | self.headers = headers or {} 53 | 54 | def urlopen( 55 | self, 56 | method: str, 57 | url: str, 58 | body: _TYPE_BODY | None = None, 59 | headers: typing.Mapping[str, str] | None = None, 60 | encode_multipart: bool = True, 61 | multipart_boundary: str | None = None, 62 | **kw: typing.Any, 63 | ) -> BaseHTTPResponse: # Abstract 64 | raise NotImplementedError( 65 | "Classes extending RequestMethods must implement " 66 | "their own ``urlopen`` method." 67 | ) 68 | 69 | def request( 70 | self, 71 | method: str, 72 | url: str, 73 | body: _TYPE_BODY | None = None, 74 | fields: _TYPE_FIELDS | None = None, 75 | headers: typing.Mapping[str, str] | None = None, 76 | json: typing.Any | None = None, 77 | **urlopen_kw: typing.Any, 78 | ) -> BaseHTTPResponse: 79 | """ 80 | Make a request using :meth:`urlopen` with the appropriate encoding of 81 | ``fields`` based on the ``method`` used. 82 | 83 | This is a convenience method that requires the least amount of manual 84 | effort. It can be used in most situations, while still having the 85 | option to drop down to more specific methods when necessary, such as 86 | :meth:`request_encode_url`, :meth:`request_encode_body`, 87 | or even the lowest level :meth:`urlopen`. 88 | """ 89 | method = method.upper() 90 | 91 | if json is not None and body is not None: 92 | raise TypeError( 93 | "request got values for both 'body' and 'json' parameters which are mutually exclusive" 94 | ) 95 | 96 | if json is not None: 97 | if headers is None: 98 | headers = self.headers.copy() # type: ignore 99 | if not ("content-type" in map(str.lower, headers.keys())): 100 | headers["Content-Type"] = "application/json" # type: ignore 101 | 102 | body = _json.dumps(json, separators=(",", ":"), ensure_ascii=False).encode( 103 | "utf-8" 104 | ) 105 | 106 | if body is not None: 107 | urlopen_kw["body"] = body 108 | 109 | if method in self._encode_url_methods: 110 | return self.request_encode_url( 111 | method, 112 | url, 113 | fields=fields, # type: ignore[arg-type] 114 | headers=headers, 115 | **urlopen_kw, 116 | ) 117 | else: 118 | return self.request_encode_body( 119 | method, url, fields=fields, headers=headers, **urlopen_kw 120 | ) 121 | 122 | def request_encode_url( 123 | self, 124 | method: str, 125 | url: str, 126 | fields: _TYPE_ENCODE_URL_FIELDS | None = None, 127 | headers: typing.Mapping[str, str] | None = None, 128 | **urlopen_kw: str, 129 | ) -> BaseHTTPResponse: 130 | """ 131 | Make a request using :meth:`urlopen` with the ``fields`` encoded in 132 | the url. This is useful for request methods like GET, HEAD, DELETE, etc. 133 | """ 134 | if headers is None: 135 | headers = self.headers 136 | 137 | extra_kw: dict[str, typing.Any] = {"headers": headers} 138 | extra_kw.update(urlopen_kw) 139 | 140 | if fields: 141 | url += "?" + urlencode(fields) 142 | 143 | return self.urlopen(method, url, **extra_kw) 144 | 145 | def request_encode_body( 146 | self, 147 | method: str, 148 | url: str, 149 | fields: _TYPE_FIELDS | None = None, 150 | headers: typing.Mapping[str, str] | None = None, 151 | encode_multipart: bool = True, 152 | multipart_boundary: str | None = None, 153 | **urlopen_kw: str, 154 | ) -> BaseHTTPResponse: 155 | """ 156 | Make a request using :meth:`urlopen` with the ``fields`` encoded in 157 | the body. This is useful for request methods like POST, PUT, PATCH, etc. 158 | 159 | When ``encode_multipart=True`` (default), then 160 | :func:`urllib3.encode_multipart_formdata` is used to encode 161 | the payload with the appropriate content type. Otherwise 162 | :func:`urllib.parse.urlencode` is used with the 163 | 'application/x-www-form-urlencoded' content type. 164 | 165 | Multipart encoding must be used when posting files, and it's reasonably 166 | safe to use it in other times too. However, it may break request 167 | signing, such as with OAuth. 168 | 169 | Supports an optional ``fields`` parameter of key/value strings AND 170 | key/filetuple. A filetuple is a (filename, data, MIME type) tuple where 171 | the MIME type is optional. For example:: 172 | 173 | fields = { 174 | 'foo': 'bar', 175 | 'fakefile': ('foofile.txt', 'contents of foofile'), 176 | 'realfile': ('barfile.txt', open('realfile').read()), 177 | 'typedfile': ('bazfile.bin', open('bazfile').read(), 178 | 'image/jpeg'), 179 | 'nonamefile': 'contents of nonamefile field', 180 | } 181 | 182 | When uploading a file, providing a filename (the first parameter of the 183 | tuple) is optional but recommended to best mimic behavior of browsers. 184 | 185 | Note that if ``headers`` are supplied, the 'Content-Type' header will 186 | be overwritten because it depends on the dynamic random boundary string 187 | which is used to compose the body of the request. The random boundary 188 | string can be explicitly set with the ``multipart_boundary`` parameter. 189 | """ 190 | if headers is None: 191 | headers = self.headers 192 | 193 | extra_kw: dict[str, typing.Any] = {"headers": HTTPHeaderDict(headers)} 194 | body: bytes | str 195 | 196 | if fields: 197 | if "body" in urlopen_kw: 198 | raise TypeError( 199 | "request got values for both 'fields' and 'body', can only specify one." 200 | ) 201 | 202 | if encode_multipart: 203 | body, content_type = encode_multipart_formdata( 204 | fields, boundary=multipart_boundary 205 | ) 206 | else: 207 | body, content_type = ( 208 | urlencode(fields), # type: ignore[arg-type] 209 | "application/x-www-form-urlencoded", 210 | ) 211 | 212 | extra_kw["body"] = body 213 | extra_kw["headers"].setdefault("Content-Type", content_type) 214 | 215 | extra_kw.update(urlopen_kw) 216 | 217 | return self.urlopen(method, url, **extra_kw) 218 | -------------------------------------------------------------------------------- /layer/python/urllib3/contrib/socks.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module contains provisional support for SOCKS proxies from within 3 | urllib3. This module supports SOCKS4, SOCKS4A (an extension of SOCKS4), and 4 | SOCKS5. To enable its functionality, either install PySocks or install this 5 | module with the ``socks`` extra. 6 | 7 | The SOCKS implementation supports the full range of urllib3 features. It also 8 | supports the following SOCKS features: 9 | 10 | - SOCKS4A (``proxy_url='socks4a://...``) 11 | - SOCKS4 (``proxy_url='socks4://...``) 12 | - SOCKS5 with remote DNS (``proxy_url='socks5h://...``) 13 | - SOCKS5 with local DNS (``proxy_url='socks5://...``) 14 | - Usernames and passwords for the SOCKS proxy 15 | 16 | .. note:: 17 | It is recommended to use ``socks5h://`` or ``socks4a://`` schemes in 18 | your ``proxy_url`` to ensure that DNS resolution is done from the remote 19 | server instead of client-side when connecting to a domain name. 20 | 21 | SOCKS4 supports IPv4 and domain names with the SOCKS4A extension. SOCKS5 22 | supports IPv4, IPv6, and domain names. 23 | 24 | When connecting to a SOCKS4 proxy the ``username`` portion of the ``proxy_url`` 25 | will be sent as the ``userid`` section of the SOCKS request: 26 | 27 | .. code-block:: python 28 | 29 | proxy_url="socks4a://@proxy-host" 30 | 31 | When connecting to a SOCKS5 proxy the ``username`` and ``password`` portion 32 | of the ``proxy_url`` will be sent as the username/password to authenticate 33 | with the proxy: 34 | 35 | .. code-block:: python 36 | 37 | proxy_url="socks5h://:@proxy-host" 38 | 39 | """ 40 | 41 | from __future__ import annotations 42 | 43 | try: 44 | import socks # type: ignore[import] 45 | except ImportError: 46 | import warnings 47 | 48 | from ..exceptions import DependencyWarning 49 | 50 | warnings.warn( 51 | ( 52 | "SOCKS support in urllib3 requires the installation of optional " 53 | "dependencies: specifically, PySocks. For more information, see " 54 | "https://urllib3.readthedocs.io/en/latest/contrib.html#socks-proxies" 55 | ), 56 | DependencyWarning, 57 | ) 58 | raise 59 | 60 | import typing 61 | from socket import timeout as SocketTimeout 62 | 63 | from ..connection import HTTPConnection, HTTPSConnection 64 | from ..connectionpool import HTTPConnectionPool, HTTPSConnectionPool 65 | from ..exceptions import ConnectTimeoutError, NewConnectionError 66 | from ..poolmanager import PoolManager 67 | from ..util.url import parse_url 68 | 69 | try: 70 | import ssl 71 | except ImportError: 72 | ssl = None # type: ignore[assignment] 73 | 74 | try: 75 | from typing import TypedDict 76 | 77 | class _TYPE_SOCKS_OPTIONS(TypedDict): 78 | socks_version: int 79 | proxy_host: str | None 80 | proxy_port: str | None 81 | username: str | None 82 | password: str | None 83 | rdns: bool 84 | 85 | except ImportError: # Python 3.7 86 | _TYPE_SOCKS_OPTIONS = typing.Dict[str, typing.Any] # type: ignore[misc, assignment] 87 | 88 | 89 | class SOCKSConnection(HTTPConnection): 90 | """ 91 | A plain-text HTTP connection that connects via a SOCKS proxy. 92 | """ 93 | 94 | def __init__( 95 | self, 96 | _socks_options: _TYPE_SOCKS_OPTIONS, 97 | *args: typing.Any, 98 | **kwargs: typing.Any, 99 | ) -> None: 100 | self._socks_options = _socks_options 101 | super().__init__(*args, **kwargs) 102 | 103 | def _new_conn(self) -> socks.socksocket: 104 | """ 105 | Establish a new connection via the SOCKS proxy. 106 | """ 107 | extra_kw: dict[str, typing.Any] = {} 108 | if self.source_address: 109 | extra_kw["source_address"] = self.source_address 110 | 111 | if self.socket_options: 112 | extra_kw["socket_options"] = self.socket_options 113 | 114 | try: 115 | conn = socks.create_connection( 116 | (self.host, self.port), 117 | proxy_type=self._socks_options["socks_version"], 118 | proxy_addr=self._socks_options["proxy_host"], 119 | proxy_port=self._socks_options["proxy_port"], 120 | proxy_username=self._socks_options["username"], 121 | proxy_password=self._socks_options["password"], 122 | proxy_rdns=self._socks_options["rdns"], 123 | timeout=self.timeout, 124 | **extra_kw, 125 | ) 126 | 127 | except SocketTimeout as e: 128 | raise ConnectTimeoutError( 129 | self, 130 | f"Connection to {self.host} timed out. (connect timeout={self.timeout})", 131 | ) from e 132 | 133 | except socks.ProxyError as e: 134 | # This is fragile as hell, but it seems to be the only way to raise 135 | # useful errors here. 136 | if e.socket_err: 137 | error = e.socket_err 138 | if isinstance(error, SocketTimeout): 139 | raise ConnectTimeoutError( 140 | self, 141 | f"Connection to {self.host} timed out. (connect timeout={self.timeout})", 142 | ) from e 143 | else: 144 | # Adding `from e` messes with coverage somehow, so it's omitted. 145 | # See #2386. 146 | raise NewConnectionError( 147 | self, f"Failed to establish a new connection: {error}" 148 | ) 149 | else: 150 | raise NewConnectionError( 151 | self, f"Failed to establish a new connection: {e}" 152 | ) from e 153 | 154 | except OSError as e: # Defensive: PySocks should catch all these. 155 | raise NewConnectionError( 156 | self, f"Failed to establish a new connection: {e}" 157 | ) from e 158 | 159 | return conn 160 | 161 | 162 | # We don't need to duplicate the Verified/Unverified distinction from 163 | # urllib3/connection.py here because the HTTPSConnection will already have been 164 | # correctly set to either the Verified or Unverified form by that module. This 165 | # means the SOCKSHTTPSConnection will automatically be the correct type. 166 | class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection): 167 | pass 168 | 169 | 170 | class SOCKSHTTPConnectionPool(HTTPConnectionPool): 171 | ConnectionCls = SOCKSConnection 172 | 173 | 174 | class SOCKSHTTPSConnectionPool(HTTPSConnectionPool): 175 | ConnectionCls = SOCKSHTTPSConnection 176 | 177 | 178 | class SOCKSProxyManager(PoolManager): 179 | """ 180 | A version of the urllib3 ProxyManager that routes connections via the 181 | defined SOCKS proxy. 182 | """ 183 | 184 | pool_classes_by_scheme = { 185 | "http": SOCKSHTTPConnectionPool, 186 | "https": SOCKSHTTPSConnectionPool, 187 | } 188 | 189 | def __init__( 190 | self, 191 | proxy_url: str, 192 | username: str | None = None, 193 | password: str | None = None, 194 | num_pools: int = 10, 195 | headers: typing.Mapping[str, str] | None = None, 196 | **connection_pool_kw: typing.Any, 197 | ): 198 | parsed = parse_url(proxy_url) 199 | 200 | if username is None and password is None and parsed.auth is not None: 201 | split = parsed.auth.split(":") 202 | if len(split) == 2: 203 | username, password = split 204 | if parsed.scheme == "socks5": 205 | socks_version = socks.PROXY_TYPE_SOCKS5 206 | rdns = False 207 | elif parsed.scheme == "socks5h": 208 | socks_version = socks.PROXY_TYPE_SOCKS5 209 | rdns = True 210 | elif parsed.scheme == "socks4": 211 | socks_version = socks.PROXY_TYPE_SOCKS4 212 | rdns = False 213 | elif parsed.scheme == "socks4a": 214 | socks_version = socks.PROXY_TYPE_SOCKS4 215 | rdns = True 216 | else: 217 | raise ValueError(f"Unable to determine SOCKS version from {proxy_url}") 218 | 219 | self.proxy_url = proxy_url 220 | 221 | socks_options = { 222 | "socks_version": socks_version, 223 | "proxy_host": parsed.host, 224 | "proxy_port": parsed.port, 225 | "username": username, 226 | "password": password, 227 | "rdns": rdns, 228 | } 229 | connection_pool_kw["_socks_options"] = socks_options 230 | 231 | super().__init__(num_pools, headers, **connection_pool_kw) 232 | 233 | self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme 234 | -------------------------------------------------------------------------------- /layer/python/urllib3/util/request.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import io 4 | import typing 5 | from base64 import b64encode 6 | from enum import Enum 7 | 8 | from ..exceptions import UnrewindableBodyError 9 | from .util import to_bytes 10 | 11 | if typing.TYPE_CHECKING: 12 | from typing_extensions import Final 13 | 14 | # Pass as a value within ``headers`` to skip 15 | # emitting some HTTP headers that are added automatically. 16 | # The only headers that are supported are ``Accept-Encoding``, 17 | # ``Host``, and ``User-Agent``. 18 | SKIP_HEADER = "@@@SKIP_HEADER@@@" 19 | SKIPPABLE_HEADERS = frozenset(["accept-encoding", "host", "user-agent"]) 20 | 21 | ACCEPT_ENCODING = "gzip,deflate" 22 | try: 23 | try: 24 | import brotlicffi as _unused_module_brotli # type: ignore[import] # noqa: F401 25 | except ImportError: 26 | import brotli as _unused_module_brotli # type: ignore[import] # noqa: F401 27 | except ImportError: 28 | pass 29 | else: 30 | ACCEPT_ENCODING += ",br" 31 | try: 32 | import zstandard as _unused_module_zstd # type: ignore[import] # noqa: F401 33 | except ImportError: 34 | pass 35 | else: 36 | ACCEPT_ENCODING += ",zstd" 37 | 38 | 39 | class _TYPE_FAILEDTELL(Enum): 40 | token = 0 41 | 42 | 43 | _FAILEDTELL: Final[_TYPE_FAILEDTELL] = _TYPE_FAILEDTELL.token 44 | 45 | _TYPE_BODY_POSITION = typing.Union[int, _TYPE_FAILEDTELL] 46 | 47 | # When sending a request with these methods we aren't expecting 48 | # a body so don't need to set an explicit 'Content-Length: 0' 49 | # The reason we do this in the negative instead of tracking methods 50 | # which 'should' have a body is because unknown methods should be 51 | # treated as if they were 'POST' which *does* expect a body. 52 | _METHODS_NOT_EXPECTING_BODY = {"GET", "HEAD", "DELETE", "TRACE", "OPTIONS", "CONNECT"} 53 | 54 | 55 | def make_headers( 56 | keep_alive: bool | None = None, 57 | accept_encoding: bool | list[str] | str | None = None, 58 | user_agent: str | None = None, 59 | basic_auth: str | None = None, 60 | proxy_basic_auth: str | None = None, 61 | disable_cache: bool | None = None, 62 | ) -> dict[str, str]: 63 | """ 64 | Shortcuts for generating request headers. 65 | 66 | :param keep_alive: 67 | If ``True``, adds 'connection: keep-alive' header. 68 | 69 | :param accept_encoding: 70 | Can be a boolean, list, or string. 71 | ``True`` translates to 'gzip,deflate'. If either the ``brotli`` or 72 | ``brotlicffi`` package is installed 'gzip,deflate,br' is used instead. 73 | List will get joined by comma. 74 | String will be used as provided. 75 | 76 | :param user_agent: 77 | String representing the user-agent you want, such as 78 | "python-urllib3/0.6" 79 | 80 | :param basic_auth: 81 | Colon-separated username:password string for 'authorization: basic ...' 82 | auth header. 83 | 84 | :param proxy_basic_auth: 85 | Colon-separated username:password string for 'proxy-authorization: basic ...' 86 | auth header. 87 | 88 | :param disable_cache: 89 | If ``True``, adds 'cache-control: no-cache' header. 90 | 91 | Example: 92 | 93 | .. code-block:: python 94 | 95 | import urllib3 96 | 97 | print(urllib3.util.make_headers(keep_alive=True, user_agent="Batman/1.0")) 98 | # {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'} 99 | print(urllib3.util.make_headers(accept_encoding=True)) 100 | # {'accept-encoding': 'gzip,deflate'} 101 | """ 102 | headers: dict[str, str] = {} 103 | if accept_encoding: 104 | if isinstance(accept_encoding, str): 105 | pass 106 | elif isinstance(accept_encoding, list): 107 | accept_encoding = ",".join(accept_encoding) 108 | else: 109 | accept_encoding = ACCEPT_ENCODING 110 | headers["accept-encoding"] = accept_encoding 111 | 112 | if user_agent: 113 | headers["user-agent"] = user_agent 114 | 115 | if keep_alive: 116 | headers["connection"] = "keep-alive" 117 | 118 | if basic_auth: 119 | headers[ 120 | "authorization" 121 | ] = f"Basic {b64encode(basic_auth.encode('latin-1')).decode()}" 122 | 123 | if proxy_basic_auth: 124 | headers[ 125 | "proxy-authorization" 126 | ] = f"Basic {b64encode(proxy_basic_auth.encode('latin-1')).decode()}" 127 | 128 | if disable_cache: 129 | headers["cache-control"] = "no-cache" 130 | 131 | return headers 132 | 133 | 134 | def set_file_position( 135 | body: typing.Any, pos: _TYPE_BODY_POSITION | None 136 | ) -> _TYPE_BODY_POSITION | None: 137 | """ 138 | If a position is provided, move file to that point. 139 | Otherwise, we'll attempt to record a position for future use. 140 | """ 141 | if pos is not None: 142 | rewind_body(body, pos) 143 | elif getattr(body, "tell", None) is not None: 144 | try: 145 | pos = body.tell() 146 | except OSError: 147 | # This differentiates from None, allowing us to catch 148 | # a failed `tell()` later when trying to rewind the body. 149 | pos = _FAILEDTELL 150 | 151 | return pos 152 | 153 | 154 | def rewind_body(body: typing.IO[typing.AnyStr], body_pos: _TYPE_BODY_POSITION) -> None: 155 | """ 156 | Attempt to rewind body to a certain position. 157 | Primarily used for request redirects and retries. 158 | 159 | :param body: 160 | File-like object that supports seek. 161 | 162 | :param int pos: 163 | Position to seek to in file. 164 | """ 165 | body_seek = getattr(body, "seek", None) 166 | if body_seek is not None and isinstance(body_pos, int): 167 | try: 168 | body_seek(body_pos) 169 | except OSError as e: 170 | raise UnrewindableBodyError( 171 | "An error occurred when rewinding request body for redirect/retry." 172 | ) from e 173 | elif body_pos is _FAILEDTELL: 174 | raise UnrewindableBodyError( 175 | "Unable to record file position for rewinding " 176 | "request body during a redirect/retry." 177 | ) 178 | else: 179 | raise ValueError( 180 | f"body_pos must be of type integer, instead it was {type(body_pos)}." 181 | ) 182 | 183 | 184 | class ChunksAndContentLength(typing.NamedTuple): 185 | chunks: typing.Iterable[bytes] | None 186 | content_length: int | None 187 | 188 | 189 | def body_to_chunks( 190 | body: typing.Any | None, method: str, blocksize: int 191 | ) -> ChunksAndContentLength: 192 | """Takes the HTTP request method, body, and blocksize and 193 | transforms them into an iterable of chunks to pass to 194 | socket.sendall() and an optional 'Content-Length' header. 195 | 196 | A 'Content-Length' of 'None' indicates the length of the body 197 | can't be determined so should use 'Transfer-Encoding: chunked' 198 | for framing instead. 199 | """ 200 | 201 | chunks: typing.Iterable[bytes] | None 202 | content_length: int | None 203 | 204 | # No body, we need to make a recommendation on 'Content-Length' 205 | # based on whether that request method is expected to have 206 | # a body or not. 207 | if body is None: 208 | chunks = None 209 | if method.upper() not in _METHODS_NOT_EXPECTING_BODY: 210 | content_length = 0 211 | else: 212 | content_length = None 213 | 214 | # Bytes or strings become bytes 215 | elif isinstance(body, (str, bytes)): 216 | chunks = (to_bytes(body),) 217 | content_length = len(chunks[0]) 218 | 219 | # File-like object, TODO: use seek() and tell() for length? 220 | elif hasattr(body, "read"): 221 | 222 | def chunk_readable() -> typing.Iterable[bytes]: 223 | nonlocal body, blocksize 224 | encode = isinstance(body, io.TextIOBase) 225 | while True: 226 | datablock = body.read(blocksize) # type: ignore[union-attr] 227 | if not datablock: 228 | break 229 | if encode: 230 | datablock = datablock.encode("iso-8859-1") 231 | yield datablock 232 | 233 | chunks = chunk_readable() 234 | content_length = None 235 | 236 | # Otherwise we need to start checking via duck-typing. 237 | else: 238 | try: 239 | # Check if the body implements the buffer API. 240 | mv = memoryview(body) 241 | except TypeError: 242 | try: 243 | # Check if the body is an iterable 244 | chunks = iter(body) 245 | content_length = None 246 | except TypeError: 247 | raise TypeError( 248 | f"'body' must be a bytes-like object, file-like " 249 | f"object, or iterable. Instead was {body!r}" 250 | ) from None 251 | else: 252 | # Since it implements the buffer API can be passed directly to socket.sendall() 253 | chunks = (body,) 254 | content_length = mv.nbytes 255 | 256 | return ChunksAndContentLength(chunks=chunks, content_length=content_length) 257 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Creating a Rest API with Infrastructure as Code (Terraform) & Serverless (Lambda + Python) 2 | 3 | ## Deploying an API in minutes instead of days 4 | 5 | Deploying to production is becoming easier every day. Best practices and tools are available to us to create software that eventually contributes to automating tedious tasks, creating new businesses and startups, and ultimately generating value for society 🌎. 6 | 7 | As a software engineer, I firmly believe that open-source code (OSS) provides us with a multitude of benefits, including a community focused on excellence and knowledge sharing. This is one of the reasons why I try to publish the code I have been creating and leverage open-source tools like **Terraform** to deploy infrastructure. 8 | 9 | An important idea for me, and why I joined the AWS community, is always to **study, build, and share** in order to create a multiplier effect with more and better engineers in a field where creativity and cooperation are fundamental pillars. 10 | 11 | > In a future post, I will explain and take the code to a more advanced level with: **Continuous Deployment** using CircleCI, **Lambda Layers**, and **Best Practices** or steps to follow when publishing an API in production (Authentication, Rate-Limiting, etc.). 12 | 13 | ## Deployment Anti-Patterns 🙅🏻‍♂️ 14 | 15 | - Manually created infrastructure 16 | - Deploying at specific times due to lack of technical capacity (production errors, unstable software, service unavailability) rather than business decisions 17 | - Having to send emails to another team for deployment 18 | - Branches like "development" that differ from what exists in "main" and sometimes generate conflicts during merging 19 | - Manual testing 20 | - Tickets for deployment 21 | - SOAP as the only alternative 22 | 23 | All the points mentioned above, along with excessive bureaucracy, committees, and meetings, are still the norm in many organizations, and it was my reality for many years. But there is an alternative, a way of seeing the software development process where **simplicity is key**, both in deployments with pipelines and automation, and in the creation of software, where the focus should be on reducing accidental complexity, building modular, testable applications that are easy to understand for the team. 24 | 25 | ## A Brighter Future 💻 26 | 27 | - Unit testing and automation 28 | - Trunk-based development 29 | - AI, ChatGPT, Copilot 30 | - DevOps as a mindset, not just a role 31 | - Infrastructure as code 32 | - Serverless and containers 33 | - REST, SOAP, GraphQL, gRPC 34 | 35 | The purpose of this post is to showcase an essential part of software delivery with Terraform and Serverless. 36 | 37 | ## Let's talk about the code! 38 | 39 | **Code on GitHub:** [serverless-terraform](https://github.com/jorgetovar/serverless-terraform) 40 | 41 | ### Project Structure 🏭 42 | 43 | ![Project Structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/80bk086lppmisj8rysm0.png) 44 | 45 | The following files are typical in a Terraform project, and this is the convention we are using in this project. 46 | 47 | - *main.tf* - Entry point for Terraform 48 | - *variables.tf* - Input variables 49 | - *terraform.tfvars* - Variable definition file 50 | - *providers.tf* - Provider declarations 51 | - *outputs.tf* - Output values 52 | - *versions.tf* - Provider version locking 53 | 54 | ### Why Terraform ☁️ 55 | 56 | - Open source 57 | - Multi-cloud 58 | - Immutable infrastructure 59 | - Declarative paradigm 60 | - Domain-specific language (DSL) 61 | - Does not require agents or master nodes 62 | - Free 63 | - Large community 64 | - Mature software 65 | 66 | ### Why Serverless and Python 🐍 67 | 68 | - Automatic scalability 69 | - Pay-per-use 70 | - High availability 71 | - Easy development and deployment 72 | 73 | 74 | - Integration with managed services 75 | 76 | ### Code 77 | 78 | **Lambda:** The code for a Lambda function should generally be simple and adhere to the single responsibility principle or reason to change. The name of the method should correspond to the entry point defined in the Terraform code. 79 | 80 | ```Python 81 | import json 82 | import requests 83 | 84 | def lambda_handler(event, context): 85 | response = requests.get("https://test-api.k6.io/public/crocodiles/") 86 | if response.status_code == 200: 87 | data = response.json() 88 | random_info = data 89 | else: 90 | random_info = "No data!" 91 | 92 | return { 93 | "statusCode": 200, 94 | "body": json.dumps({ 95 | "message": "hello world - We are going to create a DevOps as a service powered by AI", 96 | "random_info": random_info 97 | }), 98 | } 99 | ``` 100 | 101 | **.gitignore:** Definition of files that we do not want to add to the repository. It is crucial not to publish AWS credential information. 102 | 103 | ```terraform 104 | # Terraform-specific files 105 | .terraform/ 106 | terraform.tfstate 107 | terraform.tfstate.backup 108 | *.tfvars 109 | 110 | hello_world.zip 111 | response.json 112 | .layer 113 | layer.zip 114 | 115 | # AWS credentials file 116 | .aws/credentials 117 | ``` 118 | 119 | **Provider:** The first step is usually to define the provider, where we typically specify the cloud and deployment region. 120 | 121 | ```terraform 122 | provider "aws" { 123 | region = var.aws_region 124 | 125 | default_tags { 126 | tags = { 127 | created-by = "terraform" 128 | } 129 | } 130 | } 131 | ``` 132 | 133 | **Lambda Resources:** Configures the Lambda to retrieve the source code from a bucket also created by Terraform, and we define `lambda_handler` as the method that will receive and process the API event. 134 | 135 | ```terraform 136 | resource "aws_lambda_function" "hello_world" { 137 | function_name = "HelloCommunityBuilders" 138 | s3_bucket = aws_s3_bucket.lambda_bucket.id 139 | s3_key = aws_s3_object.lambda_hello_world.key 140 | runtime = "python3.9" 141 | handler = "app.lambda_handler" 142 | source_code_hash = data.archive_file.lambda_hello_world.output_base64sha256 143 | role = aws_iam_role.lambda_exec.arn 144 | layers = [aws_lambda_layer_version.lambda_layer.arn] 145 | } 146 | ``` 147 | 148 | **Permissions:** Defines the required permissions to execute the Lambda and access AWS services. 149 | 150 | ```terraform 151 | resource "aws_iam_role" "lambda_exec" { 152 | name = "serverless_lambda" 153 | 154 | assume_role_policy = jsonencode({ 155 | Version = "2012-10-17" 156 | Statement = [ 157 | { 158 | Action = "sts:AssumeRole" 159 | Effect = "Allow" 160 | Sid = "" 161 | Principal = { 162 | Service = "lambda.amazonaws.com" 163 | } 164 | } 165 | ] 166 | }) 167 | } 168 | 169 | resource "aws_iam_role_policy_attachment" "lambda_policy" { 170 | role = aws_iam_role.lambda_exec.name 171 | policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 172 | } 173 | ``` 174 | 175 | **API Definition:** Specifies the verb and resource name according to the conventions of a REST API. 176 | 177 | ```terraform 178 | resource "aws_apigatewayv2_integration" "hello_world" { 179 | api_id = aws_apigatewayv2_api.lambda.id 180 | integration_uri = aws_lambda_function.hello_world.invoke_arn 181 | integration_type = "AWS_PROXY" 182 | integration_method = "POST" 183 | } 184 | 185 | resource "aws_apigatewayv2_route" "hello_world" { 186 | api_id = aws_apigatewayv2_api.lambda.id 187 | route_key = "GET /hello" 188 | target = "integrations/${aws_apigatewayv 189 | 190 | 2_integration.hello_world.id}" 191 | } 192 | ``` 193 | 194 | ### Terraform Deployment 195 | 196 | Deploying the API should be as simple as cloning the code and running a couple of Terraform commands. 197 | 198 | The `terraform init` command is used to initialize a project in a working directory. This command downloads provider code and configures the backend, where state and other key data are stored. 199 | 200 | ```sh 201 | terraform init 202 | ``` 203 | 204 | The `terraform apply` command is used to apply the changes defined in your infrastructure configuration and create or modify the corresponding resources in your infrastructure provider. 205 | 206 | ```sh 207 | terraform apply 208 | ``` 209 | 210 | Finally, you can retrieve the information from the *Outputs* and make the API call. 211 | 212 | ```sh 213 | http "$(terraform output -raw base_url)/hello" 214 | ``` 215 | 216 | ![Http API Call](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zep5h1akb7o9t5hryf6q.png) 217 | 218 | ## Conclusion 🤔 219 | 220 | In this article, we have explored the combination of Terraform and Serverless to create an API in minutes. 221 | 222 | By leveraging CI/CD best practices, infrastructure as code, and automated testing, we can achieve faster and more reliable deployments while maintaining high-quality code. 223 | 224 | Not too long ago, deploying an API in production was a tedious task that involved multiple actors. Nowadays, we have a wealth of open-source software and developer-focused tools that emphasize immutability, automation, productivity-enhancing AI, and powerful IDEs. In summary, there has never been a better time to be an engineer and create value in society through software. 225 | 226 | It is important to emphasize that the major issues in the software development and delivery process are organizational and human. Ultimately, building applications is a social activity, but the more we solve technical problems and improve communication, the easier it should be to reach production. Happy coding! 🎉 227 | 228 | - [LinkedIn](https://www.linkedin.com/in/%F0%9F%91%A8%E2%80%8D%F0%9F%8F%AB-jorge-tovar-71847669/) 229 | - [Twitter](https://twitter.com/jorgetovar621) 230 | - [GitHub](https://github.com/jorgetovar) 231 | 232 | If you enjoyed the articles, visit my blog at [jorgetovar.dev](https://jorgetovar.dev). -------------------------------------------------------------------------------- /layer/python/urllib3/util/ssltransport.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import io 4 | import socket 5 | import ssl 6 | import typing 7 | 8 | from ..exceptions import ProxySchemeUnsupported 9 | 10 | if typing.TYPE_CHECKING: 11 | from typing_extensions import Literal 12 | 13 | from .ssl_ import _TYPE_PEER_CERT_RET, _TYPE_PEER_CERT_RET_DICT 14 | 15 | 16 | _SelfT = typing.TypeVar("_SelfT", bound="SSLTransport") 17 | _WriteBuffer = typing.Union[bytearray, memoryview] 18 | _ReturnValue = typing.TypeVar("_ReturnValue") 19 | 20 | SSL_BLOCKSIZE = 16384 21 | 22 | 23 | class SSLTransport: 24 | """ 25 | The SSLTransport wraps an existing socket and establishes an SSL connection. 26 | 27 | Contrary to Python's implementation of SSLSocket, it allows you to chain 28 | multiple TLS connections together. It's particularly useful if you need to 29 | implement TLS within TLS. 30 | 31 | The class supports most of the socket API operations. 32 | """ 33 | 34 | @staticmethod 35 | def _validate_ssl_context_for_tls_in_tls(ssl_context: ssl.SSLContext) -> None: 36 | """ 37 | Raises a ProxySchemeUnsupported if the provided ssl_context can't be used 38 | for TLS in TLS. 39 | 40 | The only requirement is that the ssl_context provides the 'wrap_bio' 41 | methods. 42 | """ 43 | 44 | if not hasattr(ssl_context, "wrap_bio"): 45 | raise ProxySchemeUnsupported( 46 | "TLS in TLS requires SSLContext.wrap_bio() which isn't " 47 | "available on non-native SSLContext" 48 | ) 49 | 50 | def __init__( 51 | self, 52 | socket: socket.socket, 53 | ssl_context: ssl.SSLContext, 54 | server_hostname: str | None = None, 55 | suppress_ragged_eofs: bool = True, 56 | ) -> None: 57 | """ 58 | Create an SSLTransport around socket using the provided ssl_context. 59 | """ 60 | self.incoming = ssl.MemoryBIO() 61 | self.outgoing = ssl.MemoryBIO() 62 | 63 | self.suppress_ragged_eofs = suppress_ragged_eofs 64 | self.socket = socket 65 | 66 | self.sslobj = ssl_context.wrap_bio( 67 | self.incoming, self.outgoing, server_hostname=server_hostname 68 | ) 69 | 70 | # Perform initial handshake. 71 | self._ssl_io_loop(self.sslobj.do_handshake) 72 | 73 | def __enter__(self: _SelfT) -> _SelfT: 74 | return self 75 | 76 | def __exit__(self, *_: typing.Any) -> None: 77 | self.close() 78 | 79 | def fileno(self) -> int: 80 | return self.socket.fileno() 81 | 82 | def read(self, len: int = 1024, buffer: typing.Any | None = None) -> int | bytes: 83 | return self._wrap_ssl_read(len, buffer) 84 | 85 | def recv(self, buflen: int = 1024, flags: int = 0) -> int | bytes: 86 | if flags != 0: 87 | raise ValueError("non-zero flags not allowed in calls to recv") 88 | return self._wrap_ssl_read(buflen) 89 | 90 | def recv_into( 91 | self, 92 | buffer: _WriteBuffer, 93 | nbytes: int | None = None, 94 | flags: int = 0, 95 | ) -> None | int | bytes: 96 | if flags != 0: 97 | raise ValueError("non-zero flags not allowed in calls to recv_into") 98 | if nbytes is None: 99 | nbytes = len(buffer) 100 | return self.read(nbytes, buffer) 101 | 102 | def sendall(self, data: bytes, flags: int = 0) -> None: 103 | if flags != 0: 104 | raise ValueError("non-zero flags not allowed in calls to sendall") 105 | count = 0 106 | with memoryview(data) as view, view.cast("B") as byte_view: 107 | amount = len(byte_view) 108 | while count < amount: 109 | v = self.send(byte_view[count:]) 110 | count += v 111 | 112 | def send(self, data: bytes, flags: int = 0) -> int: 113 | if flags != 0: 114 | raise ValueError("non-zero flags not allowed in calls to send") 115 | return self._ssl_io_loop(self.sslobj.write, data) 116 | 117 | def makefile( 118 | self, 119 | mode: str, 120 | buffering: int | None = None, 121 | *, 122 | encoding: str | None = None, 123 | errors: str | None = None, 124 | newline: str | None = None, 125 | ) -> typing.BinaryIO | typing.TextIO | socket.SocketIO: 126 | """ 127 | Python's httpclient uses makefile and buffered io when reading HTTP 128 | messages and we need to support it. 129 | 130 | This is unfortunately a copy and paste of socket.py makefile with small 131 | changes to point to the socket directly. 132 | """ 133 | if not set(mode) <= {"r", "w", "b"}: 134 | raise ValueError(f"invalid mode {mode!r} (only r, w, b allowed)") 135 | 136 | writing = "w" in mode 137 | reading = "r" in mode or not writing 138 | assert reading or writing 139 | binary = "b" in mode 140 | rawmode = "" 141 | if reading: 142 | rawmode += "r" 143 | if writing: 144 | rawmode += "w" 145 | raw = socket.SocketIO(self, rawmode) # type: ignore[arg-type] 146 | self.socket._io_refs += 1 # type: ignore[attr-defined] 147 | if buffering is None: 148 | buffering = -1 149 | if buffering < 0: 150 | buffering = io.DEFAULT_BUFFER_SIZE 151 | if buffering == 0: 152 | if not binary: 153 | raise ValueError("unbuffered streams must be binary") 154 | return raw 155 | buffer: typing.BinaryIO 156 | if reading and writing: 157 | buffer = io.BufferedRWPair(raw, raw, buffering) # type: ignore[assignment] 158 | elif reading: 159 | buffer = io.BufferedReader(raw, buffering) 160 | else: 161 | assert writing 162 | buffer = io.BufferedWriter(raw, buffering) 163 | if binary: 164 | return buffer 165 | text = io.TextIOWrapper(buffer, encoding, errors, newline) 166 | text.mode = mode # type: ignore[misc] 167 | return text 168 | 169 | def unwrap(self) -> None: 170 | self._ssl_io_loop(self.sslobj.unwrap) 171 | 172 | def close(self) -> None: 173 | self.socket.close() 174 | 175 | @typing.overload 176 | def getpeercert( 177 | self, binary_form: Literal[False] = ... 178 | ) -> _TYPE_PEER_CERT_RET_DICT | None: 179 | ... 180 | 181 | @typing.overload 182 | def getpeercert(self, binary_form: Literal[True]) -> bytes | None: 183 | ... 184 | 185 | def getpeercert(self, binary_form: bool = False) -> _TYPE_PEER_CERT_RET: 186 | return self.sslobj.getpeercert(binary_form) # type: ignore[return-value] 187 | 188 | def version(self) -> str | None: 189 | return self.sslobj.version() 190 | 191 | def cipher(self) -> tuple[str, str, int] | None: 192 | return self.sslobj.cipher() 193 | 194 | def selected_alpn_protocol(self) -> str | None: 195 | return self.sslobj.selected_alpn_protocol() 196 | 197 | def selected_npn_protocol(self) -> str | None: 198 | return self.sslobj.selected_npn_protocol() 199 | 200 | def shared_ciphers(self) -> list[tuple[str, str, int]] | None: 201 | return self.sslobj.shared_ciphers() 202 | 203 | def compression(self) -> str | None: 204 | return self.sslobj.compression() 205 | 206 | def settimeout(self, value: float | None) -> None: 207 | self.socket.settimeout(value) 208 | 209 | def gettimeout(self) -> float | None: 210 | return self.socket.gettimeout() 211 | 212 | def _decref_socketios(self) -> None: 213 | self.socket._decref_socketios() # type: ignore[attr-defined] 214 | 215 | def _wrap_ssl_read(self, len: int, buffer: bytearray | None = None) -> int | bytes: 216 | try: 217 | return self._ssl_io_loop(self.sslobj.read, len, buffer) 218 | except ssl.SSLError as e: 219 | if e.errno == ssl.SSL_ERROR_EOF and self.suppress_ragged_eofs: 220 | return 0 # eof, return 0. 221 | else: 222 | raise 223 | 224 | # func is sslobj.do_handshake or sslobj.unwrap 225 | @typing.overload 226 | def _ssl_io_loop(self, func: typing.Callable[[], None]) -> None: 227 | ... 228 | 229 | # func is sslobj.write, arg1 is data 230 | @typing.overload 231 | def _ssl_io_loop(self, func: typing.Callable[[bytes], int], arg1: bytes) -> int: 232 | ... 233 | 234 | # func is sslobj.read, arg1 is len, arg2 is buffer 235 | @typing.overload 236 | def _ssl_io_loop( 237 | self, 238 | func: typing.Callable[[int, bytearray | None], bytes], 239 | arg1: int, 240 | arg2: bytearray | None, 241 | ) -> bytes: 242 | ... 243 | 244 | def _ssl_io_loop( 245 | self, 246 | func: typing.Callable[..., _ReturnValue], 247 | arg1: None | bytes | int = None, 248 | arg2: bytearray | None = None, 249 | ) -> _ReturnValue: 250 | """Performs an I/O loop between incoming/outgoing and the socket.""" 251 | should_loop = True 252 | ret = None 253 | 254 | while should_loop: 255 | errno = None 256 | try: 257 | if arg1 is None and arg2 is None: 258 | ret = func() 259 | elif arg2 is None: 260 | ret = func(arg1) 261 | else: 262 | ret = func(arg1, arg2) 263 | except ssl.SSLError as e: 264 | if e.errno not in (ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE): 265 | # WANT_READ, and WANT_WRITE are expected, others are not. 266 | raise e 267 | errno = e.errno 268 | 269 | buf = self.outgoing.read() 270 | self.socket.sendall(buf) 271 | 272 | if errno is None: 273 | should_loop = False 274 | elif errno == ssl.SSL_ERROR_WANT_READ: 275 | buf = self.socket.recv(SSL_BLOCKSIZE) 276 | if buf: 277 | self.incoming.write(buf) 278 | else: 279 | self.incoming.write_eof() 280 | return typing.cast(_ReturnValue, ret) 281 | --------------------------------------------------------------------------------