diff --git a/.env b/.env deleted file mode 100644 index ec9a647..0000000 --- a/.env +++ /dev/null @@ -1,11 +0,0 @@ -MONGO_HOST=mongodb -MONGO_PORT=27017 -MONGO_USER=admin -MONGO_PASS=IHxq1V8j3hwH -MONGO_AUTHSRC=admin - - -# This is the URL of the GESSI dashboard API. -BASE_GESSI_URL =http://gessi-dashboard.essi.upc.edu:8888/api - -QUALITY_MODELS_DIR = ./QUALITY_MODELS diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1e3f799 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,124 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main, dev] + + + +jobs: + test: + permissions: + contents: read + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.14" + cache: pip + cache-dependency-path: requirements.txt + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pytest pytest-cov + + - name: Run tests with coverage + run: pytest --cov=. --cov-report=term --cov-report=xml --cov-report=html + + - name: Upload coverage artifacts + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: | + coverage.xml + htmlcov/ + if-no-files-found: error + + lint: + permissions: + contents: read + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.14" + cache: pip + + - name: Install Ruff + run: | + python -m pip install --upgrade pip + pip install ruff + + - name: Run Ruff + run: ruff check + + bandit: + permissions: + contents: read + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.14" + cache: pip + + - name: Install Bandit + run: | + python -m pip install --upgrade pip + pip install bandit + + - name: Run Bandit + run: bandit -c pyproject.toml -r . -ll -x ./tests/ + + gitleaks: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout repository (full history) + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install Gitleaks (via cli) + run: | + curl -sSL -o /tmp/gitleaks.tar.gz https://github.com/gitleaks/gitleaks/releases/download/v8.30.0/gitleaks_8.30.0_linux_x64.tar.gz + tar xz -C /tmp -f /tmp/gitleaks.tar.gz + sudo mv /tmp/gitleaks /usr/local/bin/gitleaks + sudo chmod +x /usr/local/bin/gitleaks + + - name: Run Gitleaks + run: gitleaks detect --source . --redact --verbose --exit-code 1 + + semgrep: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Run Semgrep + uses: semgrep/semgrep-action@v1 + with: + config: p/security-audit diff --git a/.github/workflows/main-pr.yml b/.github/workflows/main-pr.yml new file mode 100644 index 0000000..bc61ee5 --- /dev/null +++ b/.github/workflows/main-pr.yml @@ -0,0 +1,20 @@ +name: Restrict PR Source + +on: + pull_request: + branches: + - main + +jobs: + check-branch: + runs-on: ubuntu-latest + env: + GITHUB_HEAD_REF: ${{ github.head_ref }} + steps: + + - name: Fail if not from develop + run: | + if [[ $GITHUB_HEAD_REF != "dev" ]]; then + echo "PR must come from develop branch only." + exit 1 + fi diff --git a/.gitignore b/.gitignore index 1bf159f..64d49ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,216 @@ -# .gitignore +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[codz] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py.cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +# Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +# poetry.lock +# poetry.toml + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. +# https://pdm-project.org/en/latest/usage/project/#working-with-version-control +# pdm.lock +# pdm.toml +.pdm-python +.pdm-build/ + +# pixi +# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. +# pixi.lock +# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one +# in the .venv directory. It is recommended not to include this directory in version control. +.pixi + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# Redis +*.rdb +*.aof +*.pid + +# RabbitMQ +mnesia/ +rabbitmq/ +rabbitmq-data/ + +# ActiveMQ +activemq-data/ + +# SageMath parsed files +*.sage.py + +# Environments .env -/menv/ \ No newline at end of file +.envrc +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +# .idea/ + +# Abstra +# Abstra is an AI-powered process automation framework. +# Ignore directories containing user credentials, local state, and settings. +# Learn more at https://abstra.io/docs +.abstra/ + +# Visual Studio Code +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# and can be added to the global gitignore or merged into this file. However, if you prefer, +# you could uncomment the following to ignore the entire vscode folder +# .vscode/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Marimo +marimo/_static/ +marimo/_lsp/ +__marimo__/ + +# Streamlit +.streamlit/secrets.toml \ No newline at end of file diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..07df002 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,10 @@ +title = "LD Eval Event gitleaks overrides" + +[extend] +useDefault = true + +[[allowlists]] +description = "Ignore local virtualenv files" +paths = [ + '''^env/''', +] diff --git a/.semgrepignore b/.semgrepignore new file mode 100644 index 0000000..bdaab25 --- /dev/null +++ b/.semgrepignore @@ -0,0 +1 @@ +env/ diff --git a/API_calls/StudentDatafromLDRESTAPI.py b/API_calls/StudentDatafromLDRESTAPI.py index 1b26741..01da9dd 100644 --- a/API_calls/StudentDatafromLDRESTAPI.py +++ b/API_calls/StudentDatafromLDRESTAPI.py @@ -3,21 +3,21 @@ def fetch_projects() -> list: - ''' + """ Retrieve the list of projects from the LD REST API. - ''' + """ url = f"{BASE_GESSI_URL}/projects" response = requests.get(url, timeout=10) response.raise_for_status() # Raise an exception if status != 200 projects = response.json() - - + return projects + def fetch_project_details(project_id: int) -> dict: - ''' - Retrieve detailed information for a given project ID. - ''' + """ + Retrieve detailed information for a given project ID. + """ url = f"{BASE_GESSI_URL}/projects/{project_id}" response = requests.get(url, timeout=10) response.raise_for_status() @@ -25,9 +25,9 @@ def fetch_project_details(project_id: int) -> dict: def build_team_students_map() -> dict: - ''' + """ Build a mapping from project external_id to student identity lists. - ''' + """ # Fetch all projects from the LD REST API projects = fetch_projects() team_students_map = {} @@ -39,15 +39,14 @@ def build_team_students_map() -> dict: details = fetch_project_details(p_id) students_list = details.get("students") or [] - + if not students_list: team_students_map[ext_id] = {} continue - - subdict={} - names_list= [] - - + + subdict = {} + names_list = [] + # details["students"] is an array of student objects if not details["students"]: # no students => maybe skip or store empty @@ -55,12 +54,10 @@ def build_team_students_map() -> dict: continue for student_obj in students_list: - student_name = student_obj.get("name") if student_name: names_list.append(student_name) - - + identities = student_obj.get("identities", {}) # e.g. identities => {"GITHUB": {"username":"danipenalba"}, "TAIGA": {...}, "GITLAB": {...}} @@ -76,23 +73,14 @@ def build_team_students_map() -> dict: subdict["EXCEL"] = names_list team_students_map[ext_id] = subdict + # ADDED MANUALLY TO MAKE THE TESTS WORK, REMOVE LATER - - - # ADDED MANUALLY TO MAKE THE TESTS WORK, REMOVE LATER - + """ team_students_map["LD_Test_Project"] = { "GITHUB": ["PabloGomezNa", "PepitoGomezNa", "charlie"], "TAIGA": ["pgomezn", "pablogz5", "Charlie55"], "EXCEL": ["Pablo", "Marc", "Charlie"] } - + """ return team_students_map - - - - - - - diff --git a/API_calls/__pycache__/StudentDatafromLDRESTAPI.cpython-310.pyc b/API_calls/__pycache__/StudentDatafromLDRESTAPI.cpython-310.pyc deleted file mode 100644 index 03c7625..0000000 Binary files a/API_calls/__pycache__/StudentDatafromLDRESTAPI.cpython-310.pyc and /dev/null differ diff --git a/API_calls/__pycache__/__init__.cpython-310.pyc b/API_calls/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 84d1067..0000000 Binary files a/API_calls/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/API_calls/__pycache__/load_config_file.cpython-310.pyc b/API_calls/__pycache__/load_config_file.cpython-310.pyc deleted file mode 100644 index cc8bbad..0000000 Binary files a/API_calls/__pycache__/load_config_file.cpython-310.pyc and /dev/null differ diff --git a/API_calls/__pycache__/logger_setup.cpython-310.pyc b/API_calls/__pycache__/logger_setup.cpython-310.pyc deleted file mode 100644 index 8811807..0000000 Binary files a/API_calls/__pycache__/logger_setup.cpython-310.pyc and /dev/null differ diff --git a/API_calls/__pycache__/quality_model_config.cpython-310.pyc b/API_calls/__pycache__/quality_model_config.cpython-310.pyc deleted file mode 100644 index e0bd189..0000000 Binary files a/API_calls/__pycache__/quality_model_config.cpython-310.pyc and /dev/null differ diff --git a/API_calls/__pycache__/quality_model_loader.cpython-310.pyc b/API_calls/__pycache__/quality_model_loader.cpython-310.pyc deleted file mode 100644 index d5aa964..0000000 Binary files a/API_calls/__pycache__/quality_model_loader.cpython-310.pyc and /dev/null differ diff --git a/QUALITY_MODELS/AMEP/metrics/acceptance_criteria_check.properties b/QUALITY_MODELS/AMEP/metrics/acceptance_criteria_check.properties index f1673d3..ee2453d 100644 --- a/QUALITY_MODELS/AMEP/metrics/acceptance_criteria_check.properties +++ b/QUALITY_MODELS/AMEP/metrics/acceptance_criteria_check.properties @@ -1,7 +1,7 @@ # metric props name=Acceptance Criteria Application description=Percentage of user stories with acceptance criteria with respect to the total number of user stories in this sprint -factors=userstoriesdefinitionquality +factors=userstoriesdefinition_quality weights=1.0 diff --git a/QUALITY_MODELS/AMEP/metrics/assignedtasks.properties b/QUALITY_MODELS/AMEP/metrics/assignedtasks.properties index 22cc749..b99ffb6 100644 --- a/QUALITY_MODELS/AMEP/metrics/assignedtasks.properties +++ b/QUALITY_MODELS/AMEP/metrics/assignedtasks.properties @@ -1,7 +1,7 @@ # metric props name=tasks description=Percentage of tasks made by a student with respect to the total number of tasks in the project -factors=taskscontribution +factors=taskcontribution weights=1.0 diff --git a/QUALITY_MODELS/AMEP/metrics/closed_tasks_with_AE.properties b/QUALITY_MODELS/AMEP/metrics/closed_tasks_with_AE.properties index 1f06c1c..e361c86 100644 --- a/QUALITY_MODELS/AMEP/metrics/closed_tasks_with_AE.properties +++ b/QUALITY_MODELS/AMEP/metrics/closed_tasks_with_AE.properties @@ -1,7 +1,7 @@ # metric props name=Closed Tasks with Actual Effort Information description=Percentage of closed tasks with actual effort information added with respect to the total number of closed tasks in this sprint -factors=taskseffortinformation +factors=taskeffortinformation weights=1.0 diff --git a/QUALITY_MODELS/AMEP/metrics/closedtasks.properties b/QUALITY_MODELS/AMEP/metrics/closedtasks.properties index 7712805..63d3bc2 100644 --- a/QUALITY_MODELS/AMEP/metrics/closedtasks.properties +++ b/QUALITY_MODELS/AMEP/metrics/closedtasks.properties @@ -1,7 +1,7 @@ # metric props name=closed tasks description=Percentage of closed tasks made by a student with respect to the total number of tasks assigned to student -factors=fulfillmentoftasks +factors=fullfillmentoftasks weights=1.0 diff --git a/QUALITY_MODELS/AMEP/metrics/pattern_check.properties b/QUALITY_MODELS/AMEP/metrics/pattern_check.properties index 1fd93bb..15c175b 100644 --- a/QUALITY_MODELS/AMEP/metrics/pattern_check.properties +++ b/QUALITY_MODELS/AMEP/metrics/pattern_check.properties @@ -1,7 +1,7 @@ # metric props name=Use of User Story pattern description=Percentage of user stories with the pattern (AS - I WANT - SO THAT - ) with respect to the total number of user stories in this sprint -factors=userstoriesdefinitionquality +factors=userstoriesdefinition_quality weights=1.0 diff --git a/QUALITY_MODELS/AMEP/metrics/tasks_with_EE.properties b/QUALITY_MODELS/AMEP/metrics/tasks_with_EE.properties index d04ac37..50cfd16 100644 --- a/QUALITY_MODELS/AMEP/metrics/tasks_with_EE.properties +++ b/QUALITY_MODELS/AMEP/metrics/tasks_with_EE.properties @@ -1,7 +1,7 @@ # metric props name=Tasks with Estimated Effort Information description= Percentage of tasks with estimated effort information added with respect to the total number of tasks in this sprint -factors=taskseffortinformation +factors=taskeffortinformation weights=1.0 diff --git a/QUALITY_MODELS/DEFAULT/metrics/acceptance_criteria_check.properties b/QUALITY_MODELS/DEFAULT/metrics/acceptance_criteria_check.properties index b70b6d4..b5da2ef 100644 --- a/QUALITY_MODELS/DEFAULT/metrics/acceptance_criteria_check.properties +++ b/QUALITY_MODELS/DEFAULT/metrics/acceptance_criteria_check.properties @@ -5,7 +5,7 @@ index=$$taiga.userstory.index enabled=true name=Acceptance Criteria Application description=Percentage of user stories with acceptance criteria with respect to the total number of user stories in this sprint -factors=userstoriesdefinitionquality +factors=userstoriesdefinition_quality weights=1.0 # query results diff --git a/QUALITY_MODELS/DEFAULT/metrics/assignedtasks.properties b/QUALITY_MODELS/DEFAULT/metrics/assignedtasks.properties index 498347d..25afa81 100644 --- a/QUALITY_MODELS/DEFAULT/metrics/assignedtasks.properties +++ b/QUALITY_MODELS/DEFAULT/metrics/assignedtasks.properties @@ -5,7 +5,7 @@ index=$$taiga.task.index enabled=true name=tasks description=Percentage of tasks made by a student with respect to the total number of tasks in the project -factors=taskscontribution +factors=taskcontribution weights=1.0 # query results diff --git a/QUALITY_MODELS/DEFAULT/metrics/closed_tasks_with_AE.properties b/QUALITY_MODELS/DEFAULT/metrics/closed_tasks_with_AE.properties index 8e8b03c..ebd7b3a 100644 --- a/QUALITY_MODELS/DEFAULT/metrics/closed_tasks_with_AE.properties +++ b/QUALITY_MODELS/DEFAULT/metrics/closed_tasks_with_AE.properties @@ -5,7 +5,7 @@ index=$$taiga.task.index enabled=true name=Closed Tasks with Actual Effort Information description=Percentage of closed tasks with actual effort information added with respect to the total number of closed tasks in this sprint -factors=taskseffortinformation +factors=taskeffortinformation weights=1.0 # query results diff --git a/QUALITY_MODELS/DEFAULT/metrics/closedtasks.properties b/QUALITY_MODELS/DEFAULT/metrics/closedtasks.properties index 0c80a6a..7e4641c 100644 --- a/QUALITY_MODELS/DEFAULT/metrics/closedtasks.properties +++ b/QUALITY_MODELS/DEFAULT/metrics/closedtasks.properties @@ -5,7 +5,7 @@ index=$$taiga.task.index enabled=true name=closed tasks description=Percentage of closed tasks made by a student with respect to the total number of tasks assigned to student -factors=fulfillmentoftasks +factors=fullfillmentoftasks weights=1.0 # query results diff --git a/QUALITY_MODELS/DEFAULT/metrics/pattern_check.properties b/QUALITY_MODELS/DEFAULT/metrics/pattern_check.properties index f569a51..7eabcce 100644 --- a/QUALITY_MODELS/DEFAULT/metrics/pattern_check.properties +++ b/QUALITY_MODELS/DEFAULT/metrics/pattern_check.properties @@ -5,7 +5,7 @@ index=$$taiga.userstory.index enabled=true name=Use of User Story pattern description=Percentage of user stories with the pattern (AS - I WANT - SO THAT - ) with respect to the total number of user stories in this sprint -factors=userstoriesdefinitionquality +factors=userstoriesdefinition_quality weights=1.0 # query results diff --git a/QUALITY_MODELS/DEFAULT/metrics/tasks_with_EE.properties b/QUALITY_MODELS/DEFAULT/metrics/tasks_with_EE.properties index 2ba726b..bc78555 100644 --- a/QUALITY_MODELS/DEFAULT/metrics/tasks_with_EE.properties +++ b/QUALITY_MODELS/DEFAULT/metrics/tasks_with_EE.properties @@ -5,7 +5,7 @@ index=$$taiga.task.index enabled=true name=Tasks with Estimated Effort Information description= Percentage of tasks with estimated effort information added with respect to the total number of tasks in this sprint -factors=taskseffortinformation +factors=taskeffortinformation weights=1.0 # query results diff --git a/QUALITY_MODELS/DEFAULT/metrics/test copy.properties b/QUALITY_MODELS/DEFAULT/metrics/test copy.properties deleted file mode 100644 index e25d212..0000000 --- a/QUALITY_MODELS/DEFAULT/metrics/test copy.properties +++ /dev/null @@ -1,22 +0,0 @@ -# values starting with $$ are looked up in project.properties -index=$$github.index - -# metric props -enabled=true -name=Number of Students commits -description=Number of commits made by a student -factors=commitscontribution -weights=1.0 - -# query results -result.commitsAssignee=commitsAssignee - -# metric defines a formula based on execution results of parameter- and metric-queries -metric= commitsAssignee -onError=set0 - - -#possible related_events sent by LD_connect from GITHUB: push, issues from TAIGA: issue, epic, task, userstory, relatedusertory -relatedEvent=push -scope=individual_only - diff --git a/QUALITY_MODELS/DEFAULT/metrics/test copy.query b/QUALITY_MODELS/DEFAULT/metrics/test copy.query deleted file mode 100644 index 60f752b..0000000 --- a/QUALITY_MODELS/DEFAULT/metrics/test copy.query +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "$group": { - "_id": null, - "commitsAssignee": { - "$sum": { - "$cond": [ - { - "$eq": [ - "$user.login", - "$$studentUser" - ] - }, - 1, - 0 - ] - } - } - } - }, - { - "$project": { - "_id": 0, - "commitsAssignee": 1 - } - } -] diff --git a/QUALITY_MODELS/DEFAULT/metrics/test.properties b/QUALITY_MODELS/DEFAULT/metrics/test.properties deleted file mode 100644 index e25d212..0000000 --- a/QUALITY_MODELS/DEFAULT/metrics/test.properties +++ /dev/null @@ -1,22 +0,0 @@ -# values starting with $$ are looked up in project.properties -index=$$github.index - -# metric props -enabled=true -name=Number of Students commits -description=Number of commits made by a student -factors=commitscontribution -weights=1.0 - -# query results -result.commitsAssignee=commitsAssignee - -# metric defines a formula based on execution results of parameter- and metric-queries -metric= commitsAssignee -onError=set0 - - -#possible related_events sent by LD_connect from GITHUB: push, issues from TAIGA: issue, epic, task, userstory, relatedusertory -relatedEvent=push -scope=individual_only - diff --git a/QUALITY_MODELS/DEFAULT/metrics/test.query b/QUALITY_MODELS/DEFAULT/metrics/test.query deleted file mode 100644 index 60f752b..0000000 --- a/QUALITY_MODELS/DEFAULT/metrics/test.query +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "$group": { - "_id": null, - "commitsAssignee": { - "$sum": { - "$cond": [ - { - "$eq": [ - "$user.login", - "$$studentUser" - ] - }, - 1, - 0 - ] - } - } - } - }, - { - "$project": { - "_id": 0, - "commitsAssignee": 1 - } - } -] diff --git a/__pycache__/app.cpython-310.pyc b/__pycache__/app.cpython-310.pyc deleted file mode 100644 index 5564c0f..0000000 Binary files a/__pycache__/app.cpython-310.pyc and /dev/null differ diff --git a/__pycache__/app.cpython-311.pyc b/__pycache__/app.cpython-311.pyc deleted file mode 100644 index 7f39039..0000000 Binary files a/__pycache__/app.cpython-311.pyc and /dev/null differ diff --git a/__pycache__/ld_refresh.cpython-310.pyc b/__pycache__/ld_refresh.cpython-310.pyc deleted file mode 100644 index 6b7613b..0000000 Binary files a/__pycache__/ld_refresh.cpython-310.pyc and /dev/null differ diff --git a/app.py b/app.py index 4fec048..b0bcb9f 100644 --- a/app.py +++ b/app.py @@ -1,15 +1,24 @@ +import json +import os import threading from flask import Flask, request, jsonify import logging +from datetime import datetime from logic.metrics_logic.metric_event_mapping import build_metrics_index_per_qm -from logic.metrics_logic.metric_recalculation import compute_metric_for_student, compute_metric_for_team +from logic.metrics_logic.metric_recalculation import ( + compute_metric_for_student, + compute_metric_for_team, +) from logic.factors_logic.factor_event_mapping import build_factors_index_per_qm from logic.factors_logic.factor_recalculation import compute_factor, latest_metric_value from logic.indicators_logic.indicator_event_mapping import build_indicators_index_per_qm -from logic.indicators_logic.indicator_recalculation import compute_indicator, latest_factor_value +from logic.indicators_logic.indicator_recalculation import ( + compute_indicator, + latest_factor_value, +) from config.load_config_file import get_event_meta from config.logger_config import setup_logging @@ -18,7 +27,13 @@ from config.settings import QUALITY_MODELS_DIR -from config_files.config_variables import _Start_scheduler_date, _End_scheduler_date, _Hour_scheduler, _Minute_scheduler, _Second_scheduler +from config_files.config_variables import ( + _Start_scheduler_date, + _End_scheduler_date, + _Hour_scheduler, + _Minute_scheduler, + _Second_scheduler, +) from apscheduler.schedulers.background import BackgroundScheduler from pytz import timezone @@ -30,48 +45,52 @@ logger = logging.getLogger(__name__) - - app = Flask(__name__) # Build the metrics event map at startup scaning all the quality models metrics subfolders -ALL_METRICS_BY_QM, EVENT_METRICS_BY_QM = build_metrics_index_per_qm(QUALITY_MODELS_DIR) +ALL_METRICS_BY_QM, EVENT_METRICS_BY_QM = build_metrics_index_per_qm(QUALITY_MODELS_DIR) # Build the metrics event map at startup scaning all the quality models factors subfolders -ALL_FACTORS_BY_QM, EVENT_FACTORS_BY_QM = build_factors_index_per_qm(QUALITY_MODELS_DIR) +ALL_FACTORS_BY_QM, EVENT_FACTORS_BY_QM = build_factors_index_per_qm(QUALITY_MODELS_DIR) # Build the metrics event map at startup scaning all the quality models indicators subfolders -ALL_INDICATORS_BY_QM, EVENT_INDICATORS_BY_QM = build_indicators_index_per_qm(QUALITY_MODELS_DIR) +ALL_INDICATORS_BY_QM, EVENT_INDICATORS_BY_QM = build_indicators_index_per_qm( + QUALITY_MODELS_DIR +) # Build the team->students map at startup TEAM_STUDENTS_MAP = build_team_students_map() +logging.getLogger(__name__).info( + "MAPA PROJECTES-ESTUDIANTS: %s", + json.dumps(TEAM_STUDENTS_MAP, indent=2, ensure_ascii=False), +) TEAM_QUALITYMODEL_MAP = load_qualitymodel_map() - def background_process_event(event_data): - event_type = event_data.get("event_type") external_id = event_data.get("prj") author_name = event_data.get("author_login") - - #If there is no quality model, we assume it is the default one - quality_model_in_query = (event_data.get("quality_model")) - quality_model = choose_qualitymodel(external_id, quality_model_in_query, TEAM_QUALITYMODEL_MAP) - + + # If there is no quality model, we assume it is the default one + quality_model_in_query = event_data.get("quality_model") + quality_model = choose_qualitymodel( + external_id, quality_model_in_query, TEAM_QUALITYMODEL_MAP + ) + if not external_id: logger.warning("No 'team_external_id' found in event_data, skipping.") - return + return # Using the sources_config.json file, we extract the data source for the event type meta = get_event_meta(event_type) - - # Retrieve the students for that team with the corresponding data source - data_source= meta["data_source"] - students=TEAM_STUDENTS_MAP.get(external_id, {}).get(data_source, []) - logger.info(f"Event={event_type}, team with external_id={external_id}, students={students}, quality_model={quality_model}") - + # Retrieve the students for that team with the corresponding data source + data_source = meta["data_source"] + students = TEAM_STUDENTS_MAP.get(external_id, {}).get(data_source, []) + logger.info( + f"Event={event_type}, team with external_id={external_id}, students={students}, quality_model={quality_model}" + ) # RECALCULTION OF THE METRICS # Retrieve the triggered metrics for the quality model @@ -83,54 +102,63 @@ def background_process_event(event_data): scope = metric_def["scope"] if scope == "individual": for student_name in students: - compute_metric_for_student(metric_def, event_type, student_name, team_name=external_id) + compute_metric_for_student( + metric_def, event_type, student_name, team_name=external_id + ) elif scope == "team": # scope == "team" - compute_metric_for_team(metric_def, event_type, team_name=external_id,students=students) - else: #scope == "individual_only" - compute_metric_for_student(metric_def, event_type, author_name, team_name=external_id) - - + compute_metric_for_team( + metric_def, event_type, team_name=external_id, students=students + ) + else: # scope == "individual_only" + compute_metric_for_student( + metric_def, event_type, author_name, team_name=external_id + ) # RECALCULTION OF THE FACTORS triggered_factors = EVENT_FACTORS_BY_QM.get(quality_model, {}).get(event_type, []) logger.info(f"Triggered factors: {[f['name'] for f in triggered_factors]}") - + for factor_def in triggered_factors: - factor_values= {} # Empty dictionary to store the values for each metric in the factor - + factor_values = {} # Empty dictionary to store the values for each metric in the factor + for metrics in factor_def["metric"]: - #For each metric in the factor, we need to get the latest value for that metric + # For each metric in the factor, we need to get the latest value for that metric # We need to store these values in a dictionary with the metric name as key and the value as value factor_values[metrics] = latest_metric_value(external_id, metrics) - - logger.info(f"Values of the metrics of factor {factor_def['name']}: {factor_values}") - final_value_factors= compute_factor(external_id, factor_def, factor_values) - + logger.info( + f"Values of the metrics of factor {factor_def['name']}: {factor_values}" + ) + final_value_factors = compute_factor(external_id, factor_def, factor_values) + logger.info(f"Final value of factor {factor_def['name']}: {final_value_factors}") # RECALCULTION OF THE INDICATORS - triggered_indicators = EVENT_INDICATORS_BY_QM.get(quality_model, {}).get(event_type, []) + triggered_indicators = EVENT_INDICATORS_BY_QM.get(quality_model, {}).get( + event_type, [] + ) logger.info(f"Triggered factors: {[f['name'] for f in triggered_indicators]}") logger.info(f"Triggered indicators: {[i['name'] for i in triggered_indicators]}") - + for indicator_def in triggered_indicators: - indicator_values= {} # Empty dictionary to store the values for each metric in the factor - + indicator_values = {} # Empty dictionary to store the values for each metric in the factor + for factors in indicator_def["factor"]: - #For each metric in the factor, we need to get the latest value for that metric + # For each metric in the factor, we need to get the latest value for that metric # We need to store these values in a dictionary with the metric name as key and the value as value indicator_values[factors] = latest_factor_value(external_id, factors) - - logger.info(f"Values of the factor of indicator {indicator_def['name']}: {indicator_values}") - final_value_indicator= compute_indicator(external_id, indicator_def, indicator_values) - - logger.info("Done processing event.") + logger.info( + f"Values of the factor of indicator {indicator_def['name']}: {indicator_values}" + ) + final_value_indicator = compute_indicator( + external_id, indicator_def, indicator_values + ) + logger.info(f"Final value of indicator {indicator_def['name']}: {final_value_indicator}") + logger.info("Done processing event.") def start_scheduler(): - schedule = BackgroundScheduler(timezone=timezone("Europe/Madrid")) schedule.add_job( func=run_daily_refresh, @@ -160,22 +188,104 @@ def handle_event(): return jsonify({"status": "received"}), 200 + +###################################### NEW CODE ###################################### +@app.route("/api/refresh", methods=["POST"]) +def handle_refresh(): + """ + HTTP endpoint per refrescar el mapa d'estudiants i recalcular mètriques. + Reconstrueix la variable global TEAM_STUDENTS_MAP. + """ + + def refresh_and_update_map(): + global TEAM_STUDENTS_MAP + + logger.info("=" * 80) + logger.info("🔄 REFRESH INICIAT") + logger.info("=" * 80) + + # Mostrar mapa ABANS del refresh + logger.info("📋 MAPA ABANS DEL REFRESH:") + for team_id, sources in TEAM_STUDENTS_MAP.items(): + excel_students = sources.get("EXCEL", []) + logger.info(f" - {team_id}: {len(excel_students)} estudiants (EXCEL)") + + # CRÍTICO: Reconstruir el mapa global desde PostgreSQL + logger.info("🔄 Reconstruint TEAM_STUDENTS_MAP des de PostgreSQL...") + TEAM_STUDENTS_MAP = build_team_students_map() + + # Mostrar mapa DESPRÉS del refresh + logger.info("✅ MAPA DESPRÉS DEL REFRESH:") + for team_id, sources in TEAM_STUDENTS_MAP.items(): + excel_students = sources.get("EXCEL", []) + logger.info(f" - {team_id}: {len(excel_students)} estudiants (EXCEL)") + + logger.info("=" * 80) + logger.info(f"✅ Mapa actualitzat amb {len(TEAM_STUDENTS_MAP)} equips") + logger.info("=" * 80) + + # Executar refresh diari (recalcular totes les mètriques) + logger.info("🔄 Executant recàlcul de mètriques...") + run_daily_refresh() + logger.info("✅ Refresh completat") + logging.getLogger(__name__).info( + "MAPA PROJECTES-ESTUDIANTS AFTER REFRESH: %s", + json.dumps(TEAM_STUDENTS_MAP, indent=2, ensure_ascii=False), + ) + + threading.Thread(target=refresh_and_update_map).start() + return jsonify({"status": "refresh started"}), 200 + + +@app.route("/api/debug/students-map", methods=["GET"]) +def get_students_map(): + """ + Endpoint de debug per veure el mapa d'estudiants actual. + Mostra quin worker i thread gestiona la petició. + """ + result = {} + for team_id, sources in TEAM_STUDENTS_MAP.items(): + result[team_id] = { + "EXCEL": sources.get("EXCEL", []), + "GITHUB": sources.get("GITHUB", []), + "TAIGA": sources.get("TAIGA", []), + "total_students": len(sources.get("EXCEL", [])), + } + + return jsonify( + { + "TEAM_STUDENTS_MAP": result, + "total_teams": len(TEAM_STUDENTS_MAP), + "timestamp": datetime.now().isoformat(), + # INFORMACIÓ DEL WORKER/THREAD + "worker_info": { + "process_id": os.getpid(), # PID del worker + "thread_id": threading.current_thread().ident, # ID del thread + "thread_name": threading.current_thread().name, # Nom del thread + }, + } + ), 200 + + +####################################### END NEW CODE ###################################### + + def run_app(): # Runs the Flask app - app.run(host="0.0.0.0", port=5001, debug=False, use_reloader=False) - + host = os.getenv("FLASK_HOST", "127.0.0.1") + port = int(os.getenv("FLASK_PORT", "5001")) + debug = os.getenv("FLASK_DEBUG", "").lower() in {"1", "true", "yes", "on"} + app.run(host=host, port=port, debug=debug) def create_app(): """ Create and configure the Flask application. """ - if not hasattr(app, "scheduler_started"): - start_scheduler() - app.scheduler_started = True - return app + return app # ← la variable que ya tienes arriba + if __name__ == "__main__": # Run the app directly - start_scheduler() # Start the background scheduler, - run_app() \ No newline at end of file + start_scheduler() # Start the background scheduler, + run_app() diff --git a/config/__pycache__/__init__.cpython-310.pyc b/config/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 1747361..0000000 Binary files a/config/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/config/__pycache__/load_config_file.cpython-310.pyc b/config/__pycache__/load_config_file.cpython-310.pyc deleted file mode 100644 index 26c0401..0000000 Binary files a/config/__pycache__/load_config_file.cpython-310.pyc and /dev/null differ diff --git a/config/__pycache__/logger_config.cpython-310.pyc b/config/__pycache__/logger_config.cpython-310.pyc deleted file mode 100644 index 2ede9d2..0000000 Binary files a/config/__pycache__/logger_config.cpython-310.pyc and /dev/null differ diff --git a/config/__pycache__/quality_model_config.cpython-310.pyc b/config/__pycache__/quality_model_config.cpython-310.pyc deleted file mode 100644 index bac435b..0000000 Binary files a/config/__pycache__/quality_model_config.cpython-310.pyc and /dev/null differ diff --git a/config/__pycache__/settings.cpython-310.pyc b/config/__pycache__/settings.cpython-310.pyc deleted file mode 100644 index c6efd1b..0000000 Binary files a/config/__pycache__/settings.cpython-310.pyc and /dev/null differ diff --git a/config/load_config_file.py b/config/load_config_file.py index 23a1f94..e3d72f1 100644 --- a/config/load_config_file.py +++ b/config/load_config_file.py @@ -3,24 +3,23 @@ from typing import Optional, Dict, List -def load_sources_config()-> dict: - ''' +def load_sources_config() -> dict: + """ Load the JSON configuration for event sources. - ''' - # Determine config file path (env override or default) + """ + # Determine config file path (env override or default) config_path = os.getenv("SOURCES_CONFIG", "config_files/sources_config.json") # Read and parse the JSON configuration with open(config_path, "r", encoding="utf-8") as file: return json.load(file) - def get_event_meta(event_type: str) -> Optional[Dict]: - ''' + """ Retrieve metadata for a given event type from sources_config. - ''' - event_meta=load_sources_config().get(event_type) - + """ + event_meta = load_sources_config().get(event_type) + return event_meta @@ -28,4 +27,4 @@ def get_available_events() -> List[str]: """ Returns a list of all available event types from the sources_config.json file. """ - return list(load_sources_config().keys()) \ No newline at end of file + return list(load_sources_config().keys()) diff --git a/config/logger_config.py b/config/logger_config.py index f755f71..7547fd1 100644 --- a/config/logger_config.py +++ b/config/logger_config.py @@ -4,6 +4,7 @@ _DEFAULT_FMT = "%(asctime)s [%(levelname)s] %(name)s: %(message)s" + def setup_logging() -> None: """ Configure the logger. diff --git a/config/quality_model_config.py b/config/quality_model_config.py index be3840c..3f2dd73 100644 --- a/config/quality_model_config.py +++ b/config/quality_model_config.py @@ -2,20 +2,24 @@ from typing import Optional -def load_qualitymodel_map(path: str = "config_files/quality_models_teams_config.json") -> dict: - ''' +def load_qualitymodel_map( + path: str = "config_files/quality_models_teams_config.json", +) -> dict: + """ Load a mapping from external project IDs to quality model keys. - ''' + """ with open(path, encoding="utf-8") as fh: raw = json.load(fh) # {'aws': ['AMEP11Beats', ...], ... } → {'AMEP11Beats':'aws', ...} return {team: qm.lower() for qm, teams in raw.items() for team in teams} -def choose_qualitymodel(external_id: str, explicit_qm: Optional[str], qm_map: dict) -> str: - ''' +def choose_qualitymodel( + external_id: str, explicit_qm: Optional[str], qm_map: dict +) -> str: + """ Decide which quality model to use for a project. Either the one specified in the event or the one from the config file. - ''' + """ if explicit_qm: return explicit_qm.lower() - return qm_map.get(external_id, "default") \ No newline at end of file + return qm_map.get(external_id, "default") diff --git a/config/settings.py b/config/settings.py index 4440dec..302986a 100644 --- a/config/settings.py +++ b/config/settings.py @@ -7,29 +7,26 @@ # Load environment variables from the .env file -load_dotenv(BASE_DIR / '.env') - - +load_dotenv(BASE_DIR / ".env") QUALITY_MODELS_DIR = os.getenv("QUALITY_MODELS_DIR", "QUALITY_MODELS") BASE_GESSI_URL = os.getenv("BASE_GESSI_URL", "") -#Mongo database settings -MONGO_HOST = os.getenv("MONGO_HOST", "mongodb") -MONGO_PORT = os.getenv("MONGO_PORT", "27017") -MONGO_DB = os.getenv("MONGO_DB", "event_dashboard") -MONGO_USER = os.getenv("MONGO_USER", "") -MONGO_PASS = os.getenv("MONGO_PASS", "") -MONGO_AUTHSRC = os.getenv("MONGO_AUTHSRC", MONGO_DB) +# Mongo database settings +MONGO_HOST = os.getenv("MONGO_HOST", "mongodb") +MONGO_PORT = os.getenv("MONGO_PORT", "27017") +MONGO_DB = os.getenv("MONGO_DB", "mongo") +MONGO_USER = os.getenv("MONGO_USER", "") +MONGO_PASS = os.getenv("MONGO_PASS", "") +MONGO_AUTHSRC = os.getenv("MONGO_AUTHSRC", MONGO_DB) if MONGO_USER and MONGO_PASS: - MONGO_URI = (f"mongodb://{MONGO_USER}:{MONGO_PASS}" - f"@{MONGO_HOST}:{MONGO_PORT}/{MONGO_DB}" - f"?authSource={MONGO_AUTHSRC}") + MONGO_URI = ( + f"mongodb://{MONGO_USER}:{MONGO_PASS}" + f"@{MONGO_HOST}:{MONGO_PORT}/{MONGO_DB}" + f"?authSource={MONGO_AUTHSRC}" + ) else: MONGO_URI = f"mongodb://{MONGO_HOST}:{MONGO_PORT}/{MONGO_DB}" - -MONGO_URI = "mongodb://localhost:27017" - diff --git a/config_files/__pycache__/config_variables.cpython-310.pyc b/config_files/__pycache__/config_variables.cpython-310.pyc deleted file mode 100644 index b2a0bd8..0000000 Binary files a/config_files/__pycache__/config_variables.cpython-310.pyc and /dev/null differ diff --git a/config_files/config_variables.py b/config_files/config_variables.py index e679ea7..ebf18ae 100644 --- a/config_files/config_variables.py +++ b/config_files/config_variables.py @@ -1,8 +1,8 @@ # Scheduler configuration variables # The date must be in the format YYYY-MM-DD. It must contain something, an empty string will not work and will raise an error. -_Start_scheduler_date = "2025-06-10" +_Start_scheduler_date = "2025-06-10" _End_scheduler_date = "2025-06-20" _Hour_scheduler = "10" # Runs every day at midnight _Minute_scheduler = "28" -_Second_scheduler = "30" \ No newline at end of file +_Second_scheduler = "30" diff --git a/database/__pycache__/__init__.cpython-310.pyc b/database/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index d233cfe..0000000 Binary files a/database/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/database/__pycache__/mongo_client.cpython-310.pyc b/database/__pycache__/mongo_client.cpython-310.pyc deleted file mode 100644 index 97e8dc8..0000000 Binary files a/database/__pycache__/mongo_client.cpython-310.pyc and /dev/null differ diff --git a/database/__pycache__/quality_model_loader.cpython-310.pyc b/database/__pycache__/quality_model_loader.cpython-310.pyc deleted file mode 100644 index 67c484e..0000000 Binary files a/database/__pycache__/quality_model_loader.cpython-310.pyc and /dev/null differ diff --git a/database/quality_model_loader.py b/database/quality_model_loader.py index 5fc68b9..21ed035 100644 --- a/database/quality_model_loader.py +++ b/database/quality_model_loader.py @@ -1,6 +1,7 @@ import os from collections import defaultdict - + + def scan_quality_model_folder(root_dir, subfolder, props_loader, build_def): """ Scanner for quality-model directories. @@ -16,10 +17,10 @@ def scan_quality_model_folder(root_dir, subfolder, props_loader, build_def): for qm in os.listdir(root_dir): qm_path = os.path.join(root_dir, qm) if not os.path.isdir(qm_path): - continue # Skip if not a directory + continue # Skip if not a directory # Look for the specified subfolder (e.g., "metrics", "factors", "indicators") - folder = os.path.join(qm_path, subfolder) # p.ej. QUALITY_MODELS/AWS/metrics + folder = os.path.join(qm_path, subfolder) # p.ej. QUALITY_MODELS/AWS/metrics if not os.path.isdir(folder): continue @@ -27,10 +28,14 @@ def scan_quality_model_folder(root_dir, subfolder, props_loader, build_def): # Walk through all .properties files in subfolder tree for dirpath, _, files in os.walk(folder): - for fname in [f for f in files if f.endswith(".properties")]: # Only .properties files - fpath = os.path.join(dirpath, fname) - props = props_loader(fpath) # Load properties using the provided loader function - dfn = build_def(props, qm.lower(), fpath) + for fname in [ + f for f in files if f.endswith(".properties") + ]: # Only .properties files + fpath = os.path.join(dirpath, fname) + props = props_loader( + fpath + ) # Load properties using the provided loader function + dfn = build_def(props, qm.lower(), fpath) defs.append(dfn) # Index by each relatedEvent for evt in props.get("relatedEvent", "").split(","): diff --git a/dockerfile b/dockerfile index 1a334f2..6541caa 100644 --- a/dockerfile +++ b/dockerfile @@ -20,4 +20,4 @@ EXPOSE 5001 ENV TZ=Europe/Madrid # Run gunicorn for "app:create_app()" factory function -CMD ["gunicorn", "-k", "gthread", "--bind", "0.0.0.0:5001", "--workers", "4", "--threads", "25", "app:create_app()"] +CMD ["gunicorn", "-k", "gthread", "--bind", "0.0.0.0:5001", "--workers", "1", "--threads", "25", "app:create_app()"] diff --git a/gitleaks b/gitleaks new file mode 100644 index 0000000..99722f9 Binary files /dev/null and b/gitleaks differ diff --git a/ld_refresh.py b/ld_refresh.py index 27a1528..523e1c2 100644 --- a/ld_refresh.py +++ b/ld_refresh.py @@ -1,52 +1,129 @@ -import os, logging, requests +from asyncio.log import logger +import os +import logging +import requests import time from API_calls.StudentDatafromLDRESTAPI import build_team_students_map -from config.quality_model_config import load_qualitymodel_map, choose_qualitymodel -from config.load_config_file import get_event_meta, get_available_events -from database.mongo_client import db +from config.quality_model_config import load_qualitymodel_map, choose_qualitymodel +from config.load_config_file import get_available_events +from database.mongo_client import db -API_URL = os.getenv("EVAL_API_URL", "http://localhost:5001/api/event") -TEAM_STUDENTS = build_team_students_map() -QM_MAP = load_qualitymodel_map() +API_URL = os.getenv("EVAL_API_URL", "http://localhost:5001/api/event") +QM_MAP = load_qualitymodel_map() +EVENT_TYPES = ["push", "task", "userstory"] def team_is_active(team_id: str) -> bool: """Function to check if a team has any activity in the database. If not active, it will not trigger any events.""" - coll = f"metrics.{team_id}" # We will only check the metrics collection, because if it has metrics, the factors and indicators collections will also have data. - return coll in db.list_collection_names() and db[coll].estimated_document_count() > 0 - + coll = f"metrics.{team_id}" # We will only check the metrics collection, because if it has metrics, the factors and indicators collections will also have data. + return ( + coll in db.list_collection_names() and db[coll].estimated_document_count() > 0 + ) def trigger_team_event(team_id: str, event_type: str) -> None: - '''Function to trigger an event for a team. - It will send a POST request to the API with the event data.''' + """Function to trigger an event for a team. + It will send a POST request to the API with the event data.""" payload = { - "event_type" : event_type, - "prj" : team_id, + "event_type": event_type, + "prj": team_id, "author_login": "system", - "quality_model": choose_qualitymodel(team_id, None, QM_MAP) + "quality_model": choose_qualitymodel(team_id, None, QM_MAP), } start = time.perf_counter() - r = requests.post(API_URL, json=payload, timeout=(0.4, 1)) #connect timeout 0.2s, read timeout 1s - logging.info("POST took %.3f s", time.perf_counter() - start) - logging.info("team=%s event=%s → status=%s", team_id, event_type, r.status_code) + r = requests.post( + API_URL, json=payload, timeout=(0.2, 1) + ) # connect timeout 0.2s, read timeout 1s + + logger.info( + f"Triggered event {event_type} for team {team_id} with status code {r.status_code} in {time.perf_counter() - start:.2f} seconds." + ) + +def delete_orphan_collections_from_mongo(actual_teams): + for prefix in [ + "metrics", + "factors", + "strategic_indicators", + ]: # afegeix tots els prefixes que toquin + collections = db.list_collection_names() + for coll in collections: + if coll.startswith(prefix + "."): + team_collection = coll.split(".", 1)[1] + if team_collection not in actual_teams: + db.drop_collection(coll) + + +def delete_orphan_student_documents(team_students_map): + """ + Elimina documentos de estudiantes que ya no existen en el mapa de estudiantes. + Busca en las colecciones metrics, factors y strategic_indicators. + """ + for team_id, sources in team_students_map.items(): + # Obtener lista de estudiantes válidos: incluye nombres reales (EXCEL) + usernames (GITHUB + TAIGA) + valid_students = [] + valid_students.extend(sources.get("EXCEL", [])) + valid_students.extend(sources.get("GITHUB", [])) + valid_students.extend(sources.get("TAIGA", [])) + + # Eliminar duplicados + valid_students = list(set(valid_students)) + + # Limpiar en cada tipo de colección + for prefix in ["metrics", "factors", "strategic_indicators"]: + collection_name = f"{prefix}.{team_id}" + + if collection_name not in db.list_collection_names(): + continue + + collection = db[collection_name] + + # Buscar documentos con student_name que no esté en la lista de válidos + # Los documentos de equipo no tienen student_name, así que los ignoramos + orphan_docs = collection.find( + {"student_name": {"$exists": True, "$nin": valid_students}} + ) + + deleted_count = 0 + for doc in orphan_docs: + doc.get("student_name") + # Intentar obtener el nombre de la métrica/factor/indicador + ( + doc.get("metric_name") + or doc.get("factor_name") + or doc.get("indicator_name") + or doc.get("name", "documento") + ) + + collection.delete_one({"_id": doc["_id"]}) + deleted_count += 1 def run_daily_refresh() -> None: - '''Function to run the daily refresh of events.''' - for team in TEAM_STUDENTS.keys(): # Get all the teams from the TEAM_STUDENTS map + """Function to run the daily refresh of events.""" + TEAM_STUDENTS = build_team_students_map() + actual_teams = list(TEAM_STUDENTS.keys()) + + # 1. Eliminar colecciones de equipos que ya no existen + delete_orphan_collections_from_mongo(actual_teams) + + # 2. Eliminar documentos de estudiantes que ya no están en los equipos + # delete_orphan_student_documents(TEAM_STUDENTS) + + # 3. Recalcular métricas para todos los equipos activos + for team in TEAM_STUDENTS.keys(): # Get all the teams from the TEAM_STUDENTS map + """ if not team_is_active(team): - logging.info("Team %s does not have activity we ommit", team) # If the team is not active, skip it + logging.info("Equipo %s sin actividad previa; se omite.", team) # If the team is not active, skip it continue - - events= get_available_events() - for event in events: # If the team is active, trigger all the events + """ + events = get_available_events() + for event in events: # If the team is active, trigger all the events trigger_team_event(team, event) + if __name__ == "__main__": logging.basicConfig(level=logging.INFO, format="%(levelname)s:%(message)s") run_daily_refresh() - diff --git a/logic/factors_logic/__pycache__/factor_event_mapping.cpython-310.pyc b/logic/factors_logic/__pycache__/factor_event_mapping.cpython-310.pyc deleted file mode 100644 index 063350f..0000000 Binary files a/logic/factors_logic/__pycache__/factor_event_mapping.cpython-310.pyc and /dev/null differ diff --git a/logic/factors_logic/__pycache__/factor_recalculation.cpython-310.pyc b/logic/factors_logic/__pycache__/factor_recalculation.cpython-310.pyc deleted file mode 100644 index 03900f1..0000000 Binary files a/logic/factors_logic/__pycache__/factor_recalculation.cpython-310.pyc and /dev/null differ diff --git a/logic/factors_logic/__pycache__/store_factors_mongo.cpython-310.pyc b/logic/factors_logic/__pycache__/store_factors_mongo.cpython-310.pyc deleted file mode 100644 index b588ac7..0000000 Binary files a/logic/factors_logic/__pycache__/store_factors_mongo.cpython-310.pyc and /dev/null differ diff --git a/logic/factors_logic/factor_event_mapping.py b/logic/factors_logic/factor_event_mapping.py index c75658f..ec92a39 100644 --- a/logic/factors_logic/factor_event_mapping.py +++ b/logic/factors_logic/factor_event_mapping.py @@ -1,23 +1,33 @@ from database.quality_model_loader import scan_quality_model_folder from config.settings import QUALITY_MODELS_DIR + def load_required_fields_factor(filepath: str) -> dict: """ - Reads some allowed keys from the .properties factor files. Returns a dict with those fields. - """ + Reads some allowed keys from the .properties factor files. Returns a dict with those fields. + """ # Allowed keys for the factor properties - allowed_keys = {'name', 'description','metric','formula','weights','relatedEvent', 'indicators', 'category'} + allowed_keys = { + "name", + "description", + "metric", + "formula", + "weights", + "relatedEvent", + "indicators", + "category", + } props = {} params = {} # Read each line, skip comments and blank lines - with open(filepath, 'r', encoding='utf-8') as f: + with open(filepath, "r", encoding="utf-8") as f: for line in f: line = line.strip() - if not line or line.startswith('#'): + if not line or line.startswith("#"): continue - if '=' not in line: + if "=" not in line: continue - key, value = line.split('=', 1) + key, value = line.split("=", 1) key = key.strip() value = value.strip() @@ -26,45 +36,47 @@ def load_required_fields_factor(filepath: str) -> dict: props[key] = value # Capture any param. entries into params dict - elif key.startswith('param.'): - raw = value.strip() #Get the value of the parameter - try: # turn to int/float if we can + elif key.startswith("param."): + raw = value.strip() # Get the value of the parameter + try: # turn to int/float if we can val = int(raw) if raw.isdigit() else float(raw) - except ValueError: #if not a number, keep it as string + except ValueError: # if not a number, keep it as string val = raw - + params[key[6:]] = val - # Attach params if present + # Attach params if present if params: - props['params'] = params - + props["params"] = params + return props def build_factor_def(props: dict, qm: str, path: str) -> dict: - ''' + """ Builds a factor definition from the loaded properties file. - ''' + """ return { "filePath": path, "name": props["name"], - "description": props.get("description",""), - "metric": [m.strip() for m in props.get("metric","").split(",") if m], - "indicators": [i.strip() for i in props.get("indicators","").split(",") if i], + "description": props.get("description", ""), + "metric": [m.strip() for m in props.get("metric", "").split(",") if m], + "indicators": [i.strip() for i in props.get("indicators", "").split(",") if i], "formula": props.get("formula", "average"), - "weights": [w.strip() for w in props.get('weights', '').split(',') if w.strip()], + "weights": [ + w.strip() for w in props.get("weights", "").split(",") if w.strip() + ], "quality_model": qm, "category": props.get("category", "NoCategory"), } + def build_factors_index_per_qm(qm_root=QUALITY_MODELS_DIR): - ''' + """ Scan all quality-model subfolders for factor definitions. - ''' + """ return scan_quality_model_folder( qm_root, subfolder="factors", props_loader=load_required_fields_factor, - build_def=build_factor_def + build_def=build_factor_def, ) - diff --git a/logic/factors_logic/factor_recalculation.py b/logic/factors_logic/factor_recalculation.py index 925574a..0cc1ff0 100644 --- a/logic/factors_logic/factor_recalculation.py +++ b/logic/factors_logic/factor_recalculation.py @@ -2,94 +2,91 @@ from database.mongo_client import get_collection import re -def latest_metric_value(team: str, metric_name: str, student: str = None)-> list: - ''' + +def latest_metric_value(team: str, metric_name: str, student: str = None) -> list: + """ Retrieve the latest metric value(s) from MongoDB for a given metric. - ''' + """ coll = get_collection(f"metrics.{team}") - - - + # TEAM LEVEL METRIC - doc = coll.find_one({'metric': metric_name}, sort=[('evaluationDate', -1)]) + doc = coll.find_one({"metric": metric_name}, sort=[("evaluationDate", -1)]) if doc: - return [(None, doc['value'])] # Team metric hallada - - + return [(None, doc["value"])] # Team metric hallada regex = re.compile(f"^{re.escape(metric_name)}_[a-zA-Z0-9_]+$") - + pipeline = [ - {'$match': { - 'metric': {'$regex':regex}, - 'student_name': {'$exists': True}}}, - {'$sort': {'student_name': 1, 'evaluationDate': -1}}, - {'$group': {'_id': '$student_name', - 'latest': {'$first': '$value'}}} + {"$match": {"metric": {"$regex": regex}, "student_name": {"$exists": True}}}, + {"$sort": {"student_name": 1, "evaluationDate": -1}}, + {"$group": {"_id": "$student_name", "latest": {"$first": "$value"}}}, ] rows = list(coll.aggregate(pipeline)) if rows: - return [(row['_id'], row['latest']) for row in rows] + return [(row["_id"], row["latest"]) for row in rows] # Return list of ("metric_student", latest) + return [ + (None, 0.0) + ] # For instance: {'closedtasks_Student': [('closedtasks_Student_pablogz5', 0.0), ('closedtasks_Student_Charlie55', 0.0), ('closedtasks_Student_pgomezn', 0.5)]} - return [(None, 0.0)] # For instance: {'closedtasks_Student': [('closedtasks_Student_pablogz5', 0.0), ('closedtasks_Student_Charlie55', 0.0), ('closedtasks_Student_pgomezn', 0.5)]} - - - -def compute_factor(team_name: str, factor_def: dict, values_dict: dict)-> tuple: +def compute_factor(team_name: str, factor_def: dict, values_dict: dict) -> tuple: """ Compute a factor's final value based on its constituent metric values. Supports 'average' and 'weighted_average' operations. """ # Loop over the values_dict to get the values, the metric names and students - flat_vals = [] - flat_metric = [] - flat_student = [] - + flat_vals = [] + flat_metric = [] + flat_student = [] # Loop over the values_dict to get the values, the metric names and students for m, tup_list in values_dict.items(): for student, val in tup_list: - flat_vals.append(val) # List of all the values - flat_metric.append(m) # List of the names of the metrics that compose the factors - flat_student.append(student) #List of the students names in case the factor uses an individual metric - - + flat_vals.append(val) # List of all the values + flat_metric.append( + m + ) # List of the names of the metrics that compose the factors + flat_student.append( + student + ) # List of the students names in case the factor uses an individual metric + # No data return 0.0 and "no input" - if not flat_vals: + if not flat_vals: return 0.0, "no input" # Get the operation to be performed from the factor definition - op = factor_def.get('formula', 'average') + op = factor_def.get("formula", "average") # Calculate the final value based on the weighted average - if op == 'average': - final_val = sum(flat_vals)/len(flat_vals) + if op == "average": + final_val = sum(flat_vals) / len(flat_vals) info = f"avg({flat_vals})" # Calculate the final value based on the weighted average - elif op == 'weighted_average': - base_w = [float(w) for w in factor_def.get('weights', [])] - if not base_w or len(base_w) != len(factor_def['metric']): - base_w = [1.0]*len(factor_def['metric']) + elif op == "weighted_average": + base_w = [float(w) for w in factor_def.get("weights", [])] + if not base_w or len(base_w) != len(factor_def["metric"]): + base_w = [1.0] * len(factor_def["metric"]) # Replicate each metric-weight for every student value - metric2weight = dict(zip(factor_def['metric'], base_w)) - w_expanded = [metric2weight[m] for m in flat_metric] + metric2weight = dict(zip(factor_def["metric"], base_w)) + w_expanded = [metric2weight[m] for m in flat_metric] - final_val = sum(v*w for v, w in zip(flat_vals, w_expanded)) / sum(w_expanded) + final_val = sum(v * w for v, w in zip(flat_vals, w_expanded)) / sum(w_expanded) info = f"w_avg({list(zip(flat_metric, flat_vals, w_expanded))})" else: raise ValueError(f"Unknown operation '{op}'") - #Store the factor result in the mongo database - store_factor_result(team_name=team_name, factor_def=factor_def, final_value=final_val, intermediate_metric_values=values_dict) - - return final_val, info - - + # Store the factor result in the mongo database + store_factor_result( + team_name=team_name, + factor_def=factor_def, + final_value=final_val, + intermediate_metric_values=values_dict, + ) + return final_val, info diff --git a/logic/factors_logic/store_factors_mongo.py b/logic/factors_logic/store_factors_mongo.py index 13b28fb..dc5ac81 100644 --- a/logic/factors_logic/store_factors_mongo.py +++ b/logic/factors_logic/store_factors_mongo.py @@ -1,35 +1,36 @@ from datetime import datetime from zoneinfo import ZoneInfo import os -import itertools from database.mongo_client import get_collection -def store_factor_result(team_name:str, factor_def: dict, final_value: float, intermediate_metric_values: dict)-> None: - ''' +def store_factor_result( + team_name: str, + factor_def: dict, + final_value: float, + intermediate_metric_values: dict, +) -> None: + """ Insert a indicator result into the MongoDB database under a certain collection name. - ''' - # Sets the collection name to the team name + "_indicators" + """ + # Sets the collection name to the team name + "_indicators" collection = get_collection(f"factors.{team_name}") # Sets the evaluation date to the current date and time in the Europe/Madrid timezone - #evaluation_date = datetime.now(ZoneInfo("Europe/Madrid")).strftime("%Y-%m-%d,%H:%M:%S") + # evaluation_date = datetime.now(ZoneInfo("Europe/Madrid")).strftime("%Y-%m-%d,%H:%M:%S") evaluation_date = datetime.now(ZoneInfo("Europe/Madrid")).strftime("%Y-%m-%d") # Factor label, its the name .properties file, for instance task_effort.properties metric, the name is task_effort full_path = factor_def["filePath"] factor_name = os.path.splitext(os.path.basename(full_path))[0] - #Map the factor metric to a weight if it exists, otherwise use an empty list - weight_map = dict(zip( - factor_def.get("metric", []), - factor_def.get("weights", []) - )) + # Map the factor metric to a weight if it exists, otherwise use an empty list + weight_map = dict(zip(factor_def.get("metric", []), factor_def.get("weights", []))) - # Build a unique _id: team-factorName-timestamp + # Build a unique _id: team-factorName-timestamp _id = f"{team_name}_{factor_name}_{evaluation_date}" - + # We need to check if the document already exists, and if it does, we increment the number of times modified # Check if the document already exists existing_doc = collection.find_one({"_id": _id}) @@ -37,7 +38,7 @@ def store_factor_result(team_name:str, factor_def: dict, final_value: float, int # If it exists, increment the number of times modified n = existing_doc.get("times_modified", 0) + 1 else: - n=1 + n = 1 metric_parts = [] for metric_root, tuples in intermediate_metric_values.items(): @@ -45,22 +46,18 @@ def store_factor_result(team_name:str, factor_def: dict, final_value: float, int is_weighted = base_w not in (None, 1, 1.0) for student, val in tuples: - if student is None: label = metric_root else: label = f"{metric_root}_{student}" weight_text = f"{str(base_w) if is_weighted else 'no weighted'}" - - metric_parts.append( - f"{label} (value: {round(val, 10)}, {weight_text})" - ) - - metrics_block = "; ".join(metric_parts)+ ";" - formula_name = factor_def.get("formula", "average") - category = factor_def.get("category", "NoCategory") + metric_parts.append(f"{label} (value: {round(val, 10)}, {weight_text})") + + metrics_block = "; ".join(metric_parts) + ";" + formula_name = factor_def.get("formula", "average") + category = factor_def.get("category", "NoCategory") info_field = ( f"metrics: {{ {metrics_block} }}, " @@ -68,42 +65,34 @@ def store_factor_result(team_name:str, factor_def: dict, final_value: float, int f"value: {round(final_value, 10)}, " f"category: {category}" ) - # Part of the mongo document that does not change static = { - "name" : factor_def['name'], - "description" : factor_def['description'], - "project" : team_name, - "factor" : factor_name, - "indicators" : factor_def.get("indicators", []), - "datasource" : "QRapids Dashboard", + "name": factor_def["name"], + "description": factor_def["description"], + "project": team_name, + "factor": factor_name, + "indicators": factor_def.get("indicators", []), + "datasource": "QRapids Dashboard", "missing_metrics": [], "dates_mismatch_days": 0, - "createdAt" : datetime.now(ZoneInfo("Europe/Madrid")).strftime("%Y-%m-%d %H:%M:%S"), + "createdAt": datetime.now(ZoneInfo("Europe/Madrid")).strftime( + "%Y-%m-%d %H:%M:%S" + ), } # Part of the mongo document that will change each time there is an event dynamic = { "evaluationDate": evaluation_date, - "value" : final_value, - "info" : info_field, - "modifiedAt" : datetime.now(ZoneInfo("Europe/Madrid")).strftime("%Y-%m-%d %H:%M:%S"), + "value": final_value, + "info": info_field, + "modifiedAt": datetime.now(ZoneInfo("Europe/Madrid")).strftime( + "%Y-%m-%d %H:%M:%S" + ), "times_modified": n, } - - - - - # Insert into MongoDB, with the dynamic and static parts, upserting or inserting + # Insert into MongoDB, with the dynamic and static parts, upserting or inserting collection.update_one( - {"_id": _id}, - { - "$set": dynamic, - "$setOnInsert": static - }, - upsert=True - ) - - + {"_id": _id}, {"$set": dynamic, "$setOnInsert": static}, upsert=True + ) diff --git a/logic/indicators_logic/__pycache__/indicator_event_mapping.cpython-310.pyc b/logic/indicators_logic/__pycache__/indicator_event_mapping.cpython-310.pyc deleted file mode 100644 index 6162ea5..0000000 Binary files a/logic/indicators_logic/__pycache__/indicator_event_mapping.cpython-310.pyc and /dev/null differ diff --git a/logic/indicators_logic/__pycache__/indicator_recalculation.cpython-310.pyc b/logic/indicators_logic/__pycache__/indicator_recalculation.cpython-310.pyc deleted file mode 100644 index d11d5f1..0000000 Binary files a/logic/indicators_logic/__pycache__/indicator_recalculation.cpython-310.pyc and /dev/null differ diff --git a/logic/indicators_logic/__pycache__/store_indicator_mongo.cpython-310.pyc b/logic/indicators_logic/__pycache__/store_indicator_mongo.cpython-310.pyc deleted file mode 100644 index 31be928..0000000 Binary files a/logic/indicators_logic/__pycache__/store_indicator_mongo.cpython-310.pyc and /dev/null differ diff --git a/logic/indicators_logic/indicator_event_mapping.py b/logic/indicators_logic/indicator_event_mapping.py index f32496e..62f5503 100644 --- a/logic/indicators_logic/indicator_event_mapping.py +++ b/logic/indicators_logic/indicator_event_mapping.py @@ -4,65 +4,74 @@ def load_required_fields_indicator(filepath: str) -> dict: """ - Reads some allowed keys from the .properties indicator files. Returns a dict with those fields. + Reads some allowed keys from the .properties indicator files. Returns a dict with those fields. """ - allowed_keys = {'name', 'description','factor','formula','weights','relatedEvent','category'} + allowed_keys = { + "name", + "description", + "factor", + "formula", + "weights", + "relatedEvent", + "category", + } props = {} params = {} - + # Read each line, skip comments and blank lines - with open(filepath, 'r', encoding='utf-8') as f: + with open(filepath, "r", encoding="utf-8") as f: for line in f: line = line.strip() - if not line or line.startswith('#'): + if not line or line.startswith("#"): continue - if '=' not in line: + if "=" not in line: continue - key, value = line.split('=', 1) + key, value = line.split("=", 1) key = key.strip() value = value.strip() # Capture only allowed keys if key in allowed_keys: props[key] = value # Capture any param. entries into params dict - elif key.startswith('param.'): - raw = value.strip() #Get the value of the parameter - try: # turn to int/float if we can + elif key.startswith("param."): + raw = value.strip() # Get the value of the parameter + try: # turn to int/float if we can val = int(raw) if raw.isdigit() else float(raw) - except ValueError: #if not a number, keep it as string + except ValueError: # if not a number, keep it as string val = raw - + params[key[6:]] = val # Attach params if present if params: - props['params'] = params - + props["params"] = params + return props def build_indicator_def(props: dict, qm: str, path: str) -> dict: - ''' - Builds a indicator definition from the loaded properties file.''' + """ + Builds a indicator definition from the loaded properties file.""" return { "filePath": path, "name": props["name"], - "description": props.get("description",""), - "factor": [m.strip() for m in props.get("factor","").split(",") if m], + "description": props.get("description", ""), + "factor": [m.strip() for m in props.get("factor", "").split(",") if m], "formula": props.get("formula", "average"), - "weights": [w.strip() for w in props.get('weights', '').split(',') if w.strip()], + "weights": [ + w.strip() for w in props.get("weights", "").split(",") if w.strip() + ], "quality_model": qm, "category": props.get("category", "NoCategory"), } + def build_indicators_index_per_qm(qm_root=QUALITY_MODELS_DIR): - ''' + """ Scan all quality-model subfolders for indicator definitions. - ''' + """ return scan_quality_model_folder( qm_root, subfolder="indicators", props_loader=load_required_fields_indicator, - build_def=build_indicator_def + build_def=build_indicator_def, ) - - diff --git a/logic/indicators_logic/indicator_recalculation.py b/logic/indicators_logic/indicator_recalculation.py index a7d67c1..5e089cb 100644 --- a/logic/indicators_logic/indicator_recalculation.py +++ b/logic/indicators_logic/indicator_recalculation.py @@ -1,22 +1,22 @@ from logic.indicators_logic.store_indicator_mongo import store_indicator_result from database.mongo_client import get_collection + def latest_factor_value(team, factor_name, student=None): - ''' + """ Retrieve the latest factor value(s) from MongoDB for a given metric. - ''' - + """ # Sets the collection name to the team name + "_factors" coll = get_collection(f"factors.{team}") # Try finding any document for this factor - doc = coll.find_one({'factor': factor_name}) - - #if no document found, return empty list - if doc is None: + doc = coll.find_one({"factor": factor_name}) + + # if no document found, return empty list + if doc is None: return [(None, 0.0)] - - # if 'student_name' in doc: #means we have one individual value per student + + # if 'student_name' in doc: #means we have one individual value per student # pipeline = [ # {'$match': {'metric': factor_name}}, # {'$sort': {'student_name': 1, 'evaluationDate': -1}}, @@ -24,64 +24,67 @@ def latest_factor_value(team, factor_name, student=None): # 'latest': {'$first': '$value'}}} # ] # return [(doc['_id'], doc['latest']) for doc in coll.aggregate(pipeline)] - - + # If its a team metric, only one per team, we find it and return it as is - doc = coll.find_one({'factor': factor_name}, sort=[('evaluationDate', -1)]) - - return [(None, doc['value'])] if doc else [] + doc = coll.find_one({"factor": factor_name}, sort=[("evaluationDate", -1)]) + return [(None, doc["value"])] if doc else [] -def compute_indicator(team_name: str, indicator_def: dict, values_dict: dict)-> tuple: +def compute_indicator(team_name: str, indicator_def: dict, values_dict: dict) -> tuple: """ Compute a factor's final value based on its constituent metric values. Supports 'average' and 'weighted_average' operations. """ # Loop over the values_dict to get the values, the metric names and students - flat_vals = [] - flat_metric = [] # parallel list remembering which metric produced each val - flat_student = [] # parallel list remembering which student produced each val - + flat_vals = [] + flat_metric = [] # parallel list remembering which metric produced each val + flat_student = [] # parallel list remembering which student produced each val + # Loop over the values_dict to get the values, the metric names and students for m, tup_list in values_dict.items(): for student, val in tup_list: - flat_vals.append(val) # List of all the values - flat_metric.append(m) # List of the names of the metrics that compose the factors - flat_student.append(student) #List of the students names in case the factor uses an individual metric - - + flat_vals.append(val) # List of all the values + flat_metric.append( + m + ) # List of the names of the metrics that compose the factors + flat_student.append( + student + ) # List of the students names in case the factor uses an individual metric + # No data return 0.0 and "no input" - if not flat_vals: + if not flat_vals: return 0.0, "no input" # Get the operation to be performed from the indicator definition - op = indicator_def.get('formula', 'average') + op = indicator_def.get("formula", "average") # Calculate the final value based on the weighted average - if op == 'average': - final_val = sum(flat_vals)/len(flat_vals) + if op == "average": + final_val = sum(flat_vals) / len(flat_vals) info = f"avg({flat_vals})" # Calculate the final value based on the weighted average - elif op == 'weighted_average': - base_w = [float(w) for w in indicator_def.get('weights', [])] - if not base_w or len(base_w) != len(indicator_def['metric']): - base_w = [1.0]*len(indicator_def['metric']) + elif op == "weighted_average": + base_w = [float(w) for w in indicator_def.get("weights", [])] + if not base_w or len(base_w) != len(indicator_def["metric"]): + base_w = [1.0] * len(indicator_def["metric"]) # replicate each metric-weight for every student value - metric2weight = dict(zip(indicator_def['metric'], base_w)) - w_expanded = [metric2weight[m] for m in flat_metric] + metric2weight = dict(zip(indicator_def["metric"], base_w)) + w_expanded = [metric2weight[m] for m in flat_metric] - final_val = sum(v*w for v, w in zip(flat_vals, w_expanded)) / sum(w_expanded) + final_val = sum(v * w for v, w in zip(flat_vals, w_expanded)) / sum(w_expanded) info = f"w_avg({list(zip(flat_metric, flat_vals, w_expanded))})" else: raise ValueError(f"Unknown operation '{op}'") - #Store the factor result in the mongo database - store_indicator_result(team_name=team_name, indicator_def=indicator_def, final_value=final_val, intermediate_factor_values=values_dict) - - return final_val, info - - + # Store the factor result in the mongo database + store_indicator_result( + team_name=team_name, + indicator_def=indicator_def, + final_value=final_val, + intermediate_factor_values=values_dict, + ) + return final_val, info diff --git a/logic/indicators_logic/store_indicator_mongo.py b/logic/indicators_logic/store_indicator_mongo.py index 985732b..9af52e1 100644 --- a/logic/indicators_logic/store_indicator_mongo.py +++ b/logic/indicators_logic/store_indicator_mongo.py @@ -4,12 +4,17 @@ from database.mongo_client import get_collection -def store_indicator_result(team_name:str, indicator_def: dict, final_value: float, intermediate_factor_values: dict)-> None: - ''' +def store_indicator_result( + team_name: str, + indicator_def: dict, + final_value: float, + intermediate_factor_values: dict, +) -> None: + """ Insert a indicator result into the MongoDB database under a certain collection name. - ''' - # Sets the collection name to the team name + "_indicators" - collection= get_collection(f"strategic_indicators.{team_name}") + """ + # Sets the collection name to the team name + "_indicators" + collection = get_collection(f"strategic_indicators.{team_name}") # Sets the evaluation date to the current date and time in the Europe/Madrid timezone evaluation_date = datetime.now(ZoneInfo("Europe/Madrid")).strftime("%Y-%m-%d") @@ -17,14 +22,18 @@ def store_indicator_result(team_name:str, indicator_def: dict, final_value: floa # Indicator label, its the name .properties file, for instance task_effort.properties metric, the name is task_effort indicator_label = os.path.splitext(os.path.basename(indicator_def["filePath"]))[0] - - weight_map = dict(zip( - indicator_def.get("factor", []), # en tu .properties suele llamarse "factor" - indicator_def.get("weights", []) - )) - - _id = f"{team_name}_{indicator_label}_{evaluation_date}" - # We need to check if the document already exists, and if it does, we increment the number of times modified + + weight_map = dict( + zip( + indicator_def.get( + "factor", [] + ), # en tu .properties suele llamarse "factor" + indicator_def.get("weights", []), + ) + ) + + _id = f"{team_name}_{indicator_label}_{evaluation_date}" + # We need to check if the document already exists, and if it does, we increment the number of times modified # Check if the document already exists existing_doc = collection.find_one({"_id": _id}) if existing_doc: @@ -32,24 +41,22 @@ def store_indicator_result(team_name:str, indicator_def: dict, final_value: floa n = existing_doc.get("times_modified", 0) + 1 else: n = 1 - + # Build a unique _id: team-factorName-timestamp factor_parts = [] for factor_name, tuples in intermediate_factor_values.items(): - base_w = weight_map.get(factor_name, None) + base_w = weight_map.get(factor_name, None) is_weighted = base_w not in (None, 1, 1.0) # The strategic indicator receives only 1 value always (student=None) # pero iteramos igual por coherencia for _student, val in tuples: wtxt = f"weighted:{base_w}" if is_weighted else "no weighted" - factor_parts.append( - f"{factor_name} (value: {round(val, 10)}, {wtxt})" - ) + factor_parts.append(f"{factor_name} (value: {round(val, 10)}, {wtxt})") factors_block = "; ".join(factor_parts) + ";" - formula_name = indicator_def.get("formula", "average") - category = indicator_def.get("category", "Neutral") + formula_name = indicator_def.get("formula", "average") + category = indicator_def.get("category", "Neutral") info_field = ( f"factors: {{ {factors_block} }}, " @@ -57,39 +64,33 @@ def store_indicator_result(team_name:str, indicator_def: dict, final_value: floa f"value: {round(final_value, 10)}, " f"category: {category}" ) - - # Part of the mongo document that does not change + + # Part of the mongo document that does not change static = { - "name" : indicator_def['name'], - "description" : indicator_def['description'], - "project" : team_name, - "strategic_indicator" : indicator_label, - "datasource" : "QRapids Dashboard", + "name": indicator_def["name"], + "description": indicator_def["description"], + "project": team_name, + "strategic_indicator": indicator_label, + "datasource": "QRapids Dashboard", "dates_mismatch_days": 0, "missing_factors": [], - "createdAt" : datetime.now(ZoneInfo("Europe/Madrid")).strftime("%Y-%m-%d %H:%M:%S"), + "createdAt": datetime.now(ZoneInfo("Europe/Madrid")).strftime( + "%Y-%m-%d %H:%M:%S" + ), } # Part of the mongo document that will change each time there is an event dynamic = { "evaluationDate": evaluation_date, - "value" : final_value, - "info" : info_field, - "modifiedAt" : datetime.now(ZoneInfo("Europe/Madrid")).strftime("%Y-%m-%d %H:%M:%S"), + "value": final_value, + "info": info_field, + "modifiedAt": datetime.now(ZoneInfo("Europe/Madrid")).strftime( + "%Y-%m-%d %H:%M:%S" + ), "times_modified": n, } - - - - # Insert into MongoDB, with the dynamic and static parts, upserting or inserting - collection.update_one( - {"_id": _id}, - { - "$set": dynamic, - "$setOnInsert": static - }, - upsert=True - ) - - + # Insert into MongoDB, with the dynamic and static parts, upserting or inserting + collection.update_one( + {"_id": _id}, {"$set": dynamic, "$setOnInsert": static}, upsert=True + ) diff --git a/logic/metrics_logic/__pycache__/metric_event_mapping.cpython-310.pyc b/logic/metrics_logic/__pycache__/metric_event_mapping.cpython-310.pyc deleted file mode 100644 index 0cc0d9c..0000000 Binary files a/logic/metrics_logic/__pycache__/metric_event_mapping.cpython-310.pyc and /dev/null differ diff --git a/logic/metrics_logic/__pycache__/metric_event_mapping.cpython-311.pyc b/logic/metrics_logic/__pycache__/metric_event_mapping.cpython-311.pyc deleted file mode 100644 index 200db90..0000000 Binary files a/logic/metrics_logic/__pycache__/metric_event_mapping.cpython-311.pyc and /dev/null differ diff --git a/logic/metrics_logic/__pycache__/metric_placeholder.cpython-310.pyc b/logic/metrics_logic/__pycache__/metric_placeholder.cpython-310.pyc deleted file mode 100644 index 8adeed7..0000000 Binary files a/logic/metrics_logic/__pycache__/metric_placeholder.cpython-310.pyc and /dev/null differ diff --git a/logic/metrics_logic/__pycache__/metric_placeholder.cpython-311.pyc b/logic/metrics_logic/__pycache__/metric_placeholder.cpython-311.pyc deleted file mode 100644 index 8eb41ba..0000000 Binary files a/logic/metrics_logic/__pycache__/metric_placeholder.cpython-311.pyc and /dev/null differ diff --git a/logic/metrics_logic/__pycache__/metric_recalculation.cpython-310.pyc b/logic/metrics_logic/__pycache__/metric_recalculation.cpython-310.pyc deleted file mode 100644 index 878d549..0000000 Binary files a/logic/metrics_logic/__pycache__/metric_recalculation.cpython-310.pyc and /dev/null differ diff --git a/logic/metrics_logic/__pycache__/metric_recalculation.cpython-311.pyc b/logic/metrics_logic/__pycache__/metric_recalculation.cpython-311.pyc deleted file mode 100644 index f30792b..0000000 Binary files a/logic/metrics_logic/__pycache__/metric_recalculation.cpython-311.pyc and /dev/null differ diff --git a/logic/metrics_logic/__pycache__/metricsrelation.cpython-310.pyc b/logic/metrics_logic/__pycache__/metricsrelation.cpython-310.pyc deleted file mode 100644 index 13bc1db..0000000 Binary files a/logic/metrics_logic/__pycache__/metricsrelation.cpython-310.pyc and /dev/null differ diff --git a/logic/metrics_logic/__pycache__/run_mogo_query.cpython-310.pyc b/logic/metrics_logic/__pycache__/run_mogo_query.cpython-310.pyc deleted file mode 100644 index 3fc7dbd..0000000 Binary files a/logic/metrics_logic/__pycache__/run_mogo_query.cpython-310.pyc and /dev/null differ diff --git a/logic/metrics_logic/__pycache__/store_metric_mongo.cpython-310.pyc b/logic/metrics_logic/__pycache__/store_metric_mongo.cpython-310.pyc deleted file mode 100644 index 08ab85f..0000000 Binary files a/logic/metrics_logic/__pycache__/store_metric_mongo.cpython-310.pyc and /dev/null differ diff --git a/logic/metrics_logic/metric_event_mapping.py b/logic/metrics_logic/metric_event_mapping.py index 12e76c7..e2c6c8d 100644 --- a/logic/metrics_logic/metric_event_mapping.py +++ b/logic/metrics_logic/metric_event_mapping.py @@ -1,49 +1,58 @@ from database.quality_model_loader import scan_quality_model_folder from config.settings import QUALITY_MODELS_DIR -def load_required_fields_metrics(filepath: str)-> dict: + +def load_required_fields_metrics(filepath: str) -> dict: """ Reads some keys from the .properties metrics files. Returns a dict with those fields """ - allowed_keys = {'name', 'relatedEvent', 'scope', 'metric','description','factors','weights'} + allowed_keys = { + "name", + "relatedEvent", + "scope", + "metric", + "description", + "factors", + "weights", + } props = {} params = {} - + # Opeen the properties file and read the lines - with open(filepath, 'r', encoding='utf-8') as f: + with open(filepath, "r", encoding="utf-8") as f: for line in f: line = line.strip() # Skip empty lines and comments - if not line or line.startswith('#'): + if not line or line.startswith("#"): continue - if '=' not in line: + if "=" not in line: continue - key, value = line.split('=', 1) + key, value = line.split("=", 1) key = key.strip() value = value.strip() # Collect the properties that we are interested in if key in allowed_keys: props[key] = value # Collect the parameters that we are interested in - elif key.startswith('param.'): - raw = value.strip() #Get the value of the parameter - try: # turn to int/float if we can + elif key.startswith("param."): + raw = value.strip() # Get the value of the parameter + try: # turn to int/float if we can val = int(raw) if raw.isdigit() else float(raw) - except ValueError: #if not a number, keep it as string + except ValueError: # if not a number, keep it as string val = raw - + params[key[6:]] = val - # Attach any params to the props dict + # Attach any params to the props dict if params: - props['params'] = params - + props["params"] = params + return props def build_metric_def(props: dict, qm: str, path: str) -> dict: - ''' + """ Builds a metric definition from the loaded properties file. - ''' + """ return { "filePath": path, "name": props["name"], @@ -51,20 +60,19 @@ def build_metric_def(props: dict, qm: str, path: str) -> dict: "formula": props["metric"], "params": props.get("params", {}), "description": props.get("description", ""), - "factors": [x.strip() for x in props.get("factors","").split(",") if x], - "weights": [float(w) for w in props.get("weights","").split(",") if w], + "factors": [x.strip() for x in props.get("factors", "").split(",") if x], + "weights": [float(w) for w in props.get("weights", "").split(",") if w], "quality_model": qm, } -def build_metrics_index_per_qm(qm_root=QUALITY_MODELS_DIR)-> dict: - ''' + +def build_metrics_index_per_qm(qm_root=QUALITY_MODELS_DIR) -> dict: + """ Scans the quality model folder and builds a dictionary with the metrics found in each quality model. - ''' + """ return scan_quality_model_folder( qm_root, subfolder="metrics", props_loader=load_required_fields_metrics, - build_def=build_metric_def + build_def=build_metric_def, ) - - diff --git a/logic/metrics_logic/metric_placeholder.py b/logic/metrics_logic/metric_placeholder.py index b0ef964..c06f91a 100644 --- a/logic/metrics_logic/metric_placeholder.py +++ b/logic/metrics_logic/metric_placeholder.py @@ -1,49 +1,45 @@ import json + def load_query_template(query_file, param_map=None) -> dict: """ Loads the .query file as a Python object (list/dict). It also replaces the placeholders in the template with the values from the param_map. """ - with open(query_file, 'r', encoding='utf-8') as f: - + with open(query_file, "r", encoding="utf-8") as f: query_str = f.read() - - if param_map: - for placeholder, value in param_map.items(): #If we have a param_map, we replace the placeholders in the template with the values from the param_map, to avoid integer problems - query_str = query_str.replace(placeholder, str(value)) - - return json.loads(query_str) - + if param_map: + for ( + placeholder, + value, + ) in param_map.items(): # If we have a param_map, we replace the placeholders in the template with the values from the param_map, to avoid integer problems + query_str = query_str.replace(placeholder, str(value)) + return json.loads(query_str) def replace_placeholders_in_query(query_ast: list, param_map: dict) -> list: """ - All this function simply replaces the "$$StudentUser" for the value in param_map. - In our case param_map will the name of the student in each state of the loop. - With that we can define once the query and use all the students names in the loop. - - If query_ast is a list, we need to iterate over each item and replace placeholders in each one. - #Normally this is the case, the pipeline in mongo is a dictionary with the stages as keys and the values as the query. - - [ - { "$match": { "student": "$$studentUser" } }, - { "$group": { "_id": "$team", "count": { "$sum": 1 } } } - ] + All this function simply replaces the "$$StudentUser" for the value in param_map. + In our case param_map will the name of the student in each state of the loop. + With that we can define once the query and use all the students names in the loop. + + If query_ast is a list, we need to iterate over each item and replace placeholders in each one. + #Normally this is the case, the pipeline in mongo is a dictionary with the stages as keys and the values as the query. + + [ + { "$match": { "student": "$$studentUser" } }, + { "$group": { "_id": "$team", "count": { "$sum": 1 } } } + ] """ if isinstance(query_ast, dict): return { - k: replace_placeholders_in_query(v, param_map) - for k,v in query_ast.items() + k: replace_placeholders_in_query(v, param_map) for k, v in query_ast.items() } - #if the query_ast is a list, we need to iterate over each item and replace placeholders in each one. + # if the query_ast is a list, we need to iterate over each item and replace placeholders in each one. elif isinstance(query_ast, list): - return [ - replace_placeholders_in_query(item, param_map) - for item in query_ast - ] + return [replace_placeholders_in_query(item, param_map) for item in query_ast] # If query_ast is a string, we replace the placeholders with the real values from param_map. elif isinstance(query_ast, str): out_str = query_ast @@ -51,4 +47,4 @@ def replace_placeholders_in_query(query_ast: list, param_map: dict) -> list: out_str = out_str.replace(placeholder, str(real_value)) return out_str else: - return query_ast \ No newline at end of file + return query_ast diff --git a/logic/metrics_logic/metric_recalculation.py b/logic/metrics_logic/metric_recalculation.py index 1521b60..e8fadaf 100644 --- a/logic/metrics_logic/metric_recalculation.py +++ b/logic/metrics_logic/metric_recalculation.py @@ -1,7 +1,13 @@ import os -from logic.metrics_logic.metric_placeholder import load_query_template, replace_placeholders_in_query +from logic.metrics_logic.metric_placeholder import ( + load_query_template, + replace_placeholders_in_query, +) from logic.metrics_logic.store_metric_mongo import store_metric_result -from logic.metrics_logic.run_mogo_query import run_mongo_query_for_metric, evaluate_formula +from logic.metrics_logic.run_mogo_query import ( + run_mongo_query_for_metric, + evaluate_formula, +) from statistics import pstdev from database.mongo_client import get_collection @@ -13,48 +19,64 @@ logger = logging.getLogger(__name__) - -def compute_metric_for_student(metric_def: dict, event_type: str, student_name: str, team_name: str)-> None: +def compute_metric_for_student( + metric_def: dict, event_type: str, student_name: str, team_name: str +) -> None: """ Compute, evaluate and store an individual metric for a given student. """ - # Derive the .query filename from the .properties path - basepath = os.path.splitext(metric_def["filePath"])[0] # strip ".properties" #HERE WE WILL PUT IT IN .ENV LATER + # Derive the .query filename from the .properties path + basepath = os.path.splitext(metric_def["filePath"])[ + 0 + ] # strip ".properties" #HERE WE WILL PUT IT IN .ENV LATER query_file = basepath + ".query" - + meta = get_event_meta(event_type) - collection_name= f"{meta['data_source'].lower()}_{team_name}.{meta['collection_suffix']}" + collection_name = ( + f"{meta['data_source'].lower()}_{team_name}.{meta['collection_suffix']}" + ) formula_str = metric_def["formula"] # e.g. "commitsAssignee / commitsTotal" - logger.info(f"Recomputing INDIV metric '{metric_def['name']}' formula=({formula_str}) for student='{student_name}' team with external_id='{team_name}'") - + logger.info( + f"Recomputing INDIV metric '{metric_def['name']}' formula=({formula_str}) for student='{student_name}' team with external_id='{team_name}'" + ) + # Prepare the placeholders, will replace $$studentUser with the student name - param_map = { "$$studentUser": student_name } + param_map = {"$$studentUser": student_name} # Can be the case that we have parameters in the query, like the threshold for the stdev. If in the metric definition, we will replace them with the values in the query for pname, pval in metric_def.get("params", {}).items(): param_map[f"{{{{{pname}}}}}"] = pval logger.debug(f"param_map: {param_map}") # Run the aggregation pipeline and get raw doc - doc = run_mongo_query_for_metric(team_name, student_name, query_file, event_type, param_map) + doc = run_mongo_query_for_metric( + team_name, student_name, query_file, event_type, param_map + ) logger.debug(f"doc: {doc}") - - + if not doc: - final_val=0.0 - logger.warning(f"No aggregator results, setting final_val to 0.0") + final_val = 0.0 + logger.warning("No aggregator results, setting final_val to 0.0") else: # Evaluate the formula against the aggregation result - final_val=evaluate_formula(formula_str,doc) + final_val = evaluate_formula(formula_str, doc) logger.info(f"Result: {final_val}\n") - - # Store the metric result in MongoDB - store_metric_result(team_name=team_name, metric_def=metric_def, final_val=final_val, event_type=event_type, student_name=student_name, aggregator_doc=doc, info_collection_name=collection_name) - - - -def compute_metric_for_team(metric_def: dict, event_type: str, team_name: str,students: list)-> None: + # Store the metric result in MongoDB + store_metric_result( + team_name=team_name, + metric_def=metric_def, + final_val=final_val, + event_type=event_type, + student_name=student_name, + aggregator_doc=doc, + info_collection_name=collection_name, + ) + + +def compute_metric_for_team( + metric_def: dict, event_type: str, team_name: str, students: list +) -> None: """ Compute, evaluate and store a team-level metric (no individual placeholders). """ @@ -64,77 +86,90 @@ def compute_metric_for_team(metric_def: dict, event_type: str, team_name: str,st logger.warning(f"Unknown event type '{event_type}', skipping metric") return logger.debug(f"Event meta: {meta}") - - - collection_name= f"{meta['data_source'].lower()}_{team_name}.{meta['collection_suffix']}" + + collection_name = ( + f"{meta['data_source'].lower()}_{team_name}.{meta['collection_suffix']}" + ) print(f"Collection name: {collection_name}") collection = get_collection(collection_name) basepath = os.path.splitext(metric_def["filePath"])[0] query_file = basepath + ".query" formula_str = metric_def["formula"] - + # For standard deviation metrics, delegate to special handler if formula_str in ["stdevCommits", "stdevTasks"]: - return compute_team_sd_metric(metric_def, event_type, team_name, collection_name, students) - + return compute_team_sd_metric( + metric_def, event_type, team_name, collection_name, students + ) + else: - - logger.info(f"Recomputing TEAM metric '{metric_def['name']}' formula=({formula_str}) for team with external_id='{team_name}'") - + logger.info( + f"Recomputing TEAM metric '{metric_def['name']}' formula=({formula_str}) for team with external_id='{team_name}'" + ) + # Load and substitute the placeholders in the query template - param_map={} #Empty param map as we are not using any placeholders in any team query + param_map = {} # Empty param map as we are not using any placeholders in any team query # Can be the case that we have parameters in the query, like the threshold for the stdev. If in the metric definition, we will replace them with the values in the query for pname, pval in metric_def.get("params", {}).items(): param_map[f"{{{{{pname}}}}}"] = pval - + # Run the aggregation pipeline and get raw doc - pipeline = load_query_template(query_file, param_map) #Load the query template, we will replace the placeholders later if needed + pipeline = load_query_template( + query_file, param_map + ) # Load the query template, we will replace the placeholders later if needed pipeline = replace_placeholders_in_query(pipeline, param_map) - - results = list(collection.aggregate(pipeline)) - - logger.debug(f"pipeline: {pipeline}") - logger.debug(f"db collection name: {collection_name}") - logger.debug(f"Results from aggregator: {results}") - + logger.debug(f"pipeline: {pipeline}") + logger.debug(f"db collection name: {collection_name}") + logger.debug(f"Results from aggregator: {results}") if not results: final_val = 0.0 logger.warning(f"No aggregator results; defaulting to {final_val}") - doc = {} # If no results, we can set doc to empty dict or None + doc = {} # If no results, we can set doc to empty dict or None else: # Evaluate the formula - doc = results[0] # aggregator typically returns one doc + doc = results[0] # aggregator typically returns one doc final_val = evaluate_formula(formula_str, doc) logger.info(f"TEAM metric result: {final_val}\n") # Store the metric result in MongoDB - store_metric_result( team_name=team_name, metric_def=metric_def, final_val=final_val, event_type=event_type, student_name=None, aggregator_doc=doc, info_collection_name=collection_name) - + store_metric_result( + team_name=team_name, + metric_def=metric_def, + final_val=final_val, + event_type=event_type, + student_name=None, + aggregator_doc=doc, + info_collection_name=collection_name, + ) -def compute_team_sd_metric(metric_def, event_type, team_name, collection_name, team_members=None): +def compute_team_sd_metric( + metric_def, event_type, team_name, collection_name, team_members=None +): """ Special-case: compute population standard deviation of counts per member. """ - + basepath = os.path.splitext(metric_def["filePath"])[0] query_file = basepath + ".query" - logger.info(f"Recomputing metric '{metric_def['name']}' for team with external_id='{team_name}'") + logger.info( + f"Recomputing metric '{metric_def['name']}' for team with external_id='{team_name}'" + ) - collection= get_collection(collection_name) + collection = get_collection(collection_name) # Load and substitute the placeholders in the query template - param_map={} #Empty param map as we are not using any placeholders in any team query - # Can be the case that we have parameters in the query, like the threshold for the stdev. If in the metric definition, we will replace them with the values in the query + param_map = {} # Empty param map as we are not using any placeholders in any team query + # Can be the case that we have parameters in the query, like the threshold for the stdev. If in the metric definition, we will replace them with the values in the query for pname, pval in metric_def.get("params", {}).items(): param_map[f"{{{{{pname}}}}}"] = pval - - #Load the query template - pipeline = load_query_template(query_file, param_map) + + # Load the query template + pipeline = load_query_template(query_file, param_map) docs = list(collection.aggregate(pipeline)) logger.debug(f"Aggregator results: {docs}") @@ -151,15 +186,12 @@ def compute_team_sd_metric(metric_def, event_type, team_name, collection_name, t team_members = [] # Create a list of team members with 0 if not in the map - commitsTotal=sum(aggregator_map.values()) # total number of commits/tasks - + commitsTotal = sum(aggregator_map.values()) # total number of commits/tasks + if commitsTotal == 0: final_val = 0.0 logger.warning("No commits in team, setting stdev to 0.0") - aggregator_doc = { - "perUserCounts": aggregator_map, - "teamMembers": team_members - } + aggregator_doc = {"perUserCounts": aggregator_map, "teamMembers": team_members} store_metric_result( team_name=team_name, metric_def=metric_def, @@ -167,16 +199,14 @@ def compute_team_sd_metric(metric_def, event_type, team_name, collection_name, t event_type=event_type, student_name=None, aggregator_doc=aggregator_doc, - info_collection_name=collection_name + info_collection_name=collection_name, ) logger.info(f"Final stdev: {final_val}\n") return final_val - - - + fractions = [ - aggregator_map.get(member, 0) / commitsTotal for member in team_members -] + aggregator_map.get(member, 0) / commitsTotal for member in team_members + ] # Calculate the standard deviation of the fractions if len(fractions) > 1: @@ -185,17 +215,17 @@ def compute_team_sd_metric(metric_def, event_type, team_name, collection_name, t final_val = 0.0 # Create the aggregator document to store in MongoDB - aggregator_doc = { - "perUserCounts": aggregator_map, - "teamMembers": team_members - } + aggregator_doc = {"perUserCounts": aggregator_map, "teamMembers": team_members} # Store the metric result in MongoDB - store_metric_result(team_name=team_name, metric_def=metric_def, final_val=final_val, event_type=event_type, student_name=None, aggregator_doc=aggregator_doc, info_collection_name=collection_name) + store_metric_result( + team_name=team_name, + metric_def=metric_def, + final_val=final_val, + event_type=event_type, + student_name=None, + aggregator_doc=aggregator_doc, + info_collection_name=collection_name, + ) logger.info(f"Final stdev: {final_val}\n") return final_val - - - - - diff --git a/logic/metrics_logic/run_mogo_query.py b/logic/metrics_logic/run_mogo_query.py index e2f7268..54167a4 100644 --- a/logic/metrics_logic/run_mogo_query.py +++ b/logic/metrics_logic/run_mogo_query.py @@ -1,85 +1,134 @@ -from logic.metrics_logic.metric_placeholder import load_query_template, replace_placeholders_in_query +from logic.metrics_logic.metric_placeholder import ( + load_query_template, + replace_placeholders_in_query, +) from config.load_config_file import get_event_meta from config.logger_config import setup_logging from database.mongo_client import get_collection +import ast import logging + setup_logging() logger = logging.getLogger(__name__) +_BINARY_OPERATORS = { + ast.Add: lambda left, right: left + right, + ast.Sub: lambda left, right: left - right, + ast.Mult: lambda left, right: left * right, + ast.Div: lambda left, right: left / right, + ast.Mod: lambda left, right: left % right, + ast.Pow: lambda left, right: left**right, +} + +_UNARY_OPERATORS = { + ast.UAdd: lambda operand: operand, + ast.USub: lambda operand: -operand, +} + + +def _safe_eval_node(node: ast.AST, variables: dict[str, float]) -> float: + if isinstance(node, ast.Expression): + return _safe_eval_node(node.body, variables) + + if isinstance(node, ast.BinOp): + operator = _BINARY_OPERATORS.get(type(node.op)) + if operator is None: + raise ValueError(f"Unsupported operator: {type(node.op).__name__}") + return operator( + _safe_eval_node(node.left, variables), + _safe_eval_node(node.right, variables), + ) + + if isinstance(node, ast.UnaryOp): + operator = _UNARY_OPERATORS.get(type(node.op)) + if operator is None: + raise ValueError(f"Unsupported unary operator: {type(node.op).__name__}") + return operator(_safe_eval_node(node.operand, variables)) + if isinstance(node, ast.Name): + return variables[node.id] -def evaluate_formula(formula_str: str, result_doc: dict) -> float: + if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)): + return float(node.value) + + raise ValueError(f"Unsupported expression node: {type(node).__name__}") + + +def evaluate_formula(formula_str: str, result_doc: dict) -> float: """ Evaluate the formula string using the values from the result_doc. """ # create a variable dict with all keys from the doc # e.g. commitsAssignee -> 10, commitsTotal -> 11 local_vars = {} - - #Scan the result_doc and create a dictionary with the values of the keys - for k, v in result_doc.items(): - if isinstance(v, (int, float)): # if the value is a number, we can use it directly - local_vars[k] = float(v) # convert to float for safety - else: # if the value is not a number, we can skip it or set it to 0.0 - local_vars[k] = 0.0 - - - # Now evaluate the expression + + # Scan the result_doc and create a dictionary with the values of the keys + for k, v in result_doc.items(): + if isinstance( + v, (int, float) + ): # if the value is a number, we can use it directly + local_vars[k] = float(v) # convert to float for safety + else: # if the value is not a number, we can skip it or set it to 0.0 + local_vars[k] = 0.0 + try: - ## Use eval to evaluate the formula string with the local_vars as context - value = eval(formula_str, {}, local_vars) #eval takes a string expression and "transforms" it into a python expression that can be calculated - except ZeroDivisionError: # handle division by zero + expression = ast.parse(formula_str, mode="eval") + value = _safe_eval_node(expression, local_vars) + except ZeroDivisionError: value = 0.0 - except Exception as e: # handle any other exceptions giving the error and set to 0 + except Exception as e: print(f"[evaluate_formula] Error: {e}, defaulting to 0.0") value = 0.0 return value - - - - - -def run_mongo_query_for_metric(team_name: str, student_name: str, query_file: str, event_type, placeholder_map: dict) -> dict: +def run_mongo_query_for_metric( + team_name: str, + student_name: str, + query_file: str, + event_type, + placeholder_map: dict, +) -> dict: """ Run a MongoDB query for a specific metric and return the result. """ - + # #possible related_events sent by LD_connect from GITHUB: push, issues from TAIGA: issue, epic, task, userstory, relatedusertory meta = get_event_meta(event_type) - logger.debug(f"Event meta: {meta}") #CHANGE THIS LATER TO DEBUG LEVEL - + logger.debug(f"Event meta: {meta}") # CHANGE THIS LATER TO DEBUG LEVEL if meta is None: logger.warning(f"Event type '{event_type}' not found in meta data.") return - - collection_name= f"{meta['data_source'].lower()}_{team_name}.{meta['collection_suffix']}" - - - # load the aggregator pipeline from the .query file - pipeline = load_query_template(query_file, placeholder_map) #load the query template from the file and replace the placeholders with the values in the param_map - logger.debug(f"pipeline: {pipeline}") #REMOVED LATER, ONLY TO LOG - - + + collection_name = ( + f"{meta['data_source'].lower()}_{team_name}.{meta['collection_suffix']}" + ) + + # load the aggregator pipeline from the .query file + pipeline = load_query_template( + query_file, placeholder_map + ) # load the query template from the file and replace the placeholders with the values in the param_map + logger.debug(f"pipeline: {pipeline}") # REMOVED LATER, ONLY TO LOG + # .query has placeholders "$$studentUser", need the function to recursively replace them pipeline = replace_placeholders_in_query(pipeline, {"$$studentUser": student_name}) - # connect to mongo, run the pipeline - - collection = get_collection(collection_name) #get the collection from the mongo client + + collection = get_collection( + collection_name + ) # get the collection from the mongo client cursor = collection.aggregate(pipeline) results = list(cursor) - logger.info(f"Results: {results}") #REMOVED LATER, ONLY TO LOG + logger.info(f"Results: {results}") # REMOVED LATER, ONLY TO LOG - if not results: #if for some case the query returns no results, we return 0,0 - return {} + if not results: # if for some case the query returns no results, we return 0,0 + return {} doc = results[0] - + return doc diff --git a/logic/metrics_logic/store_metric_mongo.py b/logic/metrics_logic/store_metric_mongo.py index 83cddd9..08ec829 100644 --- a/logic/metrics_logic/store_metric_mongo.py +++ b/logic/metrics_logic/store_metric_mongo.py @@ -1,20 +1,29 @@ -import os +import os from datetime import datetime from zoneinfo import ZoneInfo from database.mongo_client import get_collection import re -#def store_metric_result(team_name: str, metric_name: str, scope: str, final_val: float, event_type: str, student_name: str = None, aggregator_doc: dict = None): -def store_metric_result(team_name: str, metric_def: str, final_val: float, event_type: str, student_name: str = None, aggregator_doc: dict = None, info_collection_name: str = None): - ''' + +# def store_metric_result(team_name: str, metric_name: str, scope: str, final_val: float, event_type: str, student_name: str = None, aggregator_doc: dict = None): +def store_metric_result( + team_name: str, + metric_def: str, + final_val: float, + event_type: str, + student_name: str = None, + aggregator_doc: dict = None, + info_collection_name: str = None, +): + """ Insert a metric result into the MongoDB database. - ''' + """ collection_name = f"metrics.{team_name}" - # Sets the collection name to the team name + "_metrics" + # Sets the collection name to the team name + "_metrics" collection = get_collection(collection_name) # Sets the evaluation date to the current date and time in the Europe/Madrid timezone evaluation_date = datetime.now(ZoneInfo("Europe/Madrid")).strftime("%Y-%m-%d") - + # Metric label, its the name .properties file, for instance task_effort.properties metric, the name is task_effort full_path = metric_def["filePath"] filename = os.path.basename(full_path) @@ -23,7 +32,7 @@ def store_metric_result(team_name: str, metric_def: str, final_val: float, event # Unique _id = project‑metric‑[student‑]date id_parts = [team_name, metric_label, evaluation_date] if student_name: - id_parts.insert(2, student_name) # add between team & date + id_parts.insert(2, student_name) # add between team & date doc_id = "-".join(id_parts) # We need to check if the document already exists, and if it does, we increment the number of times modified @@ -33,15 +42,14 @@ def store_metric_result(team_name: str, metric_def: str, final_val: float, event # If it exists, increment the number of times modified n = existing_doc.get("times_modified", 0) + 1 else: - n=1 + n = 1 -# just abans de construir info_lines: + # just abans de construir info_lines: if student_name: - query_props = { 'studentUser': student_name } + query_props = {"studentUser": student_name} else: query_props = {} - # Compose the info block, with parameters and formula info_lines = [ f"parameters: {{evaluationDate={evaluation_date}}}", @@ -53,75 +61,71 @@ def store_metric_result(team_name: str, metric_def: str, final_val: float, event info_lines.append(f"value: {final_val}") info = "\n".join(info_lines) - if student_name: - username = re.sub(r"[ \-]", "_", student_name) - + static = { - "name" : f"{student_name} {metric_def['name']}", - "description" : metric_def['description'], - "project" : team_name, - "metric" : f"{metric_label}_{username}", - "factors" : metric_def.get("factors", []), - "source" : f"mongodb:27017/mongo.{info_collection_name}", - "type" : "metrics", - "weights" : metric_def.get("weights", []), - "scope" : "individual", - "student_name" : student_name, - "event_type" : event_type, - "createdAt" : datetime.now(ZoneInfo("Europe/Madrid")).strftime("%Y-%m-%d %H:%M:%S"), + "name": f"{student_name} {metric_def['name']}", + "description": metric_def["description"], + "project": team_name, + "metric": f"{metric_label}_{username}", + "factors": metric_def.get("factors", []), + "source": f"mongodb:27017/mongo.{info_collection_name}", + "type": "metrics", + "weights": metric_def.get("weights", []), + "scope": "individual", + "student_name": student_name, + "event_type": event_type, + "createdAt": datetime.now(ZoneInfo("Europe/Madrid")).strftime( + "%Y-%m-%d %H:%M:%S" + ), } - # Part of the mongo document that will change each time there is an event dynamic = { "evaluationDate": evaluation_date, - "value" : final_val, - "info" : info, - "modifiedAt" : datetime.now(ZoneInfo("Europe/Madrid")).strftime("%Y-%m-%d %H:%M:%S"), + "value": final_val, + "info": info, + "modifiedAt": datetime.now(ZoneInfo("Europe/Madrid")).strftime( + "%Y-%m-%d %H:%M:%S" + ), "times_modified": n, } - - else: - # Part of the mongo document that does not change + # Part of the mongo document that does not change static = { - "name" : metric_def['name'], - "description" : metric_def['description'], - "project" : team_name, - "metric" : metric_label, - "factors" : metric_def.get("factors", []), - "source" : f"mongodb:27017/mongo.{info_collection_name}", - "type" : "metrics", - "weights" : metric_def.get("weights", []), - "scope" : "individual" if student_name else "team", - "event_type" : event_type, - "createdAt" : datetime.now(ZoneInfo("Europe/Madrid")).strftime("%Y-%m-%d %H:%M:%S"), + "name": metric_def["name"], + "description": metric_def["description"], + "project": team_name, + "metric": metric_label, + "factors": metric_def.get("factors", []), + "source": f"mongodb:27017/mongo.{info_collection_name}", + "type": "metrics", + "weights": metric_def.get("weights", []), + "scope": "individual" if student_name else "team", + "event_type": event_type, + "createdAt": datetime.now(ZoneInfo("Europe/Madrid")).strftime( + "%Y-%m-%d %H:%M:%S" + ), } - if student_name: # If the metric is for a student, add the student name to the document + if ( + student_name + ): # If the metric is for a student, add the student name to the document static["student_name"] = student_name # Part of the mongo document that will change each time there is an event dynamic = { "evaluationDate": evaluation_date, - "value" : final_val, - "info" : info, - "modifiedAt" : datetime.now(ZoneInfo("Europe/Madrid")).strftime("%Y-%m-%d %H:%M:%S"), + "value": final_val, + "info": info, + "modifiedAt": datetime.now(ZoneInfo("Europe/Madrid")).strftime( + "%Y-%m-%d %H:%M:%S" + ), "times_modified": n, } - - - # Insert into MongoDB, with the dynamic and static parts, upserting or inserting + + # Insert into MongoDB, with the dynamic and static parts, upserting or inserting collection.update_one( - {"_id": doc_id}, - { - "$set": dynamic, - "$setOnInsert": static - }, - upsert=True - ) - - - \ No newline at end of file + {"_id": doc_id}, {"$set": dynamic, "$setOnInsert": static}, upsert=True + ) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..322422c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,29 @@ +[tool.pytest.ini_options] +testpaths = ["tests"] +norecursedirs = [ + "env", + ".git", + ".pytest_cache", + "__pycache__", +] + +[tool.coverage.run] +branch = true +omit = [ + "env/*", + "tests/*", + "utils/evaluation_test.py", +] + +[tool.coverage.report] +omit = [ + "env/*", + "tests/*", + "utils/evaluation_test.py", +] + +[tool.ruff] +exclude = ["env"] + +[tool.bandit] +exclude_dirs = ["env"] diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..e855bd4 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,8 @@ +import sys +from pathlib import Path + + +ROOT_DIR = Path(__file__).resolve().parents[1] + +if str(ROOT_DIR) not in sys.path: + sys.path.insert(0, str(ROOT_DIR)) diff --git a/tests/test_app.py b/tests/test_app.py new file mode 100644 index 0000000..569d55a --- /dev/null +++ b/tests/test_app.py @@ -0,0 +1,247 @@ +import importlib +import sys +from types import ModuleType + +import pytest + + +@pytest.fixture +def app_module(monkeypatch): + import API_calls.StudentDatafromLDRESTAPI as student_api + import config.load_config_file as load_config_file + import config.quality_model_config as quality_model_config + import ld_refresh + import logic.factors_logic.factor_event_mapping as factor_event_mapping + import logic.indicators_logic.indicator_event_mapping as indicator_event_mapping + import logic.metrics_logic.metric_event_mapping as metric_event_mapping + + monkeypatch.setattr( + metric_event_mapping, "build_metrics_index_per_qm", lambda *_: ({}, {}) + ) + monkeypatch.setattr( + factor_event_mapping, "build_factors_index_per_qm", lambda *_: ({}, {}) + ) + monkeypatch.setattr( + indicator_event_mapping, "build_indicators_index_per_qm", lambda *_: ({}, {}) + ) + monkeypatch.setattr( + student_api, + "build_team_students_map", + lambda: { + "team-1": { + "EXCEL": ["Alice", "Bob"], + "GITHUB": ["alice", "bob"], + "TAIGA": ["alice-taiga"], + } + }, + ) + monkeypatch.setattr( + quality_model_config, "load_qualitymodel_map", lambda: {"team-1": "amep"} + ) + monkeypatch.setattr( + quality_model_config, + "choose_qualitymodel", + lambda external_id, explicit_qm, qm_map: ( + explicit_qm or qm_map.get(external_id, "default") + ).lower(), + ) + monkeypatch.setattr( + load_config_file, "get_event_meta", lambda _event_type: {"data_source": "GITHUB"} + ) + monkeypatch.setattr(ld_refresh, "run_daily_refresh", lambda: None) + + fake_background_module = ModuleType("apscheduler.schedulers.background") + + class FakeBackgroundScheduler: + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + + def add_job(self, *args, **kwargs): + return None + + def start(self): + return None + + fake_background_module.BackgroundScheduler = FakeBackgroundScheduler + monkeypatch.setitem(sys.modules, "apscheduler", ModuleType("apscheduler")) + monkeypatch.setitem( + sys.modules, "apscheduler.schedulers", ModuleType("apscheduler.schedulers") + ) + monkeypatch.setitem( + sys.modules, "apscheduler.schedulers.background", fake_background_module + ) + + sys.modules.pop("app", None) + module = importlib.import_module("app") + yield module + sys.modules.pop("app", None) + + +def test_handle_event_returns_200_and_starts_thread(app_module, monkeypatch): + payload = {"event_type": "push", "prj": "team-1", "author_login": "alice"} + started = {} + + class FakeThread: + def __init__(self, target, args=(), kwargs=None): + started["target"] = target + started["args"] = args + started["kwargs"] = kwargs or {} + + def start(self): + started["started"] = True + + monkeypatch.setattr(app_module.threading, "Thread", FakeThread) + + response = app_module.app.test_client().post("/api/event", json=payload) + + assert response.status_code == 200 + assert response.get_json() == {"status": "received"} + assert started["target"] is app_module.background_process_event + assert started["args"] == (payload,) + assert started["kwargs"] == {} + assert started["started"] is True + + +def test_background_process_event_dispatches_metrics_factors_and_indicators( + app_module, monkeypatch +): + app_module.TEAM_STUDENTS_MAP = {"team-1": {"GITHUB": ["alice", "bob"]}} + app_module.TEAM_QUALITYMODEL_MAP = {"team-1": "amep"} + app_module.EVENT_METRICS_BY_QM = { + "amep": { + "push": [ + {"name": "student-activity", "scope": "individual"}, + {"name": "team-health", "scope": "team"}, + {"name": "author-activity", "scope": "individual_only"}, + ] + } + } + app_module.EVENT_FACTORS_BY_QM = { + "amep": { + "push": [{"name": "delivery", "metric": ["metric-a", "metric-b"]}] + } + } + app_module.EVENT_INDICATORS_BY_QM = { + "amep": {"push": [{"name": "overall", "factor": ["delivery"]}]} + } + + student_metric_calls = [] + team_metric_calls = [] + latest_metric_calls = [] + factor_calls = [] + latest_factor_calls = [] + indicator_calls = [] + + monkeypatch.setattr( + app_module, "get_event_meta", lambda _event_type: {"data_source": "GITHUB"} + ) + + def fake_compute_metric_for_student( + metric_def, event_type, student_name, team_name + ): + student_metric_calls.append( + (metric_def["name"], event_type, student_name, team_name) + ) + + def fake_compute_metric_for_team(metric_def, event_type, team_name, students): + team_metric_calls.append((metric_def["name"], event_type, team_name, students)) + + def fake_latest_metric_value(team_name, metric_name): + latest_metric_calls.append((team_name, metric_name)) + return [(None, float(len(metric_name)))] + + def fake_compute_factor(team_name, factor_def, factor_values): + factor_calls.append((team_name, factor_def["name"], factor_values)) + return 0.5, "ok" + + def fake_latest_factor_value(team_name, factor_name): + latest_factor_calls.append((team_name, factor_name)) + return [(None, 0.5)] + + def fake_compute_indicator(team_name, indicator_def, indicator_values): + indicator_calls.append((team_name, indicator_def["name"], indicator_values)) + return 0.5, "ok" + + monkeypatch.setattr( + app_module, "compute_metric_for_student", fake_compute_metric_for_student + ) + monkeypatch.setattr( + app_module, "compute_metric_for_team", fake_compute_metric_for_team + ) + monkeypatch.setattr(app_module, "latest_metric_value", fake_latest_metric_value) + monkeypatch.setattr(app_module, "compute_factor", fake_compute_factor) + monkeypatch.setattr(app_module, "latest_factor_value", fake_latest_factor_value) + monkeypatch.setattr(app_module, "compute_indicator", fake_compute_indicator) + + app_module.background_process_event( + {"event_type": "push", "prj": "team-1", "author_login": "carol"} + ) + + assert student_metric_calls == [ + ("student-activity", "push", "alice", "team-1"), + ("student-activity", "push", "bob", "team-1"), + ("author-activity", "push", "carol", "team-1"), + ] + assert team_metric_calls == [ + ("team-health", "push", "team-1", ["alice", "bob"]) + ] + assert latest_metric_calls == [ + ("team-1", "metric-a"), + ("team-1", "metric-b"), + ] + assert factor_calls == [ + ( + "team-1", + "delivery", + {"metric-a": [(None, 8.0)], "metric-b": [(None, 8.0)]}, + ) + ] + assert latest_factor_calls == [("team-1", "delivery")] + assert indicator_calls == [ + ("team-1", "overall", {"delivery": [(None, 0.5)]}) + ] + + +def test_handle_refresh_rebuilds_students_map_and_runs_refresh(app_module, monkeypatch): + app_module.TEAM_STUDENTS_MAP = { + "team-1": {"EXCEL": ["Alice"], "GITHUB": ["alice"], "TAIGA": []} + } + refresh_calls = [] + + class FakeThread: + def __init__(self, target, args=(), kwargs=None): + self.target = target + self.args = args + self.kwargs = kwargs or {} + + def start(self): + self.target(*self.args, **self.kwargs) + + monkeypatch.setattr(app_module.threading, "Thread", FakeThread) + monkeypatch.setattr( + app_module, + "build_team_students_map", + lambda: {"team-2": {"EXCEL": ["Carol"], "GITHUB": ["carol"], "TAIGA": []}}, + ) + monkeypatch.setattr(app_module, "run_daily_refresh", lambda: refresh_calls.append(True)) + + response = app_module.app.test_client().post("/api/refresh") + + assert response.status_code == 200 + assert response.get_json() == {"status": "refresh started"} + assert app_module.TEAM_STUDENTS_MAP == { + "team-2": {"EXCEL": ["Carol"], "GITHUB": ["carol"], "TAIGA": []} + } + assert refresh_calls == [True] + + +def test_get_students_map_returns_debug_payload(app_module): + response = app_module.app.test_client().get("/api/debug/students-map") + payload = response.get_json() + + assert response.status_code == 200 + assert payload["total_teams"] == 1 + assert payload["TEAM_STUDENTS_MAP"]["team-1"]["total_students"] == 2 + assert payload["TEAM_STUDENTS_MAP"]["team-1"]["GITHUB"] == ["alice", "bob"] + assert set(payload["worker_info"]) == {"process_id", "thread_id", "thread_name"} diff --git a/tests/test_config_loading.py b/tests/test_config_loading.py new file mode 100644 index 0000000..81be46b --- /dev/null +++ b/tests/test_config_loading.py @@ -0,0 +1,44 @@ +import json + +from config.load_config_file import ( + get_available_events, + get_event_meta, + load_sources_config, +) +from config.quality_model_config import choose_qualitymodel, load_qualitymodel_map + + +def test_sources_config_helpers_use_env_override(tmp_path, monkeypatch): + config_path = tmp_path / "sources.json" + config_data = { + "push": {"data_source": "GITHUB", "collection_suffix": "commits"}, + "task": {"data_source": "TAIGA", "collection_suffix": "tasks"}, + } + config_path.write_text(json.dumps(config_data), encoding="utf-8") + monkeypatch.setenv("SOURCES_CONFIG", str(config_path)) + + assert load_sources_config() == config_data + assert get_event_meta("push") == config_data["push"] + assert set(get_available_events()) == {"push", "task"} + + +def test_load_qualitymodel_map_inverts_and_lowercases_values(tmp_path): + config_path = tmp_path / "quality_models.json" + config_path.write_text( + json.dumps({"AMEP": ["TeamA", "TeamB"], "DEFAULT": ["TeamC"]}), + encoding="utf-8", + ) + + assert load_qualitymodel_map(str(config_path)) == { + "TeamA": "amep", + "TeamB": "amep", + "TeamC": "default", + } + + +def test_choose_qualitymodel_prefers_explicit_then_mapping_then_default(): + qm_map = {"TeamA": "amep"} + + assert choose_qualitymodel("TeamA", "DEFAULT", qm_map) == "default" + assert choose_qualitymodel("TeamA", None, qm_map) == "amep" + assert choose_qualitymodel("UnknownTeam", None, qm_map) == "default" diff --git a/tests/test_ld_refresh.py b/tests/test_ld_refresh.py new file mode 100644 index 0000000..6ccdea8 --- /dev/null +++ b/tests/test_ld_refresh.py @@ -0,0 +1,173 @@ +from types import SimpleNamespace + +import ld_refresh + + +class FakeCollection: + def __init__(self, docs=None): + self.docs = list(docs or []) + self.deleted_ids = [] + + def estimated_document_count(self): + return len(self.docs) + + def find(self, query): + allowed_students = set(query["student_name"]["$nin"]) + return [ + doc + for doc in list(self.docs) + if "student_name" in doc and doc["student_name"] not in allowed_students + ] + + def delete_one(self, query): + self.deleted_ids.append(query["_id"]) + self.docs = [doc for doc in self.docs if doc["_id"] != query["_id"]] + + +class FakeDb: + def __init__(self, collections=None): + self.collections = dict(collections or {}) + self.dropped = [] + + def list_collection_names(self): + return list(self.collections) + + def drop_collection(self, collection_name): + self.dropped.append(collection_name) + self.collections.pop(collection_name, None) + + def __getitem__(self, collection_name): + return self.collections[collection_name] + + +def test_team_is_active_checks_metrics_collection(monkeypatch): + fake_db = FakeDb({"metrics.Team1": FakeCollection([{"_id": 1}])}) + monkeypatch.setattr(ld_refresh, "db", fake_db) + + assert ld_refresh.team_is_active("Team1") is True + assert ld_refresh.team_is_active("Team2") is False + + +def test_trigger_team_event_posts_expected_payload(monkeypatch): + captured = {} + + def fake_post(url, json, timeout): + captured["url"] = url + captured["json"] = json + captured["timeout"] = timeout + return SimpleNamespace(status_code=202) + + monkeypatch.setattr(ld_refresh.requests, "post", fake_post) + monkeypatch.setattr(ld_refresh, "API_URL", "http://service.test/api/event") + monkeypatch.setattr(ld_refresh, "QM_MAP", {"Team1": "amep"}) + monkeypatch.setattr(ld_refresh, "choose_qualitymodel", lambda *_args: "amep") + + ld_refresh.trigger_team_event("Team1", "push") + + assert captured == { + "url": "http://service.test/api/event", + "json": { + "event_type": "push", + "prj": "Team1", + "author_login": "system", + "quality_model": "amep", + }, + "timeout": (0.2, 1), + } + + +def test_delete_orphan_collections_drops_missing_team_collections(monkeypatch): + fake_db = FakeDb( + { + "metrics.Team1": FakeCollection(), + "metrics.Legacy": FakeCollection(), + "factors.Legacy": FakeCollection(), + "strategic_indicators.Legacy": FakeCollection(), + "misc.Legacy": FakeCollection(), + } + ) + monkeypatch.setattr(ld_refresh, "db", fake_db) + + ld_refresh.delete_orphan_collections_from_mongo(["Team1"]) + + assert set(fake_db.dropped) == { + "metrics.Legacy", + "factors.Legacy", + "strategic_indicators.Legacy", + } + assert "misc.Legacy" not in fake_db.dropped + + +def test_delete_orphan_student_documents_removes_unknown_students(monkeypatch): + metrics_collection = FakeCollection( + [ + {"_id": 1, "student_name": "alice"}, + {"_id": 2, "student_name": "ghost"}, + {"_id": 3, "metric_name": "team-metric"}, + ] + ) + factors_collection = FakeCollection( + [ + {"_id": 4, "student_name": "ghost"}, + {"_id": 5, "student_name": "bob"}, + ] + ) + indicators_collection = FakeCollection([{"_id": 6, "student_name": "ghost"}]) + fake_db = FakeDb( + { + "metrics.Team1": metrics_collection, + "factors.Team1": factors_collection, + "strategic_indicators.Team1": indicators_collection, + } + ) + monkeypatch.setattr(ld_refresh, "db", fake_db) + + ld_refresh.delete_orphan_student_documents( + { + "Team1": { + "EXCEL": ["Alice Example"], + "GITHUB": ["alice"], + "TAIGA": ["bob"], + } + } + ) + + assert metrics_collection.deleted_ids == [2] + assert factors_collection.deleted_ids == [4] + assert indicators_collection.deleted_ids == [6] + + +def test_run_daily_refresh_triggers_every_available_event(monkeypatch): + delete_calls = [] + trigger_calls = [] + team_students = { + "Team1": {"GITHUB": ["alice"]}, + "Team2": {"GITHUB": ["bob"]}, + } + + monkeypatch.setattr(ld_refresh, "build_team_students_map", lambda: team_students) + monkeypatch.setattr( + ld_refresh, + "delete_orphan_collections_from_mongo", + lambda actual_teams: delete_calls.append(actual_teams), + ) + monkeypatch.setattr( + ld_refresh, "get_available_events", lambda: ["push", "task", "userstory"] + ) + monkeypatch.setattr( + ld_refresh, + "trigger_team_event", + lambda team_id, event_type: trigger_calls.append((team_id, event_type)), + ) + + ld_refresh.run_daily_refresh() + + assert delete_calls == [["Team1", "Team2"]] + assert trigger_calls == [ + ("Team1", "push"), + ("Team1", "task"), + ("Team1", "userstory"), + ("Team2", "push"), + ("Team2", "task"), + ("Team2", "userstory"), + ] diff --git a/tests/test_metric_formula_evaluation.py b/tests/test_metric_formula_evaluation.py new file mode 100644 index 0000000..2df3f2f --- /dev/null +++ b/tests/test_metric_formula_evaluation.py @@ -0,0 +1,12 @@ +from logic.metrics_logic.run_mogo_query import evaluate_formula + + +def test_evaluate_formula_supports_basic_arithmetic(): + result = evaluate_formula("commitsAssignee / commitsTotal", {"commitsAssignee": 3, "commitsTotal": 4}) + + assert result == 0.75 + + +def test_evaluate_formula_returns_zero_for_invalid_expressions(): + assert evaluate_formula("commitsAssignee / 0", {"commitsAssignee": 3}) == 0.0 + assert evaluate_formula("__import__('os').system('whoami')", {}) == 0.0 diff --git a/tests/test_quality_model_parsing.py b/tests/test_quality_model_parsing.py new file mode 100644 index 0000000..4b3af55 --- /dev/null +++ b/tests/test_quality_model_parsing.py @@ -0,0 +1,121 @@ +import textwrap + +from logic.factors_logic.factor_event_mapping import ( + build_factors_index_per_qm, + load_required_fields_factor, +) +from logic.indicators_logic.indicator_event_mapping import ( + build_indicators_index_per_qm, + load_required_fields_indicator, +) +from logic.metrics_logic.metric_event_mapping import ( + build_metrics_index_per_qm, + load_required_fields_metrics, +) + + +def write_properties(path, content): + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(textwrap.dedent(content).strip() + "\n", encoding="utf-8") + return path + + +def test_load_required_fields_metrics_parses_supported_keys_and_params(tmp_path): + properties_path = write_properties( + tmp_path / "commits.properties", + """ + name=commits + relatedEvent=push, task + scope=individual + metric=commitsTotal / 2 + description=Commit ratio + factors=activity, ownership + weights=0.75,0.25 + param.threshold=3 + param.weight=2.5 + ignored=value + """, + ) + + assert load_required_fields_metrics(str(properties_path)) == { + "name": "commits", + "relatedEvent": "push, task", + "scope": "individual", + "metric": "commitsTotal / 2", + "description": "Commit ratio", + "factors": "activity, ownership", + "weights": "0.75,0.25", + "params": {"threshold": 3, "weight": 2.5}, + } + + +def test_build_metrics_index_per_qm_indexes_definitions_by_related_event(tmp_path): + properties_path = write_properties( + tmp_path / "AMEP" / "metrics" / "commits.properties", + """ + name=commits + relatedEvent=push, task + scope=team + metric=commitsTotal + factors=activity + weights=1 + """, + ) + + all_by_qm, events_by_qm = build_metrics_index_per_qm(tmp_path) + + metric_def = all_by_qm["amep"][0] + assert metric_def["filePath"] == str(properties_path) + assert metric_def["quality_model"] == "amep" + assert metric_def["factors"] == ["activity"] + assert metric_def["weights"] == [1.0] + assert [metric["name"] for metric in events_by_qm["amep"]["push"]] == ["commits"] + assert [metric["name"] for metric in events_by_qm["amep"]["task"]] == ["commits"] + + +def test_build_factors_index_per_qm_parses_metric_lists_and_defaults(tmp_path): + properties_path = write_properties( + tmp_path / "DEFAULT" / "factors" / "delivery.properties", + """ + name=delivery + metric=commits, tasks + weights=0.7,0.3 + relatedEvent=push + """, + ) + + props = load_required_fields_factor(str(properties_path)) + all_by_qm, events_by_qm = build_factors_index_per_qm(tmp_path) + + assert props["metric"] == "commits, tasks" + factor_def = all_by_qm["default"][0] + assert factor_def["metric"] == ["commits", "tasks"] + assert factor_def["weights"] == ["0.7", "0.3"] + assert factor_def["category"] == "NoCategory" + assert [factor["name"] for factor in events_by_qm["default"]["push"]] == [ + "delivery" + ] + + +def test_build_indicators_index_per_qm_parses_factor_lists_and_defaults(tmp_path): + properties_path = write_properties( + tmp_path / "DEFAULT" / "indicators" / "health.properties", + """ + name=health + factor=delivery, planning + weights=0.4,0.6 + relatedEvent=push + """, + ) + + props = load_required_fields_indicator(str(properties_path)) + all_by_qm, events_by_qm = build_indicators_index_per_qm(tmp_path) + + assert props["factor"] == "delivery, planning" + indicator_def = all_by_qm["default"][0] + assert indicator_def["factor"] == ["delivery", "planning"] + assert indicator_def["weights"] == ["0.4", "0.6"] + assert indicator_def["category"] == "NoCategory" + assert [indicator["name"] for indicator in events_by_qm["default"]["push"]] == [ + "health" + ] diff --git a/utils/__pycache__/__init__.cpython-310.pyc b/utils/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index d5a01b8..0000000 Binary files a/utils/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/utils/__pycache__/evaluation_test.cpython-310.pyc b/utils/__pycache__/evaluation_test.cpython-310.pyc deleted file mode 100644 index 49b8ea5..0000000 Binary files a/utils/__pycache__/evaluation_test.cpython-310.pyc and /dev/null differ diff --git a/utils/evaluation_test.py b/utils/evaluation_test.py index 8a01341..23eaf01 100644 --- a/utils/evaluation_test.py +++ b/utils/evaluation_test.py @@ -1,15 +1,16 @@ # app.py -import threading -from flask import Flask, request, jsonify +from flask import Flask import logging -import os +import os from logic.metrics_logic.metric_event_mapping import build_metrics_index_per_qm from logic.factors_logic.factor_event_mapping import build_factors_index_per_qm -from logic.metrics_logic.metric_event_mapping import build_metrics_index_per_qm -from logic.metrics_logic.metric_recalculation import compute_metric_for_student, compute_metric_for_team +from logic.metrics_logic.metric_recalculation import ( + compute_metric_for_student, + compute_metric_for_team, +) from logic.factors_logic.factor_recalculation import compute_factor, latest_metric_value @@ -18,33 +19,34 @@ from config.load_config_file import get_event_meta from config.logger_config import setup_logging from API_calls.StudentDatafromLDRESTAPI import build_team_students_map -from config.quality_model_config import load_qualitymodel_map, choose_qualitymodel +from config.quality_model_config import load_qualitymodel_map from pymongo import MongoClient +from indicators_logic.indicator_recalculation import ( + compute_indicator, + latest_factor_value, +) -from config.logger_config import setup_logging -import logging - setup_logging() logger = logging.getLogger(__name__) - - QUALITY_MODELS_DIR = os.getenv("QUALITY_MODELS_DIR", "QUALITY_MODELS") app = Flask(__name__) # Build the metrics event map at startup scaning all the quality models metrics subfolders -ALL_METRICS_BY_QM, EVENT_METRICS_BY_QM = build_metrics_index_per_qm(QUALITY_MODELS_DIR) +ALL_METRICS_BY_QM, EVENT_METRICS_BY_QM = build_metrics_index_per_qm(QUALITY_MODELS_DIR) # Build the metrics event map at startup scaning all the quality models factors subfolders -ALL_FACTORS_BY_QM, EVENT_FACTORS_BY_QM = build_factors_index_per_qm(QUALITY_MODELS_DIR) +ALL_FACTORS_BY_QM, EVENT_FACTORS_BY_QM = build_factors_index_per_qm(QUALITY_MODELS_DIR) # Build the metrics event map at startup scaning all the quality models indicators subfolders -ALL_INDICATORS_BY_QM, EVENT_INDICATORS_BY_QM = build_indicators_index_per_qm(QUALITY_MODELS_DIR) +ALL_INDICATORS_BY_QM, EVENT_INDICATORS_BY_QM = build_indicators_index_per_qm( + QUALITY_MODELS_DIR +) # Build the team->students map at startup TEAM_STUDENTS_MAP = build_team_students_map() @@ -54,32 +56,29 @@ print(EVENT_INDICATORS_BY_QM) - - -event_type = "push" # "push" or "issue" or "userstory" +event_type = "push" # "push" or "issue" or "userstory" external_id = "LD_TEST_Project" author_name = "LD_TEST_User" # No funciona en mayusculas, solo en minusculas -quality_model="amep" -#team_name = event_data.get("team_name") +quality_model = "amep" +# team_name = event_data.get("team_name") client = MongoClient("mongodb://localhost:27017") db = client["event_dashboard"] - -meta = get_event_meta(event_type) -logger.info(meta) #PUT THIS LATER IN DEBUG LEVEL - +meta = get_event_meta(event_type) +logger.info(meta) # PUT THIS LATER IN DEBUG LEVEL -data_source= meta["data_source"] -students=TEAM_STUDENTS_MAP.get(external_id, {}).get(data_source, []) +data_source = meta["data_source"] +students = TEAM_STUDENTS_MAP.get(external_id, {}).get(data_source, []) - # Retrieve the students for that team -logger.info(f"Event={event_type}, team with external_id={external_id}, students={students}") +logger.info( + f"Event={event_type}, team with external_id={external_id}, students={students}" +) # Retrieve the triggered metrics triggered_metrics = EVENT_METRICS_BY_QM.get(quality_model, {}).get(event_type, {}) @@ -87,37 +86,39 @@ logger.info(f"Triggered metrics: {[m['name'] for m in triggered_metrics]}") - # Recompute each metric for metric_def in triggered_metrics: scope = metric_def["scope"] if scope == "individual": for student_name in students: - compute_metric_for_student(metric_def, event_type, student_name, team_name=external_id) + compute_metric_for_student( + metric_def, event_type, student_name, team_name=external_id + ) elif scope == "team": # scope == "team" - compute_metric_for_team(metric_def, event_type, team_name=external_id,students=students) - else: #scope == "individual_only" - compute_metric_for_student(metric_def, event_type, author_name, team_name=external_id) - + compute_metric_for_team( + metric_def, event_type, team_name=external_id, students=students + ) + else: # scope == "individual_only" + compute_metric_for_student( + metric_def, event_type, author_name, team_name=external_id + ) + # RECALCULTION OF THE FACTORS triggered_factors = EVENT_FACTORS_BY_QM.get(quality_model, {}).get(event_type, []) logger.info(f"Triggered factors: {[f['name'] for f in triggered_factors]}") - + for factor_def in triggered_factors: - - values= {} # Empty dictionary to store the values for each metric in the factor - + values = {} # Empty dictionary to store the values for each metric in the factor + for metrics in factor_def["metric"]: - print(f"metrics to use: {metrics}") - #For each metric in the factor, we need to get the latest value for that metric + # For each metric in the factor, we need to get the latest value for that metric # We need to store these values in a dictionary with the metric name as key and the value as value values[metrics] = latest_metric_value(external_id, metrics) - - logger.info(f"Values of the metrics of factor {factor_def['name']}: {values}") - final_val= compute_factor(external_id, factor_def, values) + logger.info(f"Values of the metrics of factor {factor_def['name']}: {values}") + final_val = compute_factor(external_id, factor_def, values) # RECALCULTION OF THE INDICATORS @@ -125,17 +126,18 @@ logger.info(f"Triggered factors: {[f['name'] for f in triggered_indicators]}") -from indicators_logic.indicator_recalculation import compute_indicator, latest_factor_value for indicator_def in triggered_indicators: - - indicator_values= {} # Empty dictionary to store the values for each metric in the factor - + indicator_values = {} # Empty dictionary to store the values for each metric in the factor + for factors in indicator_def["factor"]: - #For each metric in the factor, we need to get the latest value for that metric + # For each metric in the factor, we need to get the latest value for that metric # We need to store these values in a dictionary with the metric name as key and the value as value indicator_values[factors] = latest_factor_value(external_id, factors) - - print(indicator_values) - logger.info(f"Values of the factors of indicator {indicator_def['name']}: {indicator_values}") - final_val_indicator= compute_indicator(external_id, indicator_def, indicator_values) + print(indicator_values) + logger.info( + f"Values of the factors of indicator {indicator_def['name']}: {indicator_values}" + ) + final_val_indicator = compute_indicator( + external_id, indicator_def, indicator_values + ) diff --git a/venv/Lib/site-packages/DateTime-5.5.dist-info/INSTALLER b/venv/Lib/site-packages/DateTime-5.5.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/Lib/site-packages/DateTime-5.5.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/Lib/site-packages/DateTime-5.5.dist-info/LICENSE.txt b/venv/Lib/site-packages/DateTime-5.5.dist-info/LICENSE.txt deleted file mode 100644 index e1f9ad7..0000000 --- a/venv/Lib/site-packages/DateTime-5.5.dist-info/LICENSE.txt +++ /dev/null @@ -1,44 +0,0 @@ -Zope Public License (ZPL) Version 2.1 - -A copyright notice accompanies this license document that identifies the -copyright holders. - -This license has been certified as open source. It has also been designated as -GPL compatible by the Free Software Foundation (FSF). - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions in source code must retain the accompanying copyright -notice, this list of conditions, and the following disclaimer. - -2. Redistributions in binary form must reproduce the accompanying copyright -notice, this list of conditions, and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -3. Names of the copyright holders must not be used to endorse or promote -products derived from this software without prior written permission from the -copyright holders. - -4. The right to distribute this software or to use it for any purpose does not -give you the right to use Servicemarks (sm) or Trademarks (tm) of the -copyright -holders. Use of them is covered by separate agreement with the copyright -holders. - -5. If any files are modified, you must cause the modified files to carry -prominent notices stating that you changed the files and the date of any -change. - -Disclaimer - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED -OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/DateTime-5.5.dist-info/METADATA b/venv/Lib/site-packages/DateTime-5.5.dist-info/METADATA deleted file mode 100644 index 2c08685..0000000 --- a/venv/Lib/site-packages/DateTime-5.5.dist-info/METADATA +++ /dev/null @@ -1,1167 +0,0 @@ -Metadata-Version: 2.1 -Name: DateTime -Version: 5.5 -Summary: This package provides a DateTime data type, as known from Zope. Unless you need to communicate with Zope APIs, you're probably better off using Python's built-in datetime module. -Home-page: https://github.com/zopefoundation/DateTime -Author: Zope Foundation and Contributors -Author-email: zope-dev@zope.org -License: ZPL 2.1 -Classifier: Development Status :: 6 - Mature -Classifier: Environment :: Web Environment -Classifier: Framework :: Zope :: 4 -Classifier: License :: OSI Approved :: Zope Public License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Requires-Python: >=3.7 -License-File: LICENSE.txt -Requires-Dist: zope.interface -Requires-Dist: pytz - -.. image:: https://github.com/zopefoundation/DateTime/workflows/tests/badge.svg - :target: https://github.com/zopefoundation/DateTime/actions?query=workflow%3Atests - :alt: CI status - -.. image:: https://img.shields.io/pypi/v/DateTime.svg - :target: https://pypi.org/project/DateTime/ - :alt: Current version on PyPI - -.. image:: https://img.shields.io/pypi/pyversions/DateTime.svg - :target: https://pypi.org/project/DateTime/ - :alt: Supported Python versions - - -DateTime -======== - -This package provides a DateTime data type, as known from Zope. - -Unless you need to communicate with Zope APIs, you're probably better -off using Python's built-in datetime module. - -For further documentation, please have a look at `src/DateTime/DateTime.txt`. - - -.. contents:: - -The DateTime package -==================== - -Encapsulation of date/time values. - - -Function Timezones() --------------------- - -Returns the list of recognized timezone names: - - >>> from DateTime import Timezones - >>> zones = set(Timezones()) - -Almost all of the standard pytz timezones are included, with the exception -of some commonly-used but ambiguous abbreviations, where historical Zope -usage conflicts with the name used by pytz: - - >>> import pytz - >>> [x for x in pytz.all_timezones if x not in zones] - ['CET', 'EET', 'EST', 'MET', 'MST', 'WET'] - -Class DateTime --------------- - -DateTime objects represent instants in time and provide interfaces for -controlling its representation without affecting the absolute value of -the object. - -DateTime objects may be created from a wide variety of string or -numeric data, or may be computed from other DateTime objects. -DateTimes support the ability to convert their representations to many -major timezones, as well as the ability to create a DateTime object -in the context of a given timezone. - -DateTime objects provide partial numerical behavior: - -* Two date-time objects can be subtracted to obtain a time, in days - between the two. - -* A date-time object and a positive or negative number may be added to - obtain a new date-time object that is the given number of days later - than the input date-time object. - -* A positive or negative number and a date-time object may be added to - obtain a new date-time object that is the given number of days later - than the input date-time object. - -* A positive or negative number may be subtracted from a date-time - object to obtain a new date-time object that is the given number of - days earlier than the input date-time object. - -DateTime objects may be converted to integer, long, or float numbers -of days since January 1, 1901, using the standard int, long, and float -functions (Compatibility Note: int, long and float return the number -of days since 1901 in GMT rather than local machine timezone). -DateTime objects also provide access to their value in a float format -usable with the Python time module, provided that the value of the -object falls in the range of the epoch-based time module. - -A DateTime object should be considered immutable; all conversion and numeric -operations return a new DateTime object rather than modify the current object. - -A DateTime object always maintains its value as an absolute UTC time, -and is represented in the context of some timezone based on the -arguments used to create the object. A DateTime object's methods -return values based on the timezone context. - -Note that in all cases the local machine timezone is used for -representation if no timezone is specified. - -Constructor for DateTime ------------------------- - -DateTime() returns a new date-time object. DateTimes may be created -with from zero to seven arguments: - -* If the function is called with no arguments, then the current date/ - time is returned, represented in the timezone of the local machine. - -* If the function is invoked with a single string argument which is a - recognized timezone name, an object representing the current time is - returned, represented in the specified timezone. - -* If the function is invoked with a single string argument - representing a valid date/time, an object representing that date/ - time will be returned. - - As a general rule, any date-time representation that is recognized - and unambiguous to a resident of North America is acceptable. (The - reason for this qualification is that in North America, a date like: - 2/1/1994 is interpreted as February 1, 1994, while in some parts of - the world, it is interpreted as January 2, 1994.) A date/ time - string consists of two components, a date component and an optional - time component, separated by one or more spaces. If the time - component is omitted, 12:00am is assumed. - - Any recognized timezone name specified as the final element of the - date/time string will be used for computing the date/time value. - (If you create a DateTime with the string, - "Mar 9, 1997 1:45pm US/Pacific", the value will essentially be the - same as if you had captured time.time() at the specified date and - time on a machine in that timezone). If no timezone is passed, then - the timezone configured on the local machine will be used, **except** - that if the date format matches ISO 8601 ('YYYY-MM-DD'), the instance - will use UTC / GMT+0 as the timezone. - - o Returns current date/time, represented in US/Eastern: - - >>> from DateTime import DateTime - >>> e = DateTime('US/Eastern') - >>> e.timezone() - 'US/Eastern' - - o Returns specified time, represented in local machine zone: - - >>> x = DateTime('1997/3/9 1:45pm') - >>> x.parts() # doctest: +ELLIPSIS - (1997, 3, 9, 13, 45, ...) - - o Specified time in local machine zone, verbose format: - - >>> y = DateTime('Mar 9, 1997 13:45:00') - >>> y.parts() # doctest: +ELLIPSIS - (1997, 3, 9, 13, 45, ...) - >>> y == x - True - - o Specified time in UTC via ISO 8601 rule: - - >>> z = DateTime('2014-03-24') - >>> z.parts() # doctest: +ELLIPSIS - (2014, 3, 24, 0, 0, ...) - >>> z.timezone() - 'GMT+0' - - The date component consists of year, month, and day values. The - year value must be a one-, two-, or four-digit integer. If a one- - or two-digit year is used, the year is assumed to be in the - twentieth century. The month may an integer, from 1 to 12, a month - name, or a month abbreviation, where a period may optionally follow - the abbreviation. The day must be an integer from 1 to the number of - days in the month. The year, month, and day values may be separated - by periods, hyphens, forward slashes, or spaces. Extra spaces are - permitted around the delimiters. Year, month, and day values may be - given in any order as long as it is possible to distinguish the - components. If all three components are numbers that are less than - 13, then a month-day-year ordering is assumed. - - The time component consists of hour, minute, and second values - separated by colons. The hour value must be an integer between 0 - and 23 inclusively. The minute value must be an integer between 0 - and 59 inclusively. The second value may be an integer value - between 0 and 59.999 inclusively. The second value or both the - minute and second values may be omitted. The time may be followed - by am or pm in upper or lower case, in which case a 12-hour clock is - assumed. - -* If the DateTime function is invoked with a single numeric argument, - the number is assumed to be either a floating point value such as - that returned by time.time(), or a number of days after January 1, - 1901 00:00:00 UTC. - - A DateTime object is returned that represents either the GMT value - of the time.time() float represented in the local machine's - timezone, or that number of days after January 1, 1901. Note that - the number of days after 1901 need to be expressed from the - viewpoint of the local machine's timezone. A negative argument will - yield a date-time value before 1901. - -* If the function is invoked with two numeric arguments, then the - first is taken to be an integer year and the second argument is - taken to be an offset in days from the beginning of the year, in the - context of the local machine timezone. The date-time value returned - is the given offset number of days from the beginning of the given - year, represented in the timezone of the local machine. The offset - may be positive or negative. Two-digit years are assumed to be in - the twentieth century. - -* If the function is invoked with two arguments, the first a float - representing a number of seconds past the epoch in GMT (such as - those returned by time.time()) and the second a string naming a - recognized timezone, a DateTime with a value of that GMT time will - be returned, represented in the given timezone. - - >>> import time - >>> t = time.time() - - Time t represented as US/Eastern: - - >>> now_east = DateTime(t, 'US/Eastern') - - Time t represented as US/Pacific: - - >>> now_west = DateTime(t, 'US/Pacific') - - Only their representations are different: - - >>> now_east.equalTo(now_west) - True - -* If the function is invoked with three or more numeric arguments, - then the first is taken to be an integer year, the second is taken - to be an integer month, and the third is taken to be an integer day. - If the combination of values is not valid, then a DateTimeError is - raised. One- or two-digit years up to 69 are assumed to be in the - 21st century, whereas values 70-99 are assumed to be 20th century. - The fourth, fifth, and sixth arguments are floating point, positive - or negative offsets in units of hours, minutes, and days, and - default to zero if not given. An optional string may be given as - the final argument to indicate timezone (the effect of this is as if - you had taken the value of time.time() at that time on a machine in - the specified timezone). - -If a string argument passed to the DateTime constructor cannot be -parsed, it will raise SyntaxError. Invalid date, time, or -timezone components will raise a DateTimeError. - -The module function Timezones() will return a list of the timezones -recognized by the DateTime module. Recognition of timezone names is -case-insensitive. - -Instance Methods for DateTime (IDateTime interface) ---------------------------------------------------- - -Conversion and comparison methods -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* ``timeTime()`` returns the date/time as a floating-point number in - UTC, in the format used by the Python time module. Note that it is - possible to create date /time values with DateTime that have no - meaningful value to the time module, and in such cases a - DateTimeError is raised. A DateTime object's value must generally - be between Jan 1, 1970 (or your local machine epoch) and Jan 2038 to - produce a valid time.time() style value. - - >>> dt = DateTime('Mar 9, 1997 13:45:00 US/Eastern') - >>> dt.timeTime() - 857933100.0 - - >>> DateTime('2040/01/01 UTC').timeTime() - 2208988800.0 - - >>> DateTime('1900/01/01 UTC').timeTime() - -2208988800.0 - -* ``toZone(z)`` returns a DateTime with the value as the current - object, represented in the indicated timezone: - - >>> dt.toZone('UTC') - DateTime('1997/03/09 18:45:00 UTC') - - >>> dt.toZone('UTC').equalTo(dt) - True - -* ``isFuture()`` returns true if this object represents a date/time - later than the time of the call: - - >>> dt.isFuture() - False - >>> DateTime('Jan 1 3000').isFuture() # not time-machine safe! - True - -* ``isPast()`` returns true if this object represents a date/time - earlier than the time of the call: - - >>> dt.isPast() - True - >>> DateTime('Jan 1 3000').isPast() # not time-machine safe! - False - -* ``isCurrentYear()`` returns true if this object represents a - date/time that falls within the current year, in the context of this - object's timezone representation: - - >>> dt.isCurrentYear() - False - >>> DateTime().isCurrentYear() - True - -* ``isCurrentMonth()`` returns true if this object represents a - date/time that falls within the current month, in the context of - this object's timezone representation: - - >>> dt.isCurrentMonth() - False - >>> DateTime().isCurrentMonth() - True - -* ``isCurrentDay()`` returns true if this object represents a - date/time that falls within the current day, in the context of this - object's timezone representation: - - >>> dt.isCurrentDay() - False - >>> DateTime().isCurrentDay() - True - -* ``isCurrentHour()`` returns true if this object represents a - date/time that falls within the current hour, in the context of this - object's timezone representation: - - >>> dt.isCurrentHour() - False - - >>> DateTime().isCurrentHour() - True - -* ``isCurrentMinute()`` returns true if this object represents a - date/time that falls within the current minute, in the context of - this object's timezone representation: - - >>> dt.isCurrentMinute() - False - >>> DateTime().isCurrentMinute() - True - -* ``isLeapYear()`` returns true if the current year (in the context of - the object's timezone) is a leap year: - - >>> dt.isLeapYear() - False - >>> DateTime('Mar 8 2004').isLeapYear() - True - -* ``earliestTime()`` returns a new DateTime object that represents the - earliest possible time (in whole seconds) that still falls within - the current object's day, in the object's timezone context: - - >>> dt.earliestTime() - DateTime('1997/03/09 00:00:00 US/Eastern') - -* ``latestTime()`` return a new DateTime object that represents the - latest possible time (in whole seconds) that still falls within the - current object's day, in the object's timezone context - - >>> dt.latestTime() - DateTime('1997/03/09 23:59:59 US/Eastern') - -Component access -~~~~~~~~~~~~~~~~ - -* ``parts()`` returns a tuple containing the calendar year, month, - day, hour, minute second and timezone of the object - - >>> dt.parts() # doctest: +ELLIPSIS - (1997, 3, 9, 13, 45, ... 'US/Eastern') - -* ``timezone()`` returns the timezone in which the object is represented: - - >>> dt.timezone() in Timezones() - True - -* ``tzoffset()`` returns the timezone offset for the objects timezone: - - >>> dt.tzoffset() - -18000 - -* ``year()`` returns the calendar year of the object: - - >>> dt.year() - 1997 - -* ``month()`` returns the month of the object as an integer: - - >>> dt.month() - 3 - -* ``Month()`` returns the full month name: - - >>> dt.Month() - 'March' - -* ``aMonth()`` returns the abbreviated month name: - - >>> dt.aMonth() - 'Mar' - -* ``pMonth()`` returns the abbreviated (with period) month name: - - >>> dt.pMonth() - 'Mar.' - -* ``day()`` returns the integer day: - - >>> dt.day() - 9 - -* ``Day()`` returns the full name of the day of the week: - - >>> dt.Day() - 'Sunday' - -* ``dayOfYear()`` returns the day of the year, in context of the - timezone representation of the object: - - >>> dt.dayOfYear() - 68 - -* ``aDay()`` returns the abbreviated name of the day of the week: - - >>> dt.aDay() - 'Sun' - -* ``pDay()`` returns the abbreviated (with period) name of the day of - the week: - - >>> dt.pDay() - 'Sun.' - -* ``dow()`` returns the integer day of the week, where Sunday is 0: - - >>> dt.dow() - 0 - -* ``dow_1()`` returns the integer day of the week, where sunday is 1: - - >>> dt.dow_1() - 1 - -* ``h_12()`` returns the 12-hour clock representation of the hour: - - >>> dt.h_12() - 1 - -* ``h_24()`` returns the 24-hour clock representation of the hour: - - >>> dt.h_24() - 13 - -* ``ampm()`` returns the appropriate time modifier (am or pm): - - >>> dt.ampm() - 'pm' - -* ``hour()`` returns the 24-hour clock representation of the hour: - - >>> dt.hour() - 13 - -* ``minute()`` returns the minute: - - >>> dt.minute() - 45 - -* ``second()`` returns the second: - - >>> dt.second() == 0 - True - -* ``millis()`` returns the milliseconds since the epoch in GMT. - - >>> dt.millis() == 857933100000 - True - -strftime() -~~~~~~~~~~ - -See ``tests/test_datetime.py``. - -General formats from previous DateTime -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* ``Date()`` return the date string for the object: - - >>> dt.Date() - '1997/03/09' - -* ``Time()`` returns the time string for an object to the nearest - second: - - >>> dt.Time() - '13:45:00' - -* ``TimeMinutes()`` returns the time string for an object not showing - seconds: - - >>> dt.TimeMinutes() - '13:45' - -* ``AMPM()`` returns the time string for an object to the nearest second: - - >>> dt.AMPM() - '01:45:00 pm' - -* ``AMPMMinutes()`` returns the time string for an object not showing - seconds: - - >>> dt.AMPMMinutes() - '01:45 pm' - -* ``PreciseTime()`` returns the time string for the object: - - >>> dt.PreciseTime() - '13:45:00.000' - -* ``PreciseAMPM()`` returns the time string for the object: - - >>> dt.PreciseAMPM() - '01:45:00.000 pm' - -* ``yy()`` returns the calendar year as a 2 digit string - - >>> dt.yy() - '97' - -* ``mm()`` returns the month as a 2 digit string - - >>> dt.mm() - '03' - -* ``dd()`` returns the day as a 2 digit string: - - >>> dt.dd() - '09' - -* ``rfc822()`` returns the date in RFC 822 format: - - >>> dt.rfc822() - 'Sun, 09 Mar 1997 13:45:00 -0500' - -New formats -~~~~~~~~~~~ - -* ``fCommon()`` returns a string representing the object's value in - the format: March 9, 1997 1:45 pm: - - >>> dt.fCommon() - 'March 9, 1997 1:45 pm' - -* ``fCommonZ()`` returns a string representing the object's value in - the format: March 9, 1997 1:45 pm US/Eastern: - - >>> dt.fCommonZ() - 'March 9, 1997 1:45 pm US/Eastern' - -* ``aCommon()`` returns a string representing the object's value in - the format: Mar 9, 1997 1:45 pm: - - >>> dt.aCommon() - 'Mar 9, 1997 1:45 pm' - -* ``aCommonZ()`` return a string representing the object's value in - the format: Mar 9, 1997 1:45 pm US/Eastern: - - >>> dt.aCommonZ() - 'Mar 9, 1997 1:45 pm US/Eastern' - -* ``pCommon()`` returns a string representing the object's value in - the format Mar. 9, 1997 1:45 pm: - - >>> dt.pCommon() - 'Mar. 9, 1997 1:45 pm' - -* ``pCommonZ()`` returns a string representing the object's value in - the format: Mar. 9, 1997 1:45 pm US/Eastern: - - >>> dt.pCommonZ() - 'Mar. 9, 1997 1:45 pm US/Eastern' - -* ``ISO()`` returns a string with the date/time in ISO format. Note: - this is not ISO 8601-format! See the ISO8601 and HTML4 methods below - for ISO 8601-compliant output. Dates are output as: YYYY-MM-DD HH:MM:SS - - >>> dt.ISO() - '1997-03-09 13:45:00' - -* ``ISO8601()`` returns the object in ISO 8601-compatible format - containing the date, time with seconds-precision and the time zone - identifier - see http://www.w3.org/TR/NOTE-datetime. Dates are - output as: YYYY-MM-DDTHH:MM:SSTZD (T is a literal character, TZD is - Time Zone Designator, format +HH:MM or -HH:MM). - - The ``HTML4()`` method below offers the same formatting, but - converts to UTC before returning the value and sets the TZD"Z" - - >>> dt.ISO8601() - '1997-03-09T13:45:00-05:00' - - -* ``HTML4()`` returns the object in the format used in the HTML4.0 - specification, one of the standard forms in ISO8601. See - http://www.w3.org/TR/NOTE-datetime. Dates are output as: - YYYY-MM-DDTHH:MM:SSZ (T, Z are literal characters, the time is in - UTC.): - - >>> dt.HTML4() - '1997-03-09T18:45:00Z' - -* ``JulianDay()`` returns the Julian day according to - http://www.tondering.dk/claus/cal/node3.html#sec-calcjd - - >>> dt.JulianDay() - 2450517 - -* ``week()`` returns the week number according to ISO - see http://www.tondering.dk/claus/cal/node6.html#SECTION00670000000000000000 - - >>> dt.week() - 10 - -Deprecated API -~~~~~~~~~~~~~~ - -* DayOfWeek(): see Day() - -* Day_(): see pDay() - -* Mon(): see aMonth() - -* Mon_(): see pMonth - -General Services Provided by DateTime -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -DateTimes can be repr()'ed; the result will be a string indicating how -to make a DateTime object like this: - - >>> repr(dt) - "DateTime('1997/03/09 13:45:00 US/Eastern')" - -When we convert them into a string, we get a nicer string that could -actually be shown to a user: - - >>> str(dt) - '1997/03/09 13:45:00 US/Eastern' - -The hash value of a DateTime is based on the date and time and is -equal for different representations of the DateTime: - - >>> hash(dt) - 3618678 - >>> hash(dt.toZone('UTC')) - 3618678 - -DateTime objects can be compared to other DateTime objects OR floating -point numbers such as the ones which are returned by the Python time -module by using the equalTo method. Using this API, True is returned if the -object represents a date/time equal to the specified DateTime or time module -style time: - - >>> dt.equalTo(dt) - True - >>> dt.equalTo(dt.toZone('UTC')) - True - >>> dt.equalTo(dt.timeTime()) - True - >>> dt.equalTo(DateTime()) - False - -Same goes for inequalities: - - >>> dt.notEqualTo(dt) - False - >>> dt.notEqualTo(dt.toZone('UTC')) - False - >>> dt.notEqualTo(dt.timeTime()) - False - >>> dt.notEqualTo(DateTime()) - True - -Normal equality operations only work with DateTime objects and take the -timezone setting into account: - - >>> dt == dt - True - >>> dt == dt.toZone('UTC') - False - >>> dt == DateTime() - False - - >>> dt != dt - False - >>> dt != dt.toZone('UTC') - True - >>> dt != DateTime() - True - -But the other comparison operations compare the referenced moment in time and -not the representation itself: - - >>> dt > dt - False - >>> DateTime() > dt - True - >>> dt > DateTime().timeTime() - False - >>> DateTime().timeTime() > dt - True - - >>> dt.greaterThan(dt) - False - >>> DateTime().greaterThan(dt) - True - >>> dt.greaterThan(DateTime().timeTime()) - False - - >>> dt >= dt - True - >>> DateTime() >= dt - True - >>> dt >= DateTime().timeTime() - False - >>> DateTime().timeTime() >= dt - True - - >>> dt.greaterThanEqualTo(dt) - True - >>> DateTime().greaterThanEqualTo(dt) - True - >>> dt.greaterThanEqualTo(DateTime().timeTime()) - False - - >>> dt < dt - False - >>> DateTime() < dt - False - >>> dt < DateTime().timeTime() - True - >>> DateTime().timeTime() < dt - False - - >>> dt.lessThan(dt) - False - >>> DateTime().lessThan(dt) - False - >>> dt.lessThan(DateTime().timeTime()) - True - - >>> dt <= dt - True - >>> DateTime() <= dt - False - >>> dt <= DateTime().timeTime() - True - >>> DateTime().timeTime() <= dt - False - - >>> dt.lessThanEqualTo(dt) - True - >>> DateTime().lessThanEqualTo(dt) - False - >>> dt.lessThanEqualTo(DateTime().timeTime()) - True - -Numeric Services Provided by DateTime -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A DateTime may be added to a number and a number may be added to a -DateTime: - - >>> dt + 5 - DateTime('1997/03/14 13:45:00 US/Eastern') - >>> 5 + dt - DateTime('1997/03/14 13:45:00 US/Eastern') - -Two DateTimes cannot be added: - - >>> from DateTime.interfaces import DateTimeError - >>> try: - ... dt + dt - ... print('fail') - ... except DateTimeError: - ... print('ok') - ok - -Either a DateTime or a number may be subtracted from a DateTime, -however, a DateTime may not be subtracted from a number: - - >>> DateTime('1997/03/10 13:45 US/Eastern') - dt - 1.0 - >>> dt - 1 - DateTime('1997/03/08 13:45:00 US/Eastern') - >>> 1 - dt - Traceback (most recent call last): - ... - TypeError: unsupported operand type(s) for -: 'int' and 'DateTime' - -DateTimes can also be converted to integers (number of seconds since -the epoch) and floats: - - >>> int(dt) - 857933100 - >>> float(dt) - 857933100.0 - - -Changelog -========= - -5.5 (2024-03-21) ----------------- - -- Change pickle format to export the microseconds as an int, to - solve a problem with dates after 2038. - (`#56 `_) - - -5.4 (2023-12-15) ----------------- - -- Fix ``UnknownTimeZoneError`` when unpickling ``DateTime.DateTime().asdatetime()``. - (`#58 `_) - -- Repair equality comparison between DateTime instances and other types. - (`#60 `_) - - -5.3 (2023-11-14) ----------------- - -- Add support for Python 3.12. - -- Add preliminary support for Python 3.13a2. - - -5.2 (2023-07-19) ----------------- - -- Cast int to float in compare methods. -- Fix compare methods between DateTime instances and None. - (`#52 `_) - - -5.1 (2023-03-14) ----------------- - -- Add missing ``python_requires`` to ``setup.py``. - - -5.0 (2023-01-12) ----------------- - -- Drop support for Python 2.7, 3.5, 3.6. - - -4.8 (2022-12-16) ----------------- - -- Fix insidious buildout configuration bug that prevented tests on Python 2.7 - and 3.5, and fix test code that was incompatible with Python 3.5. - (`#44 `_) - -- Add support for Python 3.11. - - -4.7 (2022-09-14) ----------------- - -- Fix rounding problem with `DateTime` addition beyond the year 2038 - (`#41 `_) - - -4.6 (2022-09-10) ----------------- - -- Fix ``__format__`` method for DateTime objects - (`#39 `_) - - -4.5 (2022-07-04) ----------------- - -- Add ``__format__`` method for DateTime objects - (`#35 `_) - - -4.4 (2022-02-11) ----------------- - -- Fix WAT definition - `#31 `_. - -- Add support for Python 3.8, 3.9, and 3.10. - -- Drop support for Python 3.4. - -4.3 (2018-10-05) ----------------- - -- Add support for Python 3.7. - -4.2 (2017-04-26) ----------------- - -- Add support for Python 3.6, drop support for Python 3.3. - -4.1.1 (2016-04-30) ------------------- - -- Support unpickling instances having a numeric timezone like `+0430`. - -4.1 (2016-04-03) ----------------- - -- Add support for Python 3.4 and 3.5. - -- Drop support for Python 2.6 and 3.2. - -4.0.1 (2013-10-15) ------------------- - -- Provide more backward compatible timezones. - [vangheem] - -4.0 (2013-02-23) ----------------- - -- Added support for Python 3.2 and 3.3 in addition to 2.6 and 2.7. - -- Removed unused legacy pytz tests and the DateTimeZone module and renamed - some test internals. - -3.0.3 (2013-01-22) ------------------- - -- Allow timezone argument to be a Unicode string while creating a DateTime - object using two arguments. - -3.0.2 (2012-10-21) ------------------- - -- LP #1045233: Respect date format setting for parsing dates like `11-01-2001`. - -3.0.1 (2012-09-23) ------------------- - -- Add `_dt_reconstructor` function introduced in DateTime 2.12.7 to provide - forward compatibility with pickles that might reference this function. - -3.0 (2011-12-09) ----------------- - -- No changes. - -Backwards compatibility of DateTime 3 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -DateTime 3 changes its pickle representation. DateTime instances pickled with -former versions of DateTime can be read, but older DateTime versions cannot read -DateTime instances pickled with version 3. - -DateTime 3 changes DateTime to be a new-style class with slots instead of being -an old-style class. - -DateTime 3 tries to preserve microsecond resolution throughout most of its API's -while former versions were often only accurate to millisecond resolution. Due to -the representation of float values in Python versions before Python 2.7 you -shouldn't compare string or float representations of DateTime instances if you -want high accuracy. The same is true for calculated values returned by methods -like `timeTime()`. You get the highest accuracy of comparing DateTime values by -calling its `micros()` methods. DateTime is not particular well suited to be -used in comparing timestamps of file systems - use the time and datetime objects -from the Python standard library instead. - -3.0b3 (2011-10-19) ------------------- - -- Allow comparison of DateTime objects against None. - -3.0b2 (2011-10-19) ------------------- - -- Reverted the single argument `None` special case handling for unpickling and - continue to treat it as meaning `now`. - -3.0b1 (2011-05-07) ------------------- - -- Restored `strftimeFormatter` as a class. - -- Added tests for read-only class attributes and interface. - -3.0a2 (2011-05-07) ------------------- - -- Added back support for reading old DateTime pickles without a `_micros` value. - -- Avoid storing `_t` representing the time as a float in seconds since the - epoch, as we already have `_micros` doing the same as a long. Memory use is - down to about 300 bytes per DateTime instance. - -- Updated exception raising syntax to current style. - -- Avoid storing `_aday`, `_fday`, `_pday`, `_amon`, `_fmon`, `_pmon`, `_pmhour` - and `_pm` in memory for every instance but look them up dynamically based on - `_dayoffset`, `_month` and `_hour`. This saves another 150 bytes of memory - per DateTime instance. - -- Moved various internal parsing related class variables to module constants. - -- No longer provide the `DateError`, `DateTimeError`, `SyntaxError` and - `TimeError` exceptions as class attributes, import them from their canonical - `DateTime.interfaces` location instead. - -- Removed deprecated `_isDST` and `_localzone` class variables. - -- Moved pytz cache from `DateTime._tzinfo` to a module global `_TZINFO`. - -- Make DateTime a new-style class and limit its available attributes via a - slots definition. The pickle size increases to 110 bytes thanks to the - `ccopy_reg\n_reconstructor` stanza. But the memory size drops from 3kb to - 500 bytes for each instance. - -3.0a1 (2011-05-06) ------------------- - -- Reordered some calculations in `_calcIndependentSecondEtc` to preserve more - floating point precision. - -- Optimized the pickled data, by only storing a tuple of `_micros` and time - zone information - this reduces the pickle size from an average of 300 bytes - to just 60 bytes. - -- Optimized un-pickling, by avoiding the creation of an intermediate DateTime - value representing the current time. - -- Removed in-place migration of old DateTime pickles without a `_micros` value. - -- Removed deprecated support for using `DateTime.__cmp__`. - -- Take time zone settings into account when comparing two date times for - (non-) equality. - -- Fixed (possibly unused) _parse_iso8601 function. - -- Removed unused import of legacy DateTimeZone, strftime and re. - Remove trailing whitespace. - -- Removed reference to missing version section from buildout. - -2.12.7 (2012-08-11) -------------------- - -- Added forward compatibility with DateTime 3 pickle format. DateTime - instances constructed under version 3 can be read and unpickled by this - version. The pickled data is converted to the current versions format - (old-style class / no slots). Once converted it will be stored again in the - old format. This should allow for a transparent upgrade/downgrade path - between DateTime 2 and 3. - -2.12.6 (2010-10-17) -------------------- - -- Changed ``testDayOfWeek`` test to be independent of OS locale. - -2.12.5 (2010-07-29) -------------------- - -- Launchpad #143269: Corrected the documentation for year value - behavior when constructing a DateTime object with three numeric - arguments. - -- Launchpad #142521: Removed confusing special case in - DateTime.__str__ where DateTime instances for midnight - (e.g. '2010-07-27 00:00:00 US/Eastern') values would - render only their date and nothing else. - -2.12.4 (2010-07-12) -------------------- - -- Fixed mapping of EDT (was -> 'GMT-0400', now 'GMT-4'). - -2.12.3 (2010-07-09) -------------------- - -- Added EDT timezone support. Addresses bug #599856. - [vangheem] - -2.12.2 (2010-05-05) -------------------- - -- Launchpad #572715: Relaxed pin on pytz, after applying a patch from - Marius Gedminus which fixes the apparent API breakage. - -2.12.1 (2010-04-30) -------------------- - -- Removed an undeclared testing dependency on zope.testing.doctest in favor of - the standard libraries doctest module. - -- Added a maximum version requirement on pytz <= 2010b. Later versions produce - test failures related to timezone changes. - -2.12.0 (2009-03-04) -------------------- - -- Launchpad #290254: Forward-ported fix for '_micros'-less pickles from - the Zope 2.11 branch version. - -2.11.2 (2009-02-02) -------------------- - -- Include *all* pytz zone names, not just "common" ones. - -- Fix one fragile doctest, band-aid another. - -- Fix for launchpad #267545: DateTime(DateTime()) should preserve the - correct hour. - -2.11.1 (2008-08-05) -------------------- - -- DateTime conversion of datetime objects with non-pytz tzinfo. Timezones() - returns a copy of the timezone list (allows tests to run). - -- Merged the slinkp-datetime-200007 branch: fix the DateTime(anotherDateTime) - constructor to preserve timezones. - -2.11.0b1 (2008-01-06) ---------------------- - -- Split off from the Zope2 main source code tree. diff --git a/venv/Lib/site-packages/DateTime-5.5.dist-info/RECORD b/venv/Lib/site-packages/DateTime-5.5.dist-info/RECORD deleted file mode 100644 index 1de0834..0000000 --- a/venv/Lib/site-packages/DateTime-5.5.dist-info/RECORD +++ /dev/null @@ -1,22 +0,0 @@ -DateTime-5.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -DateTime-5.5.dist-info/LICENSE.txt,sha256=PmcdsR32h1FswdtbPWXkqjg-rKPCDOo_r1Og9zNdCjw,2070 -DateTime-5.5.dist-info/METADATA,sha256=W1k0PqPJ6SU6QTJAu40JPtHK8XeQRL0GGEpfVGPjWGI,33735 -DateTime-5.5.dist-info/RECORD,, -DateTime-5.5.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -DateTime-5.5.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92 -DateTime-5.5.dist-info/top_level.txt,sha256=iVdUvuV_RIkkMzsnPGNfwojRWvuonInryaK3hA5Hh0o,9 -DateTime/DateTime.py,sha256=65LbTcnrCSsDPGoGLVkk7NC3H8Kq-PjkC1fQVR33gE8,71364 -DateTime/DateTime.txt,sha256=KZFzxoQItLsar1ZDd2vZN74Y6L4a04H8jXMwqc8KjmY,22487 -DateTime/__init__.py,sha256=trlFzEmNkmUpxZT7krPSVDayDK1bRxToccg3CcCF8wg,714 -DateTime/__pycache__/DateTime.cpython-310.pyc,, -DateTime/__pycache__/__init__.cpython-310.pyc,, -DateTime/__pycache__/interfaces.cpython-310.pyc,, -DateTime/__pycache__/pytz_support.cpython-310.pyc,, -DateTime/interfaces.py,sha256=n47sexf1eQ6YMdYB_60PgHtSzYIj4FND-RmHFiNpm1E,12187 -DateTime/pytz.txt,sha256=9Phns9ESXs9MaOKxXztX6sJ09QczGxsbYoSRSllKUfk,5619 -DateTime/pytz_support.py,sha256=inR1SO0X17fp9C2GsRw99S_MhxKiEt5dOV3-TGsBxDI,11853 -DateTime/tests/__init__.py,sha256=H7Ixo1xp-8BlJ65u14hk5i_TKEmETyi2FmLMD6H-mpo,683 -DateTime/tests/__pycache__/__init__.cpython-310.pyc,, -DateTime/tests/__pycache__/test_datetime.cpython-310.pyc,, -DateTime/tests/julian_testdata.txt,sha256=qxvLvabVB9ayhh5UHBvPhuqW5mRL_lizzbUh6lc3d4I,1397 -DateTime/tests/test_datetime.py,sha256=dsrfAqQpz1od1bOVPvSYfZAlduJpJIpc2F_hdN7WRAU,30385 diff --git a/venv/Lib/site-packages/DateTime-5.5.dist-info/REQUESTED b/venv/Lib/site-packages/DateTime-5.5.dist-info/REQUESTED deleted file mode 100644 index e69de29..0000000 diff --git a/venv/Lib/site-packages/DateTime-5.5.dist-info/WHEEL b/venv/Lib/site-packages/DateTime-5.5.dist-info/WHEEL deleted file mode 100644 index 98c0d20..0000000 --- a/venv/Lib/site-packages/DateTime-5.5.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.42.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/venv/Lib/site-packages/DateTime-5.5.dist-info/top_level.txt b/venv/Lib/site-packages/DateTime-5.5.dist-info/top_level.txt deleted file mode 100644 index 1b8c206..0000000 --- a/venv/Lib/site-packages/DateTime-5.5.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -DateTime diff --git a/venv/Lib/site-packages/DateTime/DateTime.py b/venv/Lib/site-packages/DateTime/DateTime.py deleted file mode 100644 index fc289e6..0000000 --- a/venv/Lib/site-packages/DateTime/DateTime.py +++ /dev/null @@ -1,1948 +0,0 @@ -############################################################################## -# -# Copyright (c) 2002 Zope Foundation and Contributors. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE -# -############################################################################## - -import copyreg as copy_reg -import math -import re -from datetime import datetime -from time import altzone -from time import daylight -from time import gmtime -from time import localtime -from time import time -from time import timezone -from time import tzname - -from zope.interface import implementer - -from .interfaces import DateError -from .interfaces import DateTimeError -from .interfaces import IDateTime -from .interfaces import SyntaxError -from .interfaces import TimeError -from .pytz_support import PytzCache - - -basestring = str -long = int -explicit_unicode_type = type(None) - -default_datefmt = None - - -def getDefaultDateFormat(): - global default_datefmt - if default_datefmt is None: - try: - from App.config import getConfiguration - default_datefmt = getConfiguration().datetime_format - return default_datefmt - except Exception: - return 'us' - else: - return default_datefmt - - -# To control rounding errors, we round system time to the nearest -# microsecond. Then delicate calculations can rely on the fact that the -# maximum precision that needs to be preserved is known. -_system_time = time - - -def time(): - return round(_system_time(), 6) - - -# Determine machine epoch -tm = ((0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334), - (0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)) -yr, mo, dy, hr, mn, sc = gmtime(0)[:6] -i = int(yr - 1) -to_year = int(i * 365 + i // 4 - i // 100 + i // 400 - 693960.0) -to_month = tm[yr % 4 == 0 and (yr % 100 != 0 or yr % 400 == 0)][mo] -EPOCH = ((to_year + to_month + dy + - (hr / 24.0 + mn / 1440.0 + sc / 86400.0)) * 86400) -jd1901 = 2415385 - -_TZINFO = PytzCache() - -INT_PATTERN = re.compile(r'([0-9]+)') -FLT_PATTERN = re.compile(r':([0-9]+\.[0-9]+)') -NAME_PATTERN = re.compile(r'([a-zA-Z]+)', re.I) -SPACE_CHARS = ' \t\n' -DELIMITERS = '-/.:,+' - -_MONTH_LEN = ((0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), - (0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)) -_MONTHS = ('', 'January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', 'November', 'December') -_MONTHS_A = ('', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec') -_MONTHS_P = ('', 'Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'June', - 'July', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.') -_MONTHMAP = {'january': 1, 'jan': 1, - 'february': 2, 'feb': 2, - 'march': 3, 'mar': 3, - 'april': 4, 'apr': 4, - 'may': 5, - 'june': 6, 'jun': 6, - 'july': 7, 'jul': 7, - 'august': 8, 'aug': 8, - 'september': 9, 'sep': 9, 'sept': 9, - 'october': 10, 'oct': 10, - 'november': 11, 'nov': 11, - 'december': 12, 'dec': 12} -_DAYS = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', - 'Thursday', 'Friday', 'Saturday') -_DAYS_A = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat') -_DAYS_P = ('Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.') -_DAYMAP = {'sunday': 1, 'sun': 1, - 'monday': 2, 'mon': 2, - 'tuesday': 3, 'tues': 3, 'tue': 3, - 'wednesday': 4, 'wed': 4, - 'thursday': 5, 'thurs': 5, 'thur': 5, 'thu': 5, - 'friday': 6, 'fri': 6, - 'saturday': 7, 'sat': 7} - -numericTimeZoneMatch = re.compile(r'[+-][0-9][0-9][0-9][0-9]').match -iso8601Match = re.compile(r''' - (?P\d\d\d\d) # four digits year - (?:-? # one optional dash - (?: # followed by: - (?P\d\d\d # three digits year day - (?!\d)) # when there is no fourth digit - | # or: - W # one W - (?P\d\d) # two digits week - (?:-? # one optional dash - (?P\d) # one digit week day - )? # week day is optional - | # or: - (?P\d\d)? # two digits month - (?:-? # one optional dash - (?P\d\d)? # two digits day - )? # after day is optional - ) # - )? # after year is optional - (?:[T ] # one T or one whitespace - (?P\d\d) # two digits hour - (?::? # one optional colon - (?P\d\d)? # two digits minute - (?::? # one optional colon - (?P\d\d)? # two digits second - (?:[.,] # one dot or one comma - (?P\d+) # n digits fraction - )? # after second is optional - )? # after minute is optional - )? # after hour is optional - (?: # timezone: - (?PZ) # one Z - | # or: - (?P[-+]) # one plus or one minus as signal - (?P\d # one digit for hour offset... - (?:\d(?!\d$) # ...or two, if not the last two digits - )?) # second hour offset digit is optional - (?::? # one optional colon - (?P\d\d) # two digits minute offset - )? # after hour offset is optional - )? # timezone is optional - )? # time is optional - (?P.*) # store the extra garbage -''', re.VERBOSE).match - - -def _findLocalTimeZoneName(isDST): - if not daylight: - # Daylight savings does not occur in this time zone. - isDST = 0 - try: - # Get the name of the current time zone depending - # on DST. - _localzone = PytzCache._zmap[tzname[isDST].lower()] - except BaseException: - try: - # Generate a GMT-offset zone name. - if isDST: - localzone = altzone - else: - localzone = timezone - offset = (-localzone / 3600.0) - majorOffset = int(offset) - if majorOffset != 0: - minorOffset = abs(int((offset % majorOffset) * 60.0)) - else: - minorOffset = 0 - m = majorOffset >= 0 and '+' or '' - lz = '%s%0.02d%0.02d' % (m, majorOffset, minorOffset) - _localzone = PytzCache._zmap[('GMT%s' % lz).lower()] - except BaseException: - _localzone = '' - return _localzone - - -_localzone0 = _findLocalTimeZoneName(0) -_localzone1 = _findLocalTimeZoneName(1) -_multipleZones = (_localzone0 != _localzone1) - -# Some utility functions for calculating dates: - - -def _calcSD(t): - # Returns timezone-independent days since epoch and the fractional - # part of the days. - dd = t + EPOCH - 86400.0 - d = dd / 86400.0 - s = d - math.floor(d) - return s, d - - -def _calcDependentSecond(tz, t): - # Calculates the timezone-dependent second (integer part only) - # from the timezone-independent second. - fset = _tzoffset(tz, t) - return fset + long(math.floor(t)) + long(EPOCH) - 86400 - - -def _calcDependentSecond2(yr, mo, dy, hr, mn, sc): - # Calculates the timezone-dependent second (integer part only) - # from the date given. - ss = int(hr) * 3600 + int(mn) * 60 + int(sc) - x = long(_julianday(yr, mo, dy) - jd1901) * 86400 + ss - return x - - -def _calcIndependentSecondEtc(tz, x, ms): - # Derive the timezone-independent second from the timezone - # dependent second. - fsetAtEpoch = _tzoffset(tz, 0.0) - nearTime = x - fsetAtEpoch - long(EPOCH) + 86400 + ms - # nearTime is now within an hour of being correct. - # Recalculate t according to DST. - fset = long(_tzoffset(tz, nearTime)) - d = (x - fset) / 86400.0 + (ms / 86400.0) - t = x - fset - long(EPOCH) + 86400 + ms - micros = (x + 86400 - fset) * 1000000 + \ - long(round(ms * 1000000.0)) - long(EPOCH * 1000000.0) - s = d - math.floor(d) - return (s, d, t, micros) - - -def _calcHMS(x, ms): - # hours, minutes, seconds from integer and float. - hr = x // 3600 - x = x - hr * 3600 - mn = x // 60 - sc = x - mn * 60 + ms - return (hr, mn, sc) - - -def _calcYMDHMS(x, ms): - # x is a timezone-dependent integer of seconds. - # Produces yr,mo,dy,hr,mn,sc. - yr, mo, dy = _calendarday(x // 86400 + jd1901) - x = int(x - (x // 86400) * 86400) - hr = x // 3600 - x = x - hr * 3600 - mn = x // 60 - sc = x - mn * 60 + ms - return (yr, mo, dy, hr, mn, sc) - - -def _julianday(yr, mo, dy): - y, m, d = long(yr), long(mo), long(dy) - if m > 12: - y = y + m // 12 - m = m % 12 - elif m < 1: - m = -m - y = y - m // 12 - 1 - m = 12 - m % 12 - if y > 0: - yr_correct = 0 - else: - yr_correct = 3 - if m < 3: - y, m = y - 1, m + 12 - if y * 10000 + m * 100 + d > 15821014: - b = 2 - y // 100 + y // 400 - else: - b = 0 - return ((1461 * y - yr_correct) // 4 + - 306001 * (m + 1) // 10000 + d + 1720994 + b) - - -def _calendarday(j): - j = long(j) - if (j < 2299160): - b = j + 1525 - else: - a = (4 * j - 7468861) // 146097 - b = j + 1526 + a - a // 4 - c = (20 * b - 2442) // 7305 - d = 1461 * c // 4 - e = 10000 * (b - d) // 306001 - dy = int(b - d - 306001 * e // 10000) - mo = (e < 14) and int(e - 1) or int(e - 13) - yr = (mo > 2) and (c - 4716) or (c - 4715) - return (int(yr), int(mo), int(dy)) - - -def _tzoffset(tz, t): - """Returns the offset in seconds to GMT from a specific timezone (tz) at - a specific time (t). NB! The _tzoffset result is the same same sign as - the time zone, i.e. GMT+2 has a 7200 second offset. This is the opposite - sign of time.timezone which (confusingly) is -7200 for GMT+2.""" - try: - return _TZINFO[tz].info(t)[0] - except Exception: - if numericTimeZoneMatch(tz) is not None: - return int(tz[0:3]) * 3600 + int(tz[0] + tz[3:5]) * 60 - else: - return 0 # ?? - - -def _correctYear(year): - # Y2K patch. - if year >= 0 and year < 100: - # 00-69 means 2000-2069, 70-99 means 1970-1999. - if year < 70: - year = 2000 + year - else: - year = 1900 + year - return year - - -def safegmtime(t): - '''gmtime with a safety zone.''' - try: - return gmtime(t) - except (ValueError, OverflowError): - raise TimeError('The time %f is beyond the range of this Python ' - 'implementation.' % float(t)) - - -def safelocaltime(t): - '''localtime with a safety zone.''' - try: - return localtime(t) - except (ValueError, OverflowError): - raise TimeError('The time %f is beyond the range of this Python ' - 'implementation.' % float(t)) - - -def _tzoffset2rfc822zone(seconds): - """Takes an offset, such as from _tzoffset(), and returns an rfc822 - compliant zone specification. Please note that the result of - _tzoffset() is the negative of what time.localzone and time.altzone is. - """ - return "%+03d%02d" % divmod((seconds // 60), 60) - - -def _tzoffset2iso8601zone(seconds): - """Takes an offset, such as from _tzoffset(), and returns an ISO 8601 - compliant zone specification. Please note that the result of - _tzoffset() is the negative of what time.localzone and time.altzone is. - """ - return "%+03d:%02d" % divmod((seconds // 60), 60) - - -def Timezones(): - """Return the list of recognized timezone names""" - return sorted(list(PytzCache._zmap.values())) - - -class strftimeFormatter: - - def __init__(self, dt, format): - self.dt = dt - self.format = format - - def __call__(self): - return self.dt.strftime(self.format) - - -@implementer(IDateTime) -class DateTime: - """DateTime objects represent instants in time and provide - interfaces for controlling its representation without - affecting the absolute value of the object. - - DateTime objects may be created from a wide variety of string - or numeric data, or may be computed from other DateTime objects. - DateTimes support the ability to convert their representations - to many major timezones, as well as the ability to create a - DateTime object in the context of a given timezone. - - DateTime objects provide partial numerical behavior: - - - Two date-time objects can be subtracted to obtain a time, - in days between the two. - - - A date-time object and a positive or negative number may - be added to obtain a new date-time object that is the given - number of days later than the input date-time object. - - - A positive or negative number and a date-time object may - be added to obtain a new date-time object that is the given - number of days later than the input date-time object. - - - A positive or negative number may be subtracted from a - date-time object to obtain a new date-time object that is - the given number of days earlier than the input date-time - object. - - DateTime objects may be converted to integer, long, or float - numbers of days since January 1, 1901, using the standard int, - long, and float functions (Compatibility Note: int, long and - float return the number of days since 1901 in GMT rather than - local machine timezone). DateTime objects also provide access - to their value in a float format usable with the Python time - module, provided that the value of the object falls in the - range of the epoch-based time module, and as a datetime.datetime - object. - - A DateTime object should be considered immutable; all conversion - and numeric operations return a new DateTime object rather than - modify the current object.""" - - # For security machinery: - __roles__ = None - __allow_access_to_unprotected_subobjects__ = 1 - - # Limit the amount of instance attributes - __slots__ = ( - '_timezone_naive', - '_tz', - '_dayoffset', - '_year', - '_month', - '_day', - '_hour', - '_minute', - '_second', - '_nearsec', - '_d', - '_micros', - 'time', - ) - - def __init__(self, *args, **kw): - """Return a new date-time object""" - try: - return self._parse_args(*args, **kw) - except (DateError, TimeError, DateTimeError): - raise - except Exception: - raise SyntaxError('Unable to parse {}, {}'.format(args, kw)) - - def __getstate__(self): - return (self._micros, - getattr(self, '_timezone_naive', False), - self._tz) - - def __setstate__(self, value): - if isinstance(value, tuple): - micros, tz_naive, tz = value - if isinstance(micros, float): - # BBB: support for pickle where micros was a float - micros = int(micros * 1000000) - self._parse_args(micros / 1000000., tz) - self._micros = micros - self._timezone_naive = tz_naive - else: - for k, v in value.items(): - if k in self.__slots__: - setattr(self, k, v) - # BBB: support for very old DateTime pickles - if '_micros' not in value: - self._micros = long(value['_t'] * 1000000) - if '_timezone_naive' not in value: - self._timezone_naive = False - - def _parse_args(self, *args, **kw): - """Return a new date-time object. - - A DateTime object always maintains its value as an absolute - UTC time, and is represented in the context of some timezone - based on the arguments used to create the object. A DateTime - object's methods return values based on the timezone context. - - Note that in all cases the local machine timezone is used for - representation if no timezone is specified. - - DateTimes may be created with zero to seven arguments. - - - If the function is called with no arguments or with None, - then the current date/time is returned, represented in the - timezone of the local machine. - - - If the function is invoked with a single string argument - which is a recognized timezone name, an object representing - the current time is returned, represented in the specified - timezone. - - - If the function is invoked with a single string argument - representing a valid date/time, an object representing - that date/time will be returned. - - As a general rule, any date-time representation that is - recognized and unambiguous to a resident of North America - is acceptable. The reason for this qualification is that - in North America, a date like: 2/1/1994 is interpreted - as February 1, 1994, while in some parts of the world, - it is interpreted as January 2, 1994. - - A date/time string consists of two components, a date - component and an optional time component, separated by one - or more spaces. If the time component is omitted, 12:00am is - assumed. Any recognized timezone name specified as the final - element of the date/time string will be used for computing - the date/time value. If you create a DateTime with the - string 'Mar 9, 1997 1:45pm US/Pacific', the value will - essentially be the same as if you had captured time.time() - at the specified date and time on a machine in that timezone: - -
-            e = DateTime('US/Eastern')
-            # returns current date/time, represented in US/Eastern.
-
-            x = DateTime('1997/3/9 1:45pm')
-            # returns specified time, represented in local machine zone.
-
-            y = DateTime('Mar 9, 1997 13:45:00')
-            # y is equal to x
-            
- - The date component consists of year, month, and day - values. The year value must be a one-, two-, or - four-digit integer. If a one- or two-digit year is - used, the year is assumed to be in the twentieth - century. The month may be an integer, from 1 to 12, a - month name, or a month abbreviation, where a period may - optionally follow the abbreviation. The day must be an - integer from 1 to the number of days in the month. The - year, month, and day values may be separated by - periods, hyphens, forward slashes, or spaces. Extra - spaces are permitted around the delimiters. Year, - month, and day values may be given in any order as long - as it is possible to distinguish the components. If all - three components are numbers that are less than 13, - then a month-day-year ordering is assumed. - - The time component consists of hour, minute, and second - values separated by colons. The hour value must be an - integer between 0 and 23 inclusively. The minute value - must be an integer between 0 and 59 inclusively. The - second value may be an integer value between 0 and - 59.999 inclusively. The second value or both the minute - and second values may be omitted. The time may be - followed by am or pm in upper or lower case, in which - case a 12-hour clock is assumed. - - New in Zope 2.4: - The DateTime constructor automatically detects and handles - ISO8601 compliant dates (YYYY-MM-DDThh:ss:mmTZD). - - New in Zope 2.9.6: - The existing ISO8601 parser was extended to support almost - the whole ISO8601 specification. New formats includes: - -
-            y = DateTime('1993-045')
-            # returns the 45th day from 1993, which is 14th February
-
-            w = DateTime('1993-W06-7')
-            # returns the 7th day from the 6th week from 1993, which
-            # is also 14th February
-            
- - See http://en.wikipedia.org/wiki/ISO_8601 for full specs. - - Note that the Zope DateTime parser assumes timezone naive ISO - strings to be in UTC rather than local time as specified. - - - If the DateTime function is invoked with a single numeric - argument, the number is assumed to be a floating point value - such as that returned by time.time(). - - A DateTime object is returned that represents the GMT value - of the time.time() float represented in the local machine's - timezone. - - - If the DateTime function is invoked with a single argument - that is a DateTime instance, a copy of the passed object will - be created. - - - New in 2.11: - The DateTime function may now be invoked with a single argument - that is a datetime.datetime instance. DateTimes may be converted - back to datetime.datetime objects with asdatetime(). - DateTime instances may be converted to a timezone naive - datetime.datetime in UTC with utcdatetime(). - - - If the function is invoked with two numeric arguments, then - the first is taken to be an integer year and the second - argument is taken to be an offset in days from the beginning - of the year, in the context of the local machine timezone. - - The date-time value returned is the given offset number of - days from the beginning of the given year, represented in - the timezone of the local machine. The offset may be positive - or negative. - - Two-digit years are assumed to be in the twentieth - century. - - - If the function is invoked with two arguments, the first - a float representing a number of seconds past the epoch - in gmt (such as those returned by time.time()) and the - second a string naming a recognized timezone, a DateTime - with a value of that gmt time will be returned, represented - in the given timezone. - -
-            import time
-            t = time.time()
-
-            now_east = DateTime(t,'US/Eastern')
-            # Time t represented as US/Eastern
-
-            now_west = DateTime(t,'US/Pacific')
-            # Time t represented as US/Pacific
-
-            # now_east == now_west
-            # only their representations are different
-            
- - - If the function is invoked with three or more numeric - arguments, then the first is taken to be an integer - year, the second is taken to be an integer month, and - the third is taken to be an integer day. If the - combination of values is not valid, then a - DateError is raised. Two-digit years are assumed - to be in the twentieth century. The fourth, fifth, and - sixth arguments specify a time in hours, minutes, and - seconds; hours and minutes should be positive integers - and seconds is a positive floating point value, all of - these default to zero if not given. An optional string may - be given as the final argument to indicate timezone (the - effect of this is as if you had taken the value of time.time() - at that time on a machine in the specified timezone). - - New in Zope 2.7: - A new keyword parameter "datefmt" can be passed to the - constructor. If set to "international", the constructor - is forced to treat ambiguous dates as "days before month - before year". This useful if you need to parse non-US - dates in a reliable way - - In any case that a floating point number of seconds is given - or derived, it's rounded to the nearest millisecond. - - If a string argument passed to the DateTime constructor cannot be - parsed, it will raise DateTime.SyntaxError. Invalid date components - will raise a DateError, while invalid time or timezone components - will raise a DateTimeError. - - The module function Timezones() will return a list of the (common) - timezones recognized by the DateTime module. Recognition of - timezone names is case-insensitive. - """ - - datefmt = kw.get('datefmt', getDefaultDateFormat()) - d = t = s = None - ac = len(args) - microsecs = None - - if ac == 10: - # Internal format called only by DateTime - yr, mo, dy, hr, mn, sc, tz, t, d, s = args - elif ac == 11: - # Internal format that includes milliseconds (from the epoch) - yr, mo, dy, hr, mn, sc, tz, t, d, s, millisecs = args - microsecs = millisecs * 1000 - - elif ac == 12: - # Internal format that includes microseconds (from the epoch) and a - # flag indicating whether this was constructed in a timezone naive - # manner - yr, mo, dy, hr, mn, sc, tz, t, d, s, microsecs, tznaive = args - if tznaive is not None: # preserve this information - self._timezone_naive = tznaive - - elif not args or (ac and args[0] is None): - # Current time, to be displayed in local timezone - t = time() - lt = safelocaltime(t) - tz = self.localZone(lt) - ms = (t - math.floor(t)) - s, d = _calcSD(t) - yr, mo, dy, hr, mn, sc = lt[:6] - sc = sc + ms - self._timezone_naive = False - - elif ac == 1: - arg = args[0] - - if arg == '': - raise SyntaxError(arg) - - if isinstance(arg, DateTime): - """Construct a new DateTime instance from a given - DateTime instance. - """ - t = arg.timeTime() - s, d = _calcSD(t) - yr, mo, dy, hr, mn, sc, tz = arg.parts() - - elif isinstance(arg, datetime): - yr, mo, dy, hr, mn, sc, numerictz, tznaive = \ - self._parse_iso8601_preserving_tznaive(arg.isoformat()) - if arg.tzinfo is None: - self._timezone_naive = True - tz = None - else: - self._timezone_naive = False - # if we have a pytz tzinfo, use the `zone` attribute - # as a key - tz = getattr(arg.tzinfo, 'zone', numerictz) - ms = sc - math.floor(sc) - x = _calcDependentSecond2(yr, mo, dy, hr, mn, sc) - - if tz: - try: - zone = _TZINFO[tz] - except DateTimeError: - try: - zone = _TZINFO[numerictz] - except DateTimeError: - raise DateTimeError( - 'Unknown time zone in date: %s' % arg) - tz = zone.tzinfo.zone - else: - tz = self._calcTimezoneName(x, ms) - s, d, t, microsecs = _calcIndependentSecondEtc(tz, x, ms) - - elif (isinstance(arg, basestring) and - arg.lower() in _TZINFO._zidx): - # Current time, to be displayed in specified timezone - t, tz = time(), _TZINFO._zmap[arg.lower()] - ms = (t - math.floor(t)) - # Use integer arithmetic as much as possible. - s, d = _calcSD(t) - x = _calcDependentSecond(tz, t) - yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, ms) - - elif isinstance(arg, basestring): - # Date/time string - iso8601 = iso8601Match(arg.strip()) - fields_iso8601 = iso8601 and iso8601.groupdict() or {} - if fields_iso8601 and not fields_iso8601.get('garbage'): - yr, mo, dy, hr, mn, sc, tz, tznaive = \ - self._parse_iso8601_preserving_tznaive(arg) - self._timezone_naive = tznaive - else: - yr, mo, dy, hr, mn, sc, tz = self._parse(arg, datefmt) - - if not self._validDate(yr, mo, dy): - raise DateError('Invalid date: %s' % arg) - if not self._validTime(hr, mn, int(sc)): - raise TimeError('Invalid time: %s' % arg) - ms = sc - math.floor(sc) - x = _calcDependentSecond2(yr, mo, dy, hr, mn, sc) - - if tz: - try: - tz = _TZINFO._zmap[tz.lower()] - except KeyError: - if numericTimeZoneMatch(tz) is None: - raise DateTimeError( - 'Unknown time zone in date: %s' % arg) - else: - tz = self._calcTimezoneName(x, ms) - s, d, t, microsecs = _calcIndependentSecondEtc(tz, x, ms) - - else: - # Seconds from epoch, gmt - t = arg - lt = safelocaltime(t) - tz = self.localZone(lt) - ms = (t - math.floor(t)) - s, d = _calcSD(t) - yr, mo, dy, hr, mn, sc = lt[:6] - sc = sc + ms - - elif ac == 2: - if isinstance(args[1], basestring): - # Seconds from epoch (gmt) and timezone - t, tz = args - ms = (t - math.floor(t)) - try: - tz = _TZINFO._zmap[tz.lower()] - except KeyError: - if numericTimeZoneMatch(tz) is None: - raise DateTimeError('Unknown time zone: %s' % tz) - # Use integer arithmetic as much as possible. - s, d = _calcSD(t) - x = _calcDependentSecond(tz, t) - yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, ms) - else: - # Year, julian expressed in local zone - t = time() - lt = safelocaltime(t) - tz = self.localZone(lt) - yr, jul = args - yr = _correctYear(yr) - d = (_julianday(yr, 1, 0) - jd1901) + jul - x_float = d * 86400.0 - x_floor = math.floor(x_float) - ms = x_float - x_floor - x = long(x_floor) - yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, ms) - s, d, t, microsecs = _calcIndependentSecondEtc(tz, x, ms) - else: - # Explicit format - yr, mo, dy = args[:3] - hr, mn, sc, tz = 0, 0, 0, 0 - yr = _correctYear(yr) - if not self._validDate(yr, mo, dy): - raise DateError('Invalid date: {}'.format(args)) - args = args[3:] - if args: - hr, args = args[0], args[1:] - if args: - mn, args = args[0], args[1:] - if args: - sc, args = args[0], args[1:] - if args: - tz, args = args[0], args[1:] - if args: - raise DateTimeError('Too many arguments') - if not self._validTime(hr, mn, sc): - raise TimeError('Invalid time: %s' % repr(args)) - - x = _calcDependentSecond2(yr, mo, dy, hr, mn, sc) - ms = sc - math.floor(sc) - if tz: - try: - tz = _TZINFO._zmap[tz.lower()] - except KeyError: - if numericTimeZoneMatch(tz) is None: - raise DateTimeError('Unknown time zone: %s' % tz) - else: - # Get local time zone name - tz = self._calcTimezoneName(x, ms) - s, d, t, microsecs = _calcIndependentSecondEtc(tz, x, ms) - - self._dayoffset = int((_julianday(yr, mo, dy) + 2) % 7) - # Round to nearest microsecond in platform-independent way. You - # cannot rely on C sprintf (Python '%') formatting to round - # consistently; doing it ourselves ensures that all but truly - # horrid C sprintf implementations will yield the same result - # cross-platform, provided the format asks for exactly 6 digits after - # the decimal point. - sc = round(sc, 6) - if sc >= 60.0: # can happen if, e.g., orig sc was 59.9999999 - sc = 59.999999 - self._nearsec = math.floor(sc) - self._year, self._month, self._day = yr, mo, dy - self._hour, self._minute, self._second = hr, mn, sc - self.time, self._d, self._tz = s, d, tz - # self._micros is the time since the epoch - # in long integer microseconds. - if microsecs is None: - microsecs = long(round(t * 1000000.0)) - self._micros = microsecs - - def localZone(self, ltm=None): - '''Returns the time zone on the given date. The time zone - can change according to daylight savings.''' - if not _multipleZones: - return _localzone0 - if ltm is None: - ltm = localtime(time()) - isDST = ltm[8] - lz = isDST and _localzone1 or _localzone0 - return lz - - def _calcTimezoneName(self, x, ms): - # Derive the name of the local time zone at the given - # timezone-dependent second. - if not _multipleZones: - return _localzone0 - fsetAtEpoch = _tzoffset(_localzone0, 0.0) - nearTime = x - fsetAtEpoch - long(EPOCH) + 86400 + ms - # nearTime is within an hour of being correct. - try: - ltm = safelocaltime(nearTime) - except BaseException: - # We are beyond the range of Python's date support. - # Hopefully we can assume that daylight savings schedules - # repeat every 28 years. Calculate the name of the - # time zone using a supported range of years. - yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, 0) - yr = ((yr - 1970) % 28) + 1970 - x = _calcDependentSecond2(yr, mo, dy, hr, mn, sc) - nearTime = x - fsetAtEpoch - long(EPOCH) + 86400 + ms - - # nearTime might still be negative if we are east of Greenwich. - # But we can assume on 1969/12/31 were no timezone changes. - nearTime = max(0, nearTime) - - ltm = safelocaltime(nearTime) - tz = self.localZone(ltm) - return tz - - def _parse(self, st, datefmt=getDefaultDateFormat()): - # Parse date-time components from a string - month = year = tz = tm = None - ValidZones = _TZINFO._zidx - TimeModifiers = ['am', 'pm'] - - # Find timezone first, since it should always be the last - # element, and may contain a slash, confusing the parser. - st = st.strip() - sp = st.split() - tz = sp[-1] - if tz and (tz.lower() in ValidZones): - self._timezone_naive = False - st = ' '.join(sp[:-1]) - else: - self._timezone_naive = True - tz = None # Decide later, since the default time zone - # could depend on the date. - - ints = [] - i = 0 - len_st = len(st) - while i < len_st: - while i < len_st and st[i] in SPACE_CHARS: - i += 1 - if i < len_st and st[i] in DELIMITERS: - d = st[i] - i += 1 - else: - d = '' - while i < len_st and st[i] in SPACE_CHARS: - i += 1 - - # The float pattern needs to look back 1 character, because it - # actually looks for a preceding colon like ':33.33'. This is - # needed to avoid accidentally matching the date part of a - # dot-separated date string such as '1999.12.31'. - if i > 0: - b = i - 1 - else: - b = i - - ts_results = FLT_PATTERN.match(st, b) - if ts_results: - s = ts_results.group(1) - i = i + len(s) - ints.append(float(s)) - continue - - ts_results = INT_PATTERN.match(st, i) - if ts_results: - s = ts_results.group(0) - - ls = len(s) - i = i + ls - if (ls == 4 and d and d in '+-' and - (len(ints) + (not not month) >= 3)): - tz = '{}{}'.format(d, s) - else: - v = int(s) - ints.append(v) - continue - - ts_results = NAME_PATTERN.match(st, i) - if ts_results: - s = ts_results.group(0).lower() - i = i + len(s) - if i < len_st and st[i] == '.': - i += 1 - # Check for month name: - _v = _MONTHMAP.get(s) - if _v is not None: - if month is None: - month = _v - else: - raise SyntaxError(st) - continue - # Check for time modifier: - if s in TimeModifiers: - if tm is None: - tm = s - else: - raise SyntaxError(st) - continue - # Check for and skip day of week: - if s in _DAYMAP: - continue - - raise SyntaxError(st) - - day = None - if ints[-1] > 60 and d not in ('.', ':', '/') and len(ints) > 2: - year = ints[-1] - del ints[-1] - if month: - day = ints[0] - del ints[:1] - else: - if datefmt == "us": - month = ints[0] - day = ints[1] - else: - month = ints[1] - day = ints[0] - del ints[:2] - elif month: - if len(ints) > 1: - if ints[0] > 31: - year = ints[0] - day = ints[1] - else: - year = ints[1] - day = ints[0] - del ints[:2] - elif len(ints) > 2: - if ints[0] > 31: - year = ints[0] - if ints[1] > 12: - day = ints[1] - month = ints[2] - else: - day = ints[2] - month = ints[1] - if ints[1] > 31: - year = ints[1] - if ints[0] > 12 and ints[2] <= 12: - day = ints[0] - month = ints[2] - elif ints[2] > 12 and ints[0] <= 12: - day = ints[2] - month = ints[0] - elif ints[2] > 31: - year = ints[2] - if ints[0] > 12: - day = ints[0] - month = ints[1] - else: - if datefmt == "us": - day = ints[1] - month = ints[0] - else: - day = ints[0] - month = ints[1] - - elif ints[0] <= 12: - month = ints[0] - day = ints[1] - year = ints[2] - del ints[:3] - - if day is None: - # Use today's date. - year, month, day = localtime(time())[:3] - - year = _correctYear(year) - if year < 1000: - raise SyntaxError(st) - - leap = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) - try: - if not day or day > _MONTH_LEN[leap][month]: - raise DateError(st) - except IndexError: - raise DateError(st) - - tod = 0 - if ints: - i = ints[0] - # Modify hour to reflect am/pm - if tm and (tm == 'pm') and i < 12: - i += 12 - if tm and (tm == 'am') and i == 12: - i = 0 - if i > 24: - raise TimeError(st) - tod = tod + int(i) * 3600 - del ints[0] - if ints: - i = ints[0] - if i > 60: - raise TimeError(st) - tod = tod + int(i) * 60 - del ints[0] - if ints: - i = ints[0] - if i > 60: - raise TimeError(st) - tod = tod + i - del ints[0] - if ints: - raise SyntaxError(st) - - tod_int = int(math.floor(tod)) - ms = tod - tod_int - hr, mn, sc = _calcHMS(tod_int, ms) - if not tz: - # Figure out what time zone it is in the local area - # on the given date. - x = _calcDependentSecond2(year, month, day, hr, mn, sc) - tz = self._calcTimezoneName(x, ms) - - return year, month, day, hr, mn, sc, tz - - # Internal methods - def _validDate(self, y, m, d): - if m < 1 or m > 12 or y < 0 or d < 1 or d > 31: - return 0 - return d <= _MONTH_LEN[ - (y % 4 == 0 and (y % 100 != 0 or y % 400 == 0))][m] - - def _validTime(self, h, m, s): - return h >= 0 and h <= 23 and m >= 0 and m <= 59 and s >= 0 and s < 60 - - def __getattr__(self, name): - if '%' in name: - return strftimeFormatter(self, name) - raise AttributeError(name) - - # Conversion and comparison methods - - def timeTime(self): - """Return the date/time as a floating-point number in UTC, - in the format used by the Python time module. - - Note that it is possible to create date/time values with - DateTime that have no meaningful value to the time module. - """ - return self._micros / 1000000.0 - - def toZone(self, z): - """Return a DateTime with the value as the current - object, represented in the indicated timezone. - """ - t, tz = self._t, _TZINFO._zmap[z.lower()] - micros = self.micros() - tznaive = False # you're performing a timzone change, can't be naive - - try: - # Try to use time module for speed. - yr, mo, dy, hr, mn, sc = safegmtime(t + _tzoffset(tz, t))[:6] - sc = self._second - return self.__class__(yr, mo, dy, hr, mn, sc, tz, t, - self._d, self.time, micros, tznaive) - except Exception: - # gmtime can't perform the calculation in the given range. - # Calculate the difference between the two time zones. - tzdiff = _tzoffset(tz, t) - _tzoffset(self._tz, t) - if tzdiff == 0: - return self - sc = self._second - ms = sc - math.floor(sc) - x = _calcDependentSecond2(self._year, self._month, self._day, - self._hour, self._minute, sc) - x_new = x + tzdiff - yr, mo, dy, hr, mn, sc = _calcYMDHMS(x_new, ms) - return self.__class__(yr, mo, dy, hr, mn, sc, tz, t, - self._d, self.time, micros, tznaive) - - def isFuture(self): - """Return true if this object represents a date/time - later than the time of the call. - """ - return (self._t > time()) - - def isPast(self): - """Return true if this object represents a date/time - earlier than the time of the call. - """ - return (self._t < time()) - - def isCurrentYear(self): - """Return true if this object represents a date/time - that falls within the current year, in the context - of this object's timezone representation. - """ - t = time() - return safegmtime(t + _tzoffset(self._tz, t))[0] == self._year - - def isCurrentMonth(self): - """Return true if this object represents a date/time - that falls within the current month, in the context - of this object's timezone representation. - """ - t = time() - gmt = safegmtime(t + _tzoffset(self._tz, t)) - return gmt[0] == self._year and gmt[1] == self._month - - def isCurrentDay(self): - """Return true if this object represents a date/time - that falls within the current day, in the context - of this object's timezone representation. - """ - t = time() - gmt = safegmtime(t + _tzoffset(self._tz, t)) - return (gmt[0] == self._year and gmt[1] == self._month and - gmt[2] == self._day) - - def isCurrentHour(self): - """Return true if this object represents a date/time - that falls within the current hour, in the context - of this object's timezone representation. - """ - t = time() - gmt = safegmtime(t + _tzoffset(self._tz, t)) - return (gmt[0] == self._year and gmt[1] == self._month and - gmt[2] == self._day and gmt[3] == self._hour) - - def isCurrentMinute(self): - """Return true if this object represents a date/time - that falls within the current minute, in the context - of this object's timezone representation. - """ - t = time() - gmt = safegmtime(t + _tzoffset(self._tz, t)) - return (gmt[0] == self._year and gmt[1] == self._month and - gmt[2] == self._day and gmt[3] == self._hour and - gmt[4] == self._minute) - - def earliestTime(self): - """Return a new DateTime object that represents the earliest - possible time (in whole seconds) that still falls within - the current object's day, in the object's timezone context. - """ - return self.__class__( - self._year, self._month, self._day, 0, 0, 0, self._tz) - - def latestTime(self): - """Return a new DateTime object that represents the latest - possible time (in whole seconds) that still falls within - the current object's day, in the object's timezone context. - """ - return self.__class__( - self._year, self._month, self._day, 23, 59, 59, self._tz) - - def greaterThan(self, t): - """Compare this DateTime object to another DateTime object - OR a floating point number such as that which is returned - by the Python time module. - - Returns true if the object represents a date/time greater - than the specified DateTime or time module style time. - - Revised to give more correct results through comparison of - long integer microseconds. - """ - if t is None: - return True - if isinstance(t, (float, int)): - return self._micros > long(t * 1000000) - try: - return self._micros > t._micros - except AttributeError: - return self._micros > t - - __gt__ = greaterThan - - def greaterThanEqualTo(self, t): - """Compare this DateTime object to another DateTime object - OR a floating point number such as that which is returned - by the Python time module. - - Returns true if the object represents a date/time greater - than or equal to the specified DateTime or time module style - time. - - Revised to give more correct results through comparison of - long integer microseconds. - """ - if t is None: - return True - if isinstance(t, (float, int)): - return self._micros >= long(t * 1000000) - try: - return self._micros >= t._micros - except AttributeError: - return self._micros >= t - - __ge__ = greaterThanEqualTo - - def equalTo(self, t): - """Compare this DateTime object to another DateTime object - OR a floating point number such as that which is returned - by the Python time module. - - Returns true if the object represents a date/time equal to - the specified DateTime or time module style time. - - Revised to give more correct results through comparison of - long integer microseconds. - """ - if t is None: - return False - if isinstance(t, (float, int)): - return self._micros == long(t * 1000000) - try: - return self._micros == t._micros - except AttributeError: - return self._micros == t - - def notEqualTo(self, t): - """Compare this DateTime object to another DateTime object - OR a floating point number such as that which is returned - by the Python time module. - - Returns true if the object represents a date/time not equal - to the specified DateTime or time module style time. - - Revised to give more correct results through comparison of - long integer microseconds. - """ - return not self.equalTo(t) - - def __eq__(self, t): - """Compare this DateTime object to another DateTime object. - Return True if their internal state is the same. Two objects - representing the same time in different timezones are regared as - unequal. Use the equalTo method if you are only interested in them - referring to the same moment in time. - """ - if not isinstance(t, DateTime): - return False - return (self._micros, self._tz) == (t._micros, t._tz) - - def __ne__(self, t): - return not self.__eq__(t) - - def lessThan(self, t): - """Compare this DateTime object to another DateTime object - OR a floating point number such as that which is returned - by the Python time module. - - Returns true if the object represents a date/time less than - the specified DateTime or time module style time. - - Revised to give more correct results through comparison of - long integer microseconds. - """ - if t is None: - return False - if isinstance(t, (float, int)): - return self._micros < long(t * 1000000) - try: - return self._micros < t._micros - except AttributeError: - return self._micros < t - - __lt__ = lessThan - - def lessThanEqualTo(self, t): - """Compare this DateTime object to another DateTime object - OR a floating point number such as that which is returned - by the Python time module. - - Returns true if the object represents a date/time less than - or equal to the specified DateTime or time module style time. - - Revised to give more correct results through comparison of - long integer microseconds. - """ - if t is None: - return False - if isinstance(t, (float, int)): - return self._micros <= long(t * 1000000) - try: - return self._micros <= t._micros - except AttributeError: - return self._micros <= t - - __le__ = lessThanEqualTo - - def isLeapYear(self): - """Return true if the current year (in the context of the - object's timezone) is a leap year. - """ - return (self._year % 4 == 0 and - (self._year % 100 != 0 or self._year % 400 == 0)) - - def dayOfYear(self): - """Return the day of the year, in context of the timezone - representation of the object. - """ - d = int(self._d + (_tzoffset(self._tz, self._t) / 86400.0)) - return int((d + jd1901) - _julianday(self._year, 1, 0)) - - # Component access - def parts(self): - """Return a tuple containing the calendar year, month, - day, hour, minute second and timezone of the object. - """ - return (self._year, self._month, self._day, self._hour, - self._minute, self._second, self._tz) - - def timezone(self): - """Return the timezone in which the object is represented.""" - return self._tz - - def tzoffset(self): - """Return the timezone offset for the objects timezone.""" - return _tzoffset(self._tz, self._t) - - def year(self): - """Return the calendar year of the object.""" - return self._year - - def month(self): - """Return the month of the object as an integer.""" - return self._month - - @property - def _fmon(self): - return _MONTHS[self._month] - - def Month(self): - """Return the full month name.""" - return self._fmon - - @property - def _amon(self): - return _MONTHS_A[self._month] - - def aMonth(self): - """Return the abbreviated month name.""" - return self._amon - - def Mon(self): - """Compatibility: see aMonth.""" - return self._amon - - @property - def _pmon(self): - return _MONTHS_P[self._month] - - def pMonth(self): - """Return the abbreviated (with period) month name.""" - return self._pmon - - def Mon_(self): - """Compatibility: see pMonth.""" - return self._pmon - - def day(self): - """Return the integer day.""" - return self._day - - @property - def _fday(self): - return _DAYS[self._dayoffset] - - def Day(self): - """Return the full name of the day of the week.""" - return self._fday - - def DayOfWeek(self): - """Compatibility: see Day.""" - return self._fday - - @property - def _aday(self): - return _DAYS_A[self._dayoffset] - - def aDay(self): - """Return the abbreviated name of the day of the week.""" - return self._aday - - @property - def _pday(self): - return _DAYS_P[self._dayoffset] - - def pDay(self): - """Return the abbreviated (with period) name of the day of the week.""" - return self._pday - - def Day_(self): - """Compatibility: see pDay.""" - return self._pday - - def dow(self): - """Return the integer day of the week, where Sunday is 0.""" - return self._dayoffset - - def dow_1(self): - """Return the integer day of the week, where Sunday is 1.""" - return self._dayoffset + 1 - - @property - def _pmhour(self): - hr = self._hour - if hr > 12: - return hr - 12 - return hr or 12 - - def h_12(self): - """Return the 12-hour clock representation of the hour.""" - return self._pmhour - - def h_24(self): - """Return the 24-hour clock representation of the hour.""" - return self._hour - - @property - def _pm(self): - hr = self._hour - if hr >= 12: - return 'pm' - return 'am' - - def ampm(self): - """Return the appropriate time modifier (am or pm).""" - return self._pm - - def hour(self): - """Return the 24-hour clock representation of the hour.""" - return self._hour - - def minute(self): - """Return the minute.""" - return self._minute - - def second(self): - """Return the second.""" - return self._second - - def millis(self): - """Return the millisecond since the epoch in GMT.""" - return self._micros // 1000 - - def micros(self): - """Return the microsecond since the epoch in GMT.""" - return self._micros - - def timezoneNaive(self): - """The Python datetime module introduces the idea of distinguishing - between timezone aware and timezone naive datetime values. For lossless - conversion to and from datetime.datetime we record this - information using True / False. DateTime makes no distinction, if we - don't have any information we return None here. - """ - try: - return self._timezone_naive - except AttributeError: - return None - - def strftime(self, format): - """Format the date/time using the *current timezone representation*.""" - x = _calcDependentSecond2(self._year, self._month, self._day, - self._hour, self._minute, self._second) - ltz = self._calcTimezoneName(x, 0) - tzdiff = _tzoffset(ltz, self._t) - _tzoffset(self._tz, self._t) - zself = self + tzdiff / 86400.0 - microseconds = int((zself._second - zself._nearsec) * 1000000) - unicode_format = False - if isinstance(format, explicit_unicode_type): - format = format.encode('utf-8') - unicode_format = True - ds = datetime(zself._year, zself._month, zself._day, zself._hour, - zself._minute, int(zself._nearsec), - microseconds).strftime(format) - if unicode_format: - return ds.decode('utf-8') - return ds - - # General formats from previous DateTime - def Date(self): - """Return the date string for the object.""" - return "%s/%2.2d/%2.2d" % (self._year, self._month, self._day) - - def Time(self): - """Return the time string for an object to the nearest second.""" - return '%2.2d:%2.2d:%2.2d' % (self._hour, self._minute, self._nearsec) - - def TimeMinutes(self): - """Return the time string for an object not showing seconds.""" - return '%2.2d:%2.2d' % (self._hour, self._minute) - - def AMPM(self): - """Return the time string for an object to the nearest second.""" - return '%2.2d:%2.2d:%2.2d %s' % ( - self._pmhour, self._minute, self._nearsec, self._pm) - - def AMPMMinutes(self): - """Return the time string for an object not showing seconds.""" - return '%2.2d:%2.2d %s' % (self._pmhour, self._minute, self._pm) - - def PreciseTime(self): - """Return the time string for the object.""" - return '%2.2d:%2.2d:%06.3f' % (self._hour, self._minute, self._second) - - def PreciseAMPM(self): - """Return the time string for the object.""" - return '%2.2d:%2.2d:%06.3f %s' % ( - self._pmhour, self._minute, self._second, self._pm) - - def yy(self): - """Return calendar year as a 2 digit string.""" - return str(self._year)[-2:] - - def mm(self): - """Return month as a 2 digit string.""" - return '%02d' % self._month - - def dd(self): - """Return day as a 2 digit string.""" - return '%02d' % self._day - - def rfc822(self): - """Return the date in RFC 822 format.""" - tzoffset = _tzoffset2rfc822zone(_tzoffset(self._tz, self._t)) - return '%s, %2.2d %s %d %2.2d:%2.2d:%2.2d %s' % ( - self._aday, self._day, self._amon, self._year, - self._hour, self._minute, self._nearsec, tzoffset) - - # New formats - def fCommon(self): - """Return a string representing the object's value - in the format: March 1, 1997 1:45 pm. - """ - return '%s %s, %4.4d %s:%2.2d %s' % ( - self._fmon, self._day, self._year, self._pmhour, - self._minute, self._pm) - - def fCommonZ(self): - """Return a string representing the object's value - in the format: March 1, 1997 1:45 pm US/Eastern. - """ - return '%s %s, %4.4d %d:%2.2d %s %s' % ( - self._fmon, self._day, self._year, self._pmhour, - self._minute, self._pm, self._tz) - - def aCommon(self): - """Return a string representing the object's value - in the format: Mar 1, 1997 1:45 pm. - """ - return '%s %s, %4.4d %s:%2.2d %s' % ( - self._amon, self._day, self._year, self._pmhour, - self._minute, self._pm) - - def aCommonZ(self): - """Return a string representing the object's value - in the format: Mar 1, 1997 1:45 pm US/Eastern. - """ - return '%s %s, %4.4d %d:%2.2d %s %s' % ( - self._amon, self._day, self._year, self._pmhour, - self._minute, self._pm, self._tz) - - def pCommon(self): - """Return a string representing the object's value - in the format: Mar. 1, 1997 1:45 pm. - """ - return '%s %s, %4.4d %s:%2.2d %s' % ( - self._pmon, self._day, self._year, self._pmhour, - self._minute, self._pm) - - def pCommonZ(self): - """Return a string representing the object's value - in the format: Mar. 1, 1997 1:45 pm US/Eastern. - """ - return '%s %s, %4.4d %d:%2.2d %s %s' % ( - self._pmon, self._day, self._year, self._pmhour, - self._minute, self._pm, self._tz) - - def ISO(self): - """Return the object in ISO standard format. - - Note: this is *not* ISO 8601-format! See the ISO8601 and - HTML4 methods below for ISO 8601-compliant output. - - Dates are output as: YYYY-MM-DD HH:MM:SS - """ - return "%.4d-%.2d-%.2d %.2d:%.2d:%.2d" % ( - self._year, self._month, self._day, - self._hour, self._minute, self._second) - - def ISO8601(self): - """Return the object in ISO 8601-compatible format containing the - date, time with seconds-precision and the time zone identifier. - - See: http://www.w3.org/TR/NOTE-datetime - - Dates are output as: YYYY-MM-DDTHH:MM:SSTZD - T is a literal character. - TZD is Time Zone Designator, format +HH:MM or -HH:MM - - If the instance is timezone naive (it was not specified with a timezone - when it was constructed) then the timezone is omitted. - - The HTML4 method below offers the same formatting, but converts - to UTC before returning the value and sets the TZD "Z". - """ - if self.timezoneNaive(): - return "%0.4d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d" % ( - self._year, self._month, self._day, - self._hour, self._minute, self._second) - tzoffset = _tzoffset2iso8601zone(_tzoffset(self._tz, self._t)) - return "%0.4d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d%s" % ( - self._year, self._month, self._day, - self._hour, self._minute, self._second, tzoffset) - - def HTML4(self): - """Return the object in the format used in the HTML4.0 specification, - one of the standard forms in ISO8601. - - See: http://www.w3.org/TR/NOTE-datetime - - Dates are output as: YYYY-MM-DDTHH:MM:SSZ - T, Z are literal characters. - The time is in UTC. - """ - newdate = self.toZone('UTC') - return "%0.4d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2dZ" % ( - newdate._year, newdate._month, newdate._day, - newdate._hour, newdate._minute, newdate._second) - - def asdatetime(self): - """Return a standard library datetime.datetime - """ - tznaive = self.timezoneNaive() - if tznaive: - tzinfo = None - else: - tzinfo = _TZINFO[self._tz].tzinfo - second = int(self._second) - microsec = self.micros() % 1000000 - dt = datetime(self._year, self._month, self._day, self._hour, - self._minute, second, microsec, tzinfo) - return dt - - def utcdatetime(self): - """Convert the time to UTC and return a timezone naive datetime object - """ - utc = self.toZone('UTC') - second = int(utc._second) - microsec = utc.micros() % 1000000 - dt = datetime(utc._year, utc._month, utc._day, utc._hour, - utc._minute, second, microsec) - return dt - - def __add__(self, other): - """A DateTime may be added to a number and a number may be - added to a DateTime; two DateTimes cannot be added. - """ - if hasattr(other, '_t'): - raise DateTimeError('Cannot add two DateTimes') - o = float(other) - tz = self._tz - omicros = round(o * 86400000000) - tmicros = self.micros() + omicros - t = tmicros / 1000000.0 - d = (tmicros + long(EPOCH * 1000000)) / 86400000000.0 - s = d - math.floor(d) - ms = t - math.floor(t) - x = _calcDependentSecond(tz, t) - yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, ms) - return self.__class__(yr, mo, dy, hr, mn, sc, self._tz, - t, d, s, tmicros, self.timezoneNaive()) - - __radd__ = __add__ - - def __sub__(self, other): - """Either a DateTime or a number may be subtracted from a - DateTime, however, a DateTime may not be subtracted from - a number. - """ - if hasattr(other, '_d'): - return (self.micros() - other.micros()) / 86400000000.0 - else: - return self.__add__(-(other)) - - def __repr__(self): - """Convert a DateTime to a string that looks like a Python - expression. - """ - return '{}(\'{}\')'.format(self.__class__.__name__, str(self)) - - def __str__(self): - """Convert a DateTime to a string.""" - y, m, d = self._year, self._month, self._day - h, mn, s, t = self._hour, self._minute, self._second, self._tz - if s == int(s): - # A whole number of seconds -- suppress milliseconds. - return '%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d %s' % ( - y, m, d, h, mn, s, t) - else: - # s is already rounded to the nearest microsecond, and - # it's not a whole number of seconds. Be sure to print - # 2 digits before the decimal point. - return '%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%06.6f %s' % ( - y, m, d, h, mn, s, t) - - def __format__(self, fmt): - """Render a DateTime in an f-string.""" - if not isinstance(fmt, str): - raise TypeError("must be str, not %s" % type(fmt).__name__) - if len(fmt) != 0: - return self.strftime(fmt) - return str(self) - - def __hash__(self): - """Compute a hash value for a DateTime.""" - return int(((self._year % 100 * 12 + self._month) * 31 + - self._day + self.time) * 100) - - def __int__(self): - """Convert to an integer number of seconds since the epoch (gmt).""" - return int(self.micros() // 1000000) - - def __long__(self): - """Convert to a long-int number of seconds since the epoch (gmt).""" - return long(self.micros() // 1000000) # pragma: PY2 - - def __float__(self): - """Convert to floating-point number of seconds since the epoch (gmt). - """ - return self.micros() / 1000000.0 - - @property - def _t(self): - return self._micros / 1000000.0 - - def _parse_iso8601(self, s): - # preserve the previously implied contract - # who knows where this could be used... - return self._parse_iso8601_preserving_tznaive(s)[:7] - - def _parse_iso8601_preserving_tznaive(self, s): - try: - return self.__parse_iso8601(s) - except IndexError: - raise SyntaxError( - 'Not an ISO 8601 compliant date string: "%s"' % s) - - def __parse_iso8601(self, s): - """Parse an ISO 8601 compliant date. - - See: http://en.wikipedia.org/wiki/ISO_8601 - """ - month = day = week_day = 1 - year = hour = minute = seconds = hour_off = min_off = 0 - tznaive = True - - iso8601 = iso8601Match(s.strip()) - fields = iso8601 and iso8601.groupdict() or {} - if not iso8601 or fields.get('garbage'): - raise IndexError - - if fields['year']: - year = int(fields['year']) - if fields['month']: - month = int(fields['month']) - if fields['day']: - day = int(fields['day']) - - if fields['year_day']: - d = DateTime('%s-01-01' % year) + int(fields['year_day']) - 1 - month = d.month() - day = d.day() - - if fields['week']: - week = int(fields['week']) - if fields['week_day']: - week_day = int(fields['week_day']) - d = DateTime('%s-01-04' % year) - d = d - (d.dow() + 6) % 7 + week * 7 + week_day - 8 - month = d.month() - day = d.day() - - if fields['hour']: - hour = int(fields['hour']) - - if fields['minute']: - minute = int(fields['minute']) - elif fields['fraction']: - minute = 60.0 * float('0.%s' % fields['fraction']) - seconds, minute = math.modf(minute) - minute = int(minute) - seconds = 60.0 * seconds - # Avoid reprocess when handling seconds, bellow - fields['fraction'] = None - - if fields['second']: - seconds = int(fields['second']) - if fields['fraction']: - seconds = seconds + float('0.%s' % fields['fraction']) - elif fields['fraction']: - seconds = 60.0 * float('0.%s' % fields['fraction']) - - if fields['hour_off']: - hour_off = int(fields['hour_off']) - if fields['signal'] == '-': - hour_off *= -1 - - if fields['min_off']: - min_off = int(fields['min_off']) - - if fields['signal'] or fields['Z']: - tznaive = False - else: - tznaive = True - - # Differ from the specification here. To preserve backwards - # compatibility assume a default timezone == UTC. - tz = 'GMT%+03d%02d' % (hour_off, min_off) - - return year, month, day, hour, minute, seconds, tz, tznaive - - def JulianDay(self): - """Return the Julian day. - - See: https://www.tondering.dk/claus/cal/julperiod.php#formula - """ - a = (14 - self._month) // 12 - y = self._year + 4800 - a - m = self._month + (12 * a) - 3 - return (self._day + (153 * m + 2) // 5 + 365 * y + - y // 4 - y // 100 + y // 400 - 32045) - - def week(self): - """Return the week number according to ISO. - - See: https://www.tondering.dk/claus/cal/week.php#weekno - """ - J = self.JulianDay() - d4 = (J + 31741 - (J % 7)) % 146097 % 36524 % 1461 - L = d4 // 1460 - d1 = ((d4 - L) % 365) + L - return d1 // 7 + 1 - - def encode(self, out): - """Encode value for XML-RPC.""" - out.write('') - out.write(self.ISO8601()) - out.write('\n') - - -# Provide the _dt_reconstructor function here, in case something -# accidentally creates a reference to this function - -orig_reconstructor = copy_reg._reconstructor - - -def _dt_reconstructor(cls, base, state): - if cls is DateTime: - return cls(state) - return orig_reconstructor(cls, base, state) diff --git a/venv/Lib/site-packages/DateTime/DateTime.txt b/venv/Lib/site-packages/DateTime/DateTime.txt deleted file mode 100644 index aaa9f8f..0000000 --- a/venv/Lib/site-packages/DateTime/DateTime.txt +++ /dev/null @@ -1,785 +0,0 @@ -The DateTime package -==================== - -Encapsulation of date/time values. - - -Function Timezones() --------------------- - -Returns the list of recognized timezone names: - - >>> from DateTime import Timezones - >>> zones = set(Timezones()) - -Almost all of the standard pytz timezones are included, with the exception -of some commonly-used but ambiguous abbreviations, where historical Zope -usage conflicts with the name used by pytz: - - >>> import pytz - >>> [x for x in pytz.all_timezones if x not in zones] - ['CET', 'EET', 'EST', 'MET', 'MST', 'WET'] - -Class DateTime --------------- - -DateTime objects represent instants in time and provide interfaces for -controlling its representation without affecting the absolute value of -the object. - -DateTime objects may be created from a wide variety of string or -numeric data, or may be computed from other DateTime objects. -DateTimes support the ability to convert their representations to many -major timezones, as well as the ability to create a DateTime object -in the context of a given timezone. - -DateTime objects provide partial numerical behavior: - -* Two date-time objects can be subtracted to obtain a time, in days - between the two. - -* A date-time object and a positive or negative number may be added to - obtain a new date-time object that is the given number of days later - than the input date-time object. - -* A positive or negative number and a date-time object may be added to - obtain a new date-time object that is the given number of days later - than the input date-time object. - -* A positive or negative number may be subtracted from a date-time - object to obtain a new date-time object that is the given number of - days earlier than the input date-time object. - -DateTime objects may be converted to integer, long, or float numbers -of days since January 1, 1901, using the standard int, long, and float -functions (Compatibility Note: int, long and float return the number -of days since 1901 in GMT rather than local machine timezone). -DateTime objects also provide access to their value in a float format -usable with the Python time module, provided that the value of the -object falls in the range of the epoch-based time module. - -A DateTime object should be considered immutable; all conversion and numeric -operations return a new DateTime object rather than modify the current object. - -A DateTime object always maintains its value as an absolute UTC time, -and is represented in the context of some timezone based on the -arguments used to create the object. A DateTime object's methods -return values based on the timezone context. - -Note that in all cases the local machine timezone is used for -representation if no timezone is specified. - -Constructor for DateTime ------------------------- - -DateTime() returns a new date-time object. DateTimes may be created -with from zero to seven arguments: - -* If the function is called with no arguments, then the current date/ - time is returned, represented in the timezone of the local machine. - -* If the function is invoked with a single string argument which is a - recognized timezone name, an object representing the current time is - returned, represented in the specified timezone. - -* If the function is invoked with a single string argument - representing a valid date/time, an object representing that date/ - time will be returned. - - As a general rule, any date-time representation that is recognized - and unambiguous to a resident of North America is acceptable. (The - reason for this qualification is that in North America, a date like: - 2/1/1994 is interpreted as February 1, 1994, while in some parts of - the world, it is interpreted as January 2, 1994.) A date/ time - string consists of two components, a date component and an optional - time component, separated by one or more spaces. If the time - component is omitted, 12:00am is assumed. - - Any recognized timezone name specified as the final element of the - date/time string will be used for computing the date/time value. - (If you create a DateTime with the string, - "Mar 9, 1997 1:45pm US/Pacific", the value will essentially be the - same as if you had captured time.time() at the specified date and - time on a machine in that timezone). If no timezone is passed, then - the timezone configured on the local machine will be used, **except** - that if the date format matches ISO 8601 ('YYYY-MM-DD'), the instance - will use UTC / GMT+0 as the timezone. - - o Returns current date/time, represented in US/Eastern: - - >>> from DateTime import DateTime - >>> e = DateTime('US/Eastern') - >>> e.timezone() - 'US/Eastern' - - o Returns specified time, represented in local machine zone: - - >>> x = DateTime('1997/3/9 1:45pm') - >>> x.parts() # doctest: +ELLIPSIS - (1997, 3, 9, 13, 45, ...) - - o Specified time in local machine zone, verbose format: - - >>> y = DateTime('Mar 9, 1997 13:45:00') - >>> y.parts() # doctest: +ELLIPSIS - (1997, 3, 9, 13, 45, ...) - >>> y == x - True - - o Specified time in UTC via ISO 8601 rule: - - >>> z = DateTime('2014-03-24') - >>> z.parts() # doctest: +ELLIPSIS - (2014, 3, 24, 0, 0, ...) - >>> z.timezone() - 'GMT+0' - - The date component consists of year, month, and day values. The - year value must be a one-, two-, or four-digit integer. If a one- - or two-digit year is used, the year is assumed to be in the - twentieth century. The month may an integer, from 1 to 12, a month - name, or a month abbreviation, where a period may optionally follow - the abbreviation. The day must be an integer from 1 to the number of - days in the month. The year, month, and day values may be separated - by periods, hyphens, forward slashes, or spaces. Extra spaces are - permitted around the delimiters. Year, month, and day values may be - given in any order as long as it is possible to distinguish the - components. If all three components are numbers that are less than - 13, then a month-day-year ordering is assumed. - - The time component consists of hour, minute, and second values - separated by colons. The hour value must be an integer between 0 - and 23 inclusively. The minute value must be an integer between 0 - and 59 inclusively. The second value may be an integer value - between 0 and 59.999 inclusively. The second value or both the - minute and second values may be omitted. The time may be followed - by am or pm in upper or lower case, in which case a 12-hour clock is - assumed. - -* If the DateTime function is invoked with a single numeric argument, - the number is assumed to be either a floating point value such as - that returned by time.time(), or a number of days after January 1, - 1901 00:00:00 UTC. - - A DateTime object is returned that represents either the GMT value - of the time.time() float represented in the local machine's - timezone, or that number of days after January 1, 1901. Note that - the number of days after 1901 need to be expressed from the - viewpoint of the local machine's timezone. A negative argument will - yield a date-time value before 1901. - -* If the function is invoked with two numeric arguments, then the - first is taken to be an integer year and the second argument is - taken to be an offset in days from the beginning of the year, in the - context of the local machine timezone. The date-time value returned - is the given offset number of days from the beginning of the given - year, represented in the timezone of the local machine. The offset - may be positive or negative. Two-digit years are assumed to be in - the twentieth century. - -* If the function is invoked with two arguments, the first a float - representing a number of seconds past the epoch in GMT (such as - those returned by time.time()) and the second a string naming a - recognized timezone, a DateTime with a value of that GMT time will - be returned, represented in the given timezone. - - >>> import time - >>> t = time.time() - - Time t represented as US/Eastern: - - >>> now_east = DateTime(t, 'US/Eastern') - - Time t represented as US/Pacific: - - >>> now_west = DateTime(t, 'US/Pacific') - - Only their representations are different: - - >>> now_east.equalTo(now_west) - True - -* If the function is invoked with three or more numeric arguments, - then the first is taken to be an integer year, the second is taken - to be an integer month, and the third is taken to be an integer day. - If the combination of values is not valid, then a DateTimeError is - raised. One- or two-digit years up to 69 are assumed to be in the - 21st century, whereas values 70-99 are assumed to be 20th century. - The fourth, fifth, and sixth arguments are floating point, positive - or negative offsets in units of hours, minutes, and days, and - default to zero if not given. An optional string may be given as - the final argument to indicate timezone (the effect of this is as if - you had taken the value of time.time() at that time on a machine in - the specified timezone). - -If a string argument passed to the DateTime constructor cannot be -parsed, it will raise SyntaxError. Invalid date, time, or -timezone components will raise a DateTimeError. - -The module function Timezones() will return a list of the timezones -recognized by the DateTime module. Recognition of timezone names is -case-insensitive. - -Instance Methods for DateTime (IDateTime interface) ---------------------------------------------------- - -Conversion and comparison methods -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* ``timeTime()`` returns the date/time as a floating-point number in - UTC, in the format used by the Python time module. Note that it is - possible to create date /time values with DateTime that have no - meaningful value to the time module, and in such cases a - DateTimeError is raised. A DateTime object's value must generally - be between Jan 1, 1970 (or your local machine epoch) and Jan 2038 to - produce a valid time.time() style value. - - >>> dt = DateTime('Mar 9, 1997 13:45:00 US/Eastern') - >>> dt.timeTime() - 857933100.0 - - >>> DateTime('2040/01/01 UTC').timeTime() - 2208988800.0 - - >>> DateTime('1900/01/01 UTC').timeTime() - -2208988800.0 - -* ``toZone(z)`` returns a DateTime with the value as the current - object, represented in the indicated timezone: - - >>> dt.toZone('UTC') - DateTime('1997/03/09 18:45:00 UTC') - - >>> dt.toZone('UTC').equalTo(dt) - True - -* ``isFuture()`` returns true if this object represents a date/time - later than the time of the call: - - >>> dt.isFuture() - False - >>> DateTime('Jan 1 3000').isFuture() # not time-machine safe! - True - -* ``isPast()`` returns true if this object represents a date/time - earlier than the time of the call: - - >>> dt.isPast() - True - >>> DateTime('Jan 1 3000').isPast() # not time-machine safe! - False - -* ``isCurrentYear()`` returns true if this object represents a - date/time that falls within the current year, in the context of this - object's timezone representation: - - >>> dt.isCurrentYear() - False - >>> DateTime().isCurrentYear() - True - -* ``isCurrentMonth()`` returns true if this object represents a - date/time that falls within the current month, in the context of - this object's timezone representation: - - >>> dt.isCurrentMonth() - False - >>> DateTime().isCurrentMonth() - True - -* ``isCurrentDay()`` returns true if this object represents a - date/time that falls within the current day, in the context of this - object's timezone representation: - - >>> dt.isCurrentDay() - False - >>> DateTime().isCurrentDay() - True - -* ``isCurrentHour()`` returns true if this object represents a - date/time that falls within the current hour, in the context of this - object's timezone representation: - - >>> dt.isCurrentHour() - False - - >>> DateTime().isCurrentHour() - True - -* ``isCurrentMinute()`` returns true if this object represents a - date/time that falls within the current minute, in the context of - this object's timezone representation: - - >>> dt.isCurrentMinute() - False - >>> DateTime().isCurrentMinute() - True - -* ``isLeapYear()`` returns true if the current year (in the context of - the object's timezone) is a leap year: - - >>> dt.isLeapYear() - False - >>> DateTime('Mar 8 2004').isLeapYear() - True - -* ``earliestTime()`` returns a new DateTime object that represents the - earliest possible time (in whole seconds) that still falls within - the current object's day, in the object's timezone context: - - >>> dt.earliestTime() - DateTime('1997/03/09 00:00:00 US/Eastern') - -* ``latestTime()`` return a new DateTime object that represents the - latest possible time (in whole seconds) that still falls within the - current object's day, in the object's timezone context - - >>> dt.latestTime() - DateTime('1997/03/09 23:59:59 US/Eastern') - -Component access -~~~~~~~~~~~~~~~~ - -* ``parts()`` returns a tuple containing the calendar year, month, - day, hour, minute second and timezone of the object - - >>> dt.parts() # doctest: +ELLIPSIS - (1997, 3, 9, 13, 45, ... 'US/Eastern') - -* ``timezone()`` returns the timezone in which the object is represented: - - >>> dt.timezone() in Timezones() - True - -* ``tzoffset()`` returns the timezone offset for the objects timezone: - - >>> dt.tzoffset() - -18000 - -* ``year()`` returns the calendar year of the object: - - >>> dt.year() - 1997 - -* ``month()`` returns the month of the object as an integer: - - >>> dt.month() - 3 - -* ``Month()`` returns the full month name: - - >>> dt.Month() - 'March' - -* ``aMonth()`` returns the abbreviated month name: - - >>> dt.aMonth() - 'Mar' - -* ``pMonth()`` returns the abbreviated (with period) month name: - - >>> dt.pMonth() - 'Mar.' - -* ``day()`` returns the integer day: - - >>> dt.day() - 9 - -* ``Day()`` returns the full name of the day of the week: - - >>> dt.Day() - 'Sunday' - -* ``dayOfYear()`` returns the day of the year, in context of the - timezone representation of the object: - - >>> dt.dayOfYear() - 68 - -* ``aDay()`` returns the abbreviated name of the day of the week: - - >>> dt.aDay() - 'Sun' - -* ``pDay()`` returns the abbreviated (with period) name of the day of - the week: - - >>> dt.pDay() - 'Sun.' - -* ``dow()`` returns the integer day of the week, where Sunday is 0: - - >>> dt.dow() - 0 - -* ``dow_1()`` returns the integer day of the week, where sunday is 1: - - >>> dt.dow_1() - 1 - -* ``h_12()`` returns the 12-hour clock representation of the hour: - - >>> dt.h_12() - 1 - -* ``h_24()`` returns the 24-hour clock representation of the hour: - - >>> dt.h_24() - 13 - -* ``ampm()`` returns the appropriate time modifier (am or pm): - - >>> dt.ampm() - 'pm' - -* ``hour()`` returns the 24-hour clock representation of the hour: - - >>> dt.hour() - 13 - -* ``minute()`` returns the minute: - - >>> dt.minute() - 45 - -* ``second()`` returns the second: - - >>> dt.second() == 0 - True - -* ``millis()`` returns the milliseconds since the epoch in GMT. - - >>> dt.millis() == 857933100000 - True - -strftime() -~~~~~~~~~~ - -See ``tests/test_datetime.py``. - -General formats from previous DateTime -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* ``Date()`` return the date string for the object: - - >>> dt.Date() - '1997/03/09' - -* ``Time()`` returns the time string for an object to the nearest - second: - - >>> dt.Time() - '13:45:00' - -* ``TimeMinutes()`` returns the time string for an object not showing - seconds: - - >>> dt.TimeMinutes() - '13:45' - -* ``AMPM()`` returns the time string for an object to the nearest second: - - >>> dt.AMPM() - '01:45:00 pm' - -* ``AMPMMinutes()`` returns the time string for an object not showing - seconds: - - >>> dt.AMPMMinutes() - '01:45 pm' - -* ``PreciseTime()`` returns the time string for the object: - - >>> dt.PreciseTime() - '13:45:00.000' - -* ``PreciseAMPM()`` returns the time string for the object: - - >>> dt.PreciseAMPM() - '01:45:00.000 pm' - -* ``yy()`` returns the calendar year as a 2 digit string - - >>> dt.yy() - '97' - -* ``mm()`` returns the month as a 2 digit string - - >>> dt.mm() - '03' - -* ``dd()`` returns the day as a 2 digit string: - - >>> dt.dd() - '09' - -* ``rfc822()`` returns the date in RFC 822 format: - - >>> dt.rfc822() - 'Sun, 09 Mar 1997 13:45:00 -0500' - -New formats -~~~~~~~~~~~ - -* ``fCommon()`` returns a string representing the object's value in - the format: March 9, 1997 1:45 pm: - - >>> dt.fCommon() - 'March 9, 1997 1:45 pm' - -* ``fCommonZ()`` returns a string representing the object's value in - the format: March 9, 1997 1:45 pm US/Eastern: - - >>> dt.fCommonZ() - 'March 9, 1997 1:45 pm US/Eastern' - -* ``aCommon()`` returns a string representing the object's value in - the format: Mar 9, 1997 1:45 pm: - - >>> dt.aCommon() - 'Mar 9, 1997 1:45 pm' - -* ``aCommonZ()`` return a string representing the object's value in - the format: Mar 9, 1997 1:45 pm US/Eastern: - - >>> dt.aCommonZ() - 'Mar 9, 1997 1:45 pm US/Eastern' - -* ``pCommon()`` returns a string representing the object's value in - the format Mar. 9, 1997 1:45 pm: - - >>> dt.pCommon() - 'Mar. 9, 1997 1:45 pm' - -* ``pCommonZ()`` returns a string representing the object's value in - the format: Mar. 9, 1997 1:45 pm US/Eastern: - - >>> dt.pCommonZ() - 'Mar. 9, 1997 1:45 pm US/Eastern' - -* ``ISO()`` returns a string with the date/time in ISO format. Note: - this is not ISO 8601-format! See the ISO8601 and HTML4 methods below - for ISO 8601-compliant output. Dates are output as: YYYY-MM-DD HH:MM:SS - - >>> dt.ISO() - '1997-03-09 13:45:00' - -* ``ISO8601()`` returns the object in ISO 8601-compatible format - containing the date, time with seconds-precision and the time zone - identifier - see http://www.w3.org/TR/NOTE-datetime. Dates are - output as: YYYY-MM-DDTHH:MM:SSTZD (T is a literal character, TZD is - Time Zone Designator, format +HH:MM or -HH:MM). - - The ``HTML4()`` method below offers the same formatting, but - converts to UTC before returning the value and sets the TZD"Z" - - >>> dt.ISO8601() - '1997-03-09T13:45:00-05:00' - - -* ``HTML4()`` returns the object in the format used in the HTML4.0 - specification, one of the standard forms in ISO8601. See - http://www.w3.org/TR/NOTE-datetime. Dates are output as: - YYYY-MM-DDTHH:MM:SSZ (T, Z are literal characters, the time is in - UTC.): - - >>> dt.HTML4() - '1997-03-09T18:45:00Z' - -* ``JulianDay()`` returns the Julian day according to - http://www.tondering.dk/claus/cal/node3.html#sec-calcjd - - >>> dt.JulianDay() - 2450517 - -* ``week()`` returns the week number according to ISO - see http://www.tondering.dk/claus/cal/node6.html#SECTION00670000000000000000 - - >>> dt.week() - 10 - -Deprecated API -~~~~~~~~~~~~~~ - -* DayOfWeek(): see Day() - -* Day_(): see pDay() - -* Mon(): see aMonth() - -* Mon_(): see pMonth - -General Services Provided by DateTime -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -DateTimes can be repr()'ed; the result will be a string indicating how -to make a DateTime object like this: - - >>> repr(dt) - "DateTime('1997/03/09 13:45:00 US/Eastern')" - -When we convert them into a string, we get a nicer string that could -actually be shown to a user: - - >>> str(dt) - '1997/03/09 13:45:00 US/Eastern' - -The hash value of a DateTime is based on the date and time and is -equal for different representations of the DateTime: - - >>> hash(dt) - 3618678 - >>> hash(dt.toZone('UTC')) - 3618678 - -DateTime objects can be compared to other DateTime objects OR floating -point numbers such as the ones which are returned by the Python time -module by using the equalTo method. Using this API, True is returned if the -object represents a date/time equal to the specified DateTime or time module -style time: - - >>> dt.equalTo(dt) - True - >>> dt.equalTo(dt.toZone('UTC')) - True - >>> dt.equalTo(dt.timeTime()) - True - >>> dt.equalTo(DateTime()) - False - -Same goes for inequalities: - - >>> dt.notEqualTo(dt) - False - >>> dt.notEqualTo(dt.toZone('UTC')) - False - >>> dt.notEqualTo(dt.timeTime()) - False - >>> dt.notEqualTo(DateTime()) - True - -Normal equality operations only work with DateTime objects and take the -timezone setting into account: - - >>> dt == dt - True - >>> dt == dt.toZone('UTC') - False - >>> dt == DateTime() - False - - >>> dt != dt - False - >>> dt != dt.toZone('UTC') - True - >>> dt != DateTime() - True - -But the other comparison operations compare the referenced moment in time and -not the representation itself: - - >>> dt > dt - False - >>> DateTime() > dt - True - >>> dt > DateTime().timeTime() - False - >>> DateTime().timeTime() > dt - True - - >>> dt.greaterThan(dt) - False - >>> DateTime().greaterThan(dt) - True - >>> dt.greaterThan(DateTime().timeTime()) - False - - >>> dt >= dt - True - >>> DateTime() >= dt - True - >>> dt >= DateTime().timeTime() - False - >>> DateTime().timeTime() >= dt - True - - >>> dt.greaterThanEqualTo(dt) - True - >>> DateTime().greaterThanEqualTo(dt) - True - >>> dt.greaterThanEqualTo(DateTime().timeTime()) - False - - >>> dt < dt - False - >>> DateTime() < dt - False - >>> dt < DateTime().timeTime() - True - >>> DateTime().timeTime() < dt - False - - >>> dt.lessThan(dt) - False - >>> DateTime().lessThan(dt) - False - >>> dt.lessThan(DateTime().timeTime()) - True - - >>> dt <= dt - True - >>> DateTime() <= dt - False - >>> dt <= DateTime().timeTime() - True - >>> DateTime().timeTime() <= dt - False - - >>> dt.lessThanEqualTo(dt) - True - >>> DateTime().lessThanEqualTo(dt) - False - >>> dt.lessThanEqualTo(DateTime().timeTime()) - True - -Numeric Services Provided by DateTime -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A DateTime may be added to a number and a number may be added to a -DateTime: - - >>> dt + 5 - DateTime('1997/03/14 13:45:00 US/Eastern') - >>> 5 + dt - DateTime('1997/03/14 13:45:00 US/Eastern') - -Two DateTimes cannot be added: - - >>> from DateTime.interfaces import DateTimeError - >>> try: - ... dt + dt - ... print('fail') - ... except DateTimeError: - ... print('ok') - ok - -Either a DateTime or a number may be subtracted from a DateTime, -however, a DateTime may not be subtracted from a number: - - >>> DateTime('1997/03/10 13:45 US/Eastern') - dt - 1.0 - >>> dt - 1 - DateTime('1997/03/08 13:45:00 US/Eastern') - >>> 1 - dt - Traceback (most recent call last): - ... - TypeError: unsupported operand type(s) for -: 'int' and 'DateTime' - -DateTimes can also be converted to integers (number of seconds since -the epoch) and floats: - - >>> int(dt) - 857933100 - >>> float(dt) - 857933100.0 diff --git a/venv/Lib/site-packages/DateTime/__init__.py b/venv/Lib/site-packages/DateTime/__init__.py deleted file mode 100644 index 4e2df6d..0000000 --- a/venv/Lib/site-packages/DateTime/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -############################################################################## -# -# Copyright (c) 2002 Zope Foundation and Contributors. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE -# -############################################################################## - -from .DateTime import DateTime -from .DateTime import Timezones - - -__all__ = ('DateTime', 'Timezones') diff --git a/venv/Lib/site-packages/DateTime/__pycache__/DateTime.cpython-310.pyc b/venv/Lib/site-packages/DateTime/__pycache__/DateTime.cpython-310.pyc deleted file mode 100644 index a724fea..0000000 Binary files a/venv/Lib/site-packages/DateTime/__pycache__/DateTime.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/DateTime/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/DateTime/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 80ba466..0000000 Binary files a/venv/Lib/site-packages/DateTime/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/DateTime/__pycache__/interfaces.cpython-310.pyc b/venv/Lib/site-packages/DateTime/__pycache__/interfaces.cpython-310.pyc deleted file mode 100644 index 75c9926..0000000 Binary files a/venv/Lib/site-packages/DateTime/__pycache__/interfaces.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/DateTime/__pycache__/pytz_support.cpython-310.pyc b/venv/Lib/site-packages/DateTime/__pycache__/pytz_support.cpython-310.pyc deleted file mode 100644 index e205ec9..0000000 Binary files a/venv/Lib/site-packages/DateTime/__pycache__/pytz_support.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/DateTime/interfaces.py b/venv/Lib/site-packages/DateTime/interfaces.py deleted file mode 100644 index 80e7707..0000000 --- a/venv/Lib/site-packages/DateTime/interfaces.py +++ /dev/null @@ -1,375 +0,0 @@ -############################################################################## -# -# Copyright (c) 2005 Zope Foundation and Contributors. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE -# -############################################################################## -from zope.interface import Interface - - -class DateTimeError(Exception): - pass - - -class SyntaxError(DateTimeError): - pass - - -class DateError(DateTimeError): - pass - - -class TimeError(DateTimeError): - pass - - -class IDateTime(Interface): - # Conversion and comparison methods - - def localZone(ltm=None): - """Returns the time zone on the given date. The time zone - can change according to daylight savings.""" - - def timeTime(): - """Return the date/time as a floating-point number in UTC, in - the format used by the Python time module. Note that it is - possible to create date/time values with DateTime that have no - meaningful value to the time module.""" - - def toZone(z): - """Return a DateTime with the value as the current object, - represented in the indicated timezone.""" - - def isFuture(): - """Return true if this object represents a date/time later - than the time of the call""" - - def isPast(): - """Return true if this object represents a date/time earlier - than the time of the call""" - - def isCurrentYear(): - """Return true if this object represents a date/time that - falls within the current year, in the context of this - object's timezone representation""" - - def isCurrentMonth(): - """Return true if this object represents a date/time that - falls within the current month, in the context of this - object's timezone representation""" - - def isCurrentDay(): - """Return true if this object represents a date/time that - falls within the current day, in the context of this object's - timezone representation""" - - def isCurrentHour(): - """Return true if this object represents a date/time that - falls within the current hour, in the context of this object's - timezone representation""" - - def isCurrentMinute(): - """Return true if this object represents a date/time that - falls within the current minute, in the context of this - object's timezone representation""" - - def isLeapYear(): - """Return true if the current year (in the context of the - object's timezone) is a leap year""" - - def earliestTime(): - """Return a new DateTime object that represents the earliest - possible time (in whole seconds) that still falls within the - current object's day, in the object's timezone context""" - - def latestTime(): - """Return a new DateTime object that represents the latest - possible time (in whole seconds) that still falls within the - current object's day, in the object's timezone context""" - - def greaterThan(t): - """Compare this DateTime object to another DateTime object OR - a floating point number such as that which is returned by the - Python time module. Returns true if the object represents a - date/time greater than the specified DateTime or time module - style time. Revised to give more correct results through - comparison of long integer milliseconds.""" - - __gt__ = greaterThan - - def greaterThanEqualTo(t): - """Compare this DateTime object to another DateTime object OR - a floating point number such as that which is returned by the - Python time module. Returns true if the object represents a - date/time greater than or equal to the specified DateTime or - time module style time. Revised to give more correct results - through comparison of long integer milliseconds.""" - - __ge__ = greaterThanEqualTo - - def equalTo(t): - """Compare this DateTime object to another DateTime object OR - a floating point number such as that which is returned by the - Python time module. Returns true if the object represents a - date/time equal to the specified DateTime or time module style - time. Revised to give more correct results through comparison - of long integer milliseconds.""" - - __eq__ = equalTo - - def notEqualTo(t): - """Compare this DateTime object to another DateTime object OR - a floating point number such as that which is returned by the - Python time module. Returns true if the object represents a - date/time not equal to the specified DateTime or time module - style time. Revised to give more correct results through - comparison of long integer milliseconds.""" - - __ne__ = notEqualTo - - def lessThan(t): - """Compare this DateTime object to another DateTime object OR - a floating point number such as that which is returned by the - Python time module. Returns true if the object represents a - date/time less than the specified DateTime or time module - style time. Revised to give more correct results through - comparison of long integer milliseconds.""" - - __lt__ = lessThan - - def lessThanEqualTo(t): - """Compare this DateTime object to another DateTime object OR - a floating point number such as that which is returned by the - Python time module. Returns true if the object represents a - date/time less than or equal to the specified DateTime or time - module style time. Revised to give more correct results - through comparison of long integer milliseconds.""" - - __le__ = lessThanEqualTo - - # Component access - - def parts(): - """Return a tuple containing the calendar year, month, day, - hour, minute second and timezone of the object""" - - def timezone(): - """Return the timezone in which the object is represented.""" - - def tzoffset(): - """Return the timezone offset for the objects timezone.""" - - def year(): - """Return the calendar year of the object""" - - def month(): - """Return the month of the object as an integer""" - - def Month(): - """Return the full month name""" - - def aMonth(): - """Return the abbreviated month name.""" - - def Mon(): - """Compatibility: see aMonth""" - - def pMonth(): - """Return the abbreviated (with period) month name.""" - - def Mon_(): - """Compatibility: see pMonth""" - - def day(): - """Return the integer day""" - - def Day(): - """Return the full name of the day of the week""" - - def DayOfWeek(): - """Compatibility: see Day""" - - def dayOfYear(): - """Return the day of the year, in context of the timezone - representation of the object""" - - def aDay(): - """Return the abbreviated name of the day of the week""" - - def pDay(): - """Return the abbreviated (with period) name of the day of the - week""" - - def Day_(): - """Compatibility: see pDay""" - - def dow(): - """Return the integer day of the week, where sunday is 0""" - - def dow_1(): - """Return the integer day of the week, where sunday is 1""" - - def h_12(): - """Return the 12-hour clock representation of the hour""" - - def h_24(): - """Return the 24-hour clock representation of the hour""" - - def ampm(): - """Return the appropriate time modifier (am or pm)""" - - def hour(): - """Return the 24-hour clock representation of the hour""" - - def minute(): - """Return the minute""" - - def second(): - """Return the second""" - - def millis(): - """Return the millisecond since the epoch in GMT.""" - - def strftime(format): - """Format the date/time using the *current timezone representation*.""" - - # General formats from previous DateTime - - def Date(): - """Return the date string for the object.""" - - def Time(): - """Return the time string for an object to the nearest second.""" - - def TimeMinutes(): - """Return the time string for an object not showing seconds.""" - - def AMPM(): - """Return the time string for an object to the nearest second.""" - - def AMPMMinutes(): - """Return the time string for an object not showing seconds.""" - - def PreciseTime(): - """Return the time string for the object.""" - - def PreciseAMPM(): - """Return the time string for the object.""" - - def yy(): - """Return calendar year as a 2 digit string""" - - def mm(): - """Return month as a 2 digit string""" - - def dd(): - """Return day as a 2 digit string""" - - def rfc822(): - """Return the date in RFC 822 format""" - - # New formats - - def fCommon(): - """Return a string representing the object's value in the - format: March 1, 1997 1:45 pm""" - - def fCommonZ(): - """Return a string representing the object's value in the - format: March 1, 1997 1:45 pm US/Eastern""" - - def aCommon(): - """Return a string representing the object's value in the - format: Mar 1, 1997 1:45 pm""" - - def aCommonZ(): - """Return a string representing the object's value in the - format: Mar 1, 1997 1:45 pm US/Eastern""" - - def pCommon(): - """Return a string representing the object's value in the - format: Mar. 1, 1997 1:45 pm""" - - def pCommonZ(): - """Return a string representing the object's value - in the format: Mar. 1, 1997 1:45 pm US/Eastern""" - - def ISO(): - """Return the object in ISO standard format. Note: this is - *not* ISO 8601-format! See the ISO8601 and HTML4 methods below - for ISO 8601-compliant output - - Dates are output as: YYYY-MM-DD HH:MM:SS - """ - - def ISO8601(): - """Return the object in ISO 8601-compatible format containing - the date, time with seconds-precision and the time zone - identifier - see http://www.w3.org/TR/NOTE-datetime - - Dates are output as: YYYY-MM-DDTHH:MM:SSTZD - T is a literal character. - TZD is Time Zone Designator, format +HH:MM or -HH:MM - - The HTML4 method below offers the same formatting, but - converts to UTC before returning the value and sets the TZD"Z" - """ - - def HTML4(): - """Return the object in the format used in the HTML4.0 - specification, one of the standard forms in ISO8601. See - http://www.w3.org/TR/NOTE-datetime - - Dates are output as: YYYY-MM-DDTHH:MM:SSZ - T, Z are literal characters. - The time is in UTC. - """ - - def JulianDay(): - """Return the Julian day according to - https://www.tondering.dk/claus/cal/julperiod.php#formula - """ - - def week(): - """Return the week number according to ISO. - - See https://www.tondering.dk/claus/cal/week.php#weekno - """ - - # Python operator and conversion API - - def __add__(other): - """A DateTime may be added to a number and a number may be - added to a DateTime; two DateTimes cannot be added.""" - - __radd__ = __add__ - - def __sub__(other): - """Either a DateTime or a number may be subtracted from a - DateTime, however, a DateTime may not be subtracted from a - number.""" - - def __repr__(): - """Convert a DateTime to a string that looks like a Python - expression.""" - - def __str__(): - """Convert a DateTime to a string.""" - - def __hash__(): - """Compute a hash value for a DateTime""" - - def __int__(): - """Convert to an integer number of seconds since the epoch (gmt)""" - - def __long__(): - """Convert to a long-int number of seconds since the epoch (gmt)""" - - def __float__(): - """Convert to floating-point number of seconds since the epoch (gmt)""" diff --git a/venv/Lib/site-packages/DateTime/pytz.txt b/venv/Lib/site-packages/DateTime/pytz.txt deleted file mode 100644 index 3a87338..0000000 --- a/venv/Lib/site-packages/DateTime/pytz.txt +++ /dev/null @@ -1,192 +0,0 @@ -Pytz Support -============ - -Allows the pytz package to be used for time zone information. The -advantage of using pytz is that it has a more complete and up to date -time zone and daylight savings time database. - -Usage ------ -You don't have to do anything special to make it work. - - >>> from DateTime import DateTime, Timezones - >>> d = DateTime('March 11, 2007 US/Eastern') - -Daylight Savings ----------------- -In 2007 daylight savings time in the US was changed. The Energy Policy -Act of 2005 mandates that DST will start on the second Sunday in March -and end on the first Sunday in November. - -In 2007, the start and stop dates are March 11 and November 4, -respectively. These dates are different from previous DST start and -stop dates. In 2006, the dates were the first Sunday in April (April -2, 2006) and the last Sunday in October (October 29, 2006). - -Let's make sure that DateTime can deal with this, since the primary -motivation to use pytz for time zone information is the fact that it -is kept up to date with daylight savings changes. - - >>> DateTime('March 11, 2007 US/Eastern').tzoffset() - -18000 - >>> DateTime('March 12, 2007 US/Eastern').tzoffset() - -14400 - >>> DateTime('November 4, 2007 US/Eastern').tzoffset() - -14400 - >>> DateTime('November 5, 2007 US/Eastern').tzoffset() - -18000 - -Let's compare this to 2006. - - >>> DateTime('April 2, 2006 US/Eastern').tzoffset() - -18000 - >>> DateTime('April 3, 2006 US/Eastern').tzoffset() - -14400 - >>> DateTime('October 29, 2006 US/Eastern').tzoffset() - -14400 - >>> DateTime('October 30, 2006 US/Eastern').tzoffset() - -18000 - -Time Zones ---------- -DateTime can use pytz's large database of time zones. Here are some -examples: - - >>> d = DateTime('Pacific/Kwajalein') - >>> d = DateTime('America/Shiprock') - >>> d = DateTime('Africa/Ouagadougou') - -Of course pytz doesn't know about everything. - - >>> from DateTime.interfaces import SyntaxError - >>> try: - ... d = DateTime('July 21, 1969 Moon/Eastern') - ... print('fail') - ... except SyntaxError: - ... print('ok') - ok - -You can still use zone names that DateTime defines that aren't part of -the pytz database. - - >>> d = DateTime('eet') - >>> d = DateTime('iceland') - -These time zones use DateTimes database. So it's preferable to use the -official time zone name. - -One trickiness is that DateTime supports some zone name -abbreviations. Some of these map to pytz names, so these abbreviations -will give you time zone date from pytz. Notable among abbreviations -that work this way are 'est', 'cst', 'mst', and 'pst'. - -Let's verify that 'est' picks up the 2007 daylight savings time changes. - - >>> DateTime('March 11, 2007 est').tzoffset() - -18000 - >>> DateTime('March 12, 2007 est').tzoffset() - -14400 - >>> DateTime('November 4, 2007 est').tzoffset() - -14400 - >>> DateTime('November 5, 2007 est').tzoffset() - -18000 - -You can get a list of time zones supported by calling the Timezones() function. - - >>> Timezones() #doctest: +ELLIPSIS - ['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', ...] - -Note that you can mess with this list without hurting things. - - >>> t = Timezones() - >>> t.remove('US/Eastern') - >>> d = DateTime('US/Eastern') - - -Internal Components -------------------- - -The following are tests of internal components. - -Cache -~~~~~ - -The DateTime class uses a new time zone cache. - - >>> from DateTime.DateTime import _TZINFO - >>> _TZINFO #doctest: +ELLIPSIS - - -The cache maps time zone names to time zone instances. - - >>> cache = _TZINFO - >>> tz = cache['GMT+730'] - >>> tz = cache['US/Mountain'] - -The cache also must provide a few attributes for use by the DateTime -class. - -The _zlst attribute is a list of supported time zone names. - - >>> cache._zlst #doctest: +ELLIPSIS - ['Africa/Abidjan'... 'Africa/Accra'... 'IDLE'... 'NZST'... 'NZT'...] - -The _zidx attribute is a list of lower-case and possibly abbreviated -time zone names that can be mapped to official zone names. - - >>> 'australia/yancowinna' in cache._zidx - True - >>> 'europe/isle_of_man' in cache._zidx - True - >>> 'gmt+0500' in cache._zidx - True - -Note that there are more items in _zidx than in _zlst since there are -multiple names for some time zones. - - >>> len(cache._zidx) > len(cache._zlst) - True - -Each entry in _zlst should also be present in _zidx in lower case form. - - >>> for name in cache._zlst: - ... if not name.lower() in cache._zidx: - ... print("Error %s not in _zidx" % name.lower()) - -The _zmap attribute maps the names in _zidx to official names in _zlst. - - >>> cache._zmap['africa/abidjan'] - 'Africa/Abidjan' - >>> cache._zmap['gmt+1'] - 'GMT+1' - >>> cache._zmap['gmt+0100'] - 'GMT+1' - >>> cache._zmap['utc'] - 'UTC' - -Let's make sure that _zmap and _zidx agree. - - >>> idx = set(cache._zidx) - >>> keys = set(cache._zmap.keys()) - >>> idx == keys - True - -Timezone objects -~~~~~~~~~~~~~~~~ -The timezone instances have only one public method info(). It returns -a tuple of (offset, is_dst, name). The method takes a timestamp, which -is used to determine dst information. - - >>> t1 = DateTime('November 4, 00:00 2007 US/Mountain').timeTime() - >>> t2 = DateTime('November 4, 02:00 2007 US/Mountain').timeTime() - >>> tz.info(t1) - (-21600, 1, 'MDT') - >>> tz.info(t2) - (-25200, 0, 'MST') - -If you don't pass any arguments to info it provides daylight savings -time information as of today. - - >>> tz.info() in ((-21600, 1, 'MDT'), (-25200, 0, 'MST')) - True - diff --git a/venv/Lib/site-packages/DateTime/pytz_support.py b/venv/Lib/site-packages/DateTime/pytz_support.py deleted file mode 100644 index 4acf324..0000000 --- a/venv/Lib/site-packages/DateTime/pytz_support.py +++ /dev/null @@ -1,269 +0,0 @@ -############################################################################## -# -# Copyright (c) 2007 Zope Foundation and Contributors. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE -# -############################################################################## - -from datetime import datetime -from datetime import timedelta - -import pytz -import pytz.reference -from pytz.tzinfo import StaticTzInfo -from pytz.tzinfo import memorized_timedelta - -from .interfaces import DateTimeError - - -EPOCH = datetime.fromtimestamp(0, tz=pytz.utc) - -_numeric_timezone_data = { - 'GMT': ('GMT', 0, 1, [], '', [(0, 0, 0)], 'GMT\000'), - 'GMT+0': ('GMT+0', 0, 1, [], '', [(0, 0, 0)], 'GMT+0000\000'), - 'GMT+1': ('GMT+1', 0, 1, [], '', [(3600, 0, 0)], 'GMT+0100\000'), - 'GMT+2': ('GMT+2', 0, 1, [], '', [(7200, 0, 0)], 'GMT+0200\000'), - 'GMT+3': ('GMT+3', 0, 1, [], '', [(10800, 0, 0)], 'GMT+0300\000'), - 'GMT+4': ('GMT+4', 0, 1, [], '', [(14400, 0, 0)], 'GMT+0400\000'), - 'GMT+5': ('GMT+5', 0, 1, [], '', [(18000, 0, 0)], 'GMT+0500\000'), - 'GMT+6': ('GMT+6', 0, 1, [], '', [(21600, 0, 0)], 'GMT+0600\000'), - 'GMT+7': ('GMT+7', 0, 1, [], '', [(25200, 0, 0)], 'GMT+0700\000'), - 'GMT+8': ('GMT+8', 0, 1, [], '', [(28800, 0, 0)], 'GMT+0800\000'), - 'GMT+9': ('GMT+9', 0, 1, [], '', [(32400, 0, 0)], 'GMT+0900\000'), - 'GMT+10': ('GMT+10', 0, 1, [], '', [(36000, 0, 0)], 'GMT+1000\000'), - 'GMT+11': ('GMT+11', 0, 1, [], '', [(39600, 0, 0)], 'GMT+1100\000'), - 'GMT+12': ('GMT+12', 0, 1, [], '', [(43200, 0, 0)], 'GMT+1200\000'), - 'GMT+13': ('GMT+13', 0, 1, [], '', [(46800, 0, 0)], 'GMT+1300\000'), - - 'GMT-1': ('GMT-1', 0, 1, [], '', [(-3600, 0, 0)], 'GMT-0100\000'), - 'GMT-2': ('GMT-2', 0, 1, [], '', [(-7200, 0, 0)], 'GMT-0200\000'), - 'GMT-3': ('GMT-3', 0, 1, [], '', [(-10800, 0, 0)], 'GMT-0300\000'), - 'GMT-4': ('GMT-4', 0, 1, [], '', [(-14400, 0, 0)], 'GMT-0400\000'), - 'GMT-5': ('GMT-5', 0, 1, [], '', [(-18000, 0, 0)], 'GMT-0500\000'), - 'GMT-6': ('GMT-6', 0, 1, [], '', [(-21600, 0, 0)], 'GMT-0600\000'), - 'GMT-7': ('GMT-7', 0, 1, [], '', [(-25200, 0, 0)], 'GMT-0700\000'), - 'GMT-8': ('GMT-8', 0, 1, [], '', [(-28800, 0, 0)], 'GMT-0800\000'), - 'GMT-9': ('GMT-9', 0, 1, [], '', [(-32400, 0, 0)], 'GMT-0900\000'), - 'GMT-10': ('GMT-10', 0, 1, [], '', [(-36000, 0, 0)], 'GMT-1000\000'), - 'GMT-11': ('GMT-11', 0, 1, [], '', [(-39600, 0, 0)], 'GMT-1100\000'), - 'GMT-12': ('GMT-12', 0, 1, [], '', [(-43200, 0, 0)], 'GMT-1200\000'), - - 'GMT+0130': ('GMT+0130', 0, 1, [], '', [(5400, 0, 0)], 'GMT+0130\000'), - 'GMT+0230': ('GMT+0230', 0, 1, [], '', [(9000, 0, 0)], 'GMT+0230\000'), - 'GMT+0330': ('GMT+0330', 0, 1, [], '', [(12600, 0, 0)], 'GMT+0330\000'), - 'GMT+0430': ('GMT+0430', 0, 1, [], '', [(16200, 0, 0)], 'GMT+0430\000'), - 'GMT+0530': ('GMT+0530', 0, 1, [], '', [(19800, 0, 0)], 'GMT+0530\000'), - 'GMT+0630': ('GMT+0630', 0, 1, [], '', [(23400, 0, 0)], 'GMT+0630\000'), - 'GMT+0730': ('GMT+0730', 0, 1, [], '', [(27000, 0, 0)], 'GMT+0730\000'), - 'GMT+0830': ('GMT+0830', 0, 1, [], '', [(30600, 0, 0)], 'GMT+0830\000'), - 'GMT+0930': ('GMT+0930', 0, 1, [], '', [(34200, 0, 0)], 'GMT+0930\000'), - 'GMT+1030': ('GMT+1030', 0, 1, [], '', [(37800, 0, 0)], 'GMT+1030\000'), - 'GMT+1130': ('GMT+1130', 0, 1, [], '', [(41400, 0, 0)], 'GMT+1130\000'), - 'GMT+1230': ('GMT+1230', 0, 1, [], '', [(45000, 0, 0)], 'GMT+1230\000'), - - 'GMT-0130': ('GMT-0130', 0, 1, [], '', [(-5400, 0, 0)], 'GMT-0130\000'), - 'GMT-0230': ('GMT-0230', 0, 1, [], '', [(-9000, 0, 0)], 'GMT-0230\000'), - 'GMT-0330': ('GMT-0330', 0, 1, [], '', [(-12600, 0, 0)], 'GMT-0330\000'), - 'GMT-0430': ('GMT-0430', 0, 1, [], '', [(-16200, 0, 0)], 'GMT-0430\000'), - 'GMT-0530': ('GMT-0530', 0, 1, [], '', [(-19800, 0, 0)], 'GMT-0530\000'), - 'GMT-0630': ('GMT-0630', 0, 1, [], '', [(-23400, 0, 0)], 'GMT-0630\000'), - 'GMT-0730': ('GMT-0730', 0, 1, [], '', [(-27000, 0, 0)], 'GMT-0730\000'), - 'GMT-0830': ('GMT-0830', 0, 1, [], '', [(-30600, 0, 0)], 'GMT-0830\000'), - 'GMT-0930': ('GMT-0930', 0, 1, [], '', [(-34200, 0, 0)], 'GMT-0930\000'), - 'GMT-1030': ('GMT-1030', 0, 1, [], '', [(-37800, 0, 0)], 'GMT-1030\000'), - 'GMT-1130': ('GMT-1130', 0, 1, [], '', [(-41400, 0, 0)], 'GMT-1130\000'), - 'GMT-1230': ('GMT-1230', 0, 1, [], '', [(-45000, 0, 0)], 'GMT-1230\000'), -} - -# These are the timezones not in pytz.common_timezones -_old_zlst = [ - 'AST', 'AT', 'BST', 'BT', 'CCT', - 'CET', 'CST', 'Cuba', 'EADT', 'EAST', - 'EEST', 'EET', 'EST', 'Egypt', 'FST', - 'FWT', 'GB-Eire', 'GMT+0100', 'GMT+0130', 'GMT+0200', - 'GMT+0230', 'GMT+0300', 'GMT+0330', 'GMT+0400', 'GMT+0430', - 'GMT+0500', 'GMT+0530', 'GMT+0600', 'GMT+0630', 'GMT+0700', - 'GMT+0730', 'GMT+0800', 'GMT+0830', 'GMT+0900', 'GMT+0930', - 'GMT+1', 'GMT+1000', 'GMT+1030', 'GMT+1100', 'GMT+1130', - 'GMT+1200', 'GMT+1230', 'GMT+1300', 'GMT-0100', 'GMT-0130', - 'GMT-0200', 'GMT-0300', 'GMT-0400', 'GMT-0500', 'GMT-0600', - 'GMT-0630', 'GMT-0700', 'GMT-0730', 'GMT-0800', 'GMT-0830', - 'GMT-0900', 'GMT-0930', 'GMT-1000', 'GMT-1030', 'GMT-1100', - 'GMT-1130', 'GMT-1200', 'GMT-1230', 'GST', 'Greenwich', - 'Hongkong', 'IDLE', 'IDLW', 'Iceland', 'Iran', - 'Israel', 'JST', 'Jamaica', 'Japan', 'MEST', - 'MET', 'MEWT', 'MST', 'NT', 'NZDT', - 'NZST', 'NZT', 'PST', 'Poland', 'SST', - 'SWT', 'Singapore', 'Turkey', 'UCT', 'UT', - 'Universal', 'WADT', 'WAST', 'WAT', 'WET', - 'ZP4', 'ZP5', 'ZP6', -] - -_old_zmap = { - 'aest': 'GMT+10', 'aedt': 'GMT+11', - 'aus eastern standard time': 'GMT+10', - 'sydney standard time': 'GMT+10', - 'tasmania standard time': 'GMT+10', - 'e. australia standard time': 'GMT+10', - 'aus central standard time': 'GMT+0930', - 'cen. australia standard time': 'GMT+0930', - 'w. australia standard time': 'GMT+8', - - 'central europe standard time': 'GMT+1', - 'eastern standard time': 'US/Eastern', - 'us eastern standard time': 'US/Eastern', - 'central standard time': 'US/Central', - 'mountain standard time': 'US/Mountain', - 'pacific standard time': 'US/Pacific', - 'mst': 'US/Mountain', 'pst': 'US/Pacific', - 'cst': 'US/Central', 'est': 'US/Eastern', - - 'gmt+0000': 'GMT+0', 'gmt+0': 'GMT+0', - - 'gmt+0100': 'GMT+1', 'gmt+0200': 'GMT+2', 'gmt+0300': 'GMT+3', - 'gmt+0400': 'GMT+4', 'gmt+0500': 'GMT+5', 'gmt+0600': 'GMT+6', - 'gmt+0700': 'GMT+7', 'gmt+0800': 'GMT+8', 'gmt+0900': 'GMT+9', - 'gmt+1000': 'GMT+10', 'gmt+1100': 'GMT+11', 'gmt+1200': 'GMT+12', - 'gmt+1300': 'GMT+13', - 'gmt-0100': 'GMT-1', 'gmt-0200': 'GMT-2', 'gmt-0300': 'GMT-3', - 'gmt-0400': 'GMT-4', 'gmt-0500': 'GMT-5', 'gmt-0600': 'GMT-6', - 'gmt-0700': 'GMT-7', 'gmt-0800': 'GMT-8', 'gmt-0900': 'GMT-9', - 'gmt-1000': 'GMT-10', 'gmt-1100': 'GMT-11', 'gmt-1200': 'GMT-12', - - 'gmt+1': 'GMT+1', 'gmt+2': 'GMT+2', 'gmt+3': 'GMT+3', - 'gmt+4': 'GMT+4', 'gmt+5': 'GMT+5', 'gmt+6': 'GMT+6', - 'gmt+7': 'GMT+7', 'gmt+8': 'GMT+8', 'gmt+9': 'GMT+9', - 'gmt+10': 'GMT+10', 'gmt+11': 'GMT+11', 'gmt+12': 'GMT+12', - 'gmt+13': 'GMT+13', - 'gmt-1': 'GMT-1', 'gmt-2': 'GMT-2', 'gmt-3': 'GMT-3', - 'gmt-4': 'GMT-4', 'gmt-5': 'GMT-5', 'gmt-6': 'GMT-6', - 'gmt-7': 'GMT-7', 'gmt-8': 'GMT-8', 'gmt-9': 'GMT-9', - 'gmt-10': 'GMT-10', 'gmt-11': 'GMT-11', 'gmt-12': 'GMT-12', - - 'gmt+130': 'GMT+0130', 'gmt+0130': 'GMT+0130', - 'gmt+230': 'GMT+0230', 'gmt+0230': 'GMT+0230', - 'gmt+330': 'GMT+0330', 'gmt+0330': 'GMT+0330', - 'gmt+430': 'GMT+0430', 'gmt+0430': 'GMT+0430', - 'gmt+530': 'GMT+0530', 'gmt+0530': 'GMT+0530', - 'gmt+630': 'GMT+0630', 'gmt+0630': 'GMT+0630', - 'gmt+730': 'GMT+0730', 'gmt+0730': 'GMT+0730', - 'gmt+830': 'GMT+0830', 'gmt+0830': 'GMT+0830', - 'gmt+930': 'GMT+0930', 'gmt+0930': 'GMT+0930', - 'gmt+1030': 'GMT+1030', - 'gmt+1130': 'GMT+1130', - 'gmt+1230': 'GMT+1230', - - 'gmt-130': 'GMT-0130', 'gmt-0130': 'GMT-0130', - 'gmt-230': 'GMT-0230', 'gmt-0230': 'GMT-0230', - 'gmt-330': 'GMT-0330', 'gmt-0330': 'GMT-0330', - 'gmt-430': 'GMT-0430', 'gmt-0430': 'GMT-0430', - 'gmt-530': 'GMT-0530', 'gmt-0530': 'GMT-0530', - 'gmt-630': 'GMT-0630', 'gmt-0630': 'GMT-0630', - 'gmt-730': 'GMT-0730', 'gmt-0730': 'GMT-0730', - 'gmt-830': 'GMT-0830', 'gmt-0830': 'GMT-0830', - 'gmt-930': 'GMT-0930', 'gmt-0930': 'GMT-0930', - 'gmt-1030': 'GMT-1030', - 'gmt-1130': 'GMT-1130', - 'gmt-1230': 'GMT-1230', - - 'ut': 'Universal', - 'bst': 'GMT+1', 'mest': 'GMT+2', 'sst': 'GMT+2', - 'fst': 'GMT+2', 'wadt': 'GMT+8', 'eadt': 'GMT+11', 'nzdt': 'GMT+13', - 'wet': 'GMT', 'wat': 'GMT+1', 'at': 'GMT-2', 'ast': 'GMT-4', - 'nt': 'GMT-11', 'idlw': 'GMT-12', 'cet': 'GMT+1', 'cest': 'GMT+2', - 'met': 'GMT+1', - 'mewt': 'GMT+1', 'swt': 'GMT+1', 'fwt': 'GMT+1', 'eet': 'GMT+2', - 'eest': 'GMT+3', - 'bt': 'GMT+3', 'zp4': 'GMT+4', 'zp5': 'GMT+5', 'zp6': 'GMT+6', - 'wast': 'GMT+7', 'cct': 'GMT+8', 'jst': 'GMT+9', 'east': 'GMT+10', - 'gst': 'GMT+10', 'nzt': 'GMT+12', 'nzst': 'GMT+12', 'idle': 'GMT+12', - 'ret': 'GMT+4', 'ist': 'GMT+0530', 'edt': 'GMT-4', - -} - - -# some timezone definitions of the "-0400" are not working -# when upgrading -for hour in range(0, 13): - hour = hour - fhour = str(hour) - if len(fhour) == 1: - fhour = '0' + fhour - _old_zmap['-%s00' % fhour] = 'GMT-%i' % hour - _old_zmap['+%s00' % fhour] = 'GMT+%i' % hour - - -def _p(zone): - return _numeric_timezones[zone] - - -def _static_timezone_factory(data): - zone = data[0] - cls = type(zone, (StaticTzInfo,), dict( - __reduce__=lambda _: (_p, (zone, )), - zone=zone, - _utcoffset=memorized_timedelta(data[5][0][0]), - _tzname=data[6][:-1])) # strip the trailing null - return cls() - - -_numeric_timezones = {key: _static_timezone_factory(data) - for key, data in _numeric_timezone_data.items()} - - -class Timezone: - """ - Timezone information returned by PytzCache.__getitem__ - Adapts datetime.tzinfo object to DateTime._timezone interface - """ - - def __init__(self, tzinfo): - self.tzinfo = tzinfo - - def info(self, t=None): - if t is None: - dt = datetime.now(tz=pytz.utc) - else: - # can't use utcfromtimestamp past 2038 - dt = EPOCH + timedelta(0, t) - - # need to normalize tzinfo for the datetime to deal with - # daylight savings time. - normalized_dt = self.tzinfo.normalize(dt.astimezone(self.tzinfo)) - normalized_tzinfo = normalized_dt.tzinfo - - offset = normalized_tzinfo.utcoffset(normalized_dt) - secs = offset.days * 24 * 60 * 60 + offset.seconds - dst = normalized_tzinfo.dst(normalized_dt) - if dst == timedelta(0): - is_dst = 0 - else: - is_dst = 1 - return secs, is_dst, normalized_tzinfo.tzname(normalized_dt) - - -class PytzCache: - """ - Reimplementation of the DateTime._cache class that uses for timezone info - """ - - _zlst = pytz.common_timezones + _old_zlst # used by DateTime.TimeZones - _zmap = {name.lower(): name for name in pytz.all_timezones} - _zmap.update(_old_zmap) # These must take priority - _zidx = _zmap.keys() - - def __getitem__(self, key): - name = self._zmap.get(key.lower(), key) # fallback to key - try: - return Timezone(pytz.timezone(name)) - except pytz.UnknownTimeZoneError: - try: - return Timezone(_numeric_timezones[name]) - except KeyError: - raise DateTimeError('Unrecognized timezone: %s' % key) diff --git a/venv/Lib/site-packages/DateTime/tests/__init__.py b/venv/Lib/site-packages/DateTime/tests/__init__.py deleted file mode 100644 index e67bcb6..0000000 --- a/venv/Lib/site-packages/DateTime/tests/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## - -# This file is needed to make this a package. diff --git a/venv/Lib/site-packages/DateTime/tests/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/DateTime/tests/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index ea39a6a..0000000 Binary files a/venv/Lib/site-packages/DateTime/tests/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/DateTime/tests/__pycache__/test_datetime.cpython-310.pyc b/venv/Lib/site-packages/DateTime/tests/__pycache__/test_datetime.cpython-310.pyc deleted file mode 100644 index 13b6fa9..0000000 Binary files a/venv/Lib/site-packages/DateTime/tests/__pycache__/test_datetime.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/DateTime/tests/julian_testdata.txt b/venv/Lib/site-packages/DateTime/tests/julian_testdata.txt deleted file mode 100644 index 386c3da..0000000 --- a/venv/Lib/site-packages/DateTime/tests/julian_testdata.txt +++ /dev/null @@ -1,57 +0,0 @@ -1970-01-01 (1970, 1, 4) -1970-01-02 (1970, 1, 5) -1970-01-30 (1970, 5, 5) -1970-01-31 (1970, 5, 6) -1970-02-01 (1970, 5, 7) -1970-02-02 (1970, 6, 1) -1970-02-28 (1970, 9, 6) -1970-03-01 (1970, 9, 7) -1970-03-30 (1970, 14, 1) -1970-03-31 (1970, 14, 2) -1970-04-01 (1970, 14, 3) -1970-09-30 (1970, 40, 3) -1970-10-01 (1970, 40, 4) -1970-10-02 (1970, 40, 5) -1970-10-03 (1970, 40, 6) -1970-10-04 (1970, 40, 7) -1970-10-05 (1970, 41, 1) -1971-01-02 (1970, 53, 6) -1971-01-03 (1970, 53, 7) -1971-01-04 (1971, 1, 1) -1971-01-05 (1971, 1, 2) -1971-12-31 (1971, 52, 5) -1972-01-01 (1971, 52, 6) -1972-01-02 (1971, 52, 7) -1972-01-03 (1972, 1, 1) -1972-01-04 (1972, 1, 2) -1972-12-30 (1972, 52, 6) -1972-12-31 (1972, 52, 7) -1973-01-01 (1973, 1, 1) -1973-01-02 (1973, 1, 2) -1973-12-29 (1973, 52, 6) -1973-12-30 (1973, 52, 7) -1973-12-31 (1974, 1, 1) -1974-01-01 (1974, 1, 2) -1998-12-30 (1998, 53, 3) -1998-12-31 (1998, 53, 4) -1999-01-01 (1998, 53, 5) -1999-01-02 (1998, 53, 6) -1999-01-03 (1998, 53, 7) -1999-01-04 (1999, 1, 1) -1999-01-05 (1999, 1, 2) -1999-12-30 (1999, 52, 4) -1999-12-31 (1999, 52, 5) -2000-01-01 (1999, 52, 6) -2000-01-02 (1999, 52, 7) -2000-01-03 (2000, 1, 1) -2000-01-04 (2000, 1, 2) -2000-01-05 (2000, 1, 3) -2000-01-06 (2000, 1, 4) -2000-01-07 (2000, 1, 5) -2000-01-08 (2000, 1, 6) -2000-01-09 (2000, 1, 7) -2000-01-10 (2000, 2, 1) -2019-12-28 (2019, 52, 6) -2019-12-29 (2019, 52, 7) -2019-12-30 (2020, 1, 1) -2019-12-31 (2020, 1, 2) diff --git a/venv/Lib/site-packages/DateTime/tests/test_datetime.py b/venv/Lib/site-packages/DateTime/tests/test_datetime.py deleted file mode 100644 index f4c2644..0000000 --- a/venv/Lib/site-packages/DateTime/tests/test_datetime.py +++ /dev/null @@ -1,764 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## - -import math -import os -import pickle -import platform -import sys -import time -import unittest -from datetime import date -from datetime import datetime -from datetime import timedelta -from datetime import tzinfo - -import pytz - -from DateTime import DateTime -from DateTime.DateTime import _findLocalTimeZoneName - - -try: - __file__ -except NameError: # pragma: no cover - f = sys.argv[0] -else: - f = __file__ - -IS_PYPY = getattr(platform, 'python_implementation', lambda: None)() == 'PyPy' - -DATADIR = os.path.dirname(os.path.abspath(f)) -del f - -ZERO = timedelta(0) - - -class FixedOffset(tzinfo): - """Fixed offset in minutes east from UTC.""" - - def __init__(self, offset, name): - self.__offset = timedelta(minutes=offset) - self.__name = name - - def utcoffset(self, dt): - return self.__offset - - def tzname(self, dt): - return self.__name - - def dst(self, dt): - return ZERO - - -class DateTimeTests(unittest.TestCase): - - def _compare(self, dt1, dt2): - '''Compares the internal representation of dt1 with - the representation in dt2. Allows sub-millisecond variations. - Primarily for testing.''' - self.assertEqual(round(dt1._t, 3), round(dt2._t, 3)) - self.assertEqual(round(dt1._d, 9), round(dt2._d, 9)) - self.assertEqual(round(dt1.time, 9), round(dt2.time, 9)) - self.assertEqual(dt1.millis(), dt2.millis()) - self.assertEqual(dt1._micros, dt2._micros) - - def testBug1203(self): - # 01:59:60 occurred in old DateTime - dt = DateTime(7200, 'GMT') - self.assertTrue(str(dt).find('60') < 0, dt) - - def testDSTInEffect(self): - # Checks GMT offset for a DST date in the US/Eastern time zone - dt = DateTime(2000, 5, 9, 15, 0, 0, 'US/Eastern') - self.assertEqual(dt.toZone('GMT').hour(), 19, - (dt, dt.toZone('GMT'))) - - def testDSTNotInEffect(self): - # Checks GMT offset for a non-DST date in the US/Eastern time zone - dt = DateTime(2000, 11, 9, 15, 0, 0, 'US/Eastern') - self.assertEqual(dt.toZone('GMT').hour(), 20, - (dt, dt.toZone('GMT'))) - - def testAddPrecision(self): - # Precision of serial additions - dt = DateTime() - self.assertEqual(str(dt + 0.10 + 3.14 + 6.76 - 10), str(dt), - dt) - # checks problem reported in - # https://github.com/zopefoundation/DateTime/issues/41 - dt = DateTime(2038, 10, 7, 8, 52, 44.959840, "UTC") - self.assertEqual(str(dt + 0.10 + 3.14 + 6.76 - 10), str(dt), - dt) - - def testConsistentSecondMicroRounding(self): - dt = DateTime(2038, 10, 7, 8, 52, 44.9598398, "UTC") - self.assertEqual(int(dt.second() * 1000000), - dt.micros() % 60000000) - - def testConstructor3(self): - # Constructor from date/time string - dt = DateTime() - dt1s = '%d/%d/%d %d:%d:%f %s' % ( - dt.year(), - dt.month(), - dt.day(), - dt.hour(), - dt.minute(), - dt.second(), - dt.timezone()) - dt1 = DateTime(dt1s) - # Compare representations as it's the - # only way to compare the dates to the same accuracy - self.assertEqual(repr(dt), repr(dt1)) - - def testConstructor4(self): - # Constructor from time float - dt = DateTime() - dt1 = DateTime(float(dt)) - self._compare(dt, dt1) - - def testConstructor5(self): - # Constructor from time float and timezone - dt = DateTime() - dt1 = DateTime(float(dt), dt.timezone()) - self.assertEqual(str(dt), str(dt1), (dt, dt1)) - dt1 = DateTime(float(dt), str(dt.timezone())) - self.assertEqual(str(dt), str(dt1), (dt, dt1)) - - def testConstructor6(self): - # Constructor from year and julian date - # This test must normalize the time zone, or it *will* break when - # DST changes! - dt1 = DateTime(2000, 5.500000578705) - dt = DateTime('2000/1/5 12:00:00.050 pm %s' % dt1.localZone()) - self._compare(dt, dt1) - - def testConstructor7(self): - # Constructor from parts - dt = DateTime() - dt1 = DateTime( - dt.year(), - dt.month(), - dt.day(), - dt.hour(), - dt.minute(), - dt.second(), - dt.timezone()) - # Compare representations as it's the - # only way to compare the dates to the same accuracy - self.assertEqual(repr(dt), repr(dt1)) - - def testDayOfWeek(self): - # Compare to the datetime.date value to make it locale independent - expected = date(2000, 6, 16).strftime('%A') - # strftime() used to always be passed a day of week of 0 - dt = DateTime('2000/6/16') - s = dt.strftime('%A') - self.assertEqual(s, expected, (dt, s)) - - def testOldDate(self): - # Fails when an 1800 date is displayed with negative signs - dt = DateTime('1830/5/6 12:31:46.213 pm') - dt1 = dt.toZone('GMT+6') - self.assertTrue(str(dt1).find('-') < 0, (dt, dt1)) - - def testSubtraction(self): - # Reconstruction of a DateTime from its parts, with subtraction - # this also tests the accuracy of addition and reconstruction - dt = DateTime() - dt1 = dt - 3.141592653 - dt2 = DateTime( - dt.year(), - dt.month(), - dt.day(), - dt.hour(), - dt.minute(), - dt.second()) - dt3 = dt2 - 3.141592653 - self.assertEqual(dt1, dt3, (dt, dt1, dt2, dt3)) - - def testTZ1add(self): - # Time zone manipulation: add to a date - dt = DateTime('1997/3/8 1:45am GMT-4') - dt1 = DateTime('1997/3/9 1:45pm GMT+8') - self.assertTrue((dt + 1.0).equalTo(dt1)) - - def testTZ1sub(self): - # Time zone manipulation: subtract from a date - dt = DateTime('1997/3/8 1:45am GMT-4') - dt1 = DateTime('1997/3/9 1:45pm GMT+8') - self.assertTrue((dt1 - 1.0).equalTo(dt)) - - def testTZ1diff(self): - # Time zone manipulation: diff two dates - dt = DateTime('1997/3/8 1:45am GMT-4') - dt1 = DateTime('1997/3/9 1:45pm GMT+8') - self.assertEqual(dt1 - dt, 1.0, (dt, dt1)) - - def test_compare_methods(self): - # Compare two dates using several methods - dt = DateTime('1997/1/1') - dt1 = DateTime('1997/2/2') - self.assertTrue(dt1.greaterThan(dt)) - self.assertTrue(dt1.greaterThanEqualTo(dt)) - self.assertTrue(dt.lessThan(dt1)) - self.assertTrue(dt.lessThanEqualTo(dt1)) - self.assertTrue(dt.notEqualTo(dt1)) - self.assertFalse(dt.equalTo(dt1)) - # Compare a date to float - dt = DateTime(1.0) - self.assertTrue(dt == DateTime(1.0)) # testing __eq__ - self.assertFalse(dt != DateTime(1.0)) # testing __ne__ - self.assertFalse(dt.greaterThan(1.0)) - self.assertTrue(dt.greaterThanEqualTo(1.0)) - self.assertFalse(dt.lessThan(1.0)) - self.assertTrue(dt.lessThanEqualTo(1.0)) - self.assertFalse(dt.notEqualTo(1.0)) - self.assertTrue(dt.equalTo(1.0)) - # Compare a date to int - dt = DateTime(1) - self.assertEqual(dt, DateTime(1.0)) - self.assertTrue(dt == DateTime(1)) # testing __eq__ - self.assertFalse(dt != DateTime(1)) # testing __ne__ - self.assertFalse(dt.greaterThan(1)) - self.assertTrue(dt.greaterThanEqualTo(1)) - self.assertFalse(dt.lessThan(1)) - self.assertTrue(dt.lessThanEqualTo(1)) - self.assertFalse(dt.notEqualTo(1)) - self.assertTrue(dt.equalTo(1)) - # Compare a date to string; there is no implicit type conversion - # but behavior if consistent as when comparing, for example, an int - # and a string. - dt = DateTime("2023") - self.assertFalse(dt == "2023") # testing __eq__ - self.assertTrue(dt != "2023") # testing __ne__ - self.assertRaises(TypeError, dt.greaterThan, "2023") - self.assertRaises(TypeError, dt.greaterThanEqualTo, "2023") - self.assertRaises(TypeError, dt.lessThan, "2023") - self.assertRaises(TypeError, dt.lessThanEqualTo, "2023") - self.assertTrue(dt.notEqualTo("2023")) - self.assertFalse(dt.equalTo("2023")) - - def test_compare_methods_none(self): - # Compare a date to None - for dt in (DateTime('1997/1/1'), DateTime(0)): - self.assertTrue(dt.greaterThan(None)) - self.assertTrue(dt.greaterThanEqualTo(None)) - self.assertFalse(dt.lessThan(None)) - self.assertFalse(dt.lessThanEqualTo(None)) - self.assertTrue(dt.notEqualTo(None)) - self.assertFalse(dt.equalTo(None)) - - def test_pickle(self): - dt = DateTime() - data = pickle.dumps(dt, 1) - new = pickle.loads(data) - for key in DateTime.__slots__: - self.assertEqual(getattr(dt, key), getattr(new, key)) - - def test_pickle_with_tz(self): - dt = DateTime('2002/5/2 8:00am GMT+8') - data = pickle.dumps(dt, 1) - new = pickle.loads(data) - for key in DateTime.__slots__: - self.assertEqual(getattr(dt, key), getattr(new, key)) - - def test_pickle_asdatetime_with_tz(self): - dt = DateTime('2002/5/2 8:00am GMT+8') - data = pickle.dumps(dt.asdatetime(), 1) - new = DateTime(pickle.loads(data)) - for key in DateTime.__slots__: - self.assertEqual(getattr(dt, key), getattr(new, key)) - - def test_pickle_with_numerical_tz(self): - for dt_str in ('2007/01/02 12:34:56.789 +0300', - '2007/01/02 12:34:56.789 +0430', - '2007/01/02 12:34:56.789 -1234'): - dt = DateTime(dt_str) - data = pickle.dumps(dt, 1) - new = pickle.loads(data) - for key in DateTime.__slots__: - self.assertEqual(getattr(dt, key), getattr(new, key)) - - def test_pickle_with_micros(self): - dt = DateTime('2002/5/2 8:00:14.123 GMT+8') - data = pickle.dumps(dt, 1) - new = pickle.loads(data) - for key in DateTime.__slots__: - self.assertEqual(getattr(dt, key), getattr(new, key)) - - def test_pickle_old(self): - dt = DateTime('2002/5/2 8:00am GMT+0') - data = ( - '(cDateTime.DateTime\nDateTime\nq\x01Noq\x02}q\x03(U\x05' - '_amonq\x04U\x03Mayq\x05U\x05_adayq\x06U\x03Thuq\x07U\x05_pmonq' - '\x08h\x05U\x05_hourq\tK\x08U\x05_fmonq\nh\x05U\x05_pdayq\x0bU' - '\x04Thu.q\x0cU\x05_fdayq\rU\x08Thursdayq\x0eU\x03_pmq\x0fU\x02amq' - '\x10U\x02_tq\x11GA\xcehy\x00\x00\x00\x00U\x07_minuteq\x12K\x00U' - '\x07_microsq\x13L1020326400000000L\nU\x02_dq\x14G@\xe2\x12j\xaa' - '\xaa\xaa\xabU\x07_secondq\x15G\x00\x00\x00\x00\x00\x00\x00\x00U' - '\x03_tzq\x16U\x05GMT+0q\x17U\x06_monthq\x18K\x05U' - '\x0f_timezone_naiveq\x19I00\nU\x04_dayq\x1aK\x02U\x05_yearq' - '\x1bM\xd2\x07U\x08_nearsecq\x1cG\x00\x00\x00\x00\x00\x00\x00' - '\x00U\x07_pmhourq\x1dK\x08U\n_dayoffsetq\x1eK\x04U\x04timeq' - '\x1fG?\xd5UUUV\x00\x00ub.') - data = data.encode('latin-1') - new = pickle.loads(data) - for key in DateTime.__slots__: - self.assertEqual(getattr(dt, key), getattr(new, key)) - - def test_pickle_old_without_micros(self): - dt = DateTime('2002/5/2 8:00am GMT+0') - data = ( - '(cDateTime.DateTime\nDateTime\nq\x01Noq\x02}q\x03(U\x05' - '_amonq\x04U\x03Mayq\x05U\x05_adayq\x06U\x03Thuq\x07U\x05_pmonq' - '\x08h\x05U\x05_hourq\tK\x08U\x05_fmonq\nh\x05U\x05_pdayq\x0bU' - '\x04Thu.q\x0cU\x05_fdayq\rU\x08Thursdayq\x0eU\x03_pmq\x0fU' - '\x02amq\x10U\x02_tq\x11GA\xcehy\x00\x00\x00\x00U\x07_minuteq' - '\x12K\x00U\x02_dq\x13G@\xe2\x12j\xaa\xaa\xaa\xabU\x07_secondq' - '\x14G\x00\x00\x00\x00\x00\x00\x00\x00U\x03_tzq\x15U\x05GMT+0q' - '\x16U\x06_monthq\x17K\x05U\x0f_timezone_naiveq\x18I00\nU' - '\x04_dayq\x19K\x02U\x05_yearq\x1aM\xd2\x07U\x08_nearsecq' - '\x1bG\x00\x00\x00\x00\x00\x00\x00\x00U\x07_pmhourq\x1cK\x08U' - '\n_dayoffsetq\x1dK\x04U\x04timeq\x1eG?\xd5UUUV\x00\x00ub.') - data = data.encode('latin-1') - new = pickle.loads(data) - for key in DateTime.__slots__: - self.assertEqual(getattr(dt, key), getattr(new, key)) - - def test_pickle_dates_after_2038(self): - dt = DateTime('2039/09/02 07:07:6.235027 GMT+1') - data = pickle.dumps(dt, 1) - new = pickle.loads(data) - for key in DateTime.__slots__: - self.assertEqual(getattr(dt, key), getattr(new, key)) - - def test_pickle_old_with_micros_as_float(self): - dt = DateTime('2002/5/2 8:00am GMT+0') - data = ( - 'ccopy_reg\n_reconstructor\nq\x00(cDateTime.DateTime\nDateTime' - '\nq\x01c__builtin__\nobject\nq\x02Ntq\x03Rq\x04(GA\xcehy\x00\x00' - '\x00\x00I00\nX\x05\x00\x00\x00GMT+0q\x05tq\x06b.') - data = data.encode('latin-1') - new = pickle.loads(data) - for key in DateTime.__slots__: - self.assertEqual(getattr(dt, key), getattr(new, key)) - - def testTZ2(self): - # Time zone manipulation test 2 - dt = DateTime() - dt1 = dt.toZone('GMT') - s = dt.second() - s1 = dt1.second() - self.assertEqual(s, s1, (dt, dt1, s, s1)) - - def testTZDiffDaylight(self): - # Diff dates across daylight savings dates - dt = DateTime('2000/6/8 1:45am US/Eastern') - dt1 = DateTime('2000/12/8 12:45am US/Eastern') - self.assertEqual(dt1 - dt, 183, (dt, dt1, dt1 - dt)) - - def testY10KDate(self): - # Comparison of a Y10K date and a Y2K date - dt = DateTime('10213/09/21') - dt1 = DateTime(2000, 1, 1) - - dsec = (dt.millis() - dt1.millis()) / 1000.0 - ddays = math.floor((dsec / 86400.0) + 0.5) - - self.assertEqual(ddays, 3000000, ddays) - - def test_tzoffset(self): - # Test time-zone given as an offset - - # GMT - dt = DateTime('Tue, 10 Sep 2001 09:41:03 GMT') - self.assertEqual(dt.tzoffset(), 0) - - # Timezone by name, a timezone that hasn't got daylightsaving. - dt = DateTime('Tue, 2 Mar 2001 09:41:03 GMT+3') - self.assertEqual(dt.tzoffset(), 10800) - - # Timezone by name, has daylightsaving but is not in effect. - dt = DateTime('Tue, 21 Jan 2001 09:41:03 PST') - self.assertEqual(dt.tzoffset(), -28800) - - # Timezone by name, with daylightsaving in effect - dt = DateTime('Tue, 24 Aug 2001 09:41:03 PST') - self.assertEqual(dt.tzoffset(), -25200) - - # A negative numerical timezone - dt = DateTime('Tue, 24 Jul 2001 09:41:03 -0400') - self.assertEqual(dt.tzoffset(), -14400) - - # A positive numerical timzone - dt = DateTime('Tue, 6 Dec 1966 01:41:03 +0200') - self.assertEqual(dt.tzoffset(), 7200) - - # A negative numerical timezone with minutes. - dt = DateTime('Tue, 24 Jul 2001 09:41:03 -0637') - self.assertEqual(dt.tzoffset(), -23820) - - # A positive numerical timezone with minutes. - dt = DateTime('Tue, 24 Jul 2001 09:41:03 +0425') - self.assertEqual(dt.tzoffset(), 15900) - - def testISO8601(self): - # ISO8601 reference dates - ref0 = DateTime('2002/5/2 8:00am GMT') - ref1 = DateTime('2002/5/2 8:00am US/Eastern') - ref2 = DateTime('2006/11/6 10:30 GMT') - ref3 = DateTime('2004/06/14 14:30:15 GMT-3') - ref4 = DateTime('2006/01/01 GMT') - - # Basic tests - # Though this is timezone naive and according to specification should - # be interpreted in the local timezone, to preserve backwards - # compatibility with previously expected behaviour. - isoDt = DateTime('2002-05-02T08:00:00') - self.assertTrue(ref0.equalTo(isoDt)) - isoDt = DateTime('2002-05-02T08:00:00Z') - self.assertTrue(ref0.equalTo(isoDt)) - isoDt = DateTime('2002-05-02T08:00:00+00:00') - self.assertTrue(ref0.equalTo(isoDt)) - isoDt = DateTime('2002-05-02T08:00:00-04:00') - self.assertTrue(ref1.equalTo(isoDt)) - isoDt = DateTime('2002-05-02 08:00:00-04:00') - self.assertTrue(ref1.equalTo(isoDt)) - - # Bug 1386: the colon in the timezone offset is optional - isoDt = DateTime('2002-05-02T08:00:00-0400') - self.assertTrue(ref1.equalTo(isoDt)) - - # Bug 2191: date reduced formats - isoDt = DateTime('2006-01-01') - self.assertTrue(ref4.equalTo(isoDt)) - isoDt = DateTime('200601-01') - self.assertTrue(ref4.equalTo(isoDt)) - isoDt = DateTime('20060101') - self.assertTrue(ref4.equalTo(isoDt)) - isoDt = DateTime('2006-01') - self.assertTrue(ref4.equalTo(isoDt)) - isoDt = DateTime('200601') - self.assertTrue(ref4.equalTo(isoDt)) - isoDt = DateTime('2006') - self.assertTrue(ref4.equalTo(isoDt)) - - # Bug 2191: date/time separators are also optional - isoDt = DateTime('20020502T08:00:00') - self.assertTrue(ref0.equalTo(isoDt)) - isoDt = DateTime('2002-05-02T080000') - self.assertTrue(ref0.equalTo(isoDt)) - isoDt = DateTime('20020502T080000') - self.assertTrue(ref0.equalTo(isoDt)) - - # Bug 2191: timezones with only one digit for hour - isoDt = DateTime('20020502T080000+0') - self.assertTrue(ref0.equalTo(isoDt)) - isoDt = DateTime('20020502 080000-4') - self.assertTrue(ref1.equalTo(isoDt)) - isoDt = DateTime('20020502T080000-400') - self.assertTrue(ref1.equalTo(isoDt)) - isoDt = DateTime('20020502T080000-4:00') - self.assertTrue(ref1.equalTo(isoDt)) - - # Bug 2191: optional seconds/minutes - isoDt = DateTime('2002-05-02T0800') - self.assertTrue(ref0.equalTo(isoDt)) - isoDt = DateTime('2002-05-02T08') - self.assertTrue(ref0.equalTo(isoDt)) - - # Bug 2191: week format - isoDt = DateTime('2002-W18-4T0800') - self.assertTrue(ref0.equalTo(isoDt)) - isoDt = DateTime('2002-W184T0800') - self.assertTrue(ref0.equalTo(isoDt)) - isoDt = DateTime('2002W18-4T0800') - self.assertTrue(ref0.equalTo(isoDt)) - isoDt = DateTime('2002W184T08') - self.assertTrue(ref0.equalTo(isoDt)) - isoDt = DateTime('2004-W25-1T14:30:15-03:00') - self.assertTrue(ref3.equalTo(isoDt)) - isoDt = DateTime('2004-W25T14:30:15-03:00') - self.assertTrue(ref3.equalTo(isoDt)) - - # Bug 2191: day of year format - isoDt = DateTime('2002-122T0800') - self.assertTrue(ref0.equalTo(isoDt)) - isoDt = DateTime('2002122T0800') - self.assertTrue(ref0.equalTo(isoDt)) - - # Bug 2191: hours/minutes fractions - isoDt = DateTime('2006-11-06T10.5') - self.assertTrue(ref2.equalTo(isoDt)) - isoDt = DateTime('2006-11-06T10,5') - self.assertTrue(ref2.equalTo(isoDt)) - isoDt = DateTime('20040614T1430.25-3') - self.assertTrue(ref3.equalTo(isoDt)) - isoDt = DateTime('2004-06-14T1430,25-3') - self.assertTrue(ref3.equalTo(isoDt)) - isoDt = DateTime('2004-06-14T14:30.25-3') - self.assertTrue(ref3.equalTo(isoDt)) - isoDt = DateTime('20040614T14:30,25-3') - self.assertTrue(ref3.equalTo(isoDt)) - - # ISO8601 standard format - iso8601_string = '2002-05-02T08:00:00-04:00' - iso8601DT = DateTime(iso8601_string) - self.assertEqual(iso8601_string, iso8601DT.ISO8601()) - - # ISO format with no timezone - isoDt = DateTime('2006-01-01 00:00:00') - self.assertTrue(ref4.equalTo(isoDt)) - - def testJulianWeek(self): - # Check JulianDayWeek function - fn = os.path.join(DATADIR, 'julian_testdata.txt') - with open(fn) as fd: - lines = fd.readlines() - for line in lines: - d = DateTime(line[:10]) - result_from_mx = tuple(map(int, line[12:-2].split(','))) - self.assertEqual(result_from_mx[1], d.week()) - - def testCopyConstructor(self): - d = DateTime('2004/04/04') - self.assertEqual(DateTime(d), d) - self.assertEqual(str(DateTime(d)), str(d)) - d2 = DateTime('1999/04/12 01:00:00') - self.assertEqual(DateTime(d2), d2) - self.assertEqual(str(DateTime(d2)), str(d2)) - - def testCopyConstructorPreservesTimezone(self): - # test for https://bugs.launchpad.net/zope2/+bug/200007 - # This always worked in the local timezone, so we need at least - # two tests with different zones to be sure at least one of them - # is not local. - d = DateTime('2004/04/04') - self.assertEqual(DateTime(d).timezone(), d.timezone()) - d2 = DateTime('2008/04/25 12:00:00 EST') - self.assertEqual(DateTime(d2).timezone(), d2.timezone()) - self.assertEqual(str(DateTime(d2)), str(d2)) - d3 = DateTime('2008/04/25 12:00:00 PST') - self.assertEqual(DateTime(d3).timezone(), d3.timezone()) - self.assertEqual(str(DateTime(d3)), str(d3)) - - def testRFC822(self): - # rfc822 conversion - dt = DateTime('2002-05-02T08:00:00+00:00') - self.assertEqual(dt.rfc822(), 'Thu, 02 May 2002 08:00:00 +0000') - - dt = DateTime('2002-05-02T08:00:00+02:00') - self.assertEqual(dt.rfc822(), 'Thu, 02 May 2002 08:00:00 +0200') - - dt = DateTime('2002-05-02T08:00:00-02:00') - self.assertEqual(dt.rfc822(), 'Thu, 02 May 2002 08:00:00 -0200') - - # Checking that conversion from local time is working. - dt = DateTime() - dts = dt.rfc822().split(' ') - times = dts[4].split(':') - _isDST = time.localtime(time.time())[8] - if _isDST: - offset = time.altzone - else: - offset = time.timezone - self.assertEqual(dts[0], dt.aDay() + ',') - self.assertEqual(int(dts[1]), dt.day()) - self.assertEqual(dts[2], dt.aMonth()) - self.assertEqual(int(dts[3]), dt.year()) - self.assertEqual(int(times[0]), dt.h_24()) - self.assertEqual(int(times[1]), dt.minute()) - self.assertEqual(int(times[2]), int(dt.second())) - self.assertEqual(dts[5], "%+03d%02d" % divmod((-offset / 60), 60)) - - def testInternationalDateformat(self): - for year in (1990, 2001, 2020): - for month in (1, 12): - for day in (1, 12, 28, 31): - try: - d_us = DateTime("%d/%d/%d" % (year, month, day)) - except Exception: - continue - - d_int = DateTime("%d.%d.%d" % (day, month, year), - datefmt="international") - self.assertEqual(d_us, d_int) - - d_int = DateTime("%d/%d/%d" % (day, month, year), - datefmt="international") - self.assertEqual(d_us, d_int) - - def test_intl_format_hyphen(self): - d_jan = DateTime('2011-01-11 GMT') - d_nov = DateTime('2011-11-01 GMT') - d_us = DateTime('11-01-2011 GMT') - d_int = DateTime('11-01-2011 GMT', datefmt="international") - self.assertNotEqual(d_us, d_int) - self.assertEqual(d_us, d_nov) - self.assertEqual(d_int, d_jan) - - def test_calcTimezoneName(self): - from DateTime.interfaces import TimeError - timezone_dependent_epoch = 2177452800 - try: - DateTime()._calcTimezoneName(timezone_dependent_epoch, 0) - except TimeError: - self.fail('Zope Collector issue #484 (negative time bug): ' - 'TimeError raised') - - def testStrftimeTZhandling(self): - # strftime timezone testing - # This is a test for collector issue #1127 - format = '%Y-%m-%d %H:%M %Z' - dt = DateTime('Wed, 19 Nov 2003 18:32:07 -0215') - dt_string = dt.strftime(format) - dt_local = dt.toZone(_findLocalTimeZoneName(0)) - dt_localstring = dt_local.strftime(format) - self.assertEqual(dt_string, dt_localstring) - - def testStrftimeFarDates(self): - # Checks strftime in dates <= 1900 or >= 2038 - dt = DateTime('1900/01/30') - self.assertEqual(dt.strftime('%d/%m/%Y'), '30/01/1900') - dt = DateTime('2040/01/30') - self.assertEqual(dt.strftime('%d/%m/%Y'), '30/01/2040') - - def testZoneInFarDates(self): - # Checks time zone in dates <= 1900 or >= 2038 - dt1 = DateTime('2040/01/30 14:33 GMT+1') - dt2 = DateTime('2040/01/30 11:33 GMT-2') - self.assertEqual(dt1.strftime('%d/%m/%Y %H:%M'), - dt2.strftime('%d/%m/%Y %H:%M')) - - @unittest.skipIf( - IS_PYPY, - "Using Non-Ascii characters for strftime doesn't work in PyPy" - "https://bitbucket.org/pypy/pypy/issues/2161/pypy3-strftime-does-not-accept-unicode" # noqa: E501 line too long - ) - def testStrftimeStr(self): - dt = DateTime('2002-05-02T08:00:00+00:00') - uchar = b'\xc3\xa0'.decode('utf-8') - ok = dt.strftime('Le %d/%m/%Y a %Hh%M').replace('a', uchar) - ustr = b'Le %d/%m/%Y \xc3\xa0 %Hh%M'.decode('utf-8') - self.assertEqual(dt.strftime(ustr), ok) - - def testTimezoneNaiveHandling(self): - # checks that we assign timezone naivity correctly - dt = DateTime('2007-10-04T08:00:00+00:00') - self.assertFalse(dt.timezoneNaive(), - 'error with naivity handling in __parse_iso8601') - dt = DateTime('2007-10-04T08:00:00Z') - self.assertFalse(dt.timezoneNaive(), - 'error with naivity handling in __parse_iso8601') - dt = DateTime('2007-10-04T08:00:00') - self.assertTrue(dt.timezoneNaive(), - 'error with naivity handling in __parse_iso8601') - dt = DateTime('2007/10/04 15:12:33.487618 GMT+1') - self.assertFalse(dt.timezoneNaive(), - 'error with naivity handling in _parse') - dt = DateTime('2007/10/04 15:12:33.487618') - self.assertTrue(dt.timezoneNaive(), - 'error with naivity handling in _parse') - dt = DateTime() - self.assertFalse(dt.timezoneNaive(), - 'error with naivity for current time') - s = '2007-10-04T08:00:00' - dt = DateTime(s) - self.assertEqual(s, dt.ISO8601()) - s = '2007-10-04T08:00:00+00:00' - dt = DateTime(s) - self.assertEqual(s, dt.ISO8601()) - - def testConversions(self): - sdt0 = datetime.now() # this is a timezone naive datetime - dt0 = DateTime(sdt0) - self.assertTrue(dt0.timezoneNaive(), (sdt0, dt0)) - sdt1 = datetime(2007, 10, 4, 18, 14, 42, 580, pytz.utc) - dt1 = DateTime(sdt1) - self.assertFalse(dt1.timezoneNaive(), (sdt1, dt1)) - - # convert back - sdt2 = dt0.asdatetime() - self.assertEqual(sdt0, sdt2) - sdt3 = dt1.utcdatetime() # this returns a timezone naive datetime - self.assertEqual(sdt1.hour, sdt3.hour) - - dt4 = DateTime('2007-10-04T10:00:00+05:00') - sdt4 = datetime(2007, 10, 4, 5, 0) - self.assertEqual(dt4.utcdatetime(), sdt4) - self.assertEqual(dt4.asdatetime(), sdt4.replace(tzinfo=pytz.utc)) - - dt5 = DateTime('2007-10-23 10:00:00 US/Eastern') - tz = pytz.timezone('US/Eastern') - sdt5 = datetime(2007, 10, 23, 10, 0, tzinfo=tz) - dt6 = DateTime(sdt5) - self.assertEqual(dt5.asdatetime(), sdt5) - self.assertEqual(dt6.asdatetime(), sdt5) - self.assertEqual(dt5, dt6) - self.assertEqual(dt5.asdatetime().tzinfo, tz) - self.assertEqual(dt6.asdatetime().tzinfo, tz) - - def testBasicTZ(self): - # psycopg2 supplies it's own tzinfo instances, with no `zone` attribute - tz = FixedOffset(60, 'GMT+1') - dt1 = datetime(2008, 8, 5, 12, 0, tzinfo=tz) - DT = DateTime(dt1) - dt2 = DT.asdatetime() - offset1 = dt1.tzinfo.utcoffset(dt1) - offset2 = dt2.tzinfo.utcoffset(dt2) - self.assertEqual(offset1, offset2) - - def testEDTTimezone(self): - # should be able to parse EDT timezones: see lp:599856. - dt = DateTime("Mon, 28 Jun 2010 10:12:25 EDT") - self.assertEqual(dt.Day(), 'Monday') - self.assertEqual(dt.day(), 28) - self.assertEqual(dt.Month(), 'June') - self.assertEqual(dt.timezone(), 'GMT-4') - - def testParseISO8601(self): - parsed = DateTime()._parse_iso8601('2010-10-10') - self.assertEqual(parsed, (2010, 10, 10, 0, 0, 0, 'GMT+0000')) - - def test_interface(self): - from DateTime.interfaces import IDateTime - self.assertTrue(IDateTime.providedBy(DateTime())) - - def test_security(self): - dt = DateTime() - self.assertEqual(dt.__roles__, None) - self.assertEqual(dt.__allow_access_to_unprotected_subobjects__, 1) - - def test_format(self): - dt = DateTime(1968, 3, 10, 23, 45, 0, 'Europe/Vienna') - fmt = '%d.%m.%Y %H:%M' - result = dt.strftime(fmt) - unformatted_result = '1968/03/10 23:45:00 Europe/Vienna' - self.assertEqual(result, f'{dt:%d.%m.%Y %H:%M}') - self.assertEqual(unformatted_result, f'{dt}') - self.assertEqual(unformatted_result, f'{dt}') - self.assertEqual(result, f'{dt:{fmt}}') - self.assertEqual(unformatted_result, f'{dt:}') - self.assertEqual(unformatted_result, f'{dt}') - - -def test_suite(): - import doctest - return unittest.TestSuite([ - unittest.defaultTestLoader.loadTestsFromTestCase(DateTimeTests), - doctest.DocFileSuite('DateTime.txt', package='DateTime'), - doctest.DocFileSuite('pytz.txt', package='DateTime'), - ]) diff --git a/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/INSTALLER b/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/LICENSE.txt b/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/LICENSE.txt deleted file mode 100644 index 9d227a0..0000000 --- a/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/LICENSE.txt +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2010 Pallets - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/METADATA b/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/METADATA deleted file mode 100644 index 82261f2..0000000 --- a/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/METADATA +++ /dev/null @@ -1,92 +0,0 @@ -Metadata-Version: 2.1 -Name: MarkupSafe -Version: 3.0.2 -Summary: Safely add untrusted strings to HTML/XML markup. -Maintainer-email: Pallets -License: Copyright 2010 Pallets - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Project-URL: Donate, https://palletsprojects.com/donate -Project-URL: Documentation, https://markupsafe.palletsprojects.com/ -Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/ -Project-URL: Source, https://github.com/pallets/markupsafe/ -Project-URL: Chat, https://discord.gg/pallets -Classifier: Development Status :: 5 - Production/Stable -Classifier: Environment :: Web Environment -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content -Classifier: Topic :: Text Processing :: Markup :: HTML -Classifier: Typing :: Typed -Requires-Python: >=3.9 -Description-Content-Type: text/markdown -License-File: LICENSE.txt - -# MarkupSafe - -MarkupSafe implements a text object that escapes characters so it is -safe to use in HTML and XML. Characters that have special meanings are -replaced so that they display as the actual characters. This mitigates -injection attacks, meaning untrusted user input can safely be displayed -on a page. - - -## Examples - -```pycon ->>> from markupsafe import Markup, escape - ->>> # escape replaces special characters and wraps in Markup ->>> escape("") -Markup('<script>alert(document.cookie);</script>') - ->>> # wrap in Markup to mark text "safe" and prevent escaping ->>> Markup("Hello") -Markup('hello') - ->>> escape(Markup("Hello")) -Markup('hello') - ->>> # Markup is a str subclass ->>> # methods and operators escape their arguments ->>> template = Markup("Hello {name}") ->>> template.format(name='"World"') -Markup('Hello "World"') -``` - -## Donate - -The Pallets organization develops and supports MarkupSafe and other -popular packages. In order to grow the community of contributors and -users, and allow the maintainers to devote more time to the projects, -[please donate today][]. - -[please donate today]: https://palletsprojects.com/donate diff --git a/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/RECORD b/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/RECORD deleted file mode 100644 index 0c652a7..0000000 --- a/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/RECORD +++ /dev/null @@ -1,14 +0,0 @@ -MarkupSafe-3.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -MarkupSafe-3.0.2.dist-info/LICENSE.txt,sha256=RjHsDbX9kKVH4zaBcmTGeYIUM4FG-KyUtKV_lu6MnsQ,1503 -MarkupSafe-3.0.2.dist-info/METADATA,sha256=nhoabjupBG41j_JxPCJ3ylgrZ6Fx8oMCFbiLF9Kafqc,4067 -MarkupSafe-3.0.2.dist-info/RECORD,, -MarkupSafe-3.0.2.dist-info/WHEEL,sha256=IqiWNwTSPPvorR7mTezuRY2eqj__44JKKkjOiewDX64,101 -MarkupSafe-3.0.2.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 -markupsafe/__init__.py,sha256=pREerPwvinB62tNCMOwqxBS2YHV6R52Wcq1d-rB4Z5o,13609 -markupsafe/__pycache__/__init__.cpython-310.pyc,, -markupsafe/__pycache__/_native.cpython-310.pyc,, -markupsafe/_native.py,sha256=2ptkJ40yCcp9kq3L1NqpgjfpZB-obniYKFFKUOkHh4Q,218 -markupsafe/_speedups.c,sha256=SglUjn40ti9YgQAO--OgkSyv9tXq9vvaHyVhQows4Ok,4353 -markupsafe/_speedups.cp310-win_amd64.pyd,sha256=RTvh-UzJTX7J_4j-A5jZmnqwRKBe0pQiDPd_j60jft8,13312 -markupsafe/_speedups.pyi,sha256=LSDmXYOefH4HVpAXuL8sl7AttLw0oXh1njVoVZp2wqQ,42 -markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/WHEEL b/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/WHEEL deleted file mode 100644 index c4388f8..0000000 --- a/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (75.2.0) -Root-Is-Purelib: false -Tag: cp310-cp310-win_amd64 - diff --git a/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/top_level.txt b/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/top_level.txt deleted file mode 100644 index 75bf729..0000000 --- a/venv/Lib/site-packages/MarkupSafe-3.0.2.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -markupsafe diff --git a/venv/Lib/site-packages/__pycache__/pathlib.cpython-310.pyc b/venv/Lib/site-packages/__pycache__/pathlib.cpython-310.pyc deleted file mode 100644 index 7720b0e..0000000 Binary files a/venv/Lib/site-packages/__pycache__/pathlib.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/_distutils_hack/__init__.py b/venv/Lib/site-packages/_distutils_hack/__init__.py deleted file mode 100644 index f987a53..0000000 --- a/venv/Lib/site-packages/_distutils_hack/__init__.py +++ /dev/null @@ -1,222 +0,0 @@ -# don't import any costly modules -import sys -import os - - -is_pypy = '__pypy__' in sys.builtin_module_names - - -def warn_distutils_present(): - if 'distutils' not in sys.modules: - return - if is_pypy and sys.version_info < (3, 7): - # PyPy for 3.6 unconditionally imports distutils, so bypass the warning - # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 - return - import warnings - - warnings.warn( - "Distutils was imported before Setuptools, but importing Setuptools " - "also replaces the `distutils` module in `sys.modules`. This may lead " - "to undesirable behaviors or errors. To avoid these issues, avoid " - "using distutils directly, ensure that setuptools is installed in the " - "traditional way (e.g. not an editable install), and/or make sure " - "that setuptools is always imported before distutils." - ) - - -def clear_distutils(): - if 'distutils' not in sys.modules: - return - import warnings - - warnings.warn("Setuptools is replacing distutils.") - mods = [ - name - for name in sys.modules - if name == "distutils" or name.startswith("distutils.") - ] - for name in mods: - del sys.modules[name] - - -def enabled(): - """ - Allow selection of distutils by environment variable. - """ - which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local') - return which == 'local' - - -def ensure_local_distutils(): - import importlib - - clear_distutils() - - # With the DistutilsMetaFinder in place, - # perform an import to cause distutils to be - # loaded from setuptools._distutils. Ref #2906. - with shim(): - importlib.import_module('distutils') - - # check that submodules load as expected - core = importlib.import_module('distutils.core') - assert '_distutils' in core.__file__, core.__file__ - assert 'setuptools._distutils.log' not in sys.modules - - -def do_override(): - """ - Ensure that the local copy of distutils is preferred over stdlib. - - See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 - for more motivation. - """ - if enabled(): - warn_distutils_present() - ensure_local_distutils() - - -class _TrivialRe: - def __init__(self, *patterns): - self._patterns = patterns - - def match(self, string): - return all(pat in string for pat in self._patterns) - - -class DistutilsMetaFinder: - def find_spec(self, fullname, path, target=None): - # optimization: only consider top level modules and those - # found in the CPython test suite. - if path is not None and not fullname.startswith('test.'): - return - - method_name = 'spec_for_{fullname}'.format(**locals()) - method = getattr(self, method_name, lambda: None) - return method() - - def spec_for_distutils(self): - if self.is_cpython(): - return - - import importlib - import importlib.abc - import importlib.util - - try: - mod = importlib.import_module('setuptools._distutils') - except Exception: - # There are a couple of cases where setuptools._distutils - # may not be present: - # - An older Setuptools without a local distutils is - # taking precedence. Ref #2957. - # - Path manipulation during sitecustomize removes - # setuptools from the path but only after the hook - # has been loaded. Ref #2980. - # In either case, fall back to stdlib behavior. - return - - class DistutilsLoader(importlib.abc.Loader): - def create_module(self, spec): - mod.__name__ = 'distutils' - return mod - - def exec_module(self, module): - pass - - return importlib.util.spec_from_loader( - 'distutils', DistutilsLoader(), origin=mod.__file__ - ) - - @staticmethod - def is_cpython(): - """ - Suppress supplying distutils for CPython (build and tests). - Ref #2965 and #3007. - """ - return os.path.isfile('pybuilddir.txt') - - def spec_for_pip(self): - """ - Ensure stdlib distutils when running under pip. - See pypa/pip#8761 for rationale. - """ - if self.pip_imported_during_build(): - return - clear_distutils() - self.spec_for_distutils = lambda: None - - @classmethod - def pip_imported_during_build(cls): - """ - Detect if pip is being imported in a build script. Ref #2355. - """ - import traceback - - return any( - cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None) - ) - - @staticmethod - def frame_file_is_setup(frame): - """ - Return True if the indicated frame suggests a setup.py file. - """ - # some frames may not have __file__ (#2940) - return frame.f_globals.get('__file__', '').endswith('setup.py') - - def spec_for_sensitive_tests(self): - """ - Ensure stdlib distutils when running select tests under CPython. - - python/cpython#91169 - """ - clear_distutils() - self.spec_for_distutils = lambda: None - - sensitive_tests = ( - [ - 'test.test_distutils', - 'test.test_peg_generator', - 'test.test_importlib', - ] - if sys.version_info < (3, 10) - else [ - 'test.test_distutils', - ] - ) - - -for name in DistutilsMetaFinder.sensitive_tests: - setattr( - DistutilsMetaFinder, - f'spec_for_{name}', - DistutilsMetaFinder.spec_for_sensitive_tests, - ) - - -DISTUTILS_FINDER = DistutilsMetaFinder() - - -def add_shim(): - DISTUTILS_FINDER in sys.meta_path or insert_shim() - - -class shim: - def __enter__(self): - insert_shim() - - def __exit__(self, exc, value, tb): - remove_shim() - - -def insert_shim(): - sys.meta_path.insert(0, DISTUTILS_FINDER) - - -def remove_shim(): - try: - sys.meta_path.remove(DISTUTILS_FINDER) - except ValueError: - pass diff --git a/venv/Lib/site-packages/_distutils_hack/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/_distutils_hack/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 1acfd5d..0000000 Binary files a/venv/Lib/site-packages/_distutils_hack/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/_distutils_hack/__pycache__/override.cpython-310.pyc b/venv/Lib/site-packages/_distutils_hack/__pycache__/override.cpython-310.pyc deleted file mode 100644 index e5c44bb..0000000 Binary files a/venv/Lib/site-packages/_distutils_hack/__pycache__/override.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/_distutils_hack/override.py b/venv/Lib/site-packages/_distutils_hack/override.py deleted file mode 100644 index 2cc433a..0000000 --- a/venv/Lib/site-packages/_distutils_hack/override.py +++ /dev/null @@ -1 +0,0 @@ -__import__('_distutils_hack').do_override() diff --git a/venv/Lib/site-packages/blinker-1.9.0.dist-info/INSTALLER b/venv/Lib/site-packages/blinker-1.9.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/Lib/site-packages/blinker-1.9.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/Lib/site-packages/blinker-1.9.0.dist-info/LICENSE.txt b/venv/Lib/site-packages/blinker-1.9.0.dist-info/LICENSE.txt deleted file mode 100644 index 79c9825..0000000 --- a/venv/Lib/site-packages/blinker-1.9.0.dist-info/LICENSE.txt +++ /dev/null @@ -1,20 +0,0 @@ -Copyright 2010 Jason Kirtland - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/venv/Lib/site-packages/blinker-1.9.0.dist-info/METADATA b/venv/Lib/site-packages/blinker-1.9.0.dist-info/METADATA deleted file mode 100644 index 6d343f5..0000000 --- a/venv/Lib/site-packages/blinker-1.9.0.dist-info/METADATA +++ /dev/null @@ -1,60 +0,0 @@ -Metadata-Version: 2.3 -Name: blinker -Version: 1.9.0 -Summary: Fast, simple object-to-object and broadcast signaling -Author: Jason Kirtland -Maintainer-email: Pallets Ecosystem -Requires-Python: >=3.9 -Description-Content-Type: text/markdown -Classifier: Development Status :: 5 - Production/Stable -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python -Classifier: Typing :: Typed -Project-URL: Chat, https://discord.gg/pallets -Project-URL: Documentation, https://blinker.readthedocs.io -Project-URL: Source, https://github.com/pallets-eco/blinker/ - -# Blinker - -Blinker provides a fast dispatching system that allows any number of -interested parties to subscribe to events, or "signals". - - -## Pallets Community Ecosystem - -> [!IMPORTANT]\ -> This project is part of the Pallets Community Ecosystem. Pallets is the open -> source organization that maintains Flask; Pallets-Eco enables community -> maintenance of related projects. If you are interested in helping maintain -> this project, please reach out on [the Pallets Discord server][discord]. -> -> [discord]: https://discord.gg/pallets - - -## Example - -Signal receivers can subscribe to specific senders or receive signals -sent by any sender. - -```pycon ->>> from blinker import signal ->>> started = signal('round-started') ->>> def each(round): -... print(f"Round {round}") -... ->>> started.connect(each) - ->>> def round_two(round): -... print("This is round two.") -... ->>> started.connect(round_two, sender=2) - ->>> for round in range(1, 4): -... started.send(round) -... -Round 1! -Round 2! -This is round two. -Round 3! -``` - diff --git a/venv/Lib/site-packages/blinker-1.9.0.dist-info/RECORD b/venv/Lib/site-packages/blinker-1.9.0.dist-info/RECORD deleted file mode 100644 index e6ed9e3..0000000 --- a/venv/Lib/site-packages/blinker-1.9.0.dist-info/RECORD +++ /dev/null @@ -1,12 +0,0 @@ -blinker-1.9.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -blinker-1.9.0.dist-info/LICENSE.txt,sha256=nrc6HzhZekqhcCXSrhvjg5Ykx5XphdTw6Xac4p-spGc,1054 -blinker-1.9.0.dist-info/METADATA,sha256=uIRiM8wjjbHkCtbCyTvctU37IAZk0kEe5kxAld1dvzA,1633 -blinker-1.9.0.dist-info/RECORD,, -blinker-1.9.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82 -blinker/__init__.py,sha256=I2EdZqpy4LyjX17Hn1yzJGWCjeLaVaPzsMgHkLfj_cQ,317 -blinker/__pycache__/__init__.cpython-310.pyc,, -blinker/__pycache__/_utilities.cpython-310.pyc,, -blinker/__pycache__/base.cpython-310.pyc,, -blinker/_utilities.py,sha256=0J7eeXXTUx0Ivf8asfpx0ycVkp0Eqfqnj117x2mYX9E,1675 -blinker/base.py,sha256=QpDuvXXcwJF49lUBcH5BiST46Rz9wSG7VW_p7N_027M,19132 -blinker/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/Lib/site-packages/blinker-1.9.0.dist-info/WHEEL b/venv/Lib/site-packages/blinker-1.9.0.dist-info/WHEEL deleted file mode 100644 index e3c6fee..0000000 --- a/venv/Lib/site-packages/blinker-1.9.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: flit 3.10.1 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/venv/Lib/site-packages/blinker/__init__.py b/venv/Lib/site-packages/blinker/__init__.py deleted file mode 100644 index 1772fa4..0000000 --- a/venv/Lib/site-packages/blinker/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import annotations - -from .base import ANY -from .base import default_namespace -from .base import NamedSignal -from .base import Namespace -from .base import Signal -from .base import signal - -__all__ = [ - "ANY", - "default_namespace", - "NamedSignal", - "Namespace", - "Signal", - "signal", -] diff --git a/venv/Lib/site-packages/blinker/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/blinker/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 6af01e4..0000000 Binary files a/venv/Lib/site-packages/blinker/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/blinker/__pycache__/_utilities.cpython-310.pyc b/venv/Lib/site-packages/blinker/__pycache__/_utilities.cpython-310.pyc deleted file mode 100644 index 1c92fd9..0000000 Binary files a/venv/Lib/site-packages/blinker/__pycache__/_utilities.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/blinker/__pycache__/base.cpython-310.pyc b/venv/Lib/site-packages/blinker/__pycache__/base.cpython-310.pyc deleted file mode 100644 index 97b712f..0000000 Binary files a/venv/Lib/site-packages/blinker/__pycache__/base.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/blinker/_utilities.py b/venv/Lib/site-packages/blinker/_utilities.py deleted file mode 100644 index 000c902..0000000 --- a/venv/Lib/site-packages/blinker/_utilities.py +++ /dev/null @@ -1,64 +0,0 @@ -from __future__ import annotations - -import collections.abc as c -import inspect -import typing as t -from weakref import ref -from weakref import WeakMethod - -T = t.TypeVar("T") - - -class Symbol: - """A constant symbol, nicer than ``object()``. Repeated calls return the - same instance. - - >>> Symbol('foo') is Symbol('foo') - True - >>> Symbol('foo') - foo - """ - - symbols: t.ClassVar[dict[str, Symbol]] = {} - - def __new__(cls, name: str) -> Symbol: - if name in cls.symbols: - return cls.symbols[name] - - obj = super().__new__(cls) - cls.symbols[name] = obj - return obj - - def __init__(self, name: str) -> None: - self.name = name - - def __repr__(self) -> str: - return self.name - - def __getnewargs__(self) -> tuple[t.Any, ...]: - return (self.name,) - - -def make_id(obj: object) -> c.Hashable: - """Get a stable identifier for a receiver or sender, to be used as a dict - key or in a set. - """ - if inspect.ismethod(obj): - # The id of a bound method is not stable, but the id of the unbound - # function and instance are. - return id(obj.__func__), id(obj.__self__) - - if isinstance(obj, (str, int)): - # Instances with the same value always compare equal and have the same - # hash, even if the id may change. - return obj - - # Assume other types are not hashable but will always be the same instance. - return id(obj) - - -def make_ref(obj: T, callback: c.Callable[[ref[T]], None] | None = None) -> ref[T]: - if inspect.ismethod(obj): - return WeakMethod(obj, callback) # type: ignore[arg-type, return-value] - - return ref(obj, callback) diff --git a/venv/Lib/site-packages/blinker/base.py b/venv/Lib/site-packages/blinker/base.py deleted file mode 100644 index d051b94..0000000 --- a/venv/Lib/site-packages/blinker/base.py +++ /dev/null @@ -1,512 +0,0 @@ -from __future__ import annotations - -import collections.abc as c -import sys -import typing as t -import weakref -from collections import defaultdict -from contextlib import contextmanager -from functools import cached_property -from inspect import iscoroutinefunction - -from ._utilities import make_id -from ._utilities import make_ref -from ._utilities import Symbol - -F = t.TypeVar("F", bound=c.Callable[..., t.Any]) - -ANY = Symbol("ANY") -"""Symbol for "any sender".""" - -ANY_ID = 0 - - -class Signal: - """A notification emitter. - - :param doc: The docstring for the signal. - """ - - ANY = ANY - """An alias for the :data:`~blinker.ANY` sender symbol.""" - - set_class: type[set[t.Any]] = set - """The set class to use for tracking connected receivers and senders. - Python's ``set`` is unordered. If receivers must be dispatched in the order - they were connected, an ordered set implementation can be used. - - .. versionadded:: 1.7 - """ - - @cached_property - def receiver_connected(self) -> Signal: - """Emitted at the end of each :meth:`connect` call. - - The signal sender is the signal instance, and the :meth:`connect` - arguments are passed through: ``receiver``, ``sender``, and ``weak``. - - .. versionadded:: 1.2 - """ - return Signal(doc="Emitted after a receiver connects.") - - @cached_property - def receiver_disconnected(self) -> Signal: - """Emitted at the end of each :meth:`disconnect` call. - - The sender is the signal instance, and the :meth:`disconnect` arguments - are passed through: ``receiver`` and ``sender``. - - This signal is emitted **only** when :meth:`disconnect` is called - explicitly. This signal cannot be emitted by an automatic disconnect - when a weakly referenced receiver or sender goes out of scope, as the - instance is no longer be available to be used as the sender for this - signal. - - An alternative approach is available by subscribing to - :attr:`receiver_connected` and setting up a custom weakref cleanup - callback on weak receivers and senders. - - .. versionadded:: 1.2 - """ - return Signal(doc="Emitted after a receiver disconnects.") - - def __init__(self, doc: str | None = None) -> None: - if doc: - self.__doc__ = doc - - self.receivers: dict[ - t.Any, weakref.ref[c.Callable[..., t.Any]] | c.Callable[..., t.Any] - ] = {} - """The map of connected receivers. Useful to quickly check if any - receivers are connected to the signal: ``if s.receivers:``. The - structure and data is not part of the public API, but checking its - boolean value is. - """ - - self.is_muted: bool = False - self._by_receiver: dict[t.Any, set[t.Any]] = defaultdict(self.set_class) - self._by_sender: dict[t.Any, set[t.Any]] = defaultdict(self.set_class) - self._weak_senders: dict[t.Any, weakref.ref[t.Any]] = {} - - def connect(self, receiver: F, sender: t.Any = ANY, weak: bool = True) -> F: - """Connect ``receiver`` to be called when the signal is sent by - ``sender``. - - :param receiver: The callable to call when :meth:`send` is called with - the given ``sender``, passing ``sender`` as a positional argument - along with any extra keyword arguments. - :param sender: Any object or :data:`ANY`. ``receiver`` will only be - called when :meth:`send` is called with this sender. If ``ANY``, the - receiver will be called for any sender. A receiver may be connected - to multiple senders by calling :meth:`connect` multiple times. - :param weak: Track the receiver with a :mod:`weakref`. The receiver will - be automatically disconnected when it is garbage collected. When - connecting a receiver defined within a function, set to ``False``, - otherwise it will be disconnected when the function scope ends. - """ - receiver_id = make_id(receiver) - sender_id = ANY_ID if sender is ANY else make_id(sender) - - if weak: - self.receivers[receiver_id] = make_ref( - receiver, self._make_cleanup_receiver(receiver_id) - ) - else: - self.receivers[receiver_id] = receiver - - self._by_sender[sender_id].add(receiver_id) - self._by_receiver[receiver_id].add(sender_id) - - if sender is not ANY and sender_id not in self._weak_senders: - # store a cleanup for weakref-able senders - try: - self._weak_senders[sender_id] = make_ref( - sender, self._make_cleanup_sender(sender_id) - ) - except TypeError: - pass - - if "receiver_connected" in self.__dict__ and self.receiver_connected.receivers: - try: - self.receiver_connected.send( - self, receiver=receiver, sender=sender, weak=weak - ) - except TypeError: - # TODO no explanation or test for this - self.disconnect(receiver, sender) - raise - - return receiver - - def connect_via(self, sender: t.Any, weak: bool = False) -> c.Callable[[F], F]: - """Connect the decorated function to be called when the signal is sent - by ``sender``. - - The decorated function will be called when :meth:`send` is called with - the given ``sender``, passing ``sender`` as a positional argument along - with any extra keyword arguments. - - :param sender: Any object or :data:`ANY`. ``receiver`` will only be - called when :meth:`send` is called with this sender. If ``ANY``, the - receiver will be called for any sender. A receiver may be connected - to multiple senders by calling :meth:`connect` multiple times. - :param weak: Track the receiver with a :mod:`weakref`. The receiver will - be automatically disconnected when it is garbage collected. When - connecting a receiver defined within a function, set to ``False``, - otherwise it will be disconnected when the function scope ends.= - - .. versionadded:: 1.1 - """ - - def decorator(fn: F) -> F: - self.connect(fn, sender, weak) - return fn - - return decorator - - @contextmanager - def connected_to( - self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY - ) -> c.Generator[None, None, None]: - """A context manager that temporarily connects ``receiver`` to the - signal while a ``with`` block executes. When the block exits, the - receiver is disconnected. Useful for tests. - - :param receiver: The callable to call when :meth:`send` is called with - the given ``sender``, passing ``sender`` as a positional argument - along with any extra keyword arguments. - :param sender: Any object or :data:`ANY`. ``receiver`` will only be - called when :meth:`send` is called with this sender. If ``ANY``, the - receiver will be called for any sender. - - .. versionadded:: 1.1 - """ - self.connect(receiver, sender=sender, weak=False) - - try: - yield None - finally: - self.disconnect(receiver) - - @contextmanager - def muted(self) -> c.Generator[None, None, None]: - """A context manager that temporarily disables the signal. No receivers - will be called if the signal is sent, until the ``with`` block exits. - Useful for tests. - """ - self.is_muted = True - - try: - yield None - finally: - self.is_muted = False - - def send( - self, - sender: t.Any | None = None, - /, - *, - _async_wrapper: c.Callable[ - [c.Callable[..., c.Coroutine[t.Any, t.Any, t.Any]]], c.Callable[..., t.Any] - ] - | None = None, - **kwargs: t.Any, - ) -> list[tuple[c.Callable[..., t.Any], t.Any]]: - """Call all receivers that are connected to the given ``sender`` - or :data:`ANY`. Each receiver is called with ``sender`` as a positional - argument along with any extra keyword arguments. Return a list of - ``(receiver, return value)`` tuples. - - The order receivers are called is undefined, but can be influenced by - setting :attr:`set_class`. - - If a receiver raises an exception, that exception will propagate up. - This makes debugging straightforward, with an assumption that correctly - implemented receivers will not raise. - - :param sender: Call receivers connected to this sender, in addition to - those connected to :data:`ANY`. - :param _async_wrapper: Will be called on any receivers that are async - coroutines to turn them into sync callables. For example, could run - the receiver with an event loop. - :param kwargs: Extra keyword arguments to pass to each receiver. - - .. versionchanged:: 1.7 - Added the ``_async_wrapper`` argument. - """ - if self.is_muted: - return [] - - results = [] - - for receiver in self.receivers_for(sender): - if iscoroutinefunction(receiver): - if _async_wrapper is None: - raise RuntimeError("Cannot send to a coroutine function.") - - result = _async_wrapper(receiver)(sender, **kwargs) - else: - result = receiver(sender, **kwargs) - - results.append((receiver, result)) - - return results - - async def send_async( - self, - sender: t.Any | None = None, - /, - *, - _sync_wrapper: c.Callable[ - [c.Callable[..., t.Any]], c.Callable[..., c.Coroutine[t.Any, t.Any, t.Any]] - ] - | None = None, - **kwargs: t.Any, - ) -> list[tuple[c.Callable[..., t.Any], t.Any]]: - """Await all receivers that are connected to the given ``sender`` - or :data:`ANY`. Each receiver is called with ``sender`` as a positional - argument along with any extra keyword arguments. Return a list of - ``(receiver, return value)`` tuples. - - The order receivers are called is undefined, but can be influenced by - setting :attr:`set_class`. - - If a receiver raises an exception, that exception will propagate up. - This makes debugging straightforward, with an assumption that correctly - implemented receivers will not raise. - - :param sender: Call receivers connected to this sender, in addition to - those connected to :data:`ANY`. - :param _sync_wrapper: Will be called on any receivers that are sync - callables to turn them into async coroutines. For example, - could call the receiver in a thread. - :param kwargs: Extra keyword arguments to pass to each receiver. - - .. versionadded:: 1.7 - """ - if self.is_muted: - return [] - - results = [] - - for receiver in self.receivers_for(sender): - if not iscoroutinefunction(receiver): - if _sync_wrapper is None: - raise RuntimeError("Cannot send to a non-coroutine function.") - - result = await _sync_wrapper(receiver)(sender, **kwargs) - else: - result = await receiver(sender, **kwargs) - - results.append((receiver, result)) - - return results - - def has_receivers_for(self, sender: t.Any) -> bool: - """Check if there is at least one receiver that will be called with the - given ``sender``. A receiver connected to :data:`ANY` will always be - called, regardless of sender. Does not check if weakly referenced - receivers are still live. See :meth:`receivers_for` for a stronger - search. - - :param sender: Check for receivers connected to this sender, in addition - to those connected to :data:`ANY`. - """ - if not self.receivers: - return False - - if self._by_sender[ANY_ID]: - return True - - if sender is ANY: - return False - - return make_id(sender) in self._by_sender - - def receivers_for( - self, sender: t.Any - ) -> c.Generator[c.Callable[..., t.Any], None, None]: - """Yield each receiver to be called for ``sender``, in addition to those - to be called for :data:`ANY`. Weakly referenced receivers that are not - live will be disconnected and skipped. - - :param sender: Yield receivers connected to this sender, in addition - to those connected to :data:`ANY`. - """ - # TODO: test receivers_for(ANY) - if not self.receivers: - return - - sender_id = make_id(sender) - - if sender_id in self._by_sender: - ids = self._by_sender[ANY_ID] | self._by_sender[sender_id] - else: - ids = self._by_sender[ANY_ID].copy() - - for receiver_id in ids: - receiver = self.receivers.get(receiver_id) - - if receiver is None: - continue - - if isinstance(receiver, weakref.ref): - strong = receiver() - - if strong is None: - self._disconnect(receiver_id, ANY_ID) - continue - - yield strong - else: - yield receiver - - def disconnect(self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY) -> None: - """Disconnect ``receiver`` from being called when the signal is sent by - ``sender``. - - :param receiver: A connected receiver callable. - :param sender: Disconnect from only this sender. By default, disconnect - from all senders. - """ - sender_id: c.Hashable - - if sender is ANY: - sender_id = ANY_ID - else: - sender_id = make_id(sender) - - receiver_id = make_id(receiver) - self._disconnect(receiver_id, sender_id) - - if ( - "receiver_disconnected" in self.__dict__ - and self.receiver_disconnected.receivers - ): - self.receiver_disconnected.send(self, receiver=receiver, sender=sender) - - def _disconnect(self, receiver_id: c.Hashable, sender_id: c.Hashable) -> None: - if sender_id == ANY_ID: - if self._by_receiver.pop(receiver_id, None) is not None: - for bucket in self._by_sender.values(): - bucket.discard(receiver_id) - - self.receivers.pop(receiver_id, None) - else: - self._by_sender[sender_id].discard(receiver_id) - self._by_receiver[receiver_id].discard(sender_id) - - def _make_cleanup_receiver( - self, receiver_id: c.Hashable - ) -> c.Callable[[weakref.ref[c.Callable[..., t.Any]]], None]: - """Create a callback function to disconnect a weakly referenced - receiver when it is garbage collected. - """ - - def cleanup(ref: weakref.ref[c.Callable[..., t.Any]]) -> None: - # If the interpreter is shutting down, disconnecting can result in a - # weird ignored exception. Don't call it in that case. - if not sys.is_finalizing(): - self._disconnect(receiver_id, ANY_ID) - - return cleanup - - def _make_cleanup_sender( - self, sender_id: c.Hashable - ) -> c.Callable[[weakref.ref[t.Any]], None]: - """Create a callback function to disconnect all receivers for a weakly - referenced sender when it is garbage collected. - """ - assert sender_id != ANY_ID - - def cleanup(ref: weakref.ref[t.Any]) -> None: - self._weak_senders.pop(sender_id, None) - - for receiver_id in self._by_sender.pop(sender_id, ()): - self._by_receiver[receiver_id].discard(sender_id) - - return cleanup - - def _cleanup_bookkeeping(self) -> None: - """Prune unused sender/receiver bookkeeping. Not threadsafe. - - Connecting & disconnecting leaves behind a small amount of bookkeeping - data. Typical workloads using Blinker, for example in most web apps, - Flask, CLI scripts, etc., are not adversely affected by this - bookkeeping. - - With a long-running process performing dynamic signal routing with high - volume, e.g. connecting to function closures, senders are all unique - object instances. Doing all of this over and over may cause memory usage - to grow due to extraneous bookkeeping. (An empty ``set`` for each stale - sender/receiver pair.) - - This method will prune that bookkeeping away, with the caveat that such - pruning is not threadsafe. The risk is that cleanup of a fully - disconnected receiver/sender pair occurs while another thread is - connecting that same pair. If you are in the highly dynamic, unique - receiver/sender situation that has lead you to this method, that failure - mode is perhaps not a big deal for you. - """ - for mapping in (self._by_sender, self._by_receiver): - for ident, bucket in list(mapping.items()): - if not bucket: - mapping.pop(ident, None) - - def _clear_state(self) -> None: - """Disconnect all receivers and senders. Useful for tests.""" - self._weak_senders.clear() - self.receivers.clear() - self._by_sender.clear() - self._by_receiver.clear() - - -class NamedSignal(Signal): - """A named generic notification emitter. The name is not used by the signal - itself, but matches the key in the :class:`Namespace` that it belongs to. - - :param name: The name of the signal within the namespace. - :param doc: The docstring for the signal. - """ - - def __init__(self, name: str, doc: str | None = None) -> None: - super().__init__(doc) - - #: The name of this signal. - self.name: str = name - - def __repr__(self) -> str: - base = super().__repr__() - return f"{base[:-1]}; {self.name!r}>" # noqa: E702 - - -class Namespace(dict[str, NamedSignal]): - """A dict mapping names to signals.""" - - def signal(self, name: str, doc: str | None = None) -> NamedSignal: - """Return the :class:`NamedSignal` for the given ``name``, creating it - if required. Repeated calls with the same name return the same signal. - - :param name: The name of the signal. - :param doc: The docstring of the signal. - """ - if name not in self: - self[name] = NamedSignal(name, doc) - - return self[name] - - -class _PNamespaceSignal(t.Protocol): - def __call__(self, name: str, doc: str | None = None) -> NamedSignal: ... - - -default_namespace: Namespace = Namespace() -"""A default :class:`Namespace` for creating named signals. :func:`signal` -creates a :class:`NamedSignal` in this namespace. -""" - -signal: _PNamespaceSignal = default_namespace.signal -"""Return a :class:`NamedSignal` in :data:`default_namespace` with the given -``name``, creating it if required. Repeated calls with the same name return the -same signal. -""" diff --git a/venv/Lib/site-packages/blinker/py.typed b/venv/Lib/site-packages/blinker/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/venv/Lib/site-packages/bson/__init__.py b/venv/Lib/site-packages/bson/__init__.py deleted file mode 100644 index 790ac06..0000000 --- a/venv/Lib/site-packages/bson/__init__.py +++ /dev/null @@ -1,1484 +0,0 @@ -# Copyright 2009-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""BSON (Binary JSON) encoding and decoding. - -The mapping from Python types to BSON types is as follows: - -======================================= ============= =================== -Python Type BSON Type Supported Direction -======================================= ============= =================== -None null both -bool boolean both -int [#int]_ int32 / int64 py -> bson -:class:`bson.int64.Int64` int64 both -float number (real) both -str string both -list array both -dict object both -:class:`~bson.son.SON` object both -:py:class:`~collections.abc.Mapping` object py -> bson -:class:`~bson.raw_bson.RawBSONDocument` object both [#raw]_ -datetime.datetime [#dt]_ [#dt2]_ UTC datetime both -:class:`~bson.datetime_ms.DatetimeMS` UTC datetime both [#dt3]_ -:class:`~bson.regex.Regex` regex both -compiled re [#re]_ regex py -> bson -:class:`~bson.binary.Binary` binary both -:py:class:`uuid.UUID` [#uuid]_ binary both -:class:`~bson.objectid.ObjectId` oid both -:class:`~bson.dbref.DBRef` dbref both -:class:`~bson.dbref.DBRef` dbpointer bson -> py -None undefined bson -> py -:class:`~bson.code.Code` code both -str symbol bson -> py -bytes [#bytes]_ binary both -:class:`~bson.timestamp.Timestamp` timestamp both -:class:`~bson.decimal128.Decimal128` decimal128 both -:class:`~bson.min_key.MinKey` min key both -:class:`~bson.max_key.MaxKey` max key both -======================================= ============= =================== - -.. [#int] A Python int will be saved as a BSON int32 or BSON int64 depending - on its size. A BSON int32 will always decode to a Python int. A BSON - int64 will always decode to a :class:`~bson.int64.Int64`. -.. [#raw] Decoding a bson object to :class:`~bson.raw_bson.RawBSONDocument` can be - optionally configured via :attr:`~bson.codec_options.CodecOptions.document_class`. -.. [#dt] datetime.datetime instances are encoded with millisecond precision so - the microsecond field is truncated. -.. [#dt2] all datetime.datetime instances are encoded as UTC. By default, they - are decoded as *naive* but timezone aware datetimes are also supported. - See :doc:`/examples/datetimes` for examples. -.. [#dt3] To enable decoding a bson UTC datetime to a :class:`~bson.datetime_ms.DatetimeMS` - instance see :ref:`handling-out-of-range-datetimes`. -.. [#uuid] For :py:class:`uuid.UUID` encoding and decoding behavior see :doc:`/examples/uuid`. -.. [#re] :class:`~bson.regex.Regex` instances and regular expression - objects from ``re.compile()`` are both saved as BSON regular expressions. - BSON regular expressions are decoded as :class:`~bson.regex.Regex` - instances. -.. [#bytes] The bytes type is encoded as BSON binary with - subtype 0. It will be decoded back to bytes. -""" -from __future__ import annotations - -import datetime -import itertools -import os -import re -import struct -import sys -import uuid -from codecs import utf_8_decode as _utf_8_decode -from codecs import utf_8_encode as _utf_8_encode -from collections import abc as _abc -from typing import ( - IO, - TYPE_CHECKING, - Any, - BinaryIO, - Callable, - Generator, - Iterator, - Mapping, - MutableMapping, - NoReturn, - Optional, - Sequence, - Tuple, - Type, - TypeVar, - Union, - cast, - overload, -) - -from bson.binary import ( - ALL_UUID_SUBTYPES, - CSHARP_LEGACY, - JAVA_LEGACY, - OLD_UUID_SUBTYPE, - STANDARD, - UUID_SUBTYPE, - Binary, - UuidRepresentation, -) -from bson.code import Code -from bson.codec_options import ( - DEFAULT_CODEC_OPTIONS, - CodecOptions, - DatetimeConversion, - _raw_document_class, -) -from bson.datetime_ms import ( - EPOCH_AWARE, - EPOCH_NAIVE, - DatetimeMS, - _datetime_to_millis, - _millis_to_datetime, -) -from bson.dbref import DBRef -from bson.decimal128 import Decimal128 -from bson.errors import InvalidBSON, InvalidDocument, InvalidStringData -from bson.int64 import Int64 -from bson.max_key import MaxKey -from bson.min_key import MinKey -from bson.objectid import ObjectId -from bson.regex import Regex -from bson.son import RE_TYPE, SON -from bson.timestamp import Timestamp -from bson.tz_util import utc - -# Import some modules for type-checking only. -if TYPE_CHECKING: - from bson.raw_bson import RawBSONDocument - from bson.typings import _DocumentType, _ReadableBuffer - -try: - from bson import _cbson # type: ignore[attr-defined] - - _USE_C = True -except ImportError: - _USE_C = False - -__all__ = [ - "ALL_UUID_SUBTYPES", - "CSHARP_LEGACY", - "JAVA_LEGACY", - "OLD_UUID_SUBTYPE", - "STANDARD", - "UUID_SUBTYPE", - "Binary", - "UuidRepresentation", - "Code", - "DEFAULT_CODEC_OPTIONS", - "CodecOptions", - "DBRef", - "Decimal128", - "InvalidBSON", - "InvalidDocument", - "InvalidStringData", - "Int64", - "MaxKey", - "MinKey", - "ObjectId", - "Regex", - "RE_TYPE", - "SON", - "Timestamp", - "utc", - "EPOCH_AWARE", - "EPOCH_NAIVE", - "BSONNUM", - "BSONSTR", - "BSONOBJ", - "BSONARR", - "BSONBIN", - "BSONUND", - "BSONOID", - "BSONBOO", - "BSONDAT", - "BSONNUL", - "BSONRGX", - "BSONREF", - "BSONCOD", - "BSONSYM", - "BSONCWS", - "BSONINT", - "BSONTIM", - "BSONLON", - "BSONDEC", - "BSONMIN", - "BSONMAX", - "get_data_and_view", - "gen_list_name", - "encode", - "decode", - "decode_all", - "decode_iter", - "decode_file_iter", - "is_valid", - "BSON", - "has_c", - "DatetimeConversion", - "DatetimeMS", -] - -BSONNUM = b"\x01" # Floating point -BSONSTR = b"\x02" # UTF-8 string -BSONOBJ = b"\x03" # Embedded document -BSONARR = b"\x04" # Array -BSONBIN = b"\x05" # Binary -BSONUND = b"\x06" # Undefined -BSONOID = b"\x07" # ObjectId -BSONBOO = b"\x08" # Boolean -BSONDAT = b"\x09" # UTC Datetime -BSONNUL = b"\x0A" # Null -BSONRGX = b"\x0B" # Regex -BSONREF = b"\x0C" # DBRef -BSONCOD = b"\x0D" # Javascript code -BSONSYM = b"\x0E" # Symbol -BSONCWS = b"\x0F" # Javascript code with scope -BSONINT = b"\x10" # 32bit int -BSONTIM = b"\x11" # Timestamp -BSONLON = b"\x12" # 64bit int -BSONDEC = b"\x13" # Decimal128 -BSONMIN = b"\xFF" # Min key -BSONMAX = b"\x7F" # Max key - - -_UNPACK_FLOAT_FROM = struct.Struct(" Tuple[Any, memoryview]: - if isinstance(data, (bytes, bytearray)): - return data, memoryview(data) - view = memoryview(data) - return view.tobytes(), view - - -def _raise_unknown_type(element_type: int, element_name: str) -> NoReturn: - """Unknown type helper.""" - raise InvalidBSON( - "Detected unknown BSON type {!r} for fieldname '{}'. Are " - "you using the latest driver version?".format(chr(element_type).encode(), element_name) - ) - - -def _get_int( - data: Any, _view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any -) -> Tuple[int, int]: - """Decode a BSON int32 to python int.""" - return _UNPACK_INT_FROM(data, position)[0], position + 4 - - -def _get_c_string(data: Any, view: Any, position: int, opts: CodecOptions[Any]) -> Tuple[str, int]: - """Decode a BSON 'C' string to python str.""" - end = data.index(b"\x00", position) - return _utf_8_decode(view[position:end], opts.unicode_decode_error_handler, True)[0], end + 1 - - -def _get_float( - data: Any, _view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any -) -> Tuple[float, int]: - """Decode a BSON double to python float.""" - return _UNPACK_FLOAT_FROM(data, position)[0], position + 8 - - -def _get_string( - data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions[Any], dummy: Any -) -> Tuple[str, int]: - """Decode a BSON string to python str.""" - length = _UNPACK_INT_FROM(data, position)[0] - position += 4 - if length < 1 or obj_end - position < length: - raise InvalidBSON("invalid string length") - end = position + length - 1 - if data[end] != 0: - raise InvalidBSON("invalid end of string") - return _utf_8_decode(view[position:end], opts.unicode_decode_error_handler, True)[0], end + 1 - - -def _get_object_size(data: Any, position: int, obj_end: int) -> Tuple[int, int]: - """Validate and return a BSON document's size.""" - try: - obj_size = _UNPACK_INT_FROM(data, position)[0] - except struct.error as exc: - raise InvalidBSON(str(exc)) from None - end = position + obj_size - 1 - if end >= obj_end: - raise InvalidBSON("invalid object length") - if data[end] != 0: - raise InvalidBSON("bad eoo") - # If this is the top-level document, validate the total size too. - if position == 0 and obj_size != obj_end: - raise InvalidBSON("invalid object length") - return obj_size, end - - -def _get_object( - data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions[Any], dummy: Any -) -> Tuple[Any, int]: - """Decode a BSON subdocument to opts.document_class or bson.dbref.DBRef.""" - obj_size, end = _get_object_size(data, position, obj_end) - if _raw_document_class(opts.document_class): - return (opts.document_class(data[position : end + 1], opts), position + obj_size) - - obj = _elements_to_dict(data, view, position + 4, end, opts) - - position += obj_size - # If DBRef validation fails, return a normal doc. - if ( - isinstance(obj.get("$ref"), str) - and "$id" in obj - and isinstance(obj.get("$db"), (str, type(None))) - ): - return (DBRef(obj.pop("$ref"), obj.pop("$id", None), obj.pop("$db", None), obj), position) - return obj, position - - -def _get_array( - data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions[Any], element_name: str -) -> Tuple[Any, int]: - """Decode a BSON array to python list.""" - size = _UNPACK_INT_FROM(data, position)[0] - end = position + size - 1 - if data[end] != 0: - raise InvalidBSON("bad eoo") - - position += 4 - end -= 1 - result: list[Any] = [] - - # Avoid doing global and attribute lookups in the loop. - append = result.append - index = data.index - getter = _ELEMENT_GETTER - decoder_map = opts.type_registry._decoder_map - - while position < end: - element_type = data[position] - # Just skip the keys. - position = index(b"\x00", position) + 1 - try: - value, position = getter[element_type]( - data, view, position, obj_end, opts, element_name - ) - except KeyError: - _raise_unknown_type(element_type, element_name) - - if decoder_map: - custom_decoder = decoder_map.get(type(value)) - if custom_decoder is not None: - value = custom_decoder(value) - - append(value) - - if position != end + 1: - raise InvalidBSON("bad array length") - return result, position + 1 - - -def _get_binary( - data: Any, _view: Any, position: int, obj_end: int, opts: CodecOptions[Any], dummy1: Any -) -> Tuple[Union[Binary, uuid.UUID], int]: - """Decode a BSON binary to bson.binary.Binary or python UUID.""" - length, subtype = _UNPACK_LENGTH_SUBTYPE_FROM(data, position) - position += 5 - if subtype == 2: - length2 = _UNPACK_INT_FROM(data, position)[0] - position += 4 - if length2 != length - 4: - raise InvalidBSON("invalid binary (st 2) - lengths don't match!") - length = length2 - end = position + length - if length < 0 or end > obj_end: - raise InvalidBSON("bad binary object length") - - # Convert UUID subtypes to native UUIDs. - if subtype in ALL_UUID_SUBTYPES: - uuid_rep = opts.uuid_representation - binary_value = Binary(data[position:end], subtype) - if ( - (uuid_rep == UuidRepresentation.UNSPECIFIED) - or (subtype == UUID_SUBTYPE and uuid_rep != STANDARD) - or (subtype == OLD_UUID_SUBTYPE and uuid_rep == STANDARD) - ): - return binary_value, end - return binary_value.as_uuid(uuid_rep), end - - # Decode subtype 0 to 'bytes'. - if subtype == 0: - value = data[position:end] - else: - value = Binary(data[position:end], subtype) - - return value, end - - -def _get_oid( - data: Any, _view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any -) -> Tuple[ObjectId, int]: - """Decode a BSON ObjectId to bson.objectid.ObjectId.""" - end = position + 12 - return ObjectId(data[position:end]), end - - -def _get_boolean( - data: Any, _view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any -) -> Tuple[bool, int]: - """Decode a BSON true/false to python True/False.""" - end = position + 1 - boolean_byte = data[position:end] - if boolean_byte == b"\x00": - return False, end - elif boolean_byte == b"\x01": - return True, end - raise InvalidBSON("invalid boolean value: %r" % boolean_byte) - - -def _get_date( - data: Any, _view: Any, position: int, dummy0: int, opts: CodecOptions[Any], dummy1: Any -) -> Tuple[Union[datetime.datetime, DatetimeMS], int]: - """Decode a BSON datetime to python datetime.datetime.""" - return _millis_to_datetime(_UNPACK_LONG_FROM(data, position)[0], opts), position + 8 - - -def _get_code( - data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions[Any], element_name: str -) -> Tuple[Code, int]: - """Decode a BSON code to bson.code.Code.""" - code, position = _get_string(data, view, position, obj_end, opts, element_name) - return Code(code), position - - -def _get_code_w_scope( - data: Any, view: Any, position: int, _obj_end: int, opts: CodecOptions[Any], element_name: str -) -> Tuple[Code, int]: - """Decode a BSON code_w_scope to bson.code.Code.""" - code_end = position + _UNPACK_INT_FROM(data, position)[0] - code, position = _get_string(data, view, position + 4, code_end, opts, element_name) - scope, position = _get_object(data, view, position, code_end, opts, element_name) - if position != code_end: - raise InvalidBSON("scope outside of javascript code boundaries") - return Code(code, scope), position - - -def _get_regex( - data: Any, view: Any, position: int, dummy0: Any, opts: CodecOptions[Any], dummy1: Any -) -> Tuple[Regex[Any], int]: - """Decode a BSON regex to bson.regex.Regex or a python pattern object.""" - pattern, position = _get_c_string(data, view, position, opts) - bson_flags, position = _get_c_string(data, view, position, opts) - bson_re = Regex(pattern, bson_flags) - return bson_re, position - - -def _get_ref( - data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions[Any], element_name: str -) -> Tuple[DBRef, int]: - """Decode (deprecated) BSON DBPointer to bson.dbref.DBRef.""" - collection, position = _get_string(data, view, position, obj_end, opts, element_name) - oid, position = _get_oid(data, view, position, obj_end, opts, element_name) - return DBRef(collection, oid), position - - -def _get_timestamp( - data: Any, _view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any -) -> Tuple[Timestamp, int]: - """Decode a BSON timestamp to bson.timestamp.Timestamp.""" - inc, timestamp = _UNPACK_TIMESTAMP_FROM(data, position) - return Timestamp(timestamp, inc), position + 8 - - -def _get_int64( - data: Any, _view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any -) -> Tuple[Int64, int]: - """Decode a BSON int64 to bson.int64.Int64.""" - return Int64(_UNPACK_LONG_FROM(data, position)[0]), position + 8 - - -def _get_decimal128( - data: Any, _view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any -) -> Tuple[Decimal128, int]: - """Decode a BSON decimal128 to bson.decimal128.Decimal128.""" - end = position + 16 - return Decimal128.from_bid(data[position:end]), end - - -# Each decoder function's signature is: -# - data: bytes -# - view: memoryview that references `data` -# - position: int, beginning of object in 'data' to decode -# - obj_end: int, end of object to decode in 'data' if variable-length type -# - opts: a CodecOptions -_ELEMENT_GETTER: dict[int, Callable[..., Tuple[Any, int]]] = { - ord(BSONNUM): _get_float, - ord(BSONSTR): _get_string, - ord(BSONOBJ): _get_object, - ord(BSONARR): _get_array, - ord(BSONBIN): _get_binary, - ord(BSONUND): lambda u, v, w, x, y, z: (None, w), # noqa: ARG005 # Deprecated undefined - ord(BSONOID): _get_oid, - ord(BSONBOO): _get_boolean, - ord(BSONDAT): _get_date, - ord(BSONNUL): lambda u, v, w, x, y, z: (None, w), # noqa: ARG005 - ord(BSONRGX): _get_regex, - ord(BSONREF): _get_ref, # Deprecated DBPointer - ord(BSONCOD): _get_code, - ord(BSONSYM): _get_string, # Deprecated symbol - ord(BSONCWS): _get_code_w_scope, - ord(BSONINT): _get_int, - ord(BSONTIM): _get_timestamp, - ord(BSONLON): _get_int64, - ord(BSONDEC): _get_decimal128, - ord(BSONMIN): lambda u, v, w, x, y, z: (MinKey(), w), # noqa: ARG005 - ord(BSONMAX): lambda u, v, w, x, y, z: (MaxKey(), w), # noqa: ARG005 -} - - -if _USE_C: - - def _element_to_dict( - data: Any, - view: Any, # noqa: ARG001 - position: int, - obj_end: int, - opts: CodecOptions[Any], - raw_array: bool = False, - ) -> Tuple[str, Any, int]: - return cast( - "Tuple[str, Any, int]", - _cbson._element_to_dict(data, position, obj_end, opts, raw_array), - ) - -else: - - def _element_to_dict( - data: Any, - view: Any, - position: int, - obj_end: int, - opts: CodecOptions[Any], - raw_array: bool = False, - ) -> Tuple[str, Any, int]: - """Decode a single key, value pair.""" - element_type = data[position] - position += 1 - element_name, position = _get_c_string(data, view, position, opts) - if raw_array and element_type == ord(BSONARR): - _, end = _get_object_size(data, position, len(data)) - return element_name, view[position : end + 1], end + 1 - try: - value, position = _ELEMENT_GETTER[element_type]( - data, view, position, obj_end, opts, element_name - ) - except KeyError: - _raise_unknown_type(element_type, element_name) - - if opts.type_registry._decoder_map: - custom_decoder = opts.type_registry._decoder_map.get(type(value)) - if custom_decoder is not None: - value = custom_decoder(value) - - return element_name, value, position - - -_T = TypeVar("_T", bound=MutableMapping[str, Any]) - - -def _raw_to_dict( - data: Any, - position: int, - obj_end: int, - opts: CodecOptions[RawBSONDocument], - result: _T, - raw_array: bool = False, -) -> _T: - data, view = get_data_and_view(data) - return cast( - _T, _elements_to_dict(data, view, position, obj_end, opts, result, raw_array=raw_array) - ) - - -def _elements_to_dict( - data: Any, - view: Any, - position: int, - obj_end: int, - opts: CodecOptions[Any], - result: Any = None, - raw_array: bool = False, -) -> Any: - """Decode a BSON document into result.""" - if result is None: - result = opts.document_class() - end = obj_end - 1 - while position < end: - key, value, position = _element_to_dict( - data, view, position, obj_end, opts, raw_array=raw_array - ) - result[key] = value - if position != obj_end: - raise InvalidBSON("bad object or element length") - return result - - -def _bson_to_dict(data: Any, opts: CodecOptions[_DocumentType]) -> _DocumentType: - """Decode a BSON string to document_class.""" - data, view = get_data_and_view(data) - try: - if _raw_document_class(opts.document_class): - return opts.document_class(data, opts) # type:ignore[call-arg] - _, end = _get_object_size(data, 0, len(data)) - return cast("_DocumentType", _elements_to_dict(data, view, 4, end, opts)) - except InvalidBSON: - raise - except Exception: - # Change exception type to InvalidBSON but preserve traceback. - _, exc_value, exc_tb = sys.exc_info() - raise InvalidBSON(str(exc_value)).with_traceback(exc_tb) from None - - -if _USE_C: - _bson_to_dict = _cbson._bson_to_dict - - -_PACK_FLOAT = struct.Struct(" Generator[bytes, None, None]: - """Generate "keys" for encoded lists in the sequence - b"0\x00", b"1\x00", b"2\x00", ... - - The first 1000 keys are returned from a pre-built cache. All - subsequent keys are generated on the fly. - """ - yield from _LIST_NAMES - - counter = itertools.count(1000) - while True: - yield (str(next(counter)) + "\x00").encode("utf8") - - -def _make_c_string_check(string: Union[str, bytes]) -> bytes: - """Make a 'C' string, checking for embedded NUL characters.""" - if isinstance(string, bytes): - if b"\x00" in string: - raise InvalidDocument("BSON keys / regex patterns must not contain a NUL character") - try: - _utf_8_decode(string, None, True) - return string + b"\x00" - except UnicodeError: - raise InvalidStringData( - "strings in documents must be valid UTF-8: %r" % string - ) from None - else: - if "\x00" in string: - raise InvalidDocument("BSON keys / regex patterns must not contain a NUL character") - return _utf_8_encode(string)[0] + b"\x00" - - -def _make_c_string(string: Union[str, bytes]) -> bytes: - """Make a 'C' string.""" - if isinstance(string, bytes): - try: - _utf_8_decode(string, None, True) - return string + b"\x00" - except UnicodeError: - raise InvalidStringData( - "strings in documents must be valid UTF-8: %r" % string - ) from None - else: - return _utf_8_encode(string)[0] + b"\x00" - - -def _make_name(string: str) -> bytes: - """Make a 'C' string suitable for a BSON key.""" - if "\x00" in string: - raise InvalidDocument("BSON keys must not contain a NUL character") - return _utf_8_encode(string)[0] + b"\x00" - - -def _encode_float(name: bytes, value: float, dummy0: Any, dummy1: Any) -> bytes: - """Encode a float.""" - return b"\x01" + name + _PACK_FLOAT(value) - - -def _encode_bytes(name: bytes, value: bytes, dummy0: Any, dummy1: Any) -> bytes: - """Encode a python bytes.""" - # Python3 special case. Store 'bytes' as BSON binary subtype 0. - return b"\x05" + name + _PACK_INT(len(value)) + b"\x00" + value - - -def _encode_mapping(name: bytes, value: Any, check_keys: bool, opts: CodecOptions[Any]) -> bytes: - """Encode a mapping type.""" - if _raw_document_class(value): - return b"\x03" + name + cast(bytes, value.raw) - data = b"".join([_element_to_bson(key, val, check_keys, opts) for key, val in value.items()]) - return b"\x03" + name + _PACK_INT(len(data) + 5) + data + b"\x00" - - -def _encode_dbref(name: bytes, value: DBRef, check_keys: bool, opts: CodecOptions[Any]) -> bytes: - """Encode bson.dbref.DBRef.""" - buf = bytearray(b"\x03" + name + b"\x00\x00\x00\x00") - begin = len(buf) - 4 - - buf += _name_value_to_bson(b"$ref\x00", value.collection, check_keys, opts) - buf += _name_value_to_bson(b"$id\x00", value.id, check_keys, opts) - if value.database is not None: - buf += _name_value_to_bson(b"$db\x00", value.database, check_keys, opts) - for key, val in value._DBRef__kwargs.items(): - buf += _element_to_bson(key, val, check_keys, opts) - - buf += b"\x00" - buf[begin : begin + 4] = _PACK_INT(len(buf) - begin) - return bytes(buf) - - -def _encode_list( - name: bytes, value: Sequence[Any], check_keys: bool, opts: CodecOptions[Any] -) -> bytes: - """Encode a list/tuple.""" - lname = gen_list_name() - data = b"".join([_name_value_to_bson(next(lname), item, check_keys, opts) for item in value]) - return b"\x04" + name + _PACK_INT(len(data) + 5) + data + b"\x00" - - -def _encode_text(name: bytes, value: str, dummy0: Any, dummy1: Any) -> bytes: - """Encode a python str.""" - bvalue = _utf_8_encode(value)[0] - return b"\x02" + name + _PACK_INT(len(bvalue) + 1) + bvalue + b"\x00" - - -def _encode_binary(name: bytes, value: Binary, dummy0: Any, dummy1: Any) -> bytes: - """Encode bson.binary.Binary.""" - subtype = value.subtype - if subtype == 2: - value = _PACK_INT(len(value)) + value # type: ignore - return b"\x05" + name + _PACK_LENGTH_SUBTYPE(len(value), subtype) + value - - -def _encode_uuid(name: bytes, value: uuid.UUID, dummy: Any, opts: CodecOptions[Any]) -> bytes: - """Encode uuid.UUID.""" - uuid_representation = opts.uuid_representation - binval = Binary.from_uuid(value, uuid_representation=uuid_representation) - return _encode_binary(name, binval, dummy, opts) - - -def _encode_objectid(name: bytes, value: ObjectId, dummy: Any, dummy1: Any) -> bytes: - """Encode bson.objectid.ObjectId.""" - return b"\x07" + name + value.binary - - -def _encode_bool(name: bytes, value: bool, dummy0: Any, dummy1: Any) -> bytes: - """Encode a python boolean (True/False).""" - return b"\x08" + name + (value and b"\x01" or b"\x00") - - -def _encode_datetime(name: bytes, value: datetime.datetime, dummy0: Any, dummy1: Any) -> bytes: - """Encode datetime.datetime.""" - millis = _datetime_to_millis(value) - return b"\x09" + name + _PACK_LONG(millis) - - -def _encode_datetime_ms(name: bytes, value: DatetimeMS, dummy0: Any, dummy1: Any) -> bytes: - """Encode datetime.datetime.""" - millis = int(value) - return b"\x09" + name + _PACK_LONG(millis) - - -def _encode_none(name: bytes, dummy0: Any, dummy1: Any, dummy2: Any) -> bytes: - """Encode python None.""" - return b"\x0A" + name - - -def _encode_regex(name: bytes, value: Regex[Any], dummy0: Any, dummy1: Any) -> bytes: - """Encode a python regex or bson.regex.Regex.""" - flags = value.flags - # Python 3 common case - if flags == re.UNICODE: - return b"\x0B" + name + _make_c_string_check(value.pattern) + b"u\x00" - elif flags == 0: - return b"\x0B" + name + _make_c_string_check(value.pattern) + b"\x00" - else: - sflags = b"" - if flags & re.IGNORECASE: - sflags += b"i" - if flags & re.LOCALE: - sflags += b"l" - if flags & re.MULTILINE: - sflags += b"m" - if flags & re.DOTALL: - sflags += b"s" - if flags & re.UNICODE: - sflags += b"u" - if flags & re.VERBOSE: - sflags += b"x" - sflags += b"\x00" - return b"\x0B" + name + _make_c_string_check(value.pattern) + sflags - - -def _encode_code(name: bytes, value: Code, dummy: Any, opts: CodecOptions[Any]) -> bytes: - """Encode bson.code.Code.""" - cstring = _make_c_string(value) - cstrlen = len(cstring) - if value.scope is None: - return b"\x0D" + name + _PACK_INT(cstrlen) + cstring - scope = _dict_to_bson(value.scope, False, opts, False) - full_length = _PACK_INT(8 + cstrlen + len(scope)) - return b"\x0F" + name + full_length + _PACK_INT(cstrlen) + cstring + scope - - -def _encode_int(name: bytes, value: int, dummy0: Any, dummy1: Any) -> bytes: - """Encode a python int.""" - if -2147483648 <= value <= 2147483647: - return b"\x10" + name + _PACK_INT(value) - else: - try: - return b"\x12" + name + _PACK_LONG(value) - except struct.error: - raise OverflowError("BSON can only handle up to 8-byte ints") from None - - -def _encode_timestamp(name: bytes, value: Any, dummy0: Any, dummy1: Any) -> bytes: - """Encode bson.timestamp.Timestamp.""" - return b"\x11" + name + _PACK_TIMESTAMP(value.inc, value.time) - - -def _encode_long(name: bytes, value: Any, dummy0: Any, dummy1: Any) -> bytes: - """Encode a bson.int64.Int64.""" - try: - return b"\x12" + name + _PACK_LONG(value) - except struct.error: - raise OverflowError("BSON can only handle up to 8-byte ints") from None - - -def _encode_decimal128(name: bytes, value: Decimal128, dummy0: Any, dummy1: Any) -> bytes: - """Encode bson.decimal128.Decimal128.""" - return b"\x13" + name + value.bid - - -def _encode_minkey(name: bytes, dummy0: Any, dummy1: Any, dummy2: Any) -> bytes: - """Encode bson.min_key.MinKey.""" - return b"\xFF" + name - - -def _encode_maxkey(name: bytes, dummy0: Any, dummy1: Any, dummy2: Any) -> bytes: - """Encode bson.max_key.MaxKey.""" - return b"\x7F" + name - - -# Each encoder function's signature is: -# - name: utf-8 bytes -# - value: a Python data type, e.g. a Python int for _encode_int -# - check_keys: bool, whether to check for invalid names -# - opts: a CodecOptions -_ENCODERS = { - bool: _encode_bool, - bytes: _encode_bytes, - datetime.datetime: _encode_datetime, - DatetimeMS: _encode_datetime_ms, - dict: _encode_mapping, - float: _encode_float, - int: _encode_int, - list: _encode_list, - str: _encode_text, - tuple: _encode_list, - type(None): _encode_none, - uuid.UUID: _encode_uuid, - Binary: _encode_binary, - Int64: _encode_long, - Code: _encode_code, - DBRef: _encode_dbref, - MaxKey: _encode_maxkey, - MinKey: _encode_minkey, - ObjectId: _encode_objectid, - Regex: _encode_regex, - RE_TYPE: _encode_regex, - SON: _encode_mapping, - Timestamp: _encode_timestamp, - Decimal128: _encode_decimal128, - # Special case. This will never be looked up directly. - _abc.Mapping: _encode_mapping, -} - -# Map each _type_marker to its encoder for faster lookup. -_MARKERS = {} -for _typ in _ENCODERS: - if hasattr(_typ, "_type_marker"): - _MARKERS[_typ._type_marker] = _ENCODERS[_typ] - - -_BUILT_IN_TYPES = tuple(t for t in _ENCODERS) - - -def _name_value_to_bson( - name: bytes, - value: Any, - check_keys: bool, - opts: CodecOptions[Any], - in_custom_call: bool = False, - in_fallback_call: bool = False, -) -> bytes: - """Encode a single name, value pair.""" - - was_integer_overflow = False - - # First see if the type is already cached. KeyError will only ever - # happen once per subtype. - try: - return _ENCODERS[type(value)](name, value, check_keys, opts) # type: ignore - except KeyError: - pass - except OverflowError: - if not isinstance(value, int): - raise - - # Give the fallback_encoder a chance - was_integer_overflow = True - - # Second, fall back to trying _type_marker. This has to be done - # before the loop below since users could subclass one of our - # custom types that subclasses a python built-in (e.g. Binary) - marker = getattr(value, "_type_marker", None) - if isinstance(marker, int) and marker in _MARKERS: - func = _MARKERS[marker] - # Cache this type for faster subsequent lookup. - _ENCODERS[type(value)] = func - return func(name, value, check_keys, opts) # type: ignore - - # Third, check if a type encoder is registered for this type. - # Note that subtypes of registered custom types are not auto-encoded. - if not in_custom_call and opts.type_registry._encoder_map: - custom_encoder = opts.type_registry._encoder_map.get(type(value)) - if custom_encoder is not None: - return _name_value_to_bson( - name, custom_encoder(value), check_keys, opts, in_custom_call=True - ) - - # Fourth, test each base type. This will only happen once for - # a subtype of a supported base type. Unlike in the C-extensions, this - # is done after trying the custom type encoder because checking for each - # subtype is expensive. - for base in _BUILT_IN_TYPES: - if not was_integer_overflow and isinstance(value, base): - func = _ENCODERS[base] - # Cache this type for faster subsequent lookup. - _ENCODERS[type(value)] = func - return func(name, value, check_keys, opts) # type: ignore - - # As a last resort, try using the fallback encoder, if the user has - # provided one. - fallback_encoder = opts.type_registry._fallback_encoder - if not in_fallback_call and fallback_encoder is not None: - return _name_value_to_bson( - name, fallback_encoder(value), check_keys, opts, in_fallback_call=True - ) - - if was_integer_overflow: - raise OverflowError("BSON can only handle up to 8-byte ints") - raise InvalidDocument(f"cannot encode object: {value!r}, of type: {type(value)!r}") - - -def _element_to_bson(key: Any, value: Any, check_keys: bool, opts: CodecOptions[Any]) -> bytes: - """Encode a single key, value pair.""" - if not isinstance(key, str): - raise InvalidDocument(f"documents must have only string keys, key was {key!r}") - if check_keys: - if key.startswith("$"): - raise InvalidDocument(f"key {key!r} must not start with '$'") - if "." in key: - raise InvalidDocument(f"key {key!r} must not contain '.'") - - name = _make_name(key) - return _name_value_to_bson(name, value, check_keys, opts) - - -def _dict_to_bson( - doc: Any, check_keys: bool, opts: CodecOptions[Any], top_level: bool = True -) -> bytes: - """Encode a document to BSON.""" - if _raw_document_class(doc): - return cast(bytes, doc.raw) - try: - elements = [] - if top_level and "_id" in doc: - elements.append(_name_value_to_bson(b"_id\x00", doc["_id"], check_keys, opts)) - for key, value in doc.items(): - if not top_level or key != "_id": - try: - elements.append(_element_to_bson(key, value, check_keys, opts)) - except InvalidDocument as err: - raise InvalidDocument(f"Invalid document {doc} | {err}") from err - except AttributeError: - raise TypeError(f"encoder expected a mapping type but got: {doc!r}") from None - - encoded = b"".join(elements) - return _PACK_INT(len(encoded) + 5) + encoded + b"\x00" - - -if _USE_C: - _dict_to_bson = _cbson._dict_to_bson - - -_CODEC_OPTIONS_TYPE_ERROR = TypeError("codec_options must be an instance of CodecOptions") - - -def encode( - document: Mapping[str, Any], - check_keys: bool = False, - codec_options: CodecOptions[Any] = DEFAULT_CODEC_OPTIONS, -) -> bytes: - """Encode a document to BSON. - - A document can be any mapping type (like :class:`dict`). - - Raises :class:`TypeError` if `document` is not a mapping type, - or contains keys that are not instances of :class:`str`. Raises - :class:`~bson.errors.InvalidDocument` if `document` cannot be - converted to :class:`BSON`. - - :param document: mapping type representing a document - :param check_keys: check if keys start with '$' or - contain '.', raising :class:`~bson.errors.InvalidDocument` in - either case - :param codec_options: An instance of - :class:`~bson.codec_options.CodecOptions`. - - .. versionadded:: 3.9 - """ - if not isinstance(codec_options, CodecOptions): - raise _CODEC_OPTIONS_TYPE_ERROR - - return _dict_to_bson(document, check_keys, codec_options) - - -@overload -def decode(data: _ReadableBuffer, codec_options: None = None) -> dict[str, Any]: - ... - - -@overload -def decode(data: _ReadableBuffer, codec_options: CodecOptions[_DocumentType]) -> _DocumentType: - ... - - -def decode( - data: _ReadableBuffer, codec_options: Optional[CodecOptions[_DocumentType]] = None -) -> Union[dict[str, Any], _DocumentType]: - """Decode BSON to a document. - - By default, returns a BSON document represented as a Python - :class:`dict`. To use a different :class:`MutableMapping` class, - configure a :class:`~bson.codec_options.CodecOptions`:: - - >>> import collections # From Python standard library. - >>> import bson - >>> from bson.codec_options import CodecOptions - >>> data = bson.encode({'a': 1}) - >>> decoded_doc = bson.decode(data) - - >>> options = CodecOptions(document_class=collections.OrderedDict) - >>> decoded_doc = bson.decode(data, codec_options=options) - >>> type(decoded_doc) - - - :param data: the BSON to decode. Any bytes-like object that implements - the buffer protocol. - :param codec_options: An instance of - :class:`~bson.codec_options.CodecOptions`. - - .. versionadded:: 3.9 - """ - opts: CodecOptions[Any] = codec_options or DEFAULT_CODEC_OPTIONS - if not isinstance(opts, CodecOptions): - raise _CODEC_OPTIONS_TYPE_ERROR - - return cast("Union[dict[str, Any], _DocumentType]", _bson_to_dict(data, opts)) - - -def _decode_all(data: _ReadableBuffer, opts: CodecOptions[_DocumentType]) -> list[_DocumentType]: - """Decode a BSON data to multiple documents.""" - data, view = get_data_and_view(data) - data_len = len(data) - docs: list[_DocumentType] = [] - position = 0 - end = data_len - 1 - use_raw = _raw_document_class(opts.document_class) - try: - while position < end: - obj_size = _UNPACK_INT_FROM(data, position)[0] - if data_len - position < obj_size: - raise InvalidBSON("invalid object size") - obj_end = position + obj_size - 1 - if data[obj_end] != 0: - raise InvalidBSON("bad eoo") - if use_raw: - docs.append(opts.document_class(data[position : obj_end + 1], opts)) # type: ignore - else: - docs.append(_elements_to_dict(data, view, position + 4, obj_end, opts)) - position += obj_size - return docs - except InvalidBSON: - raise - except Exception: - # Change exception type to InvalidBSON but preserve traceback. - _, exc_value, exc_tb = sys.exc_info() - raise InvalidBSON(str(exc_value)).with_traceback(exc_tb) from None - - -if _USE_C: - _decode_all = _cbson._decode_all - - -@overload -def decode_all(data: _ReadableBuffer, codec_options: None = None) -> list[dict[str, Any]]: - ... - - -@overload -def decode_all( - data: _ReadableBuffer, codec_options: CodecOptions[_DocumentType] -) -> list[_DocumentType]: - ... - - -def decode_all( - data: _ReadableBuffer, codec_options: Optional[CodecOptions[_DocumentType]] = None -) -> Union[list[dict[str, Any]], list[_DocumentType]]: - """Decode BSON data to multiple documents. - - `data` must be a bytes-like object implementing the buffer protocol that - provides concatenated, valid, BSON-encoded documents. - - :param data: BSON data - :param codec_options: An instance of - :class:`~bson.codec_options.CodecOptions`. - - .. versionchanged:: 3.9 - Supports bytes-like objects that implement the buffer protocol. - - .. versionchanged:: 3.0 - Removed `compile_re` option: PyMongo now always represents BSON regular - expressions as :class:`~bson.regex.Regex` objects. Use - :meth:`~bson.regex.Regex.try_compile` to attempt to convert from a - BSON regular expression to a Python regular expression object. - - Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with - `codec_options`. - """ - if codec_options is None: - return _decode_all(data, DEFAULT_CODEC_OPTIONS) - - if not isinstance(codec_options, CodecOptions): - raise _CODEC_OPTIONS_TYPE_ERROR - - return _decode_all(data, codec_options) - - -def _decode_selective( - rawdoc: Any, fields: Any, codec_options: CodecOptions[_DocumentType] -) -> _DocumentType: - if _raw_document_class(codec_options.document_class): - # If document_class is RawBSONDocument, use vanilla dictionary for - # decoding command response. - doc: _DocumentType = {} # type:ignore[assignment] - else: - # Else, use the specified document_class. - doc = codec_options.document_class() - for key, value in rawdoc.items(): - if key in fields: - if fields[key] == 1: - doc[key] = _bson_to_dict(rawdoc.raw, codec_options)[key] # type:ignore[index] - else: - doc[key] = _decode_selective( # type:ignore[index] - value, fields[key], codec_options - ) - else: - doc[key] = value # type:ignore[index] - return doc - - -def _array_of_documents_to_buffer(data: Union[memoryview, bytes]) -> bytes: - # Extract the raw bytes of each document. - position = 0 - view = memoryview(data) - _, end = _get_object_size(view, position, len(view)) - position += 4 - buffers: list[memoryview] = [] - append = buffers.append - while position < end - 1: - # Just skip the keys. - while view[position] != 0: - position += 1 - position += 1 - obj_size, _ = _get_object_size(view, position, end) - append(view[position : position + obj_size]) - position += obj_size - if position != end: - raise InvalidBSON("bad object or element length") - return b"".join(buffers) - - -if _USE_C: - _array_of_documents_to_buffer = _cbson._array_of_documents_to_buffer - - -def _convert_raw_document_lists_to_streams(document: Any) -> None: - """Convert raw array of documents to a stream of BSON documents.""" - cursor = document.get("cursor") - if not cursor: - return - for key in ("firstBatch", "nextBatch"): - batch = cursor.get(key) - if not batch: - continue - data = _array_of_documents_to_buffer(batch) - if data: - cursor[key] = [data] - else: - cursor[key] = [] - - -def _decode_all_selective( - data: Any, codec_options: CodecOptions[_DocumentType], fields: Any -) -> list[_DocumentType]: - """Decode BSON data to a single document while using user-provided - custom decoding logic. - - `data` must be a string representing a valid, BSON-encoded document. - - :param data: BSON data - :param codec_options: An instance of - :class:`~bson.codec_options.CodecOptions` with user-specified type - decoders. If no decoders are found, this method is the same as - ``decode_all``. - :param fields: Map of document namespaces where data that needs - to be custom decoded lives or None. For example, to custom decode a - list of objects in 'field1.subfield1', the specified value should be - ``{'field1': {'subfield1': 1}}``. If ``fields`` is an empty map or - None, this method is the same as ``decode_all``. - - :return: Single-member list containing the decoded document. - - .. versionadded:: 3.8 - """ - if not codec_options.type_registry._decoder_map: - return decode_all(data, codec_options) - - if not fields: - return decode_all(data, codec_options.with_options(type_registry=None)) - - # Decode documents for internal use. - from bson.raw_bson import RawBSONDocument - - internal_codec_options: CodecOptions[RawBSONDocument] = codec_options.with_options( - document_class=RawBSONDocument, type_registry=None - ) - _doc = _bson_to_dict(data, internal_codec_options) - return [ - _decode_selective( - _doc, - fields, - codec_options, - ) - ] - - -@overload -def decode_iter(data: bytes, codec_options: None = None) -> Iterator[dict[str, Any]]: - ... - - -@overload -def decode_iter(data: bytes, codec_options: CodecOptions[_DocumentType]) -> Iterator[_DocumentType]: - ... - - -def decode_iter( - data: bytes, codec_options: Optional[CodecOptions[_DocumentType]] = None -) -> Union[Iterator[dict[str, Any]], Iterator[_DocumentType]]: - """Decode BSON data to multiple documents as a generator. - - Works similarly to the decode_all function, but yields one document at a - time. - - `data` must be a string of concatenated, valid, BSON-encoded - documents. - - :param data: BSON data - :param codec_options: An instance of - :class:`~bson.codec_options.CodecOptions`. - - .. versionchanged:: 3.0 - Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with - `codec_options`. - - .. versionadded:: 2.8 - """ - opts = codec_options or DEFAULT_CODEC_OPTIONS - if not isinstance(opts, CodecOptions): - raise _CODEC_OPTIONS_TYPE_ERROR - - position = 0 - end = len(data) - 1 - while position < end: - obj_size = _UNPACK_INT_FROM(data, position)[0] - elements = data[position : position + obj_size] - position += obj_size - - yield _bson_to_dict(elements, opts) # type:ignore[misc] - - -@overload -def decode_file_iter( - file_obj: Union[BinaryIO, IO[bytes]], codec_options: None = None -) -> Iterator[dict[str, Any]]: - ... - - -@overload -def decode_file_iter( - file_obj: Union[BinaryIO, IO[bytes]], codec_options: CodecOptions[_DocumentType] -) -> Iterator[_DocumentType]: - ... - - -def decode_file_iter( - file_obj: Union[BinaryIO, IO[bytes]], - codec_options: Optional[CodecOptions[_DocumentType]] = None, -) -> Union[Iterator[dict[str, Any]], Iterator[_DocumentType]]: - """Decode bson data from a file to multiple documents as a generator. - - Works similarly to the decode_all function, but reads from the file object - in chunks and parses bson in chunks, yielding one document at a time. - - :param file_obj: A file object containing BSON data. - :param codec_options: An instance of - :class:`~bson.codec_options.CodecOptions`. - - .. versionchanged:: 3.0 - Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with - `codec_options`. - - .. versionadded:: 2.8 - """ - opts = codec_options or DEFAULT_CODEC_OPTIONS - while True: - # Read size of next object. - size_data: Any = file_obj.read(4) - if not size_data: - break # Finished with file normally. - elif len(size_data) != 4: - raise InvalidBSON("cut off in middle of objsize") - obj_size = _UNPACK_INT_FROM(size_data, 0)[0] - 4 - elements = size_data + file_obj.read(max(0, obj_size)) - yield _bson_to_dict(elements, opts) # type:ignore[arg-type, misc] - - -def is_valid(bson: bytes) -> bool: - """Check that the given string represents valid :class:`BSON` data. - - Raises :class:`TypeError` if `bson` is not an instance of - :class:`bytes`. Returns ``True`` - if `bson` is valid :class:`BSON`, ``False`` otherwise. - - :param bson: the data to be validated - """ - if not isinstance(bson, bytes): - raise TypeError(f"BSON data must be an instance of a subclass of bytes, not {type(bson)}") - - try: - _bson_to_dict(bson, DEFAULT_CODEC_OPTIONS) - return True - except Exception: - return False - - -class BSON(bytes): - """BSON (Binary JSON) data. - - .. warning:: Using this class to encode and decode BSON adds a performance - cost. For better performance use the module level functions - :func:`encode` and :func:`decode` instead. - """ - - @classmethod - def encode( - cls: Type[BSON], - document: Mapping[str, Any], - check_keys: bool = False, - codec_options: CodecOptions[Any] = DEFAULT_CODEC_OPTIONS, - ) -> BSON: - """Encode a document to a new :class:`BSON` instance. - - A document can be any mapping type (like :class:`dict`). - - Raises :class:`TypeError` if `document` is not a mapping type, - or contains keys that are not instances of - :class:`str'. Raises :class:`~bson.errors.InvalidDocument` - if `document` cannot be converted to :class:`BSON`. - - :param document: mapping type representing a document - :param check_keys: check if keys start with '$' or - contain '.', raising :class:`~bson.errors.InvalidDocument` in - either case - :param codec_options: An instance of - :class:`~bson.codec_options.CodecOptions`. - - .. versionchanged:: 3.0 - Replaced `uuid_subtype` option with `codec_options`. - """ - return cls(encode(document, check_keys, codec_options)) - - def decode( # type:ignore[override] - self, codec_options: CodecOptions[Any] = DEFAULT_CODEC_OPTIONS - ) -> dict[str, Any]: - """Decode this BSON data. - - By default, returns a BSON document represented as a Python - :class:`dict`. To use a different :class:`MutableMapping` class, - configure a :class:`~bson.codec_options.CodecOptions`:: - - >>> import collections # From Python standard library. - >>> import bson - >>> from bson.codec_options import CodecOptions - >>> data = bson.BSON.encode({'a': 1}) - >>> decoded_doc = bson.BSON(data).decode() - - >>> options = CodecOptions(document_class=collections.OrderedDict) - >>> decoded_doc = bson.BSON(data).decode(codec_options=options) - >>> type(decoded_doc) - - - :param codec_options: An instance of - :class:`~bson.codec_options.CodecOptions`. - - .. versionchanged:: 3.0 - Removed `compile_re` option: PyMongo now always represents BSON - regular expressions as :class:`~bson.regex.Regex` objects. Use - :meth:`~bson.regex.Regex.try_compile` to attempt to convert from a - BSON regular expression to a Python regular expression object. - - Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with - `codec_options`. - """ - return decode(self, codec_options) - - -def has_c() -> bool: - """Is the C extension installed?""" - return _USE_C - - -def _after_fork() -> None: - """Releases the ObjectID lock child.""" - if ObjectId._inc_lock.locked(): - ObjectId._inc_lock.release() - - -if hasattr(os, "register_at_fork"): - # This will run in the same thread as the fork was called. - # If we fork in a critical region on the same thread, it should break. - # This is fine since we would never call fork directly from a critical region. - os.register_at_fork(after_in_child=_after_fork) diff --git a/venv/Lib/site-packages/bson/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index cbc0f6a..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/_helpers.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/_helpers.cpython-310.pyc deleted file mode 100644 index 05167db..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/_helpers.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/binary.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/binary.cpython-310.pyc deleted file mode 100644 index 99cdb47..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/binary.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/code.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/code.cpython-310.pyc deleted file mode 100644 index bc623d1..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/code.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/codec_options.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/codec_options.cpython-310.pyc deleted file mode 100644 index 03c8835..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/codec_options.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/datetime_ms.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/datetime_ms.cpython-310.pyc deleted file mode 100644 index 085df7d..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/datetime_ms.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/dbref.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/dbref.cpython-310.pyc deleted file mode 100644 index 2bd0cb9..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/dbref.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/decimal128.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/decimal128.cpython-310.pyc deleted file mode 100644 index 672b218..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/decimal128.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/errors.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/errors.cpython-310.pyc deleted file mode 100644 index b8c27ba..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/errors.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/int64.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/int64.cpython-310.pyc deleted file mode 100644 index cfcadfe..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/int64.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/json_util.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/json_util.cpython-310.pyc deleted file mode 100644 index ae2fd3b..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/json_util.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/max_key.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/max_key.cpython-310.pyc deleted file mode 100644 index 54c730f..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/max_key.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/min_key.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/min_key.cpython-310.pyc deleted file mode 100644 index 13db591..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/min_key.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/objectid.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/objectid.cpython-310.pyc deleted file mode 100644 index d7bfb80..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/objectid.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/raw_bson.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/raw_bson.cpython-310.pyc deleted file mode 100644 index 5eeb4e7..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/raw_bson.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/regex.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/regex.cpython-310.pyc deleted file mode 100644 index 6fe1577..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/regex.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/son.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/son.cpython-310.pyc deleted file mode 100644 index fd1a3ed..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/son.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/timestamp.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/timestamp.cpython-310.pyc deleted file mode 100644 index da55434..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/timestamp.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/typings.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/typings.cpython-310.pyc deleted file mode 100644 index 23e9811..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/typings.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/__pycache__/tz_util.cpython-310.pyc b/venv/Lib/site-packages/bson/__pycache__/tz_util.cpython-310.pyc deleted file mode 100644 index 06c2fbf..0000000 Binary files a/venv/Lib/site-packages/bson/__pycache__/tz_util.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/bson/_cbson.cp310-win_amd64.pyd b/venv/Lib/site-packages/bson/_cbson.cp310-win_amd64.pyd deleted file mode 100644 index 7b67155..0000000 Binary files a/venv/Lib/site-packages/bson/_cbson.cp310-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/bson/_cbson.cp39-win_amd64.pyd b/venv/Lib/site-packages/bson/_cbson.cp39-win_amd64.pyd deleted file mode 100644 index 8878993..0000000 Binary files a/venv/Lib/site-packages/bson/_cbson.cp39-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/bson/_cbsonmodule.c b/venv/Lib/site-packages/bson/_cbsonmodule.c deleted file mode 100644 index 672f5ee..0000000 --- a/venv/Lib/site-packages/bson/_cbsonmodule.c +++ /dev/null @@ -1,3267 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * This file contains C implementations of some of the functions - * needed by the bson module. If possible, these implementations - * should be used to speed up BSON encoding and decoding. - */ - -#define PY_SSIZE_T_CLEAN -#include "Python.h" -#include "datetime.h" - -#include "buffer.h" -#include "time64.h" - -#define _CBSON_MODULE -#include "_cbsonmodule.h" - -/* New module state and initialization code. - * See the module-initialization-and-state - * section in the following doc: - * http://docs.python.org/release/3.1.3/howto/cporting.html - * which references the following pep: - * http://www.python.org/dev/peps/pep-3121/ - * */ -struct module_state { - PyObject* Binary; - PyObject* Code; - PyObject* ObjectId; - PyObject* DBRef; - PyObject* Regex; - PyObject* UUID; - PyObject* Timestamp; - PyObject* MinKey; - PyObject* MaxKey; - PyObject* UTC; - PyTypeObject* REType; - PyObject* BSONInt64; - PyObject* Decimal128; - PyObject* Mapping; - PyObject* DatetimeMS; - PyObject* min_datetime; - PyObject* max_datetime; - PyObject* replace_args; - PyObject* replace_kwargs; - PyObject* _type_marker_str; - PyObject* _flags_str; - PyObject* _pattern_str; - PyObject* _encoder_map_str; - PyObject* _decoder_map_str; - PyObject* _fallback_encoder_str; - PyObject* _raw_str; - PyObject* _subtype_str; - PyObject* _binary_str; - PyObject* _scope_str; - PyObject* _inc_str; - PyObject* _time_str; - PyObject* _bid_str; - PyObject* _replace_str; - PyObject* _astimezone_str; - PyObject* _id_str; - PyObject* _dollar_ref_str; - PyObject* _dollar_id_str; - PyObject* _dollar_db_str; - PyObject* _tzinfo_str; - PyObject* _as_doc_str; - PyObject* _utcoffset_str; - PyObject* _from_uuid_str; - PyObject* _as_uuid_str; - PyObject* _from_bid_str; - int64_t min_millis; - int64_t max_millis; -}; - -#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) - -/* Maximum number of regex flags */ -#define FLAGS_SIZE 7 - -/* Default UUID representation type code. */ -#define PYTHON_LEGACY 3 - -/* Other UUID representations. */ -#define STANDARD 4 -#define JAVA_LEGACY 5 -#define CSHARP_LEGACY 6 -#define UNSPECIFIED 0 - -#define BSON_MAX_SIZE 2147483647 -/* The smallest possible BSON document, i.e. "{}" */ -#define BSON_MIN_SIZE 5 - -/* Datetime codec options */ -#define DATETIME 1 -#define DATETIME_CLAMP 2 -#define DATETIME_MS 3 -#define DATETIME_AUTO 4 - -/* Converts integer to its string representation in decimal notation. */ -extern int cbson_long_long_to_str(long long num, char* str, size_t size) { - // Buffer should fit 64-bit signed integer - if (size < 21) { - PyErr_Format( - PyExc_RuntimeError, - "Buffer too small to hold long long: %d < 21", size); - return -1; - } - int index = 0; - int sign = 1; - // Convert to unsigned to handle -LLONG_MIN overflow - unsigned long long absNum; - // Handle the case of 0 - if (num == 0) { - str[index++] = '0'; - str[index] = '\0'; - return 0; - } - // Handle negative numbers - if (num < 0) { - sign = -1; - absNum = 0ULL - (unsigned long long)num; - } else { - absNum = (unsigned long long)num; - } - // Convert the number to string - unsigned long long digit; - while (absNum > 0) { - digit = absNum % 10ULL; - str[index++] = (char)digit + '0'; // Convert digit to character - absNum /= 10; - } - // Add minus sign if negative - if (sign == -1) { - str[index++] = '-'; - } - str[index] = '\0'; // Null terminator - // Reverse the string - int start = 0; - int end = index - 1; - while (start < end) { - char temp = str[start]; - str[start++] = str[end]; - str[end--] = temp; - } - return 0; -} - -static PyObject* _test_long_long_to_str(PyObject* self, PyObject* args) { - // Test extreme values - Py_ssize_t maxNum = PY_SSIZE_T_MAX; - Py_ssize_t minNum = PY_SSIZE_T_MIN; - Py_ssize_t num; - char str_1[BUF_SIZE]; - char str_2[BUF_SIZE]; - int res = LL2STR(str_1, (long long)minNum); - if (res == -1) { - return NULL; - } - INT2STRING(str_2, (long long)minNum); - if (strcmp(str_1, str_2) != 0) { - PyErr_Format( - PyExc_RuntimeError, - "LL2STR != INT2STRING: %s != %s", str_1, str_2); - return NULL; - } - LL2STR(str_1, (long long)maxNum); - INT2STRING(str_2, (long long)maxNum); - if (strcmp(str_1, str_2) != 0) { - PyErr_Format( - PyExc_RuntimeError, - "LL2STR != INT2STRING: %s != %s", str_1, str_2); - return NULL; - } - - // Test common values - for (num = 0; num < 10000; num++) { - char str_1[BUF_SIZE]; - char str_2[BUF_SIZE]; - LL2STR(str_1, (long long)num); - INT2STRING(str_2, (long long)num); - if (strcmp(str_1, str_2) != 0) { - PyErr_Format( - PyExc_RuntimeError, - "LL2STR != INT2STRING: %s != %s", str_1, str_2); - return NULL; - } - } - - return args; -} - -/* Get an error class from the bson.errors module. - * - * Returns a new ref */ -static PyObject* _error(char* name) { - PyObject* error = NULL; - PyObject* errors = PyImport_ImportModule("bson.errors"); - if (!errors) { - return NULL; - } - error = PyObject_GetAttrString(errors, name); - Py_DECREF(errors); - return error; -} - -/* Safely downcast from Py_ssize_t to int, setting an - * exception and returning -1 on error. */ -static int -_downcast_and_check(Py_ssize_t size, uint8_t extra) { - if (size > BSON_MAX_SIZE || ((BSON_MAX_SIZE - extra) < size)) { - PyObject* InvalidStringData = _error("InvalidStringData"); - if (InvalidStringData) { - PyErr_SetString(InvalidStringData, - "String length must be <= 2147483647"); - Py_DECREF(InvalidStringData); - } - return -1; - } - return (int)size + extra; -} - -static PyObject* elements_to_dict(PyObject* self, const char* string, - unsigned max, - const codec_options_t* options); - -static int _write_element_to_buffer(PyObject* self, buffer_t buffer, - int type_byte, PyObject* value, - unsigned char check_keys, - const codec_options_t* options, - unsigned char in_custom_call, - unsigned char in_fallback_call); - -/* Write a RawBSONDocument to the buffer. - * Returns the number of bytes written or 0 on failure. - */ -static int write_raw_doc(buffer_t buffer, PyObject* raw, PyObject* _raw); - -/* Date stuff */ -static PyObject* datetime_from_millis(long long millis) { - /* To encode a datetime instance like datetime(9999, 12, 31, 23, 59, 59, 999999) - * we follow these steps: - * 1. Calculate a timestamp in seconds: 253402300799 - * 2. Multiply that by 1000: 253402300799000 - * 3. Add in microseconds divided by 1000 253402300799999 - * - * (Note: BSON doesn't support microsecond accuracy, hence the truncation.) - * - * To decode we could do: - * 1. Get seconds: timestamp / 1000: 253402300799 - * 2. Get micros: (timestamp % 1000) * 1000: 999000 - * Resulting in datetime(9999, 12, 31, 23, 59, 59, 999000) -- the expected result - * - * Now what if the we encode (1, 1, 1, 1, 1, 1, 111111)? - * 1. and 2. gives: -62135593139000 - * 3. Gives us: -62135593138889 - * - * Now decode: - * 1. Gives us: -62135593138 - * 2. Gives us: -889000 - * Resulting in datetime(1, 1, 1, 1, 1, 2, 15888216) -- an invalid result - * - * If instead to decode we do: - * diff = ((millis % 1000) + 1000) % 1000: 111 - * seconds = (millis - diff) / 1000: -62135593139 - * micros = diff * 1000 111000 - * Resulting in datetime(1, 1, 1, 1, 1, 1, 111000) -- the expected result - */ - PyObject* datetime = NULL; - int diff = (int)(((millis % 1000) + 1000) % 1000); - int microseconds = diff * 1000; - Time64_T seconds = (millis - diff) / 1000; - struct TM timeinfo; - cbson_gmtime64_r(&seconds, &timeinfo); - - datetime = PyDateTime_FromDateAndTime(timeinfo.tm_year + 1900, - timeinfo.tm_mon + 1, - timeinfo.tm_mday, - timeinfo.tm_hour, - timeinfo.tm_min, - timeinfo.tm_sec, - microseconds); - if(!datetime) { - PyObject *etype = NULL, *evalue = NULL, *etrace = NULL; - - /* - * Calling _error clears the error state, so fetch it first. - */ - PyErr_Fetch(&etype, &evalue, &etrace); - - /* Only add addition error message on ValueError exceptions. */ - if (PyErr_GivenExceptionMatches(etype, PyExc_ValueError)) { - if (evalue) { - PyObject* err_msg = PyObject_Str(evalue); - if (err_msg) { - PyObject* appendage = PyUnicode_FromString(" (Consider Using CodecOptions(datetime_conversion=DATETIME_AUTO) or MongoClient(datetime_conversion='DATETIME_AUTO')). See: https://www.mongodb.com/docs/languages/python/pymongo-driver/current/data-formats/dates-and-times/#handling-out-of-range-datetimes"); - if (appendage) { - PyObject* msg = PyUnicode_Concat(err_msg, appendage); - if (msg) { - Py_DECREF(evalue); - evalue = msg; - } - } - Py_XDECREF(appendage); - } - Py_XDECREF(err_msg); - } - PyErr_NormalizeException(&etype, &evalue, &etrace); - } - /* Steals references to args. */ - PyErr_Restore(etype, evalue, etrace); - } - return datetime; -} - -static long long millis_from_datetime(PyObject* datetime) { - struct TM timeinfo; - long long millis; - - timeinfo.tm_year = PyDateTime_GET_YEAR(datetime) - 1900; - timeinfo.tm_mon = PyDateTime_GET_MONTH(datetime) - 1; - timeinfo.tm_mday = PyDateTime_GET_DAY(datetime); - timeinfo.tm_hour = PyDateTime_DATE_GET_HOUR(datetime); - timeinfo.tm_min = PyDateTime_DATE_GET_MINUTE(datetime); - timeinfo.tm_sec = PyDateTime_DATE_GET_SECOND(datetime); - - millis = cbson_timegm64(&timeinfo) * 1000; - millis += PyDateTime_DATE_GET_MICROSECOND(datetime) / 1000; - return millis; -} - -/* Extended-range datetime, returns a DatetimeMS object with millis */ -static PyObject* datetime_ms_from_millis(PyObject* self, long long millis){ - // Allocate a new DatetimeMS object. - struct module_state *state = GETSTATE(self); - if (!state) { - return NULL; - } - - PyObject* dt = NULL; - PyObject* ll_millis = NULL; - - if (!(ll_millis = PyLong_FromLongLong(millis))){ - return NULL; - } - dt = PyObject_CallFunctionObjArgs(state->DatetimeMS, ll_millis, NULL); - Py_DECREF(ll_millis); - return dt; -} - -/* Extended-range datetime, takes a DatetimeMS object and extracts the long long value. */ -static int millis_from_datetime_ms(PyObject* dt, long long* out){ - PyObject* ll_millis; - long long millis; - - if (!(ll_millis = PyNumber_Long(dt))){ - return 0; - } - millis = PyLong_AsLongLong(ll_millis); - Py_DECREF(ll_millis); - if (millis == -1 && PyErr_Occurred()) { /* Overflow */ - PyErr_SetString(PyExc_OverflowError, - "MongoDB datetimes can only handle up to 8-byte ints"); - return 0; - } - *out = millis; - return 1; -} - -static PyObject* decode_datetime(PyObject* self, long long millis, const codec_options_t* options){ - PyObject* naive = NULL; - PyObject* replace = NULL; - PyObject* value = NULL; - struct module_state *state = GETSTATE(self); - if (!state) { - goto invalid; - } - if (options->datetime_conversion == DATETIME_MS){ - return datetime_ms_from_millis(self, millis); - } - - int dt_clamp = options->datetime_conversion == DATETIME_CLAMP; - int dt_auto = options->datetime_conversion == DATETIME_AUTO; - - if (dt_clamp || dt_auto){ - int64_t min_millis = state->min_millis; - int64_t max_millis = state->max_millis; - int64_t min_millis_offset = 0; - int64_t max_millis_offset = 0; - if (options->tz_aware && options->tzinfo && options->tzinfo != Py_None) { - PyObject* utcoffset = PyObject_CallMethodObjArgs(options->tzinfo, state->_utcoffset_str, state->min_datetime, NULL); - if (utcoffset == NULL) { - return 0; - } - if (utcoffset != Py_None) { - if (!PyDelta_Check(utcoffset)) { - PyObject* BSONError = _error("BSONError"); - if (BSONError) { - PyErr_SetString(BSONError, "tzinfo.utcoffset() did not return a datetime.timedelta"); - Py_DECREF(BSONError); - } - Py_DECREF(utcoffset); - return 0; - } - min_millis_offset = (PyDateTime_DELTA_GET_DAYS(utcoffset) * (int64_t)86400 + - PyDateTime_DELTA_GET_SECONDS(utcoffset)) * (int64_t)1000 + - (PyDateTime_DELTA_GET_MICROSECONDS(utcoffset) / 1000); - } - Py_DECREF(utcoffset); - utcoffset = PyObject_CallMethodObjArgs(options->tzinfo, state->_utcoffset_str, state->max_datetime, NULL); - if (utcoffset == NULL) { - return 0; - } - if (utcoffset != Py_None) { - if (!PyDelta_Check(utcoffset)) { - PyObject* BSONError = _error("BSONError"); - if (BSONError) { - PyErr_SetString(BSONError, "tzinfo.utcoffset() did not return a datetime.timedelta"); - Py_DECREF(BSONError); - } - Py_DECREF(utcoffset); - return 0; - } - max_millis_offset = (PyDateTime_DELTA_GET_DAYS(utcoffset) * (int64_t)86400 + - PyDateTime_DELTA_GET_SECONDS(utcoffset)) * (int64_t)1000 + - (PyDateTime_DELTA_GET_MICROSECONDS(utcoffset) / 1000); - } - Py_DECREF(utcoffset); - } - if (min_millis_offset < 0) { - min_millis -= min_millis_offset; - } - - if (max_millis_offset > 0) { - max_millis -= max_millis_offset; - } - - if (dt_clamp) { - if (millis < min_millis) { - millis = min_millis; - } else if (millis > max_millis) { - millis = max_millis; - } - // Continues from here to return a datetime. - } else { // dt_auto - if (millis < min_millis || millis > max_millis){ - return datetime_ms_from_millis(self, millis); - } - } - } - - naive = datetime_from_millis(millis); - if (!naive) { - goto invalid; - } - - if (!options->tz_aware) { /* In the naive case, we're done here. */ - return naive; - } - replace = PyObject_GetAttr(naive, state->_replace_str); - if (!replace) { - goto invalid; - } - value = PyObject_Call(replace, state->replace_args, state->replace_kwargs); - if (!value) { - goto invalid; - } - - /* convert to local time */ - if (options->tzinfo != Py_None) { - PyObject* temp = PyObject_CallMethodObjArgs(value, state->_astimezone_str, options->tzinfo, NULL); - Py_DECREF(value); - value = temp; - } -invalid: - Py_XDECREF(naive); - Py_XDECREF(replace); - return value; -} - -/* Just make this compatible w/ the old API. */ -int buffer_write_bytes(buffer_t buffer, const char* data, int size) { - if (pymongo_buffer_write(buffer, data, size)) { - return 0; - } - return 1; -} - -int buffer_write_double(buffer_t buffer, double data) { - double data_le = BSON_DOUBLE_TO_LE(data); - return buffer_write_bytes(buffer, (const char*)&data_le, 8); -} - -int buffer_write_int32(buffer_t buffer, int32_t data) { - uint32_t data_le = BSON_UINT32_TO_LE(data); - return buffer_write_bytes(buffer, (const char*)&data_le, 4); -} - -int buffer_write_int64(buffer_t buffer, int64_t data) { - uint64_t data_le = BSON_UINT64_TO_LE(data); - return buffer_write_bytes(buffer, (const char*)&data_le, 8); -} - -void buffer_write_int32_at_position(buffer_t buffer, - int position, - int32_t data) { - uint32_t data_le = BSON_UINT32_TO_LE(data); - memcpy(pymongo_buffer_get_buffer(buffer) + position, &data_le, 4); -} - -static int write_unicode(buffer_t buffer, PyObject* py_string) { - int size; - const char* data; - PyObject* encoded = PyUnicode_AsUTF8String(py_string); - if (!encoded) { - return 0; - } - data = PyBytes_AS_STRING(encoded); - if (!data) - goto unicodefail; - - if ((size = _downcast_and_check(PyBytes_GET_SIZE(encoded), 1)) == -1) - goto unicodefail; - - if (!buffer_write_int32(buffer, (int32_t)size)) - goto unicodefail; - - if (!buffer_write_bytes(buffer, data, size)) - goto unicodefail; - - Py_DECREF(encoded); - return 1; - -unicodefail: - Py_DECREF(encoded); - return 0; -} - -/* returns 0 on failure */ -static int write_string(buffer_t buffer, PyObject* py_string) { - int size; - const char* data; - if (PyUnicode_Check(py_string)){ - return write_unicode(buffer, py_string); - } - data = PyBytes_AsString(py_string); - if (!data) { - return 0; - } - - if ((size = _downcast_and_check(PyBytes_Size(py_string), 1)) == -1) - return 0; - - if (!buffer_write_int32(buffer, (int32_t)size)) { - return 0; - } - if (!buffer_write_bytes(buffer, data, size)) { - return 0; - } - return 1; -} - -/* Load a Python object to cache. - * - * Returns non-zero on failure. */ -static int _load_object(PyObject** object, char* module_name, char* object_name) { - PyObject* module; - - module = PyImport_ImportModule(module_name); - if (!module) { - return 1; - } - - *object = PyObject_GetAttrString(module, object_name); - Py_DECREF(module); - - return (*object) ? 0 : 2; -} - -/* Load all Python objects to cache. - * - * Returns non-zero on failure. */ -static int _load_python_objects(PyObject* module) { - PyObject* empty_string = NULL; - PyObject* re_compile = NULL; - PyObject* compiled = NULL; - PyObject* min_datetime_ms = NULL; - PyObject* max_datetime_ms = NULL; - struct module_state *state = GETSTATE(module); - if (!state) { - return 1; - } - - /* Cache commonly used attribute names to improve performance. */ - if (!((state->_type_marker_str = PyUnicode_FromString("_type_marker")) && - (state->_flags_str = PyUnicode_FromString("flags")) && - (state->_pattern_str = PyUnicode_FromString("pattern")) && - (state->_encoder_map_str = PyUnicode_FromString("_encoder_map")) && - (state->_decoder_map_str = PyUnicode_FromString("_decoder_map")) && - (state->_fallback_encoder_str = PyUnicode_FromString("_fallback_encoder")) && - (state->_raw_str = PyUnicode_FromString("raw")) && - (state->_subtype_str = PyUnicode_FromString("subtype")) && - (state->_binary_str = PyUnicode_FromString("binary")) && - (state->_scope_str = PyUnicode_FromString("scope")) && - (state->_inc_str = PyUnicode_FromString("inc")) && - (state->_time_str = PyUnicode_FromString("time")) && - (state->_bid_str = PyUnicode_FromString("bid")) && - (state->_replace_str = PyUnicode_FromString("replace")) && - (state->_astimezone_str = PyUnicode_FromString("astimezone")) && - (state->_id_str = PyUnicode_FromString("_id")) && - (state->_dollar_ref_str = PyUnicode_FromString("$ref")) && - (state->_dollar_id_str = PyUnicode_FromString("$id")) && - (state->_dollar_db_str = PyUnicode_FromString("$db")) && - (state->_tzinfo_str = PyUnicode_FromString("tzinfo")) && - (state->_as_doc_str = PyUnicode_FromString("as_doc")) && - (state->_utcoffset_str = PyUnicode_FromString("utcoffset")) && - (state->_from_uuid_str = PyUnicode_FromString("from_uuid")) && - (state->_as_uuid_str = PyUnicode_FromString("as_uuid")) && - (state->_from_bid_str = PyUnicode_FromString("from_bid")))) { - return 1; - } - - if (_load_object(&state->Binary, "bson.binary", "Binary") || - _load_object(&state->Code, "bson.code", "Code") || - _load_object(&state->ObjectId, "bson.objectid", "ObjectId") || - _load_object(&state->DBRef, "bson.dbref", "DBRef") || - _load_object(&state->Timestamp, "bson.timestamp", "Timestamp") || - _load_object(&state->MinKey, "bson.min_key", "MinKey") || - _load_object(&state->MaxKey, "bson.max_key", "MaxKey") || - _load_object(&state->UTC, "bson.tz_util", "utc") || - _load_object(&state->Regex, "bson.regex", "Regex") || - _load_object(&state->BSONInt64, "bson.int64", "Int64") || - _load_object(&state->Decimal128, "bson.decimal128", "Decimal128") || - _load_object(&state->UUID, "uuid", "UUID") || - _load_object(&state->Mapping, "collections.abc", "Mapping") || - _load_object(&state->DatetimeMS, "bson.datetime_ms", "DatetimeMS") || - _load_object(&min_datetime_ms, "bson.datetime_ms", "_MIN_UTC_MS") || - _load_object(&max_datetime_ms, "bson.datetime_ms", "_MAX_UTC_MS") || - _load_object(&state->min_datetime, "bson.datetime_ms", "_MIN_UTC") || - _load_object(&state->max_datetime, "bson.datetime_ms", "_MAX_UTC")) { - return 1; - } - - state->min_millis = PyLong_AsLongLong(min_datetime_ms); - state->max_millis = PyLong_AsLongLong(max_datetime_ms); - Py_DECREF(min_datetime_ms); - Py_DECREF(max_datetime_ms); - if ((state->min_millis == -1 || state->max_millis == -1) && PyErr_Occurred()) { - return 1; - } - - /* Speed up datetime.replace(tzinfo=utc) call */ - state->replace_args = PyTuple_New(0); - if (!state->replace_args) { - return 1; - } - state->replace_kwargs = PyDict_New(); - if (!state->replace_kwargs) { - return 1; - } - if (PyDict_SetItem(state->replace_kwargs, state->_tzinfo_str, state->UTC) == -1) { - return 1; - } - - /* Reload our REType hack too. */ - empty_string = PyBytes_FromString(""); - if (empty_string == NULL) { - state->REType = NULL; - return 1; - } - - if (_load_object(&re_compile, "re", "compile")) { - state->REType = NULL; - Py_DECREF(empty_string); - return 1; - } - - compiled = PyObject_CallFunction(re_compile, "O", empty_string); - Py_DECREF(re_compile); - if (compiled == NULL) { - state->REType = NULL; - Py_DECREF(empty_string); - return 1; - } - Py_INCREF(Py_TYPE(compiled)); - state->REType = Py_TYPE(compiled); - Py_DECREF(empty_string); - Py_DECREF(compiled); - return 0; -} - -/* - * Get the _type_marker from an Object. - * - * Return the type marker, 0 if there is no marker, or -1 on failure. - */ -static long _type_marker(PyObject* object, PyObject* _type_marker_str) { - PyObject* type_marker = NULL; - long type = 0; - - if (PyObject_HasAttr(object, _type_marker_str)) { - type_marker = PyObject_GetAttr(object, _type_marker_str); - if (type_marker == NULL) { - return -1; - } - } - - /* - * Python objects with broken __getattr__ implementations could return - * arbitrary types for a call to PyObject_GetAttrString. For example - * pymongo.database.Database returns a new Collection instance for - * __getattr__ calls with names that don't match an existing attribute - * or method. In some cases "value" could be a subtype of something - * we know how to serialize. Make a best effort to encode these types. - */ - if (type_marker && PyLong_CheckExact(type_marker)) { - type = PyLong_AsLong(type_marker); - Py_DECREF(type_marker); - } else { - Py_XDECREF(type_marker); - } - - return type; -} - -/* Fill out a type_registry_t* from a TypeRegistry object. - * - * Return 1 on success. options->document_class is a new reference. - * Return 0 on failure. - */ -int cbson_convert_type_registry(PyObject* registry_obj, type_registry_t* registry, PyObject* _encoder_map_str, PyObject* _decoder_map_str, PyObject* _fallback_encoder_str) { - registry->encoder_map = NULL; - registry->decoder_map = NULL; - registry->fallback_encoder = NULL; - registry->registry_obj = NULL; - - registry->encoder_map = PyObject_GetAttr(registry_obj, _encoder_map_str); - if (registry->encoder_map == NULL) { - goto fail; - } - registry->is_encoder_empty = (PyDict_Size(registry->encoder_map) == 0); - - registry->decoder_map = PyObject_GetAttr(registry_obj, _decoder_map_str); - if (registry->decoder_map == NULL) { - goto fail; - } - registry->is_decoder_empty = (PyDict_Size(registry->decoder_map) == 0); - - registry->fallback_encoder = PyObject_GetAttr(registry_obj, _fallback_encoder_str); - if (registry->fallback_encoder == NULL) { - goto fail; - } - registry->has_fallback_encoder = (registry->fallback_encoder != Py_None); - - registry->registry_obj = registry_obj; - Py_INCREF(registry->registry_obj); - return 1; - -fail: - Py_XDECREF(registry->encoder_map); - Py_XDECREF(registry->decoder_map); - Py_XDECREF(registry->fallback_encoder); - return 0; -} - -/* Fill out a codec_options_t* from a CodecOptions object. - * - * Return 1 on success. options->document_class is a new reference. - * Return 0 on failure. - */ -int convert_codec_options(PyObject* self, PyObject* options_obj, codec_options_t* options) { - PyObject* type_registry_obj = NULL; - struct module_state *state = GETSTATE(self); - long type_marker; - if (!state) { - return 0; - } - - options->unicode_decode_error_handler = NULL; - - if (!PyArg_ParseTuple(options_obj, "ObbzOOb", - &options->document_class, - &options->tz_aware, - &options->uuid_rep, - &options->unicode_decode_error_handler, - &options->tzinfo, - &type_registry_obj, - &options->datetime_conversion)) { - return 0; - } - - type_marker = _type_marker(options->document_class, - state->_type_marker_str); - if (type_marker < 0) { - return 0; - } - - if (!cbson_convert_type_registry(type_registry_obj, - &options->type_registry, state->_encoder_map_str, state->_decoder_map_str, state->_fallback_encoder_str)) { - return 0; - } - - options->is_raw_bson = (101 == type_marker); - options->options_obj = options_obj; - - Py_INCREF(options->options_obj); - Py_INCREF(options->document_class); - Py_INCREF(options->tzinfo); - - return 1; -} - -void destroy_codec_options(codec_options_t* options) { - Py_CLEAR(options->document_class); - Py_CLEAR(options->tzinfo); - Py_CLEAR(options->options_obj); - Py_CLEAR(options->type_registry.registry_obj); - Py_CLEAR(options->type_registry.encoder_map); - Py_CLEAR(options->type_registry.decoder_map); - Py_CLEAR(options->type_registry.fallback_encoder); -} - -static int write_element_to_buffer(PyObject* self, buffer_t buffer, - int type_byte, PyObject* value, - unsigned char check_keys, - const codec_options_t* options, - unsigned char in_custom_call, - unsigned char in_fallback_call) { - int result = 0; - if(Py_EnterRecursiveCall(" while encoding an object to BSON ")) { - return 0; - } - result = _write_element_to_buffer(self, buffer, type_byte, - value, check_keys, options, - in_custom_call, in_fallback_call); - Py_LeaveRecursiveCall(); - return result; -} - -static void -_set_cannot_encode(PyObject* value) { - if (PyLong_Check(value)) { - if ((PyLong_AsLongLong(value) == -1) && PyErr_Occurred()) { - return PyErr_SetString(PyExc_OverflowError, - "MongoDB can only handle up to 8-byte ints"); - } - } - - PyObject* type = NULL; - PyObject* InvalidDocument = _error("InvalidDocument"); - if (InvalidDocument == NULL) { - goto error; - } - - type = PyObject_Type(value); - if (type == NULL) { - goto error; - } - PyErr_Format(InvalidDocument, "cannot encode object: %R, of type: %R", - value, type); -error: - Py_XDECREF(type); - Py_XDECREF(InvalidDocument); -} - -/* - * Encode a builtin Python regular expression or our custom Regex class. - * - * Sets exception and returns 0 on failure. - */ -static int _write_regex_to_buffer( - buffer_t buffer, int type_byte, PyObject* value, PyObject* _flags_str, PyObject* _pattern_str) { - - PyObject* py_flags; - PyObject* py_pattern; - PyObject* encoded_pattern; - PyObject* decoded_pattern; - long int_flags; - char flags[FLAGS_SIZE]; - char check_utf8 = 0; - const char* pattern_data; - int pattern_length, flags_length; - - /* - * Both the builtin re type and our Regex class have attributes - * "flags" and "pattern". - */ - py_flags = PyObject_GetAttr(value, _flags_str); - if (!py_flags) { - return 0; - } - int_flags = PyLong_AsLong(py_flags); - Py_DECREF(py_flags); - if (int_flags == -1 && PyErr_Occurred()) { - return 0; - } - py_pattern = PyObject_GetAttr(value, _pattern_str); - if (!py_pattern) { - return 0; - } - - if (PyUnicode_Check(py_pattern)) { - encoded_pattern = PyUnicode_AsUTF8String(py_pattern); - Py_DECREF(py_pattern); - if (!encoded_pattern) { - return 0; - } - } else { - encoded_pattern = py_pattern; - check_utf8 = 1; - } - - if (!(pattern_data = PyBytes_AsString(encoded_pattern))) { - Py_DECREF(encoded_pattern); - return 0; - } - if ((pattern_length = _downcast_and_check(PyBytes_Size(encoded_pattern), 0)) == -1) { - Py_DECREF(encoded_pattern); - return 0; - } - - if (strlen(pattern_data) != (size_t) pattern_length){ - PyObject* InvalidDocument = _error("InvalidDocument"); - if (InvalidDocument) { - PyErr_SetString(InvalidDocument, - "regex patterns must not contain the NULL byte"); - Py_DECREF(InvalidDocument); - } - Py_DECREF(encoded_pattern); - return 0; - } - - if (check_utf8) { - decoded_pattern = PyUnicode_DecodeUTF8(pattern_data, (Py_ssize_t) pattern_length, NULL); - if (decoded_pattern == NULL) { - PyErr_Clear(); - PyObject* InvalidStringData = _error("InvalidStringData"); - if (InvalidStringData) { - PyErr_SetString(InvalidStringData, - "regex patterns must be valid UTF-8"); - Py_DECREF(InvalidStringData); - } - Py_DECREF(encoded_pattern); - return 0; - } - Py_DECREF(decoded_pattern); - } - - if (!buffer_write_bytes(buffer, pattern_data, pattern_length + 1)) { - Py_DECREF(encoded_pattern); - return 0; - } - Py_DECREF(encoded_pattern); - - flags[0] = 0; - - if (int_flags & 2) { - STRCAT(flags, FLAGS_SIZE, "i"); - } - if (int_flags & 4) { - STRCAT(flags, FLAGS_SIZE, "l"); - } - if (int_flags & 8) { - STRCAT(flags, FLAGS_SIZE, "m"); - } - if (int_flags & 16) { - STRCAT(flags, FLAGS_SIZE, "s"); - } - if (int_flags & 32) { - STRCAT(flags, FLAGS_SIZE, "u"); - } - if (int_flags & 64) { - STRCAT(flags, FLAGS_SIZE, "x"); - } - flags_length = (int)strlen(flags) + 1; - if (!buffer_write_bytes(buffer, flags, flags_length)) { - return 0; - } - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x0B; - return 1; -} - -/* Write a single value to the buffer (also write its type_byte, for which - * space has already been reserved. - * - * returns 0 on failure */ -static int _write_element_to_buffer(PyObject* self, buffer_t buffer, - int type_byte, PyObject* value, - unsigned char check_keys, - const codec_options_t* options, - unsigned char in_custom_call, - unsigned char in_fallback_call) { - PyObject* new_value = NULL; - int retval; - int is_list; - long type; - struct module_state *state = GETSTATE(self); - if (!state) { - return 0; - } - /* - * Use _type_marker attribute instead of PyObject_IsInstance for better perf. - */ - type = _type_marker(value, state->_type_marker_str); - if (type < 0) { - return 0; - } - - switch (type) { - case 5: - { - /* Binary */ - PyObject* subtype_object; - char subtype; - const char* data; - int size; - - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x05; - subtype_object = PyObject_GetAttr(value, state->_subtype_str); - if (!subtype_object) { - return 0; - } - subtype = (char)PyLong_AsLong(subtype_object); - if (subtype == -1) { - Py_DECREF(subtype_object); - return 0; - } - size = _downcast_and_check(PyBytes_Size(value), 0); - if (size == -1) { - Py_DECREF(subtype_object); - return 0; - } - - Py_DECREF(subtype_object); - if (subtype == 2) { - int other_size = _downcast_and_check(PyBytes_Size(value), 4); - if (other_size == -1) - return 0; - if (!buffer_write_int32(buffer, other_size)) { - return 0; - } - if (!buffer_write_bytes(buffer, &subtype, 1)) { - return 0; - } - } - if (!buffer_write_int32(buffer, size)) { - return 0; - } - if (subtype != 2) { - if (!buffer_write_bytes(buffer, &subtype, 1)) { - return 0; - } - } - data = PyBytes_AsString(value); - if (!data) { - return 0; - } - if (!buffer_write_bytes(buffer, data, size)) { - return 0; - } - return 1; - } - case 7: - { - /* ObjectId */ - const char* data; - PyObject* pystring = PyObject_GetAttr(value, state->_binary_str); - if (!pystring) { - return 0; - } - data = PyBytes_AsString(pystring); - if (!data) { - Py_DECREF(pystring); - return 0; - } - if (!buffer_write_bytes(buffer, data, 12)) { - Py_DECREF(pystring); - return 0; - } - Py_DECREF(pystring); - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x07; - return 1; - } - case 9: - { - /* DatetimeMS */ - long long millis; - if (!millis_from_datetime_ms(value, &millis)) { - return 0; - } - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x09; - return buffer_write_int64(buffer, (int64_t)millis); - } - case 11: - { - /* Regex */ - return _write_regex_to_buffer(buffer, type_byte, value, state->_flags_str, state->_pattern_str); - } - case 13: - { - /* Code */ - int start_position, - length_location, - length; - - PyObject* scope = PyObject_GetAttr(value, state->_scope_str); - if (!scope) { - return 0; - } - - if (scope == Py_None) { - Py_DECREF(scope); - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x0D; - return write_string(buffer, value); - } - - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x0F; - - start_position = pymongo_buffer_get_position(buffer); - /* save space for length */ - length_location = pymongo_buffer_save_space(buffer, 4); - if (length_location == -1) { - Py_DECREF(scope); - return 0; - } - - if (!write_string(buffer, value)) { - Py_DECREF(scope); - return 0; - } - - if (!write_dict(self, buffer, scope, 0, options, 0)) { - Py_DECREF(scope); - return 0; - } - Py_DECREF(scope); - - length = pymongo_buffer_get_position(buffer) - start_position; - buffer_write_int32_at_position( - buffer, length_location, (int32_t)length); - return 1; - } - case 17: - { - /* Timestamp */ - PyObject* obj; - unsigned long i; - - obj = PyObject_GetAttr(value, state->_inc_str); - if (!obj) { - return 0; - } - i = PyLong_AsUnsignedLong(obj); - Py_DECREF(obj); - if (i == (unsigned long)-1 && PyErr_Occurred()) { - return 0; - } - if (!buffer_write_int32(buffer, (int32_t)i)) { - return 0; - } - - obj = PyObject_GetAttr(value, state->_time_str); - if (!obj) { - return 0; - } - i = PyLong_AsUnsignedLong(obj); - Py_DECREF(obj); - if (i == (unsigned long)-1 && PyErr_Occurred()) { - return 0; - } - if (!buffer_write_int32(buffer, (int32_t)i)) { - return 0; - } - - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x11; - return 1; - } - case 18: - { - /* Int64 */ - const long long ll = PyLong_AsLongLong(value); - if (PyErr_Occurred()) { /* Overflow */ - PyErr_SetString(PyExc_OverflowError, - "MongoDB can only handle up to 8-byte ints"); - return 0; - } - if (!buffer_write_int64(buffer, (int64_t)ll)) { - return 0; - } - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x12; - return 1; - } - case 19: - { - /* Decimal128 */ - const char* data; - PyObject* pystring = PyObject_GetAttr(value, state->_bid_str); - if (!pystring) { - return 0; - } - data = PyBytes_AsString(pystring); - if (!data) { - Py_DECREF(pystring); - return 0; - } - if (!buffer_write_bytes(buffer, data, 16)) { - Py_DECREF(pystring); - return 0; - } - Py_DECREF(pystring); - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x13; - return 1; - } - case 100: - { - /* DBRef */ - PyObject* as_doc = PyObject_CallMethodObjArgs(value, state->_as_doc_str, NULL); - if (!as_doc) { - return 0; - } - if (!write_dict(self, buffer, as_doc, 0, options, 0)) { - Py_DECREF(as_doc); - return 0; - } - Py_DECREF(as_doc); - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x03; - return 1; - } - case 101: - { - /* RawBSONDocument */ - if (!write_raw_doc(buffer, value, state->_raw_str)) { - return 0; - } - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x03; - return 1; - } - case 255: - { - /* MinKey */ - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0xFF; - return 1; - } - case 127: - { - /* MaxKey */ - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x7F; - return 1; - } - } - - /* No _type_marker attribute or not one of our types. */ - - if (PyBool_Check(value)) { - const char c = (value == Py_True) ? 0x01 : 0x00; - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x08; - return buffer_write_bytes(buffer, &c, 1); - } - else if (PyLong_Check(value)) { - const long long long_long_value = PyLong_AsLongLong(value); - if (long_long_value == -1 && PyErr_Occurred()) { - /* Ignore error and give the fallback_encoder a chance. */ - PyErr_Clear(); - } else if (-2147483648LL <= long_long_value && long_long_value <= 2147483647LL) { - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x10; - return buffer_write_int32(buffer, (int32_t)long_long_value); - } else { - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x12; - return buffer_write_int64(buffer, (int64_t)long_long_value); - } - } else if (PyFloat_Check(value)) { - const double d = PyFloat_AsDouble(value); - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x01; - return buffer_write_double(buffer, d); - } else if (value == Py_None) { - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x0A; - return 1; - } else if (PyDict_Check(value)) { - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x03; - return write_dict(self, buffer, value, check_keys, options, 0); - } else if ((is_list = PyList_Check(value)) || PyTuple_Check(value)) { - Py_ssize_t items, i; - int start_position, - length_location, - length; - char zero = 0; - - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x04; - start_position = pymongo_buffer_get_position(buffer); - - /* save space for length */ - length_location = pymongo_buffer_save_space(buffer, 4); - if (length_location == -1) { - return 0; - } - if (is_list) { - items = PyList_Size(value); - } else { - items = PyTuple_Size(value); - } - if (items > BSON_MAX_SIZE) { - PyObject* BSONError = _error("BSONError"); - if (BSONError) { - PyErr_SetString(BSONError, - "Too many items to serialize."); - Py_DECREF(BSONError); - } - return 0; - } - for(i = 0; i < items; i++) { - int list_type_byte = pymongo_buffer_save_space(buffer, 1); - char name[BUF_SIZE]; - PyObject* item_value; - - if (list_type_byte == -1) { - return 0; - } - int res = LL2STR(name, (long long)i); - if (res == -1) { - return 0; - } - if (!buffer_write_bytes(buffer, name, (int)strlen(name) + 1)) { - return 0; - } - if (is_list) { - item_value = PyList_GET_ITEM(value, i); - } else { - item_value = PyTuple_GET_ITEM(value, i); - } - if (!item_value) { - return 0; - } - if (!write_element_to_buffer(self, buffer, list_type_byte, - item_value, check_keys, options, - 0, 0)) { - return 0; - } - } - - /* write null byte and fill in length */ - if (!buffer_write_bytes(buffer, &zero, 1)) { - return 0; - } - length = pymongo_buffer_get_position(buffer) - start_position; - buffer_write_int32_at_position( - buffer, length_location, (int32_t)length); - return 1; - /* Python3 special case. Store bytes as BSON binary subtype 0. */ - } else if (PyBytes_Check(value)) { - char subtype = 0; - int size; - const char* data = PyBytes_AS_STRING(value); - if (!data) - return 0; - if ((size = _downcast_and_check(PyBytes_GET_SIZE(value), 0)) == -1) - return 0; - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x05; - if (!buffer_write_int32(buffer, (int32_t)size)) { - return 0; - } - if (!buffer_write_bytes(buffer, &subtype, 1)) { - return 0; - } - if (!buffer_write_bytes(buffer, data, size)) { - return 0; - } - return 1; - } else if (PyUnicode_Check(value)) { - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x02; - return write_unicode(buffer, value); - } else if (PyDateTime_Check(value)) { - long long millis; - PyObject* utcoffset = PyObject_CallMethodObjArgs(value, state->_utcoffset_str , NULL); - if (utcoffset == NULL) - return 0; - if (utcoffset != Py_None) { - PyObject* result = PyNumber_Subtract(value, utcoffset); - if (!result) { - Py_DECREF(utcoffset); - return 0; - } - millis = millis_from_datetime(result); - Py_DECREF(result); - } else { - millis = millis_from_datetime(value); - } - Py_DECREF(utcoffset); - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x09; - return buffer_write_int64(buffer, (int64_t)millis); - } else if (PyObject_TypeCheck(value, state->REType)) { - return _write_regex_to_buffer(buffer, type_byte, value, state->_flags_str, state->_pattern_str); - } else if (PyObject_IsInstance(value, state->Mapping)) { - /* PyObject_IsInstance returns -1 on error */ - if (PyErr_Occurred()) { - return 0; - } - *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x03; - return write_dict(self, buffer, value, check_keys, options, 0); - } else if (PyObject_IsInstance(value, state->UUID)) { - PyObject* binary_value = NULL; - PyObject *uuid_rep_obj = NULL; - int result; - - /* PyObject_IsInstance returns -1 on error */ - if (PyErr_Occurred()) { - return 0; - } - - if (!(uuid_rep_obj = PyLong_FromLong(options->uuid_rep))) { - return 0; - } - binary_value = PyObject_CallMethodObjArgs(state->Binary, state->_from_uuid_str, value, uuid_rep_obj, NULL); - Py_DECREF(uuid_rep_obj); - - if (binary_value == NULL) { - return 0; - } - - result = _write_element_to_buffer(self, buffer, - type_byte, binary_value, - check_keys, options, - in_custom_call, - in_fallback_call); - Py_DECREF(binary_value); - return result; - } - - /* Try a custom encoder if one is provided and we have not already - * attempted to use a type encoder. */ - if (!in_custom_call && !options->type_registry.is_encoder_empty) { - PyObject* value_type = NULL; - PyObject* converter = NULL; - value_type = PyObject_Type(value); - if (value_type == NULL) { - return 0; - } - converter = PyDict_GetItem(options->type_registry.encoder_map, value_type); - Py_XDECREF(value_type); - if (converter != NULL) { - /* Transform types that have a registered converter. - * A new reference is created upon transformation. */ - new_value = PyObject_CallFunctionObjArgs(converter, value, NULL); - if (new_value == NULL) { - return 0; - } - retval = write_element_to_buffer(self, buffer, type_byte, new_value, - check_keys, options, 1, 0); - Py_XDECREF(new_value); - return retval; - } - } - - /* Try the fallback encoder if one is provided and we have not already - * attempted to use the fallback encoder. */ - if (!in_fallback_call && options->type_registry.has_fallback_encoder) { - new_value = PyObject_CallFunctionObjArgs( - options->type_registry.fallback_encoder, value, NULL); - if (new_value == NULL) { - // propagate any exception raised by the callback - return 0; - } - retval = write_element_to_buffer(self, buffer, type_byte, new_value, - check_keys, options, 0, 1); - Py_XDECREF(new_value); - return retval; - } - - /* We can't determine value's type. Fail. */ - _set_cannot_encode(value); - return 0; -} - -static int check_key_name(const char* name, int name_length) { - - if (name_length > 0 && name[0] == '$') { - PyObject* InvalidDocument = _error("InvalidDocument"); - if (InvalidDocument) { - PyObject* errmsg = PyUnicode_FromFormat( - "key '%s' must not start with '$'", name); - if (errmsg) { - PyErr_SetObject(InvalidDocument, errmsg); - Py_DECREF(errmsg); - } - Py_DECREF(InvalidDocument); - } - return 0; - } - if (strchr(name, '.')) { - PyObject* InvalidDocument = _error("InvalidDocument"); - if (InvalidDocument) { - PyObject* errmsg = PyUnicode_FromFormat( - "key '%s' must not contain '.'", name); - if (errmsg) { - PyErr_SetObject(InvalidDocument, errmsg); - Py_DECREF(errmsg); - } - Py_DECREF(InvalidDocument); - } - return 0; - } - return 1; -} - -/* Write a (key, value) pair to the buffer. - * - * Returns 0 on failure */ -int write_pair(PyObject* self, buffer_t buffer, const char* name, int name_length, - PyObject* value, unsigned char check_keys, - const codec_options_t* options, unsigned char allow_id) { - int type_byte; - - /* Don't write any _id elements unless we're explicitly told to - - * _id has to be written first so we do so, but don't bother - * deleting it from the dictionary being written. */ - if (!allow_id && strcmp(name, "_id") == 0) { - return 1; - } - - type_byte = pymongo_buffer_save_space(buffer, 1); - if (type_byte == -1) { - return 0; - } - if (check_keys && !check_key_name(name, name_length)) { - return 0; - } - if (!buffer_write_bytes(buffer, name, name_length + 1)) { - return 0; - } - if (!write_element_to_buffer(self, buffer, type_byte, - value, check_keys, options, 0, 0)) { - return 0; - } - return 1; -} - -int decode_and_write_pair(PyObject* self, buffer_t buffer, - PyObject* key, PyObject* value, - unsigned char check_keys, - const codec_options_t* options, - unsigned char top_level) { - PyObject* encoded; - const char* data; - int size; - if (PyUnicode_Check(key)) { - encoded = PyUnicode_AsUTF8String(key); - if (!encoded) { - return 0; - } - if (!(data = PyBytes_AS_STRING(encoded))) { - Py_DECREF(encoded); - return 0; - } - if ((size = _downcast_and_check(PyBytes_GET_SIZE(encoded), 1)) == -1) { - Py_DECREF(encoded); - return 0; - } - if (strlen(data) != (size_t)(size - 1)) { - PyObject* InvalidDocument = _error("InvalidDocument"); - if (InvalidDocument) { - PyErr_SetString(InvalidDocument, - "Key names must not contain the NULL byte"); - Py_DECREF(InvalidDocument); - } - Py_DECREF(encoded); - return 0; - } - } else { - PyObject* InvalidDocument = _error("InvalidDocument"); - if (InvalidDocument) { - PyObject* repr = PyObject_Repr(key); - if (repr) { - PyObject* errmsg = PyUnicode_FromString( - "documents must have only string keys, key was "); - if (errmsg) { - PyObject* error = PyUnicode_Concat(errmsg, repr); - if (error) { - PyErr_SetObject(InvalidDocument, error); - Py_DECREF(error); - } - Py_DECREF(errmsg); - Py_DECREF(repr); - } else { - Py_DECREF(repr); - } - } - Py_DECREF(InvalidDocument); - } - return 0; - } - - /* If top_level is True, don't allow writing _id here - it was already written. */ - if (!write_pair(self, buffer, data, - size - 1, value, check_keys, options, !top_level)) { - Py_DECREF(encoded); - return 0; - } - - Py_DECREF(encoded); - return 1; -} - - -/* Write a RawBSONDocument to the buffer. - * Returns the number of bytes written or 0 on failure. - */ -static int write_raw_doc(buffer_t buffer, PyObject* raw, PyObject* _raw_str) { - char* bytes; - Py_ssize_t len; - int len_int; - int bytes_written = 0; - PyObject* bytes_obj = NULL; - - bytes_obj = PyObject_GetAttr(raw, _raw_str); - if (!bytes_obj) { - goto fail; - } - - if (-1 == PyBytes_AsStringAndSize(bytes_obj, &bytes, &len)) { - goto fail; - } - len_int = _downcast_and_check(len, 0); - if (-1 == len_int) { - goto fail; - } - if (!buffer_write_bytes(buffer, bytes, len_int)) { - goto fail; - } - bytes_written = len_int; -fail: - Py_XDECREF(bytes_obj); - return bytes_written; -} - - -/* Update Invalid Document error message to include doc. - */ -void handle_invalid_doc_error(PyObject* dict) { - PyObject *etype = NULL, *evalue = NULL, *etrace = NULL; - PyObject *msg = NULL, *dict_str = NULL, *new_msg = NULL; - PyErr_Fetch(&etype, &evalue, &etrace); - PyObject *InvalidDocument = _error("InvalidDocument"); - if (InvalidDocument == NULL) { - goto cleanup; - } - - if (evalue && PyErr_GivenExceptionMatches(etype, InvalidDocument)) { - PyObject *msg = PyObject_Str(evalue); - if (msg) { - // Prepend doc to the existing message - PyObject *dict_str = PyObject_Str(dict); - if (dict_str == NULL) { - goto cleanup; - } - const char * dict_str_utf8 = PyUnicode_AsUTF8(dict_str); - if (dict_str_utf8 == NULL) { - goto cleanup; - } - const char * msg_utf8 = PyUnicode_AsUTF8(msg); - if (msg_utf8 == NULL) { - goto cleanup; - } - PyObject *new_msg = PyUnicode_FromFormat("Invalid document %s | %s", dict_str_utf8, msg_utf8); - Py_DECREF(evalue); - Py_DECREF(etype); - etype = InvalidDocument; - InvalidDocument = NULL; - if (new_msg) { - evalue = new_msg; - } else { - evalue = msg; - } - } - PyErr_NormalizeException(&etype, &evalue, &etrace); - } -cleanup: - PyErr_Restore(etype, evalue, etrace); - Py_XDECREF(msg); - Py_XDECREF(InvalidDocument); - Py_XDECREF(dict_str); - Py_XDECREF(new_msg); -} - - -/* returns the number of bytes written or 0 on failure */ -int write_dict(PyObject* self, buffer_t buffer, - PyObject* dict, unsigned char check_keys, - const codec_options_t* options, unsigned char top_level) { - PyObject* key; - PyObject* iter; - char zero = 0; - int length; - int length_location; - struct module_state *state = GETSTATE(self); - long type_marker; - int is_dict = PyDict_Check(dict); - if (!state) { - return 0; - } - - if (!is_dict) { - /* check for RawBSONDocument */ - type_marker = _type_marker(dict, state->_type_marker_str); - if (type_marker < 0) { - return 0; - } - - if (101 == type_marker) { - return write_raw_doc(buffer, dict, state->_raw_str); - } - - if (!PyObject_IsInstance(dict, state->Mapping)) { - PyObject* repr; - if ((repr = PyObject_Repr(dict))) { - PyObject* errmsg = PyUnicode_FromString( - "encoder expected a mapping type but got: "); - if (errmsg) { - PyObject* error = PyUnicode_Concat(errmsg, repr); - if (error) { - PyErr_SetObject(PyExc_TypeError, error); - Py_DECREF(error); - } - Py_DECREF(errmsg); - Py_DECREF(repr); - } - else { - Py_DECREF(repr); - } - } else { - PyErr_SetString(PyExc_TypeError, - "encoder expected a mapping type"); - } - - return 0; - } - /* PyObject_IsInstance returns -1 on error */ - if (PyErr_Occurred()) { - return 0; - } - } - - length_location = pymongo_buffer_save_space(buffer, 4); - if (length_location == -1) { - return 0; - } - - /* Write _id first if this is a top level doc. */ - if (top_level) { - /* - * If "dict" is a defaultdict we don't want to call - * PyObject_GetItem on it. That would **create** - * an _id where one didn't previously exist (PYTHON-871). - */ - if (is_dict) { - /* PyDict_GetItem returns a borrowed reference. */ - PyObject* _id = PyDict_GetItem(dict, state->_id_str); - if (_id) { - if (!write_pair(self, buffer, "_id", 3, - _id, check_keys, options, 1)) { - return 0; - } - } - } else if (PyMapping_HasKey(dict, state->_id_str)) { - PyObject* _id = PyObject_GetItem(dict, state->_id_str); - if (!_id) { - return 0; - } - if (!write_pair(self, buffer, "_id", 3, - _id, check_keys, options, 1)) { - Py_DECREF(_id); - return 0; - } - /* PyObject_GetItem returns a new reference. */ - Py_DECREF(_id); - } - } - - if (is_dict) { - PyObject* value; - Py_ssize_t pos = 0; - while (PyDict_Next(dict, &pos, &key, &value)) { - if (!decode_and_write_pair(self, buffer, key, value, - check_keys, options, top_level)) { - if (PyErr_Occurred() && top_level) { - handle_invalid_doc_error(dict); - } - return 0; - } - } - } else { - iter = PyObject_GetIter(dict); - if (iter == NULL) { - return 0; - } - while ((key = PyIter_Next(iter)) != NULL) { - PyObject* value = PyObject_GetItem(dict, key); - if (!value) { - PyErr_SetObject(PyExc_KeyError, key); - Py_DECREF(key); - Py_DECREF(iter); - return 0; - } - if (!decode_and_write_pair(self, buffer, key, value, - check_keys, options, top_level)) { - if (PyErr_Occurred() && top_level) { - handle_invalid_doc_error(dict); - } - Py_DECREF(key); - Py_DECREF(value); - Py_DECREF(iter); - return 0; - } - Py_DECREF(key); - Py_DECREF(value); - } - Py_DECREF(iter); - if (PyErr_Occurred()) { - return 0; - } - } - - /* write null byte and fill in length */ - if (!buffer_write_bytes(buffer, &zero, 1)) { - return 0; - } - length = pymongo_buffer_get_position(buffer) - length_location; - buffer_write_int32_at_position( - buffer, length_location, (int32_t)length); - return length; -} - -static PyObject* _cbson_dict_to_bson(PyObject* self, PyObject* args) { - PyObject* dict; - PyObject* result; - unsigned char check_keys; - unsigned char top_level = 1; - PyObject* options_obj = NULL; - codec_options_t options; - buffer_t buffer; - PyObject* raw_bson_document_bytes_obj; - long type_marker; - struct module_state *state = GETSTATE(self); - if (!state) { - return NULL; - } - - if (!(PyArg_ParseTuple(args, "ObO|b", &dict, &check_keys, - &options_obj, &top_level) && - convert_codec_options(self, options_obj, &options))) { - return NULL; - } - - /* check for RawBSONDocument */ - type_marker = _type_marker(dict, state->_type_marker_str); - if (type_marker < 0) { - destroy_codec_options(&options); - return NULL; - } else if (101 == type_marker) { - destroy_codec_options(&options); - raw_bson_document_bytes_obj = PyObject_GetAttr(dict, state->_raw_str); - if (NULL == raw_bson_document_bytes_obj) { - return NULL; - } - return raw_bson_document_bytes_obj; - } - - buffer = pymongo_buffer_new(); - if (!buffer) { - destroy_codec_options(&options); - return NULL; - } - - if (!write_dict(self, buffer, dict, check_keys, &options, top_level)) { - destroy_codec_options(&options); - pymongo_buffer_free(buffer); - return NULL; - } - - /* objectify buffer */ - result = Py_BuildValue("y#", pymongo_buffer_get_buffer(buffer), - (Py_ssize_t)pymongo_buffer_get_position(buffer)); - destroy_codec_options(&options); - pymongo_buffer_free(buffer); - return result; -} - -/* - * Hook for optional decoding BSON documents to DBRef. - */ -static PyObject *_dbref_hook(PyObject* self, PyObject* value) { - struct module_state *state = GETSTATE(self); - PyObject* ref = NULL; - PyObject* id = NULL; - PyObject* database = NULL; - PyObject* ret = NULL; - int db_present = 0; - if (!state) { - return NULL; - } - - /* Decoding for DBRefs */ - if (PyMapping_HasKey(value, state->_dollar_ref_str) && PyMapping_HasKey(value, state->_dollar_id_str)) { /* DBRef */ - ref = PyObject_GetItem(value, state->_dollar_ref_str); - /* PyObject_GetItem returns NULL to indicate error. */ - if (!ref) { - goto invalid; - } - id = PyObject_GetItem(value, state->_dollar_id_str); - /* PyObject_GetItem returns NULL to indicate error. */ - if (!id) { - goto invalid; - } - - if (PyMapping_HasKey(value, state->_dollar_db_str)) { - database = PyObject_GetItem(value, state->_dollar_db_str); - if (!database) { - goto invalid; - } - db_present = 1; - } else { - database = Py_None; - Py_INCREF(database); - } - - // check types - if (!(PyUnicode_Check(ref) && (database == Py_None || PyUnicode_Check(database)))) { - ret = value; - goto invalid; - } - - PyMapping_DelItem(value, state->_dollar_ref_str); - PyMapping_DelItem(value, state->_dollar_id_str); - if (db_present) { - PyMapping_DelItem(value, state->_dollar_db_str); - } - - ret = PyObject_CallFunctionObjArgs(state->DBRef, ref, id, database, value, NULL); - Py_DECREF(value); - } else { - ret = value; - } -invalid: - Py_XDECREF(ref); - Py_XDECREF(id); - Py_XDECREF(database); - return ret; -} - -static PyObject* get_value(PyObject* self, PyObject* name, const char* buffer, - unsigned* position, unsigned char type, - unsigned max, const codec_options_t* options, int raw_array) { - struct module_state *state = GETSTATE(self); - PyObject* value = NULL; - if (!state) { - return NULL; - } - switch (type) { - case 1: - { - double d; - if (max < 8) { - goto invalid; - } - memcpy(&d, buffer + *position, 8); - value = PyFloat_FromDouble(BSON_DOUBLE_FROM_LE(d)); - *position += 8; - break; - } - case 2: - case 14: - { - uint32_t value_length; - if (max < 4) { - goto invalid; - } - memcpy(&value_length, buffer + *position, 4); - value_length = BSON_UINT32_FROM_LE(value_length); - /* Encoded string length + string */ - if (!value_length || max < value_length || max < 4 + value_length) { - goto invalid; - } - *position += 4; - /* Strings must end in \0 */ - if (buffer[*position + value_length - 1]) { - goto invalid; - } - value = PyUnicode_DecodeUTF8( - buffer + *position, value_length - 1, - options->unicode_decode_error_handler); - if (!value) { - goto invalid; - } - *position += value_length; - break; - } - case 3: - { - uint32_t size; - - if (max < 4) { - goto invalid; - } - memcpy(&size, buffer + *position, 4); - size = BSON_UINT32_FROM_LE(size); - if (size < BSON_MIN_SIZE || max < size) { - goto invalid; - } - /* Check for bad eoo */ - if (buffer[*position + size - 1]) { - goto invalid; - } - - value = elements_to_dict(self, buffer + *position, - size, options); - if (!value) { - goto invalid; - } - - if (options->is_raw_bson) { - *position += size; - break; - } - - /* Hook for DBRefs */ - value = _dbref_hook(self, value); - if (!value) { - goto invalid; - } - - *position += size; - break; - } - case 4: - { - uint32_t size, end; - - if (max < 4) { - goto invalid; - } - memcpy(&size, buffer + *position, 4); - size = BSON_UINT32_FROM_LE(size); - if (size < BSON_MIN_SIZE || max < size) { - goto invalid; - } - - end = *position + size - 1; - /* Check for bad eoo */ - if (buffer[end]) { - goto invalid; - } - - if (raw_array != 0) { - // Treat it as a binary buffer. - value = PyBytes_FromStringAndSize(buffer + *position, size); - *position += size; - break; - } - - *position += 4; - - value = PyList_New(0); - if (!value) { - goto invalid; - } - while (*position < end) { - PyObject* to_append; - - unsigned char bson_type = (unsigned char)buffer[(*position)++]; - - size_t key_size = strlen(buffer + *position); - if (max < key_size) { - Py_DECREF(value); - goto invalid; - } - /* just skip the key, they're in order. */ - *position += (unsigned)key_size + 1; - if (Py_EnterRecursiveCall(" while decoding a list value")) { - Py_DECREF(value); - goto invalid; - } - to_append = get_value(self, name, buffer, position, bson_type, - max - (unsigned)key_size, options, raw_array); - Py_LeaveRecursiveCall(); - if (!to_append) { - Py_DECREF(value); - goto invalid; - } - if (PyList_Append(value, to_append) < 0) { - Py_DECREF(value); - Py_DECREF(to_append); - goto invalid; - } - Py_DECREF(to_append); - } - if (*position != end) { - goto invalid; - } - (*position)++; - break; - } - case 5: - { - PyObject* data; - PyObject* st; - uint32_t length, length2; - unsigned char subtype; - - if (max < 5) { - goto invalid; - } - memcpy(&length, buffer + *position, 4); - length = BSON_UINT32_FROM_LE(length); - if (max < length) { - goto invalid; - } - - subtype = (unsigned char)buffer[*position + 4]; - *position += 5; - if (subtype == 2) { - if (length < 4) { - goto invalid; - } - memcpy(&length2, buffer + *position, 4); - length2 = BSON_UINT32_FROM_LE(length2); - if (length2 != length - 4) { - goto invalid; - } - } - /* Python3 special case. Decode BSON binary subtype 0 to bytes. */ - if (subtype == 0) { - value = PyBytes_FromStringAndSize(buffer + *position, length); - *position += length; - break; - } - if (subtype == 2) { - data = PyBytes_FromStringAndSize(buffer + *position + 4, length - 4); - } else { - data = PyBytes_FromStringAndSize(buffer + *position, length); - } - if (!data) { - goto invalid; - } - /* Encode as UUID or Binary based on options->uuid_rep */ - if (subtype == 3 || subtype == 4) { - PyObject* binary_value = NULL; - char uuid_rep = options->uuid_rep; - - /* UUID should always be 16 bytes */ - if (length != 16) { - goto uuiderror; - } - - binary_value = PyObject_CallFunction(state->Binary, "(Oi)", data, subtype); - if (binary_value == NULL) { - goto uuiderror; - } - - if ((uuid_rep == UNSPECIFIED) || - (subtype == 4 && uuid_rep != STANDARD) || - (subtype == 3 && uuid_rep == STANDARD)) { - value = binary_value; - Py_INCREF(value); - } else { - PyObject *uuid_rep_obj = PyLong_FromLong(uuid_rep); - if (!uuid_rep_obj) { - goto uuiderror; - } - value = PyObject_CallMethodObjArgs(binary_value, state->_as_uuid_str, uuid_rep_obj, NULL); - Py_DECREF(uuid_rep_obj); - } - - uuiderror: - Py_XDECREF(binary_value); - Py_DECREF(data); - if (!value) { - goto invalid; - } - *position += length; - break; - } - - st = PyLong_FromLong(subtype); - if (!st) { - Py_DECREF(data); - goto invalid; - } - value = PyObject_CallFunctionObjArgs(state->Binary, data, st, NULL); - Py_DECREF(st); - Py_DECREF(data); - if (!value) { - goto invalid; - } - *position += length; - break; - } - case 6: - case 10: - { - value = Py_None; - Py_INCREF(value); - break; - } - case 7: - { - if (max < 12) { - goto invalid; - } - value = PyObject_CallFunction(state->ObjectId, "y#", buffer + *position, (Py_ssize_t)12); - *position += 12; - break; - } - case 8: - { - char boolean_raw = buffer[(*position)++]; - if (0 == boolean_raw) { - value = Py_False; - } else if (1 == boolean_raw) { - value = Py_True; - } else { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_Format(InvalidBSON, "invalid boolean value: %x", boolean_raw); - Py_DECREF(InvalidBSON); - } - return NULL; - } - Py_INCREF(value); - break; - } - case 9: - { - int64_t millis; - if (max < 8) { - goto invalid; - } - memcpy(&millis, buffer + *position, 8); - millis = (int64_t)BSON_UINT64_FROM_LE(millis); - *position += 8; - - value = decode_datetime(self, millis, options); - break; - } - case 11: - { - PyObject* pattern; - int flags; - size_t flags_length, i; - size_t pattern_length = strlen(buffer + *position); - if (pattern_length > BSON_MAX_SIZE || max < pattern_length) { - goto invalid; - } - pattern = PyUnicode_DecodeUTF8( - buffer + *position, pattern_length, - options->unicode_decode_error_handler); - if (!pattern) { - goto invalid; - } - *position += (unsigned)pattern_length + 1; - flags_length = strlen(buffer + *position); - if (flags_length > BSON_MAX_SIZE || - (BSON_MAX_SIZE - pattern_length) < flags_length) { - Py_DECREF(pattern); - goto invalid; - } - if (max < pattern_length + flags_length) { - Py_DECREF(pattern); - goto invalid; - } - flags = 0; - for (i = 0; i < flags_length; i++) { - if (buffer[*position + i] == 'i') { - flags |= 2; - } else if (buffer[*position + i] == 'l') { - flags |= 4; - } else if (buffer[*position + i] == 'm') { - flags |= 8; - } else if (buffer[*position + i] == 's') { - flags |= 16; - } else if (buffer[*position + i] == 'u') { - flags |= 32; - } else if (buffer[*position + i] == 'x') { - flags |= 64; - } - } - *position += (unsigned)flags_length + 1; - - value = PyObject_CallFunction(state->Regex, "Oi", pattern, flags); - Py_DECREF(pattern); - break; - } - case 12: - { - uint32_t coll_length; - PyObject* collection; - PyObject* id = NULL; - - if (max < 4) { - goto invalid; - } - memcpy(&coll_length, buffer + *position, 4); - coll_length = BSON_UINT32_FROM_LE(coll_length); - /* Encoded string length + string + 12 byte ObjectId */ - if (!coll_length || max < coll_length || max < 4 + coll_length + 12) { - goto invalid; - } - *position += 4; - /* Strings must end in \0 */ - if (buffer[*position + coll_length - 1]) { - goto invalid; - } - - collection = PyUnicode_DecodeUTF8( - buffer + *position, coll_length - 1, - options->unicode_decode_error_handler); - if (!collection) { - goto invalid; - } - *position += coll_length; - - id = PyObject_CallFunction(state->ObjectId, "y#", buffer + *position, (Py_ssize_t)12); - if (!id) { - Py_DECREF(collection); - goto invalid; - } - *position += 12; - value = PyObject_CallFunctionObjArgs(state->DBRef, collection, id, NULL); - Py_DECREF(collection); - Py_DECREF(id); - break; - } - case 13: - { - PyObject* code; - uint32_t value_length; - if (max < 4) { - goto invalid; - } - memcpy(&value_length, buffer + *position, 4); - value_length = BSON_UINT32_FROM_LE(value_length); - /* Encoded string length + string */ - if (!value_length || max < value_length || max < 4 + value_length) { - goto invalid; - } - *position += 4; - /* Strings must end in \0 */ - if (buffer[*position + value_length - 1]) { - goto invalid; - } - code = PyUnicode_DecodeUTF8( - buffer + *position, value_length - 1, - options->unicode_decode_error_handler); - if (!code) { - goto invalid; - } - *position += value_length; - value = PyObject_CallFunctionObjArgs(state->Code, code, NULL, NULL); - Py_DECREF(code); - break; - } - case 15: - { - uint32_t c_w_s_size; - uint32_t code_size; - uint32_t scope_size; - uint32_t len; - PyObject* code; - PyObject* scope; - - if (max < 8) { - goto invalid; - } - - memcpy(&c_w_s_size, buffer + *position, 4); - c_w_s_size = BSON_UINT32_FROM_LE(c_w_s_size); - *position += 4; - - if (max < c_w_s_size) { - goto invalid; - } - - memcpy(&code_size, buffer + *position, 4); - code_size = BSON_UINT32_FROM_LE(code_size); - /* code_w_scope length + code length + code + scope length */ - len = 4 + 4 + code_size + 4; - if (!code_size || max < code_size || max < len || len < code_size) { - goto invalid; - } - *position += 4; - /* Strings must end in \0 */ - if (buffer[*position + code_size - 1]) { - goto invalid; - } - code = PyUnicode_DecodeUTF8( - buffer + *position, code_size - 1, - options->unicode_decode_error_handler); - if (!code) { - goto invalid; - } - *position += code_size; - - memcpy(&scope_size, buffer + *position, 4); - scope_size = BSON_UINT32_FROM_LE(scope_size); - /* code length + code + scope length + scope */ - len = 4 + 4 + code_size + scope_size; - if (scope_size < BSON_MIN_SIZE || len != c_w_s_size || len < scope_size) { - Py_DECREF(code); - goto invalid; - } - - /* Check for bad eoo */ - if (buffer[*position + scope_size - 1]) { - goto invalid; - } - scope = elements_to_dict(self, buffer + *position, - scope_size, options); - if (!scope) { - Py_DECREF(code); - goto invalid; - } - *position += scope_size; - - value = PyObject_CallFunctionObjArgs(state->Code, code, scope, NULL); - Py_DECREF(code); - Py_DECREF(scope); - break; - } - case 16: - { - int32_t i; - if (max < 4) { - goto invalid; - } - memcpy(&i, buffer + *position, 4); - i = (int32_t)BSON_UINT32_FROM_LE(i); - value = PyLong_FromLong(i); - if (!value) { - goto invalid; - } - *position += 4; - break; - } - case 17: - { - uint32_t time, inc; - if (max < 8) { - goto invalid; - } - memcpy(&inc, buffer + *position, 4); - memcpy(&time, buffer + *position + 4, 4); - inc = BSON_UINT32_FROM_LE(inc); - time = BSON_UINT32_FROM_LE(time); - value = PyObject_CallFunction(state->Timestamp, "II", time, inc); - *position += 8; - break; - } - case 18: - { - int64_t ll; - if (max < 8) { - goto invalid; - } - memcpy(&ll, buffer + *position, 8); - ll = (int64_t)BSON_UINT64_FROM_LE(ll); - value = PyObject_CallFunction(state->BSONInt64, "L", ll); - *position += 8; - break; - } - case 19: - { - if (max < 16) { - goto invalid; - } - PyObject *_bytes_obj = PyBytes_FromStringAndSize(buffer + *position, (Py_ssize_t)16); - if (!_bytes_obj) { - goto invalid; - } - value = PyObject_CallMethodObjArgs(state->Decimal128, state->_from_bid_str, _bytes_obj, NULL); - Py_DECREF(_bytes_obj); - *position += 16; - break; - } - case 255: - { - value = PyObject_CallFunctionObjArgs(state->MinKey, NULL); - break; - } - case 127: - { - value = PyObject_CallFunctionObjArgs(state->MaxKey, NULL); - break; - } - default: - { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyObject* bobj = PyBytes_FromFormat("%c", type); - if (bobj) { - PyObject* repr = PyObject_Repr(bobj); - Py_DECREF(bobj); - /* - * See http://bugs.python.org/issue22023 for why we can't - * just use PyUnicode_FromFormat with %S or %R to do this - * work. - */ - if (repr) { - PyObject* left = PyUnicode_FromString( - "Detected unknown BSON type "); - if (left) { - PyObject* lmsg = PyUnicode_Concat(left, repr); - Py_DECREF(left); - if (lmsg) { - PyObject* errmsg = PyUnicode_FromFormat( - "%U for fieldname '%U'. Are you using the " - "latest driver version?", lmsg, name); - if (errmsg) { - PyErr_SetObject(InvalidBSON, errmsg); - Py_DECREF(errmsg); - } - Py_DECREF(lmsg); - } - } - Py_DECREF(repr); - } - } - Py_DECREF(InvalidBSON); - } - goto invalid; - } - } - - if (value) { - if (!options->type_registry.is_decoder_empty) { - PyObject* value_type = NULL; - PyObject* converter = NULL; - value_type = PyObject_Type(value); - if (value_type == NULL) { - goto invalid; - } - converter = PyDict_GetItem(options->type_registry.decoder_map, value_type); - if (converter != NULL) { - PyObject* new_value = PyObject_CallFunctionObjArgs(converter, value, NULL); - Py_DECREF(value_type); - Py_DECREF(value); - return new_value; - } else { - Py_DECREF(value_type); - return value; - } - } - return value; - } - - invalid: - - /* - * Wrap any non-InvalidBSON errors in InvalidBSON. - */ - if (PyErr_Occurred()) { - PyObject *etype = NULL, *evalue = NULL, *etrace = NULL; - PyObject *InvalidBSON = NULL; - - /* - * Calling _error clears the error state, so fetch it first. - */ - PyErr_Fetch(&etype, &evalue, &etrace); - - /* Dont reraise anything but PyExc_Exceptions as InvalidBSON. */ - if (PyErr_GivenExceptionMatches(etype, PyExc_Exception)) { - InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - if (!PyErr_GivenExceptionMatches(etype, InvalidBSON)) { - /* - * Raise InvalidBSON(str(e)). - */ - Py_DECREF(etype); - etype = InvalidBSON; - - if (evalue) { - PyObject *msg = PyObject_Str(evalue); - Py_DECREF(evalue); - evalue = msg; - } - PyErr_NormalizeException(&etype, &evalue, &etrace); - } else { - /* - * The current exception matches InvalidBSON, so we don't - * need this reference after all. - */ - Py_DECREF(InvalidBSON); - } - } - } - /* Steals references to args. */ - PyErr_Restore(etype, evalue, etrace); - } else { - PyObject *InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, "invalid length or type code"); - Py_DECREF(InvalidBSON); - } - } - return NULL; -} - -/* - * Get the next 'name' and 'value' from a document in a string, whose position - * is provided. - * - * Returns the position of the next element in the document, or -1 on error. - */ -static int _element_to_dict(PyObject* self, const char* string, - unsigned position, unsigned max, - const codec_options_t* options, - int raw_array, - PyObject** name, PyObject** value) { - unsigned char type = (unsigned char)string[position++]; - size_t name_length = strlen(string + position); - if (name_length > BSON_MAX_SIZE || position + name_length >= max) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, "field name too large"); - Py_DECREF(InvalidBSON); - } - return -1; - } - *name = PyUnicode_DecodeUTF8( - string + position, name_length, - options->unicode_decode_error_handler); - if (!*name) { - /* If NULL is returned then wrap the UnicodeDecodeError - in an InvalidBSON error */ - PyObject *etype = NULL, *evalue = NULL, *etrace = NULL; - PyObject *InvalidBSON = NULL; - - PyErr_Fetch(&etype, &evalue, &etrace); - if (PyErr_GivenExceptionMatches(etype, PyExc_Exception)) { - InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - Py_DECREF(etype); - etype = InvalidBSON; - - if (evalue) { - PyObject *msg = PyObject_Str(evalue); - Py_DECREF(evalue); - evalue = msg; - } - PyErr_NormalizeException(&etype, &evalue, &etrace); - } - } - PyErr_Restore(etype, evalue, etrace); - return -1; - } - position += (unsigned)name_length + 1; - *value = get_value(self, *name, string, &position, type, - max - position, options, raw_array); - if (!*value) { - Py_DECREF(*name); - return -1; - } - return position; -} - -static PyObject* _cbson_element_to_dict(PyObject* self, PyObject* args) { - /* TODO: Support buffer protocol */ - char* string; - PyObject* bson; - PyObject* options_obj = NULL; - codec_options_t options; - unsigned position; - unsigned max; - int new_position; - int raw_array = 0; - PyObject* name; - PyObject* value; - PyObject* result_tuple; - - if (!(PyArg_ParseTuple(args, "OIIOp", &bson, &position, &max, - &options_obj, &raw_array) && - convert_codec_options(self, options_obj, &options))) { - return NULL; - } - - if (!PyBytes_Check(bson)) { - PyErr_SetString(PyExc_TypeError, "argument to _element_to_dict must be a bytes object"); - return NULL; - } - string = PyBytes_AS_STRING(bson); - - new_position = _element_to_dict(self, string, position, max, &options, raw_array, &name, &value); - if (new_position < 0) { - return NULL; - } - - result_tuple = Py_BuildValue("NNi", name, value, new_position); - if (!result_tuple) { - Py_DECREF(name); - Py_DECREF(value); - return NULL; - } - - destroy_codec_options(&options); - return result_tuple; -} - -static PyObject* _elements_to_dict(PyObject* self, const char* string, - unsigned max, - const codec_options_t* options) { - unsigned position = 0; - PyObject* dict = PyObject_CallObject(options->document_class, NULL); - if (!dict) { - return NULL; - } - int raw_array = 0; - while (position < max) { - PyObject* name = NULL; - PyObject* value = NULL; - int new_position; - - new_position = _element_to_dict( - self, string, position, max, options, raw_array, &name, &value); - if (new_position < 0) { - Py_DECREF(dict); - return NULL; - } else { - position = (unsigned)new_position; - } - - PyObject_SetItem(dict, name, value); - Py_DECREF(name); - Py_DECREF(value); - } - return dict; -} - -static PyObject* elements_to_dict(PyObject* self, const char* string, - unsigned max, - const codec_options_t* options) { - PyObject* result; - if (options->is_raw_bson) { - return PyObject_CallFunction( - options->document_class, "y#O", - string, max, options->options_obj); - } - if (Py_EnterRecursiveCall(" while decoding a BSON document")) - return NULL; - result = _elements_to_dict(self, string + 4, max - 5, options); - Py_LeaveRecursiveCall(); - return result; -} - -static int _get_buffer(PyObject *exporter, Py_buffer *view) { - if (PyObject_GetBuffer(exporter, view, PyBUF_SIMPLE) == -1) { - return 0; - } - if (!PyBuffer_IsContiguous(view, 'C')) { - PyErr_SetString(PyExc_ValueError, - "must be a contiguous buffer"); - goto fail; - } - if (!view->buf || view->len < 0) { - PyErr_SetString(PyExc_ValueError, "invalid buffer"); - goto fail; - } - if (view->itemsize != 1) { - PyErr_SetString(PyExc_ValueError, - "buffer data must be ascii or utf8"); - goto fail; - } - return 1; -fail: - PyBuffer_Release(view); - return 0; -} - -static PyObject* _cbson_bson_to_dict(PyObject* self, PyObject* args) { - int32_t size; - Py_ssize_t total_size; - const char* string; - PyObject* bson = NULL; - codec_options_t options; - PyObject* result = NULL; - PyObject* options_obj; - Py_buffer view = {0}; - - if (! (PyArg_ParseTuple(args, "OO", &bson, &options_obj) && - convert_codec_options(self, options_obj, &options))) { - return result; - } - - if (!_get_buffer(bson, &view)) { - destroy_codec_options(&options); - return result; - } - - total_size = view.len; - - if (total_size < BSON_MIN_SIZE) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, - "not enough data for a BSON document"); - Py_DECREF(InvalidBSON); - } - goto done;; - } - - string = (char*)view.buf; - memcpy(&size, string, 4); - size = (int32_t)BSON_UINT32_FROM_LE(size); - if (size < BSON_MIN_SIZE) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, "invalid message size"); - Py_DECREF(InvalidBSON); - } - goto done; - } - - if (total_size < size || total_size > BSON_MAX_SIZE) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, "objsize too large"); - Py_DECREF(InvalidBSON); - } - goto done; - } - - if (size != total_size || string[size - 1]) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, "bad eoo"); - Py_DECREF(InvalidBSON); - } - goto done; - } - - result = elements_to_dict(self, string, (unsigned)size, &options); -done: - PyBuffer_Release(&view); - destroy_codec_options(&options); - return result; -} - -static PyObject* _cbson_decode_all(PyObject* self, PyObject* args) { - int32_t size; - Py_ssize_t total_size; - const char* string; - PyObject* bson; - PyObject* dict; - PyObject* result = NULL; - codec_options_t options; - PyObject* options_obj = NULL; - Py_buffer view = {0}; - - if (!(PyArg_ParseTuple(args, "OO", &bson, &options_obj) && - convert_codec_options(self, options_obj, &options))) { - return NULL; - } - - if (!_get_buffer(bson, &view)) { - destroy_codec_options(&options); - return NULL; - } - total_size = view.len; - string = (char*)view.buf; - - if (!(result = PyList_New(0))) { - goto fail; - } - - while (total_size > 0) { - if (total_size < BSON_MIN_SIZE) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, - "not enough data for a BSON document"); - Py_DECREF(InvalidBSON); - } - Py_DECREF(result); - goto fail; - } - - memcpy(&size, string, 4); - size = (int32_t)BSON_UINT32_FROM_LE(size); - if (size < BSON_MIN_SIZE) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, "invalid message size"); - Py_DECREF(InvalidBSON); - } - Py_DECREF(result); - goto fail; - } - - if (total_size < size) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, "objsize too large"); - Py_DECREF(InvalidBSON); - } - Py_DECREF(result); - goto fail; - } - - if (string[size - 1]) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, "bad eoo"); - Py_DECREF(InvalidBSON); - } - Py_DECREF(result); - goto fail; - } - - dict = elements_to_dict(self, string, (unsigned)size, &options); - if (!dict) { - Py_DECREF(result); - goto fail; - } - if (PyList_Append(result, dict) < 0) { - Py_DECREF(dict); - Py_DECREF(result); - goto fail; - } - Py_DECREF(dict); - string += size; - total_size -= size; - } - goto done; -fail: - result = NULL; -done: - PyBuffer_Release(&view); - destroy_codec_options(&options); - return result; -} - - -static PyObject* _cbson_array_of_documents_to_buffer(PyObject* self, PyObject* args) { - uint32_t size; - uint32_t value_length; - uint32_t position = 0; - buffer_t buffer; - const char* string; - PyObject* arr; - PyObject* result = NULL; - Py_buffer view = {0}; - - if (!PyArg_ParseTuple(args, "O", &arr)) { - return NULL; - } - - if (!_get_buffer(arr, &view)) { - return NULL; - } - - buffer = pymongo_buffer_new(); - if (!buffer) { - PyBuffer_Release(&view); - return NULL; - } - - string = (char*)view.buf; - - if (view.len < BSON_MIN_SIZE) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, - "not enough data for a BSON document"); - Py_DECREF(InvalidBSON); - } - goto fail; - } - - memcpy(&size, string, 4); - size = BSON_UINT32_FROM_LE(size); - - /* validate the size of the array */ - if (view.len != (int32_t)size || (int32_t)size < BSON_MIN_SIZE) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, "objsize too large"); - Py_DECREF(InvalidBSON); - } - goto fail; - } - - if (string[size - 1]) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, "bad eoo"); - Py_DECREF(InvalidBSON); - } - goto fail; - } - - /* save space for length */ - if (pymongo_buffer_save_space(buffer, size) == -1) { - goto fail; - } - pymongo_buffer_update_position(buffer, 0); - - position += 4; - while (position < size - 1) { - // Verify the value is an object. - unsigned char type = (unsigned char)string[position]; - if (type != 3) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, "array element was not an object"); - Py_DECREF(InvalidBSON); - } - goto fail; - } - - // Just skip the keys. - position = position + strlen(string + position) + 1; - - if (position >= size || (size - position) < BSON_MIN_SIZE) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, "invalid array content"); - Py_DECREF(InvalidBSON); - } - goto fail; - } - - memcpy(&value_length, string + position, 4); - value_length = BSON_UINT32_FROM_LE(value_length); - if (value_length < BSON_MIN_SIZE) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, "invalid message size"); - Py_DECREF(InvalidBSON); - } - goto fail; - } - - if (pymongo_buffer_write(buffer, string + position, value_length) == 1) { - goto fail; - } - position += value_length; - } - - if (position != size - 1) { - PyObject* InvalidBSON = _error("InvalidBSON"); - if (InvalidBSON) { - PyErr_SetString(InvalidBSON, - "bad object or element length"); - Py_DECREF(InvalidBSON); - } - goto fail; - } - - /* objectify buffer */ - result = Py_BuildValue("y#", pymongo_buffer_get_buffer(buffer), - (Py_ssize_t)pymongo_buffer_get_position(buffer)); - goto done; -fail: - result = NULL; -done: - PyBuffer_Release(&view); - pymongo_buffer_free(buffer); - return result; -} - - -static PyMethodDef _CBSONMethods[] = { - {"_dict_to_bson", _cbson_dict_to_bson, METH_VARARGS, - "convert a dictionary to a string containing its BSON representation."}, - {"_bson_to_dict", _cbson_bson_to_dict, METH_VARARGS, - "convert a BSON string to a SON object."}, - {"_decode_all", _cbson_decode_all, METH_VARARGS, - "convert binary data to a sequence of documents."}, - {"_element_to_dict", _cbson_element_to_dict, METH_VARARGS, - "Decode a single key, value pair."}, - {"_array_of_documents_to_buffer", _cbson_array_of_documents_to_buffer, METH_VARARGS, "Convert raw array of documents to a stream of BSON documents"}, - {"_test_long_long_to_str", _test_long_long_to_str, METH_VARARGS, "Test conversion of extreme and common Py_ssize_t values to str."}, - {NULL, NULL, 0, NULL} -}; - -#define INITERROR return -1; -static int _cbson_traverse(PyObject *m, visitproc visit, void *arg) { - struct module_state *state = GETSTATE(m); - if (!state) { - return 0; - } - Py_VISIT(state->Binary); - Py_VISIT(state->Code); - Py_VISIT(state->ObjectId); - Py_VISIT(state->DBRef); - Py_VISIT(state->Regex); - Py_VISIT(state->UUID); - Py_VISIT(state->Timestamp); - Py_VISIT(state->MinKey); - Py_VISIT(state->MaxKey); - Py_VISIT(state->UTC); - Py_VISIT(state->REType); - Py_VISIT(state->_type_marker_str); - Py_VISIT(state->_flags_str); - Py_VISIT(state->_pattern_str); - Py_VISIT(state->_encoder_map_str); - Py_VISIT(state->_decoder_map_str); - Py_VISIT(state->_fallback_encoder_str); - Py_VISIT(state->_raw_str); - Py_VISIT(state->_subtype_str); - Py_VISIT(state->_binary_str); - Py_VISIT(state->_scope_str); - Py_VISIT(state->_inc_str); - Py_VISIT(state->_time_str); - Py_VISIT(state->_bid_str); - Py_VISIT(state->_replace_str); - Py_VISIT(state->_astimezone_str); - Py_VISIT(state->_id_str); - Py_VISIT(state->_dollar_ref_str); - Py_VISIT(state->_dollar_id_str); - Py_VISIT(state->_dollar_db_str); - Py_VISIT(state->_tzinfo_str); - Py_VISIT(state->_as_doc_str); - Py_VISIT(state->_utcoffset_str); - Py_VISIT(state->_from_uuid_str); - Py_VISIT(state->_as_uuid_str); - Py_VISIT(state->_from_bid_str); - Py_VISIT(state->min_datetime); - Py_VISIT(state->max_datetime); - Py_VISIT(state->replace_args); - Py_VISIT(state->replace_kwargs); - return 0; -} - -static int _cbson_clear(PyObject *m) { - struct module_state *state = GETSTATE(m); - if (!state) { - return 0; - } - Py_CLEAR(state->Binary); - Py_CLEAR(state->Code); - Py_CLEAR(state->ObjectId); - Py_CLEAR(state->DBRef); - Py_CLEAR(state->Regex); - Py_CLEAR(state->UUID); - Py_CLEAR(state->Timestamp); - Py_CLEAR(state->MinKey); - Py_CLEAR(state->MaxKey); - Py_CLEAR(state->UTC); - Py_CLEAR(state->REType); - Py_CLEAR(state->_type_marker_str); - Py_CLEAR(state->_flags_str); - Py_CLEAR(state->_pattern_str); - Py_CLEAR(state->_encoder_map_str); - Py_CLEAR(state->_decoder_map_str); - Py_CLEAR(state->_fallback_encoder_str); - Py_CLEAR(state->_raw_str); - Py_CLEAR(state->_subtype_str); - Py_CLEAR(state->_binary_str); - Py_CLEAR(state->_scope_str); - Py_CLEAR(state->_inc_str); - Py_CLEAR(state->_time_str); - Py_CLEAR(state->_bid_str); - Py_CLEAR(state->_replace_str); - Py_CLEAR(state->_astimezone_str); - Py_CLEAR(state->_id_str); - Py_CLEAR(state->_dollar_ref_str); - Py_CLEAR(state->_dollar_id_str); - Py_CLEAR(state->_dollar_db_str); - Py_CLEAR(state->_tzinfo_str); - Py_CLEAR(state->_as_doc_str); - Py_CLEAR(state->_utcoffset_str); - Py_CLEAR(state->_from_uuid_str); - Py_CLEAR(state->_as_uuid_str); - Py_CLEAR(state->_from_bid_str); - Py_CLEAR(state->min_datetime); - Py_CLEAR(state->max_datetime); - Py_CLEAR(state->replace_args); - Py_CLEAR(state->replace_kwargs); - return 0; -} - -/* Multi-phase extension module initialization code. - * See https://peps.python.org/pep-0489/. -*/ -static int -_cbson_exec(PyObject *m) -{ - PyObject *c_api_object; - static void *_cbson_API[_cbson_API_POINTER_COUNT]; - - PyDateTime_IMPORT; - if (PyDateTimeAPI == NULL) { - INITERROR; - } - - /* Export C API */ - _cbson_API[_cbson_buffer_write_bytes_INDEX] = (void *) buffer_write_bytes; - _cbson_API[_cbson_write_dict_INDEX] = (void *) write_dict; - _cbson_API[_cbson_write_pair_INDEX] = (void *) write_pair; - _cbson_API[_cbson_decode_and_write_pair_INDEX] = (void *) decode_and_write_pair; - _cbson_API[_cbson_convert_codec_options_INDEX] = (void *) convert_codec_options; - _cbson_API[_cbson_destroy_codec_options_INDEX] = (void *) destroy_codec_options; - _cbson_API[_cbson_buffer_write_double_INDEX] = (void *) buffer_write_double; - _cbson_API[_cbson_buffer_write_int32_INDEX] = (void *) buffer_write_int32; - _cbson_API[_cbson_buffer_write_int64_INDEX] = (void *) buffer_write_int64; - _cbson_API[_cbson_buffer_write_int32_at_position_INDEX] = - (void *) buffer_write_int32_at_position; - _cbson_API[_cbson_downcast_and_check_INDEX] = (void *) _downcast_and_check; - - c_api_object = PyCapsule_New((void *) _cbson_API, "_cbson._C_API", NULL); - if (c_api_object == NULL) - INITERROR; - - /* Import several python objects */ - if (_load_python_objects(m)) { - Py_DECREF(c_api_object); - Py_DECREF(m); - INITERROR; - } - - if (PyModule_AddObject(m, "_C_API", c_api_object) < 0) { - Py_DECREF(c_api_object); - Py_DECREF(m); - INITERROR; - } - - return 0; -} - -static PyModuleDef_Slot _cbson_slots[] = { - {Py_mod_exec, _cbson_exec}, -#if defined(Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED) - {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED}, -#endif -#if PY_VERSION_HEX >= 0x030D0000 - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, -#endif - {0, NULL}, -}; - - -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "_cbson", - NULL, - sizeof(struct module_state), - _CBSONMethods, - _cbson_slots, - _cbson_traverse, - _cbson_clear, - NULL -}; - -PyMODINIT_FUNC -PyInit__cbson(void) -{ - return PyModuleDef_Init(&moduledef); -} diff --git a/venv/Lib/site-packages/bson/_cbsonmodule.h b/venv/Lib/site-packages/bson/_cbsonmodule.h deleted file mode 100644 index 3be2b74..0000000 --- a/venv/Lib/site-packages/bson/_cbsonmodule.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "bson-endian.h" - -#ifndef _CBSONMODULE_H -#define _CBSONMODULE_H - -#if defined(WIN32) || defined(_MSC_VER) -/* - * This macro is basically an implementation of asprintf for win32 - * We print to the provided buffer to get the string value as an int. - * USE LL2STR. This is kept only to test LL2STR. - */ -#if defined(_MSC_VER) && (_MSC_VER >= 1400) -#define INT2STRING(buffer, i) \ - _snprintf_s((buffer), \ - _scprintf("%lld", (i)) + 1, \ - _scprintf("%lld", (i)) + 1, \ - "%lld", \ - (i)) -#define STRCAT(dest, n, src) strcat_s((dest), (n), (src)) -#else -#define INT2STRING(buffer, i) \ - _snprintf((buffer), \ - _scprintf("%lld", (i)) + 1, \ - "%lld", \ - (i)) -#define STRCAT(dest, n, src) strcat((dest), (src)) -#endif -#else -#define INT2STRING(buffer, i) snprintf((buffer), sizeof((buffer)), "%lld", (i)) -#define STRCAT(dest, n, src) strcat((dest), (src)) -#endif - -/* Just enough space in char array to hold LLONG_MIN and null terminator */ -#define BUF_SIZE 21 -/* Converts integer to its string representation in decimal notation. */ -extern int cbson_long_long_to_str(long long int num, char* str, size_t size); -#define LL2STR(buffer, i) cbson_long_long_to_str((i), (buffer), sizeof(buffer)) - -typedef struct type_registry_t { - PyObject* encoder_map; - PyObject* decoder_map; - PyObject* fallback_encoder; - PyObject* registry_obj; - unsigned char is_encoder_empty; - unsigned char is_decoder_empty; - unsigned char has_fallback_encoder; -} type_registry_t; - -typedef struct codec_options_t { - PyObject* document_class; - unsigned char tz_aware; - unsigned char uuid_rep; - char* unicode_decode_error_handler; - PyObject* tzinfo; - type_registry_t type_registry; - unsigned char datetime_conversion; - PyObject* options_obj; - unsigned char is_raw_bson; -} codec_options_t; - -/* C API functions */ -#define _cbson_buffer_write_bytes_INDEX 0 -#define _cbson_buffer_write_bytes_RETURN int -#define _cbson_buffer_write_bytes_PROTO (buffer_t buffer, const char* data, int size) - -#define _cbson_write_dict_INDEX 1 -#define _cbson_write_dict_RETURN int -#define _cbson_write_dict_PROTO (PyObject* self, buffer_t buffer, PyObject* dict, unsigned char check_keys, const codec_options_t* options, unsigned char top_level) - -#define _cbson_write_pair_INDEX 2 -#define _cbson_write_pair_RETURN int -#define _cbson_write_pair_PROTO (PyObject* self, buffer_t buffer, const char* name, int name_length, PyObject* value, unsigned char check_keys, const codec_options_t* options, unsigned char allow_id) - -#define _cbson_decode_and_write_pair_INDEX 3 -#define _cbson_decode_and_write_pair_RETURN int -#define _cbson_decode_and_write_pair_PROTO (PyObject* self, buffer_t buffer, PyObject* key, PyObject* value, unsigned char check_keys, const codec_options_t* options, unsigned char top_level) - -#define _cbson_convert_codec_options_INDEX 4 -#define _cbson_convert_codec_options_RETURN int -#define _cbson_convert_codec_options_PROTO (PyObject* self, PyObject* options_obj, codec_options_t* options) - -#define _cbson_destroy_codec_options_INDEX 5 -#define _cbson_destroy_codec_options_RETURN void -#define _cbson_destroy_codec_options_PROTO (codec_options_t* options) - -#define _cbson_buffer_write_double_INDEX 6 -#define _cbson_buffer_write_double_RETURN int -#define _cbson_buffer_write_double_PROTO (buffer_t buffer, double data) - -#define _cbson_buffer_write_int32_INDEX 7 -#define _cbson_buffer_write_int32_RETURN int -#define _cbson_buffer_write_int32_PROTO (buffer_t buffer, int32_t data) - -#define _cbson_buffer_write_int64_INDEX 8 -#define _cbson_buffer_write_int64_RETURN int -#define _cbson_buffer_write_int64_PROTO (buffer_t buffer, int64_t data) - -#define _cbson_buffer_write_int32_at_position_INDEX 9 -#define _cbson_buffer_write_int32_at_position_RETURN void -#define _cbson_buffer_write_int32_at_position_PROTO (buffer_t buffer, int position, int32_t data) - -#define _cbson_downcast_and_check_INDEX 10 -#define _cbson_downcast_and_check_RETURN int -#define _cbson_downcast_and_check_PROTO (Py_ssize_t size, uint8_t extra) - -/* Total number of C API pointers */ -#define _cbson_API_POINTER_COUNT 11 - -#ifdef _CBSON_MODULE -/* This section is used when compiling _cbsonmodule */ - -static _cbson_buffer_write_bytes_RETURN buffer_write_bytes _cbson_buffer_write_bytes_PROTO; - -static _cbson_write_dict_RETURN write_dict _cbson_write_dict_PROTO; - -static _cbson_write_pair_RETURN write_pair _cbson_write_pair_PROTO; - -static _cbson_decode_and_write_pair_RETURN decode_and_write_pair _cbson_decode_and_write_pair_PROTO; - -static _cbson_convert_codec_options_RETURN convert_codec_options _cbson_convert_codec_options_PROTO; - -static _cbson_destroy_codec_options_RETURN destroy_codec_options _cbson_destroy_codec_options_PROTO; - -static _cbson_buffer_write_double_RETURN buffer_write_double _cbson_buffer_write_double_PROTO; - -static _cbson_buffer_write_int32_RETURN buffer_write_int32 _cbson_buffer_write_int32_PROTO; - -static _cbson_buffer_write_int64_RETURN buffer_write_int64 _cbson_buffer_write_int64_PROTO; - -static _cbson_buffer_write_int32_at_position_RETURN buffer_write_int32_at_position _cbson_buffer_write_int32_at_position_PROTO; - -static _cbson_downcast_and_check_RETURN _downcast_and_check _cbson_downcast_and_check_PROTO; - -#else -/* This section is used in modules that use _cbsonmodule's API */ - -static void **_cbson_API; - -#define buffer_write_bytes (*(_cbson_buffer_write_bytes_RETURN (*)_cbson_buffer_write_bytes_PROTO) _cbson_API[_cbson_buffer_write_bytes_INDEX]) - -#define write_dict (*(_cbson_write_dict_RETURN (*)_cbson_write_dict_PROTO) _cbson_API[_cbson_write_dict_INDEX]) - -#define write_pair (*(_cbson_write_pair_RETURN (*)_cbson_write_pair_PROTO) _cbson_API[_cbson_write_pair_INDEX]) - -#define decode_and_write_pair (*(_cbson_decode_and_write_pair_RETURN (*)_cbson_decode_and_write_pair_PROTO) _cbson_API[_cbson_decode_and_write_pair_INDEX]) - -#define convert_codec_options (*(_cbson_convert_codec_options_RETURN (*)_cbson_convert_codec_options_PROTO) _cbson_API[_cbson_convert_codec_options_INDEX]) - -#define destroy_codec_options (*(_cbson_destroy_codec_options_RETURN (*)_cbson_destroy_codec_options_PROTO) _cbson_API[_cbson_destroy_codec_options_INDEX]) - -#define buffer_write_double (*(_cbson_buffer_write_double_RETURN (*)_cbson_buffer_write_double_PROTO) _cbson_API[_cbson_buffer_write_double_INDEX]) - -#define buffer_write_int32 (*(_cbson_buffer_write_int32_RETURN (*)_cbson_buffer_write_int32_PROTO) _cbson_API[_cbson_buffer_write_int32_INDEX]) - -#define buffer_write_int64 (*(_cbson_buffer_write_int64_RETURN (*)_cbson_buffer_write_int64_PROTO) _cbson_API[_cbson_buffer_write_int64_INDEX]) - -#define buffer_write_int32_at_position (*(_cbson_buffer_write_int32_at_position_RETURN (*)_cbson_buffer_write_int32_at_position_PROTO) _cbson_API[_cbson_buffer_write_int32_at_position_INDEX]) - -#define _downcast_and_check (*(_cbson_downcast_and_check_RETURN (*)_cbson_downcast_and_check_PROTO) _cbson_API[_cbson_downcast_and_check_INDEX]) - -#define _cbson_IMPORT _cbson_API = (void **)PyCapsule_Import("_cbson._C_API", 0) - -#endif - -#endif // _CBSONMODULE_H diff --git a/venv/Lib/site-packages/bson/_helpers.py b/venv/Lib/site-packages/bson/_helpers.py deleted file mode 100644 index 5a47986..0000000 --- a/venv/Lib/site-packages/bson/_helpers.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright 2021-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Setstate and getstate functions for objects with __slots__, allowing -compatibility with default pickling protocol -""" -from __future__ import annotations - -from typing import Any, Mapping - - -def _setstate_slots(self: Any, state: Any) -> None: - for slot, value in state.items(): - setattr(self, slot, value) - - -def _mangle_name(name: str, prefix: str) -> str: - if name.startswith("__"): - prefix = "_" + prefix - else: - prefix = "" - return prefix + name - - -def _getstate_slots(self: Any) -> Mapping[Any, Any]: - prefix = self.__class__.__name__ - ret = {} - for name in self.__slots__: - mangled_name = _mangle_name(name, prefix) - if hasattr(self, mangled_name): - ret[mangled_name] = getattr(self, mangled_name) - return ret diff --git a/venv/Lib/site-packages/bson/binary.py b/venv/Lib/site-packages/bson/binary.py deleted file mode 100644 index 6698e55..0000000 --- a/venv/Lib/site-packages/bson/binary.py +++ /dev/null @@ -1,551 +0,0 @@ -# Copyright 2009-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import annotations - -import struct -from enum import Enum -from typing import TYPE_CHECKING, Any, Optional, Sequence, Tuple, Type, Union, overload -from uuid import UUID - -"""Tools for representing BSON binary data. -""" - -BINARY_SUBTYPE = 0 -"""BSON binary subtype for binary data. - -This is the default subtype for binary data. -""" - -FUNCTION_SUBTYPE = 1 -"""BSON binary subtype for functions. -""" - -OLD_BINARY_SUBTYPE = 2 -"""Old BSON binary subtype for binary data. - -This is the old default subtype, the current -default is :data:`BINARY_SUBTYPE`. -""" - -OLD_UUID_SUBTYPE = 3 -"""Old BSON binary subtype for a UUID. - -:class:`uuid.UUID` instances will automatically be encoded -by :mod:`bson` using this subtype when using -:data:`UuidRepresentation.PYTHON_LEGACY`, -:data:`UuidRepresentation.JAVA_LEGACY`, or -:data:`UuidRepresentation.CSHARP_LEGACY`. - -.. versionadded:: 2.1 -""" - -UUID_SUBTYPE = 4 -"""BSON binary subtype for a UUID. - -This is the standard BSON binary subtype for UUIDs. -:class:`uuid.UUID` instances will automatically be encoded -by :mod:`bson` using this subtype when using -:data:`UuidRepresentation.STANDARD`. -""" - - -if TYPE_CHECKING: - from array import array as _array - from mmap import mmap as _mmap - - -class UuidRepresentation: - UNSPECIFIED = 0 - """An unspecified UUID representation. - - When configured, :class:`uuid.UUID` instances will **not** be - automatically encoded to or decoded from :class:`~bson.binary.Binary`. - When encoding a :class:`uuid.UUID` instance, an error will be raised. - To encode a :class:`uuid.UUID` instance with this configuration, it must - be wrapped in the :class:`~bson.binary.Binary` class by the application - code. When decoding a BSON binary field with a UUID subtype, a - :class:`~bson.binary.Binary` instance will be returned instead of a - :class:`uuid.UUID` instance. - - See :ref:`unspecified-representation-details` for details. - - .. versionadded:: 3.11 - """ - - STANDARD = UUID_SUBTYPE - """The standard UUID representation. - - :class:`uuid.UUID` instances will automatically be encoded to - and decoded from BSON binary, using RFC-4122 byte order with - binary subtype :data:`UUID_SUBTYPE`. - - See :ref:`standard-representation-details` for details. - - .. versionadded:: 3.11 - """ - - PYTHON_LEGACY = OLD_UUID_SUBTYPE - """The Python legacy UUID representation. - - :class:`uuid.UUID` instances will automatically be encoded to - and decoded from BSON binary, using RFC-4122 byte order with - binary subtype :data:`OLD_UUID_SUBTYPE`. - - See :ref:`python-legacy-representation-details` for details. - - .. versionadded:: 3.11 - """ - - JAVA_LEGACY = 5 - """The Java legacy UUID representation. - - :class:`uuid.UUID` instances will automatically be encoded to - and decoded from BSON binary subtype :data:`OLD_UUID_SUBTYPE`, - using the Java driver's legacy byte order. - - See :ref:`java-legacy-representation-details` for details. - - .. versionadded:: 3.11 - """ - - CSHARP_LEGACY = 6 - """The C#/.net legacy UUID representation. - - :class:`uuid.UUID` instances will automatically be encoded to - and decoded from BSON binary subtype :data:`OLD_UUID_SUBTYPE`, - using the C# driver's legacy byte order. - - See :ref:`csharp-legacy-representation-details` for details. - - .. versionadded:: 3.11 - """ - - -STANDARD = UuidRepresentation.STANDARD -"""An alias for :data:`UuidRepresentation.STANDARD`. - -.. versionadded:: 3.0 -""" - -PYTHON_LEGACY = UuidRepresentation.PYTHON_LEGACY -"""An alias for :data:`UuidRepresentation.PYTHON_LEGACY`. - -.. versionadded:: 3.0 -""" - -JAVA_LEGACY = UuidRepresentation.JAVA_LEGACY -"""An alias for :data:`UuidRepresentation.JAVA_LEGACY`. - -.. versionchanged:: 3.6 - BSON binary subtype 4 is decoded using RFC-4122 byte order. -.. versionadded:: 2.3 -""" - -CSHARP_LEGACY = UuidRepresentation.CSHARP_LEGACY -"""An alias for :data:`UuidRepresentation.CSHARP_LEGACY`. - -.. versionchanged:: 3.6 - BSON binary subtype 4 is decoded using RFC-4122 byte order. -.. versionadded:: 2.3 -""" - -ALL_UUID_SUBTYPES = (OLD_UUID_SUBTYPE, UUID_SUBTYPE) -ALL_UUID_REPRESENTATIONS = ( - UuidRepresentation.UNSPECIFIED, - UuidRepresentation.STANDARD, - UuidRepresentation.PYTHON_LEGACY, - UuidRepresentation.JAVA_LEGACY, - UuidRepresentation.CSHARP_LEGACY, -) -UUID_REPRESENTATION_NAMES = { - UuidRepresentation.UNSPECIFIED: "UuidRepresentation.UNSPECIFIED", - UuidRepresentation.STANDARD: "UuidRepresentation.STANDARD", - UuidRepresentation.PYTHON_LEGACY: "UuidRepresentation.PYTHON_LEGACY", - UuidRepresentation.JAVA_LEGACY: "UuidRepresentation.JAVA_LEGACY", - UuidRepresentation.CSHARP_LEGACY: "UuidRepresentation.CSHARP_LEGACY", -} - -MD5_SUBTYPE = 5 -"""BSON binary subtype for an MD5 hash. -""" - -COLUMN_SUBTYPE = 7 -"""BSON binary subtype for columns. - -.. versionadded:: 4.0 -""" - -SENSITIVE_SUBTYPE = 8 -"""BSON binary subtype for sensitive data. - -.. versionadded:: 4.5 -""" - - -VECTOR_SUBTYPE = 9 -"""BSON binary subtype for densely packed vector data. - -.. versionadded:: 4.10 -""" - - -USER_DEFINED_SUBTYPE = 128 -"""BSON binary subtype for any user defined structure. -""" - - -class BinaryVectorDtype(Enum): - """Datatypes of vector subtype. - - :param FLOAT32: (0x27) Pack list of :class:`float` as float32 - :param INT8: (0x03) Pack list of :class:`int` in [-128, 127] as signed int8 - :param PACKED_BIT: (0x10) Pack list of :class:`int` in [0, 255] as unsigned uint8 - - The `PACKED_BIT` value represents a special case where vector values themselves - can only be of two values (0 or 1) but these are packed together into groups of 8, - a byte. In Python, these are displayed as ints in range [0, 255] - - Each value is of type bytes with a length of one. - - .. versionadded:: 4.10 - """ - - INT8 = b"\x03" - FLOAT32 = b"\x27" - PACKED_BIT = b"\x10" - - -class BinaryVector: - """Vector of numbers along with metadata for binary interoperability. - .. versionadded:: 4.10 - """ - - __slots__ = ("data", "dtype", "padding") - - def __init__(self, data: Sequence[float | int], dtype: BinaryVectorDtype, padding: int = 0): - """ - :param data: Sequence of numbers representing the mathematical vector. - :param dtype: The data type stored in binary - :param padding: The number of bits in the final byte that are to be ignored - when a vector element's size is less than a byte - and the length of the vector is not a multiple of 8. - """ - self.data = data - self.dtype = dtype - self.padding = padding - - def __repr__(self) -> str: - return f"BinaryVector(dtype={self.dtype}, padding={self.padding}, data={self.data})" - - def __eq__(self, other: Any) -> bool: - if not isinstance(other, BinaryVector): - return False - return ( - self.dtype == other.dtype and self.padding == other.padding and self.data == other.data - ) - - -class Binary(bytes): - """Representation of BSON binary data. - - We want to represent Python strings as the BSON string type. - We need to wrap binary data so that we can tell - the difference between what should be considered binary data and - what should be considered a string when we encode to BSON. - - Subtype 9 provides a space-efficient representation of 1-dimensional vector data. - Its data is prepended with two bytes of metadata. - The first (dtype) describes its data type, such as float32 or int8. - The second (padding) prescribes the number of bits to ignore in the final byte. - This is relevant when the element size of the dtype is not a multiple of 8. - - Raises TypeError if `subtype` is not an instance of :class:`int`. - Raises ValueError if `subtype` is not in [0, 256). - - .. note:: - Instances of Binary with subtype 0 will be decoded directly to :class:`bytes`. - - :param data: the binary data to represent. Can be any bytes-like type - that implements the buffer protocol. - :param subtype: the `binary subtype - `_ - to use - - .. versionchanged:: 3.9 - Support any bytes-like type that implements the buffer protocol. - - .. versionchanged:: 4.10 - Addition of vector subtype. - """ - - _type_marker = 5 - __subtype: int - - def __new__( - cls: Type[Binary], - data: Union[memoryview, bytes, _mmap, _array[Any]], - subtype: int = BINARY_SUBTYPE, - ) -> Binary: - if not isinstance(subtype, int): - raise TypeError(f"subtype must be an instance of int, not {type(subtype)}") - if subtype >= 256 or subtype < 0: - raise ValueError("subtype must be contained in [0, 256)") - # Support any type that implements the buffer protocol. - self = bytes.__new__(cls, memoryview(data).tobytes()) - self.__subtype = subtype - return self - - @classmethod - def from_uuid( - cls: Type[Binary], uuid: UUID, uuid_representation: int = UuidRepresentation.STANDARD - ) -> Binary: - """Create a BSON Binary object from a Python UUID. - - Creates a :class:`~bson.binary.Binary` object from a - :class:`uuid.UUID` instance. Assumes that the native - :class:`uuid.UUID` instance uses the byte-order implied by the - provided ``uuid_representation``. - - Raises :exc:`TypeError` if `uuid` is not an instance of - :class:`~uuid.UUID`. - - :param uuid: A :class:`uuid.UUID` instance. - :param uuid_representation: A member of - :class:`~bson.binary.UuidRepresentation`. Default: - :const:`~bson.binary.UuidRepresentation.STANDARD`. - See :ref:`handling-uuid-data-example` for details. - - .. versionadded:: 3.11 - """ - if not isinstance(uuid, UUID): - raise TypeError(f"uuid must be an instance of uuid.UUID, not {type(uuid)}") - - if uuid_representation not in ALL_UUID_REPRESENTATIONS: - raise ValueError( - "uuid_representation must be a value from bson.binary.UuidRepresentation" - ) - - if uuid_representation == UuidRepresentation.UNSPECIFIED: - raise ValueError( - "cannot encode native uuid.UUID with " - "UuidRepresentation.UNSPECIFIED. UUIDs can be manually " - "converted to bson.Binary instances using " - "bson.Binary.from_uuid() or a different UuidRepresentation " - "can be configured. See the documentation for " - "UuidRepresentation for more information." - ) - - subtype = OLD_UUID_SUBTYPE - if uuid_representation == UuidRepresentation.PYTHON_LEGACY: - payload = uuid.bytes - elif uuid_representation == UuidRepresentation.JAVA_LEGACY: - from_uuid = uuid.bytes - payload = from_uuid[0:8][::-1] + from_uuid[8:16][::-1] - elif uuid_representation == UuidRepresentation.CSHARP_LEGACY: - payload = uuid.bytes_le - else: - # uuid_representation == UuidRepresentation.STANDARD - subtype = UUID_SUBTYPE - payload = uuid.bytes - - return cls(payload, subtype) - - def as_uuid(self, uuid_representation: int = UuidRepresentation.STANDARD) -> UUID: - """Create a Python UUID from this BSON Binary object. - - Decodes this binary object as a native :class:`uuid.UUID` instance - with the provided ``uuid_representation``. - - Raises :exc:`ValueError` if this :class:`~bson.binary.Binary` instance - does not contain a UUID. - - :param uuid_representation: A member of - :class:`~bson.binary.UuidRepresentation`. Default: - :const:`~bson.binary.UuidRepresentation.STANDARD`. - See :ref:`handling-uuid-data-example` for details. - - .. versionadded:: 3.11 - """ - if self.subtype not in ALL_UUID_SUBTYPES: - raise ValueError(f"cannot decode subtype {self.subtype} as a uuid") - - if uuid_representation not in ALL_UUID_REPRESENTATIONS: - raise ValueError( - "uuid_representation must be a value from bson.binary.UuidRepresentation" - ) - - if uuid_representation == UuidRepresentation.UNSPECIFIED: - raise ValueError("uuid_representation cannot be UNSPECIFIED") - elif uuid_representation == UuidRepresentation.PYTHON_LEGACY: - if self.subtype == OLD_UUID_SUBTYPE: - return UUID(bytes=self) - elif uuid_representation == UuidRepresentation.JAVA_LEGACY: - if self.subtype == OLD_UUID_SUBTYPE: - return UUID(bytes=self[0:8][::-1] + self[8:16][::-1]) - elif uuid_representation == UuidRepresentation.CSHARP_LEGACY: - if self.subtype == OLD_UUID_SUBTYPE: - return UUID(bytes_le=self) - else: - # uuid_representation == UuidRepresentation.STANDARD - if self.subtype == UUID_SUBTYPE: - return UUID(bytes=self) - - raise ValueError( - f"cannot decode subtype {self.subtype} to {UUID_REPRESENTATION_NAMES[uuid_representation]}" - ) - - @classmethod - @overload - def from_vector(cls: Type[Binary], vector: BinaryVector) -> Binary: - ... - - @classmethod - @overload - def from_vector( - cls: Type[Binary], - vector: Union[list[int], list[float]], - dtype: BinaryVectorDtype, - padding: int = 0, - ) -> Binary: - ... - - @classmethod - def from_vector( - cls: Type[Binary], - vector: Union[BinaryVector, list[int], list[float]], - dtype: Optional[BinaryVectorDtype] = None, - padding: Optional[int] = None, - ) -> Binary: - """Create a BSON :class:`~bson.binary.Binary` of Vector subtype. - - To interpret the representation of the numbers, a data type must be included. - See :class:`~bson.binary.BinaryVectorDtype` for available types and descriptions. - - The dtype and padding are prepended to the binary data's value. - - :param vector: Either a List of values, or a :class:`~bson.binary.BinaryVector` dataclass. - :param dtype: Data type of the values - :param padding: For fractional bytes, number of bits to ignore at end of vector. - :return: Binary packed data identified by dtype and padding. - - .. versionadded:: 4.10 - """ - if isinstance(vector, BinaryVector): - if dtype or padding: - raise ValueError( - "The first argument, vector, has type BinaryVector. " - "dtype or padding cannot be separately defined, but were." - ) - dtype = vector.dtype - padding = vector.padding - vector = vector.data # type: ignore - - padding = 0 if padding is None else padding - if dtype == BinaryVectorDtype.INT8: # pack ints in [-128, 127] as signed int8 - format_str = "b" - if padding: - raise ValueError(f"padding does not apply to {dtype=}") - elif dtype == BinaryVectorDtype.PACKED_BIT: # pack ints in [0, 255] as unsigned uint8 - format_str = "B" - if 0 <= padding > 7: - raise ValueError(f"{padding=}. It must be in [0,1, ..7].") - if padding and not vector: - raise ValueError("Empty vector with non-zero padding.") - elif dtype == BinaryVectorDtype.FLOAT32: # pack floats as float32 - format_str = "f" - if padding: - raise ValueError(f"padding does not apply to {dtype=}") - else: - raise NotImplementedError("%s not yet supported" % dtype) - - metadata = struct.pack(" BinaryVector: - """From the Binary, create a list of numbers, along with dtype and padding. - - :return: BinaryVector - - .. versionadded:: 4.10 - """ - - if self.subtype != VECTOR_SUBTYPE: - raise ValueError(f"Cannot decode subtype {self.subtype} as a vector") - - position = 0 - dtype, padding = struct.unpack_from(" int: - """Subtype of this binary data.""" - return self.__subtype - - def __getnewargs__(self) -> Tuple[bytes, int]: # type: ignore[override] - # Work around http://bugs.python.org/issue7382 - data = super().__getnewargs__()[0] - if not isinstance(data, bytes): - data = data.encode("latin-1") - return data, self.__subtype - - def __eq__(self, other: Any) -> bool: - if isinstance(other, Binary): - return (self.__subtype, bytes(self)) == (other.subtype, bytes(other)) - # We don't return NotImplemented here because if we did then - # Binary("foo") == "foo" would return True, since Binary is a - # subclass of str... - return False - - def __hash__(self) -> int: - return super().__hash__() ^ hash(self.__subtype) - - def __ne__(self, other: Any) -> bool: - return not self == other - - def __repr__(self) -> str: - if self.__subtype == SENSITIVE_SUBTYPE: - return f"" - else: - return f"Binary({bytes.__repr__(self)}, {self.__subtype})" diff --git a/venv/Lib/site-packages/bson/bson-endian.h b/venv/Lib/site-packages/bson/bson-endian.h deleted file mode 100644 index e906b07..0000000 --- a/venv/Lib/site-packages/bson/bson-endian.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright 2013-2016 MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef BSON_ENDIAN_H -#define BSON_ENDIAN_H - - -#if defined(__sun) -# include -#endif - - -#ifdef _MSC_VER -# define BSON_INLINE __inline -#else -# include -# define BSON_INLINE __inline__ -#endif - - -#define BSON_BIG_ENDIAN 4321 -#define BSON_LITTLE_ENDIAN 1234 - - -/* WORDS_BIGENDIAN from pyconfig.h / Python.h */ -#ifdef WORDS_BIGENDIAN -# define BSON_BYTE_ORDER BSON_BIG_ENDIAN -#else -# define BSON_BYTE_ORDER BSON_LITTLE_ENDIAN -#endif - - -#if defined(__sun) -# define BSON_UINT16_SWAP_LE_BE(v) BSWAP_16((uint16_t)v) -# define BSON_UINT32_SWAP_LE_BE(v) BSWAP_32((uint32_t)v) -# define BSON_UINT64_SWAP_LE_BE(v) BSWAP_64((uint64_t)v) -#elif defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__) && \ - (__clang_major__ >= 3) && (__clang_minor__ >= 1) -# if __has_builtin(__builtin_bswap16) -# define BSON_UINT16_SWAP_LE_BE(v) __builtin_bswap16(v) -# endif -# if __has_builtin(__builtin_bswap32) -# define BSON_UINT32_SWAP_LE_BE(v) __builtin_bswap32(v) -# endif -# if __has_builtin(__builtin_bswap64) -# define BSON_UINT64_SWAP_LE_BE(v) __builtin_bswap64(v) -# endif -#elif defined(__GNUC__) && (__GNUC__ >= 4) -# if __GNUC__ >= 4 && defined (__GNUC_MINOR__) && __GNUC_MINOR__ >= 3 -# define BSON_UINT32_SWAP_LE_BE(v) __builtin_bswap32 ((uint32_t)v) -# define BSON_UINT64_SWAP_LE_BE(v) __builtin_bswap64 ((uint64_t)v) -# endif -# if __GNUC__ >= 4 && defined (__GNUC_MINOR__) && __GNUC_MINOR__ >= 8 -# define BSON_UINT16_SWAP_LE_BE(v) __builtin_bswap16 ((uint32_t)v) -# endif -#endif - - -#ifndef BSON_UINT16_SWAP_LE_BE -# define BSON_UINT16_SWAP_LE_BE(v) __bson_uint16_swap_slow ((uint16_t)v) -#endif - - -#ifndef BSON_UINT32_SWAP_LE_BE -# define BSON_UINT32_SWAP_LE_BE(v) __bson_uint32_swap_slow ((uint32_t)v) -#endif - - -#ifndef BSON_UINT64_SWAP_LE_BE -# define BSON_UINT64_SWAP_LE_BE(v) __bson_uint64_swap_slow ((uint64_t)v) -#endif - - -#if BSON_BYTE_ORDER == BSON_LITTLE_ENDIAN -# define BSON_UINT16_FROM_LE(v) ((uint16_t)v) -# define BSON_UINT16_TO_LE(v) ((uint16_t)v) -# define BSON_UINT16_FROM_BE(v) BSON_UINT16_SWAP_LE_BE (v) -# define BSON_UINT16_TO_BE(v) BSON_UINT16_SWAP_LE_BE (v) -# define BSON_UINT32_FROM_LE(v) ((uint32_t)v) -# define BSON_UINT32_TO_LE(v) ((uint32_t)v) -# define BSON_UINT32_FROM_BE(v) BSON_UINT32_SWAP_LE_BE (v) -# define BSON_UINT32_TO_BE(v) BSON_UINT32_SWAP_LE_BE (v) -# define BSON_UINT64_FROM_LE(v) ((uint64_t)v) -# define BSON_UINT64_TO_LE(v) ((uint64_t)v) -# define BSON_UINT64_FROM_BE(v) BSON_UINT64_SWAP_LE_BE (v) -# define BSON_UINT64_TO_BE(v) BSON_UINT64_SWAP_LE_BE (v) -# define BSON_DOUBLE_FROM_LE(v) ((double)v) -# define BSON_DOUBLE_TO_LE(v) ((double)v) -#elif BSON_BYTE_ORDER == BSON_BIG_ENDIAN -# define BSON_UINT16_FROM_LE(v) BSON_UINT16_SWAP_LE_BE (v) -# define BSON_UINT16_TO_LE(v) BSON_UINT16_SWAP_LE_BE (v) -# define BSON_UINT16_FROM_BE(v) ((uint16_t)v) -# define BSON_UINT16_TO_BE(v) ((uint16_t)v) -# define BSON_UINT32_FROM_LE(v) BSON_UINT32_SWAP_LE_BE (v) -# define BSON_UINT32_TO_LE(v) BSON_UINT32_SWAP_LE_BE (v) -# define BSON_UINT32_FROM_BE(v) ((uint32_t)v) -# define BSON_UINT32_TO_BE(v) ((uint32_t)v) -# define BSON_UINT64_FROM_LE(v) BSON_UINT64_SWAP_LE_BE (v) -# define BSON_UINT64_TO_LE(v) BSON_UINT64_SWAP_LE_BE (v) -# define BSON_UINT64_FROM_BE(v) ((uint64_t)v) -# define BSON_UINT64_TO_BE(v) ((uint64_t)v) -# define BSON_DOUBLE_FROM_LE(v) (__bson_double_swap_slow (v)) -# define BSON_DOUBLE_TO_LE(v) (__bson_double_swap_slow (v)) -#else -# error "The endianness of target architecture is unknown." -#endif - - -/* - *-------------------------------------------------------------------------- - * - * __bson_uint16_swap_slow -- - * - * Fallback endianness conversion for 16-bit integers. - * - * Returns: - * The endian swapped version. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static BSON_INLINE uint16_t -__bson_uint16_swap_slow (uint16_t v) /* IN */ -{ - return ((v & 0x00FF) << 8) | - ((v & 0xFF00) >> 8); -} - - -/* - *-------------------------------------------------------------------------- - * - * __bson_uint32_swap_slow -- - * - * Fallback endianness conversion for 32-bit integers. - * - * Returns: - * The endian swapped version. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static BSON_INLINE uint32_t -__bson_uint32_swap_slow (uint32_t v) /* IN */ -{ - return ((v & 0x000000FFU) << 24) | - ((v & 0x0000FF00U) << 8) | - ((v & 0x00FF0000U) >> 8) | - ((v & 0xFF000000U) >> 24); -} - - -/* - *-------------------------------------------------------------------------- - * - * __bson_uint64_swap_slow -- - * - * Fallback endianness conversion for 64-bit integers. - * - * Returns: - * The endian swapped version. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static BSON_INLINE uint64_t -__bson_uint64_swap_slow (uint64_t v) /* IN */ -{ - return ((v & 0x00000000000000FFULL) << 56) | - ((v & 0x000000000000FF00ULL) << 40) | - ((v & 0x0000000000FF0000ULL) << 24) | - ((v & 0x00000000FF000000ULL) << 8) | - ((v & 0x000000FF00000000ULL) >> 8) | - ((v & 0x0000FF0000000000ULL) >> 24) | - ((v & 0x00FF000000000000ULL) >> 40) | - ((v & 0xFF00000000000000ULL) >> 56); -} - - -/* - *-------------------------------------------------------------------------- - * - * __bson_double_swap_slow -- - * - * Fallback endianness conversion for double floating point. - * - * Returns: - * The endian swapped version. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - - -static BSON_INLINE double -__bson_double_swap_slow (double v) /* IN */ -{ - uint64_t uv; - - memcpy(&uv, &v, sizeof(v)); - uv = BSON_UINT64_SWAP_LE_BE(uv); - memcpy(&v, &uv, sizeof(v)); - - return v; -} - - -#endif /* BSON_ENDIAN_H */ diff --git a/venv/Lib/site-packages/bson/buffer.c b/venv/Lib/site-packages/bson/buffer.c deleted file mode 100644 index cc75202..0000000 --- a/venv/Lib/site-packages/bson/buffer.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2009-2015 MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Include Python.h so we can set Python's error indicator. */ -#define PY_SSIZE_T_CLEAN -#include "Python.h" - -#include -#include - -#include "buffer.h" - -#define INITIAL_BUFFER_SIZE 256 - -struct buffer { - char* buffer; - int size; - int position; -}; - -/* Set Python's error indicator to MemoryError. - * Called after allocation failures. */ -static void set_memory_error(void) { - PyErr_NoMemory(); -} - -/* Allocate and return a new buffer. - * Return NULL and sets MemoryError on allocation failure. */ -buffer_t pymongo_buffer_new(void) { - buffer_t buffer; - buffer = (buffer_t)malloc(sizeof(struct buffer)); - if (buffer == NULL) { - set_memory_error(); - return NULL; - } - - buffer->size = INITIAL_BUFFER_SIZE; - buffer->position = 0; - buffer->buffer = (char*)malloc(sizeof(char) * INITIAL_BUFFER_SIZE); - if (buffer->buffer == NULL) { - free(buffer); - set_memory_error(); - return NULL; - } - - return buffer; -} - -/* Free the memory allocated for `buffer`. - * Return non-zero on failure. */ -int pymongo_buffer_free(buffer_t buffer) { - if (buffer == NULL) { - return 1; - } - /* Buffer will be NULL when buffer_grow fails. */ - if (buffer->buffer != NULL) { - free(buffer->buffer); - } - free(buffer); - return 0; -} - -/* Grow `buffer` to at least `min_length`. - * Return non-zero and sets MemoryError on allocation failure. */ -static int buffer_grow(buffer_t buffer, int min_length) { - int old_size = 0; - int size = buffer->size; - char* old_buffer = buffer->buffer; - if (size >= min_length) { - return 0; - } - while (size < min_length) { - old_size = size; - size *= 2; - if (size <= old_size) { - /* Size did not increase. Could be an overflow - * or size < 1. Just go with min_length. */ - size = min_length; - } - } - buffer->buffer = (char*)realloc(buffer->buffer, sizeof(char) * size); - if (buffer->buffer == NULL) { - free(old_buffer); - set_memory_error(); - return 1; - } - buffer->size = size; - return 0; -} - -/* Assure that `buffer` has at least `size` free bytes (and grow if needed). - * Return non-zero and sets MemoryError on allocation failure. - * Return non-zero and sets ValueError if `size` would exceed 2GiB. */ -static int buffer_assure_space(buffer_t buffer, int size) { - int new_size = buffer->position + size; - /* Check for overflow. */ - if (new_size < buffer->position) { - PyErr_SetString(PyExc_ValueError, - "Document would overflow BSON size limit"); - return 1; - } - - if (new_size <= buffer->size) { - return 0; - } - return buffer_grow(buffer, new_size); -} - -/* Save `size` bytes from the current position in `buffer` (and grow if needed). - * Return offset for writing, or -1 on failure. - * Sets MemoryError or ValueError on failure. */ -buffer_position pymongo_buffer_save_space(buffer_t buffer, int size) { - int position = buffer->position; - if (buffer_assure_space(buffer, size) != 0) { - return -1; - } - buffer->position += size; - return position; -} - -/* Write `size` bytes from `data` to `buffer` (and grow if needed). - * Return non-zero on failure. - * Sets MemoryError or ValueError on failure. */ -int pymongo_buffer_write(buffer_t buffer, const char* data, int size) { - if (buffer_assure_space(buffer, size) != 0) { - return 1; - } - - memcpy(buffer->buffer + buffer->position, data, size); - buffer->position += size; - return 0; -} - -int pymongo_buffer_get_position(buffer_t buffer) { - return buffer->position; -} - -char* pymongo_buffer_get_buffer(buffer_t buffer) { - return buffer->buffer; -} - -void pymongo_buffer_update_position(buffer_t buffer, buffer_position new_position) { - buffer->position = new_position; -} diff --git a/venv/Lib/site-packages/bson/buffer.h b/venv/Lib/site-packages/bson/buffer.h deleted file mode 100644 index a78e34e..0000000 --- a/venv/Lib/site-packages/bson/buffer.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2009-2015 MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BUFFER_H -#define BUFFER_H - -/* Note: if any of these functions return a failure condition then the buffer - * has already been freed. */ - -/* A buffer */ -typedef struct buffer* buffer_t; -/* A position in the buffer */ -typedef int buffer_position; - -/* Allocate and return a new buffer. - * Return NULL on allocation failure. */ -buffer_t pymongo_buffer_new(void); - -/* Free the memory allocated for `buffer`. - * Return non-zero on failure. */ -int pymongo_buffer_free(buffer_t buffer); - -/* Save `size` bytes from the current position in `buffer` (and grow if needed). - * Return offset for writing, or -1 on allocation failure. */ -buffer_position pymongo_buffer_save_space(buffer_t buffer, int size); - -/* Write `size` bytes from `data` to `buffer` (and grow if needed). - * Return non-zero on allocation failure. */ -int pymongo_buffer_write(buffer_t buffer, const char* data, int size); - -/* Getters for the internals of a buffer_t. - * Should try to avoid using these as much as possible - * since they break the abstraction. */ -buffer_position pymongo_buffer_get_position(buffer_t buffer); -char* pymongo_buffer_get_buffer(buffer_t buffer); -void pymongo_buffer_update_position(buffer_t buffer, buffer_position new_position); - -#endif diff --git a/venv/Lib/site-packages/bson/code.py b/venv/Lib/site-packages/bson/code.py deleted file mode 100644 index f0523b2..0000000 --- a/venv/Lib/site-packages/bson/code.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright 2009-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tools for representing JavaScript code in BSON.""" -from __future__ import annotations - -from collections.abc import Mapping as _Mapping -from typing import Any, Mapping, Optional, Type, Union - - -class Code(str): - """BSON's JavaScript code type. - - Raises :class:`TypeError` if `code` is not an instance of - :class:`str` or `scope` is not ``None`` or an instance - of :class:`dict`. - - Scope variables can be set by passing a dictionary as the `scope` - argument or by using keyword arguments. If a variable is set as a - keyword argument it will override any setting for that variable in - the `scope` dictionary. - - :param code: A string containing JavaScript code to be evaluated or another - instance of Code. In the latter case, the scope of `code` becomes this - Code's :attr:`scope`. - :param scope: dictionary representing the scope in which - `code` should be evaluated - a mapping from identifiers (as - strings) to values. Defaults to ``None``. This is applied after any - scope associated with a given `code` above. - :param kwargs: scope variables can also be passed as - keyword arguments. These are applied after `scope` and `code`. - - .. versionchanged:: 3.4 - The default value for :attr:`scope` is ``None`` instead of ``{}``. - - """ - - _type_marker = 13 - __scope: Union[Mapping[str, Any], None] - - def __new__( - cls: Type[Code], - code: Union[str, Code], - scope: Optional[Mapping[str, Any]] = None, - **kwargs: Any, - ) -> Code: - if not isinstance(code, str): - raise TypeError(f"code must be an instance of str, not {type(code)}") - - self = str.__new__(cls, code) - - try: - self.__scope = code.scope # type: ignore - except AttributeError: - self.__scope = None - - if scope is not None: - if not isinstance(scope, _Mapping): - raise TypeError(f"scope must be an instance of dict, not {type(scope)}") - if self.__scope is not None: - self.__scope.update(scope) # type: ignore - else: - self.__scope = scope - - if kwargs: - if self.__scope is not None: - self.__scope.update(kwargs) # type: ignore - else: - self.__scope = kwargs - - return self - - @property - def scope(self) -> Optional[Mapping[str, Any]]: - """Scope dictionary for this instance or ``None``.""" - return self.__scope - - def __repr__(self) -> str: - return f"Code({str.__repr__(self)}, {self.__scope!r})" - - def __eq__(self, other: Any) -> bool: - if isinstance(other, Code): - return (self.__scope, str(self)) == (other.__scope, str(other)) - return False - - __hash__: Any = None - - def __ne__(self, other: Any) -> bool: - return not self == other diff --git a/venv/Lib/site-packages/bson/codec_options.py b/venv/Lib/site-packages/bson/codec_options.py deleted file mode 100644 index 258a777..0000000 --- a/venv/Lib/site-packages/bson/codec_options.py +++ /dev/null @@ -1,511 +0,0 @@ -# Copyright 2014-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tools for specifying BSON codec options.""" -from __future__ import annotations - -import abc -import datetime -import enum -from collections.abc import MutableMapping as _MutableMapping -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Generic, - Iterable, - Mapping, - NamedTuple, - Optional, - Tuple, - Type, - Union, - cast, -) - -from bson.binary import ( - ALL_UUID_REPRESENTATIONS, - UUID_REPRESENTATION_NAMES, - UuidRepresentation, -) -from bson.typings import _DocumentType - -_RAW_BSON_DOCUMENT_MARKER = 101 - - -def _raw_document_class(document_class: Any) -> bool: - """Determine if a document_class is a RawBSONDocument class.""" - marker = getattr(document_class, "_type_marker", None) - return marker == _RAW_BSON_DOCUMENT_MARKER - - -class TypeEncoder(abc.ABC): - """Base class for defining type codec classes which describe how a - custom type can be transformed to one of the types BSON understands. - - Codec classes must implement the ``python_type`` attribute, and the - ``transform_python`` method to support encoding. - - See :ref:`custom-type-type-codec` documentation for an example. - """ - - @abc.abstractproperty - def python_type(self) -> Any: - """The Python type to be converted into something serializable.""" - - @abc.abstractmethod - def transform_python(self, value: Any) -> Any: - """Convert the given Python object into something serializable.""" - - -class TypeDecoder(abc.ABC): - """Base class for defining type codec classes which describe how a - BSON type can be transformed to a custom type. - - Codec classes must implement the ``bson_type`` attribute, and the - ``transform_bson`` method to support decoding. - - See :ref:`custom-type-type-codec` documentation for an example. - """ - - @abc.abstractproperty - def bson_type(self) -> Any: - """The BSON type to be converted into our own type.""" - - @abc.abstractmethod - def transform_bson(self, value: Any) -> Any: - """Convert the given BSON value into our own type.""" - - -class TypeCodec(TypeEncoder, TypeDecoder): - """Base class for defining type codec classes which describe how a - custom type can be transformed to/from one of the types :mod:`bson` - can already encode/decode. - - Codec classes must implement the ``python_type`` attribute, and the - ``transform_python`` method to support encoding, as well as the - ``bson_type`` attribute, and the ``transform_bson`` method to support - decoding. - - See :ref:`custom-type-type-codec` documentation for an example. - """ - - -_Codec = Union[TypeEncoder, TypeDecoder, TypeCodec] -_Fallback = Callable[[Any], Any] - - -class TypeRegistry: - """Encapsulates type codecs used in encoding and / or decoding BSON, as - well as the fallback encoder. Type registries cannot be modified after - instantiation. - - ``TypeRegistry`` can be initialized with an iterable of type codecs, and - a callable for the fallback encoder:: - - >>> from bson.codec_options import TypeRegistry - >>> type_registry = TypeRegistry([Codec1, Codec2, Codec3, ...], - ... fallback_encoder) - - See :ref:`custom-type-type-registry` documentation for an example. - - :param type_codecs: iterable of type codec instances. If - ``type_codecs`` contains multiple codecs that transform a single - python or BSON type, the transformation specified by the type codec - occurring last prevails. A TypeError will be raised if one or more - type codecs modify the encoding behavior of a built-in :mod:`bson` - type. - :param fallback_encoder: callable that accepts a single, - unencodable python value and transforms it into a type that - :mod:`bson` can encode. See :ref:`fallback-encoder-callable` - documentation for an example. - """ - - def __init__( - self, - type_codecs: Optional[Iterable[_Codec]] = None, - fallback_encoder: Optional[_Fallback] = None, - ) -> None: - self.__type_codecs = list(type_codecs or []) - self._fallback_encoder = fallback_encoder - self._encoder_map: dict[Any, Any] = {} - self._decoder_map: dict[Any, Any] = {} - - if self._fallback_encoder is not None: - if not callable(fallback_encoder): - raise TypeError("fallback_encoder %r is not a callable" % (fallback_encoder)) - - for codec in self.__type_codecs: - is_valid_codec = False - if isinstance(codec, TypeEncoder): - self._validate_type_encoder(codec) - is_valid_codec = True - self._encoder_map[codec.python_type] = codec.transform_python - if isinstance(codec, TypeDecoder): - is_valid_codec = True - self._decoder_map[codec.bson_type] = codec.transform_bson - if not is_valid_codec: - raise TypeError( - f"Expected an instance of {TypeEncoder.__name__}, {TypeDecoder.__name__}, or {TypeCodec.__name__}, got {codec!r} instead" - ) - - def _validate_type_encoder(self, codec: _Codec) -> None: - from bson import _BUILT_IN_TYPES - - for pytype in _BUILT_IN_TYPES: - if issubclass(cast(TypeCodec, codec).python_type, pytype): - err_msg = ( - "TypeEncoders cannot change how built-in types are " - f"encoded (encoder {codec} transforms type {pytype})" - ) - raise TypeError(err_msg) - - def __repr__(self) -> str: - return "{}(type_codecs={!r}, fallback_encoder={!r})".format( - self.__class__.__name__, - self.__type_codecs, - self._fallback_encoder, - ) - - def __eq__(self, other: Any) -> Any: - if not isinstance(other, type(self)): - return NotImplemented - return ( - (self._decoder_map == other._decoder_map) - and (self._encoder_map == other._encoder_map) - and (self._fallback_encoder == other._fallback_encoder) - ) - - -class DatetimeConversion(int, enum.Enum): - """Options for decoding BSON datetimes.""" - - DATETIME = 1 - """Decode a BSON UTC datetime as a :class:`datetime.datetime`. - - BSON UTC datetimes that cannot be represented as a - :class:`~datetime.datetime` will raise an :class:`OverflowError` - or a :class:`ValueError`. - - .. versionadded 4.3 - """ - - DATETIME_CLAMP = 2 - """Decode a BSON UTC datetime as a :class:`datetime.datetime`, clamping - to :attr:`~datetime.datetime.min` and :attr:`~datetime.datetime.max`. - - .. versionadded 4.3 - """ - - DATETIME_MS = 3 - """Decode a BSON UTC datetime as a :class:`~bson.datetime_ms.DatetimeMS` - object. - - .. versionadded 4.3 - """ - - DATETIME_AUTO = 4 - """Decode a BSON UTC datetime as a :class:`datetime.datetime` if possible, - and a :class:`~bson.datetime_ms.DatetimeMS` if not. - - .. versionadded 4.3 - """ - - -class _BaseCodecOptions(NamedTuple): - document_class: Type[Mapping[str, Any]] - tz_aware: bool - uuid_representation: int - unicode_decode_error_handler: str - tzinfo: Optional[datetime.tzinfo] - type_registry: TypeRegistry - datetime_conversion: Optional[DatetimeConversion] - - -if TYPE_CHECKING: - - class CodecOptions(Tuple[_DocumentType], Generic[_DocumentType]): - document_class: Type[_DocumentType] - tz_aware: bool - uuid_representation: int - unicode_decode_error_handler: Optional[str] - tzinfo: Optional[datetime.tzinfo] - type_registry: TypeRegistry - datetime_conversion: Optional[int] - - def __new__( - cls: Type[CodecOptions[_DocumentType]], - document_class: Optional[Type[_DocumentType]] = ..., - tz_aware: bool = ..., - uuid_representation: Optional[int] = ..., - unicode_decode_error_handler: Optional[str] = ..., - tzinfo: Optional[datetime.tzinfo] = ..., - type_registry: Optional[TypeRegistry] = ..., - datetime_conversion: Optional[int] = ..., - ) -> CodecOptions[_DocumentType]: - ... - - # CodecOptions API - def with_options(self, **kwargs: Any) -> CodecOptions[Any]: - ... - - def _arguments_repr(self) -> str: - ... - - def _options_dict(self) -> dict[Any, Any]: - ... - - # NamedTuple API - @classmethod - def _make(cls, obj: Iterable[Any]) -> CodecOptions[_DocumentType]: - ... - - def _asdict(self) -> dict[str, Any]: - ... - - def _replace(self, **kwargs: Any) -> CodecOptions[_DocumentType]: - ... - - _source: str - _fields: Tuple[str] - -else: - - class CodecOptions(_BaseCodecOptions): - """Encapsulates options used encoding and / or decoding BSON.""" - - def __init__(self, *args, **kwargs): - """Encapsulates options used encoding and / or decoding BSON. - - The `document_class` option is used to define a custom type for use - decoding BSON documents. Access to the underlying raw BSON bytes for - a document is available using the :class:`~bson.raw_bson.RawBSONDocument` - type:: - - >>> from bson.raw_bson import RawBSONDocument - >>> from bson.codec_options import CodecOptions - >>> codec_options = CodecOptions(document_class=RawBSONDocument) - >>> coll = db.get_collection('test', codec_options=codec_options) - >>> doc = coll.find_one() - >>> doc.raw - '\\x16\\x00\\x00\\x00\\x07_id\\x00[0\\x165\\x91\\x10\\xea\\x14\\xe8\\xc5\\x8b\\x93\\x00' - - The document class can be any type that inherits from - :class:`~collections.abc.MutableMapping`:: - - >>> class AttributeDict(dict): - ... # A dict that supports attribute access. - ... def __getattr__(self, key): - ... return self[key] - ... def __setattr__(self, key, value): - ... self[key] = value - ... - >>> codec_options = CodecOptions(document_class=AttributeDict) - >>> coll = db.get_collection('test', codec_options=codec_options) - >>> doc = coll.find_one() - >>> doc._id - ObjectId('5b3016359110ea14e8c58b93') - - See :doc:`/examples/datetimes` for examples using the `tz_aware` and - `tzinfo` options. - - See :doc:`/examples/uuid` for examples using the `uuid_representation` - option. - - :param document_class: BSON documents returned in queries will be decoded - to an instance of this class. Must be a subclass of - :class:`~collections.abc.MutableMapping`. Defaults to :class:`dict`. - :param tz_aware: If ``True``, BSON datetimes will be decoded to timezone - aware instances of :class:`~datetime.datetime`. Otherwise they will be - naive. Defaults to ``False``. - :param uuid_representation: The BSON representation to use when encoding - and decoding instances of :class:`~uuid.UUID`. Defaults to - :data:`~bson.binary.UuidRepresentation.UNSPECIFIED`. New - applications should consider setting this to - :data:`~bson.binary.UuidRepresentation.STANDARD` for cross language - compatibility. See :ref:`handling-uuid-data-example` for details. - :param unicode_decode_error_handler: The error handler to apply when - a Unicode-related error occurs during BSON decoding that would - otherwise raise :exc:`UnicodeDecodeError`. Valid options include - 'strict', 'replace', 'backslashreplace', 'surrogateescape', and - 'ignore'. Defaults to 'strict'. - :param tzinfo: A :class:`~datetime.tzinfo` subclass that specifies the - timezone to/from which :class:`~datetime.datetime` objects should be - encoded/decoded. - :param type_registry: Instance of :class:`TypeRegistry` used to customize - encoding and decoding behavior. - :param datetime_conversion: Specifies how UTC datetimes should be decoded - within BSON. Valid options include 'datetime_ms' to return as a - DatetimeMS, 'datetime' to return as a datetime.datetime and - raising a ValueError for out-of-range values, 'datetime_auto' to - return DatetimeMS objects when the underlying datetime is - out-of-range and 'datetime_clamp' to clamp to the minimum and - maximum possible datetimes. Defaults to 'datetime'. - - .. versionchanged:: 4.0 - The default for `uuid_representation` was changed from - :const:`~bson.binary.UuidRepresentation.PYTHON_LEGACY` to - :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`. - - .. versionadded:: 3.8 - `type_registry` attribute. - - .. warning:: Care must be taken when changing - `unicode_decode_error_handler` from its default value ('strict'). - The 'replace' and 'ignore' modes should not be used when documents - retrieved from the server will be modified in the client application - and stored back to the server. - """ - super().__init__() - - def __new__( - cls: Type[CodecOptions], - document_class: Optional[Type[Mapping[str, Any]]] = None, - tz_aware: bool = False, - uuid_representation: Optional[int] = UuidRepresentation.UNSPECIFIED, - unicode_decode_error_handler: str = "strict", - tzinfo: Optional[datetime.tzinfo] = None, - type_registry: Optional[TypeRegistry] = None, - datetime_conversion: Optional[DatetimeConversion] = DatetimeConversion.DATETIME, - ) -> CodecOptions: - doc_class = document_class or dict - # issubclass can raise TypeError for generic aliases like SON[str, Any]. - # In that case we can use the base class for the comparison. - is_mapping = False - try: - is_mapping = issubclass(doc_class, _MutableMapping) - except TypeError: - if hasattr(doc_class, "__origin__"): - is_mapping = issubclass(doc_class.__origin__, _MutableMapping) - if not (is_mapping or _raw_document_class(doc_class)): - raise TypeError( - "document_class must be dict, bson.son.SON, " - "bson.raw_bson.RawBSONDocument, or a " - "subclass of collections.abc.MutableMapping" - ) - if not isinstance(tz_aware, bool): - raise TypeError(f"tz_aware must be True or False, was: tz_aware={tz_aware}") - if uuid_representation not in ALL_UUID_REPRESENTATIONS: - raise ValueError( - "uuid_representation must be a value from bson.binary.UuidRepresentation" - ) - if not isinstance(unicode_decode_error_handler, str): - raise ValueError( - f"unicode_decode_error_handler must be a string, not {type(unicode_decode_error_handler)}" - ) - if tzinfo is not None: - if not isinstance(tzinfo, datetime.tzinfo): - raise TypeError( - f"tzinfo must be an instance of datetime.tzinfo, not {type(tzinfo)}" - ) - if not tz_aware: - raise ValueError("cannot specify tzinfo without also setting tz_aware=True") - - type_registry = type_registry or TypeRegistry() - - if not isinstance(type_registry, TypeRegistry): - raise TypeError( - f"type_registry must be an instance of TypeRegistry, not {type(type_registry)}" - ) - - return tuple.__new__( - cls, - ( - doc_class, - tz_aware, - uuid_representation, - unicode_decode_error_handler, - tzinfo, - type_registry, - datetime_conversion, - ), - ) - - def _arguments_repr(self) -> str: - """Representation of the arguments used to create this object.""" - document_class_repr = ( - "dict" if self.document_class is dict else repr(self.document_class) - ) - - uuid_rep_repr = UUID_REPRESENTATION_NAMES.get( - self.uuid_representation, self.uuid_representation - ) - - return ( - "document_class={}, tz_aware={!r}, uuid_representation={}, " - "unicode_decode_error_handler={!r}, tzinfo={!r}, " - "type_registry={!r}, datetime_conversion={!s}".format( - document_class_repr, - self.tz_aware, - uuid_rep_repr, - self.unicode_decode_error_handler, - self.tzinfo, - self.type_registry, - self.datetime_conversion, - ) - ) - - def _options_dict(self) -> dict[str, Any]: - """Dictionary of the arguments used to create this object.""" - # TODO: PYTHON-2442 use _asdict() instead - return { - "document_class": self.document_class, - "tz_aware": self.tz_aware, - "uuid_representation": self.uuid_representation, - "unicode_decode_error_handler": self.unicode_decode_error_handler, - "tzinfo": self.tzinfo, - "type_registry": self.type_registry, - "datetime_conversion": self.datetime_conversion, - } - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self._arguments_repr()})" - - def with_options(self, **kwargs: Any) -> CodecOptions: - """Make a copy of this CodecOptions, overriding some options:: - - >>> from bson.codec_options import DEFAULT_CODEC_OPTIONS - >>> DEFAULT_CODEC_OPTIONS.tz_aware - False - >>> options = DEFAULT_CODEC_OPTIONS.with_options(tz_aware=True) - >>> options.tz_aware - True - - .. versionadded:: 3.5 - """ - opts = self._options_dict() - opts.update(kwargs) - return CodecOptions(**opts) - - -DEFAULT_CODEC_OPTIONS: CodecOptions[dict[str, Any]] = CodecOptions() - - -def _parse_codec_options(options: Any) -> CodecOptions[Any]: - """Parse BSON codec options.""" - kwargs = {} - for k in set(options) & { - "document_class", - "tz_aware", - "uuidrepresentation", - "unicode_decode_error_handler", - "tzinfo", - "type_registry", - "datetime_conversion", - }: - if k == "uuidrepresentation": - kwargs["uuid_representation"] = options[k] - else: - kwargs[k] = options[k] - return CodecOptions(**kwargs) diff --git a/venv/Lib/site-packages/bson/datetime_ms.py b/venv/Lib/site-packages/bson/datetime_ms.py deleted file mode 100644 index 679524c..0000000 --- a/venv/Lib/site-packages/bson/datetime_ms.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright 2022-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you -# may not use this file except in compliance with the License. You -# may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. See the License for the specific language governing -# permissions and limitations under the License. - -"""Tools for representing the BSON datetime type. - -.. versionadded:: 4.3 -""" -from __future__ import annotations - -import calendar -import datetime -from typing import Any, Union, cast - -from bson.codec_options import DEFAULT_CODEC_OPTIONS, CodecOptions, DatetimeConversion -from bson.errors import InvalidBSON -from bson.tz_util import utc - -EPOCH_AWARE = datetime.datetime.fromtimestamp(0, utc) -EPOCH_NAIVE = EPOCH_AWARE.replace(tzinfo=None) -_DATETIME_ERROR_SUGGESTION = ( - "(Consider Using CodecOptions(datetime_conversion=DATETIME_AUTO)" - " or MongoClient(datetime_conversion='DATETIME_AUTO'))." - " See: https://www.mongodb.com/docs/languages/python/pymongo-driver/current/data-formats/dates-and-times/#handling-out-of-range-datetimes" -) - - -class DatetimeMS: - """Represents a BSON UTC datetime.""" - - __slots__ = ("_value",) - - def __init__(self, value: Union[int, datetime.datetime]): - """Represents a BSON UTC datetime. - - BSON UTC datetimes are defined as an int64 of milliseconds since the - Unix epoch. The principal use of DatetimeMS is to represent - datetimes outside the range of the Python builtin - :class:`~datetime.datetime` class when - encoding/decoding BSON. - - To decode UTC datetimes as a ``DatetimeMS``, `datetime_conversion` in - :class:`~bson.codec_options.CodecOptions` must be set to 'datetime_ms' or - 'datetime_auto'. See :ref:`handling-out-of-range-datetimes` for - details. - - :param value: An instance of :class:`datetime.datetime` to be - represented as milliseconds since the Unix epoch, or int of - milliseconds since the Unix epoch. - """ - if isinstance(value, int): - if not (-(2**63) <= value <= 2**63 - 1): - raise OverflowError("Must be a 64-bit integer of milliseconds") - self._value = value - elif isinstance(value, datetime.datetime): - self._value = _datetime_to_millis(value) - else: - raise TypeError(f"{type(value)} is not a valid type for DatetimeMS") - - def __hash__(self) -> int: - return hash(self._value) - - def __repr__(self) -> str: - return type(self).__name__ + "(" + str(self._value) + ")" - - def __lt__(self, other: Union[DatetimeMS, int]) -> bool: - return self._value < other - - def __le__(self, other: Union[DatetimeMS, int]) -> bool: - return self._value <= other - - def __eq__(self, other: Any) -> bool: - if isinstance(other, DatetimeMS): - return self._value == other._value - return False - - def __ne__(self, other: Any) -> bool: - if isinstance(other, DatetimeMS): - return self._value != other._value - return True - - def __gt__(self, other: Union[DatetimeMS, int]) -> bool: - return self._value > other - - def __ge__(self, other: Union[DatetimeMS, int]) -> bool: - return self._value >= other - - _type_marker = 9 - - def as_datetime( - self, codec_options: CodecOptions[Any] = DEFAULT_CODEC_OPTIONS - ) -> datetime.datetime: - """Create a Python :class:`~datetime.datetime` from this DatetimeMS object. - - :param codec_options: A CodecOptions instance for specifying how the - resulting DatetimeMS object will be formatted using ``tz_aware`` - and ``tz_info``. Defaults to - :const:`~bson.codec_options.DEFAULT_CODEC_OPTIONS`. - """ - return cast(datetime.datetime, _millis_to_datetime(self._value, codec_options)) - - def __int__(self) -> int: - return self._value - - -def _datetime_to_millis(dtm: datetime.datetime) -> int: - """Convert datetime to milliseconds since epoch UTC.""" - if dtm.utcoffset() is not None: - dtm = dtm - dtm.utcoffset() # type: ignore - return int(calendar.timegm(dtm.timetuple()) * 1000 + dtm.microsecond // 1000) - - -_MIN_UTC = datetime.datetime.min.replace(tzinfo=utc) -_MAX_UTC = datetime.datetime.max.replace(tzinfo=utc) -_MIN_UTC_MS = _datetime_to_millis(_MIN_UTC) -_MAX_UTC_MS = _datetime_to_millis(_MAX_UTC) - - -# Inclusive min and max for timezones. -def _min_datetime_ms(tz: datetime.tzinfo = utc) -> int: - delta = tz.utcoffset(_MIN_UTC) - if delta is not None: - offset_millis = (delta.days * 86400 + delta.seconds) * 1000 + delta.microseconds // 1000 - else: - offset_millis = 0 - return max(_MIN_UTC_MS, _MIN_UTC_MS - offset_millis) - - -def _max_datetime_ms(tz: datetime.tzinfo = utc) -> int: - delta = tz.utcoffset(_MAX_UTC) - if delta is not None: - offset_millis = (delta.days * 86400 + delta.seconds) * 1000 + delta.microseconds // 1000 - else: - offset_millis = 0 - return min(_MAX_UTC_MS, _MAX_UTC_MS - offset_millis) - - -def _millis_to_datetime( - millis: int, opts: CodecOptions[Any] -) -> Union[datetime.datetime, DatetimeMS]: - """Convert milliseconds since epoch UTC to datetime.""" - if ( - opts.datetime_conversion == DatetimeConversion.DATETIME - or opts.datetime_conversion == DatetimeConversion.DATETIME_CLAMP - or opts.datetime_conversion == DatetimeConversion.DATETIME_AUTO - ): - tz = opts.tzinfo or utc - if opts.datetime_conversion == DatetimeConversion.DATETIME_CLAMP: - millis = max(_min_datetime_ms(tz), min(millis, _max_datetime_ms(tz))) - elif opts.datetime_conversion == DatetimeConversion.DATETIME_AUTO: - if not (_min_datetime_ms(tz) <= millis <= _max_datetime_ms(tz)): - return DatetimeMS(millis) - - diff = ((millis % 1000) + 1000) % 1000 - seconds = (millis - diff) // 1000 - micros = diff * 1000 - - try: - if opts.tz_aware: - dt = EPOCH_AWARE + datetime.timedelta(seconds=seconds, microseconds=micros) - if opts.tzinfo: - dt = dt.astimezone(tz) - return dt - else: - return EPOCH_NAIVE + datetime.timedelta(seconds=seconds, microseconds=micros) - except ArithmeticError as err: - raise InvalidBSON(f"{err} {_DATETIME_ERROR_SUGGESTION}") from err - - elif opts.datetime_conversion == DatetimeConversion.DATETIME_MS: - return DatetimeMS(millis) - else: - raise ValueError("datetime_conversion must be an element of DatetimeConversion") diff --git a/venv/Lib/site-packages/bson/dbref.py b/venv/Lib/site-packages/bson/dbref.py deleted file mode 100644 index 40bdb73..0000000 --- a/venv/Lib/site-packages/bson/dbref.py +++ /dev/null @@ -1,133 +0,0 @@ -# Copyright 2009-2015 MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tools for manipulating DBRefs (references to MongoDB documents).""" -from __future__ import annotations - -from copy import deepcopy -from typing import Any, Mapping, Optional - -from bson._helpers import _getstate_slots, _setstate_slots -from bson.son import SON - - -class DBRef: - """A reference to a document stored in MongoDB.""" - - __slots__ = "__collection", "__id", "__database", "__kwargs" - __getstate__ = _getstate_slots - __setstate__ = _setstate_slots - # DBRef isn't actually a BSON "type" so this number was arbitrarily chosen. - _type_marker = 100 - - def __init__( - self, - collection: str, - id: Any, - database: Optional[str] = None, - _extra: Optional[Mapping[str, Any]] = None, - **kwargs: Any, - ) -> None: - """Initialize a new :class:`DBRef`. - - Raises :class:`TypeError` if `collection` or `database` is not - an instance of :class:`str`. `database` is optional and allows - references to documents to work across databases. Any additional - keyword arguments will create additional fields in the resultant - embedded document. - - :param collection: name of the collection the document is stored in - :param id: the value of the document's ``"_id"`` field - :param database: name of the database to reference - :param kwargs: additional keyword arguments will - create additional, custom fields - - .. seealso:: The MongoDB documentation on `dbrefs `_. - """ - if not isinstance(collection, str): - raise TypeError(f"collection must be an instance of str, not {type(collection)}") - if database is not None and not isinstance(database, str): - raise TypeError(f"database must be an instance of str, not {type(database)}") - - self.__collection = collection - self.__id = id - self.__database = database - kwargs.update(_extra or {}) - self.__kwargs = kwargs - - @property - def collection(self) -> str: - """Get the name of this DBRef's collection.""" - return self.__collection - - @property - def id(self) -> Any: - """Get this DBRef's _id.""" - return self.__id - - @property - def database(self) -> Optional[str]: - """Get the name of this DBRef's database. - - Returns None if this DBRef doesn't specify a database. - """ - return self.__database - - def __getattr__(self, key: Any) -> Any: - try: - return self.__kwargs[key] - except KeyError: - raise AttributeError(key) from None - - def as_doc(self) -> SON[str, Any]: - """Get the SON document representation of this DBRef. - - Generally not needed by application developers - """ - doc = SON([("$ref", self.collection), ("$id", self.id)]) - if self.database is not None: - doc["$db"] = self.database - doc.update(self.__kwargs) - return doc - - def __repr__(self) -> str: - extra = "".join([f", {k}={v!r}" for k, v in self.__kwargs.items()]) - if self.database is None: - return f"DBRef({self.collection!r}, {self.id!r}{extra})" - return f"DBRef({self.collection!r}, {self.id!r}, {self.database!r}{extra})" - - def __eq__(self, other: Any) -> bool: - if isinstance(other, DBRef): - us = (self.__database, self.__collection, self.__id, self.__kwargs) - them = (other.__database, other.__collection, other.__id, other.__kwargs) - return us == them - return NotImplemented - - def __ne__(self, other: Any) -> bool: - return not self == other - - def __hash__(self) -> int: - """Get a hash value for this :class:`DBRef`.""" - return hash( - (self.__collection, self.__id, self.__database, tuple(sorted(self.__kwargs.items()))) - ) - - def __deepcopy__(self, memo: Any) -> DBRef: - """Support function for `copy.deepcopy()`.""" - return DBRef( - deepcopy(self.__collection, memo), - deepcopy(self.__id, memo), - deepcopy(self.__database, memo), - deepcopy(self.__kwargs, memo), - ) diff --git a/venv/Lib/site-packages/bson/decimal128.py b/venv/Lib/site-packages/bson/decimal128.py deleted file mode 100644 index 92c054d..0000000 --- a/venv/Lib/site-packages/bson/decimal128.py +++ /dev/null @@ -1,312 +0,0 @@ -# Copyright 2016-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tools for working with the BSON decimal128 type. - -.. versionadded:: 3.4 -""" -from __future__ import annotations - -import decimal -import struct -from typing import Any, Sequence, Tuple, Type, Union - -_PACK_64 = struct.Struct(" decimal.Context: - """Returns an instance of :class:`decimal.Context` appropriate - for working with IEEE-754 128-bit decimal floating point values. - """ - opts = _CTX_OPTIONS.copy() - opts["traps"] = [] - return decimal.Context(**opts) # type: ignore - - -def _decimal_to_128(value: _VALUE_OPTIONS) -> Tuple[int, int]: - """Converts a decimal.Decimal to BID (high bits, low bits). - - :param value: An instance of decimal.Decimal - """ - with decimal.localcontext(_DEC128_CTX) as ctx: - value = ctx.create_decimal(value) - - if value.is_infinite(): - return _NINF if value.is_signed() else _PINF - - sign, digits, exponent = value.as_tuple() - - if value.is_nan(): - if digits: - raise ValueError("NaN with debug payload is not supported") - if value.is_snan(): - return _NSNAN if value.is_signed() else _PSNAN - return _NNAN if value.is_signed() else _PNAN - - significand = int("".join([str(digit) for digit in digits])) - bit_length = significand.bit_length() - - high = 0 - low = 0 - for i in range(min(64, bit_length)): - if significand & (1 << i): - low |= 1 << i - - for i in range(64, bit_length): - if significand & (1 << i): - high |= 1 << (i - 64) - - biased_exponent = exponent + _EXPONENT_BIAS # type: ignore[operator] - - if high >> 49 == 1: - high = high & 0x7FFFFFFFFFFF - high |= _EXPONENT_MASK - high |= (biased_exponent & 0x3FFF) << 47 - else: - high |= biased_exponent << 49 - - if sign: - high |= _SIGN - - return high, low - - -class Decimal128: - """BSON Decimal128 type:: - - >>> Decimal128(Decimal("0.0005")) - Decimal128('0.0005') - >>> Decimal128("0.0005") - Decimal128('0.0005') - >>> Decimal128((3474527112516337664, 5)) - Decimal128('0.0005') - - :param value: An instance of :class:`decimal.Decimal`, string, or tuple of - (high bits, low bits) from Binary Integer Decimal (BID) format. - - .. note:: :class:`~Decimal128` uses an instance of :class:`decimal.Context` - configured for IEEE-754 Decimal128 when validating parameters. - Signals like :class:`decimal.InvalidOperation`, :class:`decimal.Inexact`, - and :class:`decimal.Overflow` are trapped and raised as exceptions:: - - >>> Decimal128(".13.1") - Traceback (most recent call last): - File "", line 1, in - ... - decimal.InvalidOperation: [] - >>> - >>> Decimal128("1E-6177") - Traceback (most recent call last): - File "", line 1, in - ... - decimal.Inexact: [] - >>> - >>> Decimal128("1E6145") - Traceback (most recent call last): - File "", line 1, in - ... - decimal.Overflow: [, ] - - To ensure the result of a calculation can always be stored as BSON - Decimal128 use the context returned by - :func:`create_decimal128_context`:: - - >>> import decimal - >>> decimal128_ctx = create_decimal128_context() - >>> with decimal.localcontext(decimal128_ctx) as ctx: - ... Decimal128(ctx.create_decimal(".13.3")) - ... - Decimal128('NaN') - >>> - >>> with decimal.localcontext(decimal128_ctx) as ctx: - ... Decimal128(ctx.create_decimal("1E-6177")) - ... - Decimal128('0E-6176') - >>> - >>> with decimal.localcontext(DECIMAL128_CTX) as ctx: - ... Decimal128(ctx.create_decimal("1E6145")) - ... - Decimal128('Infinity') - - To match the behavior of MongoDB's Decimal128 implementation - str(Decimal(value)) may not match str(Decimal128(value)) for NaN values:: - - >>> Decimal128(Decimal('NaN')) - Decimal128('NaN') - >>> Decimal128(Decimal('-NaN')) - Decimal128('NaN') - >>> Decimal128(Decimal('sNaN')) - Decimal128('NaN') - >>> Decimal128(Decimal('-sNaN')) - Decimal128('NaN') - - However, :meth:`~Decimal128.to_decimal` will return the exact value:: - - >>> Decimal128(Decimal('NaN')).to_decimal() - Decimal('NaN') - >>> Decimal128(Decimal('-NaN')).to_decimal() - Decimal('-NaN') - >>> Decimal128(Decimal('sNaN')).to_decimal() - Decimal('sNaN') - >>> Decimal128(Decimal('-sNaN')).to_decimal() - Decimal('-sNaN') - - Two instances of :class:`Decimal128` compare equal if their Binary - Integer Decimal encodings are equal:: - - >>> Decimal128('NaN') == Decimal128('NaN') - True - >>> Decimal128('NaN').bid == Decimal128('NaN').bid - True - - This differs from :class:`decimal.Decimal` comparisons for NaN:: - - >>> Decimal('NaN') == Decimal('NaN') - False - """ - - __slots__ = ("__high", "__low") - - _type_marker = 19 - - def __init__(self, value: _VALUE_OPTIONS) -> None: - if isinstance(value, (str, decimal.Decimal)): - self.__high, self.__low = _decimal_to_128(value) - elif isinstance(value, (list, tuple)): - if len(value) != 2: - raise ValueError( - "Invalid size for creation of Decimal128 " - "from list or tuple. Must have exactly 2 " - "elements." - ) - self.__high, self.__low = value - else: - raise TypeError(f"Cannot convert {value!r} to Decimal128") - - def to_decimal(self) -> decimal.Decimal: - """Returns an instance of :class:`decimal.Decimal` for this - :class:`Decimal128`. - """ - high = self.__high - low = self.__low - sign = 1 if (high & _SIGN) else 0 - - if (high & _SNAN) == _SNAN: - return decimal.Decimal((sign, (), "N")) # type: ignore - elif (high & _NAN) == _NAN: - return decimal.Decimal((sign, (), "n")) # type: ignore - elif (high & _INF) == _INF: - return decimal.Decimal((sign, (), "F")) # type: ignore - - if (high & _EXPONENT_MASK) == _EXPONENT_MASK: - exponent = ((high & 0x1FFFE00000000000) >> 47) - _EXPONENT_BIAS - return decimal.Decimal((sign, (0,), exponent)) - else: - exponent = ((high & 0x7FFF800000000000) >> 49) - _EXPONENT_BIAS - - arr = bytearray(15) - mask = 0x00000000000000FF - for i in range(14, 6, -1): - arr[i] = (low & mask) >> ((14 - i) << 3) - mask = mask << 8 - - mask = 0x00000000000000FF - for i in range(6, 0, -1): - arr[i] = (high & mask) >> ((6 - i) << 3) - mask = mask << 8 - - mask = 0x0001000000000000 - arr[0] = (high & mask) >> 48 - - # cdecimal only accepts a tuple for digits. - digits = tuple(int(digit) for digit in str(int.from_bytes(arr, "big"))) - - with decimal.localcontext(_DEC128_CTX) as ctx: - return ctx.create_decimal((sign, digits, exponent)) - - @classmethod - def from_bid(cls: Type[Decimal128], value: bytes) -> Decimal128: - """Create an instance of :class:`Decimal128` from Binary Integer - Decimal string. - - :param value: 16 byte string (128-bit IEEE 754-2008 decimal floating - point in Binary Integer Decimal (BID) format). - """ - if not isinstance(value, bytes): - raise TypeError(f"value must be an instance of bytes, not {type(value)}") - if len(value) != 16: - raise ValueError("value must be exactly 16 bytes") - return cls((_UNPACK_64(value[8:])[0], _UNPACK_64(value[:8])[0])) # type: ignore - - @property - def bid(self) -> bytes: - """The Binary Integer Decimal (BID) encoding of this instance.""" - return _PACK_64(self.__low) + _PACK_64(self.__high) - - def __str__(self) -> str: - dec = self.to_decimal() - if dec.is_nan(): - # Required by the drivers spec to match MongoDB behavior. - return "NaN" - return str(dec) - - def __repr__(self) -> str: - return f"Decimal128('{self!s}')" - - def __setstate__(self, value: Tuple[int, int]) -> None: - self.__high, self.__low = value - - def __getstate__(self) -> Tuple[int, int]: - return self.__high, self.__low - - def __eq__(self, other: Any) -> bool: - if isinstance(other, Decimal128): - return self.bid == other.bid - return NotImplemented - - def __ne__(self, other: Any) -> bool: - return not self == other diff --git a/venv/Lib/site-packages/bson/errors.py b/venv/Lib/site-packages/bson/errors.py deleted file mode 100644 index a3699e7..0000000 --- a/venv/Lib/site-packages/bson/errors.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2009-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Exceptions raised by the BSON package.""" -from __future__ import annotations - - -class BSONError(Exception): - """Base class for all BSON exceptions.""" - - -class InvalidBSON(BSONError): - """Raised when trying to create a BSON object from invalid data.""" - - -class InvalidStringData(BSONError): - """Raised when trying to encode a string containing non-UTF8 data.""" - - -class InvalidDocument(BSONError): - """Raised when trying to create a BSON object from an invalid document.""" - - -class InvalidId(BSONError): - """Raised when trying to create an ObjectId from invalid data.""" diff --git a/venv/Lib/site-packages/bson/int64.py b/venv/Lib/site-packages/bson/int64.py deleted file mode 100644 index 5846504..0000000 --- a/venv/Lib/site-packages/bson/int64.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright 2014-2015 MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""A BSON wrapper for long (int in python3)""" -from __future__ import annotations - -from typing import Any - - -class Int64(int): - """Representation of the BSON int64 type. - - This is necessary because every integral number is an :class:`int` in - Python 3. Small integral numbers are encoded to BSON int32 by default, - but Int64 numbers will always be encoded to BSON int64. - - :param value: the numeric value to represent - """ - - __slots__ = () - - _type_marker = 18 - - def __getstate__(self) -> Any: - return {} - - def __setstate__(self, state: Any) -> None: - pass diff --git a/venv/Lib/site-packages/bson/json_util.py b/venv/Lib/site-packages/bson/json_util.py deleted file mode 100644 index ecae103..0000000 --- a/venv/Lib/site-packages/bson/json_util.py +++ /dev/null @@ -1,1164 +0,0 @@ -# Copyright 2009-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tools for using Python's :mod:`json` module with BSON documents. - -This module provides two helper methods `dumps` and `loads` that wrap the -native :mod:`json` methods and provide explicit BSON conversion to and from -JSON. :class:`~bson.json_util.JSONOptions` provides a way to control how JSON -is emitted and parsed, with the default being the Relaxed Extended JSON format. -:mod:`~bson.json_util` can also generate Canonical or legacy `Extended JSON`_ -when :const:`CANONICAL_JSON_OPTIONS` or :const:`LEGACY_JSON_OPTIONS` is -provided, respectively. - -.. _Extended JSON: https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md - -Example usage (deserialization): - -.. doctest:: - - >>> from bson.json_util import loads - >>> loads( - ... '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$scope": {}, "$code": "function x() { return 1; }"}}, {"bin": {"$type": "80", "$binary": "AQIDBA=="}}]' - ... ) - [{'foo': [1, 2]}, {'bar': {'hello': 'world'}}, {'code': Code('function x() { return 1; }', {})}, {'bin': Binary(b'...', 128)}] - -Example usage with :const:`RELAXED_JSON_OPTIONS` (the default): - -.. doctest:: - - >>> from bson import Binary, Code - >>> from bson.json_util import dumps - >>> dumps( - ... [ - ... {"foo": [1, 2]}, - ... {"bar": {"hello": "world"}}, - ... {"code": Code("function x() { return 1; }")}, - ... {"bin": Binary(b"\x01\x02\x03\x04")}, - ... ] - ... ) - '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }"}}, {"bin": {"$binary": {"base64": "AQIDBA==", "subType": "00"}}}]' - -Example usage (with :const:`CANONICAL_JSON_OPTIONS`): - -.. doctest:: - - >>> from bson import Binary, Code - >>> from bson.json_util import dumps, CANONICAL_JSON_OPTIONS - >>> dumps( - ... [ - ... {"foo": [1, 2]}, - ... {"bar": {"hello": "world"}}, - ... {"code": Code("function x() { return 1; }")}, - ... {"bin": Binary(b"\x01\x02\x03\x04")}, - ... ], - ... json_options=CANONICAL_JSON_OPTIONS, - ... ) - '[{"foo": [{"$numberInt": "1"}, {"$numberInt": "2"}]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }"}}, {"bin": {"$binary": {"base64": "AQIDBA==", "subType": "00"}}}]' - -Example usage (with :const:`LEGACY_JSON_OPTIONS`): - -.. doctest:: - - >>> from bson import Binary, Code - >>> from bson.json_util import dumps, LEGACY_JSON_OPTIONS - >>> dumps( - ... [ - ... {"foo": [1, 2]}, - ... {"bar": {"hello": "world"}}, - ... {"code": Code("function x() { return 1; }", {})}, - ... {"bin": Binary(b"\x01\x02\x03\x04")}, - ... ], - ... json_options=LEGACY_JSON_OPTIONS, - ... ) - '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }", "$scope": {}}}, {"bin": {"$binary": "AQIDBA==", "$type": "00"}}]' - -Alternatively, you can manually pass the `default` to :func:`json.dumps`. -It won't handle :class:`~bson.binary.Binary` and :class:`~bson.code.Code` -instances (as they are extended strings you can't provide custom defaults), -but it will be faster as there is less recursion. - -.. note:: - If your application does not need the flexibility offered by - :class:`JSONOptions` and spends a large amount of time in the `json_util` - module, look to - `python-bsonjs `_ for a nice - performance improvement. `python-bsonjs` is a fast BSON to MongoDB - Extended JSON converter for Python built on top of - `libbson `_. `python-bsonjs` works best - with PyMongo when using :class:`~bson.raw_bson.RawBSONDocument`. -""" -from __future__ import annotations - -import base64 -import datetime -import json -import math -import re -import uuid -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Mapping, - MutableMapping, - Optional, - Sequence, - Tuple, - Type, - Union, - cast, -) - -from bson.binary import ALL_UUID_SUBTYPES, UUID_SUBTYPE, Binary, UuidRepresentation -from bson.code import Code -from bson.codec_options import CodecOptions, DatetimeConversion -from bson.datetime_ms import ( - _MAX_UTC_MS, - EPOCH_AWARE, - DatetimeMS, - _datetime_to_millis, - _millis_to_datetime, -) -from bson.dbref import DBRef -from bson.decimal128 import Decimal128 -from bson.int64 import Int64 -from bson.max_key import MaxKey -from bson.min_key import MinKey -from bson.objectid import ObjectId -from bson.regex import Regex -from bson.son import RE_TYPE -from bson.timestamp import Timestamp -from bson.tz_util import utc - -_RE_OPT_TABLE = { - "i": re.I, - "l": re.L, - "m": re.M, - "s": re.S, - "u": re.U, - "x": re.X, -} - - -class DatetimeRepresentation: - LEGACY = 0 - """Legacy MongoDB Extended JSON datetime representation. - - :class:`datetime.datetime` instances will be encoded to JSON in the - format `{"$date": }`, where `dateAsMilliseconds` is - a 64-bit signed integer giving the number of milliseconds since the Unix - epoch UTC. This was the default encoding before PyMongo version 3.4. - - .. versionadded:: 3.4 - """ - - NUMBERLONG = 1 - """NumberLong datetime representation. - - :class:`datetime.datetime` instances will be encoded to JSON in the - format `{"$date": {"$numberLong": ""}}`, - where `dateAsMilliseconds` is the string representation of a 64-bit signed - integer giving the number of milliseconds since the Unix epoch UTC. - - .. versionadded:: 3.4 - """ - - ISO8601 = 2 - """ISO-8601 datetime representation. - - :class:`datetime.datetime` instances greater than or equal to the Unix - epoch UTC will be encoded to JSON in the format `{"$date": ""}`. - :class:`datetime.datetime` instances before the Unix epoch UTC will be - encoded as if the datetime representation is - :const:`~DatetimeRepresentation.NUMBERLONG`. - - .. versionadded:: 3.4 - """ - - -class JSONMode: - LEGACY = 0 - """Legacy Extended JSON representation. - - In this mode, :func:`~bson.json_util.dumps` produces PyMongo's legacy - non-standard JSON output. Consider using - :const:`~bson.json_util.JSONMode.RELAXED` or - :const:`~bson.json_util.JSONMode.CANONICAL` instead. - - .. versionadded:: 3.5 - """ - - RELAXED = 1 - """Relaxed Extended JSON representation. - - In this mode, :func:`~bson.json_util.dumps` produces Relaxed Extended JSON, - a mostly JSON-like format. Consider using this for things like a web API, - where one is sending a document (or a projection of a document) that only - uses ordinary JSON type primitives. In particular, the ``int``, - :class:`~bson.int64.Int64`, and ``float`` numeric types are represented in - the native JSON number format. This output is also the most human readable - and is useful for debugging and documentation. - - .. seealso:: The specification for Relaxed `Extended JSON`_. - - .. versionadded:: 3.5 - """ - - CANONICAL = 2 - """Canonical Extended JSON representation. - - In this mode, :func:`~bson.json_util.dumps` produces Canonical Extended - JSON, a type preserving format. Consider using this for things like - testing, where one has to precisely specify expected types in JSON. In - particular, the ``int``, :class:`~bson.int64.Int64`, and ``float`` numeric - types are encoded with type wrappers. - - .. seealso:: The specification for Canonical `Extended JSON`_. - - .. versionadded:: 3.5 - """ - - -if TYPE_CHECKING: - _BASE_CLASS = CodecOptions[MutableMapping[str, Any]] -else: - _BASE_CLASS = CodecOptions - -_INT32_MAX = 2**31 - - -class JSONOptions(_BASE_CLASS): - json_mode: int - strict_number_long: bool - datetime_representation: int - strict_uuid: bool - document_class: Type[MutableMapping[str, Any]] - - def __init__(self, *args: Any, **kwargs: Any): - """Encapsulates JSON options for :func:`dumps` and :func:`loads`. - - :param strict_number_long: If ``True``, :class:`~bson.int64.Int64` objects - are encoded to MongoDB Extended JSON's *Strict mode* type - `NumberLong`, ie ``'{"$numberLong": "" }'``. Otherwise they - will be encoded as an `int`. Defaults to ``False``. - :param datetime_representation: The representation to use when encoding - instances of :class:`datetime.datetime`. Defaults to - :const:`~DatetimeRepresentation.LEGACY`. - :param strict_uuid: If ``True``, :class:`uuid.UUID` object are encoded to - MongoDB Extended JSON's *Strict mode* type `Binary`. Otherwise it - will be encoded as ``'{"$uuid": "" }'``. Defaults to ``False``. - :param json_mode: The :class:`JSONMode` to use when encoding BSON types to - Extended JSON. Defaults to :const:`~JSONMode.LEGACY`. - :param document_class: BSON documents returned by :func:`loads` will be - decoded to an instance of this class. Must be a subclass of - :class:`collections.MutableMapping`. Defaults to :class:`dict`. - :param uuid_representation: The :class:`~bson.binary.UuidRepresentation` - to use when encoding and decoding instances of :class:`uuid.UUID`. - Defaults to :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`. - :param tz_aware: If ``True``, MongoDB Extended JSON's *Strict mode* type - `Date` will be decoded to timezone aware instances of - :class:`datetime.datetime`. Otherwise they will be naive. Defaults - to ``False``. - :param tzinfo: A :class:`datetime.tzinfo` subclass that specifies the - timezone from which :class:`~datetime.datetime` objects should be - decoded. Defaults to :const:`~bson.tz_util.utc`. - :param datetime_conversion: Specifies how UTC datetimes should be decoded - within BSON. Valid options include 'datetime_ms' to return as a - DatetimeMS, 'datetime' to return as a datetime.datetime and - raising a ValueError for out-of-range values, 'datetime_auto' to - return DatetimeMS objects when the underlying datetime is - out-of-range and 'datetime_clamp' to clamp to the minimum and - maximum possible datetimes. Defaults to 'datetime'. See - :ref:`handling-out-of-range-datetimes` for details. - :param args: arguments to :class:`~bson.codec_options.CodecOptions` - :param kwargs: arguments to :class:`~bson.codec_options.CodecOptions` - - .. seealso:: The specification for Relaxed and Canonical `Extended JSON`_. - - .. versionchanged:: 4.0 - The default for `json_mode` was changed from :const:`JSONMode.LEGACY` - to :const:`JSONMode.RELAXED`. - The default for `uuid_representation` was changed from - :const:`~bson.binary.UuidRepresentation.PYTHON_LEGACY` to - :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`. - - .. versionchanged:: 3.5 - Accepts the optional parameter `json_mode`. - - .. versionchanged:: 4.0 - Changed default value of `tz_aware` to False. - """ - super().__init__() - - def __new__( - cls: Type[JSONOptions], - strict_number_long: Optional[bool] = None, - datetime_representation: Optional[int] = None, - strict_uuid: Optional[bool] = None, - json_mode: int = JSONMode.RELAXED, - *args: Any, - **kwargs: Any, - ) -> JSONOptions: - kwargs["tz_aware"] = kwargs.get("tz_aware", False) - if kwargs["tz_aware"]: - kwargs["tzinfo"] = kwargs.get("tzinfo", utc) - if datetime_representation not in ( - DatetimeRepresentation.LEGACY, - DatetimeRepresentation.NUMBERLONG, - DatetimeRepresentation.ISO8601, - None, - ): - raise ValueError( - "JSONOptions.datetime_representation must be one of LEGACY, " - "NUMBERLONG, or ISO8601 from DatetimeRepresentation." - ) - self = cast(JSONOptions, super().__new__(cls, *args, **kwargs)) - if json_mode not in (JSONMode.LEGACY, JSONMode.RELAXED, JSONMode.CANONICAL): - raise ValueError( - "JSONOptions.json_mode must be one of LEGACY, RELAXED, " - "or CANONICAL from JSONMode." - ) - self.json_mode = json_mode - if self.json_mode == JSONMode.RELAXED: - if strict_number_long: - raise ValueError("Cannot specify strict_number_long=True with JSONMode.RELAXED") - if datetime_representation not in (None, DatetimeRepresentation.ISO8601): - raise ValueError( - "datetime_representation must be DatetimeRepresentation." - "ISO8601 or omitted with JSONMode.RELAXED" - ) - if strict_uuid not in (None, True): - raise ValueError("Cannot specify strict_uuid=False with JSONMode.RELAXED") - self.strict_number_long = False - self.datetime_representation = DatetimeRepresentation.ISO8601 - self.strict_uuid = True - elif self.json_mode == JSONMode.CANONICAL: - if strict_number_long not in (None, True): - raise ValueError("Cannot specify strict_number_long=False with JSONMode.RELAXED") - if datetime_representation not in (None, DatetimeRepresentation.NUMBERLONG): - raise ValueError( - "datetime_representation must be DatetimeRepresentation." - "NUMBERLONG or omitted with JSONMode.RELAXED" - ) - if strict_uuid not in (None, True): - raise ValueError("Cannot specify strict_uuid=False with JSONMode.RELAXED") - self.strict_number_long = True - self.datetime_representation = DatetimeRepresentation.NUMBERLONG - self.strict_uuid = True - else: # JSONMode.LEGACY - self.strict_number_long = False - self.datetime_representation = DatetimeRepresentation.LEGACY - self.strict_uuid = False - if strict_number_long is not None: - self.strict_number_long = strict_number_long - if datetime_representation is not None: - self.datetime_representation = datetime_representation - if strict_uuid is not None: - self.strict_uuid = strict_uuid - return self - - def _arguments_repr(self) -> str: - return ( - "strict_number_long={!r}, " - "datetime_representation={!r}, " - "strict_uuid={!r}, json_mode={!r}, {}".format( - self.strict_number_long, - self.datetime_representation, - self.strict_uuid, - self.json_mode, - super()._arguments_repr(), - ) - ) - - def _options_dict(self) -> dict[Any, Any]: - # TODO: PYTHON-2442 use _asdict() instead - options_dict = super()._options_dict() - options_dict.update( - { - "strict_number_long": self.strict_number_long, - "datetime_representation": self.datetime_representation, - "strict_uuid": self.strict_uuid, - "json_mode": self.json_mode, - } - ) - return options_dict - - def with_options(self, **kwargs: Any) -> JSONOptions: - """ - Make a copy of this JSONOptions, overriding some options:: - - >>> from bson.json_util import CANONICAL_JSON_OPTIONS - >>> CANONICAL_JSON_OPTIONS.tz_aware - True - >>> json_options = CANONICAL_JSON_OPTIONS.with_options(tz_aware=False, tzinfo=None) - >>> json_options.tz_aware - False - - .. versionadded:: 3.12 - """ - opts = self._options_dict() - for opt in ("strict_number_long", "datetime_representation", "strict_uuid", "json_mode"): - opts[opt] = kwargs.get(opt, getattr(self, opt)) - opts.update(kwargs) - return JSONOptions(**opts) - - -LEGACY_JSON_OPTIONS: JSONOptions = JSONOptions(json_mode=JSONMode.LEGACY) -""":class:`JSONOptions` for encoding to PyMongo's legacy JSON format. - -.. seealso:: The documentation for :const:`bson.json_util.JSONMode.LEGACY`. - -.. versionadded:: 3.5 -""" - -CANONICAL_JSON_OPTIONS: JSONOptions = JSONOptions(json_mode=JSONMode.CANONICAL) -""":class:`JSONOptions` for Canonical Extended JSON. - -.. seealso:: The documentation for :const:`bson.json_util.JSONMode.CANONICAL`. - -.. versionadded:: 3.5 -""" - -RELAXED_JSON_OPTIONS: JSONOptions = JSONOptions(json_mode=JSONMode.RELAXED) -""":class:`JSONOptions` for Relaxed Extended JSON. - -.. seealso:: The documentation for :const:`bson.json_util.JSONMode.RELAXED`. - -.. versionadded:: 3.5 -""" - -DEFAULT_JSON_OPTIONS: JSONOptions = RELAXED_JSON_OPTIONS -"""The default :class:`JSONOptions` for JSON encoding/decoding. - -The same as :const:`RELAXED_JSON_OPTIONS`. - -.. versionchanged:: 4.0 - Changed from :const:`LEGACY_JSON_OPTIONS` to - :const:`RELAXED_JSON_OPTIONS`. - -.. versionadded:: 3.4 -""" - - -def dumps(obj: Any, *args: Any, **kwargs: Any) -> str: - """Helper function that wraps :func:`json.dumps`. - - Recursive function that handles all BSON types including - :class:`~bson.binary.Binary` and :class:`~bson.code.Code`. - - :param json_options: A :class:`JSONOptions` instance used to modify the - encoding of MongoDB Extended JSON types. Defaults to - :const:`DEFAULT_JSON_OPTIONS`. - - .. versionchanged:: 4.0 - Now outputs MongoDB Relaxed Extended JSON by default (using - :const:`DEFAULT_JSON_OPTIONS`). - - .. versionchanged:: 3.4 - Accepts optional parameter `json_options`. See :class:`JSONOptions`. - """ - json_options = kwargs.pop("json_options", DEFAULT_JSON_OPTIONS) - return json.dumps(_json_convert(obj, json_options), *args, **kwargs) - - -def loads(s: Union[str, bytes, bytearray], *args: Any, **kwargs: Any) -> Any: - """Helper function that wraps :func:`json.loads`. - - Automatically passes the object_hook for BSON type conversion. - - Raises ``TypeError``, ``ValueError``, ``KeyError``, or - :exc:`~bson.errors.InvalidId` on invalid MongoDB Extended JSON. - - :param json_options: A :class:`JSONOptions` instance used to modify the - decoding of MongoDB Extended JSON types. Defaults to - :const:`DEFAULT_JSON_OPTIONS`. - - .. versionchanged:: 4.0 - Now loads :class:`datetime.datetime` instances as naive by default. To - load timezone aware instances utilize the `json_options` parameter. - See :ref:`tz_aware_default_change` for an example. - - .. versionchanged:: 3.5 - Parses Relaxed and Canonical Extended JSON as well as PyMongo's legacy - format. Now raises ``TypeError`` or ``ValueError`` when parsing JSON - type wrappers with values of the wrong type or any extra keys. - - .. versionchanged:: 3.4 - Accepts optional parameter `json_options`. See :class:`JSONOptions`. - """ - json_options = kwargs.pop("json_options", DEFAULT_JSON_OPTIONS) - # Execution time optimization if json_options.document_class is dict - if json_options.document_class is dict: - kwargs["object_hook"] = lambda obj: object_hook(obj, json_options) - else: - kwargs["object_pairs_hook"] = lambda pairs: object_pairs_hook(pairs, json_options) - return json.loads(s, *args, **kwargs) - - -def _json_convert(obj: Any, json_options: JSONOptions = DEFAULT_JSON_OPTIONS) -> Any: - """Recursive helper method that converts BSON types so they can be - converted into json. - """ - if hasattr(obj, "items"): - return {k: _json_convert(v, json_options) for k, v in obj.items()} - elif hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes)): - return [_json_convert(v, json_options) for v in obj] - try: - return default(obj, json_options) - except TypeError: - return obj - - -def object_pairs_hook( - pairs: Sequence[Tuple[str, Any]], json_options: JSONOptions = DEFAULT_JSON_OPTIONS -) -> Any: - return object_hook(json_options.document_class(pairs), json_options) # type:ignore[call-arg] - - -def object_hook(dct: Mapping[str, Any], json_options: JSONOptions = DEFAULT_JSON_OPTIONS) -> Any: - match = None - for k in dct: - if k in _PARSERS_SET: - match = k - break - if match: - return _PARSERS[match](dct, json_options) - return dct - - -def _parse_legacy_regex(doc: Any, dummy0: Any) -> Any: - pattern = doc["$regex"] - # Check if this is the $regex query operator. - if not isinstance(pattern, (str, bytes)): - return doc - flags = 0 - # PyMongo always adds $options but some other tools may not. - for opt in doc.get("$options", ""): - flags |= _RE_OPT_TABLE.get(opt, 0) - return Regex(pattern, flags) - - -def _parse_legacy_uuid(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: - """Decode a JSON legacy $uuid to Python UUID.""" - if len(doc) != 1: - raise TypeError(f"Bad $uuid, extra field(s): {doc}") - if not isinstance(doc["$uuid"], str): - raise TypeError(f"$uuid must be a string: {doc}") - if json_options.uuid_representation == UuidRepresentation.UNSPECIFIED: - return Binary.from_uuid(uuid.UUID(doc["$uuid"])) - else: - return uuid.UUID(doc["$uuid"]) - - -def _binary_or_uuid(data: Any, subtype: int, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: - # special handling for UUID - if subtype in ALL_UUID_SUBTYPES: - uuid_representation = json_options.uuid_representation - binary_value = Binary(data, subtype) - if uuid_representation == UuidRepresentation.UNSPECIFIED: - return binary_value - if subtype == UUID_SUBTYPE: - # Legacy behavior: use STANDARD with binary subtype 4. - uuid_representation = UuidRepresentation.STANDARD - elif uuid_representation == UuidRepresentation.STANDARD: - # subtype == OLD_UUID_SUBTYPE - # Legacy behavior: STANDARD is the same as PYTHON_LEGACY. - uuid_representation = UuidRepresentation.PYTHON_LEGACY - return binary_value.as_uuid(uuid_representation) - - if subtype == 0: - return cast(uuid.UUID, data) - return Binary(data, subtype) - - -def _parse_legacy_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: - if isinstance(doc["$type"], int): - doc["$type"] = "%02x" % doc["$type"] - subtype = int(doc["$type"], 16) - if subtype >= 0xFFFFFF80: # Handle mongoexport values - subtype = int(doc["$type"][6:], 16) - data = base64.b64decode(doc["$binary"].encode()) - return _binary_or_uuid(data, subtype, json_options) - - -def _parse_canonical_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: - binary = doc["$binary"] - b64 = binary["base64"] - subtype = binary["subType"] - if not isinstance(b64, str): - raise TypeError(f"$binary base64 must be a string: {doc}") - if not isinstance(subtype, str) or len(subtype) > 2: - raise TypeError(f"$binary subType must be a string at most 2 characters: {doc}") - if len(binary) != 2: - raise TypeError(f'$binary must include only "base64" and "subType" components: {doc}') - - data = base64.b64decode(b64.encode()) - return _binary_or_uuid(data, int(subtype, 16), json_options) - - -def _parse_canonical_datetime( - doc: Any, json_options: JSONOptions -) -> Union[datetime.datetime, DatetimeMS]: - """Decode a JSON datetime to python datetime.datetime.""" - dtm = doc["$date"] - if len(doc) != 1: - raise TypeError(f"Bad $date, extra field(s): {doc}") - # mongoexport 2.6 and newer - if isinstance(dtm, str): - try: - # Parse offset - if dtm[-1] == "Z": - dt = dtm[:-1] - offset = "Z" - elif dtm[-6] in ("+", "-") and dtm[-3] == ":": - # (+|-)HH:MM - dt = dtm[:-6] - offset = dtm[-6:] - elif dtm[-5] in ("+", "-"): - # (+|-)HHMM - dt = dtm[:-5] - offset = dtm[-5:] - elif dtm[-3] in ("+", "-"): - # (+|-)HH - dt = dtm[:-3] - offset = dtm[-3:] - else: - dt = dtm - offset = "" - except IndexError as exc: - raise ValueError(f"time data {dtm!r} does not match ISO-8601 datetime format") from exc - - # Parse the optional factional seconds portion. - dot_index = dt.rfind(".") - microsecond = 0 - if dot_index != -1: - microsecond = int(float(dt[dot_index:]) * 1000000) - dt = dt[:dot_index] - - aware = datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S").replace( - microsecond=microsecond, tzinfo=utc - ) - - if offset and offset != "Z": - if len(offset) == 6: - hours, minutes = offset[1:].split(":") - secs = int(hours) * 3600 + int(minutes) * 60 - elif len(offset) == 5: - secs = int(offset[1:3]) * 3600 + int(offset[3:]) * 60 - elif len(offset) == 3: - secs = int(offset[1:3]) * 3600 - if offset[0] == "-": - secs *= -1 - aware = aware - datetime.timedelta(seconds=secs) - - if json_options.tz_aware: - if json_options.tzinfo: - aware = aware.astimezone(json_options.tzinfo) - if json_options.datetime_conversion == DatetimeConversion.DATETIME_MS: - return DatetimeMS(aware) - return aware - else: - aware_tzinfo_none = aware.replace(tzinfo=None) - if json_options.datetime_conversion == DatetimeConversion.DATETIME_MS: - return DatetimeMS(aware_tzinfo_none) - return aware_tzinfo_none - return _millis_to_datetime(int(dtm), cast("CodecOptions[Any]", json_options)) - - -def _parse_canonical_oid(doc: Any, dummy0: Any) -> ObjectId: - """Decode a JSON ObjectId to bson.objectid.ObjectId.""" - if len(doc) != 1: - raise TypeError(f"Bad $oid, extra field(s): {doc}") - return ObjectId(doc["$oid"]) - - -def _parse_canonical_symbol(doc: Any, dummy0: Any) -> str: - """Decode a JSON symbol to Python string.""" - symbol = doc["$symbol"] - if len(doc) != 1: - raise TypeError(f"Bad $symbol, extra field(s): {doc}") - return str(symbol) - - -def _parse_canonical_code(doc: Any, dummy0: Any) -> Code: - """Decode a JSON code to bson.code.Code.""" - for key in doc: - if key not in ("$code", "$scope"): - raise TypeError(f"Bad $code, extra field(s): {doc}") - return Code(doc["$code"], scope=doc.get("$scope")) - - -def _parse_canonical_regex(doc: Any, dummy0: Any) -> Regex[str]: - """Decode a JSON regex to bson.regex.Regex.""" - regex = doc["$regularExpression"] - if len(doc) != 1: - raise TypeError(f"Bad $regularExpression, extra field(s): {doc}") - if len(regex) != 2: - raise TypeError( - f'Bad $regularExpression must include only "pattern and "options" components: {doc}' - ) - opts = regex["options"] - if not isinstance(opts, str): - raise TypeError( - "Bad $regularExpression options, options must be string, was type %s" % (type(opts)) - ) - return Regex(regex["pattern"], opts) - - -def _parse_canonical_dbref(doc: Any, dummy0: Any) -> Any: - """Decode a JSON DBRef to bson.dbref.DBRef.""" - if ( - isinstance(doc.get("$ref"), str) - and "$id" in doc - and isinstance(doc.get("$db"), (str, type(None))) - ): - return DBRef(doc.pop("$ref"), doc.pop("$id"), database=doc.pop("$db", None), **doc) - return doc - - -def _parse_canonical_dbpointer(doc: Any, dummy0: Any) -> Any: - """Decode a JSON (deprecated) DBPointer to bson.dbref.DBRef.""" - dbref = doc["$dbPointer"] - if len(doc) != 1: - raise TypeError(f"Bad $dbPointer, extra field(s): {doc}") - if isinstance(dbref, DBRef): - dbref_doc = dbref.as_doc() - # DBPointer must not contain $db in its value. - if dbref.database is not None: - raise TypeError(f"Bad $dbPointer, extra field $db: {dbref_doc}") - if not isinstance(dbref.id, ObjectId): - raise TypeError(f"Bad $dbPointer, $id must be an ObjectId: {dbref_doc}") - if len(dbref_doc) != 2: - raise TypeError(f"Bad $dbPointer, extra field(s) in DBRef: {dbref_doc}") - return dbref - else: - raise TypeError(f"Bad $dbPointer, expected a DBRef: {doc}") - - -def _parse_canonical_int32(doc: Any, dummy0: Any) -> int: - """Decode a JSON int32 to python int.""" - i_str = doc["$numberInt"] - if len(doc) != 1: - raise TypeError(f"Bad $numberInt, extra field(s): {doc}") - if not isinstance(i_str, str): - raise TypeError(f"$numberInt must be string: {doc}") - return int(i_str) - - -def _parse_canonical_int64(doc: Any, dummy0: Any) -> Int64: - """Decode a JSON int64 to bson.int64.Int64.""" - l_str = doc["$numberLong"] - if len(doc) != 1: - raise TypeError(f"Bad $numberLong, extra field(s): {doc}") - return Int64(l_str) - - -def _parse_canonical_double(doc: Any, dummy0: Any) -> float: - """Decode a JSON double to python float.""" - d_str = doc["$numberDouble"] - if len(doc) != 1: - raise TypeError(f"Bad $numberDouble, extra field(s): {doc}") - if not isinstance(d_str, str): - raise TypeError(f"$numberDouble must be string: {doc}") - return float(d_str) - - -def _parse_canonical_decimal128(doc: Any, dummy0: Any) -> Decimal128: - """Decode a JSON decimal128 to bson.decimal128.Decimal128.""" - d_str = doc["$numberDecimal"] - if len(doc) != 1: - raise TypeError(f"Bad $numberDecimal, extra field(s): {doc}") - if not isinstance(d_str, str): - raise TypeError(f"$numberDecimal must be string: {doc}") - return Decimal128(d_str) - - -def _parse_canonical_minkey(doc: Any, dummy0: Any) -> MinKey: - """Decode a JSON MinKey to bson.min_key.MinKey.""" - if type(doc["$minKey"]) is not int or doc["$minKey"] != 1: # noqa: E721 - raise TypeError(f"$minKey value must be 1: {doc}") - if len(doc) != 1: - raise TypeError(f"Bad $minKey, extra field(s): {doc}") - return MinKey() - - -def _parse_canonical_maxkey(doc: Any, dummy0: Any) -> MaxKey: - """Decode a JSON MaxKey to bson.max_key.MaxKey.""" - if type(doc["$maxKey"]) is not int or doc["$maxKey"] != 1: # noqa: E721 - raise TypeError("$maxKey value must be 1: %s", (doc,)) - if len(doc) != 1: - raise TypeError(f"Bad $minKey, extra field(s): {doc}") - return MaxKey() - - -def _parse_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: - if "$type" in doc: - return _parse_legacy_binary(doc, json_options) - else: - return _parse_canonical_binary(doc, json_options) - - -def _parse_timestamp(doc: Any, dummy0: Any) -> Timestamp: - tsp = doc["$timestamp"] - return Timestamp(tsp["t"], tsp["i"]) - - -_PARSERS: dict[str, Callable[[Any, JSONOptions], Any]] = { - "$oid": _parse_canonical_oid, - "$ref": _parse_canonical_dbref, - "$date": _parse_canonical_datetime, - "$regex": _parse_legacy_regex, - "$minKey": _parse_canonical_minkey, - "$maxKey": _parse_canonical_maxkey, - "$binary": _parse_binary, - "$code": _parse_canonical_code, - "$uuid": _parse_legacy_uuid, - "$undefined": lambda _, _1: None, - "$numberLong": _parse_canonical_int64, - "$timestamp": _parse_timestamp, - "$numberDecimal": _parse_canonical_decimal128, - "$dbPointer": _parse_canonical_dbpointer, - "$regularExpression": _parse_canonical_regex, - "$symbol": _parse_canonical_symbol, - "$numberInt": _parse_canonical_int32, - "$numberDouble": _parse_canonical_double, -} -_PARSERS_SET = set(_PARSERS) - - -def _encode_binary(data: bytes, subtype: int, json_options: JSONOptions) -> Any: - if json_options.json_mode == JSONMode.LEGACY: - return {"$binary": base64.b64encode(data).decode(), "$type": "%02x" % subtype} - return {"$binary": {"base64": base64.b64encode(data).decode(), "subType": "%02x" % subtype}} - - -def _encode_datetimems(obj: Any, json_options: JSONOptions) -> dict: - if ( - json_options.datetime_representation == DatetimeRepresentation.ISO8601 - and 0 <= int(obj) <= _MAX_UTC_MS - ): - return _encode_datetime(obj.as_datetime(), json_options) - elif json_options.datetime_representation == DatetimeRepresentation.LEGACY: - return {"$date": int(obj)} - return {"$date": {"$numberLong": str(int(obj))}} - - -def _encode_code(obj: Code, json_options: JSONOptions) -> dict: - if obj.scope is None: - return {"$code": str(obj)} - else: - return {"$code": str(obj), "$scope": _json_convert(obj.scope, json_options)} - - -def _encode_int64(obj: Int64, json_options: JSONOptions) -> Any: - if json_options.strict_number_long: - return {"$numberLong": str(obj)} - else: - return int(obj) - - -def _encode_noop(obj: Any, dummy0: Any) -> Any: - return obj - - -def _encode_regex(obj: Any, json_options: JSONOptions) -> dict: - flags = "" - if obj.flags & re.IGNORECASE: - flags += "i" - if obj.flags & re.LOCALE: - flags += "l" - if obj.flags & re.MULTILINE: - flags += "m" - if obj.flags & re.DOTALL: - flags += "s" - if obj.flags & re.UNICODE: - flags += "u" - if obj.flags & re.VERBOSE: - flags += "x" - if isinstance(obj.pattern, str): - pattern = obj.pattern - else: - pattern = obj.pattern.decode("utf-8") - if json_options.json_mode == JSONMode.LEGACY: - return {"$regex": pattern, "$options": flags} - return {"$regularExpression": {"pattern": pattern, "options": flags}} - - -def _encode_int(obj: int, json_options: JSONOptions) -> Any: - if json_options.json_mode == JSONMode.CANONICAL: - if -_INT32_MAX <= obj < _INT32_MAX: - return {"$numberInt": str(obj)} - return {"$numberLong": str(obj)} - return obj - - -def _encode_float(obj: float, json_options: JSONOptions) -> Any: - if json_options.json_mode != JSONMode.LEGACY: - if math.isnan(obj): - return {"$numberDouble": "NaN"} - elif math.isinf(obj): - representation = "Infinity" if obj > 0 else "-Infinity" - return {"$numberDouble": representation} - elif json_options.json_mode == JSONMode.CANONICAL: - # repr() will return the shortest string guaranteed to produce the - # original value, when float() is called on it. - return {"$numberDouble": str(repr(obj))} - return obj - - -def _encode_datetime(obj: datetime.datetime, json_options: JSONOptions) -> dict: - if json_options.datetime_representation == DatetimeRepresentation.ISO8601: - if not obj.tzinfo: - obj = obj.replace(tzinfo=utc) - assert obj.tzinfo is not None - if obj >= EPOCH_AWARE: - off = obj.tzinfo.utcoffset(obj) - if (off.days, off.seconds, off.microseconds) == (0, 0, 0): # type: ignore - tz_string = "Z" - else: - tz_string = obj.strftime("%z") - millis = int(obj.microsecond / 1000) - fracsecs = ".%03d" % (millis,) if millis else "" - return { - "$date": "{}{}{}".format(obj.strftime("%Y-%m-%dT%H:%M:%S"), fracsecs, tz_string) - } - - millis = _datetime_to_millis(obj) - if json_options.datetime_representation == DatetimeRepresentation.LEGACY: - return {"$date": millis} - return {"$date": {"$numberLong": str(millis)}} - - -def _encode_bytes(obj: bytes, json_options: JSONOptions) -> dict: - return _encode_binary(obj, 0, json_options) - - -def _encode_binary_obj(obj: Binary, json_options: JSONOptions) -> dict: - return _encode_binary(obj, obj.subtype, json_options) - - -def _encode_uuid(obj: uuid.UUID, json_options: JSONOptions) -> dict: - if json_options.strict_uuid: - binval = Binary.from_uuid(obj, uuid_representation=json_options.uuid_representation) - return _encode_binary(binval, binval.subtype, json_options) - else: - return {"$uuid": obj.hex} - - -def _encode_objectid(obj: ObjectId, dummy0: Any) -> dict: - return {"$oid": str(obj)} - - -def _encode_timestamp(obj: Timestamp, dummy0: Any) -> dict: - return {"$timestamp": {"t": obj.time, "i": obj.inc}} - - -def _encode_decimal128(obj: Timestamp, dummy0: Any) -> dict: - return {"$numberDecimal": str(obj)} - - -def _encode_dbref(obj: DBRef, json_options: JSONOptions) -> dict: - return _json_convert(obj.as_doc(), json_options=json_options) - - -def _encode_minkey(dummy0: Any, dummy1: Any) -> dict: - return {"$minKey": 1} - - -def _encode_maxkey(dummy0: Any, dummy1: Any) -> dict: - return {"$maxKey": 1} - - -# Encoders for BSON types -# Each encoder function's signature is: -# - obj: a Python data type, e.g. a Python int for _encode_int -# - json_options: a JSONOptions -_ENCODERS: dict[Type, Callable[[Any, JSONOptions], Any]] = { - bool: _encode_noop, - bytes: _encode_bytes, - datetime.datetime: _encode_datetime, - DatetimeMS: _encode_datetimems, - float: _encode_float, - int: _encode_int, - str: _encode_noop, - type(None): _encode_noop, - uuid.UUID: _encode_uuid, - Binary: _encode_binary_obj, - Int64: _encode_int64, - Code: _encode_code, - DBRef: _encode_dbref, - MaxKey: _encode_maxkey, - MinKey: _encode_minkey, - ObjectId: _encode_objectid, - Regex: _encode_regex, - RE_TYPE: _encode_regex, - Timestamp: _encode_timestamp, - Decimal128: _encode_decimal128, -} - -# Map each _type_marker to its encoder for faster lookup. -_MARKERS: dict[int, Callable[[Any, JSONOptions], Any]] = {} -for _typ in _ENCODERS: - if hasattr(_typ, "_type_marker"): - _MARKERS[_typ._type_marker] = _ENCODERS[_typ] - -_BUILT_IN_TYPES = tuple(t for t in _ENCODERS) - - -def default(obj: Any, json_options: JSONOptions = DEFAULT_JSON_OPTIONS) -> Any: - # First see if the type is already cached. KeyError will only ever - # happen once per subtype. - try: - return _ENCODERS[type(obj)](obj, json_options) - except KeyError: - pass - - # Second, fall back to trying _type_marker. This has to be done - # before the loop below since users could subclass one of our - # custom types that subclasses a python built-in (e.g. Binary) - if hasattr(obj, "_type_marker"): - marker = obj._type_marker - if marker in _MARKERS: - func = _MARKERS[marker] - # Cache this type for faster subsequent lookup. - _ENCODERS[type(obj)] = func - return func(obj, json_options) - - # Third, test each base type. This will only happen once for - # a subtype of a supported base type. - for base in _BUILT_IN_TYPES: - if isinstance(obj, base): - func = _ENCODERS[base] - # Cache this type for faster subsequent lookup. - _ENCODERS[type(obj)] = func - return func(obj, json_options) - - raise TypeError("%r is not JSON serializable" % obj) - - -def _get_str_size(obj: Any) -> int: - return len(obj) - - -def _get_datetime_size(obj: datetime.datetime) -> int: - return 5 + len(str(obj.time())) - - -def _get_regex_size(obj: Regex) -> int: - return 18 + len(obj.pattern) - - -def _get_dbref_size(obj: DBRef) -> int: - return 34 + len(obj.collection) - - -_CONSTANT_SIZE_TABLE: dict[Any, int] = { - ObjectId: 28, - int: 11, - Int64: 11, - Decimal128: 11, - Timestamp: 14, - MinKey: 8, - MaxKey: 8, -} - -_VARIABLE_SIZE_TABLE: dict[Any, Callable[[Any], int]] = { - str: _get_str_size, - bytes: _get_str_size, - datetime.datetime: _get_datetime_size, - Regex: _get_regex_size, - DBRef: _get_dbref_size, -} - - -def get_size(obj: Any, max_size: int, current_size: int = 0) -> int: - """Recursively finds size of objects""" - if current_size >= max_size: - return current_size - - obj_type = type(obj) - - # Check to see if the obj has a constant size estimate - try: - return _CONSTANT_SIZE_TABLE[obj_type] - except KeyError: - pass - - # Check to see if the obj has a variable but simple size estimate - try: - return _VARIABLE_SIZE_TABLE[obj_type](obj) - except KeyError: - pass - - # Special cases that require recursion - if obj_type == Code: - if obj.scope: - current_size += ( - 5 + get_size(obj.scope, max_size, current_size) + len(obj) - len(obj.scope) - ) - else: - current_size += 5 + len(obj) - elif obj_type == dict: - for k, v in obj.items(): - current_size += get_size(k, max_size, current_size) - current_size += get_size(v, max_size, current_size) - if current_size >= max_size: - return current_size - elif hasattr(obj, "__iter__"): - for i in obj: - current_size += get_size(i, max_size, current_size) - if current_size >= max_size: - return current_size - return current_size - - -def _truncate_documents(obj: Any, max_length: int) -> Tuple[Any, int]: - """Recursively truncate documents as needed to fit inside max_length characters.""" - if max_length <= 0: - return None, 0 - remaining = max_length - if hasattr(obj, "items"): - truncated: Any = {} - for k, v in obj.items(): - truncated_v, remaining = _truncate_documents(v, remaining) - if truncated_v: - truncated[k] = truncated_v - if remaining <= 0: - break - return truncated, remaining - elif hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes)): - truncated: Any = [] # type:ignore[no-redef] - for v in obj: - truncated_v, remaining = _truncate_documents(v, remaining) - if truncated_v: - truncated.append(truncated_v) - if remaining <= 0: - break - return truncated, remaining - else: - return _truncate(obj, remaining) - - -def _truncate(obj: Any, remaining: int) -> Tuple[Any, int]: - size = get_size(obj, remaining) - - if size <= remaining: - return obj, remaining - size - else: - try: - truncated = obj[:remaining] - except TypeError: - truncated = obj - return truncated, remaining - size diff --git a/venv/Lib/site-packages/bson/max_key.py b/venv/Lib/site-packages/bson/max_key.py deleted file mode 100644 index 445e12f..0000000 --- a/venv/Lib/site-packages/bson/max_key.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright 2010-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Representation for the MongoDB internal MaxKey type.""" -from __future__ import annotations - -from typing import Any - - -class MaxKey: - """MongoDB internal MaxKey type.""" - - __slots__ = () - - _type_marker = 127 - - def __getstate__(self) -> Any: - return {} - - def __setstate__(self, state: Any) -> None: - pass - - def __eq__(self, other: Any) -> bool: - return isinstance(other, MaxKey) - - def __hash__(self) -> int: - return hash(self._type_marker) - - def __ne__(self, other: Any) -> bool: - return not self == other - - def __le__(self, other: Any) -> bool: - return isinstance(other, MaxKey) - - def __lt__(self, dummy: Any) -> bool: - return False - - def __ge__(self, dummy: Any) -> bool: - return True - - def __gt__(self, other: Any) -> bool: - return not isinstance(other, MaxKey) - - def __repr__(self) -> str: - return "MaxKey()" diff --git a/venv/Lib/site-packages/bson/min_key.py b/venv/Lib/site-packages/bson/min_key.py deleted file mode 100644 index 37828dc..0000000 --- a/venv/Lib/site-packages/bson/min_key.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright 2010-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Representation for the MongoDB internal MinKey type.""" -from __future__ import annotations - -from typing import Any - - -class MinKey: - """MongoDB internal MinKey type.""" - - __slots__ = () - - _type_marker = 255 - - def __getstate__(self) -> Any: - return {} - - def __setstate__(self, state: Any) -> None: - pass - - def __eq__(self, other: Any) -> bool: - return isinstance(other, MinKey) - - def __hash__(self) -> int: - return hash(self._type_marker) - - def __ne__(self, other: Any) -> bool: - return not self == other - - def __le__(self, dummy: Any) -> bool: - return True - - def __lt__(self, other: Any) -> bool: - return not isinstance(other, MinKey) - - def __ge__(self, other: Any) -> bool: - return isinstance(other, MinKey) - - def __gt__(self, dummy: Any) -> bool: - return False - - def __repr__(self) -> str: - return "MinKey()" diff --git a/venv/Lib/site-packages/bson/objectid.py b/venv/Lib/site-packages/bson/objectid.py deleted file mode 100644 index 970c4e5..0000000 --- a/venv/Lib/site-packages/bson/objectid.py +++ /dev/null @@ -1,274 +0,0 @@ -# Copyright 2009-2015 MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tools for working with MongoDB ObjectIds.""" -from __future__ import annotations - -import binascii -import datetime -import os -import struct -import threading -import time -from random import SystemRandom -from typing import Any, NoReturn, Optional, Type, Union - -from bson.datetime_ms import _datetime_to_millis -from bson.errors import InvalidId -from bson.tz_util import utc - -_MAX_COUNTER_VALUE = 0xFFFFFF -_PACK_INT = struct.Struct(">I").pack -_PACK_INT_RANDOM = struct.Struct(">I5s").pack -_UNPACK_INT = struct.Struct(">I").unpack - - -def _raise_invalid_id(oid: str) -> NoReturn: - raise InvalidId( - "%r is not a valid ObjectId, it must be a 12-byte input" - " or a 24-character hex string" % oid - ) - - -def _random_bytes() -> bytes: - """Get the 5-byte random field of an ObjectId.""" - return os.urandom(5) - - -class ObjectId: - """A MongoDB ObjectId.""" - - _pid = os.getpid() - - _inc = SystemRandom().randint(0, _MAX_COUNTER_VALUE) - _inc_lock = threading.Lock() - - __random = _random_bytes() - - __slots__ = ("__id",) - - _type_marker = 7 - - def __init__(self, oid: Optional[Union[str, ObjectId, bytes]] = None) -> None: - """Initialize a new ObjectId. - - An ObjectId is a 12-byte unique identifier consisting of: - - - a 4-byte value representing the seconds since the Unix epoch, - - a 5-byte random value, - - a 3-byte counter, starting with a random value. - - By default, ``ObjectId()`` creates a new unique identifier. The - optional parameter `oid` can be an :class:`ObjectId`, or any 12 - :class:`bytes`. - - For example, the 12 bytes b'foo-bar-quux' do not follow the ObjectId - specification but they are acceptable input:: - - >>> ObjectId(b'foo-bar-quux') - ObjectId('666f6f2d6261722d71757578') - - `oid` can also be a :class:`str` of 24 hex digits:: - - >>> ObjectId('0123456789ab0123456789ab') - ObjectId('0123456789ab0123456789ab') - - Raises :class:`~bson.errors.InvalidId` if `oid` is not 12 bytes nor - 24 hex digits, or :class:`TypeError` if `oid` is not an accepted type. - - :param oid: a valid ObjectId. - - .. seealso:: The MongoDB documentation on `ObjectIds `_. - - .. versionchanged:: 3.8 - :class:`~bson.objectid.ObjectId` now implements the `ObjectID - specification version 0.2 - `_. - """ - if oid is None: - self.__generate() - elif isinstance(oid, bytes) and len(oid) == 12: - self.__id = oid - else: - self.__validate(oid) - - @classmethod - def from_datetime(cls: Type[ObjectId], generation_time: datetime.datetime) -> ObjectId: - """Create a dummy ObjectId instance with a specific generation time. - - This method is useful for doing range queries on a field - containing :class:`ObjectId` instances. - - .. warning:: - It is not safe to insert a document containing an ObjectId - generated using this method. This method deliberately - eliminates the uniqueness guarantee that ObjectIds - generally provide. ObjectIds generated with this method - should be used exclusively in queries. - - `generation_time` will be converted to UTC. Naive datetime - instances will be treated as though they already contain UTC. - - An example using this helper to get documents where ``"_id"`` - was generated before January 1, 2010 would be: - - >>> gen_time = datetime.datetime(2010, 1, 1) - >>> dummy_id = ObjectId.from_datetime(gen_time) - >>> result = collection.find({"_id": {"$lt": dummy_id}}) - - :param generation_time: :class:`~datetime.datetime` to be used - as the generation time for the resulting ObjectId. - """ - oid = ( - _PACK_INT(_datetime_to_millis(generation_time) // 1000) - + b"\x00\x00\x00\x00\x00\x00\x00\x00" - ) - return cls(oid) - - @classmethod - def is_valid(cls: Type[ObjectId], oid: Any) -> bool: - """Checks if a `oid` string is valid or not. - - :param oid: the object id to validate - - .. versionadded:: 2.3 - """ - if not oid: - return False - - try: - ObjectId(oid) - return True - except (InvalidId, TypeError): - return False - - @classmethod - def _random(cls) -> bytes: - """Generate a 5-byte random number once per process.""" - pid = os.getpid() - if pid != cls._pid: - cls._pid = pid - cls.__random = _random_bytes() - return cls.__random - - def __generate(self) -> None: - """Generate a new value for this ObjectId.""" - with ObjectId._inc_lock: - inc = ObjectId._inc - ObjectId._inc = (inc + 1) % (_MAX_COUNTER_VALUE + 1) - - # 4 bytes current time, 5 bytes random, 3 bytes inc. - self.__id = _PACK_INT_RANDOM(int(time.time()), ObjectId._random()) + _PACK_INT(inc)[1:4] - - def __validate(self, oid: Any) -> None: - """Validate and use the given id for this ObjectId. - - Raises TypeError if id is not an instance of :class:`str`, - :class:`bytes`, or ObjectId. Raises InvalidId if it is not a - valid ObjectId. - - :param oid: a valid ObjectId - """ - if isinstance(oid, ObjectId): - self.__id = oid.binary - elif isinstance(oid, str): - if len(oid) == 24: - try: - self.__id = bytes.fromhex(oid) - except (TypeError, ValueError): - _raise_invalid_id(oid) - else: - _raise_invalid_id(oid) - else: - raise TypeError(f"id must be an instance of (bytes, str, ObjectId), not {type(oid)}") - - @property - def binary(self) -> bytes: - """12-byte binary representation of this ObjectId.""" - return self.__id - - @property - def generation_time(self) -> datetime.datetime: - """A :class:`datetime.datetime` instance representing the time of - generation for this :class:`ObjectId`. - - The :class:`datetime.datetime` is timezone aware, and - represents the generation time in UTC. It is precise to the - second. - """ - timestamp = _UNPACK_INT(self.__id[0:4])[0] - return datetime.datetime.fromtimestamp(timestamp, utc) - - def __getstate__(self) -> bytes: - """Return value of object for pickling. - needed explicitly because __slots__() defined. - """ - return self.__id - - def __setstate__(self, value: Any) -> None: - """Explicit state set from pickling""" - # Provide backwards compatibility with OIDs - # pickled with pymongo-1.9 or older. - if isinstance(value, dict): - oid = value["_ObjectId__id"] - else: - oid = value - # ObjectIds pickled in python 2.x used `str` for __id. - # In python 3.x this has to be converted to `bytes` - # by encoding latin-1. - if isinstance(oid, str): - self.__id = oid.encode("latin-1") - else: - self.__id = oid - - def __str__(self) -> str: - return binascii.hexlify(self.__id).decode() - - def __repr__(self) -> str: - return f"ObjectId('{self!s}')" - - def __eq__(self, other: Any) -> bool: - if isinstance(other, ObjectId): - return self.__id == other.binary - return NotImplemented - - def __ne__(self, other: Any) -> bool: - if isinstance(other, ObjectId): - return self.__id != other.binary - return NotImplemented - - def __lt__(self, other: Any) -> bool: - if isinstance(other, ObjectId): - return self.__id < other.binary - return NotImplemented - - def __le__(self, other: Any) -> bool: - if isinstance(other, ObjectId): - return self.__id <= other.binary - return NotImplemented - - def __gt__(self, other: Any) -> bool: - if isinstance(other, ObjectId): - return self.__id > other.binary - return NotImplemented - - def __ge__(self, other: Any) -> bool: - if isinstance(other, ObjectId): - return self.__id >= other.binary - return NotImplemented - - def __hash__(self) -> int: - """Get a hash value for this :class:`ObjectId`.""" - return hash(self.__id) diff --git a/venv/Lib/site-packages/bson/py.typed b/venv/Lib/site-packages/bson/py.typed deleted file mode 100644 index 0f40570..0000000 --- a/venv/Lib/site-packages/bson/py.typed +++ /dev/null @@ -1,2 +0,0 @@ -# PEP-561 Support File. -# "Package maintainers who wish to support type checking of their code MUST add a marker file named py.typed to their package supporting typing". diff --git a/venv/Lib/site-packages/bson/raw_bson.py b/venv/Lib/site-packages/bson/raw_bson.py deleted file mode 100644 index 2ce5314..0000000 --- a/venv/Lib/site-packages/bson/raw_bson.py +++ /dev/null @@ -1,196 +0,0 @@ -# Copyright 2015-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tools for representing raw BSON documents. - -Inserting and Retrieving RawBSONDocuments -========================================= - -Example: Moving a document between different databases/collections - -.. doctest:: - - >>> import bson - >>> from pymongo import MongoClient - >>> from bson.raw_bson import RawBSONDocument - >>> client = MongoClient(document_class=RawBSONDocument) - >>> client.drop_database("db") - >>> client.drop_database("replica_db") - >>> db = client.db - >>> result = db.test.insert_many( - ... [{"_id": 1, "a": 1}, {"_id": 2, "b": 1}, {"_id": 3, "c": 1}, {"_id": 4, "d": 1}] - ... ) - >>> replica_db = client.replica_db - >>> for doc in db.test.find(): - ... print(f"raw document: {doc.raw}") - ... print(f"decoded document: {bson.decode(doc.raw)}") - ... result = replica_db.test.insert_one(doc) - ... - raw document: b'...' - decoded document: {'_id': 1, 'a': 1} - raw document: b'...' - decoded document: {'_id': 2, 'b': 1} - raw document: b'...' - decoded document: {'_id': 3, 'c': 1} - raw document: b'...' - decoded document: {'_id': 4, 'd': 1} - -For use cases like moving documents across different databases or writing binary -blobs to disk, using raw BSON documents provides better speed and avoids the -overhead of decoding or encoding BSON. -""" -from __future__ import annotations - -from typing import Any, ItemsView, Iterator, Mapping, Optional - -from bson import _get_object_size, _raw_to_dict -from bson.codec_options import _RAW_BSON_DOCUMENT_MARKER, CodecOptions -from bson.codec_options import DEFAULT_CODEC_OPTIONS as DEFAULT - - -def _inflate_bson( - bson_bytes: bytes, codec_options: CodecOptions[RawBSONDocument], raw_array: bool = False -) -> dict[str, Any]: - """Inflates the top level fields of a BSON document. - - :param bson_bytes: the BSON bytes that compose this document - :param codec_options: An instance of - :class:`~bson.codec_options.CodecOptions` whose ``document_class`` - must be :class:`RawBSONDocument`. - """ - return _raw_to_dict(bson_bytes, 4, len(bson_bytes) - 1, codec_options, {}, raw_array=raw_array) - - -class RawBSONDocument(Mapping[str, Any]): - """Representation for a MongoDB document that provides access to the raw - BSON bytes that compose it. - - Only when a field is accessed or modified within the document does - RawBSONDocument decode its bytes. - """ - - __slots__ = ("__raw", "__inflated_doc", "__codec_options") - _type_marker = _RAW_BSON_DOCUMENT_MARKER - __codec_options: CodecOptions[RawBSONDocument] - - def __init__( - self, bson_bytes: bytes, codec_options: Optional[CodecOptions[RawBSONDocument]] = None - ) -> None: - """Create a new :class:`RawBSONDocument` - - :class:`RawBSONDocument` is a representation of a BSON document that - provides access to the underlying raw BSON bytes. Only when a field is - accessed or modified within the document does RawBSONDocument decode - its bytes. - - :class:`RawBSONDocument` implements the ``Mapping`` abstract base - class from the standard library so it can be used like a read-only - ``dict``:: - - >>> from bson import encode - >>> raw_doc = RawBSONDocument(encode({'_id': 'my_doc'})) - >>> raw_doc.raw - b'...' - >>> raw_doc['_id'] - 'my_doc' - - :param bson_bytes: the BSON bytes that compose this document - :param codec_options: An instance of - :class:`~bson.codec_options.CodecOptions` whose ``document_class`` - must be :class:`RawBSONDocument`. The default is - :attr:`DEFAULT_RAW_BSON_OPTIONS`. - - .. versionchanged:: 3.8 - :class:`RawBSONDocument` now validates that the ``bson_bytes`` - passed in represent a single bson document. - - .. versionchanged:: 3.5 - If a :class:`~bson.codec_options.CodecOptions` is passed in, its - `document_class` must be :class:`RawBSONDocument`. - """ - self.__raw = bson_bytes - self.__inflated_doc: Optional[Mapping[str, Any]] = None - # Can't default codec_options to DEFAULT_RAW_BSON_OPTIONS in signature, - # it refers to this class RawBSONDocument. - if codec_options is None: - codec_options = DEFAULT_RAW_BSON_OPTIONS - elif not issubclass(codec_options.document_class, RawBSONDocument): - raise TypeError( - "RawBSONDocument cannot use CodecOptions with document " - f"class {codec_options.document_class}" - ) - self.__codec_options = codec_options - # Validate the bson object size. - _get_object_size(bson_bytes, 0, len(bson_bytes)) - - @property - def raw(self) -> bytes: - """The raw BSON bytes composing this document.""" - return self.__raw - - def items(self) -> ItemsView[str, Any]: - """Lazily decode and iterate elements in this document.""" - return self.__inflated.items() - - @property - def __inflated(self) -> Mapping[str, Any]: - if self.__inflated_doc is None: - # We already validated the object's size when this document was - # created, so no need to do that again. - self.__inflated_doc = self._inflate_bson(self.__raw, self.__codec_options) - return self.__inflated_doc - - @staticmethod - def _inflate_bson( - bson_bytes: bytes, codec_options: CodecOptions[RawBSONDocument] - ) -> Mapping[str, Any]: - return _inflate_bson(bson_bytes, codec_options) - - def __getitem__(self, item: str) -> Any: - return self.__inflated[item] - - def __iter__(self) -> Iterator[str]: - return iter(self.__inflated) - - def __len__(self) -> int: - return len(self.__inflated) - - def __eq__(self, other: Any) -> bool: - if isinstance(other, RawBSONDocument): - return self.__raw == other.raw - return NotImplemented - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.raw!r}, codec_options={self.__codec_options!r})" - - -class _RawArrayBSONDocument(RawBSONDocument): - """A RawBSONDocument that only expands sub-documents and arrays when accessed.""" - - @staticmethod - def _inflate_bson( - bson_bytes: bytes, codec_options: CodecOptions[RawBSONDocument] - ) -> Mapping[str, Any]: - return _inflate_bson(bson_bytes, codec_options, raw_array=True) - - -DEFAULT_RAW_BSON_OPTIONS: CodecOptions[RawBSONDocument] = DEFAULT.with_options( - document_class=RawBSONDocument -) -_RAW_ARRAY_BSON_OPTIONS: CodecOptions[_RawArrayBSONDocument] = DEFAULT.with_options( - document_class=_RawArrayBSONDocument -) -"""The default :class:`~bson.codec_options.CodecOptions` for -:class:`RawBSONDocument`. -""" diff --git a/venv/Lib/site-packages/bson/regex.py b/venv/Lib/site-packages/bson/regex.py deleted file mode 100644 index 60cff4f..0000000 --- a/venv/Lib/site-packages/bson/regex.py +++ /dev/null @@ -1,133 +0,0 @@ -# Copyright 2013-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tools for representing MongoDB regular expressions.""" -from __future__ import annotations - -import re -from typing import Any, Generic, Pattern, Type, TypeVar, Union - -from bson._helpers import _getstate_slots, _setstate_slots -from bson.son import RE_TYPE - - -def str_flags_to_int(str_flags: str) -> int: - flags = 0 - if "i" in str_flags: - flags |= re.IGNORECASE - if "l" in str_flags: - flags |= re.LOCALE - if "m" in str_flags: - flags |= re.MULTILINE - if "s" in str_flags: - flags |= re.DOTALL - if "u" in str_flags: - flags |= re.UNICODE - if "x" in str_flags: - flags |= re.VERBOSE - - return flags - - -_T = TypeVar("_T", str, bytes) - - -class Regex(Generic[_T]): - """BSON regular expression data.""" - - __slots__ = ("pattern", "flags") - - __getstate__ = _getstate_slots - __setstate__ = _setstate_slots - - _type_marker = 11 - - @classmethod - def from_native(cls: Type[Regex[Any]], regex: Pattern[_T]) -> Regex[_T]: - """Convert a Python regular expression into a ``Regex`` instance. - - Note that in Python 3, a regular expression compiled from a - :class:`str` has the ``re.UNICODE`` flag set. If it is undesirable - to store this flag in a BSON regular expression, unset it first:: - - >>> pattern = re.compile('.*') - >>> regex = Regex.from_native(pattern) - >>> regex.flags ^= re.UNICODE - >>> db.collection.insert_one({'pattern': regex}) - - :param regex: A regular expression object from ``re.compile()``. - - .. warning:: - Python regular expressions use a different syntax and different - set of flags than MongoDB, which uses `PCRE`_. A regular - expression retrieved from the server may not compile in - Python, or may match a different set of strings in Python than - when used in a MongoDB query. - - .. _PCRE: http://www.pcre.org/ - """ - if not isinstance(regex, RE_TYPE): - raise TypeError("regex must be a compiled regular expression, not %s" % type(regex)) - - return Regex(regex.pattern, regex.flags) - - def __init__(self, pattern: _T, flags: Union[str, int] = 0) -> None: - """BSON regular expression data. - - This class is useful to store and retrieve regular expressions that are - incompatible with Python's regular expression dialect. - - :param pattern: string - :param flags: an integer bitmask, or a string of flag - characters like "im" for IGNORECASE and MULTILINE - """ - if not isinstance(pattern, (str, bytes)): - raise TypeError("pattern must be a string, not %s" % type(pattern)) - self.pattern: _T = pattern - - if isinstance(flags, str): - self.flags = str_flags_to_int(flags) - elif isinstance(flags, int): - self.flags = flags - else: - raise TypeError("flags must be a string or int, not %s" % type(flags)) - - def __eq__(self, other: Any) -> bool: - if isinstance(other, Regex): - return self.pattern == other.pattern and self.flags == other.flags - else: - return NotImplemented - - __hash__ = None # type: ignore - - def __ne__(self, other: Any) -> bool: - return not self == other - - def __repr__(self) -> str: - return f"Regex({self.pattern!r}, {self.flags!r})" - - def try_compile(self) -> Pattern[_T]: - """Compile this :class:`Regex` as a Python regular expression. - - .. warning:: - Python regular expressions use a different syntax and different - set of flags than MongoDB, which uses `PCRE`_. A regular - expression retrieved from the server may not compile in - Python, or may match a different set of strings in Python than - when used in a MongoDB query. :meth:`try_compile()` may raise - :exc:`re.error`. - - .. _PCRE: http://www.pcre.org/ - """ - return re.compile(self.pattern, self.flags) diff --git a/venv/Lib/site-packages/bson/son.py b/venv/Lib/site-packages/bson/son.py deleted file mode 100644 index 24275fc..0000000 --- a/venv/Lib/site-packages/bson/son.py +++ /dev/null @@ -1,211 +0,0 @@ -# Copyright 2009-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tools for creating and manipulating SON, the Serialized Ocument Notation. - -Regular dictionaries can be used instead of SON objects, but not when the order -of keys is important. A SON object can be used just like a normal Python -dictionary. -""" -from __future__ import annotations - -import copy -import re -from collections.abc import Mapping as _Mapping -from typing import ( - Any, - Dict, - Iterable, - Iterator, - Mapping, - Optional, - Pattern, - Tuple, - Type, - TypeVar, - Union, - cast, -) - -# This sort of sucks, but seems to be as good as it gets... -# This is essentially the same as re._pattern_type -RE_TYPE: Type[Pattern[Any]] = type(re.compile("")) - -_Key = TypeVar("_Key") -_Value = TypeVar("_Value") -_T = TypeVar("_T") - - -class SON(Dict[_Key, _Value]): - """SON data. - - A subclass of dict that maintains ordering of keys and provides a - few extra niceties for dealing with SON. SON provides an API - similar to collections.OrderedDict. - """ - - __keys: list[Any] - - def __init__( - self, - data: Optional[Union[Mapping[_Key, _Value], Iterable[Tuple[_Key, _Value]]]] = None, - **kwargs: Any, - ) -> None: - self.__keys = [] - dict.__init__(self) - self.update(data) - self.update(kwargs) - - def __new__(cls: Type[SON[_Key, _Value]], *args: Any, **kwargs: Any) -> SON[_Key, _Value]: - instance = super().__new__(cls, *args, **kwargs) - instance.__keys = [] - return instance - - def __repr__(self) -> str: - result = [] - for key in self.__keys: - result.append(f"({key!r}, {self[key]!r})") - return "SON([%s])" % ", ".join(result) - - def __setitem__(self, key: _Key, value: _Value) -> None: - if key not in self.__keys: - self.__keys.append(key) - dict.__setitem__(self, key, value) - - def __delitem__(self, key: _Key) -> None: - self.__keys.remove(key) - dict.__delitem__(self, key) - - def copy(self) -> SON[_Key, _Value]: - other: SON[_Key, _Value] = SON() - other.update(self) - return other - - # TODO this is all from UserDict.DictMixin. it could probably be made more - # efficient. - # second level definitions support higher levels - def __iter__(self) -> Iterator[_Key]: - yield from self.__keys - - def has_key(self, key: _Key) -> bool: - return key in self.__keys - - def iterkeys(self) -> Iterator[_Key]: - return self.__iter__() - - # fourth level uses definitions from lower levels - def itervalues(self) -> Iterator[_Value]: - for _, v in self.items(): - yield v - - def values(self) -> list[_Value]: # type: ignore[override] - return [v for _, v in self.items()] - - def clear(self) -> None: - self.__keys = [] - super().clear() - - def setdefault(self, key: _Key, default: _Value) -> _Value: - try: - return self[key] - except KeyError: - self[key] = default - return default - - def pop(self, key: _Key, *args: Union[_Value, _T]) -> Union[_Value, _T]: - if len(args) > 1: - raise TypeError("pop expected at most 2 arguments, got " + repr(1 + len(args))) - try: - value = self[key] - except KeyError: - if args: - return args[0] - raise - del self[key] - return value - - def popitem(self) -> Tuple[_Key, _Value]: - try: - k, v = next(iter(self.items())) - except StopIteration: - raise KeyError("container is empty") from None - del self[k] - return (k, v) - - def update(self, other: Optional[Any] = None, **kwargs: _Value) -> None: # type: ignore[override] - # Make progressively weaker assumptions about "other" - if other is None: - pass - elif hasattr(other, "items"): - for k, v in other.items(): - self[k] = v - elif hasattr(other, "keys"): - for k in other: - self[k] = other[k] - else: - for k, v in other: - self[k] = v - if kwargs: - self.update(kwargs) - - def get( # type: ignore[override] - self, key: _Key, default: Optional[Union[_Value, _T]] = None - ) -> Union[_Value, _T, None]: - try: - return self[key] - except KeyError: - return default - - def __eq__(self, other: Any) -> bool: - """Comparison to another SON is order-sensitive while comparison to a - regular dictionary is order-insensitive. - """ - if isinstance(other, SON): - return len(self) == len(other) and list(self.items()) == list(other.items()) - return cast(bool, self.to_dict() == other) - - def __ne__(self, other: Any) -> bool: - return not self == other - - def __len__(self) -> int: - return len(self.__keys) - - def to_dict(self) -> dict[_Key, _Value]: - """Convert a SON document to a normal Python dictionary instance. - - This is trickier than just *dict(...)* because it needs to be - recursive. - """ - - def transform_value(value: Any) -> Any: - if isinstance(value, list): - return [transform_value(v) for v in value] - elif isinstance(value, _Mapping): - return {k: transform_value(v) for k, v in value.items()} - else: - return value - - return cast("dict[_Key, _Value]", transform_value(dict(self))) - - def __deepcopy__(self, memo: dict[int, SON[_Key, _Value]]) -> SON[_Key, _Value]: - out: SON[_Key, _Value] = SON() - val_id = id(self) - if val_id in memo: - return memo[val_id] - memo[val_id] = out - for k, v in self.items(): - if not isinstance(v, RE_TYPE): - v = copy.deepcopy(v, memo) # noqa: PLW2901 - out[k] = v - return out diff --git a/venv/Lib/site-packages/bson/time64.c b/venv/Lib/site-packages/bson/time64.c deleted file mode 100644 index a21fbb9..0000000 --- a/venv/Lib/site-packages/bson/time64.c +++ /dev/null @@ -1,781 +0,0 @@ -/* - -Copyright (c) 2007-2010 Michael G Schwern - -This software originally derived from Paul Sheer's pivotal_gmtime_r.c. - -The MIT License: - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -*/ - -/* - -Programmers who have available to them 64-bit time values as a 'long -long' type can use cbson_localtime64_r() and cbson_gmtime64_r() which correctly -converts the time even on 32-bit systems. Whether you have 64-bit time -values will depend on the operating system. - -cbson_localtime64_r() is a 64-bit equivalent of localtime_r(). - -cbson_gmtime64_r() is a 64-bit equivalent of gmtime_r(). - -*/ - -#ifdef _MSC_VER - #define _CRT_SECURE_NO_WARNINGS -#endif - -/* Including Python.h fixes issues with interpreters built with -std=c99. */ -#define PY_SSIZE_T_CLEAN -#include "Python.h" - -#include -#include "time64.h" -#include "time64_limits.h" - - -/* Spec says except for stftime() and the _r() functions, these - all return static memory. Stabbings! */ -static struct TM Static_Return_Date; - -static const int days_in_month[2][12] = { - {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, - {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, -}; - -static const int julian_days_by_month[2][12] = { - {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, - {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}, -}; - -static const int length_of_year[2] = { 365, 366 }; - -/* Some numbers relating to the gregorian cycle */ -static const Year years_in_gregorian_cycle = 400; -#define days_in_gregorian_cycle ((365 * 400) + 100 - 4 + 1) -static const Time64_T seconds_in_gregorian_cycle = days_in_gregorian_cycle * 60LL * 60LL * 24LL; - -/* Year range we can trust the time functions with */ -#define MAX_SAFE_YEAR 2037 -#define MIN_SAFE_YEAR 1971 - -/* 28 year Julian calendar cycle */ -#define SOLAR_CYCLE_LENGTH 28 - -/* Year cycle from MAX_SAFE_YEAR down. */ -static const int safe_years_high[SOLAR_CYCLE_LENGTH] = { - 2016, 2017, 2018, 2019, - 2020, 2021, 2022, 2023, - 2024, 2025, 2026, 2027, - 2028, 2029, 2030, 2031, - 2032, 2033, 2034, 2035, - 2036, 2037, 2010, 2011, - 2012, 2013, 2014, 2015 -}; - -/* Year cycle from MIN_SAFE_YEAR up */ -static const int safe_years_low[SOLAR_CYCLE_LENGTH] = { - 1996, 1997, 1998, 1971, - 1972, 1973, 1974, 1975, - 1976, 1977, 1978, 1979, - 1980, 1981, 1982, 1983, - 1984, 1985, 1986, 1987, - 1988, 1989, 1990, 1991, - 1992, 1993, 1994, 1995, -}; - -/* Let's assume people are going to be looking for dates in the future. - Let's provide some cheats so you can skip ahead. - This has a 4x speed boost when near 2008. -*/ -/* Number of days since epoch on Jan 1st, 2008 GMT */ -#define CHEAT_DAYS (1199145600 / 24 / 60 / 60) -#define CHEAT_YEARS 108 - -#define IS_LEAP(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0) -#define _TIME64_WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a)) - -#ifdef USE_SYSTEM_LOCALTIME -# define SHOULD_USE_SYSTEM_LOCALTIME(a) ( \ - (a) <= SYSTEM_LOCALTIME_MAX && \ - (a) >= SYSTEM_LOCALTIME_MIN \ -) -#else -# define SHOULD_USE_SYSTEM_LOCALTIME(a) (0) -#endif - -#ifdef USE_SYSTEM_GMTIME -# define SHOULD_USE_SYSTEM_GMTIME(a) ( \ - (a) <= SYSTEM_GMTIME_MAX && \ - (a) >= SYSTEM_GMTIME_MIN \ -) -#else -# define SHOULD_USE_SYSTEM_GMTIME(a) (0) -#endif - -/* Multi varadic macros are a C99 thing, alas */ -#ifdef TIME_64_DEBUG -# define TIME64_TRACE(format) (fprintf(stderr, format)) -# define TIME64_TRACE1(format, var1) (fprintf(stderr, format, var1)) -# define TIME64_TRACE2(format, var1, var2) (fprintf(stderr, format, var1, var2)) -# define TIME64_TRACE3(format, var1, var2, var3) (fprintf(stderr, format, var1, var2, var3)) -#else -# define TIME64_TRACE(format) ((void)0) -# define TIME64_TRACE1(format, var1) ((void)0) -# define TIME64_TRACE2(format, var1, var2) ((void)0) -# define TIME64_TRACE3(format, var1, var2, var3) ((void)0) -#endif - - -static int is_exception_century(Year year) -{ - int is_exception = ((year % 100 == 0) && !(year % 400 == 0)); - TIME64_TRACE1("# is_exception_century: %s\n", is_exception ? "yes" : "no"); - - return(is_exception); -} - - -/* Compare two dates. - The result is like cmp. - Ignores things like gmtoffset and dst -*/ -int cbson_cmp_date( const struct TM* left, const struct tm* right ) { - if( left->tm_year > right->tm_year ) - return 1; - else if( left->tm_year < right->tm_year ) - return -1; - - if( left->tm_mon > right->tm_mon ) - return 1; - else if( left->tm_mon < right->tm_mon ) - return -1; - - if( left->tm_mday > right->tm_mday ) - return 1; - else if( left->tm_mday < right->tm_mday ) - return -1; - - if( left->tm_hour > right->tm_hour ) - return 1; - else if( left->tm_hour < right->tm_hour ) - return -1; - - if( left->tm_min > right->tm_min ) - return 1; - else if( left->tm_min < right->tm_min ) - return -1; - - if( left->tm_sec > right->tm_sec ) - return 1; - else if( left->tm_sec < right->tm_sec ) - return -1; - - return 0; -} - - -/* Check if a date is safely inside a range. - The intention is to check if its a few days inside. -*/ -int cbson_date_in_safe_range( const struct TM* date, const struct tm* min, const struct tm* max ) { - if( cbson_cmp_date(date, min) == -1 ) - return 0; - - if( cbson_cmp_date(date, max) == 1 ) - return 0; - - return 1; -} - - -/* timegm() is not in the C or POSIX spec, but it is such a useful - extension I would be remiss in leaving it out. Also I need it - for cbson_localtime64() -*/ -Time64_T cbson_timegm64(const struct TM *date) { - Time64_T days = 0; - Time64_T seconds = 0; - Year year; - Year orig_year = (Year)date->tm_year; - int cycles = 0; - - if( orig_year > 100 ) { - cycles = (int)((orig_year - 100) / 400); - orig_year -= cycles * 400; - days += (Time64_T)cycles * days_in_gregorian_cycle; - } - else if( orig_year < -300 ) { - cycles = (int)((orig_year - 100) / 400); - orig_year -= cycles * 400; - days += (Time64_T)cycles * days_in_gregorian_cycle; - } - TIME64_TRACE3("# timegm/ cycles: %d, days: %lld, orig_year: %lld\n", cycles, days, orig_year); - - if( orig_year > 70 ) { - year = 70; - while( year < orig_year ) { - days += length_of_year[IS_LEAP(year)]; - year++; - } - } - else if ( orig_year < 70 ) { - year = 69; - do { - days -= length_of_year[IS_LEAP(year)]; - year--; - } while( year >= orig_year ); - } - - days += julian_days_by_month[IS_LEAP(orig_year)][date->tm_mon]; - days += date->tm_mday - 1; - - seconds = days * 60 * 60 * 24; - - seconds += date->tm_hour * 60 * 60; - seconds += date->tm_min * 60; - seconds += date->tm_sec; - - return(seconds); -} - - -#ifndef NDEBUG -static int check_tm(struct TM *tm) -{ - /* Don't forget leap seconds */ - assert(tm->tm_sec >= 0); - assert(tm->tm_sec <= 61); - - assert(tm->tm_min >= 0); - assert(tm->tm_min <= 59); - - assert(tm->tm_hour >= 0); - assert(tm->tm_hour <= 23); - - assert(tm->tm_mday >= 1); - assert(tm->tm_mday <= days_in_month[IS_LEAP(tm->tm_year)][tm->tm_mon]); - - assert(tm->tm_mon >= 0); - assert(tm->tm_mon <= 11); - - assert(tm->tm_wday >= 0); - assert(tm->tm_wday <= 6); - - assert(tm->tm_yday >= 0); - assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]); - -#ifdef HAS_TM_TM_GMTOFF - assert(tm->tm_gmtoff >= -24 * 60 * 60); - assert(tm->tm_gmtoff <= 24 * 60 * 60); -#endif - - return 1; -} -#endif - - -/* The exceptional centuries without leap years cause the cycle to - shift by 16 -*/ -static Year cycle_offset(Year year) -{ - const Year start_year = 2000; - Year year_diff = year - start_year; - Year exceptions; - - if( year > start_year ) - year_diff--; - - exceptions = year_diff / 100; - exceptions -= year_diff / 400; - - TIME64_TRACE3("# year: %lld, exceptions: %lld, year_diff: %lld\n", - year, exceptions, year_diff); - - return exceptions * 16; -} - -/* For a given year after 2038, pick the latest possible matching - year in the 28 year calendar cycle. - - A matching year... - 1) Starts on the same day of the week. - 2) Has the same leap year status. - - This is so the calendars match up. - - Also the previous year must match. When doing Jan 1st you might - wind up on Dec 31st the previous year when doing a -UTC time zone. - - Finally, the next year must have the same start day of week. This - is for Dec 31st with a +UTC time zone. - It doesn't need the same leap year status since we only care about - January 1st. -*/ -static int safe_year(const Year year) -{ - int safe_year = 0; - Year year_cycle; - - if( year >= MIN_SAFE_YEAR && year <= MAX_SAFE_YEAR ) { - return (int)year; - } - - year_cycle = year + cycle_offset(year); - - /* safe_years_low is off from safe_years_high by 8 years */ - if( year < MIN_SAFE_YEAR ) - year_cycle -= 8; - - /* Change non-leap xx00 years to an equivalent */ - if( is_exception_century(year) ) - year_cycle += 11; - - /* Also xx01 years, since the previous year will be wrong */ - if( is_exception_century(year - 1) ) - year_cycle += 17; - - year_cycle %= SOLAR_CYCLE_LENGTH; - if( year_cycle < 0 ) - year_cycle = SOLAR_CYCLE_LENGTH + year_cycle; - - assert( year_cycle >= 0 ); - assert( year_cycle < SOLAR_CYCLE_LENGTH ); - if( year < MIN_SAFE_YEAR ) - safe_year = safe_years_low[year_cycle]; - else if( year > MAX_SAFE_YEAR ) - safe_year = safe_years_high[year_cycle]; - else - assert(0); - - TIME64_TRACE3("# year: %lld, year_cycle: %lld, safe_year: %d\n", - year, year_cycle, safe_year); - - assert(safe_year <= MAX_SAFE_YEAR && safe_year >= MIN_SAFE_YEAR); - - return safe_year; -} - - -void pymongo_copy_tm_to_TM64(const struct tm *src, struct TM *dest) { - if( src == NULL ) { - memset(dest, 0, sizeof(*dest)); - } - else { -# ifdef USE_TM64 - dest->tm_sec = src->tm_sec; - dest->tm_min = src->tm_min; - dest->tm_hour = src->tm_hour; - dest->tm_mday = src->tm_mday; - dest->tm_mon = src->tm_mon; - dest->tm_year = (Year)src->tm_year; - dest->tm_wday = src->tm_wday; - dest->tm_yday = src->tm_yday; - dest->tm_isdst = src->tm_isdst; - -# ifdef HAS_TM_TM_GMTOFF - dest->tm_gmtoff = src->tm_gmtoff; -# endif - -# ifdef HAS_TM_TM_ZONE - dest->tm_zone = src->tm_zone; -# endif - -# else - /* They're the same type */ - memcpy(dest, src, sizeof(*dest)); -# endif - } -} - - -void cbson_copy_TM64_to_tm(const struct TM *src, struct tm *dest) { - if( src == NULL ) { - memset(dest, 0, sizeof(*dest)); - } - else { -# ifdef USE_TM64 - dest->tm_sec = src->tm_sec; - dest->tm_min = src->tm_min; - dest->tm_hour = src->tm_hour; - dest->tm_mday = src->tm_mday; - dest->tm_mon = src->tm_mon; - dest->tm_year = (int)src->tm_year; - dest->tm_wday = src->tm_wday; - dest->tm_yday = src->tm_yday; - dest->tm_isdst = src->tm_isdst; - -# ifdef HAS_TM_TM_GMTOFF - dest->tm_gmtoff = src->tm_gmtoff; -# endif - -# ifdef HAS_TM_TM_ZONE - dest->tm_zone = src->tm_zone; -# endif - -# else - /* They're the same type */ - memcpy(dest, src, sizeof(*dest)); -# endif - } -} - - -/* Simulate localtime_r() to the best of our ability */ -struct tm * cbson_fake_localtime_r(const time_t *time, struct tm *result) { - const struct tm *static_result = localtime(time); - - assert(result != NULL); - - if( static_result == NULL ) { - memset(result, 0, sizeof(*result)); - return NULL; - } - else { - memcpy(result, static_result, sizeof(*result)); - return result; - } -} - - -/* Simulate gmtime_r() to the best of our ability */ -struct tm * cbson_fake_gmtime_r(const time_t *time, struct tm *result) { - const struct tm *static_result = gmtime(time); - - assert(result != NULL); - - if( static_result == NULL ) { - memset(result, 0, sizeof(*result)); - return NULL; - } - else { - memcpy(result, static_result, sizeof(*result)); - return result; - } -} - - -static Time64_T seconds_between_years(Year left_year, Year right_year) { - int increment = (left_year > right_year) ? 1 : -1; - Time64_T seconds = 0; - int cycles; - - if( left_year > 2400 ) { - cycles = (int)((left_year - 2400) / 400); - left_year -= cycles * 400; - seconds += cycles * seconds_in_gregorian_cycle; - } - else if( left_year < 1600 ) { - cycles = (int)((left_year - 1600) / 400); - left_year += cycles * 400; - seconds += cycles * seconds_in_gregorian_cycle; - } - - while( left_year != right_year ) { - seconds += length_of_year[IS_LEAP(right_year - 1900)] * 60 * 60 * 24; - right_year += increment; - } - - return seconds * increment; -} - - -Time64_T cbson_mktime64(const struct TM *input_date) { - struct tm safe_date; - struct TM date; - Time64_T time; - Year year = input_date->tm_year + 1900; - - if( cbson_date_in_safe_range(input_date, &SYSTEM_MKTIME_MIN, &SYSTEM_MKTIME_MAX) ) - { - cbson_copy_TM64_to_tm(input_date, &safe_date); - return (Time64_T)mktime(&safe_date); - } - - /* Have to make the year safe in date else it won't fit in safe_date */ - date = *input_date; - date.tm_year = safe_year(year) - 1900; - cbson_copy_TM64_to_tm(&date, &safe_date); - - time = (Time64_T)mktime(&safe_date); - - time += seconds_between_years(year, (Year)(safe_date.tm_year + 1900)); - - return time; -} - - -/* Because I think mktime() is a crappy name */ -Time64_T timelocal64(const struct TM *date) { - return cbson_mktime64(date); -} - - -struct TM *cbson_gmtime64_r (const Time64_T *in_time, struct TM *p) -{ - int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday; - Time64_T v_tm_tday; - int leap; - Time64_T m; - Time64_T time = *in_time; - Year year = 70; - int cycles = 0; - - assert(p != NULL); - -#ifdef USE_SYSTEM_GMTIME - /* Use the system gmtime() if time_t is small enough */ - if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) { - time_t safe_time = (time_t)*in_time; - struct tm safe_date; - GMTIME_R(&safe_time, &safe_date); - - pymongo_copy_tm_to_TM64(&safe_date, p); - assert(check_tm(p)); - - return p; - } -#endif - -#ifdef HAS_TM_TM_GMTOFF - p->tm_gmtoff = 0; -#endif - p->tm_isdst = 0; - -#ifdef HAS_TM_TM_ZONE - p->tm_zone = "UTC"; -#endif - - v_tm_sec = (int)(time % 60); - time /= 60; - v_tm_min = (int)(time % 60); - time /= 60; - v_tm_hour = (int)(time % 24); - time /= 24; - v_tm_tday = time; - - _TIME64_WRAP (v_tm_sec, v_tm_min, 60); - _TIME64_WRAP (v_tm_min, v_tm_hour, 60); - _TIME64_WRAP (v_tm_hour, v_tm_tday, 24); - - v_tm_wday = (int)((v_tm_tday + 4) % 7); - if (v_tm_wday < 0) - v_tm_wday += 7; - m = v_tm_tday; - - if (m >= CHEAT_DAYS) { - year = CHEAT_YEARS; - m -= CHEAT_DAYS; - } - - if (m >= 0) { - /* Gregorian cycles, this is huge optimization for distant times */ - cycles = (int)(m / (Time64_T) days_in_gregorian_cycle); - if( cycles ) { - m -= (cycles * (Time64_T) days_in_gregorian_cycle); - year += (cycles * years_in_gregorian_cycle); - } - - /* Years */ - leap = IS_LEAP (year); - while (m >= (Time64_T) length_of_year[leap]) { - m -= (Time64_T) length_of_year[leap]; - year++; - leap = IS_LEAP (year); - } - - /* Months */ - v_tm_mon = 0; - while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) { - m -= (Time64_T) days_in_month[leap][v_tm_mon]; - v_tm_mon++; - } - } else { - year--; - - /* Gregorian cycles */ - cycles = (int)((m / (Time64_T) days_in_gregorian_cycle) + 1); - if( cycles ) { - m -= (cycles * (Time64_T) days_in_gregorian_cycle); - year += (cycles * years_in_gregorian_cycle); - } - - /* Years */ - leap = IS_LEAP (year); - while (m < (Time64_T) -length_of_year[leap]) { - m += (Time64_T) length_of_year[leap]; - year--; - leap = IS_LEAP (year); - } - - /* Months */ - v_tm_mon = 11; - while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) { - m += (Time64_T) days_in_month[leap][v_tm_mon]; - v_tm_mon--; - } - m += (Time64_T) days_in_month[leap][v_tm_mon]; - } - - p->tm_year = (int)year; - if( p->tm_year != year ) { -#ifdef EOVERFLOW - errno = EOVERFLOW; -#endif - return NULL; - } - - /* At this point m is less than a year so casting to an int is safe */ - p->tm_mday = (int) m + 1; - p->tm_yday = julian_days_by_month[leap][v_tm_mon] + (int)m; - p->tm_sec = v_tm_sec; - p->tm_min = v_tm_min; - p->tm_hour = v_tm_hour; - p->tm_mon = v_tm_mon; - p->tm_wday = v_tm_wday; - - assert(check_tm(p)); - - return p; -} - - -struct TM *cbson_localtime64_r (const Time64_T *time, struct TM *local_tm) -{ - time_t safe_time; - struct tm safe_date; - struct TM gm_tm; - Year orig_year; - int month_diff; - - assert(local_tm != NULL); - -#ifdef USE_SYSTEM_LOCALTIME - /* Use the system localtime() if time_t is small enough */ - if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) { - safe_time = (time_t)*time; - - TIME64_TRACE1("Using system localtime for %lld\n", *time); - - LOCALTIME_R(&safe_time, &safe_date); - - pymongo_copy_tm_to_TM64(&safe_date, local_tm); - assert(check_tm(local_tm)); - - return local_tm; - } -#endif - - if( cbson_gmtime64_r(time, &gm_tm) == NULL ) { - TIME64_TRACE1("cbson_gmtime64_r returned null for %lld\n", *time); - return NULL; - } - - orig_year = gm_tm.tm_year; - - if (gm_tm.tm_year > (2037 - 1900) || - gm_tm.tm_year < (1970 - 1900) - ) - { - TIME64_TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year); - gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year + 1900)) - 1900; - } - - safe_time = (time_t)cbson_timegm64(&gm_tm); - if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) { - TIME64_TRACE1("localtime_r(%d) returned NULL\n", (int)safe_time); - return NULL; - } - - pymongo_copy_tm_to_TM64(&safe_date, local_tm); - - local_tm->tm_year = (int)orig_year; - if( local_tm->tm_year != orig_year ) { - TIME64_TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n", - (Year)local_tm->tm_year, (Year)orig_year); - -#ifdef EOVERFLOW - errno = EOVERFLOW; -#endif - return NULL; - } - - - month_diff = local_tm->tm_mon - gm_tm.tm_mon; - - /* When localtime is Dec 31st previous year and - gmtime is Jan 1st next year. - */ - if( month_diff == 11 ) { - local_tm->tm_year--; - } - - /* When localtime is Jan 1st, next year and - gmtime is Dec 31st, previous year. - */ - if( month_diff == -11 ) { - local_tm->tm_year++; - } - - /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st - in a non-leap xx00. There is one point in the cycle - we can't account for which the safe xx00 year is a leap - year. So we need to correct for Dec 31st coming out as - the 366th day of the year. - */ - if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 ) - local_tm->tm_yday--; - - assert(check_tm(local_tm)); - - return local_tm; -} - - -int cbson_valid_tm_wday( const struct TM* date ) { - if( 0 <= date->tm_wday && date->tm_wday <= 6 ) - return 1; - else - return 0; -} - -int cbson_valid_tm_mon( const struct TM* date ) { - if( 0 <= date->tm_mon && date->tm_mon <= 11 ) - return 1; - else - return 0; -} - - -/* Non-thread safe versions of the above */ -struct TM *cbson_localtime64(const Time64_T *time) { -#ifdef _MSC_VER - _tzset(); -#else - tzset(); -#endif - return cbson_localtime64_r(time, &Static_Return_Date); -} - -struct TM *cbson_gmtime64(const Time64_T *time) { - return cbson_gmtime64_r(time, &Static_Return_Date); -} diff --git a/venv/Lib/site-packages/bson/time64.h b/venv/Lib/site-packages/bson/time64.h deleted file mode 100644 index 6321eb3..0000000 --- a/venv/Lib/site-packages/bson/time64.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef TIME64_H -# define TIME64_H - -#include -#include "time64_config.h" - -/* Set our custom types */ -typedef INT_64_T Int64; -typedef Int64 Time64_T; -typedef Int64 Year; - - -/* A copy of the tm struct but with a 64 bit year */ -struct TM64 { - int tm_sec; - int tm_min; - int tm_hour; - int tm_mday; - int tm_mon; - Year tm_year; - int tm_wday; - int tm_yday; - int tm_isdst; - -#ifdef HAS_TM_TM_GMTOFF - long tm_gmtoff; -#endif - -#ifdef HAS_TM_TM_ZONE - char *tm_zone; -#endif -}; - - -/* Decide which tm struct to use */ -#ifdef USE_TM64 -#define TM TM64 -#else -#define TM tm -#endif - - -/* Declare public functions */ -struct TM *cbson_gmtime64_r (const Time64_T *, struct TM *); -struct TM *cbson_localtime64_r (const Time64_T *, struct TM *); -struct TM *cbson_gmtime64 (const Time64_T *); -struct TM *cbson_localtime64 (const Time64_T *); - -Time64_T cbson_timegm64 (const struct TM *); -Time64_T cbson_mktime64 (const struct TM *); -Time64_T timelocal64 (const struct TM *); - - -/* Not everyone has gm/localtime_r(), provide a replacement */ -#ifdef HAS_LOCALTIME_R -# define LOCALTIME_R(clock, result) localtime_r(clock, result) -#else -# define LOCALTIME_R(clock, result) cbson_fake_localtime_r(clock, result) -#endif -#ifdef HAS_GMTIME_R -# define GMTIME_R(clock, result) gmtime_r(clock, result) -#else -# define GMTIME_R(clock, result) cbson_fake_gmtime_r(clock, result) -#endif - - -#endif diff --git a/venv/Lib/site-packages/bson/time64_config.h b/venv/Lib/site-packages/bson/time64_config.h deleted file mode 100644 index 9d4c111..0000000 --- a/venv/Lib/site-packages/bson/time64_config.h +++ /dev/null @@ -1,78 +0,0 @@ -/* Configuration - ------------- - Define as appropriate for your system. - Sensible defaults provided. -*/ - - -#ifndef TIME64_CONFIG_H -# define TIME64_CONFIG_H - -/* Debugging - TIME_64_DEBUG - Define if you want debugging messages -*/ -/* #define TIME_64_DEBUG */ - - -/* INT_64_T - A 64 bit integer type to use to store time and others. - Must be defined. -*/ -#define INT_64_T long long - - -/* USE_TM64 - Should we use a 64 bit safe replacement for tm? This will - let you go past year 2 billion but the struct will be incompatible - with tm. Conversion functions will be provided. -*/ -/* #define USE_TM64 */ - - -/* Availability of system functions. - - HAS_GMTIME_R - Define if your system has gmtime_r() - - HAS_LOCALTIME_R - Define if your system has localtime_r() - - HAS_TIMEGM - Define if your system has timegm(), a GNU extension. -*/ -#if !defined(WIN32) && !defined(_MSC_VER) -#define HAS_GMTIME_R -#define HAS_LOCALTIME_R -#endif -/* #define HAS_TIMEGM */ - - -/* Details of non-standard tm struct elements. - - HAS_TM_TM_GMTOFF - True if your tm struct has a "tm_gmtoff" element. - A BSD extension. - - HAS_TM_TM_ZONE - True if your tm struct has a "tm_zone" element. - A BSD extension. -*/ -/* #define HAS_TM_TM_GMTOFF */ -/* #define HAS_TM_TM_ZONE */ - - -/* USE_SYSTEM_LOCALTIME - USE_SYSTEM_GMTIME - USE_SYSTEM_MKTIME - USE_SYSTEM_TIMEGM - Should we use the system functions if the time is inside their range? - Your system localtime() is probably more accurate, but our gmtime() is - fast and safe. -*/ -#define USE_SYSTEM_LOCALTIME -/* #define USE_SYSTEM_GMTIME */ -#define USE_SYSTEM_MKTIME -/* #define USE_SYSTEM_TIMEGM */ - -#endif /* TIME64_CONFIG_H */ diff --git a/venv/Lib/site-packages/bson/time64_limits.h b/venv/Lib/site-packages/bson/time64_limits.h deleted file mode 100644 index 1d30607..0000000 --- a/venv/Lib/site-packages/bson/time64_limits.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - Maximum and minimum inputs your system's respective time functions - can correctly handle. time64.h will use your system functions if - the input falls inside these ranges and corresponding USE_SYSTEM_* - constant is defined. -*/ - -#ifndef TIME64_LIMITS_H -#define TIME64_LIMITS_H - -/* Max/min for localtime() */ -#define SYSTEM_LOCALTIME_MAX 2147483647 -#define SYSTEM_LOCALTIME_MIN -2147483647-1 - -/* Max/min for gmtime() */ -#define SYSTEM_GMTIME_MAX 2147483647 -#define SYSTEM_GMTIME_MIN -2147483647-1 - -/* Max/min for mktime() */ -static const struct tm SYSTEM_MKTIME_MAX = { - 7, - 14, - 19, - 18, - 0, - 138, - 1, - 17, - 0 -#ifdef HAS_TM_TM_GMTOFF - ,-28800 -#endif -#ifdef HAS_TM_TM_ZONE - ,"PST" -#endif -}; - -static const struct tm SYSTEM_MKTIME_MIN = { - 52, - 45, - 12, - 13, - 11, - 1, - 5, - 346, - 0 -#ifdef HAS_TM_TM_GMTOFF - ,-28800 -#endif -#ifdef HAS_TM_TM_ZONE - ,"PST" -#endif -}; - -/* Max/min for timegm() */ -#ifdef HAS_TIMEGM -static const struct tm SYSTEM_TIMEGM_MAX = { - 7, - 14, - 3, - 19, - 0, - 138, - 2, - 18, - 0 - #ifdef HAS_TM_TM_GMTOFF - ,0 - #endif - #ifdef HAS_TM_TM_ZONE - ,"UTC" - #endif -}; - -static const struct tm SYSTEM_TIMEGM_MIN = { - 52, - 45, - 20, - 13, - 11, - 1, - 5, - 346, - 0 - #ifdef HAS_TM_TM_GMTOFF - ,0 - #endif - #ifdef HAS_TM_TM_ZONE - ,"UTC" - #endif -}; -#endif /* HAS_TIMEGM */ - -#endif /* TIME64_LIMITS_H */ diff --git a/venv/Lib/site-packages/bson/timestamp.py b/venv/Lib/site-packages/bson/timestamp.py deleted file mode 100644 index 949bd7b..0000000 --- a/venv/Lib/site-packages/bson/timestamp.py +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright 2010-2015 MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tools for representing MongoDB internal Timestamps.""" -from __future__ import annotations - -import calendar -import datetime -from typing import Any, Union - -from bson._helpers import _getstate_slots, _setstate_slots -from bson.tz_util import utc - -UPPERBOUND = 4294967296 - - -class Timestamp: - """MongoDB internal timestamps used in the opLog.""" - - __slots__ = ("__time", "__inc") - - __getstate__ = _getstate_slots - __setstate__ = _setstate_slots - - _type_marker = 17 - - def __init__(self, time: Union[datetime.datetime, int], inc: int) -> None: - """Create a new :class:`Timestamp`. - - This class is only for use with the MongoDB opLog. If you need - to store a regular timestamp, please use a - :class:`~datetime.datetime`. - - Raises :class:`TypeError` if `time` is not an instance of - :class: `int` or :class:`~datetime.datetime`, or `inc` is not - an instance of :class:`int`. Raises :class:`ValueError` if - `time` or `inc` is not in [0, 2**32). - - :param time: time in seconds since epoch UTC, or a naive UTC - :class:`~datetime.datetime`, or an aware - :class:`~datetime.datetime` - :param inc: the incrementing counter - """ - if isinstance(time, datetime.datetime): - offset = time.utcoffset() - if offset is not None: - time = time - offset - time = int(calendar.timegm(time.timetuple())) - if not isinstance(time, int): - raise TypeError(f"time must be an instance of int, not {type(time)}") - if not isinstance(inc, int): - raise TypeError(f"inc must be an instance of int, not {type(inc)}") - if not 0 <= time < UPPERBOUND: - raise ValueError("time must be contained in [0, 2**32)") - if not 0 <= inc < UPPERBOUND: - raise ValueError("inc must be contained in [0, 2**32)") - - self.__time = time - self.__inc = inc - - @property - def time(self) -> int: - """Get the time portion of this :class:`Timestamp`.""" - return self.__time - - @property - def inc(self) -> int: - """Get the inc portion of this :class:`Timestamp`.""" - return self.__inc - - def __eq__(self, other: Any) -> bool: - if isinstance(other, Timestamp): - return self.__time == other.time and self.__inc == other.inc - else: - return NotImplemented - - def __hash__(self) -> int: - return hash(self.time) ^ hash(self.inc) - - def __ne__(self, other: Any) -> bool: - return not self == other - - def __lt__(self, other: Any) -> bool: - if isinstance(other, Timestamp): - return (self.time, self.inc) < (other.time, other.inc) - return NotImplemented - - def __le__(self, other: Any) -> bool: - if isinstance(other, Timestamp): - return (self.time, self.inc) <= (other.time, other.inc) - return NotImplemented - - def __gt__(self, other: Any) -> bool: - if isinstance(other, Timestamp): - return (self.time, self.inc) > (other.time, other.inc) - return NotImplemented - - def __ge__(self, other: Any) -> bool: - if isinstance(other, Timestamp): - return (self.time, self.inc) >= (other.time, other.inc) - return NotImplemented - - def __repr__(self) -> str: - return f"Timestamp({self.__time}, {self.__inc})" - - def as_datetime(self) -> datetime.datetime: - """Return a :class:`~datetime.datetime` instance corresponding - to the time portion of this :class:`Timestamp`. - - The returned datetime's timezone is UTC. - """ - return datetime.datetime.fromtimestamp(self.__time, utc) diff --git a/venv/Lib/site-packages/bson/typings.py b/venv/Lib/site-packages/bson/typings.py deleted file mode 100644 index b80c661..0000000 --- a/venv/Lib/site-packages/bson/typings.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2023-Present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Type aliases used by bson""" -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Mapping, MutableMapping, TypeVar, Union - -if TYPE_CHECKING: - from array import array - from mmap import mmap - - from bson.raw_bson import RawBSONDocument - - -# Common Shared Types. -_DocumentOut = Union[MutableMapping[str, Any], "RawBSONDocument"] -_DocumentType = TypeVar("_DocumentType", bound=Mapping[str, Any]) -_DocumentTypeArg = TypeVar("_DocumentTypeArg", bound=Mapping[str, Any]) -_ReadableBuffer = Union[bytes, memoryview, "mmap", "array"] diff --git a/venv/Lib/site-packages/bson/tz_util.py b/venv/Lib/site-packages/bson/tz_util.py deleted file mode 100644 index 4d31c04..0000000 --- a/venv/Lib/site-packages/bson/tz_util.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright 2010-2015 MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Timezone related utilities for BSON.""" -from __future__ import annotations - -from datetime import datetime, timedelta, tzinfo -from typing import Optional, Tuple, Union - -ZERO: timedelta = timedelta(0) - - -class FixedOffset(tzinfo): - """Fixed offset timezone, in minutes east from UTC. - - Implementation based from the Python `standard library documentation - `_. - Defining __getinitargs__ enables pickling / copying. - """ - - def __init__(self, offset: Union[float, timedelta], name: str) -> None: - if isinstance(offset, timedelta): - self.__offset = offset - else: - self.__offset = timedelta(minutes=offset) - self.__name = name - - def __getinitargs__(self) -> Tuple[timedelta, str]: - return self.__offset, self.__name - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.__offset!r}, {self.__name!r})" - - def utcoffset(self, dt: Optional[datetime]) -> timedelta: - return self.__offset - - def tzname(self, dt: Optional[datetime]) -> str: - return self.__name - - def dst(self, dt: Optional[datetime]) -> timedelta: - return ZERO - - -utc: FixedOffset = FixedOffset(0, "UTC") -"""Fixed offset timezone representing UTC.""" diff --git a/venv/Lib/site-packages/certifi-2025.4.26.dist-info/INSTALLER b/venv/Lib/site-packages/certifi-2025.4.26.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/Lib/site-packages/certifi-2025.4.26.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/Lib/site-packages/certifi-2025.4.26.dist-info/METADATA b/venv/Lib/site-packages/certifi-2025.4.26.dist-info/METADATA deleted file mode 100644 index bba2b69..0000000 --- a/venv/Lib/site-packages/certifi-2025.4.26.dist-info/METADATA +++ /dev/null @@ -1,78 +0,0 @@ -Metadata-Version: 2.4 -Name: certifi -Version: 2025.4.26 -Summary: Python package for providing Mozilla's CA Bundle. -Home-page: https://github.com/certifi/python-certifi -Author: Kenneth Reitz -Author-email: me@kennethreitz.com -License: MPL-2.0 -Project-URL: Source, https://github.com/certifi/python-certifi -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) -Classifier: Natural Language :: English -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Requires-Python: >=3.6 -License-File: LICENSE -Dynamic: author -Dynamic: author-email -Dynamic: classifier -Dynamic: description -Dynamic: home-page -Dynamic: license -Dynamic: license-file -Dynamic: project-url -Dynamic: requires-python -Dynamic: summary - -Certifi: Python SSL Certificates -================================ - -Certifi provides Mozilla's carefully curated collection of Root Certificates for -validating the trustworthiness of SSL certificates while verifying the identity -of TLS hosts. It has been extracted from the `Requests`_ project. - -Installation ------------- - -``certifi`` is available on PyPI. Simply install it with ``pip``:: - - $ pip install certifi - -Usage ------ - -To reference the installed certificate authority (CA) bundle, you can use the -built-in function:: - - >>> import certifi - - >>> certifi.where() - '/usr/local/lib/python3.7/site-packages/certifi/cacert.pem' - -Or from the command line:: - - $ python -m certifi - /usr/local/lib/python3.7/site-packages/certifi/cacert.pem - -Enjoy! - -.. _`Requests`: https://requests.readthedocs.io/en/master/ - -Addition/Removal of Certificates --------------------------------- - -Certifi does not support any addition/removal or other modification of the -CA trust store content. This project is intended to provide a reliable and -highly portable root of trust to python deployments. Look to upstream projects -for methods to use alternate trust. diff --git a/venv/Lib/site-packages/certifi-2025.4.26.dist-info/RECORD b/venv/Lib/site-packages/certifi-2025.4.26.dist-info/RECORD deleted file mode 100644 index 764cca2..0000000 --- a/venv/Lib/site-packages/certifi-2025.4.26.dist-info/RECORD +++ /dev/null @@ -1,14 +0,0 @@ -certifi-2025.4.26.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -certifi-2025.4.26.dist-info/METADATA,sha256=Q1SDFkY5LOQAJmDltZz2wU3VTv1Kh5X-rjGI4KiPHNM,2473 -certifi-2025.4.26.dist-info/RECORD,, -certifi-2025.4.26.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91 -certifi-2025.4.26.dist-info/licenses/LICENSE,sha256=6TcW2mucDVpKHfYP5pWzcPBpVgPSH2-D8FPkLPwQyvc,989 -certifi-2025.4.26.dist-info/top_level.txt,sha256=KMu4vUCfsjLrkPbSNdgdekS-pVJzBAJFO__nI8NF6-U,8 -certifi/__init__.py,sha256=9pyWUGr6sbAlksfOHo0BTV0Gxljjh4IK1kXAjHgjL4I,94 -certifi/__main__.py,sha256=xBBoj905TUWBLRGANOcf7oi6e-3dMP4cEoG9OyMs11g,243 -certifi/__pycache__/__init__.cpython-310.pyc,, -certifi/__pycache__/__main__.cpython-310.pyc,, -certifi/__pycache__/core.cpython-310.pyc,, -certifi/cacert.pem,sha256=K3sQJvGKKX4hSBicoMvn0-f578NvcjHMwoIKQE_rVZY,283771 -certifi/core.py,sha256=qRDDFyXVJwTB_EmoGppaXU_R9qCZvhl-EzxPMuV3nTA,4426 -certifi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/Lib/site-packages/certifi-2025.4.26.dist-info/WHEEL b/venv/Lib/site-packages/certifi-2025.4.26.dist-info/WHEEL deleted file mode 100644 index 8acb955..0000000 --- a/venv/Lib/site-packages/certifi-2025.4.26.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (79.0.1) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/venv/Lib/site-packages/certifi-2025.4.26.dist-info/licenses/LICENSE b/venv/Lib/site-packages/certifi-2025.4.26.dist-info/licenses/LICENSE deleted file mode 100644 index 62b076c..0000000 --- a/venv/Lib/site-packages/certifi-2025.4.26.dist-info/licenses/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -This package contains a modified version of ca-bundle.crt: - -ca-bundle.crt -- Bundle of CA Root Certificates - -This is a bundle of X.509 certificates of public Certificate Authorities -(CA). These were automatically extracted from Mozilla's root certificates -file (certdata.txt). This file can be found in the mozilla source tree: -https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt -It contains the certificates in PEM format and therefore -can be directly used with curl / libcurl / php_curl, or with -an Apache+mod_ssl webserver for SSL client authentication. -Just configure this file as the SSLCACertificateFile.# - -***** BEGIN LICENSE BLOCK ***** -This Source Code Form is subject to the terms of the Mozilla Public License, -v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain -one at http://mozilla.org/MPL/2.0/. - -***** END LICENSE BLOCK ***** -@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $ diff --git a/venv/Lib/site-packages/certifi-2025.4.26.dist-info/top_level.txt b/venv/Lib/site-packages/certifi-2025.4.26.dist-info/top_level.txt deleted file mode 100644 index 963eac5..0000000 --- a/venv/Lib/site-packages/certifi-2025.4.26.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -certifi diff --git a/venv/Lib/site-packages/certifi/__init__.py b/venv/Lib/site-packages/certifi/__init__.py deleted file mode 100644 index bf83fa9..0000000 --- a/venv/Lib/site-packages/certifi/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .core import contents, where - -__all__ = ["contents", "where"] -__version__ = "2025.04.26" diff --git a/venv/Lib/site-packages/certifi/__main__.py b/venv/Lib/site-packages/certifi/__main__.py deleted file mode 100644 index 8945b5d..0000000 --- a/venv/Lib/site-packages/certifi/__main__.py +++ /dev/null @@ -1,12 +0,0 @@ -import argparse - -from certifi import contents, where - -parser = argparse.ArgumentParser() -parser.add_argument("-c", "--contents", action="store_true") -args = parser.parse_args() - -if args.contents: - print(contents()) -else: - print(where()) diff --git a/venv/Lib/site-packages/certifi/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/certifi/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 8fe7fe7..0000000 Binary files a/venv/Lib/site-packages/certifi/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/certifi/__pycache__/__main__.cpython-310.pyc b/venv/Lib/site-packages/certifi/__pycache__/__main__.cpython-310.pyc deleted file mode 100644 index 801f3be..0000000 Binary files a/venv/Lib/site-packages/certifi/__pycache__/__main__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/certifi/__pycache__/core.cpython-310.pyc b/venv/Lib/site-packages/certifi/__pycache__/core.cpython-310.pyc deleted file mode 100644 index ae6d707..0000000 Binary files a/venv/Lib/site-packages/certifi/__pycache__/core.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/certifi/cacert.pem b/venv/Lib/site-packages/certifi/cacert.pem deleted file mode 100644 index b1d0cfd..0000000 --- a/venv/Lib/site-packages/certifi/cacert.pem +++ /dev/null @@ -1,4676 +0,0 @@ - -# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. -# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. -# Label: "Entrust Root Certification Authority" -# Serial: 1164660820 -# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4 -# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9 -# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c ------BEGIN CERTIFICATE----- -MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC -VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 -Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW -KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl -cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw -NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw -NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy -ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV -BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo -Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 -4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 -KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI -rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi -94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB -sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi -gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo -kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE -vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA -A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t -O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua -AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP -9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ -eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m -0vdXcDazv/wor3ElhVsT/h5/WrQ8 ------END CERTIFICATE----- - -# Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited -# Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited -# Label: "QuoVadis Root CA 2" -# Serial: 1289 -# MD5 Fingerprint: 5e:39:7b:dd:f8:ba:ec:82:e9:ac:62:ba:0c:54:00:2b -# SHA1 Fingerprint: ca:3a:fb:cf:12:40:36:4b:44:b2:16:20:88:80:48:39:19:93:7c:f7 -# SHA256 Fingerprint: 85:a0:dd:7d:d7:20:ad:b7:ff:05:f8:3d:54:2b:20:9d:c7:ff:45:28:f7:d6:77:b1:83:89:fe:a5:e5:c4:9e:86 ------BEGIN CERTIFICATE----- -MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x -GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv -b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV -BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W -YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa -GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg -Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J -WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB -rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp -+ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 -ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i -Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz -PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og -/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH -oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI -yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud -EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 -A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL -MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT -ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f -BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn -g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl -fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K -WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha -B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc -hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR -TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD -mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z -ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y -4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza -8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u ------END CERTIFICATE----- - -# Issuer: CN=QuoVadis Root CA 3 O=QuoVadis Limited -# Subject: CN=QuoVadis Root CA 3 O=QuoVadis Limited -# Label: "QuoVadis Root CA 3" -# Serial: 1478 -# MD5 Fingerprint: 31:85:3c:62:94:97:63:b9:aa:fd:89:4e:af:6f:e0:cf -# SHA1 Fingerprint: 1f:49:14:f7:d8:74:95:1d:dd:ae:02:c0:be:fd:3a:2d:82:75:51:85 -# SHA256 Fingerprint: 18:f1:fc:7f:20:5d:f8:ad:dd:eb:7f:e0:07:dd:57:e3:af:37:5a:9c:4d:8d:73:54:6b:f4:f1:fe:d1:e1:8d:35 ------BEGIN CERTIFICATE----- -MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x -GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv -b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV -BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W -YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM -V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB -4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr -H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd -8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv -vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT -mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe -btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc -T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt -WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ -c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A -4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD -VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG -CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 -aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 -aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu -dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw -czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G -A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC -TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg -Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 -7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem -d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd -+LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B -4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN -t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x -DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 -k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s -zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j -Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT -mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK -4SVhM7JZG+Ju1zdXtg2pEto= ------END CERTIFICATE----- - -# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Assured ID Root CA" -# Serial: 17154717934120587862167794914071425081 -# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72 -# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43 -# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c ------BEGIN CERTIFICATE----- -MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv -b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl -cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c -JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP -mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ -wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 -VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ -AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB -AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW -BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun -pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC -dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf -fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm -NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx -H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe -+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== ------END CERTIFICATE----- - -# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Global Root CA" -# Serial: 10944719598952040374951832963794454346 -# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e -# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36 -# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61 ------BEGIN CERTIFICATE----- -MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD -QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB -CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 -nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt -43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P -T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 -gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO -BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR -TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw -DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr -hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg -06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF -PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls -YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk -CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= ------END CERTIFICATE----- - -# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert High Assurance EV Root CA" -# Serial: 3553400076410547919724730734378100087 -# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a -# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25 -# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm -+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW -PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM -xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB -Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 -hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg -EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA -FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec -nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z -eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF -hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 -Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe -vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep -+OkuE6N36B9K ------END CERTIFICATE----- - -# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG -# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG -# Label: "SwissSign Gold CA - G2" -# Serial: 13492815561806991280 -# MD5 Fingerprint: 24:77:d9:a8:91:d1:3b:fa:88:2d:c2:ff:f8:cd:33:93 -# SHA1 Fingerprint: d8:c5:38:8a:b7:30:1b:1b:6e:d4:7a:e6:45:25:3a:6f:9f:1a:27:61 -# SHA256 Fingerprint: 62:dd:0b:e9:b9:f5:0a:16:3e:a0:f8:e7:5c:05:3b:1e:ca:57:ea:55:c8:68:8f:64:7c:68:81:f2:c8:35:7b:95 ------BEGIN CERTIFICATE----- -MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV -BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln -biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF -MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT -d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC -CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 -76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ -bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c -6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE -emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd -MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt -MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y -MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y -FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi -aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM -gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB -qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 -lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn -8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov -L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 -45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO -UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 -O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC -bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv -GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a -77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC -hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 -92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp -Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w -ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt -Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ ------END CERTIFICATE----- - -# Issuer: CN=SecureTrust CA O=SecureTrust Corporation -# Subject: CN=SecureTrust CA O=SecureTrust Corporation -# Label: "SecureTrust CA" -# Serial: 17199774589125277788362757014266862032 -# MD5 Fingerprint: dc:32:c3:a7:6d:25:57:c7:68:09:9d:ea:2d:a9:a2:d1 -# SHA1 Fingerprint: 87:82:c6:c3:04:35:3b:cf:d2:96:92:d2:59:3e:7d:44:d9:34:ff:11 -# SHA256 Fingerprint: f1:c1:b5:0a:e5:a2:0d:d8:03:0e:c9:f6:bc:24:82:3d:d3:67:b5:25:57:59:b4:e7:1b:61:fc:e9:f7:37:5d:73 ------BEGIN CERTIFICATE----- -MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI -MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x -FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz -MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv -cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN -AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz -Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO -0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao -wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj -7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS -8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT -BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB -/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg -JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC -NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 -6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ -3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm -D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS -CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR -3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= ------END CERTIFICATE----- - -# Issuer: CN=Secure Global CA O=SecureTrust Corporation -# Subject: CN=Secure Global CA O=SecureTrust Corporation -# Label: "Secure Global CA" -# Serial: 9751836167731051554232119481456978597 -# MD5 Fingerprint: cf:f4:27:0d:d4:ed:dc:65:16:49:6d:3d:da:bf:6e:de -# SHA1 Fingerprint: 3a:44:73:5a:e5:81:90:1f:24:86:61:46:1e:3b:9c:c4:5f:f5:3a:1b -# SHA256 Fingerprint: 42:00:f5:04:3a:c8:59:0e:bb:52:7d:20:9e:d1:50:30:29:fb:cb:d4:1c:a1:b5:06:ec:27:f1:5a:de:7d:ac:69 ------BEGIN CERTIFICATE----- -MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK -MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x -GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx -MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg -Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ -iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa -/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ -jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI -HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 -sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w -gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw -KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG -AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L -URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO -H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm -I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY -iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc -f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW ------END CERTIFICATE----- - -# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited -# Subject: CN=COMODO Certification Authority O=COMODO CA Limited -# Label: "COMODO Certification Authority" -# Serial: 104350513648249232941998508985834464573 -# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75 -# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b -# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66 ------BEGIN CERTIFICATE----- -MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB -gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV -BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw -MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl -YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P -RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 -UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI -2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 -Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp -+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ -DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O -nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW -/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g -PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u -QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY -SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv -IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ -RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 -zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd -BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB -ZQ== ------END CERTIFICATE----- - -# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited -# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited -# Label: "COMODO ECC Certification Authority" -# Serial: 41578283867086692638256921589707938090 -# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 -# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 -# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 ------BEGIN CERTIFICATE----- -MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL -MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE -BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT -IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw -MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy -ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N -T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv -biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR -FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J -cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW -BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm -fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv -GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= ------END CERTIFICATE----- - -# Issuer: CN=Certigna O=Dhimyotis -# Subject: CN=Certigna O=Dhimyotis -# Label: "Certigna" -# Serial: 18364802974209362175 -# MD5 Fingerprint: ab:57:a6:5b:7d:42:82:19:b5:d8:58:26:28:5e:fd:ff -# SHA1 Fingerprint: b1:2e:13:63:45:86:a4:6f:1a:b2:60:68:37:58:2d:c4:ac:fd:94:97 -# SHA256 Fingerprint: e3:b6:a2:db:2e:d7:ce:48:84:2f:7a:c5:32:41:c7:b7:1d:54:14:4b:fb:40:c1:1f:3f:1d:0b:42:f5:ee:a1:2d ------BEGIN CERTIFICATE----- -MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV -BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X -DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ -BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 -QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny -gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw -zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q -130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 -JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw -DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw -ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT -AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj -AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG -9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h -bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc -fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu -HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w -t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw -WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== ------END CERTIFICATE----- - -# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority -# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority -# Label: "ePKI Root Certification Authority" -# Serial: 28956088682735189655030529057352760477 -# MD5 Fingerprint: 1b:2e:00:ca:26:06:90:3d:ad:fe:6f:15:68:d3:6b:b3 -# SHA1 Fingerprint: 67:65:0d:f1:7e:8e:7e:5b:82:40:a4:f4:56:4b:cf:e2:3d:69:c6:f0 -# SHA256 Fingerprint: c0:a6:f4:dc:63:a2:4b:fd:cf:54:ef:2a:6a:08:2a:0a:72:de:35:80:3e:2f:f5:ff:52:7a:e5:d8:72:06:df:d5 ------BEGIN CERTIFICATE----- -MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe -MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 -ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe -Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw -IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL -SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF -AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH -SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh -ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X -DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 -TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ -fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA -sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU -WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS -nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH -dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip -NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC -AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF -MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH -ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB -uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl -PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP -JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ -gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 -j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 -5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB -o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS -/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z -Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE -W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D -hNQ+IIX3Sj0rnP0qCglN6oH4EZw= ------END CERTIFICATE----- - -# Issuer: O=certSIGN OU=certSIGN ROOT CA -# Subject: O=certSIGN OU=certSIGN ROOT CA -# Label: "certSIGN ROOT CA" -# Serial: 35210227249154 -# MD5 Fingerprint: 18:98:c0:d6:e9:3a:fc:f9:b0:f5:0c:f7:4b:01:44:17 -# SHA1 Fingerprint: fa:b7:ee:36:97:26:62:fb:2d:b0:2a:f6:bf:03:fd:e8:7c:4b:2f:9b -# SHA256 Fingerprint: ea:a9:62:c4:fa:4a:6b:af:eb:e4:15:19:6d:35:1c:cd:88:8d:4f:53:f3:fa:8a:e6:d7:c4:66:a9:4e:60:42:bb ------BEGIN CERTIFICATE----- -MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT -AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD -QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP -MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do -0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ -UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d -RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ -OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv -JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C -AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O -BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ -LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY -MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ -44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I -Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw -i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN -9u6wWk5JRFRYX0KD ------END CERTIFICATE----- - -# Issuer: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) -# Subject: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) -# Label: "NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny" -# Serial: 80544274841616 -# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88 -# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91 -# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98 ------BEGIN CERTIFICATE----- -MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG -EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 -MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl -cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR -dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB -pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM -b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm -aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz -IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT -lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz -AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 -VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG -ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 -BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG -AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M -U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh -bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C -+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC -bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F -uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 -XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= ------END CERTIFICATE----- - -# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. -# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. -# Label: "Microsec e-Szigno Root CA 2009" -# Serial: 14014712776195784473 -# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1 -# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e -# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78 ------BEGIN CERTIFICATE----- -MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD -VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 -ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G -CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y -OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx -FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp -Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o -dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP -kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc -cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U -fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 -N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC -xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 -+rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G -A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM -Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG -SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h -mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk -ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 -tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c -2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t -HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW ------END CERTIFICATE----- - -# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 -# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 -# Label: "GlobalSign Root CA - R3" -# Serial: 4835703278459759426209954 -# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 -# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad -# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b ------BEGIN CERTIFICATE----- -MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G -A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp -Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 -MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG -A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 -RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT -gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm -KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd -QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ -XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw -DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o -LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU -RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp -jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK -6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX -mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs -Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH -WD9f ------END CERTIFICATE----- - -# Issuer: CN=Izenpe.com O=IZENPE S.A. -# Subject: CN=Izenpe.com O=IZENPE S.A. -# Label: "Izenpe.com" -# Serial: 917563065490389241595536686991402621 -# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73 -# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19 -# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f ------BEGIN CERTIFICATE----- -MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 -MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 -ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD -VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j -b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq -scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO -xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H -LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX -uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD -yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ -JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q -rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN -BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L -hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB -QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ -HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu -Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg -QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB -BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx -MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA -A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb -laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 -awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo -JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw -LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT -VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk -LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb -UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ -QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ -naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls -QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== ------END CERTIFICATE----- - -# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. -# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. -# Label: "Go Daddy Root Certificate Authority - G2" -# Serial: 0 -# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 -# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b -# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT -EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp -ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz -NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH -EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE -AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD -E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH -/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy -DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh -GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR -tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA -AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE -FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX -WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu -9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr -gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo -2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO -LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI -4uJEvlz36hz1 ------END CERTIFICATE----- - -# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. -# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. -# Label: "Starfield Root Certificate Authority - G2" -# Serial: 0 -# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 -# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e -# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 ------BEGIN CERTIFICATE----- -MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT -HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs -ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw -MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 -b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj -aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp -Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg -nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 -HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N -Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN -dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 -HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO -BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G -CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU -sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 -4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg -8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K -pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 -mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 ------END CERTIFICATE----- - -# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. -# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. -# Label: "Starfield Services Root Certificate Authority - G2" -# Serial: 0 -# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2 -# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f -# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5 ------BEGIN CERTIFICATE----- -MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT -HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs -ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 -MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD -VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy -ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy -dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p -OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 -8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K -Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe -hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk -6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q -AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI -bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB -ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z -qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd -iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn -0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN -sSi6 ------END CERTIFICATE----- - -# Issuer: CN=AffirmTrust Commercial O=AffirmTrust -# Subject: CN=AffirmTrust Commercial O=AffirmTrust -# Label: "AffirmTrust Commercial" -# Serial: 8608355977964138876 -# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7 -# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7 -# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7 ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz -dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL -MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp -cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP -Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr -ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL -MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 -yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr -VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ -nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ -KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG -XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj -vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt -Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g -N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC -nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= ------END CERTIFICATE----- - -# Issuer: CN=AffirmTrust Networking O=AffirmTrust -# Subject: CN=AffirmTrust Networking O=AffirmTrust -# Label: "AffirmTrust Networking" -# Serial: 8957382827206547757 -# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f -# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f -# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz -dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL -MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp -cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y -YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua -kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL -QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp -6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG -yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i -QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ -KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO -tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu -QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ -Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u -olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 -x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= ------END CERTIFICATE----- - -# Issuer: CN=AffirmTrust Premium O=AffirmTrust -# Subject: CN=AffirmTrust Premium O=AffirmTrust -# Label: "AffirmTrust Premium" -# Serial: 7893706540734352110 -# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57 -# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27 -# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a ------BEGIN CERTIFICATE----- -MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz -dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG -A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U -cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf -qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ -JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ -+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS -s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 -HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 -70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG -V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S -qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S -5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia -C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX -OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE -FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ -BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 -KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg -Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B -8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ -MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc -0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ -u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF -u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH -YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 -GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO -RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e -KeC2uAloGRwYQw== ------END CERTIFICATE----- - -# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust -# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust -# Label: "AffirmTrust Premium ECC" -# Serial: 8401224907861490260 -# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d -# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb -# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23 ------BEGIN CERTIFICATE----- -MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC -VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ -cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ -BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt -VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D -0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 -ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G -A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G -A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs -aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I -flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== ------END CERTIFICATE----- - -# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority -# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority -# Label: "Certum Trusted Network CA" -# Serial: 279744 -# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 -# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e -# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e ------BEGIN CERTIFICATE----- -MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM -MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D -ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU -cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 -WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg -Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw -IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH -UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM -TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU -BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM -kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x -AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV -HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y -sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL -I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 -J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY -VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI -03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= ------END CERTIFICATE----- - -# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA -# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA -# Label: "TWCA Root Certification Authority" -# Serial: 1 -# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79 -# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48 -# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44 ------BEGIN CERTIFICATE----- -MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES -MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU -V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz -WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO -LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm -aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE -AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH -K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX -RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z -rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx -3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq -hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC -MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls -XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D -lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn -aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ -YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== ------END CERTIFICATE----- - -# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 -# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 -# Label: "Security Communication RootCA2" -# Serial: 0 -# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43 -# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74 -# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6 ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl -MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe -U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX -DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy -dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj -YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV -OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr -zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM -VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ -hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO -ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw -awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs -OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 -DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF -coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc -okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 -t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy -1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ -SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 ------END CERTIFICATE----- - -# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 -# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 -# Label: "Actalis Authentication Root CA" -# Serial: 6271844772424770508 -# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6 -# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac -# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66 ------BEGIN CERTIFICATE----- -MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE -BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w -MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 -IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC -SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 -ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv -UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX -4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 -KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ -gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb -rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ -51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F -be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe -KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F -v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn -fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 -jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz -ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt -ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL -e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 -jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz -WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V -SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j -pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX -X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok -fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R -K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU -ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU -LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT -LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== ------END CERTIFICATE----- - -# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 -# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 -# Label: "Buypass Class 2 Root CA" -# Serial: 2 -# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29 -# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99 -# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48 ------BEGIN CERTIFICATE----- -MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd -MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg -Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow -TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw -HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB -BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr -6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV -L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 -1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx -MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ -QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB -arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr -Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi -FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS -P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN -9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP -AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz -uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h -9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s -A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t -OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo -+fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 -KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 -DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us -H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ -I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 -5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h -3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz -Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= ------END CERTIFICATE----- - -# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 -# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 -# Label: "Buypass Class 3 Root CA" -# Serial: 2 -# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec -# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57 -# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d ------BEGIN CERTIFICATE----- -MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd -MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg -Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow -TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw -HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB -BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y -ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E -N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 -tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX -0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c -/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X -KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY -zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS -O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D -34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP -K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 -AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv -Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj -QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV -cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS -IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 -HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa -O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv -033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u -dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE -kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 -3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD -u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq -4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= ------END CERTIFICATE----- - -# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center -# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center -# Label: "T-TeleSec GlobalRoot Class 3" -# Serial: 1 -# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef -# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1 -# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd ------BEGIN CERTIFICATE----- -MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx -KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd -BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl -YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 -OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy -aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 -ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN -8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ -RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 -hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 -ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM -EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj -QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 -A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy -WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ -1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 -6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT -91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml -e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p -TpPDpFQUWw== ------END CERTIFICATE----- - -# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH -# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH -# Label: "D-TRUST Root Class 3 CA 2 2009" -# Serial: 623603 -# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f -# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0 -# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1 ------BEGIN CERTIFICATE----- -MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF -MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD -bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha -ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM -HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 -UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 -tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R -ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM -lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp -/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G -A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G -A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj -dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy -MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl -cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js -L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL -BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni -acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 -o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K -zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 -PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y -Johw1+qRzT65ysCQblrGXnRl11z+o+I= ------END CERTIFICATE----- - -# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH -# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH -# Label: "D-TRUST Root Class 3 CA 2 EV 2009" -# Serial: 623604 -# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6 -# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83 -# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81 ------BEGIN CERTIFICATE----- -MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF -MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD -bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw -NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV -BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn -ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 -3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z -qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR -p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 -HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw -ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea -HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw -Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh -c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E -RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt -dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku -Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp -3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 -nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF -CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na -xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX -KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 ------END CERTIFICATE----- - -# Issuer: CN=CA Disig Root R2 O=Disig a.s. -# Subject: CN=CA Disig Root R2 O=Disig a.s. -# Label: "CA Disig Root R2" -# Serial: 10572350602393338211 -# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03 -# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71 -# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03 ------BEGIN CERTIFICATE----- -MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV -BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu -MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy -MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx -EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw -ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe -NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH -PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I -x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe -QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR -yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO -QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 -H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ -QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD -i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs -nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 -rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud -DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI -hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM -tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf -GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb -lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka -+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal -TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i -nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 -gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr -G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os -zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x -L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL ------END CERTIFICATE----- - -# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV -# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV -# Label: "ACCVRAIZ1" -# Serial: 6828503384748696800 -# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02 -# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17 -# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13 ------BEGIN CERTIFICATE----- -MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE -AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw -CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ -BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND -VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb -qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY -HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo -G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA -lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr -IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ -0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH -k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 -4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO -m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa -cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl -uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI -KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls -ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG -AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 -VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT -VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG -CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA -cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA -QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA -7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA -cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA -QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA -czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu -aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt -aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud -DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF -BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp -D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU -JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m -AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD -vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms -tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH -7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h -I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA -h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF -d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H -pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 ------END CERTIFICATE----- - -# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA -# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA -# Label: "TWCA Global Root CA" -# Serial: 3262 -# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96 -# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65 -# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b ------BEGIN CERTIFICATE----- -MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx -EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT -VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 -NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT -B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF -10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz -0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh -MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH -zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc -46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 -yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi -laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP -oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA -BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE -qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm -4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB -/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL -1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn -LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF -H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo -RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ -nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh -15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW -6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW -nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j -wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz -aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy -KwbQBM0= ------END CERTIFICATE----- - -# Issuer: CN=TeliaSonera Root CA v1 O=TeliaSonera -# Subject: CN=TeliaSonera Root CA v1 O=TeliaSonera -# Label: "TeliaSonera Root CA v1" -# Serial: 199041966741090107964904287217786801558 -# MD5 Fingerprint: 37:41:49:1b:18:56:9a:26:f5:ad:c2:66:fb:40:a5:4c -# SHA1 Fingerprint: 43:13:bb:96:f1:d5:86:9b:c1:4e:6a:92:f6:cf:f6:34:69:87:82:37 -# SHA256 Fingerprint: dd:69:36:fe:21:f8:f0:77:c1:23:a1:a5:21:c1:22:24:f7:22:55:b7:3e:03:a7:26:06:93:e8:a2:4b:0f:a3:89 ------BEGIN CERTIFICATE----- -MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw -NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv -b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD -VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 -MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F -VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 -7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X -Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ -/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs -81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm -dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe -Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu -sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 -pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs -slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ -arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD -VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG -9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl -dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx -0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj -TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed -Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 -Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI -OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 -vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW -t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn -HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx -SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= ------END CERTIFICATE----- - -# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center -# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center -# Label: "T-TeleSec GlobalRoot Class 2" -# Serial: 1 -# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a -# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 -# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 ------BEGIN CERTIFICATE----- -MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx -KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd -BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl -YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 -OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy -aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 -ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd -AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC -FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi -1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq -jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ -wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj -QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ -WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy -NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC -uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw -IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 -g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN -9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP -BSeOE6Fuwg== ------END CERTIFICATE----- - -# Issuer: CN=Atos TrustedRoot 2011 O=Atos -# Subject: CN=Atos TrustedRoot 2011 O=Atos -# Label: "Atos TrustedRoot 2011" -# Serial: 6643877497813316402 -# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56 -# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21 -# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74 ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE -AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG -EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM -FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC -REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp -Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM -VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ -SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ -4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L -cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi -eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV -HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG -A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 -DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j -vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP -DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc -maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D -lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv -KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed ------END CERTIFICATE----- - -# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited -# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited -# Label: "QuoVadis Root CA 1 G3" -# Serial: 687049649626669250736271037606554624078720034195 -# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab -# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 -# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL -BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc -BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 -MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM -aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV -wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe -rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 -68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh -4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp -UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o -abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc -3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G -KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt -hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO -Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt -zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD -ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC -MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 -cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN -qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 -YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv -b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 -8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k -NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj -ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp -q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt -nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD ------END CERTIFICATE----- - -# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited -# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited -# Label: "QuoVadis Root CA 2 G3" -# Serial: 390156079458959257446133169266079962026824725800 -# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 -# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 -# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL -BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc -BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 -MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM -aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf -qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW -n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym -c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ -O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 -o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j -IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq -IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz -8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh -vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l -7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG -cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD -ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 -AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC -roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga -W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n -lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE -+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV -csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd -dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg -KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM -HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 -WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M ------END CERTIFICATE----- - -# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited -# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited -# Label: "QuoVadis Root CA 3 G3" -# Serial: 268090761170461462463995952157327242137089239581 -# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 -# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d -# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL -BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc -BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 -MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM -aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR -/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu -FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR -U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c -ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR -FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k -A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw -eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl -sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp -VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q -A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ -ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD -ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px -KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI -FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv -oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg -u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP -0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf -3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl -8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ -DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN -PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ -ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 ------END CERTIFICATE----- - -# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Assured ID Root G2" -# Serial: 15385348160840213938643033620894905419 -# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d -# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f -# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 ------BEGIN CERTIFICATE----- -MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv -b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl -cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA -n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc -biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp -EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA -bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu -YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB -AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW -BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI -QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I -0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni -lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 -B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv -ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo -IhNzbM8m9Yop5w== ------END CERTIFICATE----- - -# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Assured ID Root G3" -# Serial: 15459312981008553731928384953135426796 -# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb -# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 -# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 ------BEGIN CERTIFICATE----- -MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw -CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu -ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg -RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV -UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu -Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq -hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf -Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q -RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ -BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD -AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY -JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv -6pZjamVFkpUBtA== ------END CERTIFICATE----- - -# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Global Root G2" -# Serial: 4293743540046975378534879503202253541 -# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 -# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 -# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f ------BEGIN CERTIFICATE----- -MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH -MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI -2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx -1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ -q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz -tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ -vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP -BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV -5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY -1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 -NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG -Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 -8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe -pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl -MrY= ------END CERTIFICATE----- - -# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Global Root G3" -# Serial: 7089244469030293291760083333884364146 -# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca -# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e -# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 ------BEGIN CERTIFICATE----- -MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw -CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu -ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe -Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw -EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x -IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF -K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG -fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO -Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd -BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx -AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ -oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 -sycX ------END CERTIFICATE----- - -# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Trusted Root G4" -# Serial: 7451500558977370777930084869016614236 -# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 -# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 -# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 ------BEGIN CERTIFICATE----- -MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg -RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV -UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu -Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y -ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If -xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV -ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO -DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ -jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ -CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi -EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM -fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY -uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK -chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t -9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD -ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 -SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd -+SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc -fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa -sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N -cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N -0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie -4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI -r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 -/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm -gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ ------END CERTIFICATE----- - -# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited -# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited -# Label: "COMODO RSA Certification Authority" -# Serial: 101909084537582093308941363524873193117 -# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 -# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 -# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 ------BEGIN CERTIFICATE----- -MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB -hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV -BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 -MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT -EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR -Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh -dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR -6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X -pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC -9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV -/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf -Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z -+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w -qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah -SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC -u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf -Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq -crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E -FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB -/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl -wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM -4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV -2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna -FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ -CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK -boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke -jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL -S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb -QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl -0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB -NVOFBkpdn627G190 ------END CERTIFICATE----- - -# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network -# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network -# Label: "USERTrust RSA Certification Authority" -# Serial: 2645093764781058787591871645665788717 -# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 -# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e -# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 ------BEGIN CERTIFICATE----- -MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB -iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl -cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV -BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw -MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV -BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU -aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy -dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK -AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B -3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY -tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ -Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 -VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT -79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 -c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT -Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l -c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee -UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE -Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd -BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G -A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF -Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO -VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 -ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs -8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR -iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze -Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ -XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ -qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB -VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB -L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG -jjxDah2nGN59PRbxYvnKkKj9 ------END CERTIFICATE----- - -# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network -# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network -# Label: "USERTrust ECC Certification Authority" -# Serial: 123013823720199481456569720443997572134 -# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 -# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 -# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a ------BEGIN CERTIFICATE----- -MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL -MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl -eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT -JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx -MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT -Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg -VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm -aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo -I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng -o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G -A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB -zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW -RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= ------END CERTIFICATE----- - -# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 -# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 -# Label: "GlobalSign ECC Root CA - R5" -# Serial: 32785792099990507226680698011560947931244 -# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 -# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa -# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 ------BEGIN CERTIFICATE----- -MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk -MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH -bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX -DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD -QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu -MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc -8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke -hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI -KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg -515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO -xwy8p2Fp8fc74SrL+SvzZpA3 ------END CERTIFICATE----- - -# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust -# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust -# Label: "IdenTrust Commercial Root CA 1" -# Serial: 13298821034946342390520003877796839426 -# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7 -# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25 -# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK -MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu -VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw -MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw -JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT -3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU -+ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp -S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 -bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi -T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL -vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK -Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK -dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT -c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv -l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N -iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB -/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD -ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH -6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt -LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 -nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 -+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK -W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT -AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq -l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG -4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ -mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A -7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H ------END CERTIFICATE----- - -# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust -# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust -# Label: "IdenTrust Public Sector Root CA 1" -# Serial: 13298821034946342390521976156843933698 -# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba -# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd -# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f ------BEGIN CERTIFICATE----- -MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN -MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu -VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN -MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 -MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 -ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy -RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS -bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF -/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R -3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw -EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy -9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V -GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ -2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV -WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD -W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN -AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj -t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV -DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 -TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G -lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW -mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df -WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 -+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ -tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA -GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv -8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c ------END CERTIFICATE----- - -# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only -# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only -# Label: "Entrust Root Certification Authority - G2" -# Serial: 1246989352 -# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2 -# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4 -# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39 ------BEGIN CERTIFICATE----- -MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC -VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 -cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs -IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz -dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy -NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu -dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt -dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 -aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj -YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T -RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN -cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW -wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 -U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 -jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP -BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN -BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ -jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ -Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v -1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R -nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH -VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== ------END CERTIFICATE----- - -# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only -# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only -# Label: "Entrust Root Certification Authority - EC1" -# Serial: 51543124481930649114116133369 -# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc -# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47 -# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5 ------BEGIN CERTIFICATE----- -MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG -A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 -d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu -dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq -RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy -MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD -VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 -L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g -Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD -ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi -A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt -ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH -Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O -BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC -R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX -hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G ------END CERTIFICATE----- - -# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority -# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority -# Label: "CFCA EV ROOT" -# Serial: 407555286 -# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30 -# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83 -# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd ------BEGIN CERTIFICATE----- -MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD -TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y -aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx -MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j -aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP -T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 -sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL -TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 -/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp -7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz -EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt -hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP -a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot -aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg -TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV -PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv -cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL -tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd -BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB -ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT -ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL -jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS -ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy -P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 -xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d -Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN -5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe -/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z -AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ -5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su ------END CERTIFICATE----- - -# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed -# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed -# Label: "OISTE WISeKey Global Root GB CA" -# Serial: 157768595616588414422159278966750757568 -# MD5 Fingerprint: a4:eb:b9:61:28:2e:b7:2f:98:b0:35:26:90:99:51:1d -# SHA1 Fingerprint: 0f:f9:40:76:18:d3:d7:6a:4b:98:f0:a8:35:9e:0c:fd:27:ac:cc:ed -# SHA256 Fingerprint: 6b:9c:08:e8:6e:b0:f7:67:cf:ad:65:cd:98:b6:21:49:e5:49:4a:67:f5:84:5e:7b:d1:ed:01:9f:27:b8:6b:d6 ------BEGIN CERTIFICATE----- -MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt -MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg -Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i -YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x -CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG -b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh -bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 -HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx -WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX -1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk -u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P -99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r -M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw -AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB -BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh -cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 -gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO -ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf -aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic -Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= ------END CERTIFICATE----- - -# Issuer: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. -# Subject: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. -# Label: "SZAFIR ROOT CA2" -# Serial: 357043034767186914217277344587386743377558296292 -# MD5 Fingerprint: 11:64:c1:89:b0:24:b1:8c:b1:07:7e:89:9e:51:9e:99 -# SHA1 Fingerprint: e2:52:fa:95:3f:ed:db:24:60:bd:6e:28:f3:9c:cc:cf:5e:b3:3f:de -# SHA256 Fingerprint: a1:33:9d:33:28:1a:0b:56:e5:57:d3:d3:2b:1c:e7:f9:36:7e:b0:94:bd:5f:a7:2a:7e:50:04:c8:de:d7:ca:fe ------BEGIN CERTIFICATE----- -MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL -BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 -ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw -NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L -cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg -Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN -QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT -3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw -3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 -3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 -BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN -XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD -AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF -AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw -8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG -nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP -oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy -d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg -LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== ------END CERTIFICATE----- - -# Issuer: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority -# Subject: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority -# Label: "Certum Trusted Network CA 2" -# Serial: 44979900017204383099463764357512596969 -# MD5 Fingerprint: 6d:46:9e:d9:25:6d:08:23:5b:5e:74:7d:1e:27:db:f2 -# SHA1 Fingerprint: d3:dd:48:3e:2b:bf:4c:05:e8:af:10:f5:fa:76:26:cf:d3:dc:30:92 -# SHA256 Fingerprint: b6:76:f2:ed:da:e8:77:5c:d3:6c:b0:f6:3c:d1:d4:60:39:61:f4:9e:62:65:ba:01:3a:2f:03:07:b6:d0:b8:04 ------BEGIN CERTIFICATE----- -MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB -gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu -QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG -A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz -OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ -VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 -b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA -DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn -0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB -OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE -fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E -Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m -o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i -sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW -OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez -Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS -adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n -3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD -AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC -AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ -F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf -CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 -XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm -djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ -WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb -AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq -P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko -b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj -XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P -5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi -DrW5viSP ------END CERTIFICATE----- - -# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority -# Subject: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority -# Label: "Hellenic Academic and Research Institutions RootCA 2015" -# Serial: 0 -# MD5 Fingerprint: ca:ff:e2:db:03:d9:cb:4b:e9:0f:ad:84:fd:7b:18:ce -# SHA1 Fingerprint: 01:0c:06:95:a6:98:19:14:ff:bf:5f:c6:b0:b6:95:ea:29:e9:12:a6 -# SHA256 Fingerprint: a0:40:92:9a:02:ce:53:b4:ac:f4:f2:ff:c6:98:1c:e4:49:6f:75:5e:6d:45:fe:0b:2a:69:2b:cd:52:52:3f:36 ------BEGIN CERTIFICATE----- -MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix -DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k -IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT -N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v -dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG -A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh -ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx -QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 -dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC -AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA -4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 -AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 -4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C -ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV -9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD -gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 -Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq -NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko -LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc -Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV -HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd -ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I -XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI -M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot -9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V -Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea -j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh -X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ -l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf -bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 -pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK -e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 -vm9qp/UsQu0yrbYhnr68 ------END CERTIFICATE----- - -# Issuer: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority -# Subject: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority -# Label: "Hellenic Academic and Research Institutions ECC RootCA 2015" -# Serial: 0 -# MD5 Fingerprint: 81:e5:b4:17:eb:c2:f5:e1:4b:0d:41:7b:49:92:fe:ef -# SHA1 Fingerprint: 9f:f1:71:8d:92:d5:9a:f3:7d:74:97:b4:bc:6f:84:68:0b:ba:b6:66 -# SHA256 Fingerprint: 44:b5:45:aa:8a:25:e6:5a:73:ca:15:dc:27:fc:36:d2:4c:1c:b9:95:3a:06:65:39:b1:15:82:dc:48:7b:48:33 ------BEGIN CERTIFICATE----- -MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN -BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl -c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl -bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv -b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ -BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj -YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 -MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 -dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg -QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa -jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC -MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi -C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep -lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof -TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR ------END CERTIFICATE----- - -# Issuer: CN=ISRG Root X1 O=Internet Security Research Group -# Subject: CN=ISRG Root X1 O=Internet Security Research Group -# Label: "ISRG Root X1" -# Serial: 172886928669790476064670243504169061120 -# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e -# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8 -# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6 ------BEGIN CERTIFICATE----- -MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw -TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh -cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 -WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu -ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY -MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc -h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ -0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U -A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW -T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH -B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC -B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv -KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn -OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn -jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw -qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI -rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq -hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL -ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ -3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK -NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 -ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur -TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC -jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc -oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq -4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA -mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d -emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= ------END CERTIFICATE----- - -# Issuer: O=FNMT-RCM OU=AC RAIZ FNMT-RCM -# Subject: O=FNMT-RCM OU=AC RAIZ FNMT-RCM -# Label: "AC RAIZ FNMT-RCM" -# Serial: 485876308206448804701554682760554759 -# MD5 Fingerprint: e2:09:04:b4:d3:bd:d1:a0:14:fd:1a:d2:47:c4:57:1d -# SHA1 Fingerprint: ec:50:35:07:b2:15:c4:95:62:19:e2:a8:9a:5b:42:99:2c:4c:2c:20 -# SHA256 Fingerprint: eb:c5:57:0c:29:01:8c:4d:67:b1:aa:12:7b:af:12:f7:03:b4:61:1e:bc:17:b7:da:b5:57:38:94:17:9b:93:fa ------BEGIN CERTIFICATE----- -MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx -CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ -WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ -BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG -Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ -yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf -BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz -WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF -tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z -374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC -IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL -mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 -wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS -MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 -ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet -UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw -AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H -YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 -LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD -nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 -RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM -LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf -77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N -JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm -fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp -6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp -1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B -9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok -RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv -uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= ------END CERTIFICATE----- - -# Issuer: CN=Amazon Root CA 1 O=Amazon -# Subject: CN=Amazon Root CA 1 O=Amazon -# Label: "Amazon Root CA 1" -# Serial: 143266978916655856878034712317230054538369994 -# MD5 Fingerprint: 43:c6:bf:ae:ec:fe:ad:2f:18:c6:88:68:30:fc:c8:e6 -# SHA1 Fingerprint: 8d:a7:f9:65:ec:5e:fc:37:91:0f:1c:6e:59:fd:c1:cc:6a:6e:de:16 -# SHA256 Fingerprint: 8e:cd:e6:88:4f:3d:87:b1:12:5b:a3:1a:c3:fc:b1:3d:70:16:de:7f:57:cc:90:4f:e1:cb:97:c6:ae:98:19:6e ------BEGIN CERTIFICATE----- -MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF -ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 -b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL -MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv -b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj -ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM -9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw -IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 -VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L -93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm -jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA -A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI -U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs -N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv -o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU -5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy -rqXRfboQnoZsG4q5WTP468SQvvG5 ------END CERTIFICATE----- - -# Issuer: CN=Amazon Root CA 2 O=Amazon -# Subject: CN=Amazon Root CA 2 O=Amazon -# Label: "Amazon Root CA 2" -# Serial: 143266982885963551818349160658925006970653239 -# MD5 Fingerprint: c8:e5:8d:ce:a8:42:e2:7a:c0:2a:5c:7c:9e:26:bf:66 -# SHA1 Fingerprint: 5a:8c:ef:45:d7:a6:98:59:76:7a:8c:8b:44:96:b5:78:cf:47:4b:1a -# SHA256 Fingerprint: 1b:a5:b2:aa:8c:65:40:1a:82:96:01:18:f8:0b:ec:4f:62:30:4d:83:ce:c4:71:3a:19:c3:9c:01:1e:a4:6d:b4 ------BEGIN CERTIFICATE----- -MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF -ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 -b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL -MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv -b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK -gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ -W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg -1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K -8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r -2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me -z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR -8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj -mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz -7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 -+XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI -0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB -Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm -UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 -LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY -+gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS -k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl -7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm -btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl -urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ -fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 -n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE -76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H -9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT -4PsJYGw= ------END CERTIFICATE----- - -# Issuer: CN=Amazon Root CA 3 O=Amazon -# Subject: CN=Amazon Root CA 3 O=Amazon -# Label: "Amazon Root CA 3" -# Serial: 143266986699090766294700635381230934788665930 -# MD5 Fingerprint: a0:d4:ef:0b:f7:b5:d8:49:95:2a:ec:f5:c4:fc:81:87 -# SHA1 Fingerprint: 0d:44:dd:8c:3c:8c:1a:1a:58:75:64:81:e9:0f:2e:2a:ff:b3:d2:6e -# SHA256 Fingerprint: 18:ce:6c:fe:7b:f1:4e:60:b2:e3:47:b8:df:e8:68:cb:31:d0:2e:bb:3a:da:27:15:69:f5:03:43:b4:6d:b3:a4 ------BEGIN CERTIFICATE----- -MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 -MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g -Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG -A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg -Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl -ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j -QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr -ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr -BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM -YyRIHN8wfdVoOw== ------END CERTIFICATE----- - -# Issuer: CN=Amazon Root CA 4 O=Amazon -# Subject: CN=Amazon Root CA 4 O=Amazon -# Label: "Amazon Root CA 4" -# Serial: 143266989758080763974105200630763877849284878 -# MD5 Fingerprint: 89:bc:27:d5:eb:17:8d:06:6a:69:d5:fd:89:47:b4:cd -# SHA1 Fingerprint: f6:10:84:07:d6:f8:bb:67:98:0c:c2:e2:44:c2:eb:ae:1c:ef:63:be -# SHA256 Fingerprint: e3:5d:28:41:9e:d0:20:25:cf:a6:90:38:cd:62:39:62:45:8d:a5:c6:95:fb:de:a3:c2:2b:0b:fb:25:89:70:92 ------BEGIN CERTIFICATE----- -MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 -MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g -Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG -A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg -Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi -9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk -M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB -/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB -MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw -CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW -1KyLa2tJElMzrdfkviT8tQp21KW8EA== ------END CERTIFICATE----- - -# Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM -# Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM -# Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" -# Serial: 1 -# MD5 Fingerprint: dc:00:81:dc:69:2f:3e:2f:b0:3b:f6:3d:5a:91:8e:49 -# SHA1 Fingerprint: 31:43:64:9b:ec:ce:27:ec:ed:3a:3f:0b:8f:0d:e4:e8:91:dd:ee:ca -# SHA256 Fingerprint: 46:ed:c3:68:90:46:d5:3a:45:3f:b3:10:4a:b8:0d:ca:ec:65:8b:26:60:ea:16:29:dd:7e:86:79:90:64:87:16 ------BEGIN CERTIFICATE----- -MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx -GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp -bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w -KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 -BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy -dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG -EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll -IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU -QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT -TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg -LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 -a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr -LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr -N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X -YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ -iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f -AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH -V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL -BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh -AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf -IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 -lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c -8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf -lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= ------END CERTIFICATE----- - -# Issuer: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. -# Subject: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. -# Label: "GDCA TrustAUTH R5 ROOT" -# Serial: 9009899650740120186 -# MD5 Fingerprint: 63:cc:d9:3d:34:35:5c:6f:53:a3:e2:08:70:48:1f:b4 -# SHA1 Fingerprint: 0f:36:38:5b:81:1a:25:c3:9b:31:4e:83:ca:e9:34:66:70:cc:74:b4 -# SHA256 Fingerprint: bf:ff:8f:d0:44:33:48:7d:6a:8a:a6:0c:1a:29:76:7a:9f:c2:bb:b0:5e:42:0f:71:3a:13:b9:92:89:1d:38:93 ------BEGIN CERTIFICATE----- -MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE -BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ -IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 -MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV -BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w -HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF -AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj -Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj -TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u -KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj -qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm -MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 -ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP -zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk -L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC -jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA -HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC -AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB -/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg -p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm -DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 -COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry -L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf -JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg -IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io -2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV -09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ -XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq -T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe -MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== ------END CERTIFICATE----- - -# Issuer: CN=SSL.com Root Certification Authority RSA O=SSL Corporation -# Subject: CN=SSL.com Root Certification Authority RSA O=SSL Corporation -# Label: "SSL.com Root Certification Authority RSA" -# Serial: 8875640296558310041 -# MD5 Fingerprint: 86:69:12:c0:70:f1:ec:ac:ac:c2:d5:bc:a5:5b:a1:29 -# SHA1 Fingerprint: b7:ab:33:08:d1:ea:44:77:ba:14:80:12:5a:6f:bd:a9:36:49:0c:bb -# SHA256 Fingerprint: 85:66:6a:56:2e:e0:be:5c:e9:25:c1:d8:89:0a:6f:76:a8:7e:c1:6d:4d:7d:5f:29:ea:74:19:cf:20:12:3b:69 ------BEGIN CERTIFICATE----- -MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE -BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK -DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp -Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz -OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv -dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv -bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN -AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R -xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX -qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC -C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 -6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh -/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF -YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E -JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc -US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 -ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm -+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi -M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV -HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G -A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV -cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc -Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs -PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ -q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 -cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr -a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I -H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y -K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu -nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf -oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY -Ic2wBlX7Jz9TkHCpBB5XJ7k= ------END CERTIFICATE----- - -# Issuer: CN=SSL.com Root Certification Authority ECC O=SSL Corporation -# Subject: CN=SSL.com Root Certification Authority ECC O=SSL Corporation -# Label: "SSL.com Root Certification Authority ECC" -# Serial: 8495723813297216424 -# MD5 Fingerprint: 2e:da:e4:39:7f:9c:8f:37:d1:70:9f:26:17:51:3a:8e -# SHA1 Fingerprint: c3:19:7c:39:24:e6:54:af:1b:c4:ab:20:95:7a:e2:c3:0e:13:02:6a -# SHA256 Fingerprint: 34:17:bb:06:cc:60:07:da:1b:96:1c:92:0b:8a:b4:ce:3f:ad:82:0e:4a:a3:0b:9a:cb:c4:a7:4e:bd:ce:bc:65 ------BEGIN CERTIFICATE----- -MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC -VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T -U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 -aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz -WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 -b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS -b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB -BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI -7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg -CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud -EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD -VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T -kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ -gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl ------END CERTIFICATE----- - -# Issuer: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation -# Subject: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation -# Label: "SSL.com EV Root Certification Authority RSA R2" -# Serial: 6248227494352943350 -# MD5 Fingerprint: e1:1e:31:58:1a:ae:54:53:02:f6:17:6a:11:7b:4d:95 -# SHA1 Fingerprint: 74:3a:f0:52:9b:d0:32:a0:f4:4a:83:cd:d4:ba:a9:7b:7c:2e:c4:9a -# SHA256 Fingerprint: 2e:7b:f1:6c:c2:24:85:a7:bb:e2:aa:86:96:75:07:61:b0:ae:39:be:3b:2f:e9:d0:cc:6d:4e:f7:34:91:42:5c ------BEGIN CERTIFICATE----- -MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV -BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE -CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy -dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy -MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G -A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD -DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq -M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf -OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa -4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 -HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR -aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA -b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ -Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV -PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO -pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu -UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY -MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV -HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 -9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW -s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 -Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg -cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM -79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz -/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt -ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm -Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK -QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ -w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi -S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 -mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== ------END CERTIFICATE----- - -# Issuer: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation -# Subject: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation -# Label: "SSL.com EV Root Certification Authority ECC" -# Serial: 3182246526754555285 -# MD5 Fingerprint: 59:53:22:65:83:42:01:54:c0:ce:42:b9:5a:7c:f2:90 -# SHA1 Fingerprint: 4c:dd:51:a3:d1:f5:20:32:14:b0:c6:c5:32:23:03:91:c7:46:42:6d -# SHA256 Fingerprint: 22:a2:c1:f7:bd:ed:70:4c:c1:e7:01:b5:f4:08:c3:10:88:0f:e9:56:b5:de:2a:4a:44:f9:9c:87:3a:25:a7:c8 ------BEGIN CERTIFICATE----- -MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC -VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T -U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp -Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx -NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv -dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv -bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 -AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA -VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku -WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP -MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX -5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ -ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg -h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== ------END CERTIFICATE----- - -# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 -# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 -# Label: "GlobalSign Root CA - R6" -# Serial: 1417766617973444989252670301619537 -# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae -# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1 -# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69 ------BEGIN CERTIFICATE----- -MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg -MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh -bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx -MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET -MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ -KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI -xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k -ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD -aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw -LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw -1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX -k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 -SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h -bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n -WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY -rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce -MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD -AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu -bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN -nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt -Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 -55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj -vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf -cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz -oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp -nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs -pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v -JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R -8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 -5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= ------END CERTIFICATE----- - -# Issuer: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed -# Subject: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed -# Label: "OISTE WISeKey Global Root GC CA" -# Serial: 44084345621038548146064804565436152554 -# MD5 Fingerprint: a9:d6:b9:2d:2f:93:64:f8:a5:69:ca:91:e9:68:07:23 -# SHA1 Fingerprint: e0:11:84:5e:34:de:be:88:81:b9:9c:f6:16:26:d1:96:1f:c3:b9:31 -# SHA256 Fingerprint: 85:60:f9:1c:36:24:da:ba:95:70:b5:fe:a0:db:e3:6f:f1:1a:83:23:be:94:86:85:4f:b3:f3:4a:55:71:19:8d ------BEGIN CERTIFICATE----- -MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw -CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 -bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg -Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ -BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu -ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS -b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni -eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W -p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T -rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV -57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg -Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 ------END CERTIFICATE----- - -# Issuer: CN=UCA Global G2 Root O=UniTrust -# Subject: CN=UCA Global G2 Root O=UniTrust -# Label: "UCA Global G2 Root" -# Serial: 124779693093741543919145257850076631279 -# MD5 Fingerprint: 80:fe:f0:c4:4a:f0:5c:62:32:9f:1c:ba:78:a9:50:f8 -# SHA1 Fingerprint: 28:f9:78:16:19:7a:ff:18:25:18:aa:44:fe:c1:a0:ce:5c:b6:4c:8a -# SHA256 Fingerprint: 9b:ea:11:c9:76:fe:01:47:64:c1:be:56:a6:f9:14:b5:a5:60:31:7a:bd:99:88:39:33:82:e5:16:1a:a0:49:3c ------BEGIN CERTIFICATE----- -MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9 -MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBH -bG9iYWwgRzIgUm9vdDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0x -CzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEds -b2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYr -b3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmToni9 -kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzm -VHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/R -VogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc -C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIj -tm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLY -D0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyv -j5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6Dl -NaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6 -iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznP -O6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/ -BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/pQwIX4wV -ZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj -L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 -1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl -1qnN3e92mI0ADs0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oU -b3n09tDh05S60FdRvScFDcH9yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LV -PtateJLbXDzz2K36uGt/xDYotgIVilQsnLAXc47QN6MUPJiVAAwpBVueSUmxX8fj -y88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHojhJi6IjMtX9Gl8Cb -EGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZkbxqg -DMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI -+Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy -YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bX -UB+K+wb1whnw0A== ------END CERTIFICATE----- - -# Issuer: CN=UCA Extended Validation Root O=UniTrust -# Subject: CN=UCA Extended Validation Root O=UniTrust -# Label: "UCA Extended Validation Root" -# Serial: 106100277556486529736699587978573607008 -# MD5 Fingerprint: a1:f3:5f:43:c6:34:9b:da:bf:8c:7e:05:53:ad:96:e2 -# SHA1 Fingerprint: a3:a1:b0:6f:24:61:23:4a:e3:36:a5:c2:37:fc:a6:ff:dd:f0:d7:3a -# SHA256 Fingerprint: d4:3a:f9:b3:54:73:75:5c:96:84:fc:06:d7:d8:cb:70:ee:5c:28:e7:73:fb:29:4e:b4:1e:e7:17:22:92:4d:24 ------BEGIN CERTIFICATE----- -MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBH -MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBF -eHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMx -MDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNV -BAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrsiWog -D4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvS -sPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aop -O2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk -sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfi -c0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpj -VMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLz -KuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/ -TuDvB0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41G -sx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs -1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQD -fwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQwDwYDVR0T -AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN -l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR -ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQ -VBcZEhrxH9cMaVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5 -c6sq1WnIeJEmMX3ixzDx/BR4dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp -4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb+7lsq+KePRXBOy5nAliRn+/4Qh8s -t2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOWF3sGPjLtx7dCvHaj -2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwiGpWO -vpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2C -xR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx -cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbM -fjKaiJUINlK73nZfdklJrX+9ZSCyycErdhh2n1ax ------END CERTIFICATE----- - -# Issuer: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 -# Subject: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 -# Label: "Certigna Root CA" -# Serial: 269714418870597844693661054334862075617 -# MD5 Fingerprint: 0e:5c:30:62:27:eb:5b:bc:d7:ae:62:ba:e9:d5:df:77 -# SHA1 Fingerprint: 2d:0d:52:14:ff:9e:ad:99:24:01:74:20:47:6e:6c:85:27:27:f5:43 -# SHA256 Fingerprint: d4:8d:3d:23:ee:db:50:a4:59:e5:51:97:60:1c:27:77:4b:9d:7b:18:c9:4d:5a:05:95:11:a1:02:50:b9:31:68 ------BEGIN CERTIFICATE----- -MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw -WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw -MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x -MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD -VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX -BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw -ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO -ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M -CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu -I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm -TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh -C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf -ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz -IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT -Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k -JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5 -hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB -GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE -FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of -1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov -L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo -dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr -aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq -hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L -6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG -HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6 -0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB -lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi -o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1 -gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v -faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63 -Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh -jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw -3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= ------END CERTIFICATE----- - -# Issuer: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI -# Subject: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI -# Label: "emSign Root CA - G1" -# Serial: 235931866688319308814040 -# MD5 Fingerprint: 9c:42:84:57:dd:cb:0b:a7:2e:95:ad:b6:f3:da:bc:ac -# SHA1 Fingerprint: 8a:c7:ad:8f:73:ac:4e:c1:b5:75:4d:a5:40:f4:fc:cf:7c:b5:8e:8c -# SHA256 Fingerprint: 40:f6:af:03:46:a9:9a:a1:cd:1d:55:5a:4e:9c:ce:62:c7:f9:63:46:03:ee:40:66:15:83:3d:c8:c8:d0:03:67 ------BEGIN CERTIFICATE----- -MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD -VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU -ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH -MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO -MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv -Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz -f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO -8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq -d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM -tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt -Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB -o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD -AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x -PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM -wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d -GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH -6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby -RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx -iN66zB+Afko= ------END CERTIFICATE----- - -# Issuer: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI -# Subject: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI -# Label: "emSign ECC Root CA - G3" -# Serial: 287880440101571086945156 -# MD5 Fingerprint: ce:0b:72:d1:9f:88:8e:d0:50:03:e8:e3:b8:8b:67:40 -# SHA1 Fingerprint: 30:43:fa:4f:f2:57:dc:a0:c3:80:ee:2e:58:ea:78:b2:3f:e6:bb:c1 -# SHA256 Fingerprint: 86:a1:ec:ba:08:9c:4a:8d:3b:be:27:34:c6:12:ba:34:1d:81:3e:04:3c:f9:e8:a8:62:cd:5c:57:a3:6b:be:6b ------BEGIN CERTIFICATE----- -MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG -EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo -bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g -RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ -TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s -b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw -djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0 -WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS -fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB -zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq -hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB -CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD -+JbNR6iC8hZVdyR+EhCVBCyj ------END CERTIFICATE----- - -# Issuer: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI -# Subject: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI -# Label: "emSign Root CA - C1" -# Serial: 825510296613316004955058 -# MD5 Fingerprint: d8:e3:5d:01:21:fa:78:5a:b0:df:ba:d2:ee:2a:5f:68 -# SHA1 Fingerprint: e7:2e:f1:df:fc:b2:09:28:cf:5d:d4:d5:67:37:b1:51:cb:86:4f:01 -# SHA256 Fingerprint: 12:56:09:aa:30:1d:a0:a2:49:b9:7a:82:39:cb:6a:34:21:6f:44:dc:ac:9f:39:54:b1:42:92:f2:e8:c8:60:8f ------BEGIN CERTIFICATE----- -MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG -A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg -SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw -MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln -biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v -dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ -BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ -HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH -3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH -GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c -xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1 -aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq -TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL -BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87 -/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4 -kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG -YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT -+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo -WXzhriKi4gp6D/piq1JM4fHfyr6DDUI= ------END CERTIFICATE----- - -# Issuer: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI -# Subject: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI -# Label: "emSign ECC Root CA - C3" -# Serial: 582948710642506000014504 -# MD5 Fingerprint: 3e:53:b3:a3:81:ee:d7:10:f8:d3:b0:1d:17:92:f5:d5 -# SHA1 Fingerprint: b6:af:43:c2:9b:81:53:7d:f6:ef:6b:c3:1f:1f:60:15:0c:ee:48:66 -# SHA256 Fingerprint: bc:4d:80:9b:15:18:9d:78:db:3e:1d:8c:f4:f9:72:6a:79:5d:a1:64:3c:a5:f1:35:8e:1d:db:0e:dc:0d:7e:b3 ------BEGIN CERTIFICATE----- -MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG -EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx -IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw -MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln -biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND -IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci -MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti -sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O -BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB -Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c -3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J -0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== ------END CERTIFICATE----- - -# Issuer: CN=Hongkong Post Root CA 3 O=Hongkong Post -# Subject: CN=Hongkong Post Root CA 3 O=Hongkong Post -# Label: "Hongkong Post Root CA 3" -# Serial: 46170865288971385588281144162979347873371282084 -# MD5 Fingerprint: 11:fc:9f:bd:73:30:02:8a:fd:3f:f3:58:b9:cb:20:f0 -# SHA1 Fingerprint: 58:a2:d0:ec:20:52:81:5b:c1:f3:f8:64:02:24:4e:c2:8e:02:4b:02 -# SHA256 Fingerprint: 5a:2f:c0:3f:0c:83:b0:90:bb:fa:40:60:4b:09:88:44:6c:76:36:18:3d:f9:84:6e:17:10:1a:44:7f:b8:ef:d6 ------BEGIN CERTIFICATE----- -MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL -BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ -SG9uZyBLb25nMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25n -a29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2MDMwMjI5NDZaFw00MjA2MDMwMjI5 -NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxEjAQBgNVBAcT -CUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMXSG9u -Z2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK -AoICAQCziNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFO -dem1p+/l6TWZ5Mwc50tfjTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mI -VoBc+L0sPOFMV4i707mV78vH9toxdCim5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV -9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOesL4jpNrcyCse2m5FHomY -2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj0mRiikKY -vLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+Tt -bNe/JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZb -x39ri1UbSsUgYT2uy1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+ -l2oBlKN8W4UdKjk60FSh0Tlxnf0h+bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YK -TE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsGxVd7GYYKecsAyVKvQv83j+Gj -Hno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwIDAQABo2MwYTAP -BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e -i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEw -DQYJKoZIhvcNAQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG -7BJ8dNVI0lkUmcDrudHr9EgwW62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCk -MpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWldy8joRTnU+kLBEUx3XZL7av9YROXr -gZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov+BS5gLNdTaqX4fnk -GMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDceqFS -3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJm -Ozj/2ZQw9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+ -l6mc1X5VTMbeRRAc6uk7nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6c -JfTzPV4e0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefP -L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa -LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG -mpv0 ------END CERTIFICATE----- - -# Issuer: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation -# Subject: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation -# Label: "Microsoft ECC Root Certificate Authority 2017" -# Serial: 136839042543790627607696632466672567020 -# MD5 Fingerprint: dd:a1:03:e6:4a:93:10:d1:bf:f0:19:42:cb:fe:ed:67 -# SHA1 Fingerprint: 99:9a:64:c3:7f:f4:7d:9f:ab:95:f1:47:69:89:14:60:ee:c4:c3:c5 -# SHA256 Fingerprint: 35:8d:f3:9d:76:4a:f9:e1:b7:66:e9:c9:72:df:35:2e:e1:5c:fa:c2:27:af:6a:d1:d7:0e:8e:4a:6e:dc:ba:02 ------BEGIN CERTIFICATE----- -MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw -CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD -VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw -MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV -UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy -b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq -hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR -ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb -hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 -FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV -L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB -iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= ------END CERTIFICATE----- - -# Issuer: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation -# Subject: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation -# Label: "Microsoft RSA Root Certificate Authority 2017" -# Serial: 40975477897264996090493496164228220339 -# MD5 Fingerprint: 10:ff:00:ff:cf:c9:f8:c7:7a:c0:ee:35:8e:c9:0f:47 -# SHA1 Fingerprint: 73:a5:e6:4a:3b:ff:83:16:ff:0e:dc:cc:61:8a:90:6e:4e:ae:4d:74 -# SHA256 Fingerprint: c7:41:f7:0f:4b:2a:8d:88:bf:2e:71:c1:41:22:ef:53:ef:10:eb:a0:cf:a5:e6:4c:fa:20:f4:18:85:30:73:e0 ------BEGIN CERTIFICATE----- -MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl -MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw -NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 -IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG -EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N -aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ -Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0 -ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1 -HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm -gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ -jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc -aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG -YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6 -W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K -UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH -+FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q -W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ -BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC -NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC -LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC -gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6 -tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh -SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2 -TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3 -pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR -xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp -GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 -dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN -AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB -RA+GsCyRxj3qrg+E ------END CERTIFICATE----- - -# Issuer: CN=e-Szigno Root CA 2017 O=Microsec Ltd. -# Subject: CN=e-Szigno Root CA 2017 O=Microsec Ltd. -# Label: "e-Szigno Root CA 2017" -# Serial: 411379200276854331539784714 -# MD5 Fingerprint: de:1f:f6:9e:84:ae:a7:b4:21:ce:1e:58:7d:d1:84:98 -# SHA1 Fingerprint: 89:d4:83:03:4f:9e:9a:48:80:5f:72:37:d4:a9:a6:ef:cb:7c:1f:d1 -# SHA256 Fingerprint: be:b0:0b:30:83:9b:9b:c3:2c:32:e4:44:79:05:95:06:41:f2:64:21:b1:5e:d0:89:19:8b:51:8a:e2:ea:1b:99 ------BEGIN CERTIFICATE----- -MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV -BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk -LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv -b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ -BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg -THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v -IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv -xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H -Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G -A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB -eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo -jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ -+efcMQ== ------END CERTIFICATE----- - -# Issuer: O=CERTSIGN SA OU=certSIGN ROOT CA G2 -# Subject: O=CERTSIGN SA OU=certSIGN ROOT CA G2 -# Label: "certSIGN Root CA G2" -# Serial: 313609486401300475190 -# MD5 Fingerprint: 8c:f1:75:8a:c6:19:cf:94:b7:f7:65:20:87:c3:97:c7 -# SHA1 Fingerprint: 26:f9:93:b4:ed:3d:28:27:b0:b9:4b:a7:e9:15:1d:a3:8d:92:e5:32 -# SHA256 Fingerprint: 65:7c:fe:2f:a7:3f:aa:38:46:25:71:f3:32:a2:36:3a:46:fc:e7:02:09:51:71:07:02:cd:fb:b6:ee:da:33:05 ------BEGIN CERTIFICATE----- -MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV -BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g -Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ -BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ -R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF -dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw -vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ -uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp -n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs -cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW -xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P -rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF -DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx -DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy -LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C -eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB -/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ -d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq -kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC -b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl -qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0 -OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c -NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk -ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO -pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj -03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk -PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE -1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX -QRBdJ3NghVdJIgc= ------END CERTIFICATE----- - -# Issuer: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc. -# Subject: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc. -# Label: "Trustwave Global Certification Authority" -# Serial: 1846098327275375458322922162 -# MD5 Fingerprint: f8:1c:18:2d:2f:ba:5f:6d:a1:6c:bc:c7:ab:91:c7:0e -# SHA1 Fingerprint: 2f:8f:36:4f:e1:58:97:44:21:59:87:a5:2a:9a:d0:69:95:26:7f:b5 -# SHA256 Fingerprint: 97:55:20:15:f5:dd:fc:3c:87:88:c0:06:94:45:55:40:88:94:45:00:84:f1:00:86:70:86:bc:1a:2b:b5:8d:c8 ------BEGIN CERTIFICATE----- -MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQsw -CQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28x -ITAfBgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1 -c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMx -OTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJVUzERMA8GA1UECAwI -SWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2ZSBI -b2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZp -Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB -ALldUShLPDeS0YLOvR29zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0Xzn -swuvCAAJWX/NKSqIk4cXGIDtiLK0thAfLdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu -7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4BqstTnoApTAbqOl5F2brz8 -1Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9oWN0EACyW -80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotP -JqX+OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1l -RtzuzWniTY+HKE40Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfw -hI0Vcnyh78zyiGG69Gm7DIwLdVcEuE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10 -coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm+9jaJXLE9gCxInm943xZYkqc -BW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqjifLJS3tBEW1n -twiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud -EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1Ud -DwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W -0OhUKDtkLSGm+J1WE2pIPU/HPinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfe -uyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0HZJDmHvUqoai7PF35owgLEQzxPy0Q -lG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla4gt5kNdXElE1GYhB -aCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5RvbbE -sLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPT -MaCm/zjdzyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qe -qu5AvzSxnI9O4fKSTx+O856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxh -VicGaeVyQYHTtgGJoC86cnn+OjC/QezHYj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8 -h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu3R3y4G5OBVixwJAWKqQ9 -EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP29FpHOTK -yeC2nOnOcXHebD8WpHk= ------END CERTIFICATE----- - -# Issuer: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc. -# Subject: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc. -# Label: "Trustwave Global ECC P256 Certification Authority" -# Serial: 4151900041497450638097112925 -# MD5 Fingerprint: 5b:44:e3:8d:5d:36:86:26:e8:0d:05:d2:59:a7:83:54 -# SHA1 Fingerprint: b4:90:82:dd:45:0c:be:8b:5b:b1:66:d3:e2:a4:08:26:cd:ed:42:cf -# SHA256 Fingerprint: 94:5b:bc:82:5e:a5:54:f4:89:d1:fd:51:a7:3d:df:2e:a6:24:ac:70:19:a0:52:05:22:5c:22:a7:8c:cf:a8:b4 ------BEGIN CERTIFICATE----- -MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYD -VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf -BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 -YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x -NzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYDVQQGEwJVUzERMA8G -A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 -d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF -Q0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqG -SM49AwEHA0IABH77bOYj43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoN -FWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqmP62jQzBBMA8GA1UdEwEB/wQFMAMBAf8w -DwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt0UrrdaVKEJmzsaGLSvcw -CgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjzRM4q3wgh -DDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 ------END CERTIFICATE----- - -# Issuer: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc. -# Subject: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc. -# Label: "Trustwave Global ECC P384 Certification Authority" -# Serial: 2704997926503831671788816187 -# MD5 Fingerprint: ea:cf:60:c4:3b:b9:15:29:40:a1:97:ed:78:27:93:d6 -# SHA1 Fingerprint: e7:f3:a3:c8:cf:6f:c3:04:2e:6d:0e:67:32:c5:9e:68:95:0d:5e:d2 -# SHA256 Fingerprint: 55:90:38:59:c8:c0:c3:eb:b8:75:9e:ce:4e:25:57:22:5f:f5:75:8b:bd:38:eb:d4:82:76:60:1e:1b:d5:80:97 ------BEGIN CERTIFICATE----- -MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYD -VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf -BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 -YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x -NzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYDVQQGEwJVUzERMA8G -A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 -d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF -Q0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuB -BAAiA2IABGvaDXU1CDFHBa5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJ -j9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr/TklZvFe/oyujUF5nQlgziip04pt89ZF -1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwYAMB0G -A1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNnADBkAjA3 -AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsC -MGclCrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVu -Sw== ------END CERTIFICATE----- - -# Issuer: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. -# Subject: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. -# Label: "NAVER Global Root Certification Authority" -# Serial: 9013692873798656336226253319739695165984492813 -# MD5 Fingerprint: c8:7e:41:f6:25:3b:f5:09:b3:17:e8:46:3d:bf:d0:9b -# SHA1 Fingerprint: 8f:6b:f2:a9:27:4a:da:14:a0:c4:f4:8e:61:27:f9:c0:1e:78:5d:d1 -# SHA256 Fingerprint: 88:f4:38:dc:f8:ff:d1:fa:8f:42:91:15:ff:e5:f8:2a:e1:e0:6e:0c:70:c3:75:fa:ad:71:7b:34:a4:9e:72:65 ------BEGIN CERTIFICATE----- -MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM -BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG -T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0 -aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkx -CzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVTUyBQTEFURk9STSBD -b3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlvbiBB -dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVA -iQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH -38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE -HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZaz -kVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYP -szuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8Obtoq -vC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHf -nZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaG -YQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo -0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3a -CJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzClrkvJE+2K -AQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I -36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB -Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoN -qo0hV4/GPnrK21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatj -cu3cvuzHV+YwIHHW1xDBE1UBjCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm -+LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bxhYTeodoS76TiEJd6eN4MUZeoIUCL -hr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTgE34h5prCy8VCZLQe -lHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTHD8z7 -p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8 -piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR -LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX -5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KO -dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul -9XXeifdy ------END CERTIFICATE----- - -# Issuer: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres -# Subject: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres -# Label: "AC RAIZ FNMT-RCM SERVIDORES SEGUROS" -# Serial: 131542671362353147877283741781055151509 -# MD5 Fingerprint: 19:36:9c:52:03:2f:d2:d1:bb:23:cc:dd:1e:12:55:bb -# SHA1 Fingerprint: 62:ff:d9:9e:c0:65:0d:03:ce:75:93:d2:ed:3f:2d:32:c9:e3:e5:4a -# SHA256 Fingerprint: 55:41:53:b1:3d:2c:f9:dd:b7:53:bf:be:1a:4e:0a:e0:8d:0a:a4:18:70:58:fe:60:a2:b8:62:b2:e4:b8:7b:cb ------BEGIN CERTIFICATE----- -MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw -CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw -FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S -Q00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5 -MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQL -DAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNBQyBS -QUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuB -BAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LH -sbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK -Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD -VR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCu -SuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5IC -MQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJy -v+c= ------END CERTIFICATE----- - -# Issuer: CN=GlobalSign Root R46 O=GlobalSign nv-sa -# Subject: CN=GlobalSign Root R46 O=GlobalSign nv-sa -# Label: "GlobalSign Root R46" -# Serial: 1552617688466950547958867513931858518042577 -# MD5 Fingerprint: c4:14:30:e4:fa:66:43:94:2a:6a:1b:24:5f:19:d0:ef -# SHA1 Fingerprint: 53:a2:b0:4b:ca:6b:d6:45:e6:39:8a:8e:c4:0d:d2:bf:77:c3:a2:90 -# SHA256 Fingerprint: 4f:a3:12:6d:8d:3a:11:d1:c4:85:5a:4f:80:7c:ba:d6:cf:91:9d:3a:5a:88:b0:3b:ea:2c:63:72:d9:3c:40:c9 ------BEGIN CERTIFICATE----- -MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA -MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD -VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy -MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt -c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ -OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG -vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud -316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo -0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE -y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF -zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE -+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN -I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs -x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa -ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC -4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV -HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4 -7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg -JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti -2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk -pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF -FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt -rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk -ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5 -u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP -4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6 -N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3 -vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6 ------END CERTIFICATE----- - -# Issuer: CN=GlobalSign Root E46 O=GlobalSign nv-sa -# Subject: CN=GlobalSign Root E46 O=GlobalSign nv-sa -# Label: "GlobalSign Root E46" -# Serial: 1552617690338932563915843282459653771421763 -# MD5 Fingerprint: b5:b8:66:ed:de:08:83:e3:c9:e2:01:34:06:ac:51:6f -# SHA1 Fingerprint: 39:b4:6c:d5:fe:80:06:eb:e2:2f:4a:bb:08:33:a0:af:db:b9:dd:84 -# SHA256 Fingerprint: cb:b9:c4:4d:84:b8:04:3e:10:50:ea:31:a6:9f:51:49:55:d7:bf:d2:e2:c6:b4:93:01:01:9a:d6:1d:9f:50:58 ------BEGIN CERTIFICATE----- -MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx -CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD -ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw -MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex -HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA -IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq -R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd -yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud -DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ -7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8 -+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A= ------END CERTIFICATE----- - -# Issuer: CN=GLOBALTRUST 2020 O=e-commerce monitoring GmbH -# Subject: CN=GLOBALTRUST 2020 O=e-commerce monitoring GmbH -# Label: "GLOBALTRUST 2020" -# Serial: 109160994242082918454945253 -# MD5 Fingerprint: 8a:c7:6f:cb:6d:e3:cc:a2:f1:7c:83:fa:0e:78:d7:e8 -# SHA1 Fingerprint: d0:67:c1:13:51:01:0c:aa:d0:c7:6a:65:37:31:16:26:4f:53:71:a2 -# SHA256 Fingerprint: 9a:29:6a:51:82:d1:d4:51:a2:e3:7f:43:9b:74:da:af:a2:67:52:33:29:f9:0f:9a:0d:20:07:c3:34:e2:3c:9a ------BEGIN CERTIFICATE----- -MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkG -A1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkw -FwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYx -MDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9u -aXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMIICIjANBgkq -hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWiD59b -RatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9Z -YybNpyrOVPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3 -QWPKzv9pj2gOlTblzLmMCcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPw -yJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCmfecqQjuCgGOlYx8ZzHyyZqjC0203b+J+ -BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKAA1GqtH6qRNdDYfOiaxaJ -SaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9ORJitHHmkH -r96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj0 -4KlGDfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9Me -dKZssCz3AwyIDMvUclOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIw -q7ejMZdnrY8XD2zHc+0klGvIg5rQmjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2 -nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1UdIwQYMBaAFNwu -H9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA -VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJC -XtzoRlgHNQIw4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd -6IwPS3BD0IL/qMy/pJTAvoe9iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf -+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS8cE54+X1+NZK3TTN+2/BT+MAi1bi -kvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2HcqtbepBEX4tdJP7 -wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxSvTOB -TI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6C -MUO+1918oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn -4rnvyOL2NSl6dPrFf4IFYqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+I -aFvowdlxfv1k7/9nR4hYJS8+hge9+6jlgqispdNpQ80xiEmEU5LAsTkbOYMBMMTy -qfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== ------END CERTIFICATE----- - -# Issuer: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz -# Subject: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz -# Label: "ANF Secure Server Root CA" -# Serial: 996390341000653745 -# MD5 Fingerprint: 26:a6:44:5a:d9:af:4e:2f:b2:1d:b6:65:b0:4e:e8:96 -# SHA1 Fingerprint: 5b:6e:68:d0:cc:15:b6:a0:5f:1e:c1:5f:ae:02:fc:6b:2f:5d:6f:74 -# SHA256 Fingerprint: fb:8f:ec:75:91:69:b9:10:6b:1e:51:16:44:c6:18:c5:13:04:37:3f:6c:06:43:08:8d:8b:ef:fd:1b:99:75:99 ------BEGIN CERTIFICATE----- -MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNV -BAUTCUc2MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlk -YWQgZGUgQ2VydGlmaWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNV -BAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3QgQ0EwHhcNMTkwOTA0MTAwMDM4WhcN -MzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEwMQswCQYDVQQGEwJF -UzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQwEgYD -VQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9v -dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCj -cqQZAZ2cC4Ffc0m6p6zzBE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9q -yGFOtibBTI3/TO80sh9l2Ll49a2pcbnvT1gdpd50IJeh7WhM3pIXS7yr/2WanvtH -2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcvB2VSAKduyK9o7PQUlrZX -H1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXsezx76W0OL -zc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyR -p1RMVwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQz -W7i1o0TJrH93PB0j7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/ -SiOL9V8BY9KHcyi1Swr1+KuCLH5zJTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJn -LNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe8TZBAQIvfXOn3kLMTOmJDVb3 -n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVOHj1tyRRM4y5B -u8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj -o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAO -BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC -AgEATh65isagmD9uw2nAalxJUqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L -9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzxj6ptBZNscsdW699QIyjlRRA96Gej -rw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDtdD+4E5UGUcjohybK -pFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM5gf0 -vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjq -OknkJjCb5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ -/zo1PqVUSlJZS2Db7v54EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ9 -2zg/LFis6ELhDtjTO0wugumDLmsx2d1Hhk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI -+PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGyg77FGr8H6lnco4g175x2 -MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3r5+qPeoo -tt7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= ------END CERTIFICATE----- - -# Issuer: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority -# Subject: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority -# Label: "Certum EC-384 CA" -# Serial: 160250656287871593594747141429395092468 -# MD5 Fingerprint: b6:65:b3:96:60:97:12:a1:ec:4e:e1:3d:a3:c6:c9:f1 -# SHA1 Fingerprint: f3:3e:78:3c:ac:df:f4:a2:cc:ac:67:55:69:56:d7:e5:16:3c:e1:ed -# SHA256 Fingerprint: 6b:32:80:85:62:53:18:aa:50:d1:73:c9:8d:8b:da:09:d5:7e:27:41:3d:11:4c:f7:87:a0:f5:d0:6c:03:0c:f6 ------BEGIN CERTIFICATE----- -MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw -CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw -JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT -EENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0 -WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBT -LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAX -BgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATE -KI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7Tm -Fy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj -QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8 -EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/J -UG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vn -nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= ------END CERTIFICATE----- - -# Issuer: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority -# Subject: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority -# Label: "Certum Trusted Root CA" -# Serial: 40870380103424195783807378461123655149 -# MD5 Fingerprint: 51:e1:c2:e7:fe:4c:84:af:59:0e:2f:f4:54:6f:ea:29 -# SHA1 Fingerprint: c8:83:44:c0:18:ae:9f:cc:f1:87:b7:8f:22:d1:c5:d7:45:84:ba:e5 -# SHA256 Fingerprint: fe:76:96:57:38:55:77:3e:37:a9:5e:7a:d4:d9:cc:96:c3:01:57:c1:5d:31:76:5b:a9:b1:57:04:e1:ae:78:fd ------BEGIN CERTIFICATE----- -MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6 -MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu -MScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNV -BAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwHhcNMTgwMzE2MTIxMDEzWhcNNDMw -MzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEg -U3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRo -b3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZ -n0EGze2jusDbCSzBfN8pfktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/q -p1x4EaTByIVcJdPTsuclzxFUl6s1wB52HO8AU5853BSlLCIls3Jy/I2z5T4IHhQq -NwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2fJmItdUDmj0VDT06qKhF -8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGtg/BKEiJ3 -HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGa -mqi4NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi -7VdNIuJGmj8PkTQkfVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSF -ytKAQd8FqKPVhJBPC/PgP5sZ0jeJP/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0P -qafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSYnjYJdmZm/Bo/6khUHL4wvYBQ -v3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHKHRzQ+8S1h9E6 -Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 -vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQAD -ggIBAEii1QALLtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4 -WxmB82M+w85bj/UvXgF2Ez8sALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvo -zMrnadyHncI013nR03e4qllY/p0m+jiGPp2Kh2RX5Rc64vmNueMzeMGQ2Ljdt4NR -5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8CYyqOhNf6DR5UMEQ -GfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA4kZf -5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq -0Uc9NneoWWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7D -P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTM -qJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP -0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZf -E2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb ------END CERTIFICATE----- - -# Issuer: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique -# Subject: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique -# Label: "TunTrust Root CA" -# Serial: 108534058042236574382096126452369648152337120275 -# MD5 Fingerprint: 85:13:b9:90:5b:36:5c:b6:5e:b8:5a:f8:e0:31:57:b4 -# SHA1 Fingerprint: cf:e9:70:84:0f:e0:73:0f:9d:f6:0c:7f:2c:4b:ee:20:46:34:9c:bb -# SHA256 Fingerprint: 2e:44:10:2a:b5:8c:b8:54:19:45:1c:8e:19:d9:ac:f3:66:2c:af:bc:61:4b:6a:53:96:0a:30:f7:d0:e2:eb:41 ------BEGIN CERTIFICATE----- -MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQEL -BQAwYTELMAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUg -Q2VydGlmaWNhdGlvbiBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJv -b3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQwNDI2MDg1NzU2WjBhMQswCQYDVQQG -EwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBDZXJ0aWZpY2F0aW9u -IEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIwDQYJ -KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZ -n56eY+hz2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd -2JQDoOw05TDENX37Jk0bbjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgF -VwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZ -GoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAdgjH8KcwAWJeRTIAAHDOF -li/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViWVSHbhlnU -r8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2 -eY8fTpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIb -MlEsPvLfe/ZdeikZjuXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISg -jwBUFfyRbVinljvrS5YnzWuioYasDXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB -7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwSVXAkPcvCFDVDXSdOvsC9qnyW -5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI04Y+oXNZtPdE -ITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 -90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+z -xiD2BkewhpMl0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYu -QEkHDVneixCwSQXi/5E/S7fdAo74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4 -FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRYYdZ2vyJ/0Adqp2RT8JeNnYA/u8EH -22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJpadbGNjHh/PqAulxP -xOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65xxBzn -dFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5 -Xc0yGYuPjCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7b -nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQ -CvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZH -u/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujj -d9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= ------END CERTIFICATE----- - -# Issuer: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA -# Subject: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA -# Label: "HARICA TLS RSA Root CA 2021" -# Serial: 76817823531813593706434026085292783742 -# MD5 Fingerprint: 65:47:9b:58:86:dd:2c:f0:fc:a2:84:1f:1e:96:c4:91 -# SHA1 Fingerprint: 02:2d:05:82:fa:88:ce:14:0c:06:79:de:7f:14:10:e9:45:d7:a5:6d -# SHA256 Fingerprint: d9:5d:0e:8e:da:79:52:5b:f9:be:b1:1b:14:d2:10:0d:32:94:98:5f:0c:62:d9:fa:bd:9c:d9:99:ec:cb:7b:1d ------BEGIN CERTIFICATE----- -MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBs -MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl -c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0Eg -Um9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUzOFoXDTQ1MDIxMzEwNTUzN1owbDEL -MAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl -YXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNBIFJv -b3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569l -mwVnlskNJLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE -4VGC/6zStGndLuwRo0Xua2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uv -a9of08WRiFukiZLRgeaMOVig1mlDqa2YUlhu2wr7a89o+uOkXjpFc5gH6l8Cct4M -pbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K5FrZx40d/JiZ+yykgmvw -Kh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEvdmn8kN3b -LW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcY -AuUR0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqB -AGMUuTNe3QvboEUHGjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYq -E613TBoYm5EPWNgGVMWX+Ko/IIqmhaZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHr -W2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQCPxrvrNQKlr9qEgYRtaQQJKQ -CoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE -AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAU -X15QvWiWkKQUEapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3 -f5Z2EMVGpdAgS1D0NTsY9FVqQRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxaja -H6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxDQpSbIPDRzbLrLFPCU3hKTwSUQZqP -JzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcRj88YxeMn/ibvBZ3P -zzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5vZSt -jBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0 -/L5H9MG0qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pT -BGIBnfHAT+7hOtSLIBD6Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79 -aPib8qXPMThcFarmlwDB31qlpzmq6YR/PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YW -xw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnnkf3/W9b3raYvAwtt41dU -63ZTGI0RmLo= ------END CERTIFICATE----- - -# Issuer: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA -# Subject: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA -# Label: "HARICA TLS ECC Root CA 2021" -# Serial: 137515985548005187474074462014555733966 -# MD5 Fingerprint: ae:f7:4c:e5:66:35:d1:b7:9b:8c:22:93:74:d3:4b:b0 -# SHA1 Fingerprint: bc:b0:c1:9d:e9:98:92:70:19:38:57:e9:8d:a7:b4:5d:6e:ee:01:48 -# SHA256 Fingerprint: 3f:99:cc:47:4a:cf:ce:4d:fe:d5:87:94:66:5e:47:8d:15:47:73:9f:2e:78:0f:1b:b4:ca:9b:13:30:97:d4:01 ------BEGIN CERTIFICATE----- -MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQsw -CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh -cmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9v -dCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoXDTQ1MDIxMzExMDEwOVowbDELMAkG -A1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj -aCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJvb3Qg -Q0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7 -KKrxcm1lAEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9Y -STHMmE5gEYd103KUkE+bECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUw -AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQD -AgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNw -SaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygN -nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps ------END CERTIFICATE----- - -# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 -# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 -# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" -# Serial: 1977337328857672817 -# MD5 Fingerprint: 4e:6e:9b:54:4c:ca:b7:fa:48:e4:90:b1:15:4b:1c:a3 -# SHA1 Fingerprint: 0b:be:c2:27:22:49:cb:39:aa:db:35:5c:53:e3:8c:ae:78:ff:b6:fe -# SHA256 Fingerprint: 57:de:05:83:ef:d2:b2:6e:03:61:da:99:da:9d:f4:64:8d:ef:7e:e8:44:1c:3b:72:8a:fa:9b:cd:e0:f9:b2:6a ------BEGIN CERTIFICATE----- -MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE -BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h -cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1 -MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg -Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 -thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM -cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG -L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i -NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h -X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b -m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy -Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja -EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T -KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF -6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh -OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc -tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd -IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j -b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC -AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw -ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m -iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF -Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ -hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P -Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE -EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV -1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t -CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR -5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw -f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9 -ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK -GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV ------END CERTIFICATE----- - -# Issuer: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. -# Subject: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. -# Label: "vTrus ECC Root CA" -# Serial: 630369271402956006249506845124680065938238527194 -# MD5 Fingerprint: de:4b:c1:f5:52:8c:9b:43:e1:3e:8f:55:54:17:8d:85 -# SHA1 Fingerprint: f6:9c:db:b0:fc:f6:02:13:b6:52:32:a6:a3:91:3f:16:70:da:c3:e1 -# SHA256 Fingerprint: 30:fb:ba:2c:32:23:8e:2a:98:54:7a:f9:79:31:e5:50:42:8b:9b:3f:1c:8e:eb:66:33:dc:fa:86:c5:b2:7d:d3 ------BEGIN CERTIFICATE----- -MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMw -RzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAY -BgNVBAMTEXZUcnVzIEVDQyBSb290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDcz -MTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28u -LEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYwEAYHKoZIzj0CAQYF -K4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+cToL0 -v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUd -e4BdS49nTPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYD -VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIw -V53dVvHH4+m4SVBrm2nDb+zDfSXkV5UTQJtS0zvzQBm8JsctBp61ezaf9SXUY2sA -AjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQLYgmRWAD5Tfs0aNoJrSEG -GJTO ------END CERTIFICATE----- - -# Issuer: CN=vTrus Root CA O=iTrusChina Co.,Ltd. -# Subject: CN=vTrus Root CA O=iTrusChina Co.,Ltd. -# Label: "vTrus Root CA" -# Serial: 387574501246983434957692974888460947164905180485 -# MD5 Fingerprint: b8:c9:37:df:fa:6b:31:84:64:c5:ea:11:6a:1b:75:fc -# SHA1 Fingerprint: 84:1a:69:fb:f5:cd:1a:25:34:13:3d:e3:f8:fc:b8:99:d0:c9:14:b7 -# SHA256 Fingerprint: 8a:71:de:65:59:33:6f:42:6c:26:e5:38:80:d0:0d:88:a1:8d:a4:c6:a9:1f:0d:cb:61:94:e2:06:c5:c9:63:87 ------BEGIN CERTIFICATE----- -MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQEL -BQAwQzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4x -FjAUBgNVBAMTDXZUcnVzIFJvb3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMx -MDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoGA1UEChMTaVRydXNDaGluYSBDby4s -THRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZotsSKYc -IrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykU -AyyNJJrIZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+ -GrPSbcKvdmaVayqwlHeFXgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z9 -8Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KAYPxMvDVTAWqXcoKv8R1w6Jz1717CbMdH -flqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70kLJrxLT5ZOrpGgrIDajt -J8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2AXPKBlim -0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZN -pGvu/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQ -UqqzApVg+QxMaPnu1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHW -OXSuTEGC2/KmSNGzm/MzqvOmwMVO9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMB -AAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYgscasGrz2iTAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAKbqSSaet -8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd -nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1j -bhd47F18iMjrjld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvM -Kar5CKXiNxTKsbhm7xqC5PD48acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIiv -TDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJnxDHO2zTlJQNgJXtxmOTAGytfdELS -S8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554WgicEFOwE30z9J4nfr -I8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4sEb9 -b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNB -UvupLnKWnyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1P -Ti07NEPhmg4NpGaXutIcSkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929ven -sBxXVsFy6K2ir40zSbofitzmdHxghm+Hl3s= ------END CERTIFICATE----- - -# Issuer: CN=ISRG Root X2 O=Internet Security Research Group -# Subject: CN=ISRG Root X2 O=Internet Security Research Group -# Label: "ISRG Root X2" -# Serial: 87493402998870891108772069816698636114 -# MD5 Fingerprint: d3:9e:c4:1e:23:3c:a6:df:cf:a3:7e:6d:e0:14:e6:e5 -# SHA1 Fingerprint: bd:b1:b9:3c:d5:97:8d:45:c6:26:14:55:f8:db:95:c7:5a:d1:53:af -# SHA256 Fingerprint: 69:72:9b:8e:15:a8:6e:fc:17:7a:57:af:b7:17:1d:fc:64:ad:d2:8c:2f:ca:8c:f1:50:7e:34:45:3c:cb:14:70 ------BEGIN CERTIFICATE----- -MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw -CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg -R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 -MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT -ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw -EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW -+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 -ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T -AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI -zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW -tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 -/q4AaOeMSQ+2b1tbFfLn ------END CERTIFICATE----- - -# Issuer: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. -# Subject: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. -# Label: "HiPKI Root CA - G1" -# Serial: 60966262342023497858655262305426234976 -# MD5 Fingerprint: 69:45:df:16:65:4b:e8:68:9a:8f:76:5f:ff:80:9e:d3 -# SHA1 Fingerprint: 6a:92:e4:a8:ee:1b:ec:96:45:37:e3:29:57:49:cd:96:e3:e5:d2:60 -# SHA256 Fingerprint: f0:15:ce:3c:c2:39:bf:ef:06:4b:e9:f1:d2:c4:17:e1:a0:26:4a:0a:94:be:1f:0c:8d:12:18:64:eb:69:49:cc ------BEGIN CERTIFICATE----- -MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP -MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 -ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa -Fw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3 -YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kgUm9vdCBDQSAtIEcx -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0o9Qw -qNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twv -Vcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6 -lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnz -Qs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRtU6M9/Aes1MU3guvklQgZ -KILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfdhSi8MEyr48KxRURHH+CK -FgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj1jOXTyFj -HluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDr -y+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ -/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgM -a/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6 -fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV -HQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQDAgGGMA0GCSqG -SIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi -7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqc -SE5XCV0vrPSltJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6Fza -ZsT0pPBWGTMpWmWSBUdGSquEwx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9Tc -XzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07QJNBAsNB1CI69aO4I1258EHBGG3zg -iLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv5wiZqAxeJoBF1Pho -L5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+GpzjLrF -Ne85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wr -kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+ -vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU -YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== ------END CERTIFICATE----- - -# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 -# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 -# Label: "GlobalSign ECC Root CA - R4" -# Serial: 159662223612894884239637590694 -# MD5 Fingerprint: 26:29:f8:6d:e1:88:bf:a2:65:7f:aa:c4:cd:0f:7f:fc -# SHA1 Fingerprint: 6b:a0:b0:98:e1:71:ef:5a:ad:fe:48:15:80:77:10:f4:bd:6f:0b:28 -# SHA256 Fingerprint: b0:85:d7:0b:96:4f:19:1a:73:e4:af:0d:54:ae:7a:0e:07:aa:fd:af:9b:71:dd:08:62:13:8a:b7:32:5a:24:a2 ------BEGIN CERTIFICATE----- -MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD -VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh -bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw -MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g -UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT -BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx -uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV -HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/ -+wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147 -bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm ------END CERTIFICATE----- - -# Issuer: CN=GTS Root R1 O=Google Trust Services LLC -# Subject: CN=GTS Root R1 O=Google Trust Services LLC -# Label: "GTS Root R1" -# Serial: 159662320309726417404178440727 -# MD5 Fingerprint: 05:fe:d0:bf:71:a8:a3:76:63:da:01:e0:d8:52:dc:40 -# SHA1 Fingerprint: e5:8c:1c:c4:91:3b:38:63:4b:e9:10:6e:e3:ad:8e:6b:9d:d9:81:4a -# SHA256 Fingerprint: d9:47:43:2a:bd:e7:b7:fa:90:fc:2e:6b:59:10:1b:12:80:e0:e1:c7:e4:e4:0f:a3:c6:88:7f:ff:57:a7:f4:cf ------BEGIN CERTIFICATE----- -MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw -CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU -MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw -MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp -Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA -A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo -27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w -Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw -TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl -qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH -szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 -Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk -MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 -wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p -aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN -VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID -AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E -FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb -C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe -QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy -h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 -7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J -ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef -MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ -Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT -6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ -0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm -2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb -bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c ------END CERTIFICATE----- - -# Issuer: CN=GTS Root R2 O=Google Trust Services LLC -# Subject: CN=GTS Root R2 O=Google Trust Services LLC -# Label: "GTS Root R2" -# Serial: 159662449406622349769042896298 -# MD5 Fingerprint: 1e:39:c0:53:e6:1e:29:82:0b:ca:52:55:36:5d:57:dc -# SHA1 Fingerprint: 9a:44:49:76:32:db:de:fa:d0:bc:fb:5a:7b:17:bd:9e:56:09:24:94 -# SHA256 Fingerprint: 8d:25:cd:97:22:9d:bf:70:35:6b:da:4e:b3:cc:73:40:31:e2:4c:f0:0f:af:cf:d3:2d:c7:6e:b5:84:1c:7e:a8 ------BEGIN CERTIFICATE----- -MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw -CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU -MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw -MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp -Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA -A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt -nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY -6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu -MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k -RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg -f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV -+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo -dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW -Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa -G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq -gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID -AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E -FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H -vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 -0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC -B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u -NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg -yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev -HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6 -xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR -TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg -JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV -7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl -6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL ------END CERTIFICATE----- - -# Issuer: CN=GTS Root R3 O=Google Trust Services LLC -# Subject: CN=GTS Root R3 O=Google Trust Services LLC -# Label: "GTS Root R3" -# Serial: 159662495401136852707857743206 -# MD5 Fingerprint: 3e:e7:9d:58:02:94:46:51:94:e5:e0:22:4a:8b:e7:73 -# SHA1 Fingerprint: ed:e5:71:80:2b:c8:92:b9:5b:83:3c:d2:32:68:3f:09:cd:a0:1e:46 -# SHA256 Fingerprint: 34:d8:a7:3e:e2:08:d9:bc:db:0d:95:65:20:93:4b:4e:40:e6:94:82:59:6e:8b:6f:73:c8:42:6b:01:0a:6f:48 ------BEGIN CERTIFICATE----- -MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD -VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG -A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw -WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz -IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi -AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G -jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2 -4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW -BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7 -VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm -ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X ------END CERTIFICATE----- - -# Issuer: CN=GTS Root R4 O=Google Trust Services LLC -# Subject: CN=GTS Root R4 O=Google Trust Services LLC -# Label: "GTS Root R4" -# Serial: 159662532700760215368942768210 -# MD5 Fingerprint: 43:96:83:77:19:4d:76:b3:9d:65:52:e4:1d:22:a5:e8 -# SHA1 Fingerprint: 77:d3:03:67:b5:e0:0c:15:f6:0c:38:61:df:7c:e1:3b:92:46:4d:47 -# SHA256 Fingerprint: 34:9d:fa:40:58:c5:e2:63:12:3b:39:8a:e7:95:57:3c:4e:13:13:c8:3f:e6:8f:93:55:6c:d5:e8:03:1b:3c:7d ------BEGIN CERTIFICATE----- -MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD -VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG -A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw -WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz -IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi -AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi -QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR -HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW -BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D -9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 -p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD ------END CERTIFICATE----- - -# Issuer: CN=Telia Root CA v2 O=Telia Finland Oyj -# Subject: CN=Telia Root CA v2 O=Telia Finland Oyj -# Label: "Telia Root CA v2" -# Serial: 7288924052977061235122729490515358 -# MD5 Fingerprint: 0e:8f:ac:aa:82:df:85:b1:f4:dc:10:1c:fc:99:d9:48 -# SHA1 Fingerprint: b9:99:cd:d1:73:50:8a:c4:47:05:08:9c:8c:88:fb:be:a0:2b:40:cd -# SHA256 Fingerprint: 24:2b:69:74:2f:cb:1e:5b:2a:bf:98:89:8b:94:57:21:87:54:4e:5b:4d:99:11:78:65:73:62:1f:6a:74:b8:2c ------BEGIN CERTIFICATE----- -MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx -CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE -AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1 -NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZ -MBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZIhvcNAQEBBQADggIP -ADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ76zBq -AMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9 -vVYiQJ3q9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9 -lRdU2HhE8Qx3FZLgmEKnpNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTOD -n3WhUidhOPFZPY5Q4L15POdslv5e2QJltI5c0BE0312/UqeBAMN/mUWZFdUXyApT -7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW5olWK8jjfN7j/4nlNW4o -6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNrRBH0pUPC -TEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6 -WT0EBXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63R -DolUK5X6wK0dmBR4M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZI -pEYslOqodmJHixBTB0hXbOKSTbauBcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGj -YzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7Wxy+G2CQ5MB0GA1UdDgQWBBRy -rOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw -AwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ -8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi -0f6X+J8wfBj5tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMM -A8iZGok1GTzTyVR8qPAs5m4HeW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBS -SRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+Cy748fdHif64W1lZYudogsYMVoe+K -TTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygCQMez2P2ccGrGKMOF -6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15h2Er -3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMt -Ty3EHD70sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pT -VmBds9hCG1xLEooc6+t9xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAW -ysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQraVplI/owd8k+BsHMYeB2F326CjYSlKA -rBPuUBQemMc= ------END CERTIFICATE----- - -# Issuer: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH -# Subject: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH -# Label: "D-TRUST BR Root CA 1 2020" -# Serial: 165870826978392376648679885835942448534 -# MD5 Fingerprint: b5:aa:4b:d5:ed:f7:e3:55:2e:8f:72:0a:f3:75:b8:ed -# SHA1 Fingerprint: 1f:5b:98:f0:e3:b5:f7:74:3c:ed:e6:b0:36:7d:32:cd:f4:09:41:67 -# SHA256 Fingerprint: e5:9a:aa:81:60:09:c2:2b:ff:5b:25:ba:d3:7d:f3:06:f0:49:79:7c:1f:81:d8:5a:b0:89:e6:57:bd:8f:00:44 ------BEGIN CERTIFICATE----- -MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw -CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS -VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5 -NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG -A1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB -BAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7dPYS -zuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0 -QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/ -VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g -PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3Rf -Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l -dC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 -c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO -PQQDAwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFW -wKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHV -dWNbFJWcHwHP2NVypw87 ------END CERTIFICATE----- - -# Issuer: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH -# Subject: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH -# Label: "D-TRUST EV Root CA 1 2020" -# Serial: 126288379621884218666039612629459926992 -# MD5 Fingerprint: 8c:2d:9d:70:9f:48:99:11:06:11:fb:e9:cb:30:c0:6e -# SHA1 Fingerprint: 61:db:8c:21:59:69:03:90:d8:7c:9c:12:86:54:cf:9d:3d:f4:dd:07 -# SHA256 Fingerprint: 08:17:0d:1a:a3:64:53:90:1a:2f:95:92:45:e3:47:db:0c:8d:37:ab:aa:bc:56:b8:1a:a1:00:dc:95:89:70:db ------BEGIN CERTIFICATE----- -MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw -CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS -VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5 -NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG -A1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB -BAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8ZRCC -/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rD -wpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3 -OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g -PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3Rf -Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l -dC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 -c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO -PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA -y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb -gfM0agPnIjhQW+0ZT0MW ------END CERTIFICATE----- - -# Issuer: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. -# Subject: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. -# Label: "DigiCert TLS ECC P384 Root G5" -# Serial: 13129116028163249804115411775095713523 -# MD5 Fingerprint: d3:71:04:6a:43:1c:db:a6:59:e1:a8:a3:aa:c5:71:ed -# SHA1 Fingerprint: 17:f3:de:5e:9f:0f:19:e9:8e:f6:1f:32:26:6e:20:c4:07:ae:30:ee -# SHA256 Fingerprint: 01:8e:13:f0:77:25:32:cf:80:9b:d1:b1:72:81:86:72:83:fc:48:c6:e1:3b:e9:c6:98:12:85:4a:49:0c:1b:05 ------BEGIN CERTIFICATE----- -MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp -Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2 -MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ -bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG -ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS -7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp -0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS -B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 -BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ -LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4 -DXZDjC5Ty3zfDBeWUA== ------END CERTIFICATE----- - -# Issuer: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. -# Subject: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. -# Label: "DigiCert TLS RSA4096 Root G5" -# Serial: 11930366277458970227240571539258396554 -# MD5 Fingerprint: ac:fe:f7:34:96:a9:f2:b3:b4:12:4b:e4:27:41:6f:e1 -# SHA1 Fingerprint: a7:88:49:dc:5d:7c:75:8c:8c:de:39:98:56:b3:aa:d0:b2:a5:71:35 -# SHA256 Fingerprint: 37:1a:00:dc:05:33:b3:72:1a:7e:eb:40:e8:41:9e:70:79:9d:2b:0a:0f:2c:1d:80:69:31:65:f7:ce:c4:ad:75 ------BEGIN CERTIFICATE----- -MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN -MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT -HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN -NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs -IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+ -ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0 -2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp -wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM -pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD -nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po -sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx -Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd -Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX -KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe -XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL -tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv -TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN -AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw -GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H -PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF -O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ -REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik -AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv -/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+ -p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw -MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF -qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK -ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+ ------END CERTIFICATE----- - -# Issuer: CN=Certainly Root R1 O=Certainly -# Subject: CN=Certainly Root R1 O=Certainly -# Label: "Certainly Root R1" -# Serial: 188833316161142517227353805653483829216 -# MD5 Fingerprint: 07:70:d4:3e:82:87:a0:fa:33:36:13:f4:fa:33:e7:12 -# SHA1 Fingerprint: a0:50:ee:0f:28:71:f4:27:b2:12:6d:6f:50:96:25:ba:cc:86:42:af -# SHA256 Fingerprint: 77:b8:2c:d8:64:4c:43:05:f7:ac:c5:cb:15:6b:45:67:50:04:03:3d:51:c6:0c:62:02:a8:e0:c3:34:67:d3:a0 ------BEGIN CERTIFICATE----- -MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw -PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy -dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9 -MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0 -YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANA2 -1B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O5MQT -vqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbed -aFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b0 -1C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5 -r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5 -cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQ -wHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ -6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA -2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyH -Wyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMR -eiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB -/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9EXME66C6u -d0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr -PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d -8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi -1wrykXprOQ4vMMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrd -rRT90+7iIgXr0PK3aBLXWopBGsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9di -taY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7 -lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgHJBu6haEaBQmAupVj -yTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7fpYn -Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy -yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n -wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6 -OV+KmalBWQewLK8= ------END CERTIFICATE----- - -# Issuer: CN=Certainly Root E1 O=Certainly -# Subject: CN=Certainly Root E1 O=Certainly -# Label: "Certainly Root E1" -# Serial: 8168531406727139161245376702891150584 -# MD5 Fingerprint: 0a:9e:ca:cd:3e:52:50:c6:36:f3:4b:a3:ed:a7:53:e9 -# SHA1 Fingerprint: f9:e1:6d:dc:01:89:cf:d5:82:45:63:3e:c5:37:7d:c2:eb:93:6f:2b -# SHA256 Fingerprint: b4:58:5f:22:e4:ac:75:6a:4e:86:12:a1:36:1c:5d:9d:03:1a:93:fd:84:fe:bb:77:8f:a3:06:8b:0f:c4:2d:c2 ------BEGIN CERTIFICATE----- -MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw -CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu -bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ -BgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5s -eSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4fxzf7flHh4axpMCK -+IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9YBk2 -QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4 -hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm -ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG -BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR ------END CERTIFICATE----- - -# Issuer: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. -# Subject: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. -# Label: "Security Communication ECC RootCA1" -# Serial: 15446673492073852651 -# MD5 Fingerprint: 7e:43:b0:92:68:ec:05:43:4c:98:ab:5d:35:2e:7e:86 -# SHA1 Fingerprint: b8:0e:26:a9:bf:d2:b2:3b:c0:ef:46:c9:ba:c7:bb:f6:1d:0d:41:41 -# SHA256 Fingerprint: e7:4f:bd:a5:5b:d5:64:c4:73:a3:6b:44:1a:a7:99:c8:a6:8e:07:74:40:e8:28:8b:9f:a1:e5:0e:4b:ba:ca:11 ------BEGIN CERTIFICATE----- -MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT -AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD -VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx -NjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoT -HFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNVBAMTIlNlY3VyaXR5 -IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNi -AASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+Cnnfdl -dB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpK -ULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu -9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O -be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k= ------END CERTIFICATE----- - -# Issuer: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY -# Subject: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY -# Label: "BJCA Global Root CA1" -# Serial: 113562791157148395269083148143378328608 -# MD5 Fingerprint: 42:32:99:76:43:33:36:24:35:07:82:9b:28:f9:d0:90 -# SHA1 Fingerprint: d5:ec:8d:7b:4c:ba:79:f4:e7:e8:cb:9d:6b:ae:77:83:10:03:21:6a -# SHA256 Fingerprint: f3:89:6f:88:fe:7c:0a:88:27:66:a7:fa:6a:d2:74:9f:b5:7a:7f:3e:98:fb:76:9c:1f:a7:b0:9c:2c:44:d5:ae ------BEGIN CERTIFICATE----- -MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBU -MQswCQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRI -T1JJVFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAz -MTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJF -SUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2Jh -bCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFmCL3Z -xRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZ -spDyRhySsTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O5 -58dnJCNPYwpj9mZ9S1WnP3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgR -at7GGPZHOiJBhyL8xIkoVNiMpTAK+BcWyqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll -5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRjeulumijWML3mG90Vr4Tq -nMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNnMoH1V6XK -V0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/ -pj+bOT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZO -z2nxbkRs1CTqjSShGL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXn -jSXWgXSHRtQpdaJCbPdzied9v3pKH9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+ -WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMBAAGjQjBAMB0GA1UdDgQWBBTF -7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE -AwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4 -YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3Kli -awLwQ8hOnThJdMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u -+2D2/VnGKhs/I0qUJDAnyIm860Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88 -X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuhTaRjAv04l5U/BXCga99igUOLtFkN -SoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW4AB+dAb/OMRyHdOo -P2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmpGQrI -+pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRz -znfSxqxx4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9 -eVzYH6Eze9mCUAyTF6ps3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2 -YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4SSPfSKcOYKMryMguTjClPPGAyzQWWYezy -r/6zcCwupvI= ------END CERTIFICATE----- - -# Issuer: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY -# Subject: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY -# Label: "BJCA Global Root CA2" -# Serial: 58605626836079930195615843123109055211 -# MD5 Fingerprint: 5e:0a:f6:47:5f:a6:14:e8:11:01:95:3f:4d:01:eb:3c -# SHA1 Fingerprint: f4:27:86:eb:6e:b8:6d:88:31:67:02:fb:ba:66:a4:53:00:aa:7a:a6 -# SHA256 Fingerprint: 57:4d:f6:93:1e:27:80:39:66:7b:72:0a:fd:c1:60:0f:c2:7e:b6:6d:d3:09:29:79:fb:73:85:64:87:21:28:82 ------BEGIN CERTIFICATE----- -MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQsw -CQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJ -VFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgy -MVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJ -TkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBS -b290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jlSR9B -IgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK+ -+kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJK -sVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD -AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA -94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B -43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w== ------END CERTIFICATE----- - -# Issuer: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited -# Subject: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited -# Label: "Sectigo Public Server Authentication Root E46" -# Serial: 88989738453351742415770396670917916916 -# MD5 Fingerprint: 28:23:f8:b2:98:5c:37:16:3b:3e:46:13:4e:b0:b3:01 -# SHA1 Fingerprint: ec:8a:39:6c:40:f0:2e:bc:42:75:d4:9f:ab:1c:1a:5b:67:be:d2:9a -# SHA256 Fingerprint: c9:0f:26:f0:fb:1b:40:18:b2:22:27:51:9b:5c:a2:b5:3e:2c:a5:b3:be:5c:f1:8e:fe:1b:ef:47:38:0c:53:83 ------BEGIN CERTIFICATE----- -MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw -CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T -ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN -MjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYG -A1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT -ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA -IgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccC -WvkEN/U0NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+ -6xnOQ6OjQjBAMB0GA1UdDgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8B -Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjAn7qRa -qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q -4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21USAGKcw== ------END CERTIFICATE----- - -# Issuer: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited -# Subject: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited -# Label: "Sectigo Public Server Authentication Root R46" -# Serial: 156256931880233212765902055439220583700 -# MD5 Fingerprint: 32:10:09:52:00:d5:7e:6c:43:df:15:c0:b1:16:93:e5 -# SHA1 Fingerprint: ad:98:f9:f3:e4:7d:75:3b:65:d4:82:b3:a4:52:17:bb:6e:f5:e4:38 -# SHA256 Fingerprint: 7b:b6:47:a6:2a:ee:ac:88:bf:25:7a:a5:22:d0:1f:fe:a3:95:e0:ab:45:c7:3f:93:f6:56:54:ec:38:f2:5a:06 ------BEGIN CERTIFICATE----- -MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf -MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD -Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw -HhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEY -MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1Ymxp -YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDa -ef0rty2k1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnz -SDBh+oF8HqcIStw+KxwfGExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xf -iOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMPFF1bFOdLvt30yNoDN9HWOaEhUTCDsG3X -ME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vuZDCQOc2TZYEhMbUjUDM3 -IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5QazYw6A3OAS -VYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgE -SJ/AwSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu -+Zd4KKTIRJLpfSYFplhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt -8uaZFURww3y8nDnAtOFr94MlI1fZEoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+L -HaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW6aWWrL3DkJiy4Pmi1KZHQ3xt -zwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWIIUkwDgYDVR0P -AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c -mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQ -YKlJfp/imTYpE0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52 -gDY9hAaLMyZlbcp+nv4fjFg4exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZA -Fv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M0ejf5lG5Nkc/kLnHvALcWxxPDkjB -JYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI84HxZmduTILA7rpX -DhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9mpFui -TdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5 -dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65 -LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp -0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY -QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL ------END CERTIFICATE----- - -# Issuer: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation -# Subject: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation -# Label: "SSL.com TLS RSA Root CA 2022" -# Serial: 148535279242832292258835760425842727825 -# MD5 Fingerprint: d8:4e:c6:59:30:d8:fe:a0:d6:7a:5a:2c:2c:69:78:da -# SHA1 Fingerprint: ec:2c:83:40:72:af:26:95:10:ff:0e:f2:03:ee:31:70:f6:78:9d:ca -# SHA256 Fingerprint: 8f:af:7d:2e:2c:b4:70:9b:b8:e0:b3:36:66:bf:75:a5:dd:45:b5:de:48:0f:8e:a8:d4:bf:e6:be:bc:17:f2:ed ------BEGIN CERTIFICATE----- -MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBO -MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD -DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloX -DTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jw -b3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJvb3QgQ0EgMjAyMjCC -AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u9nTP -L3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OY -t6/wNr/y7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0ins -S657Lb85/bRi3pZ7QcacoOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3 -PnxEX4MN8/HdIGkWCVDi1FW24IBydm5MR7d1VVm0U3TZlMZBrViKMWYPHqIbKUBO -L9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDGD6C1vBdOSHtRwvzpXGk3 -R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEWTO6Af77w -dr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS -+YCk8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYS -d66UNHsef8JmAOSqg+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoG -AtUjHBPW6dvbxrB6y3snm/vg1UYk7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2f -gTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j -BBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsuN+7jhHonLs0Z -NbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt -hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsM -QtfhWsSWTVTNj8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvf -R4iyrT7gJ4eLSYwfqUdYe5byiB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJ -DPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjUo3KUQyxi4U5cMj29TH0ZR6LDSeeW -P4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqoENjwuSfr98t67wVy -lrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7EgkaibMOlq -bLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2w -AgDHbICivRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3q -r5nsLFR+jM4uElZI7xc7P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sji -Mho6/4UIyYOf8kpIEFR3N+2ivEC+5BB09+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU -98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA= ------END CERTIFICATE----- - -# Issuer: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation -# Subject: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation -# Label: "SSL.com TLS ECC Root CA 2022" -# Serial: 26605119622390491762507526719404364228 -# MD5 Fingerprint: 99:d7:5c:f1:51:36:cc:e9:ce:d9:19:2e:77:71:56:c5 -# SHA1 Fingerprint: 9f:5f:d9:1a:54:6d:f5:0c:71:f0:ee:7a:bd:17:49:98:84:73:e2:39 -# SHA256 Fingerprint: c3:2f:fd:9f:46:f9:36:d1:6c:36:73:99:09:59:43:4b:9a:d6:0a:af:bb:9e:7c:f3:36:54:f1:44:cc:1b:a1:43 ------BEGIN CERTIFICATE----- -MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw -CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT -U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2 -MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3Jh -dGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3QgQ0EgMjAyMjB2MBAG -ByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWyJGYm -acCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFN -SeR7T5v15wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME -GDAWgBSJjy+j6CugFFR781a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NW -uCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp -15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w7deedWo1dlJF4AIxAMeN -b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g== ------END CERTIFICATE----- - -# Issuer: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos -# Subject: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos -# Label: "Atos TrustedRoot Root CA ECC TLS 2021" -# Serial: 81873346711060652204712539181482831616 -# MD5 Fingerprint: 16:9f:ad:f1:70:ad:79:d6:ed:29:b4:d1:c5:79:70:a8 -# SHA1 Fingerprint: 9e:bc:75:10:42:b3:02:f3:81:f4:f7:30:62:d4:8f:c3:a7:51:b2:dd -# SHA256 Fingerprint: b2:fa:e5:3e:14:cc:d7:ab:92:12:06:47:01:ae:27:9c:1d:89:88:fa:cb:77:5f:a8:a0:08:91:4e:66:39:88:a8 ------BEGIN CERTIFICATE----- -MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4w -LAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0w -CwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0 -MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBF -Q0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMHYwEAYHKoZI -zj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6KDP/X -tXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4 -AjJn8ZQSb+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2 -KCXWfeBmmnoJsmo7jjPXNtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMD -aAAwZQIwW5kp85wxtolrbNa9d+F851F+uDrNozZffPc8dz7kUK2o59JZDCaOMDtu -CCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGYa3cpetskz2VAv9LcjBHo -9H1/IISpQuQo ------END CERTIFICATE----- - -# Issuer: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos -# Subject: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos -# Label: "Atos TrustedRoot Root CA RSA TLS 2021" -# Serial: 111436099570196163832749341232207667876 -# MD5 Fingerprint: d4:d3:46:b8:9a:c0:9c:76:5d:9e:3a:c3:b9:99:31:d2 -# SHA1 Fingerprint: 18:52:3b:0d:06:37:e4:d6:3a:df:23:e4:98:fb:5b:16:fb:86:74:48 -# SHA256 Fingerprint: 81:a9:08:8e:a5:9f:b3:64:c5:48:a6:f8:55:59:09:9b:6f:04:05:ef:bf:18:e5:32:4e:c9:f4:57:ba:00:11:2f ------BEGIN CERTIFICATE----- -MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBM -MS4wLAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIx -MQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00 -MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBD -QSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMIICIjAN -BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BBl01Z -4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYv -Ye+W/CBGvevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZ -kmGbzSoXfduP9LVq6hdKZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDs -GY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt0xU6kGpn8bRrZtkh68rZYnxGEFzedUln -nkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVKPNe0OwANwI8f4UDErmwh -3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMYsluMWuPD -0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzy -geBYBr3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8 -ANSbhqRAvNncTFd+rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezB -c6eUWsuSZIKmAMFwoW4sKeFYV+xafJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lI -pw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU -dEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB -DAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS -4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPs -o0UvFJ/1TCplQ3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJ -qM7F78PRreBrAwA0JrRUITWXAdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuyw -xfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9GslA9hGCZcbUztVdF5kJHdWoOsAgM -rr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2VktafcxBPTy+av5EzH4 -AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9qTFsR -0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuY -o7Ey7Nmj1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5 -dDTedk+SKlOxJTnbPP/lPqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcE -oji2jbDwN/zIIX8/syQbPYtuzE2wFg2WHYMfRsCbvUOZ58SWLs5fyQ== ------END CERTIFICATE----- - -# Issuer: CN=TrustAsia Global Root CA G3 O=TrustAsia Technologies, Inc. -# Subject: CN=TrustAsia Global Root CA G3 O=TrustAsia Technologies, Inc. -# Label: "TrustAsia Global Root CA G3" -# Serial: 576386314500428537169965010905813481816650257167 -# MD5 Fingerprint: 30:42:1b:b7:bb:81:75:35:e4:16:4f:53:d2:94:de:04 -# SHA1 Fingerprint: 63:cf:b6:c1:27:2b:56:e4:88:8e:1c:23:9a:b6:2e:81:47:24:c3:c7 -# SHA256 Fingerprint: e0:d3:22:6a:eb:11:63:c2:e4:8f:f9:be:3b:50:b4:c6:43:1b:e7:bb:1e:ac:c5:c3:6b:5d:5e:c5:09:03:9a:08 ------BEGIN CERTIFICATE----- -MIIFpTCCA42gAwIBAgIUZPYOZXdhaqs7tOqFhLuxibhxkw8wDQYJKoZIhvcNAQEM -BQAwWjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dp -ZXMsIEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHMzAe -Fw0yMTA1MjAwMjEwMTlaFw00NjA1MTkwMjEwMTlaMFoxCzAJBgNVBAYTAkNOMSUw -IwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtU -cnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4IC -DwAwggIKAoICAQDAMYJhkuSUGwoqZdC+BqmHO1ES6nBBruL7dOoKjbmzTNyPtxNS -T1QY4SxzlZHFZjtqz6xjbYdT8PfxObegQ2OwxANdV6nnRM7EoYNl9lA+sX4WuDqK -AtCWHwDNBSHvBm3dIZwZQ0WhxeiAysKtQGIXBsaqvPPW5vxQfmZCHzyLpnl5hkA1 -nyDvP+uLRx+PjsXUjrYsyUQE49RDdT/VP68czH5GX6zfZBCK70bwkPAPLfSIC7Ep -qq+FqklYqL9joDiR5rPmd2jE+SoZhLsO4fWvieylL1AgdB4SQXMeJNnKziyhWTXA -yB1GJ2Faj/lN03J5Zh6fFZAhLf3ti1ZwA0pJPn9pMRJpxx5cynoTi+jm9WAPzJMs -hH/x/Gr8m0ed262IPfN2dTPXS6TIi/n1Q1hPy8gDVI+lhXgEGvNz8teHHUGf59gX -zhqcD0r83ERoVGjiQTz+LISGNzzNPy+i2+f3VANfWdP3kXjHi3dqFuVJhZBFcnAv -kV34PmVACxmZySYgWmjBNb9Pp1Hx2BErW+Canig7CjoKH8GB5S7wprlppYiU5msT -f9FkPz2ccEblooV7WIQn3MSAPmeamseaMQ4w7OYXQJXZRe0Blqq/DPNL0WP3E1jA -uPP6Z92bfW1K/zJMtSU7/xxnD4UiWQWRkUF3gdCFTIcQcf+eQxuulXUtgQIDAQAB -o2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEDk5PIj7zjKsK5Xf/Ih -MBY027ySMB0GA1UdDgQWBBRA5OTyI+84yrCuV3/yITAWNNu8kjAOBgNVHQ8BAf8E -BAMCAQYwDQYJKoZIhvcNAQEMBQADggIBACY7UeFNOPMyGLS0XuFlXsSUT9SnYaP4 -wM8zAQLpw6o1D/GUE3d3NZ4tVlFEbuHGLige/9rsR82XRBf34EzC4Xx8MnpmyFq2 -XFNFV1pF1AWZLy4jVe5jaN/TG3inEpQGAHUNcoTpLrxaatXeL1nHo+zSh2bbt1S1 -JKv0Q3jbSwTEb93mPmY+KfJLaHEih6D4sTNjduMNhXJEIlU/HHzp/LgV6FL6qj6j -ITk1dImmasI5+njPtqzn59ZW/yOSLlALqbUHM/Q4X6RJpstlcHboCoWASzY9M/eV -VHUl2qzEc4Jl6VL1XP04lQJqaTDFHApXB64ipCz5xUG3uOyfT0gA+QEEVcys+TIx -xHWVBqB/0Y0n3bOppHKH/lmLmnp0Ft0WpWIp6zqW3IunaFnT63eROfjXy9mPX1on -AX1daBli2MjN9LdyR75bl87yraKZk62Uy5P2EgmVtqvXO9A/EcswFi55gORngS1d -7XB4tmBZrOFdRWOPyN9yaFvqHbgB8X7754qz41SgOAngPN5C8sLtLpvzHzW2Ntjj -gKGLzZlkD8Kqq7HK9W+eQ42EVJmzbsASZthwEPEGNTNDqJwuuhQxzhB/HIbjj9LV -+Hfsm6vxL2PZQl/gZ4FkkfGXL/xuJvYz+NO1+MRiqzFRJQJ6+N1rZdVtTTDIZbpo -FGWsJwt0ivKH ------END CERTIFICATE----- - -# Issuer: CN=TrustAsia Global Root CA G4 O=TrustAsia Technologies, Inc. -# Subject: CN=TrustAsia Global Root CA G4 O=TrustAsia Technologies, Inc. -# Label: "TrustAsia Global Root CA G4" -# Serial: 451799571007117016466790293371524403291602933463 -# MD5 Fingerprint: 54:dd:b2:d7:5f:d8:3e:ed:7c:e0:0b:2e:cc:ed:eb:eb -# SHA1 Fingerprint: 57:73:a5:61:5d:80:b2:e6:ac:38:82:fc:68:07:31:ac:9f:b5:92:5a -# SHA256 Fingerprint: be:4b:56:cb:50:56:c0:13:6a:52:6d:f4:44:50:8d:aa:36:a0:b5:4f:42:e4:ac:38:f7:2a:f4:70:e4:79:65:4c ------BEGIN CERTIFICATE----- -MIICVTCCAdygAwIBAgIUTyNkuI6XY57GU4HBdk7LKnQV1tcwCgYIKoZIzj0EAwMw -WjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs -IEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHNDAeFw0y -MTA1MjAwMjEwMjJaFw00NjA1MTkwMjEwMjJaMFoxCzAJBgNVBAYTAkNOMSUwIwYD -VQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtUcnVz -dEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATx -s8045CVD5d4ZCbuBeaIVXxVjAd7Cq92zphtnS4CDr5nLrBfbK5bKfFJV4hrhPVbw -LxYI+hW8m7tH5j/uqOFMjPXTNvk4XatwmkcN4oFBButJ+bAp3TPsUKV/eSm4IJij -YzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUpbtKl86zK3+kMd6Xg1mD -pm9xy94wHQYDVR0OBBYEFKW7SpfOsyt/pDHel4NZg6ZvccveMA4GA1UdDwEB/wQE -AwIBBjAKBggqhkjOPQQDAwNnADBkAjBe8usGzEkxn0AAbbd+NvBNEU/zy4k6LHiR -UKNbwMp1JvK/kF0LgoxgKJ/GcJpo5PECMFxYDlZ2z1jD1xCMuo6u47xkdUfFVZDj -/bpV6wfEU6s3qe4hsiFbYI89MvHVI5TWWA== ------END CERTIFICATE----- - -# Issuer: CN=CommScope Public Trust ECC Root-01 O=CommScope -# Subject: CN=CommScope Public Trust ECC Root-01 O=CommScope -# Label: "CommScope Public Trust ECC Root-01" -# Serial: 385011430473757362783587124273108818652468453534 -# MD5 Fingerprint: 3a:40:a7:fc:03:8c:9c:38:79:2f:3a:a2:6c:b6:0a:16 -# SHA1 Fingerprint: 07:86:c0:d8:dd:8e:c0:80:98:06:98:d0:58:7a:ef:de:a6:cc:a2:5d -# SHA256 Fingerprint: 11:43:7c:da:7b:b4:5e:41:36:5f:45:b3:9a:38:98:6b:0d:e0:0d:ef:34:8e:0c:7b:b0:87:36:33:80:0b:c3:8b ------BEGIN CERTIFICATE----- -MIICHTCCAaOgAwIBAgIUQ3CCd89NXTTxyq4yLzf39H91oJ4wCgYIKoZIzj0EAwMw -TjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29t -bVNjb3BlIFB1YmxpYyBUcnVzdCBFQ0MgUm9vdC0wMTAeFw0yMTA0MjgxNzM1NDNa -Fw00NjA0MjgxNzM1NDJaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2Nv -cGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDEw -djAQBgcqhkjOPQIBBgUrgQQAIgNiAARLNumuV16ocNfQj3Rid8NeeqrltqLxeP0C -flfdkXmcbLlSiFS8LwS+uM32ENEp7LXQoMPwiXAZu1FlxUOcw5tjnSCDPgYLpkJE -hRGnSjot6dZoL0hOUysHP029uax3OVejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD -VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSOB2LAUN3GGQYARnQE9/OufXVNMDAKBggq -hkjOPQQDAwNoADBlAjEAnDPfQeMjqEI2Jpc1XHvr20v4qotzVRVcrHgpD7oh2MSg -2NED3W3ROT3Ek2DS43KyAjB8xX6I01D1HiXo+k515liWpDVfG2XqYZpwI7UNo5uS -Um9poIyNStDuiw7LR47QjRE= ------END CERTIFICATE----- - -# Issuer: CN=CommScope Public Trust ECC Root-02 O=CommScope -# Subject: CN=CommScope Public Trust ECC Root-02 O=CommScope -# Label: "CommScope Public Trust ECC Root-02" -# Serial: 234015080301808452132356021271193974922492992893 -# MD5 Fingerprint: 59:b0:44:d5:65:4d:b8:5c:55:19:92:02:b6:d1:94:b2 -# SHA1 Fingerprint: 3c:3f:ef:57:0f:fe:65:93:86:9e:a0:fe:b0:f6:ed:8e:d1:13:c7:e5 -# SHA256 Fingerprint: 2f:fb:7f:81:3b:bb:b3:c8:9a:b4:e8:16:2d:0f:16:d7:15:09:a8:30:cc:9d:73:c2:62:e5:14:08:75:d1:ad:4a ------BEGIN CERTIFICATE----- -MIICHDCCAaOgAwIBAgIUKP2ZYEFHpgE6yhR7H+/5aAiDXX0wCgYIKoZIzj0EAwMw -TjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29t -bVNjb3BlIFB1YmxpYyBUcnVzdCBFQ0MgUm9vdC0wMjAeFw0yMTA0MjgxNzQ0NTRa -Fw00NjA0MjgxNzQ0NTNaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2Nv -cGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDIw -djAQBgcqhkjOPQIBBgUrgQQAIgNiAAR4MIHoYx7l63FRD/cHB8o5mXxO1Q/MMDAL -j2aTPs+9xYa9+bG3tD60B8jzljHz7aRP+KNOjSkVWLjVb3/ubCK1sK9IRQq9qEmU -v4RDsNuESgMjGWdqb8FuvAY5N9GIIvejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD -VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTmGHX/72DehKT1RsfeSlXjMjZ59TAKBggq -hkjOPQQDAwNnADBkAjAmc0l6tqvmSfR9Uj/UQQSugEODZXW5hYA4O9Zv5JOGq4/n -ich/m35rChJVYaoR4HkCMHfoMXGsPHED1oQmHhS48zs73u1Z/GtMMH9ZzkXpc2AV -mkzw5l4lIhVtwodZ0LKOag== ------END CERTIFICATE----- - -# Issuer: CN=CommScope Public Trust RSA Root-01 O=CommScope -# Subject: CN=CommScope Public Trust RSA Root-01 O=CommScope -# Label: "CommScope Public Trust RSA Root-01" -# Serial: 354030733275608256394402989253558293562031411421 -# MD5 Fingerprint: 0e:b4:15:bc:87:63:5d:5d:02:73:d4:26:38:68:73:d8 -# SHA1 Fingerprint: 6d:0a:5f:f7:b4:23:06:b4:85:b3:b7:97:64:fc:ac:75:f5:33:f2:93 -# SHA256 Fingerprint: 02:bd:f9:6e:2a:45:dd:9b:f1:8f:c7:e1:db:df:21:a0:37:9b:a3:c9:c2:61:03:44:cf:d8:d6:06:fe:c1:ed:81 ------BEGIN CERTIFICATE----- -MIIFbDCCA1SgAwIBAgIUPgNJgXUWdDGOTKvVxZAplsU5EN0wDQYJKoZIhvcNAQEL -BQAwTjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwi -Q29tbVNjb3BlIFB1YmxpYyBUcnVzdCBSU0EgUm9vdC0wMTAeFw0yMTA0MjgxNjQ1 -NTRaFw00NjA0MjgxNjQ1NTNaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21t -U2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3Qt -MDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwSGWjDR1C45FtnYSk -YZYSwu3D2iM0GXb26v1VWvZVAVMP8syMl0+5UMuzAURWlv2bKOx7dAvnQmtVzslh -suitQDy6uUEKBU8bJoWPQ7VAtYXR1HHcg0Hz9kXHgKKEUJdGzqAMxGBWBB0HW0al -DrJLpA6lfO741GIDuZNqihS4cPgugkY4Iw50x2tBt9Apo52AsH53k2NC+zSDO3Oj -WiE260f6GBfZumbCk6SP/F2krfxQapWsvCQz0b2If4b19bJzKo98rwjyGpg/qYFl -P8GMicWWMJoKz/TUyDTtnS+8jTiGU+6Xn6myY5QXjQ/cZip8UlF1y5mO6D1cv547 -KI2DAg+pn3LiLCuz3GaXAEDQpFSOm117RTYm1nJD68/A6g3czhLmfTifBSeolz7p -UcZsBSjBAg/pGG3svZwG1KdJ9FQFa2ww8esD1eo9anbCyxooSU1/ZOD6K9pzg4H/ -kQO9lLvkuI6cMmPNn7togbGEW682v3fuHX/3SZtS7NJ3Wn2RnU3COS3kuoL4b/JO -Hg9O5j9ZpSPcPYeoKFgo0fEbNttPxP/hjFtyjMcmAyejOQoBqsCyMWCDIqFPEgkB -Ea801M/XrmLTBQe0MXXgDW1XT2mH+VepuhX2yFJtocucH+X8eKg1mp9BFM6ltM6U -CBwJrVbl2rZJmkrqYxhTnCwuwwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G -A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUN12mmnQywsL5x6YVEFm45P3luG0wDQYJ -KoZIhvcNAQELBQADggIBAK+nz97/4L1CjU3lIpbfaOp9TSp90K09FlxD533Ahuh6 -NWPxzIHIxgvoLlI1pKZJkGNRrDSsBTtXAOnTYtPZKdVUvhwQkZyybf5Z/Xn36lbQ -nmhUQo8mUuJM3y+Xpi/SB5io82BdS5pYV4jvguX6r2yBS5KPQJqTRlnLX3gWsWc+ -QgvfKNmwrZggvkN80V4aCRckjXtdlemrwWCrWxhkgPut4AZ9HcpZuPN4KWfGVh2v -trV0KnahP/t1MJ+UXjulYPPLXAziDslg+MkfFoom3ecnf+slpoq9uC02EJqxWE2a -aE9gVOX2RhOOiKy8IUISrcZKiX2bwdgt6ZYD9KJ0DLwAHb/WNyVntHKLr4W96ioD -j8z7PEQkguIBpQtZtjSNMgsSDesnwv1B10A8ckYpwIzqug/xBpMu95yo9GA+o/E4 -Xo4TwbM6l4c/ksp4qRyv0LAbJh6+cOx69TOY6lz/KwsETkPdY34Op054A5U+1C0w -lREQKC6/oAI+/15Z0wUOlV9TRe9rh9VIzRamloPh37MG88EU26fsHItdkJANclHn -YfkUyq+Dj7+vsQpZXdxc1+SWrVtgHdqul7I52Qb1dgAT+GhMIbA1xNxVssnBQVoc -icCMb3SgazNNtQEo/a2tiRc7ppqEvOuM6sRxJKi6KfkIsidWNTJf6jn7MZrVGczw ------END CERTIFICATE----- - -# Issuer: CN=CommScope Public Trust RSA Root-02 O=CommScope -# Subject: CN=CommScope Public Trust RSA Root-02 O=CommScope -# Label: "CommScope Public Trust RSA Root-02" -# Serial: 480062499834624527752716769107743131258796508494 -# MD5 Fingerprint: e1:29:f9:62:7b:76:e2:96:6d:f3:d4:d7:0f:ae:1f:aa -# SHA1 Fingerprint: ea:b0:e2:52:1b:89:93:4c:11:68:f2:d8:9a:ac:22:4c:a3:8a:57:ae -# SHA256 Fingerprint: ff:e9:43:d7:93:42:4b:4f:7c:44:0c:1c:3d:64:8d:53:63:f3:4b:82:dc:87:aa:7a:9f:11:8f:c5:de:e1:01:f1 ------BEGIN CERTIFICATE----- -MIIFbDCCA1SgAwIBAgIUVBa/O345lXGN0aoApYYNK496BU4wDQYJKoZIhvcNAQEL -BQAwTjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwi -Q29tbVNjb3BlIFB1YmxpYyBUcnVzdCBSU0EgUm9vdC0wMjAeFw0yMTA0MjgxNzE2 -NDNaFw00NjA0MjgxNzE2NDJaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21t -U2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3Qt -MDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDh+g77aAASyE3VrCLE -NQE7xVTlWXZjpX/rwcRqmL0yjReA61260WI9JSMZNRTpf4mnG2I81lDnNJUDMrG0 -kyI9p+Kx7eZ7Ti6Hmw0zdQreqjXnfuU2mKKuJZ6VszKWpCtYHu8//mI0SFHRtI1C -rWDaSWqVcN3SAOLMV2MCe5bdSZdbkk6V0/nLKR8YSvgBKtJjCW4k6YnS5cciTNxz -hkcAqg2Ijq6FfUrpuzNPDlJwnZXjfG2WWy09X6GDRl224yW4fKcZgBzqZUPckXk2 -LHR88mcGyYnJ27/aaL8j7dxrrSiDeS/sOKUNNwFnJ5rpM9kzXzehxfCrPfp4sOcs -n/Y+n2Dg70jpkEUeBVF4GiwSLFworA2iI540jwXmojPOEXcT1A6kHkIfhs1w/tku -FT0du7jyU1fbzMZ0KZwYszZ1OC4PVKH4kh+Jlk+71O6d6Ts2QrUKOyrUZHk2EOH5 -kQMreyBUzQ0ZGshBMjTRsJnhkB4BQDa1t/qp5Xd1pCKBXbCL5CcSD1SIxtuFdOa3 -wNemKfrb3vOTlycEVS8KbzfFPROvCgCpLIscgSjX74Yxqa7ybrjKaixUR9gqiC6v -wQcQeKwRoi9C8DfF8rhW3Q5iLc4tVn5V8qdE9isy9COoR+jUKgF4z2rDN6ieZdIs -5fq6M8EGRPbmz6UNp2YINIos8wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G -A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUR9DnsSL/nSz12Vdgs7GxcJXvYXowDQYJ -KoZIhvcNAQELBQADggIBAIZpsU0v6Z9PIpNojuQhmaPORVMbc0RTAIFhzTHjCLqB -KCh6krm2qMhDnscTJk3C2OVVnJJdUNjCK9v+5qiXz1I6JMNlZFxHMaNlNRPDk7n3 -+VGXu6TwYofF1gbTl4MgqX67tiHCpQ2EAOHyJxCDut0DgdXdaMNmEMjRdrSzbyme -APnCKfWxkxlSaRosTKCL4BWaMS/TiJVZbuXEs1DIFAhKm4sTg7GkcrI7djNB3Nyq -pgdvHSQSn8h2vS/ZjvQs7rfSOBAkNlEv41xdgSGn2rtO/+YHqP65DSdsu3BaVXoT -6fEqSWnHX4dXTEN5bTpl6TBcQe7rd6VzEojov32u5cSoHw2OHG1QAk8mGEPej1WF -sQs3BWDJVTkSBKEqz3EWnzZRSb9wO55nnPt7eck5HHisd5FUmrh1CoFSl+NmYWvt -PjgelmFV4ZFUjO2MJB+ByRCac5krFk5yAD9UG/iNuovnFNa2RU9g7Jauwy8CTl2d -lklyALKrdVwPaFsdZcJfMw8eD/A7hvWwTruc9+olBdytoptLFwG+Qt81IR2tq670 -v64fG9PiO/yzcnMcmyiQiRM9HcEARwmWmjgb3bHPDcK0RPOWlc4yOo80nOAXx17O -rg3bhzjlP1v9mxnhMUF6cKojawHhRUzNlM47ni3niAIi9G7oyOzWPPO5std3eqx7 ------END CERTIFICATE----- - -# Issuer: CN=Telekom Security TLS ECC Root 2020 O=Deutsche Telekom Security GmbH -# Subject: CN=Telekom Security TLS ECC Root 2020 O=Deutsche Telekom Security GmbH -# Label: "Telekom Security TLS ECC Root 2020" -# Serial: 72082518505882327255703894282316633856 -# MD5 Fingerprint: c1:ab:fe:6a:10:2c:03:8d:bc:1c:22:32:c0:85:a7:fd -# SHA1 Fingerprint: c0:f8:96:c5:a9:3b:01:06:21:07:da:18:42:48:bc:e9:9d:88:d5:ec -# SHA256 Fingerprint: 57:8a:f4:de:d0:85:3f:4e:59:98:db:4a:ea:f9:cb:ea:8d:94:5f:60:b6:20:a3:8d:1a:3c:13:b2:bc:7b:a8:e1 ------BEGIN CERTIFICATE----- -MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQsw -CQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBH -bWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIw -MB4XDTIwMDgyNTA3NDgyMFoXDTQ1MDgyNTIzNTk1OVowYzELMAkGA1UEBhMCREUx -JzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkGA1UE -AwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgRUNDIFJvb3QgMjAyMDB2MBAGByqGSM49 -AgEGBSuBBAAiA2IABM6//leov9Wq9xCazbzREaK9Z0LMkOsVGJDZos0MKiXrPk/O -tdKPD/M12kOLAoC+b1EkHQ9rK8qfwm9QMuU3ILYg/4gND21Ju9sGpIeQkpT0CdDP -f8iAC8GXs7s1J8nCG6NCMEAwHQYDVR0OBBYEFONyzG6VmUex5rNhTNHLq+O6zd6f -MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA -MGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZMo7k+5Dck2TOrbRBR2Di -z6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdUga/sf+Rn -27iQ7t0l ------END CERTIFICATE----- - -# Issuer: CN=Telekom Security TLS RSA Root 2023 O=Deutsche Telekom Security GmbH -# Subject: CN=Telekom Security TLS RSA Root 2023 O=Deutsche Telekom Security GmbH -# Label: "Telekom Security TLS RSA Root 2023" -# Serial: 44676229530606711399881795178081572759 -# MD5 Fingerprint: bf:5b:eb:54:40:cd:48:71:c4:20:8d:7d:de:0a:42:f2 -# SHA1 Fingerprint: 54:d3:ac:b3:bd:57:56:f6:85:9d:ce:e5:c3:21:e2:d4:ad:83:d0:93 -# SHA256 Fingerprint: ef:c6:5c:ad:bb:59:ad:b6:ef:e8:4d:a2:23:11:b3:56:24:b7:1b:3b:1e:a0:da:8b:66:55:17:4e:c8:97:86:46 ------BEGIN CERTIFICATE----- -MIIFszCCA5ugAwIBAgIQIZxULej27HF3+k7ow3BXlzANBgkqhkiG9w0BAQwFADBj -MQswCQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0 -eSBHbWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBSU0EgUm9vdCAy -MDIzMB4XDTIzMDMyODEyMTY0NVoXDTQ4MDMyNzIzNTk1OVowYzELMAkGA1UEBhMC -REUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkG -A1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgUlNBIFJvb3QgMjAyMzCCAiIwDQYJ -KoZIhvcNAQEBBQADggIPADCCAgoCggIBAO01oYGA88tKaVvC+1GDrib94W7zgRJ9 -cUD/h3VCKSHtgVIs3xLBGYSJwb3FKNXVS2xE1kzbB5ZKVXrKNoIENqil/Cf2SfHV -cp6R+SPWcHu79ZvB7JPPGeplfohwoHP89v+1VmLhc2o0mD6CuKyVU/QBoCcHcqMA -U6DksquDOFczJZSfvkgdmOGjup5czQRxUX11eKvzWarE4GC+j4NSuHUaQTXtvPM6 -Y+mpFEXX5lLRbtLevOP1Czvm4MS9Q2QTps70mDdsipWol8hHD/BeEIvnHRz+sTug -BTNoBUGCwQMrAcjnj02r6LX2zWtEtefdi+zqJbQAIldNsLGyMcEWzv/9FIS3R/qy -8XDe24tsNlikfLMR0cN3f1+2JeANxdKz+bi4d9s3cXFH42AYTyS2dTd4uaNir73J -co4vzLuu2+QVUhkHM/tqty1LkCiCc/4YizWN26cEar7qwU02OxY2kTLvtkCJkUPg -8qKrBC7m8kwOFjQgrIfBLX7JZkcXFBGk8/ehJImr2BrIoVyxo/eMbcgByU/J7MT8 -rFEz0ciD0cmfHdRHNCk+y7AO+oMLKFjlKdw/fKifybYKu6boRhYPluV75Gp6SG12 -mAWl3G0eQh5C2hrgUve1g8Aae3g1LDj1H/1Joy7SWWO/gLCMk3PLNaaZlSJhZQNg -+y+TS/qanIA7AgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtqeX -gj10hZv3PJ+TmpV5dVKMbUcwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS2 -p5eCPXSFm/c8n5OalXl1UoxtRzANBgkqhkiG9w0BAQwFAAOCAgEAqMxhpr51nhVQ -pGv7qHBFfLp+sVr8WyP6Cnf4mHGCDG3gXkaqk/QeoMPhk9tLrbKmXauw1GLLXrtm -9S3ul0A8Yute1hTWjOKWi0FpkzXmuZlrYrShF2Y0pmtjxrlO8iLpWA1WQdH6DErw -M807u20hOq6OcrXDSvvpfeWxm4bu4uB9tPcy/SKE8YXJN3nptT+/XOR0so8RYgDd -GGah2XsjX/GO1WfoVNpbOms2b/mBsTNHM3dA+VKq3dSDz4V4mZqTuXNnQkYRIer+ -CqkbGmVps4+uFrb2S1ayLfmlyOw7YqPta9BO1UAJpB+Y1zqlklkg5LB9zVtzaL1t -xKITDmcZuI1CfmwMmm6gJC3VRRvcxAIU/oVbZZfKTpBQCHpCNfnqwmbU+AGuHrS+ -w6jv/naaoqYfRvaE7fzbzsQCzndILIyy7MMAo+wsVRjBfhnu4S/yrYObnqsZ38aK -L4x35bcF7DvB7L6Gs4a8wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+lj -X273CXE2whJdV/LItM3z7gLfEdxquVeEHVlNjM7IDiPCtyaaEBRx/pOyiriA8A4Q -ntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0o82bNSQ3+pCTE4FCxpgm -dTdmQRCsu/WU48IxK63nI1bMNSWSs1A= ------END CERTIFICATE----- - -# Issuer: CN=FIRMAPROFESIONAL CA ROOT-A WEB O=Firmaprofesional SA -# Subject: CN=FIRMAPROFESIONAL CA ROOT-A WEB O=Firmaprofesional SA -# Label: "FIRMAPROFESIONAL CA ROOT-A WEB" -# Serial: 65916896770016886708751106294915943533 -# MD5 Fingerprint: 82:b2:ad:45:00:82:b0:66:63:f8:5f:c3:67:4e:ce:a3 -# SHA1 Fingerprint: a8:31:11:74:a6:14:15:0d:ca:77:dd:0e:e4:0c:5d:58:fc:a0:72:a5 -# SHA256 Fingerprint: be:f2:56:da:f2:6e:9c:69:bd:ec:16:02:35:97:98:f3:ca:f7:18:21:a0:3e:01:82:57:c5:3c:65:61:7f:3d:4a ------BEGIN CERTIFICATE----- -MIICejCCAgCgAwIBAgIQMZch7a+JQn81QYehZ1ZMbTAKBggqhkjOPQQDAzBuMQsw -CQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UE -YQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENB -IFJPT1QtQSBXRUIwHhcNMjIwNDA2MDkwMTM2WhcNNDcwMzMxMDkwMTM2WjBuMQsw -CQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UE -YQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENB -IFJPT1QtQSBXRUIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARHU+osEaR3xyrq89Zf -e9MEkVz6iMYiuYMQYneEMy3pA4jU4DP37XcsSmDq5G+tbbT4TIqk5B/K6k84Si6C -cyvHZpsKjECcfIr28jlgst7L7Ljkb+qbXbdTkBgyVcUgt5SjYzBhMA8GA1UdEwEB -/wQFMAMBAf8wHwYDVR0jBBgwFoAUk+FDY1w8ndYn81LsF7Kpryz3dvgwHQYDVR0O -BBYEFJPhQ2NcPJ3WJ/NS7Beyqa8s93b4MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjO -PQQDAwNoADBlAjAdfKR7w4l1M+E7qUW/Runpod3JIha3RxEL2Jq68cgLcFBTApFw -hVmpHqTm6iMxoAACMQD94vizrxa5HnPEluPBMBnYfubDl94cT7iJLzPrSA8Z94dG -XSaQpYXFuXqUPoeovQA= ------END CERTIFICATE----- - -# Issuer: CN=TWCA CYBER Root CA O=TAIWAN-CA OU=Root CA -# Subject: CN=TWCA CYBER Root CA O=TAIWAN-CA OU=Root CA -# Label: "TWCA CYBER Root CA" -# Serial: 85076849864375384482682434040119489222 -# MD5 Fingerprint: 0b:33:a0:97:52:95:d4:a9:fd:bb:db:6e:a3:55:5b:51 -# SHA1 Fingerprint: f6:b1:1c:1a:83:38:e9:7b:db:b3:a8:c8:33:24:e0:2d:9c:7f:26:66 -# SHA256 Fingerprint: 3f:63:bb:28:14:be:17:4e:c8:b6:43:9c:f0:8d:6d:56:f0:b7:c4:05:88:3a:56:48:a3:34:42:4d:6b:3e:c5:58 ------BEGIN CERTIFICATE----- -MIIFjTCCA3WgAwIBAgIQQAE0jMIAAAAAAAAAATzyxjANBgkqhkiG9w0BAQwFADBQ -MQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290 -IENBMRswGQYDVQQDExJUV0NBIENZQkVSIFJvb3QgQ0EwHhcNMjIxMTIyMDY1NDI5 -WhcNNDcxMTIyMTU1OTU5WjBQMQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FO -LUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NBIENZQkVSIFJvb3Qg -Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDG+Moe2Qkgfh1sTs6P -40czRJzHyWmqOlt47nDSkvgEs1JSHWdyKKHfi12VCv7qze33Kc7wb3+szT3vsxxF -avcokPFhV8UMxKNQXd7UtcsZyoC5dc4pztKFIuwCY8xEMCDa6pFbVuYdHNWdZsc/ -34bKS1PE2Y2yHer43CdTo0fhYcx9tbD47nORxc5zb87uEB8aBs/pJ2DFTxnk684i -JkXXYJndzk834H/nY62wuFm40AZoNWDTNq5xQwTxaWV4fPMf88oon1oglWa0zbfu -j3ikRRjpJi+NmykosaS3Om251Bw4ckVYsV7r8Cibt4LK/c/WMw+f+5eesRycnupf -Xtuq3VTpMCEobY5583WSjCb+3MX2w7DfRFlDo7YDKPYIMKoNM+HvnKkHIuNZW0CP -2oi3aQiotyMuRAlZN1vH4xfyIutuOVLF3lSnmMlLIJXcRolftBL5hSmO68gnFSDA -S9TMfAxsNAwmmyYxpjyn9tnQS6Jk/zuZQXLB4HCX8SS7K8R0IrGsayIyJNN4KsDA -oS/xUgXJP+92ZuJF2A09rZXIx4kmyA+upwMu+8Ff+iDhcK2wZSA3M2Cw1a/XDBzC -kHDXShi8fgGwsOsVHkQGzaRP6AzRwyAQ4VRlnrZR0Bp2a0JaWHY06rc3Ga4udfmW -5cFZ95RXKSWNOkyrTZpB0F8mAwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSdhWEUfMFib5do5E83QOGt4A1WNzAd -BgNVHQ4EFgQUnYVhFHzBYm+XaORPN0DhreANVjcwDQYJKoZIhvcNAQEMBQADggIB -AGSPesRiDrWIzLjHhg6hShbNcAu3p4ULs3a2D6f/CIsLJc+o1IN1KriWiLb73y0t -tGlTITVX1olNc79pj3CjYcya2x6a4CD4bLubIp1dhDGaLIrdaqHXKGnK/nZVekZn -68xDiBaiA9a5F/gZbG0jAn/xX9AKKSM70aoK7akXJlQKTcKlTfjF/biBzysseKNn -TKkHmvPfXvt89YnNdJdhEGoHK4Fa0o635yDRIG4kqIQnoVesqlVYL9zZyvpoBJ7t -RCT5dEA7IzOrg1oYJkK2bVS1FmAwbLGg+LhBoF1JSdJlBTrq/p1hvIbZv97Tujqx -f36SNI7JAG7cmL3c7IAFrQI932XtCwP39xaEBDG6k5TY8hL4iuO/Qq+n1M0RFxbI -Qh0UqEL20kCGoE8jypZFVmAGzbdVAaYBlGX+bgUJurSkquLvWL69J1bY73NxW0Qz -8ppy6rBePm6pUlvscG21h483XjyMnM7k8M4MZ0HMzvaAq07MTFb1wWFZk7Q+ptq4 -NxKfKjLji7gh7MMrZQzvIt6IKTtM1/r+t+FHvpw+PoP7UV31aPcuIYXcv/Fa4nzX -xeSDwWrruoBa3lwtcHb4yOWHh8qgnaHlIhInD0Q9HWzq1MKLL295q39QpsQZp6F6 -t5b5wR9iWqJDB0BeJsas7a5wFsWqynKKTbDPAYsDP27X ------END CERTIFICATE----- - -# Issuer: CN=SecureSign Root CA12 O=Cybertrust Japan Co., Ltd. -# Subject: CN=SecureSign Root CA12 O=Cybertrust Japan Co., Ltd. -# Label: "SecureSign Root CA12" -# Serial: 587887345431707215246142177076162061960426065942 -# MD5 Fingerprint: c6:89:ca:64:42:9b:62:08:49:0b:1e:7f:e9:07:3d:e8 -# SHA1 Fingerprint: 7a:22:1e:3d:de:1b:06:ac:9e:c8:47:70:16:8e:3c:e5:f7:6b:06:f4 -# SHA256 Fingerprint: 3f:03:4b:b5:70:4d:44:b2:d0:85:45:a0:20:57:de:93:eb:f3:90:5f:ce:72:1a:cb:c7:30:c0:6d:da:ee:90:4e ------BEGIN CERTIFICATE----- -MIIDcjCCAlqgAwIBAgIUZvnHwa/swlG07VOX5uaCwysckBYwDQYJKoZIhvcNAQEL -BQAwUTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28u -LCBMdGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExMjAeFw0yMDA0MDgw -NTM2NDZaFw00MDA0MDgwNTM2NDZaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpD -eWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBS -b290IENBMTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6OcE3emhF -KxS06+QT61d1I02PJC0W6K6OyX2kVzsqdiUzg2zqMoqUm048luT9Ub+ZyZN+v/mt -p7JIKwccJ/VMvHASd6SFVLX9kHrko+RRWAPNEHl57muTH2SOa2SroxPjcf59q5zd -J1M3s6oYwlkm7Fsf0uZlfO+TvdhYXAvA42VvPMfKWeP+bl+sg779XSVOKik71gur -FzJ4pOE+lEa+Ym6b3kaosRbnhW70CEBFEaCeVESE99g2zvVQR9wsMJvuwPWW0v4J -hscGWa5Pro4RmHvzC1KqYiaqId+OJTN5lxZJjfU+1UefNzFJM3IFTQy2VYzxV4+K -h9GtxRESOaCtAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD -AgEGMB0GA1UdDgQWBBRXNPN0zwRL1SXm8UC2LEzZLemgrTANBgkqhkiG9w0BAQsF -AAOCAQEAPrvbFxbS8hQBICw4g0utvsqFepq2m2um4fylOqyttCg6r9cBg0krY6Ld -mmQOmFxv3Y67ilQiLUoT865AQ9tPkbeGGuwAtEGBpE/6aouIs3YIcipJQMPTw4WJ -mBClnW8Zt7vPemVV2zfrPIpyMpcemik+rY3moxtt9XUa5rBouVui7mlHJzWhhpmA -8zNL4WukJsPvdFlseqJkth5Ew1DgDzk9qTPxpfPSvWKErI4cqc1avTc7bgoitPQV -55FYxTpE05Uo2cBl6XLK0A+9H7MV2anjpEcJnuDLN/v9vZfVvhgaaaI5gdka9at/ -yOPiZwud9AzqVN/Ssq+xIvEg37xEHA== ------END CERTIFICATE----- - -# Issuer: CN=SecureSign Root CA14 O=Cybertrust Japan Co., Ltd. -# Subject: CN=SecureSign Root CA14 O=Cybertrust Japan Co., Ltd. -# Label: "SecureSign Root CA14" -# Serial: 575790784512929437950770173562378038616896959179 -# MD5 Fingerprint: 71:0d:72:fa:92:19:65:5e:89:04:ac:16:33:f0:bc:d5 -# SHA1 Fingerprint: dd:50:c0:f7:79:b3:64:2e:74:a2:b8:9d:9f:d3:40:dd:bb:f0:f2:4f -# SHA256 Fingerprint: 4b:00:9c:10:34:49:4f:9a:b5:6b:ba:3b:a1:d6:27:31:fc:4d:20:d8:95:5a:dc:ec:10:a9:25:60:72:61:e3:38 ------BEGIN CERTIFICATE----- -MIIFcjCCA1qgAwIBAgIUZNtaDCBO6Ncpd8hQJ6JaJ90t8sswDQYJKoZIhvcNAQEM -BQAwUTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28u -LCBMdGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExNDAeFw0yMDA0MDgw -NzA2MTlaFw00NTA0MDgwNzA2MTlaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpD -eWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBS -b290IENBMTQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDF0nqh1oq/ -FjHQmNE6lPxauG4iwWL3pwon71D2LrGeaBLwbCRjOfHw3xDG3rdSINVSW0KZnvOg -vlIfX8xnbacuUKLBl422+JX1sLrcneC+y9/3OPJH9aaakpUqYllQC6KxNedlsmGy -6pJxaeQp8E+BgQQ8sqVb1MWoWWd7VRxJq3qdwudzTe/NCcLEVxLbAQ4jeQkHO6Lo -/IrPj8BGJJw4J+CDnRugv3gVEOuGTgpa/d/aLIJ+7sr2KeH6caH3iGicnPCNvg9J -kdjqOvn90Ghx2+m1K06Ckm9mH+Dw3EzsytHqunQG+bOEkJTRX45zGRBdAuVwpcAQ -0BB8b8VYSbSwbprafZX1zNoCr7gsfXmPvkPx+SgojQlD+Ajda8iLLCSxjVIHvXib -y8posqTdDEx5YMaZ0ZPxMBoH064iwurO8YQJzOAUbn8/ftKChazcqRZOhaBgy/ac -18izju3Gm5h1DVXoX+WViwKkrkMpKBGk5hIwAUt1ax5mnXkvpXYvHUC0bcl9eQjs -0Wq2XSqypWa9a4X0dFbD9ed1Uigspf9mR6XU/v6eVL9lfgHWMI+lNpyiUBzuOIAB -SMbHdPTGrMNASRZhdCyvjG817XsYAFs2PJxQDcqSMxDxJklt33UkN4Ii1+iW/RVL -ApY+B3KVfqs9TC7XyvDf4Fg/LS8EmjijAQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD -AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUBpOjCl4oaTeqYR3r6/wtbyPk -86AwDQYJKoZIhvcNAQEMBQADggIBAJaAcgkGfpzMkwQWu6A6jZJOtxEaCnFxEM0E -rX+lRVAQZk5KQaID2RFPeje5S+LGjzJmdSX7684/AykmjbgWHfYfM25I5uj4V7Ib -ed87hwriZLoAymzvftAj63iP/2SbNDefNWWipAA9EiOWWF3KY4fGoweITedpdopT -zfFP7ELyk+OZpDc8h7hi2/DsHzc/N19DzFGdtfCXwreFamgLRB7lUe6TzktuhsHS -DCRZNhqfLJGP4xjblJUK7ZGqDpncllPjYYPGFrojutzdfhrGe0K22VoF3Jpf1d+4 -2kd92jjbrDnVHmtsKheMYc2xbXIBw8MgAGJoFjHVdqqGuw6qnsb58Nn4DSEC5MUo -FlkRudlpcyqSeLiSV5sI8jrlL5WwWLdrIBRtFO8KvH7YVdiI2i/6GaX7i+B/OfVy -K4XELKzvGUWSTLNhB9xNH27SgRNcmvMSZ4PPmz+Ln52kuaiWA3rF7iDeM9ovnhp6 -dB7h7sxaOgTdsxoEqBRjrLdHEoOabPXm6RUVkRqEGQ6UROcSjiVbgGcZ3GOTEAtl -Lor6CZpO2oYofaphNdgOpygau1LgePhsumywbrmHXumZNTfxPWQrqaA0k89jL9WB -365jJ6UeTo3cKXhZ+PmhIIynJkBugnLNeLLIjzwec+fBH7/PzqUqm9tEZDKgu39c -JRNItX+S ------END CERTIFICATE----- - -# Issuer: CN=SecureSign Root CA15 O=Cybertrust Japan Co., Ltd. -# Subject: CN=SecureSign Root CA15 O=Cybertrust Japan Co., Ltd. -# Label: "SecureSign Root CA15" -# Serial: 126083514594751269499665114766174399806381178503 -# MD5 Fingerprint: 13:30:fc:c4:62:a6:a9:de:b5:c1:68:af:b5:d2:31:47 -# SHA1 Fingerprint: cb:ba:83:c8:c1:5a:5d:f1:f9:73:6f:ca:d7:ef:28:13:06:4a:07:7d -# SHA256 Fingerprint: e7:78:f0:f0:95:fe:84:37:29:cd:1a:00:82:17:9e:53:14:a9:c2:91:44:28:05:e1:fb:1d:8f:b6:b8:88:6c:3a ------BEGIN CERTIFICATE----- -MIICIzCCAamgAwIBAgIUFhXHw9hJp75pDIqI7fBw+d23PocwCgYIKoZIzj0EAwMw -UTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBM -dGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExNTAeFw0yMDA0MDgwODMy -NTZaFw00NTA0MDgwODMyNTZaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpDeWJl -cnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBSb290 -IENBMTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQLUHSNZDKZmbPSYAi4Io5GdCx4 -wCtELW1fHcmuS1Iggz24FG1Th2CeX2yF2wYUleDHKP+dX+Sq8bOLbe1PL0vJSpSR -ZHX+AezB2Ot6lHhWGENfa4HL9rzatAy2KZMIaY+jQjBAMA8GA1UdEwEB/wQFMAMB -Af8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTrQciu/NWeUUj1vYv0hyCTQSvT -9DAKBggqhkjOPQQDAwNoADBlAjEA2S6Jfl5OpBEHvVnCB96rMjhTKkZEBhd6zlHp -4P9mLQlO4E/0BdGF9jVg3PVys0Z9AjBEmEYagoUeYWmJSwdLZrWeqrqgHkHZAXQ6 -bkU6iYAZezKYVWOr62Nuk22rGwlgMU4= ------END CERTIFICATE----- - -# Issuer: CN=D-TRUST BR Root CA 2 2023 O=D-Trust GmbH -# Subject: CN=D-TRUST BR Root CA 2 2023 O=D-Trust GmbH -# Label: "D-TRUST BR Root CA 2 2023" -# Serial: 153168538924886464690566649552453098598 -# MD5 Fingerprint: e1:09:ed:d3:60:d4:56:1b:47:1f:b7:0c:5f:1b:5f:85 -# SHA1 Fingerprint: 2d:b0:70:ee:71:94:af:69:68:17:db:79:ce:58:9f:a0:6b:96:f7:87 -# SHA256 Fingerprint: 05:52:e6:f8:3f:df:65:e8:fa:96:70:e6:66:df:28:a4:e2:13:40:b5:10:cb:e5:25:66:f9:7c:4f:b9:4b:2b:d1 ------BEGIN CERTIFICATE----- -MIIFqTCCA5GgAwIBAgIQczswBEhb2U14LnNLyaHcZjANBgkqhkiG9w0BAQ0FADBI -MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlE -LVRSVVNUIEJSIFJvb3QgQ0EgMiAyMDIzMB4XDTIzMDUwOTA4NTYzMVoXDTM4MDUw -OTA4NTYzMFowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEi -MCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDIgMjAyMzCCAiIwDQYJKoZIhvcN -AQEBBQADggIPADCCAgoCggIBAK7/CVmRgApKaOYkP7in5Mg6CjoWzckjYaCTcfKr -i3OPoGdlYNJUa2NRb0kz4HIHE304zQaSBylSa053bATTlfrdTIzZXcFhfUvnKLNE -gXtRr90zsWh81k5M/itoucpmacTsXld/9w3HnDY25QdgrMBM6ghs7wZ8T1soegj8 -k12b9py0i4a6Ibn08OhZWiihNIQaJZG2tY/vsvmA+vk9PBFy2OMvhnbFeSzBqZCT -Rphny4NqoFAjpzv2gTng7fC5v2Xx2Mt6++9zA84A9H3X4F07ZrjcjrqDy4d2A/wl -2ecjbwb9Z/Pg/4S8R7+1FhhGaRTMBffb00msa8yr5LULQyReS2tNZ9/WtT5PeB+U -cSTq3nD88ZP+npNa5JRal1QMNXtfbO4AHyTsA7oC9Xb0n9Sa7YUsOCIvx9gvdhFP -/Wxc6PWOJ4d/GUohR5AdeY0cW/jPSoXk7bNbjb7EZChdQcRurDhaTyN0dKkSw/bS -uREVMweR2Ds3OmMwBtHFIjYoYiMQ4EbMl6zWK11kJNXuHA7e+whadSr2Y23OC0K+ -0bpwHJwh5Q8xaRfX/Aq03u2AnMuStIv13lmiWAmlY0cL4UEyNEHZmrHZqLAbWt4N -DfTisl01gLmB1IRpkQLLddCNxbU9CZEJjxShFHR5PtbJFR2kWVki3PaKRT08EtY+ -XTIvAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUZ5Dw1t61 -GNVGKX5cq/ieCLxklRAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRCMEAwPqA8oDqG -OGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfYnJfcm9vdF9jYV8y -XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQA097N3U9swFrktpSHxQCF16+tI -FoE9c+CeJyrrd6kTpGoKWloUMz1oH4Guaf2Mn2VsNELZLdB/eBaxOqwjMa1ef67n -riv6uvw8l5VAk1/DLQOj7aRvU9f6QA4w9QAgLABMjDu0ox+2v5Eyq6+SmNMW5tTR -VFxDWy6u71cqqLRvpO8NVhTaIasgdp4D/Ca4nj8+AybmTNudX0KEPUUDAxxZiMrc -LmEkWqTqJwtzEr5SswrPMhfiHocaFpVIbVrg0M8JkiZmkdijYQ6qgYF/6FKC0ULn -4B0Y+qSFNueG4A3rvNTJ1jxD8V1Jbn6Bm2m1iWKPiFLY1/4nwSPFyysCu7Ff/vtD -hQNGvl3GyiEm/9cCnnRK3PgTFbGBVzbLZVzRHTF36SXDw7IyN9XxmAnkbWOACKsG -koHU6XCPpz+y7YaMgmo1yEJagtFSGkUPFaUA8JR7ZSdXOUPPfH/mvTWze/EZTN46 -ls/pdu4D58JDUjxqgejBWoC9EV2Ta/vH5mQ/u2kc6d0li690yVRAysuTEwrt+2aS -Ecr1wPrYg1UDfNPFIkZ1cGt5SAYqgpq/5usWDiJFAbzdNpQ0qTUmiteXue4Icr80 -knCDgKs4qllo3UCkGJCy89UDyibK79XH4I9TjvAA46jtn/mtd+ArY0+ew+43u3gJ -hJ65bvspmZDogNOfJA== ------END CERTIFICATE----- - -# Issuer: CN=D-TRUST EV Root CA 2 2023 O=D-Trust GmbH -# Subject: CN=D-TRUST EV Root CA 2 2023 O=D-Trust GmbH -# Label: "D-TRUST EV Root CA 2 2023" -# Serial: 139766439402180512324132425437959641711 -# MD5 Fingerprint: 96:b4:78:09:f0:09:cb:77:eb:bb:1b:4d:6f:36:bc:b6 -# SHA1 Fingerprint: a5:5b:d8:47:6c:8f:19:f7:4c:f4:6d:6b:b6:c2:79:82:22:df:54:8b -# SHA256 Fingerprint: 8e:82:21:b2:e7:d4:00:78:36:a1:67:2f:0d:cc:29:9c:33:bc:07:d3:16:f1:32:fa:1a:20:6d:58:71:50:f1:ce ------BEGIN CERTIFICATE----- -MIIFqTCCA5GgAwIBAgIQaSYJfoBLTKCnjHhiU19abzANBgkqhkiG9w0BAQ0FADBI -MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlE -LVRSVVNUIEVWIFJvb3QgQ0EgMiAyMDIzMB4XDTIzMDUwOTA5MTAzM1oXDTM4MDUw -OTA5MTAzMlowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEi -MCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENBIDIgMjAyMzCCAiIwDQYJKoZIhvcN -AQEBBQADggIPADCCAgoCggIBANiOo4mAC7JXUtypU0w3uX9jFxPvp1sjW2l1sJkK -F8GLxNuo4MwxusLyzV3pt/gdr2rElYfXR8mV2IIEUD2BCP/kPbOx1sWy/YgJ25yE -7CUXFId/MHibaljJtnMoPDT3mfd/06b4HEV8rSyMlD/YZxBTfiLNTiVR8CUkNRFe -EMbsh2aJgWi6zCudR3Mfvc2RpHJqnKIbGKBv7FD0fUDCqDDPvXPIEysQEx6Lmqg6 -lHPTGGkKSv/BAQP/eX+1SH977ugpbzZMlWGG2Pmic4ruri+W7mjNPU0oQvlFKzIb -RlUWaqZLKfm7lVa/Rh3sHZMdwGWyH6FDrlaeoLGPaxK3YG14C8qKXO0elg6DpkiV -jTujIcSuWMYAsoS0I6SWhjW42J7YrDRJmGOVxcttSEfi8i4YHtAxq9107PncjLgc -jmgjutDzUNzPZY9zOjLHfP7KgiJPvo5iR2blzYfi6NUPGJ/lBHJLRjwQ8kTCZFZx -TnXonMkmdMV9WdEKWw9t/p51HBjGGjp82A0EzM23RWV6sY+4roRIPrN6TagD4uJ+ -ARZZaBhDM7DS3LAaQzXupdqpRlyuhoFBAUp0JuyfBr/CBTdkdXgpaP3F9ev+R/nk -hbDhezGdpn9yo7nELC7MmVcOIQxFAZRl62UJxmMiCzNJkkg8/M3OsD6Onov4/knF -NXJHAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUqvyREBuH -kV8Wub9PS5FeAByxMoAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRCMEAwPqA8oDqG -OGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfZXZfcm9vdF9jYV8y -XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQCTy6UfmRHsmg1fLBWTxj++EI14 -QvBukEdHjqOSMo1wj/Zbjb6JzkcBahsgIIlbyIIQbODnmaprxiqgYzWRaoUlrRc4 -pZt+UPJ26oUFKidBK7GB0aL2QHWpDsvxVUjY7NHss+jOFKE17MJeNRqrphYBBo7q -3C+jisosketSjl8MmxfPy3MHGcRqwnNU73xDUmPBEcrCRbH0O1P1aa4846XerOhU -t7KR/aypH/KH5BfGSah82ApB9PI+53c0BFLd6IHyTS9URZ0V4U/M5d40VxDJI3IX -cI1QcB9WbMy5/zpaT2N6w25lBx2Eof+pDGOJbbJAiDnXH3dotfyc1dZnaVuodNv8 -ifYbMvekJKZ2t0dT741Jj6m2g1qllpBFYfXeA08mD6iL8AOWsKwV0HFaanuU5nCT -2vFp4LJiTZ6P/4mdm13NRemUAiKN4DV/6PEEeXFsVIP4M7kFMhtYVRFP0OUnR3Hs -7dpn1mKmS00PaaLJvOwiS5THaJQXfuKOKD62xur1NGyfN4gHONuGcfrNlUhDbqNP -gofXNJhuS5N5YHVpD/Aa1VP6IQzCP+k/HxiMkl14p3ZnGbuy6n/pcAlWVqOwDAst -Nl7F6cTVg8uGF5csbBNvh1qvSaYd2804BC5f4ko1Di1L+KIkBI3Y4WNeApI02phh -XBxvWHZks/wCuPWdCg== ------END CERTIFICATE----- diff --git a/venv/Lib/site-packages/certifi/core.py b/venv/Lib/site-packages/certifi/core.py deleted file mode 100644 index 91f538b..0000000 --- a/venv/Lib/site-packages/certifi/core.py +++ /dev/null @@ -1,114 +0,0 @@ -""" -certifi.py -~~~~~~~~~~ - -This module returns the installation location of cacert.pem or its contents. -""" -import sys -import atexit - -def exit_cacert_ctx() -> None: - _CACERT_CTX.__exit__(None, None, None) # type: ignore[union-attr] - - -if sys.version_info >= (3, 11): - - from importlib.resources import as_file, files - - _CACERT_CTX = None - _CACERT_PATH = None - - def where() -> str: - # This is slightly terrible, but we want to delay extracting the file - # in cases where we're inside of a zipimport situation until someone - # actually calls where(), but we don't want to re-extract the file - # on every call of where(), so we'll do it once then store it in a - # global variable. - global _CACERT_CTX - global _CACERT_PATH - if _CACERT_PATH is None: - # This is slightly janky, the importlib.resources API wants you to - # manage the cleanup of this file, so it doesn't actually return a - # path, it returns a context manager that will give you the path - # when you enter it and will do any cleanup when you leave it. In - # the common case of not needing a temporary file, it will just - # return the file system location and the __exit__() is a no-op. - # - # We also have to hold onto the actual context manager, because - # it will do the cleanup whenever it gets garbage collected, so - # we will also store that at the global level as well. - _CACERT_CTX = as_file(files("certifi").joinpath("cacert.pem")) - _CACERT_PATH = str(_CACERT_CTX.__enter__()) - atexit.register(exit_cacert_ctx) - - return _CACERT_PATH - - def contents() -> str: - return files("certifi").joinpath("cacert.pem").read_text(encoding="ascii") - -elif sys.version_info >= (3, 7): - - from importlib.resources import path as get_path, read_text - - _CACERT_CTX = None - _CACERT_PATH = None - - def where() -> str: - # This is slightly terrible, but we want to delay extracting the - # file in cases where we're inside of a zipimport situation until - # someone actually calls where(), but we don't want to re-extract - # the file on every call of where(), so we'll do it once then store - # it in a global variable. - global _CACERT_CTX - global _CACERT_PATH - if _CACERT_PATH is None: - # This is slightly janky, the importlib.resources API wants you - # to manage the cleanup of this file, so it doesn't actually - # return a path, it returns a context manager that will give - # you the path when you enter it and will do any cleanup when - # you leave it. In the common case of not needing a temporary - # file, it will just return the file system location and the - # __exit__() is a no-op. - # - # We also have to hold onto the actual context manager, because - # it will do the cleanup whenever it gets garbage collected, so - # we will also store that at the global level as well. - _CACERT_CTX = get_path("certifi", "cacert.pem") - _CACERT_PATH = str(_CACERT_CTX.__enter__()) - atexit.register(exit_cacert_ctx) - - return _CACERT_PATH - - def contents() -> str: - return read_text("certifi", "cacert.pem", encoding="ascii") - -else: - import os - import types - from typing import Union - - Package = Union[types.ModuleType, str] - Resource = Union[str, "os.PathLike"] - - # This fallback will work for Python versions prior to 3.7 that lack the - # importlib.resources module but relies on the existing `where` function - # so won't address issues with environments like PyOxidizer that don't set - # __file__ on modules. - def read_text( - package: Package, - resource: Resource, - encoding: str = 'utf-8', - errors: str = 'strict' - ) -> str: - with open(where(), encoding=encoding) as data: - return data.read() - - # If we don't have importlib.resources, then we will just do the old logic - # of assuming we're on the filesystem and munge the path directly. - def where() -> str: - f = os.path.dirname(__file__) - - return os.path.join(f, "cacert.pem") - - def contents() -> str: - return read_text("certifi", "cacert.pem", encoding="ascii") diff --git a/venv/Lib/site-packages/certifi/py.typed b/venv/Lib/site-packages/certifi/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/INSTALLER b/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/METADATA b/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/METADATA deleted file mode 100644 index 573d88b..0000000 --- a/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/METADATA +++ /dev/null @@ -1,731 +0,0 @@ -Metadata-Version: 2.4 -Name: charset-normalizer -Version: 3.4.2 -Summary: The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet. -Author-email: "Ahmed R. TAHRI" -Maintainer-email: "Ahmed R. TAHRI" -License: MIT -Project-URL: Changelog, https://github.com/jawah/charset_normalizer/blob/master/CHANGELOG.md -Project-URL: Documentation, https://charset-normalizer.readthedocs.io/ -Project-URL: Code, https://github.com/jawah/charset_normalizer -Project-URL: Issue tracker, https://github.com/jawah/charset_normalizer/issues -Keywords: encoding,charset,charset-detector,detector,normalization,unicode,chardet,detect -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Text Processing :: Linguistic -Classifier: Topic :: Utilities -Classifier: Typing :: Typed -Requires-Python: >=3.7 -Description-Content-Type: text/markdown -License-File: LICENSE -Provides-Extra: unicode-backport -Dynamic: license-file - -

Charset Detection, for Everyone 👋

- -

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

-

- Featured Packages
- - Static Badge - - - Static Badge - -

-

- In other language (unofficial port - by the community)
- - Static Badge - -

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

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

- -This project offers you an alternative to **Universal Charset Encoding Detector**, also known as **Chardet**. - -| Feature | [Chardet](https://github.com/chardet/chardet) | Charset Normalizer | [cChardet](https://github.com/PyYoshi/cChardet) | -|--------------------------------------------------|:---------------------------------------------:|:--------------------------------------------------------------------------------------------------:|:-----------------------------------------------:| -| `Fast` | ❌ | ✅ | ✅ | -| `Universal**` | ❌ | ✅ | ❌ | -| `Reliable` **without** distinguishable standards | ❌ | ✅ | ✅ | -| `Reliable` **with** distinguishable standards | ✅ | ✅ | ✅ | -| `License` | LGPL-2.1
_restrictive_ | MIT | MPL-1.1
_restrictive_ | -| `Native Python` | ✅ | ✅ | ❌ | -| `Detect spoken language` | ❌ | ✅ | N/A | -| `UnicodeDecodeError Safety` | ❌ | ✅ | ❌ | -| `Whl Size (min)` | 193.6 kB | 42 kB | ~200 kB | -| `Supported Encoding` | 33 | 🎉 [99](https://charset-normalizer.readthedocs.io/en/latest/user/support.html#supported-encodings) | 40 | - -

-Reading Normalized TextCat Reading Text -

- -*\*\* : They are clearly using specific code for a specific encoding even if covering most of used one*
- -## ⚡ Performance - -This package offer better performance than its counterpart Chardet. Here are some numbers. - -| Package | Accuracy | Mean per file (ms) | File per sec (est) | -|-----------------------------------------------|:--------:|:------------------:|:------------------:| -| [chardet](https://github.com/chardet/chardet) | 86 % | 63 ms | 16 file/sec | -| charset-normalizer | **98 %** | **10 ms** | 100 file/sec | - -| Package | 99th percentile | 95th percentile | 50th percentile | -|-----------------------------------------------|:---------------:|:---------------:|:---------------:| -| [chardet](https://github.com/chardet/chardet) | 265 ms | 71 ms | 7 ms | -| charset-normalizer | 100 ms | 50 ms | 5 ms | - -_updated as of december 2024 using CPython 3.12_ - -Chardet's performance on larger file (1MB+) are very poor. Expect huge difference on large payload. - -> Stats are generated using 400+ files using default parameters. More details on used files, see GHA workflows. -> And yes, these results might change at any time. The dataset can be updated to include more files. -> The actual delays heavily depends on your CPU capabilities. The factors should remain the same. -> Keep in mind that the stats are generous and that Chardet accuracy vs our is measured using Chardet initial capability -> (e.g. Supported Encoding) Challenge-them if you want. - -## ✨ Installation - -Using pip: - -```sh -pip install charset-normalizer -U -``` - -## 🚀 Basic Usage - -### CLI -This package comes with a CLI. - -``` -usage: normalizer [-h] [-v] [-a] [-n] [-m] [-r] [-f] [-t THRESHOLD] - file [file ...] - -The Real First Universal Charset Detector. Discover originating encoding used -on text file. Normalize text to unicode. - -positional arguments: - files File(s) to be analysed - -optional arguments: - -h, --help show this help message and exit - -v, --verbose Display complementary information about file if any. - Stdout will contain logs about the detection process. - -a, --with-alternative - Output complementary possibilities if any. Top-level - JSON WILL be a list. - -n, --normalize Permit to normalize input file. If not set, program - does not write anything. - -m, --minimal Only output the charset detected to STDOUT. Disabling - JSON output. - -r, --replace Replace file when trying to normalize it instead of - creating a new one. - -f, --force Replace file without asking if you are sure, use this - flag with caution. - -t THRESHOLD, --threshold THRESHOLD - Define a custom maximum amount of chaos allowed in - decoded content. 0. <= chaos <= 1. - --version Show version information and exit. -``` - -```bash -normalizer ./data/sample.1.fr.srt -``` - -or - -```bash -python -m charset_normalizer ./data/sample.1.fr.srt -``` - -🎉 Since version 1.4.0 the CLI produce easily usable stdout result in JSON format. - -```json -{ - "path": "/home/default/projects/charset_normalizer/data/sample.1.fr.srt", - "encoding": "cp1252", - "encoding_aliases": [ - "1252", - "windows_1252" - ], - "alternative_encodings": [ - "cp1254", - "cp1256", - "cp1258", - "iso8859_14", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_9", - "latin_1", - "mbcs" - ], - "language": "French", - "alphabets": [ - "Basic Latin", - "Latin-1 Supplement" - ], - "has_sig_or_bom": false, - "chaos": 0.149, - "coherence": 97.152, - "unicode_path": null, - "is_preferred": true -} -``` - -### Python -*Just print out normalized text* -```python -from charset_normalizer import from_path - -results = from_path('./my_subtitle.srt') - -print(str(results.best())) -``` - -*Upgrade your code without effort* -```python -from charset_normalizer import detect -``` - -The above code will behave the same as **chardet**. We ensure that we offer the best (reasonable) BC result possible. - -See the docs for advanced usage : [readthedocs.io](https://charset-normalizer.readthedocs.io/en/latest/) - -## 😇 Why - -When I started using Chardet, I noticed that it was not suited to my expectations, and I wanted to propose a -reliable alternative using a completely different method. Also! I never back down on a good challenge! - -I **don't care** about the **originating charset** encoding, because **two different tables** can -produce **two identical rendered string.** -What I want is to get readable text, the best I can. - -In a way, **I'm brute forcing text decoding.** How cool is that ? 😎 - -Don't confuse package **ftfy** with charset-normalizer or chardet. ftfy goal is to repair Unicode string whereas charset-normalizer to convert raw file in unknown encoding to unicode. - -## 🍰 How - - - Discard all charset encoding table that could not fit the binary content. - - Measure noise, or the mess once opened (by chunks) with a corresponding charset encoding. - - Extract matches with the lowest mess detected. - - Additionally, we measure coherence / probe for a language. - -**Wait a minute**, what is noise/mess and coherence according to **YOU ?** - -*Noise :* I opened hundred of text files, **written by humans**, with the wrong encoding table. **I observed**, then -**I established** some ground rules about **what is obvious** when **it seems like** a mess (aka. defining noise in rendered text). - I know that my interpretation of what is noise is probably incomplete, feel free to contribute in order to - improve or rewrite it. - -*Coherence :* For each language there is on earth, we have computed ranked letter appearance occurrences (the best we can). So I thought -that intel is worth something here. So I use those records against decoded text to check if I can detect intelligent design. - -## ⚡ Known limitations - - - Language detection is unreliable when text contains two or more languages sharing identical letters. (eg. HTML (english tags) + Turkish content (Sharing Latin characters)) - - Every charset detector heavily depends on sufficient content. In common cases, do not bother run detection on very tiny content. - -## ⚠️ About Python EOLs - -**If you are running:** - -- Python >=2.7,<3.5: Unsupported -- Python 3.5: charset-normalizer < 2.1 -- Python 3.6: charset-normalizer < 3.1 -- Python 3.7: charset-normalizer < 4.0 - -Upgrade your Python interpreter as soon as possible. - -## 👤 Contributing - -Contributions, issues and feature requests are very much welcome.
-Feel free to check [issues page](https://github.com/ousret/charset_normalizer/issues) if you want to contribute. - -## 📝 License - -Copyright © [Ahmed TAHRI @Ousret](https://github.com/Ousret).
-This project is [MIT](https://github.com/Ousret/charset_normalizer/blob/master/LICENSE) licensed. - -Characters frequencies used in this project © 2012 [Denny Vrandečić](http://simia.net/letters/) - -## 💼 For Enterprise - -Professional support for charset-normalizer is available as part of the [Tidelift -Subscription][1]. Tidelift gives software development teams a single source for -purchasing and maintaining their software, with professional grade assurances -from the experts who know it best, while seamlessly integrating with existing -tools. - -[1]: https://tidelift.com/subscription/pkg/pypi-charset-normalizer?utm_source=pypi-charset-normalizer&utm_medium=readme - -[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/7297/badge)](https://www.bestpractices.dev/projects/7297) - -# Changelog -All notable changes to charset-normalizer will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - -## [3.4.2](https://github.com/Ousret/charset_normalizer/compare/3.4.1...3.4.2) (2025-05-02) - -### Fixed -- Addressed the DeprecationWarning in our CLI regarding `argparse.FileType` by backporting the target class into the package. (#591) -- Improved the overall reliability of the detector with CJK Ideographs. (#605) (#587) - -### Changed -- Optional mypyc compilation upgraded to version 1.15 for Python >= 3.8 - -## [3.4.1](https://github.com/Ousret/charset_normalizer/compare/3.4.0...3.4.1) (2024-12-24) - -### Changed -- Project metadata are now stored using `pyproject.toml` instead of `setup.cfg` using setuptools as the build backend. -- Enforce annotation delayed loading for a simpler and consistent types in the project. -- Optional mypyc compilation upgraded to version 1.14 for Python >= 3.8 - -### Added -- pre-commit configuration. -- noxfile. - -### Removed -- `build-requirements.txt` as per using `pyproject.toml` native build configuration. -- `bin/integration.py` and `bin/serve.py` in favor of downstream integration test (see noxfile). -- `setup.cfg` in favor of `pyproject.toml` metadata configuration. -- Unused `utils.range_scan` function. - -### Fixed -- Converting content to Unicode bytes may insert `utf_8` instead of preferred `utf-8`. (#572) -- Deprecation warning "'count' is passed as positional argument" when converting to Unicode bytes on Python 3.13+ - -## [3.4.0](https://github.com/Ousret/charset_normalizer/compare/3.3.2...3.4.0) (2024-10-08) - -### Added -- Argument `--no-preemptive` in the CLI to prevent the detector to search for hints. -- Support for Python 3.13 (#512) - -### Fixed -- Relax the TypeError exception thrown when trying to compare a CharsetMatch with anything else than a CharsetMatch. -- Improved the general reliability of the detector based on user feedbacks. (#520) (#509) (#498) (#407) (#537) -- Declared charset in content (preemptive detection) not changed when converting to utf-8 bytes. (#381) - -## [3.3.2](https://github.com/Ousret/charset_normalizer/compare/3.3.1...3.3.2) (2023-10-31) - -### Fixed -- Unintentional memory usage regression when using large payload that match several encoding (#376) -- Regression on some detection case showcased in the documentation (#371) - -### Added -- Noise (md) probe that identify malformed arabic representation due to the presence of letters in isolated form (credit to my wife) - -## [3.3.1](https://github.com/Ousret/charset_normalizer/compare/3.3.0...3.3.1) (2023-10-22) - -### Changed -- Optional mypyc compilation upgraded to version 1.6.1 for Python >= 3.8 -- Improved the general detection reliability based on reports from the community - -## [3.3.0](https://github.com/Ousret/charset_normalizer/compare/3.2.0...3.3.0) (2023-09-30) - -### Added -- Allow to execute the CLI (e.g. normalizer) through `python -m charset_normalizer.cli` or `python -m charset_normalizer` -- Support for 9 forgotten encoding that are supported by Python but unlisted in `encoding.aliases` as they have no alias (#323) - -### Removed -- (internal) Redundant utils.is_ascii function and unused function is_private_use_only -- (internal) charset_normalizer.assets is moved inside charset_normalizer.constant - -### Changed -- (internal) Unicode code blocks in constants are updated using the latest v15.0.0 definition to improve detection -- Optional mypyc compilation upgraded to version 1.5.1 for Python >= 3.8 - -### Fixed -- Unable to properly sort CharsetMatch when both chaos/noise and coherence were close due to an unreachable condition in \_\_lt\_\_ (#350) - -## [3.2.0](https://github.com/Ousret/charset_normalizer/compare/3.1.0...3.2.0) (2023-06-07) - -### Changed -- Typehint for function `from_path` no longer enforce `PathLike` as its first argument -- Minor improvement over the global detection reliability - -### Added -- Introduce function `is_binary` that relies on main capabilities, and optimized to detect binaries -- Propagate `enable_fallback` argument throughout `from_bytes`, `from_path`, and `from_fp` that allow a deeper control over the detection (default True) -- Explicit support for Python 3.12 - -### Fixed -- Edge case detection failure where a file would contain 'very-long' camel cased word (Issue #289) - -## [3.1.0](https://github.com/Ousret/charset_normalizer/compare/3.0.1...3.1.0) (2023-03-06) - -### Added -- Argument `should_rename_legacy` for legacy function `detect` and disregard any new arguments without errors (PR #262) - -### Removed -- Support for Python 3.6 (PR #260) - -### Changed -- Optional speedup provided by mypy/c 1.0.1 - -## [3.0.1](https://github.com/Ousret/charset_normalizer/compare/3.0.0...3.0.1) (2022-11-18) - -### Fixed -- Multi-bytes cutter/chunk generator did not always cut correctly (PR #233) - -### Changed -- Speedup provided by mypy/c 0.990 on Python >= 3.7 - -## [3.0.0](https://github.com/Ousret/charset_normalizer/compare/2.1.1...3.0.0) (2022-10-20) - -### Added -- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results -- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES -- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio -- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl) - -### Changed -- Build with static metadata using 'build' frontend -- Make the language detection stricter -- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1 - -### Fixed -- CLI with opt --normalize fail when using full path for files -- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it -- Sphinx warnings when generating the documentation - -### Removed -- Coherence detector no longer return 'Simple English' instead return 'English' -- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese' -- Breaking: Method `first()` and `best()` from CharsetMatch -- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII) -- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches -- Breaking: Top-level function `normalize` -- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch -- Support for the backport `unicodedata2` - -## [3.0.0rc1](https://github.com/Ousret/charset_normalizer/compare/3.0.0b2...3.0.0rc1) (2022-10-18) - -### Added -- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results -- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES -- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio - -### Changed -- Build with static metadata using 'build' frontend -- Make the language detection stricter - -### Fixed -- CLI with opt --normalize fail when using full path for files -- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it - -### Removed -- Coherence detector no longer return 'Simple English' instead return 'English' -- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese' - -## [3.0.0b2](https://github.com/Ousret/charset_normalizer/compare/3.0.0b1...3.0.0b2) (2022-08-21) - -### Added -- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl) - -### Removed -- Breaking: Method `first()` and `best()` from CharsetMatch -- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII) - -### Fixed -- Sphinx warnings when generating the documentation - -## [3.0.0b1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...3.0.0b1) (2022-08-15) - -### Changed -- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1 - -### Removed -- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches -- Breaking: Top-level function `normalize` -- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch -- Support for the backport `unicodedata2` - -## [2.1.1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...2.1.1) (2022-08-19) - -### Deprecated -- Function `normalize` scheduled for removal in 3.0 - -### Changed -- Removed useless call to decode in fn is_unprintable (#206) - -### Fixed -- Third-party library (i18n xgettext) crashing not recognizing utf_8 (PEP 263) with underscore from [@aleksandernovikov](https://github.com/aleksandernovikov) (#204) - -## [2.1.0](https://github.com/Ousret/charset_normalizer/compare/2.0.12...2.1.0) (2022-06-19) - -### Added -- Output the Unicode table version when running the CLI with `--version` (PR #194) - -### Changed -- Re-use decoded buffer for single byte character sets from [@nijel](https://github.com/nijel) (PR #175) -- Fixing some performance bottlenecks from [@deedy5](https://github.com/deedy5) (PR #183) - -### Fixed -- Workaround potential bug in cpython with Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space (PR #175) -- CLI default threshold aligned with the API threshold from [@oleksandr-kuzmenko](https://github.com/oleksandr-kuzmenko) (PR #181) - -### Removed -- Support for Python 3.5 (PR #192) - -### Deprecated -- Use of backport unicodedata from `unicodedata2` as Python is quickly catching up, scheduled for removal in 3.0 (PR #194) - -## [2.0.12](https://github.com/Ousret/charset_normalizer/compare/2.0.11...2.0.12) (2022-02-12) - -### Fixed -- ASCII miss-detection on rare cases (PR #170) - -## [2.0.11](https://github.com/Ousret/charset_normalizer/compare/2.0.10...2.0.11) (2022-01-30) - -### Added -- Explicit support for Python 3.11 (PR #164) - -### Changed -- The logging behavior have been completely reviewed, now using only TRACE and DEBUG levels (PR #163 #165) - -## [2.0.10](https://github.com/Ousret/charset_normalizer/compare/2.0.9...2.0.10) (2022-01-04) - -### Fixed -- Fallback match entries might lead to UnicodeDecodeError for large bytes sequence (PR #154) - -### Changed -- Skipping the language-detection (CD) on ASCII (PR #155) - -## [2.0.9](https://github.com/Ousret/charset_normalizer/compare/2.0.8...2.0.9) (2021-12-03) - -### Changed -- Moderating the logging impact (since 2.0.8) for specific environments (PR #147) - -### Fixed -- Wrong logging level applied when setting kwarg `explain` to True (PR #146) - -## [2.0.8](https://github.com/Ousret/charset_normalizer/compare/2.0.7...2.0.8) (2021-11-24) -### Changed -- Improvement over Vietnamese detection (PR #126) -- MD improvement on trailing data and long foreign (non-pure latin) data (PR #124) -- Efficiency improvements in cd/alphabet_languages from [@adbar](https://github.com/adbar) (PR #122) -- call sum() without an intermediary list following PEP 289 recommendations from [@adbar](https://github.com/adbar) (PR #129) -- Code style as refactored by Sourcery-AI (PR #131) -- Minor adjustment on the MD around european words (PR #133) -- Remove and replace SRTs from assets / tests (PR #139) -- Initialize the library logger with a `NullHandler` by default from [@nmaynes](https://github.com/nmaynes) (PR #135) -- Setting kwarg `explain` to True will add provisionally (bounded to function lifespan) a specific stream handler (PR #135) - -### Fixed -- Fix large (misleading) sequence giving UnicodeDecodeError (PR #137) -- Avoid using too insignificant chunk (PR #137) - -### Added -- Add and expose function `set_logging_handler` to configure a specific StreamHandler from [@nmaynes](https://github.com/nmaynes) (PR #135) -- Add `CHANGELOG.md` entries, format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) (PR #141) - -## [2.0.7](https://github.com/Ousret/charset_normalizer/compare/2.0.6...2.0.7) (2021-10-11) -### Added -- Add support for Kazakh (Cyrillic) language detection (PR #109) - -### Changed -- Further, improve inferring the language from a given single-byte code page (PR #112) -- Vainly trying to leverage PEP263 when PEP3120 is not supported (PR #116) -- Refactoring for potential performance improvements in loops from [@adbar](https://github.com/adbar) (PR #113) -- Various detection improvement (MD+CD) (PR #117) - -### Removed -- Remove redundant logging entry about detected language(s) (PR #115) - -### Fixed -- Fix a minor inconsistency between Python 3.5 and other versions regarding language detection (PR #117 #102) - -## [2.0.6](https://github.com/Ousret/charset_normalizer/compare/2.0.5...2.0.6) (2021-09-18) -### Fixed -- Unforeseen regression with the loss of the backward-compatibility with some older minor of Python 3.5.x (PR #100) -- Fix CLI crash when using --minimal output in certain cases (PR #103) - -### Changed -- Minor improvement to the detection efficiency (less than 1%) (PR #106 #101) - -## [2.0.5](https://github.com/Ousret/charset_normalizer/compare/2.0.4...2.0.5) (2021-09-14) -### Changed -- The project now comply with: flake8, mypy, isort and black to ensure a better overall quality (PR #81) -- The BC-support with v1.x was improved, the old staticmethods are restored (PR #82) -- The Unicode detection is slightly improved (PR #93) -- Add syntax sugar \_\_bool\_\_ for results CharsetMatches list-container (PR #91) - -### Removed -- The project no longer raise warning on tiny content given for detection, will be simply logged as warning instead (PR #92) - -### Fixed -- In some rare case, the chunks extractor could cut in the middle of a multi-byte character and could mislead the mess detection (PR #95) -- Some rare 'space' characters could trip up the UnprintablePlugin/Mess detection (PR #96) -- The MANIFEST.in was not exhaustive (PR #78) - -## [2.0.4](https://github.com/Ousret/charset_normalizer/compare/2.0.3...2.0.4) (2021-07-30) -### Fixed -- The CLI no longer raise an unexpected exception when no encoding has been found (PR #70) -- Fix accessing the 'alphabets' property when the payload contains surrogate characters (PR #68) -- The logger could mislead (explain=True) on detected languages and the impact of one MBCS match (PR #72) -- Submatch factoring could be wrong in rare edge cases (PR #72) -- Multiple files given to the CLI were ignored when publishing results to STDOUT. (After the first path) (PR #72) -- Fix line endings from CRLF to LF for certain project files (PR #67) - -### Changed -- Adjust the MD to lower the sensitivity, thus improving the global detection reliability (PR #69 #76) -- Allow fallback on specified encoding if any (PR #71) - -## [2.0.3](https://github.com/Ousret/charset_normalizer/compare/2.0.2...2.0.3) (2021-07-16) -### Changed -- Part of the detection mechanism has been improved to be less sensitive, resulting in more accurate detection results. Especially ASCII. (PR #63) -- According to the community wishes, the detection will fall back on ASCII or UTF-8 in a last-resort case. (PR #64) - -## [2.0.2](https://github.com/Ousret/charset_normalizer/compare/2.0.1...2.0.2) (2021-07-15) -### Fixed -- Empty/Too small JSON payload miss-detection fixed. Report from [@tseaver](https://github.com/tseaver) (PR #59) - -### Changed -- Don't inject unicodedata2 into sys.modules from [@akx](https://github.com/akx) (PR #57) - -## [2.0.1](https://github.com/Ousret/charset_normalizer/compare/2.0.0...2.0.1) (2021-07-13) -### Fixed -- Make it work where there isn't a filesystem available, dropping assets frequencies.json. Report from [@sethmlarson](https://github.com/sethmlarson). (PR #55) -- Using explain=False permanently disable the verbose output in the current runtime (PR #47) -- One log entry (language target preemptive) was not show in logs when using explain=True (PR #47) -- Fix undesired exception (ValueError) on getitem of instance CharsetMatches (PR #52) - -### Changed -- Public function normalize default args values were not aligned with from_bytes (PR #53) - -### Added -- You may now use charset aliases in cp_isolation and cp_exclusion arguments (PR #47) - -## [2.0.0](https://github.com/Ousret/charset_normalizer/compare/1.4.1...2.0.0) (2021-07-02) -### Changed -- 4x to 5 times faster than the previous 1.4.0 release. At least 2x faster than Chardet. -- Accent has been made on UTF-8 detection, should perform rather instantaneous. -- The backward compatibility with Chardet has been greatly improved. The legacy detect function returns an identical charset name whenever possible. -- The detection mechanism has been slightly improved, now Turkish content is detected correctly (most of the time) -- The program has been rewritten to ease the readability and maintainability. (+Using static typing)+ -- utf_7 detection has been reinstated. - -### Removed -- This package no longer require anything when used with Python 3.5 (Dropped cached_property) -- Removed support for these languages: Catalan, Esperanto, Kazakh, Baque, Volapük, Azeri, Galician, Nynorsk, Macedonian, and Serbocroatian. -- The exception hook on UnicodeDecodeError has been removed. - -### Deprecated -- Methods coherence_non_latin, w_counter, chaos_secondary_pass of the class CharsetMatch are now deprecated and scheduled for removal in v3.0 - -### Fixed -- The CLI output used the relative path of the file(s). Should be absolute. - -## [1.4.1](https://github.com/Ousret/charset_normalizer/compare/1.4.0...1.4.1) (2021-05-28) -### Fixed -- Logger configuration/usage no longer conflict with others (PR #44) - -## [1.4.0](https://github.com/Ousret/charset_normalizer/compare/1.3.9...1.4.0) (2021-05-21) -### Removed -- Using standard logging instead of using the package loguru. -- Dropping nose test framework in favor of the maintained pytest. -- Choose to not use dragonmapper package to help with gibberish Chinese/CJK text. -- Require cached_property only for Python 3.5 due to constraint. Dropping for every other interpreter version. -- Stop support for UTF-7 that does not contain a SIG. -- Dropping PrettyTable, replaced with pure JSON output in CLI. - -### Fixed -- BOM marker in a CharsetNormalizerMatch instance could be False in rare cases even if obviously present. Due to the sub-match factoring process. -- Not searching properly for the BOM when trying utf32/16 parent codec. - -### Changed -- Improving the package final size by compressing frequencies.json. -- Huge improvement over the larges payload. - -### Added -- CLI now produces JSON consumable output. -- Return ASCII if given sequences fit. Given reasonable confidence. - -## [1.3.9](https://github.com/Ousret/charset_normalizer/compare/1.3.8...1.3.9) (2021-05-13) - -### Fixed -- In some very rare cases, you may end up getting encode/decode errors due to a bad bytes payload (PR #40) - -## [1.3.8](https://github.com/Ousret/charset_normalizer/compare/1.3.7...1.3.8) (2021-05-12) - -### Fixed -- Empty given payload for detection may cause an exception if trying to access the `alphabets` property. (PR #39) - -## [1.3.7](https://github.com/Ousret/charset_normalizer/compare/1.3.6...1.3.7) (2021-05-12) - -### Fixed -- The legacy detect function should return UTF-8-SIG if sig is present in the payload. (PR #38) - -## [1.3.6](https://github.com/Ousret/charset_normalizer/compare/1.3.5...1.3.6) (2021-02-09) - -### Changed -- Amend the previous release to allow prettytable 2.0 (PR #35) - -## [1.3.5](https://github.com/Ousret/charset_normalizer/compare/1.3.4...1.3.5) (2021-02-08) - -### Fixed -- Fix error while using the package with a python pre-release interpreter (PR #33) - -### Changed -- Dependencies refactoring, constraints revised. - -### Added -- Add python 3.9 and 3.10 to the supported interpreters - -MIT License - -Copyright (c) 2025 TAHRI Ahmed R. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/RECORD b/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/RECORD deleted file mode 100644 index 8b64ff5..0000000 --- a/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/RECORD +++ /dev/null @@ -1,35 +0,0 @@ -../../Scripts/normalizer.exe,sha256=b6A9TmeBmJl13JbrRLSvC67XCABYexMxT4L8hQB9ww0,107905 -charset_normalizer-3.4.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -charset_normalizer-3.4.2.dist-info/METADATA,sha256=WneNNyl9QvsRZYzK1FeEC6Wwag4iIFoTAoevPgpZFTY,36474 -charset_normalizer-3.4.2.dist-info/RECORD,, -charset_normalizer-3.4.2.dist-info/WHEEL,sha256=AVu1DavlWPCypqX8sBQ58NLvImUaO9Rw16t7hIak0pw,101 -charset_normalizer-3.4.2.dist-info/entry_points.txt,sha256=8C-Y3iXIfyXQ83Tpir2B8t-XLJYpxF5xbb38d_js-h4,65 -charset_normalizer-3.4.2.dist-info/licenses/LICENSE,sha256=GFd0hdNwTxpHne2OVzwJds_tMV_S_ReYP6mI2kwvcNE,1092 -charset_normalizer-3.4.2.dist-info/top_level.txt,sha256=7ASyzePr8_xuZWJsnqJjIBtyV8vhEo0wBCv1MPRRi3Q,19 -charset_normalizer/__init__.py,sha256=0NT8MHi7SKq3juMqYfOdrkzjisK0L73lneNHH4qaUAs,1638 -charset_normalizer/__main__.py,sha256=2sj_BS6H0sU25C1bMqz9DVwa6kOK9lchSEbSU-_iu7M,115 -charset_normalizer/__pycache__/__init__.cpython-310.pyc,, -charset_normalizer/__pycache__/__main__.cpython-310.pyc,, -charset_normalizer/__pycache__/api.cpython-310.pyc,, -charset_normalizer/__pycache__/cd.cpython-310.pyc,, -charset_normalizer/__pycache__/constant.cpython-310.pyc,, -charset_normalizer/__pycache__/legacy.cpython-310.pyc,, -charset_normalizer/__pycache__/md.cpython-310.pyc,, -charset_normalizer/__pycache__/models.cpython-310.pyc,, -charset_normalizer/__pycache__/utils.cpython-310.pyc,, -charset_normalizer/__pycache__/version.cpython-310.pyc,, -charset_normalizer/api.py,sha256=2a0p2Gnhbdo9O6C04CNxTSN23fIbgOF20nxb0pWPNFM,23285 -charset_normalizer/cd.py,sha256=uq8nVxRpR6Guc16ACvOWtL8KO3w7vYaCh8hHisuOyTg,12917 -charset_normalizer/cli/__init__.py,sha256=d9MUx-1V_qD3x9igIy4JT4oC5CU0yjulk7QyZWeRFhg,144 -charset_normalizer/cli/__main__.py,sha256=-pdJCyPywouPyFsC8_eTSgTmvh1YEvgjsvy1WZ0XjaA,13027 -charset_normalizer/cli/__pycache__/__init__.cpython-310.pyc,, -charset_normalizer/cli/__pycache__/__main__.cpython-310.pyc,, -charset_normalizer/constant.py,sha256=mCJmYzpBU27Ut9kiNWWoBbhhxQ-aRVw3K7LSwoFwBGI,44728 -charset_normalizer/legacy.py,sha256=NgK-8ZQa_M9FHgQjdNSiYzMaB332QGuElZSfCf2y2sQ,2351 -charset_normalizer/md.cp310-win_amd64.pyd,sha256=nVB8MQoL3zbH_TwpkkT5LVviMxP7-ZVdJ6vO-D0q06Q,10752 -charset_normalizer/md.py,sha256=LSuW2hNgXSgF7JGdRapLAHLuj6pABHiP85LTNAYmu7c,20780 -charset_normalizer/md__mypyc.cp310-win_amd64.pyd,sha256=n9N3u1ftw-olnp75d80uuG-H-s0Sjrk0Nawm6KukcAc,122880 -charset_normalizer/models.py,sha256=ZR2PE-fqf6dASZfqdE5Uhkmr0o1MciSdXOjuNqwkmvg,12754 -charset_normalizer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -charset_normalizer/utils.py,sha256=XtWIQeOuz7cnGebMzyi4Vvi1JtA84QBSIeR9PDzF7pw,12584 -charset_normalizer/version.py,sha256=wtpyUZ7M57rCLclP3QjzRD0Nj2hvnMOzLZI-vwfTdWs,123 diff --git a/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/WHEEL b/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/WHEEL deleted file mode 100644 index cd0a146..0000000 --- a/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (80.1.0) -Root-Is-Purelib: false -Tag: cp310-cp310-win_amd64 - diff --git a/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/entry_points.txt b/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/entry_points.txt deleted file mode 100644 index ec92012..0000000 --- a/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[console_scripts] -normalizer = charset_normalizer:cli.cli_detect diff --git a/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/licenses/LICENSE b/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/licenses/LICENSE deleted file mode 100644 index 9725772..0000000 --- a/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/licenses/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 TAHRI Ahmed R. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/top_level.txt b/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/top_level.txt deleted file mode 100644 index 66958f0..0000000 --- a/venv/Lib/site-packages/charset_normalizer-3.4.2.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -charset_normalizer diff --git a/venv/Lib/site-packages/charset_normalizer/__init__.py b/venv/Lib/site-packages/charset_normalizer/__init__.py deleted file mode 100644 index 0d3a379..0000000 --- a/venv/Lib/site-packages/charset_normalizer/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Charset-Normalizer -~~~~~~~~~~~~~~ -The Real First Universal Charset Detector. -A library that helps you read text from an unknown charset encoding. -Motivated by chardet, This package is trying to resolve the issue by taking a new approach. -All IANA character set names for which the Python core library provides codecs are supported. - -Basic usage: - >>> from charset_normalizer import from_bytes - >>> results = from_bytes('Bсеки човек има право на образование. Oбразованието!'.encode('utf_8')) - >>> best_guess = results.best() - >>> str(best_guess) - 'Bсеки човек има право на образование. Oбразованието!' - -Others methods and usages are available - see the full documentation -at . -:copyright: (c) 2021 by Ahmed TAHRI -:license: MIT, see LICENSE for more details. -""" - -from __future__ import annotations - -import logging - -from .api import from_bytes, from_fp, from_path, is_binary -from .legacy import detect -from .models import CharsetMatch, CharsetMatches -from .utils import set_logging_handler -from .version import VERSION, __version__ - -__all__ = ( - "from_fp", - "from_path", - "from_bytes", - "is_binary", - "detect", - "CharsetMatch", - "CharsetMatches", - "__version__", - "VERSION", - "set_logging_handler", -) - -# Attach a NullHandler to the top level logger by default -# https://docs.python.org/3.3/howto/logging.html#configuring-logging-for-a-library - -logging.getLogger("charset_normalizer").addHandler(logging.NullHandler()) diff --git a/venv/Lib/site-packages/charset_normalizer/__main__.py b/venv/Lib/site-packages/charset_normalizer/__main__.py deleted file mode 100644 index e0e76f7..0000000 --- a/venv/Lib/site-packages/charset_normalizer/__main__.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import annotations - -from .cli import cli_detect - -if __name__ == "__main__": - cli_detect() diff --git a/venv/Lib/site-packages/charset_normalizer/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/charset_normalizer/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 0e65d58..0000000 Binary files a/venv/Lib/site-packages/charset_normalizer/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/charset_normalizer/__pycache__/__main__.cpython-310.pyc b/venv/Lib/site-packages/charset_normalizer/__pycache__/__main__.cpython-310.pyc deleted file mode 100644 index 0e9fb5d..0000000 Binary files a/venv/Lib/site-packages/charset_normalizer/__pycache__/__main__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/charset_normalizer/__pycache__/api.cpython-310.pyc b/venv/Lib/site-packages/charset_normalizer/__pycache__/api.cpython-310.pyc deleted file mode 100644 index 25ee96c..0000000 Binary files a/venv/Lib/site-packages/charset_normalizer/__pycache__/api.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/charset_normalizer/__pycache__/cd.cpython-310.pyc b/venv/Lib/site-packages/charset_normalizer/__pycache__/cd.cpython-310.pyc deleted file mode 100644 index 9fd1d4f..0000000 Binary files a/venv/Lib/site-packages/charset_normalizer/__pycache__/cd.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/charset_normalizer/__pycache__/constant.cpython-310.pyc b/venv/Lib/site-packages/charset_normalizer/__pycache__/constant.cpython-310.pyc deleted file mode 100644 index 5bbba9a..0000000 Binary files a/venv/Lib/site-packages/charset_normalizer/__pycache__/constant.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/charset_normalizer/__pycache__/legacy.cpython-310.pyc b/venv/Lib/site-packages/charset_normalizer/__pycache__/legacy.cpython-310.pyc deleted file mode 100644 index 6bdeadb..0000000 Binary files a/venv/Lib/site-packages/charset_normalizer/__pycache__/legacy.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/charset_normalizer/__pycache__/md.cpython-310.pyc b/venv/Lib/site-packages/charset_normalizer/__pycache__/md.cpython-310.pyc deleted file mode 100644 index 0c12628..0000000 Binary files a/venv/Lib/site-packages/charset_normalizer/__pycache__/md.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/charset_normalizer/__pycache__/models.cpython-310.pyc b/venv/Lib/site-packages/charset_normalizer/__pycache__/models.cpython-310.pyc deleted file mode 100644 index 369862a..0000000 Binary files a/venv/Lib/site-packages/charset_normalizer/__pycache__/models.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/charset_normalizer/__pycache__/utils.cpython-310.pyc b/venv/Lib/site-packages/charset_normalizer/__pycache__/utils.cpython-310.pyc deleted file mode 100644 index 485ffe3..0000000 Binary files a/venv/Lib/site-packages/charset_normalizer/__pycache__/utils.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/charset_normalizer/__pycache__/version.cpython-310.pyc b/venv/Lib/site-packages/charset_normalizer/__pycache__/version.cpython-310.pyc deleted file mode 100644 index 9b45ebf..0000000 Binary files a/venv/Lib/site-packages/charset_normalizer/__pycache__/version.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/charset_normalizer/api.py b/venv/Lib/site-packages/charset_normalizer/api.py deleted file mode 100644 index 2c8c061..0000000 --- a/venv/Lib/site-packages/charset_normalizer/api.py +++ /dev/null @@ -1,668 +0,0 @@ -from __future__ import annotations - -import logging -from os import PathLike -from typing import BinaryIO - -from .cd import ( - coherence_ratio, - encoding_languages, - mb_encoding_languages, - merge_coherence_ratios, -) -from .constant import IANA_SUPPORTED, TOO_BIG_SEQUENCE, TOO_SMALL_SEQUENCE, TRACE -from .md import mess_ratio -from .models import CharsetMatch, CharsetMatches -from .utils import ( - any_specified_encoding, - cut_sequence_chunks, - iana_name, - identify_sig_or_bom, - is_cp_similar, - is_multi_byte_encoding, - should_strip_sig_or_bom, -) - -logger = logging.getLogger("charset_normalizer") -explain_handler = logging.StreamHandler() -explain_handler.setFormatter( - logging.Formatter("%(asctime)s | %(levelname)s | %(message)s") -) - - -def from_bytes( - sequences: bytes | bytearray, - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.2, - cp_isolation: list[str] | None = None, - cp_exclusion: list[str] | None = None, - preemptive_behaviour: bool = True, - explain: bool = False, - language_threshold: float = 0.1, - enable_fallback: bool = True, -) -> CharsetMatches: - """ - Given a raw bytes sequence, return the best possibles charset usable to render str objects. - If there is no results, it is a strong indicator that the source is binary/not text. - By default, the process will extract 5 blocks of 512o each to assess the mess and coherence of a given sequence. - And will give up a particular code page after 20% of measured mess. Those criteria are customizable at will. - - The preemptive behavior DOES NOT replace the traditional detection workflow, it prioritize a particular code page - but never take it for granted. Can improve the performance. - - You may want to focus your attention to some code page or/and not others, use cp_isolation and cp_exclusion for that - purpose. - - This function will strip the SIG in the payload/sequence every time except on UTF-16, UTF-32. - By default the library does not setup any handler other than the NullHandler, if you choose to set the 'explain' - toggle to True it will alter the logger configuration to add a StreamHandler that is suitable for debugging. - Custom logging format and handler can be set manually. - """ - - if not isinstance(sequences, (bytearray, bytes)): - raise TypeError( - "Expected object of type bytes or bytearray, got: {}".format( - type(sequences) - ) - ) - - if explain: - previous_logger_level: int = logger.level - logger.addHandler(explain_handler) - logger.setLevel(TRACE) - - length: int = len(sequences) - - if length == 0: - logger.debug("Encoding detection on empty bytes, assuming utf_8 intention.") - if explain: # Defensive: ensure exit path clean handler - logger.removeHandler(explain_handler) - logger.setLevel(previous_logger_level or logging.WARNING) - return CharsetMatches([CharsetMatch(sequences, "utf_8", 0.0, False, [], "")]) - - if cp_isolation is not None: - logger.log( - TRACE, - "cp_isolation is set. use this flag for debugging purpose. " - "limited list of encoding allowed : %s.", - ", ".join(cp_isolation), - ) - cp_isolation = [iana_name(cp, False) for cp in cp_isolation] - else: - cp_isolation = [] - - if cp_exclusion is not None: - logger.log( - TRACE, - "cp_exclusion is set. use this flag for debugging purpose. " - "limited list of encoding excluded : %s.", - ", ".join(cp_exclusion), - ) - cp_exclusion = [iana_name(cp, False) for cp in cp_exclusion] - else: - cp_exclusion = [] - - if length <= (chunk_size * steps): - logger.log( - TRACE, - "override steps (%i) and chunk_size (%i) as content does not fit (%i byte(s) given) parameters.", - steps, - chunk_size, - length, - ) - steps = 1 - chunk_size = length - - if steps > 1 and length / steps < chunk_size: - chunk_size = int(length / steps) - - is_too_small_sequence: bool = len(sequences) < TOO_SMALL_SEQUENCE - is_too_large_sequence: bool = len(sequences) >= TOO_BIG_SEQUENCE - - if is_too_small_sequence: - logger.log( - TRACE, - "Trying to detect encoding from a tiny portion of ({}) byte(s).".format( - length - ), - ) - elif is_too_large_sequence: - logger.log( - TRACE, - "Using lazy str decoding because the payload is quite large, ({}) byte(s).".format( - length - ), - ) - - prioritized_encodings: list[str] = [] - - specified_encoding: str | None = ( - any_specified_encoding(sequences) if preemptive_behaviour else None - ) - - if specified_encoding is not None: - prioritized_encodings.append(specified_encoding) - logger.log( - TRACE, - "Detected declarative mark in sequence. Priority +1 given for %s.", - specified_encoding, - ) - - tested: set[str] = set() - tested_but_hard_failure: list[str] = [] - tested_but_soft_failure: list[str] = [] - - fallback_ascii: CharsetMatch | None = None - fallback_u8: CharsetMatch | None = None - fallback_specified: CharsetMatch | None = None - - results: CharsetMatches = CharsetMatches() - - early_stop_results: CharsetMatches = CharsetMatches() - - sig_encoding, sig_payload = identify_sig_or_bom(sequences) - - if sig_encoding is not None: - prioritized_encodings.append(sig_encoding) - logger.log( - TRACE, - "Detected a SIG or BOM mark on first %i byte(s). Priority +1 given for %s.", - len(sig_payload), - sig_encoding, - ) - - prioritized_encodings.append("ascii") - - if "utf_8" not in prioritized_encodings: - prioritized_encodings.append("utf_8") - - for encoding_iana in prioritized_encodings + IANA_SUPPORTED: - if cp_isolation and encoding_iana not in cp_isolation: - continue - - if cp_exclusion and encoding_iana in cp_exclusion: - continue - - if encoding_iana in tested: - continue - - tested.add(encoding_iana) - - decoded_payload: str | None = None - bom_or_sig_available: bool = sig_encoding == encoding_iana - strip_sig_or_bom: bool = bom_or_sig_available and should_strip_sig_or_bom( - encoding_iana - ) - - if encoding_iana in {"utf_16", "utf_32"} and not bom_or_sig_available: - logger.log( - TRACE, - "Encoding %s won't be tested as-is because it require a BOM. Will try some sub-encoder LE/BE.", - encoding_iana, - ) - continue - if encoding_iana in {"utf_7"} and not bom_or_sig_available: - logger.log( - TRACE, - "Encoding %s won't be tested as-is because detection is unreliable without BOM/SIG.", - encoding_iana, - ) - continue - - try: - is_multi_byte_decoder: bool = is_multi_byte_encoding(encoding_iana) - except (ModuleNotFoundError, ImportError): - logger.log( - TRACE, - "Encoding %s does not provide an IncrementalDecoder", - encoding_iana, - ) - continue - - try: - if is_too_large_sequence and is_multi_byte_decoder is False: - str( - ( - sequences[: int(50e4)] - if strip_sig_or_bom is False - else sequences[len(sig_payload) : int(50e4)] - ), - encoding=encoding_iana, - ) - else: - decoded_payload = str( - ( - sequences - if strip_sig_or_bom is False - else sequences[len(sig_payload) :] - ), - encoding=encoding_iana, - ) - except (UnicodeDecodeError, LookupError) as e: - if not isinstance(e, LookupError): - logger.log( - TRACE, - "Code page %s does not fit given bytes sequence at ALL. %s", - encoding_iana, - str(e), - ) - tested_but_hard_failure.append(encoding_iana) - continue - - similar_soft_failure_test: bool = False - - for encoding_soft_failed in tested_but_soft_failure: - if is_cp_similar(encoding_iana, encoding_soft_failed): - similar_soft_failure_test = True - break - - if similar_soft_failure_test: - logger.log( - TRACE, - "%s is deemed too similar to code page %s and was consider unsuited already. Continuing!", - encoding_iana, - encoding_soft_failed, - ) - continue - - r_ = range( - 0 if not bom_or_sig_available else len(sig_payload), - length, - int(length / steps), - ) - - multi_byte_bonus: bool = ( - is_multi_byte_decoder - and decoded_payload is not None - and len(decoded_payload) < length - ) - - if multi_byte_bonus: - logger.log( - TRACE, - "Code page %s is a multi byte encoding table and it appear that at least one character " - "was encoded using n-bytes.", - encoding_iana, - ) - - max_chunk_gave_up: int = int(len(r_) / 4) - - max_chunk_gave_up = max(max_chunk_gave_up, 2) - early_stop_count: int = 0 - lazy_str_hard_failure = False - - md_chunks: list[str] = [] - md_ratios = [] - - try: - for chunk in cut_sequence_chunks( - sequences, - encoding_iana, - r_, - chunk_size, - bom_or_sig_available, - strip_sig_or_bom, - sig_payload, - is_multi_byte_decoder, - decoded_payload, - ): - md_chunks.append(chunk) - - md_ratios.append( - mess_ratio( - chunk, - threshold, - explain is True and 1 <= len(cp_isolation) <= 2, - ) - ) - - if md_ratios[-1] >= threshold: - early_stop_count += 1 - - if (early_stop_count >= max_chunk_gave_up) or ( - bom_or_sig_available and strip_sig_or_bom is False - ): - break - except ( - UnicodeDecodeError - ) as e: # Lazy str loading may have missed something there - logger.log( - TRACE, - "LazyStr Loading: After MD chunk decode, code page %s does not fit given bytes sequence at ALL. %s", - encoding_iana, - str(e), - ) - early_stop_count = max_chunk_gave_up - lazy_str_hard_failure = True - - # We might want to check the sequence again with the whole content - # Only if initial MD tests passes - if ( - not lazy_str_hard_failure - and is_too_large_sequence - and not is_multi_byte_decoder - ): - try: - sequences[int(50e3) :].decode(encoding_iana, errors="strict") - except UnicodeDecodeError as e: - logger.log( - TRACE, - "LazyStr Loading: After final lookup, code page %s does not fit given bytes sequence at ALL. %s", - encoding_iana, - str(e), - ) - tested_but_hard_failure.append(encoding_iana) - continue - - mean_mess_ratio: float = sum(md_ratios) / len(md_ratios) if md_ratios else 0.0 - if mean_mess_ratio >= threshold or early_stop_count >= max_chunk_gave_up: - tested_but_soft_failure.append(encoding_iana) - logger.log( - TRACE, - "%s was excluded because of initial chaos probing. Gave up %i time(s). " - "Computed mean chaos is %f %%.", - encoding_iana, - early_stop_count, - round(mean_mess_ratio * 100, ndigits=3), - ) - # Preparing those fallbacks in case we got nothing. - if ( - enable_fallback - and encoding_iana in ["ascii", "utf_8", specified_encoding] - and not lazy_str_hard_failure - ): - fallback_entry = CharsetMatch( - sequences, - encoding_iana, - threshold, - False, - [], - decoded_payload, - preemptive_declaration=specified_encoding, - ) - if encoding_iana == specified_encoding: - fallback_specified = fallback_entry - elif encoding_iana == "ascii": - fallback_ascii = fallback_entry - else: - fallback_u8 = fallback_entry - continue - - logger.log( - TRACE, - "%s passed initial chaos probing. Mean measured chaos is %f %%", - encoding_iana, - round(mean_mess_ratio * 100, ndigits=3), - ) - - if not is_multi_byte_decoder: - target_languages: list[str] = encoding_languages(encoding_iana) - else: - target_languages = mb_encoding_languages(encoding_iana) - - if target_languages: - logger.log( - TRACE, - "{} should target any language(s) of {}".format( - encoding_iana, str(target_languages) - ), - ) - - cd_ratios = [] - - # We shall skip the CD when its about ASCII - # Most of the time its not relevant to run "language-detection" on it. - if encoding_iana != "ascii": - for chunk in md_chunks: - chunk_languages = coherence_ratio( - chunk, - language_threshold, - ",".join(target_languages) if target_languages else None, - ) - - cd_ratios.append(chunk_languages) - - cd_ratios_merged = merge_coherence_ratios(cd_ratios) - - if cd_ratios_merged: - logger.log( - TRACE, - "We detected language {} using {}".format( - cd_ratios_merged, encoding_iana - ), - ) - - current_match = CharsetMatch( - sequences, - encoding_iana, - mean_mess_ratio, - bom_or_sig_available, - cd_ratios_merged, - ( - decoded_payload - if ( - is_too_large_sequence is False - or encoding_iana in [specified_encoding, "ascii", "utf_8"] - ) - else None - ), - preemptive_declaration=specified_encoding, - ) - - results.append(current_match) - - if ( - encoding_iana in [specified_encoding, "ascii", "utf_8"] - and mean_mess_ratio < 0.1 - ): - # If md says nothing to worry about, then... stop immediately! - if mean_mess_ratio == 0.0: - logger.debug( - "Encoding detection: %s is most likely the one.", - current_match.encoding, - ) - if explain: # Defensive: ensure exit path clean handler - logger.removeHandler(explain_handler) - logger.setLevel(previous_logger_level) - return CharsetMatches([current_match]) - - early_stop_results.append(current_match) - - if ( - len(early_stop_results) - and (specified_encoding is None or specified_encoding in tested) - and "ascii" in tested - and "utf_8" in tested - ): - probable_result: CharsetMatch = early_stop_results.best() # type: ignore[assignment] - logger.debug( - "Encoding detection: %s is most likely the one.", - probable_result.encoding, - ) - if explain: # Defensive: ensure exit path clean handler - logger.removeHandler(explain_handler) - logger.setLevel(previous_logger_level) - - return CharsetMatches([probable_result]) - - if encoding_iana == sig_encoding: - logger.debug( - "Encoding detection: %s is most likely the one as we detected a BOM or SIG within " - "the beginning of the sequence.", - encoding_iana, - ) - if explain: # Defensive: ensure exit path clean handler - logger.removeHandler(explain_handler) - logger.setLevel(previous_logger_level) - return CharsetMatches([results[encoding_iana]]) - - if len(results) == 0: - if fallback_u8 or fallback_ascii or fallback_specified: - logger.log( - TRACE, - "Nothing got out of the detection process. Using ASCII/UTF-8/Specified fallback.", - ) - - if fallback_specified: - logger.debug( - "Encoding detection: %s will be used as a fallback match", - fallback_specified.encoding, - ) - results.append(fallback_specified) - elif ( - (fallback_u8 and fallback_ascii is None) - or ( - fallback_u8 - and fallback_ascii - and fallback_u8.fingerprint != fallback_ascii.fingerprint - ) - or (fallback_u8 is not None) - ): - logger.debug("Encoding detection: utf_8 will be used as a fallback match") - results.append(fallback_u8) - elif fallback_ascii: - logger.debug("Encoding detection: ascii will be used as a fallback match") - results.append(fallback_ascii) - - if results: - logger.debug( - "Encoding detection: Found %s as plausible (best-candidate) for content. With %i alternatives.", - results.best().encoding, # type: ignore - len(results) - 1, - ) - else: - logger.debug("Encoding detection: Unable to determine any suitable charset.") - - if explain: - logger.removeHandler(explain_handler) - logger.setLevel(previous_logger_level) - - return results - - -def from_fp( - fp: BinaryIO, - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.20, - cp_isolation: list[str] | None = None, - cp_exclusion: list[str] | None = None, - preemptive_behaviour: bool = True, - explain: bool = False, - language_threshold: float = 0.1, - enable_fallback: bool = True, -) -> CharsetMatches: - """ - Same thing than the function from_bytes but using a file pointer that is already ready. - Will not close the file pointer. - """ - return from_bytes( - fp.read(), - steps, - chunk_size, - threshold, - cp_isolation, - cp_exclusion, - preemptive_behaviour, - explain, - language_threshold, - enable_fallback, - ) - - -def from_path( - path: str | bytes | PathLike, # type: ignore[type-arg] - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.20, - cp_isolation: list[str] | None = None, - cp_exclusion: list[str] | None = None, - preemptive_behaviour: bool = True, - explain: bool = False, - language_threshold: float = 0.1, - enable_fallback: bool = True, -) -> CharsetMatches: - """ - Same thing than the function from_bytes but with one extra step. Opening and reading given file path in binary mode. - Can raise IOError. - """ - with open(path, "rb") as fp: - return from_fp( - fp, - steps, - chunk_size, - threshold, - cp_isolation, - cp_exclusion, - preemptive_behaviour, - explain, - language_threshold, - enable_fallback, - ) - - -def is_binary( - fp_or_path_or_payload: PathLike | str | BinaryIO | bytes, # type: ignore[type-arg] - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.20, - cp_isolation: list[str] | None = None, - cp_exclusion: list[str] | None = None, - preemptive_behaviour: bool = True, - explain: bool = False, - language_threshold: float = 0.1, - enable_fallback: bool = False, -) -> bool: - """ - Detect if the given input (file, bytes, or path) points to a binary file. aka. not a string. - Based on the same main heuristic algorithms and default kwargs at the sole exception that fallbacks match - are disabled to be stricter around ASCII-compatible but unlikely to be a string. - """ - if isinstance(fp_or_path_or_payload, (str, PathLike)): - guesses = from_path( - fp_or_path_or_payload, - steps=steps, - chunk_size=chunk_size, - threshold=threshold, - cp_isolation=cp_isolation, - cp_exclusion=cp_exclusion, - preemptive_behaviour=preemptive_behaviour, - explain=explain, - language_threshold=language_threshold, - enable_fallback=enable_fallback, - ) - elif isinstance( - fp_or_path_or_payload, - ( - bytes, - bytearray, - ), - ): - guesses = from_bytes( - fp_or_path_or_payload, - steps=steps, - chunk_size=chunk_size, - threshold=threshold, - cp_isolation=cp_isolation, - cp_exclusion=cp_exclusion, - preemptive_behaviour=preemptive_behaviour, - explain=explain, - language_threshold=language_threshold, - enable_fallback=enable_fallback, - ) - else: - guesses = from_fp( - fp_or_path_or_payload, - steps=steps, - chunk_size=chunk_size, - threshold=threshold, - cp_isolation=cp_isolation, - cp_exclusion=cp_exclusion, - preemptive_behaviour=preemptive_behaviour, - explain=explain, - language_threshold=language_threshold, - enable_fallback=enable_fallback, - ) - - return not guesses diff --git a/venv/Lib/site-packages/charset_normalizer/cd.py b/venv/Lib/site-packages/charset_normalizer/cd.py deleted file mode 100644 index 71a3ed5..0000000 --- a/venv/Lib/site-packages/charset_normalizer/cd.py +++ /dev/null @@ -1,395 +0,0 @@ -from __future__ import annotations - -import importlib -from codecs import IncrementalDecoder -from collections import Counter -from functools import lru_cache -from typing import Counter as TypeCounter - -from .constant import ( - FREQUENCIES, - KO_NAMES, - LANGUAGE_SUPPORTED_COUNT, - TOO_SMALL_SEQUENCE, - ZH_NAMES, -) -from .md import is_suspiciously_successive_range -from .models import CoherenceMatches -from .utils import ( - is_accentuated, - is_latin, - is_multi_byte_encoding, - is_unicode_range_secondary, - unicode_range, -) - - -def encoding_unicode_range(iana_name: str) -> list[str]: - """ - Return associated unicode ranges in a single byte code page. - """ - if is_multi_byte_encoding(iana_name): - raise OSError("Function not supported on multi-byte code page") - - decoder = importlib.import_module(f"encodings.{iana_name}").IncrementalDecoder - - p: IncrementalDecoder = decoder(errors="ignore") - seen_ranges: dict[str, int] = {} - character_count: int = 0 - - for i in range(0x40, 0xFF): - chunk: str = p.decode(bytes([i])) - - if chunk: - character_range: str | None = unicode_range(chunk) - - if character_range is None: - continue - - if is_unicode_range_secondary(character_range) is False: - if character_range not in seen_ranges: - seen_ranges[character_range] = 0 - seen_ranges[character_range] += 1 - character_count += 1 - - return sorted( - [ - character_range - for character_range in seen_ranges - if seen_ranges[character_range] / character_count >= 0.15 - ] - ) - - -def unicode_range_languages(primary_range: str) -> list[str]: - """ - Return inferred languages used with a unicode range. - """ - languages: list[str] = [] - - for language, characters in FREQUENCIES.items(): - for character in characters: - if unicode_range(character) == primary_range: - languages.append(language) - break - - return languages - - -@lru_cache() -def encoding_languages(iana_name: str) -> list[str]: - """ - Single-byte encoding language association. Some code page are heavily linked to particular language(s). - This function does the correspondence. - """ - unicode_ranges: list[str] = encoding_unicode_range(iana_name) - primary_range: str | None = None - - for specified_range in unicode_ranges: - if "Latin" not in specified_range: - primary_range = specified_range - break - - if primary_range is None: - return ["Latin Based"] - - return unicode_range_languages(primary_range) - - -@lru_cache() -def mb_encoding_languages(iana_name: str) -> list[str]: - """ - Multi-byte encoding language association. Some code page are heavily linked to particular language(s). - This function does the correspondence. - """ - if ( - iana_name.startswith("shift_") - or iana_name.startswith("iso2022_jp") - or iana_name.startswith("euc_j") - or iana_name == "cp932" - ): - return ["Japanese"] - if iana_name.startswith("gb") or iana_name in ZH_NAMES: - return ["Chinese"] - if iana_name.startswith("iso2022_kr") or iana_name in KO_NAMES: - return ["Korean"] - - return [] - - -@lru_cache(maxsize=LANGUAGE_SUPPORTED_COUNT) -def get_target_features(language: str) -> tuple[bool, bool]: - """ - Determine main aspects from a supported language if it contains accents and if is pure Latin. - """ - target_have_accents: bool = False - target_pure_latin: bool = True - - for character in FREQUENCIES[language]: - if not target_have_accents and is_accentuated(character): - target_have_accents = True - if target_pure_latin and is_latin(character) is False: - target_pure_latin = False - - return target_have_accents, target_pure_latin - - -def alphabet_languages( - characters: list[str], ignore_non_latin: bool = False -) -> list[str]: - """ - Return associated languages associated to given characters. - """ - languages: list[tuple[str, float]] = [] - - source_have_accents = any(is_accentuated(character) for character in characters) - - for language, language_characters in FREQUENCIES.items(): - target_have_accents, target_pure_latin = get_target_features(language) - - if ignore_non_latin and target_pure_latin is False: - continue - - if target_have_accents is False and source_have_accents: - continue - - character_count: int = len(language_characters) - - character_match_count: int = len( - [c for c in language_characters if c in characters] - ) - - ratio: float = character_match_count / character_count - - if ratio >= 0.2: - languages.append((language, ratio)) - - languages = sorted(languages, key=lambda x: x[1], reverse=True) - - return [compatible_language[0] for compatible_language in languages] - - -def characters_popularity_compare( - language: str, ordered_characters: list[str] -) -> float: - """ - Determine if a ordered characters list (by occurrence from most appearance to rarest) match a particular language. - The result is a ratio between 0. (absolutely no correspondence) and 1. (near perfect fit). - Beware that is function is not strict on the match in order to ease the detection. (Meaning close match is 1.) - """ - if language not in FREQUENCIES: - raise ValueError(f"{language} not available") - - character_approved_count: int = 0 - FREQUENCIES_language_set = set(FREQUENCIES[language]) - - ordered_characters_count: int = len(ordered_characters) - target_language_characters_count: int = len(FREQUENCIES[language]) - - large_alphabet: bool = target_language_characters_count > 26 - - for character, character_rank in zip( - ordered_characters, range(0, ordered_characters_count) - ): - if character not in FREQUENCIES_language_set: - continue - - character_rank_in_language: int = FREQUENCIES[language].index(character) - expected_projection_ratio: float = ( - target_language_characters_count / ordered_characters_count - ) - character_rank_projection: int = int(character_rank * expected_projection_ratio) - - if ( - large_alphabet is False - and abs(character_rank_projection - character_rank_in_language) > 4 - ): - continue - - if ( - large_alphabet is True - and abs(character_rank_projection - character_rank_in_language) - < target_language_characters_count / 3 - ): - character_approved_count += 1 - continue - - characters_before_source: list[str] = FREQUENCIES[language][ - 0:character_rank_in_language - ] - characters_after_source: list[str] = FREQUENCIES[language][ - character_rank_in_language: - ] - characters_before: list[str] = ordered_characters[0:character_rank] - characters_after: list[str] = ordered_characters[character_rank:] - - before_match_count: int = len( - set(characters_before) & set(characters_before_source) - ) - - after_match_count: int = len( - set(characters_after) & set(characters_after_source) - ) - - if len(characters_before_source) == 0 and before_match_count <= 4: - character_approved_count += 1 - continue - - if len(characters_after_source) == 0 and after_match_count <= 4: - character_approved_count += 1 - continue - - if ( - before_match_count / len(characters_before_source) >= 0.4 - or after_match_count / len(characters_after_source) >= 0.4 - ): - character_approved_count += 1 - continue - - return character_approved_count / len(ordered_characters) - - -def alpha_unicode_split(decoded_sequence: str) -> list[str]: - """ - Given a decoded text sequence, return a list of str. Unicode range / alphabet separation. - Ex. a text containing English/Latin with a bit a Hebrew will return two items in the resulting list; - One containing the latin letters and the other hebrew. - """ - layers: dict[str, str] = {} - - for character in decoded_sequence: - if character.isalpha() is False: - continue - - character_range: str | None = unicode_range(character) - - if character_range is None: - continue - - layer_target_range: str | None = None - - for discovered_range in layers: - if ( - is_suspiciously_successive_range(discovered_range, character_range) - is False - ): - layer_target_range = discovered_range - break - - if layer_target_range is None: - layer_target_range = character_range - - if layer_target_range not in layers: - layers[layer_target_range] = character.lower() - continue - - layers[layer_target_range] += character.lower() - - return list(layers.values()) - - -def merge_coherence_ratios(results: list[CoherenceMatches]) -> CoherenceMatches: - """ - This function merge results previously given by the function coherence_ratio. - The return type is the same as coherence_ratio. - """ - per_language_ratios: dict[str, list[float]] = {} - for result in results: - for sub_result in result: - language, ratio = sub_result - if language not in per_language_ratios: - per_language_ratios[language] = [ratio] - continue - per_language_ratios[language].append(ratio) - - merge = [ - ( - language, - round( - sum(per_language_ratios[language]) / len(per_language_ratios[language]), - 4, - ), - ) - for language in per_language_ratios - ] - - return sorted(merge, key=lambda x: x[1], reverse=True) - - -def filter_alt_coherence_matches(results: CoherenceMatches) -> CoherenceMatches: - """ - We shall NOT return "English—" in CoherenceMatches because it is an alternative - of "English". This function only keeps the best match and remove the em-dash in it. - """ - index_results: dict[str, list[float]] = dict() - - for result in results: - language, ratio = result - no_em_name: str = language.replace("—", "") - - if no_em_name not in index_results: - index_results[no_em_name] = [] - - index_results[no_em_name].append(ratio) - - if any(len(index_results[e]) > 1 for e in index_results): - filtered_results: CoherenceMatches = [] - - for language in index_results: - filtered_results.append((language, max(index_results[language]))) - - return filtered_results - - return results - - -@lru_cache(maxsize=2048) -def coherence_ratio( - decoded_sequence: str, threshold: float = 0.1, lg_inclusion: str | None = None -) -> CoherenceMatches: - """ - Detect ANY language that can be identified in given sequence. The sequence will be analysed by layers. - A layer = Character extraction by alphabets/ranges. - """ - - results: list[tuple[str, float]] = [] - ignore_non_latin: bool = False - - sufficient_match_count: int = 0 - - lg_inclusion_list = lg_inclusion.split(",") if lg_inclusion is not None else [] - if "Latin Based" in lg_inclusion_list: - ignore_non_latin = True - lg_inclusion_list.remove("Latin Based") - - for layer in alpha_unicode_split(decoded_sequence): - sequence_frequencies: TypeCounter[str] = Counter(layer) - most_common = sequence_frequencies.most_common() - - character_count: int = sum(o for c, o in most_common) - - if character_count <= TOO_SMALL_SEQUENCE: - continue - - popular_character_ordered: list[str] = [c for c, o in most_common] - - for language in lg_inclusion_list or alphabet_languages( - popular_character_ordered, ignore_non_latin - ): - ratio: float = characters_popularity_compare( - language, popular_character_ordered - ) - - if ratio < threshold: - continue - elif ratio >= 0.8: - sufficient_match_count += 1 - - results.append((language, round(ratio, 4))) - - if sufficient_match_count >= 3: - break - - return sorted( - filter_alt_coherence_matches(results), key=lambda x: x[1], reverse=True - ) diff --git a/venv/Lib/site-packages/charset_normalizer/cli/__init__.py b/venv/Lib/site-packages/charset_normalizer/cli/__init__.py deleted file mode 100644 index 543a5a4..0000000 --- a/venv/Lib/site-packages/charset_normalizer/cli/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import annotations - -from .__main__ import cli_detect, query_yes_no - -__all__ = ( - "cli_detect", - "query_yes_no", -) diff --git a/venv/Lib/site-packages/charset_normalizer/cli/__main__.py b/venv/Lib/site-packages/charset_normalizer/cli/__main__.py deleted file mode 100644 index cb64156..0000000 --- a/venv/Lib/site-packages/charset_normalizer/cli/__main__.py +++ /dev/null @@ -1,381 +0,0 @@ -from __future__ import annotations - -import argparse -import sys -import typing -from json import dumps -from os.path import abspath, basename, dirname, join, realpath -from platform import python_version -from unicodedata import unidata_version - -import charset_normalizer.md as md_module -from charset_normalizer import from_fp -from charset_normalizer.models import CliDetectionResult -from charset_normalizer.version import __version__ - - -def query_yes_no(question: str, default: str = "yes") -> bool: - """Ask a yes/no question via input() and return their answer. - - "question" is a string that is presented to the user. - "default" is the presumed answer if the user just hits . - It must be "yes" (the default), "no" or None (meaning - an answer is required of the user). - - The "answer" return value is True for "yes" or False for "no". - - Credit goes to (c) https://stackoverflow.com/questions/3041986/apt-command-line-interface-like-yes-no-input - """ - valid = {"yes": True, "y": True, "ye": True, "no": False, "n": False} - if default is None: - prompt = " [y/n] " - elif default == "yes": - prompt = " [Y/n] " - elif default == "no": - prompt = " [y/N] " - else: - raise ValueError("invalid default answer: '%s'" % default) - - while True: - sys.stdout.write(question + prompt) - choice = input().lower() - if default is not None and choice == "": - return valid[default] - elif choice in valid: - return valid[choice] - else: - sys.stdout.write("Please respond with 'yes' or 'no' (or 'y' or 'n').\n") - - -class FileType: - """Factory for creating file object types - - Instances of FileType are typically passed as type= arguments to the - ArgumentParser add_argument() method. - - Keyword Arguments: - - mode -- A string indicating how the file is to be opened. Accepts the - same values as the builtin open() function. - - bufsize -- The file's desired buffer size. Accepts the same values as - the builtin open() function. - - encoding -- The file's encoding. Accepts the same values as the - builtin open() function. - - errors -- A string indicating how encoding and decoding errors are to - be handled. Accepts the same value as the builtin open() function. - - Backported from CPython 3.12 - """ - - def __init__( - self, - mode: str = "r", - bufsize: int = -1, - encoding: str | None = None, - errors: str | None = None, - ): - self._mode = mode - self._bufsize = bufsize - self._encoding = encoding - self._errors = errors - - def __call__(self, string: str) -> typing.IO: # type: ignore[type-arg] - # the special argument "-" means sys.std{in,out} - if string == "-": - if "r" in self._mode: - return sys.stdin.buffer if "b" in self._mode else sys.stdin - elif any(c in self._mode for c in "wax"): - return sys.stdout.buffer if "b" in self._mode else sys.stdout - else: - msg = f'argument "-" with mode {self._mode}' - raise ValueError(msg) - - # all other arguments are used as file names - try: - return open(string, self._mode, self._bufsize, self._encoding, self._errors) - except OSError as e: - message = f"can't open '{string}': {e}" - raise argparse.ArgumentTypeError(message) - - def __repr__(self) -> str: - args = self._mode, self._bufsize - kwargs = [("encoding", self._encoding), ("errors", self._errors)] - args_str = ", ".join( - [repr(arg) for arg in args if arg != -1] - + [f"{kw}={arg!r}" for kw, arg in kwargs if arg is not None] - ) - return f"{type(self).__name__}({args_str})" - - -def cli_detect(argv: list[str] | None = None) -> int: - """ - CLI assistant using ARGV and ArgumentParser - :param argv: - :return: 0 if everything is fine, anything else equal trouble - """ - parser = argparse.ArgumentParser( - description="The Real First Universal Charset Detector. " - "Discover originating encoding used on text file. " - "Normalize text to unicode." - ) - - parser.add_argument( - "files", type=FileType("rb"), nargs="+", help="File(s) to be analysed" - ) - parser.add_argument( - "-v", - "--verbose", - action="store_true", - default=False, - dest="verbose", - help="Display complementary information about file if any. " - "Stdout will contain logs about the detection process.", - ) - parser.add_argument( - "-a", - "--with-alternative", - action="store_true", - default=False, - dest="alternatives", - help="Output complementary possibilities if any. Top-level JSON WILL be a list.", - ) - parser.add_argument( - "-n", - "--normalize", - action="store_true", - default=False, - dest="normalize", - help="Permit to normalize input file. If not set, program does not write anything.", - ) - parser.add_argument( - "-m", - "--minimal", - action="store_true", - default=False, - dest="minimal", - help="Only output the charset detected to STDOUT. Disabling JSON output.", - ) - parser.add_argument( - "-r", - "--replace", - action="store_true", - default=False, - dest="replace", - help="Replace file when trying to normalize it instead of creating a new one.", - ) - parser.add_argument( - "-f", - "--force", - action="store_true", - default=False, - dest="force", - help="Replace file without asking if you are sure, use this flag with caution.", - ) - parser.add_argument( - "-i", - "--no-preemptive", - action="store_true", - default=False, - dest="no_preemptive", - help="Disable looking at a charset declaration to hint the detector.", - ) - parser.add_argument( - "-t", - "--threshold", - action="store", - default=0.2, - type=float, - dest="threshold", - help="Define a custom maximum amount of noise allowed in decoded content. 0. <= noise <= 1.", - ) - parser.add_argument( - "--version", - action="version", - version="Charset-Normalizer {} - Python {} - Unicode {} - SpeedUp {}".format( - __version__, - python_version(), - unidata_version, - "OFF" if md_module.__file__.lower().endswith(".py") else "ON", - ), - help="Show version information and exit.", - ) - - args = parser.parse_args(argv) - - if args.replace is True and args.normalize is False: - if args.files: - for my_file in args.files: - my_file.close() - print("Use --replace in addition of --normalize only.", file=sys.stderr) - return 1 - - if args.force is True and args.replace is False: - if args.files: - for my_file in args.files: - my_file.close() - print("Use --force in addition of --replace only.", file=sys.stderr) - return 1 - - if args.threshold < 0.0 or args.threshold > 1.0: - if args.files: - for my_file in args.files: - my_file.close() - print("--threshold VALUE should be between 0. AND 1.", file=sys.stderr) - return 1 - - x_ = [] - - for my_file in args.files: - matches = from_fp( - my_file, - threshold=args.threshold, - explain=args.verbose, - preemptive_behaviour=args.no_preemptive is False, - ) - - best_guess = matches.best() - - if best_guess is None: - print( - 'Unable to identify originating encoding for "{}". {}'.format( - my_file.name, - ( - "Maybe try increasing maximum amount of chaos." - if args.threshold < 1.0 - else "" - ), - ), - file=sys.stderr, - ) - x_.append( - CliDetectionResult( - abspath(my_file.name), - None, - [], - [], - "Unknown", - [], - False, - 1.0, - 0.0, - None, - True, - ) - ) - else: - x_.append( - CliDetectionResult( - abspath(my_file.name), - best_guess.encoding, - best_guess.encoding_aliases, - [ - cp - for cp in best_guess.could_be_from_charset - if cp != best_guess.encoding - ], - best_guess.language, - best_guess.alphabets, - best_guess.bom, - best_guess.percent_chaos, - best_guess.percent_coherence, - None, - True, - ) - ) - - if len(matches) > 1 and args.alternatives: - for el in matches: - if el != best_guess: - x_.append( - CliDetectionResult( - abspath(my_file.name), - el.encoding, - el.encoding_aliases, - [ - cp - for cp in el.could_be_from_charset - if cp != el.encoding - ], - el.language, - el.alphabets, - el.bom, - el.percent_chaos, - el.percent_coherence, - None, - False, - ) - ) - - if args.normalize is True: - if best_guess.encoding.startswith("utf") is True: - print( - '"{}" file does not need to be normalized, as it already came from unicode.'.format( - my_file.name - ), - file=sys.stderr, - ) - if my_file.closed is False: - my_file.close() - continue - - dir_path = dirname(realpath(my_file.name)) - file_name = basename(realpath(my_file.name)) - - o_: list[str] = file_name.split(".") - - if args.replace is False: - o_.insert(-1, best_guess.encoding) - if my_file.closed is False: - my_file.close() - elif ( - args.force is False - and query_yes_no( - 'Are you sure to normalize "{}" by replacing it ?'.format( - my_file.name - ), - "no", - ) - is False - ): - if my_file.closed is False: - my_file.close() - continue - - try: - x_[0].unicode_path = join(dir_path, ".".join(o_)) - - with open(x_[0].unicode_path, "wb") as fp: - fp.write(best_guess.output()) - except OSError as e: - print(str(e), file=sys.stderr) - if my_file.closed is False: - my_file.close() - return 2 - - if my_file.closed is False: - my_file.close() - - if args.minimal is False: - print( - dumps( - [el.__dict__ for el in x_] if len(x_) > 1 else x_[0].__dict__, - ensure_ascii=True, - indent=4, - ) - ) - else: - for my_file in args.files: - print( - ", ".join( - [ - el.encoding or "undefined" - for el in x_ - if el.path == abspath(my_file.name) - ] - ) - ) - - return 0 - - -if __name__ == "__main__": - cli_detect() diff --git a/venv/Lib/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 2985da8..0000000 Binary files a/venv/Lib/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/charset_normalizer/cli/__pycache__/__main__.cpython-310.pyc b/venv/Lib/site-packages/charset_normalizer/cli/__pycache__/__main__.cpython-310.pyc deleted file mode 100644 index 8492f9d..0000000 Binary files a/venv/Lib/site-packages/charset_normalizer/cli/__pycache__/__main__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/charset_normalizer/constant.py b/venv/Lib/site-packages/charset_normalizer/constant.py deleted file mode 100644 index cc71a01..0000000 --- a/venv/Lib/site-packages/charset_normalizer/constant.py +++ /dev/null @@ -1,2015 +0,0 @@ -from __future__ import annotations - -from codecs import BOM_UTF8, BOM_UTF16_BE, BOM_UTF16_LE, BOM_UTF32_BE, BOM_UTF32_LE -from encodings.aliases import aliases -from re import IGNORECASE -from re import compile as re_compile - -# Contain for each eligible encoding a list of/item bytes SIG/BOM -ENCODING_MARKS: dict[str, bytes | list[bytes]] = { - "utf_8": BOM_UTF8, - "utf_7": [ - b"\x2b\x2f\x76\x38", - b"\x2b\x2f\x76\x39", - b"\x2b\x2f\x76\x2b", - b"\x2b\x2f\x76\x2f", - b"\x2b\x2f\x76\x38\x2d", - ], - "gb18030": b"\x84\x31\x95\x33", - "utf_32": [BOM_UTF32_BE, BOM_UTF32_LE], - "utf_16": [BOM_UTF16_BE, BOM_UTF16_LE], -} - -TOO_SMALL_SEQUENCE: int = 32 -TOO_BIG_SEQUENCE: int = int(10e6) - -UTF8_MAXIMAL_ALLOCATION: int = 1_112_064 - -# Up-to-date Unicode ucd/15.0.0 -UNICODE_RANGES_COMBINED: dict[str, range] = { - "Control character": range(32), - "Basic Latin": range(32, 128), - "Latin-1 Supplement": range(128, 256), - "Latin Extended-A": range(256, 384), - "Latin Extended-B": range(384, 592), - "IPA Extensions": range(592, 688), - "Spacing Modifier Letters": range(688, 768), - "Combining Diacritical Marks": range(768, 880), - "Greek and Coptic": range(880, 1024), - "Cyrillic": range(1024, 1280), - "Cyrillic Supplement": range(1280, 1328), - "Armenian": range(1328, 1424), - "Hebrew": range(1424, 1536), - "Arabic": range(1536, 1792), - "Syriac": range(1792, 1872), - "Arabic Supplement": range(1872, 1920), - "Thaana": range(1920, 1984), - "NKo": range(1984, 2048), - "Samaritan": range(2048, 2112), - "Mandaic": range(2112, 2144), - "Syriac Supplement": range(2144, 2160), - "Arabic Extended-B": range(2160, 2208), - "Arabic Extended-A": range(2208, 2304), - "Devanagari": range(2304, 2432), - "Bengali": range(2432, 2560), - "Gurmukhi": range(2560, 2688), - "Gujarati": range(2688, 2816), - "Oriya": range(2816, 2944), - "Tamil": range(2944, 3072), - "Telugu": range(3072, 3200), - "Kannada": range(3200, 3328), - "Malayalam": range(3328, 3456), - "Sinhala": range(3456, 3584), - "Thai": range(3584, 3712), - "Lao": range(3712, 3840), - "Tibetan": range(3840, 4096), - "Myanmar": range(4096, 4256), - "Georgian": range(4256, 4352), - "Hangul Jamo": range(4352, 4608), - "Ethiopic": range(4608, 4992), - "Ethiopic Supplement": range(4992, 5024), - "Cherokee": range(5024, 5120), - "Unified Canadian Aboriginal Syllabics": range(5120, 5760), - "Ogham": range(5760, 5792), - "Runic": range(5792, 5888), - "Tagalog": range(5888, 5920), - "Hanunoo": range(5920, 5952), - "Buhid": range(5952, 5984), - "Tagbanwa": range(5984, 6016), - "Khmer": range(6016, 6144), - "Mongolian": range(6144, 6320), - "Unified Canadian Aboriginal Syllabics Extended": range(6320, 6400), - "Limbu": range(6400, 6480), - "Tai Le": range(6480, 6528), - "New Tai Lue": range(6528, 6624), - "Khmer Symbols": range(6624, 6656), - "Buginese": range(6656, 6688), - "Tai Tham": range(6688, 6832), - "Combining Diacritical Marks Extended": range(6832, 6912), - "Balinese": range(6912, 7040), - "Sundanese": range(7040, 7104), - "Batak": range(7104, 7168), - "Lepcha": range(7168, 7248), - "Ol Chiki": range(7248, 7296), - "Cyrillic Extended-C": range(7296, 7312), - "Georgian Extended": range(7312, 7360), - "Sundanese Supplement": range(7360, 7376), - "Vedic Extensions": range(7376, 7424), - "Phonetic Extensions": range(7424, 7552), - "Phonetic Extensions Supplement": range(7552, 7616), - "Combining Diacritical Marks Supplement": range(7616, 7680), - "Latin Extended Additional": range(7680, 7936), - "Greek Extended": range(7936, 8192), - "General Punctuation": range(8192, 8304), - "Superscripts and Subscripts": range(8304, 8352), - "Currency Symbols": range(8352, 8400), - "Combining Diacritical Marks for Symbols": range(8400, 8448), - "Letterlike Symbols": range(8448, 8528), - "Number Forms": range(8528, 8592), - "Arrows": range(8592, 8704), - "Mathematical Operators": range(8704, 8960), - "Miscellaneous Technical": range(8960, 9216), - "Control Pictures": range(9216, 9280), - "Optical Character Recognition": range(9280, 9312), - "Enclosed Alphanumerics": range(9312, 9472), - "Box Drawing": range(9472, 9600), - "Block Elements": range(9600, 9632), - "Geometric Shapes": range(9632, 9728), - "Miscellaneous Symbols": range(9728, 9984), - "Dingbats": range(9984, 10176), - "Miscellaneous Mathematical Symbols-A": range(10176, 10224), - "Supplemental Arrows-A": range(10224, 10240), - "Braille Patterns": range(10240, 10496), - "Supplemental Arrows-B": range(10496, 10624), - "Miscellaneous Mathematical Symbols-B": range(10624, 10752), - "Supplemental Mathematical Operators": range(10752, 11008), - "Miscellaneous Symbols and Arrows": range(11008, 11264), - "Glagolitic": range(11264, 11360), - "Latin Extended-C": range(11360, 11392), - "Coptic": range(11392, 11520), - "Georgian Supplement": range(11520, 11568), - "Tifinagh": range(11568, 11648), - "Ethiopic Extended": range(11648, 11744), - "Cyrillic Extended-A": range(11744, 11776), - "Supplemental Punctuation": range(11776, 11904), - "CJK Radicals Supplement": range(11904, 12032), - "Kangxi Radicals": range(12032, 12256), - "Ideographic Description Characters": range(12272, 12288), - "CJK Symbols and Punctuation": range(12288, 12352), - "Hiragana": range(12352, 12448), - "Katakana": range(12448, 12544), - "Bopomofo": range(12544, 12592), - "Hangul Compatibility Jamo": range(12592, 12688), - "Kanbun": range(12688, 12704), - "Bopomofo Extended": range(12704, 12736), - "CJK Strokes": range(12736, 12784), - "Katakana Phonetic Extensions": range(12784, 12800), - "Enclosed CJK Letters and Months": range(12800, 13056), - "CJK Compatibility": range(13056, 13312), - "CJK Unified Ideographs Extension A": range(13312, 19904), - "Yijing Hexagram Symbols": range(19904, 19968), - "CJK Unified Ideographs": range(19968, 40960), - "Yi Syllables": range(40960, 42128), - "Yi Radicals": range(42128, 42192), - "Lisu": range(42192, 42240), - "Vai": range(42240, 42560), - "Cyrillic Extended-B": range(42560, 42656), - "Bamum": range(42656, 42752), - "Modifier Tone Letters": range(42752, 42784), - "Latin Extended-D": range(42784, 43008), - "Syloti Nagri": range(43008, 43056), - "Common Indic Number Forms": range(43056, 43072), - "Phags-pa": range(43072, 43136), - "Saurashtra": range(43136, 43232), - "Devanagari Extended": range(43232, 43264), - "Kayah Li": range(43264, 43312), - "Rejang": range(43312, 43360), - "Hangul Jamo Extended-A": range(43360, 43392), - "Javanese": range(43392, 43488), - "Myanmar Extended-B": range(43488, 43520), - "Cham": range(43520, 43616), - "Myanmar Extended-A": range(43616, 43648), - "Tai Viet": range(43648, 43744), - "Meetei Mayek Extensions": range(43744, 43776), - "Ethiopic Extended-A": range(43776, 43824), - "Latin Extended-E": range(43824, 43888), - "Cherokee Supplement": range(43888, 43968), - "Meetei Mayek": range(43968, 44032), - "Hangul Syllables": range(44032, 55216), - "Hangul Jamo Extended-B": range(55216, 55296), - "High Surrogates": range(55296, 56192), - "High Private Use Surrogates": range(56192, 56320), - "Low Surrogates": range(56320, 57344), - "Private Use Area": range(57344, 63744), - "CJK Compatibility Ideographs": range(63744, 64256), - "Alphabetic Presentation Forms": range(64256, 64336), - "Arabic Presentation Forms-A": range(64336, 65024), - "Variation Selectors": range(65024, 65040), - "Vertical Forms": range(65040, 65056), - "Combining Half Marks": range(65056, 65072), - "CJK Compatibility Forms": range(65072, 65104), - "Small Form Variants": range(65104, 65136), - "Arabic Presentation Forms-B": range(65136, 65280), - "Halfwidth and Fullwidth Forms": range(65280, 65520), - "Specials": range(65520, 65536), - "Linear B Syllabary": range(65536, 65664), - "Linear B Ideograms": range(65664, 65792), - "Aegean Numbers": range(65792, 65856), - "Ancient Greek Numbers": range(65856, 65936), - "Ancient Symbols": range(65936, 66000), - "Phaistos Disc": range(66000, 66048), - "Lycian": range(66176, 66208), - "Carian": range(66208, 66272), - "Coptic Epact Numbers": range(66272, 66304), - "Old Italic": range(66304, 66352), - "Gothic": range(66352, 66384), - "Old Permic": range(66384, 66432), - "Ugaritic": range(66432, 66464), - "Old Persian": range(66464, 66528), - "Deseret": range(66560, 66640), - "Shavian": range(66640, 66688), - "Osmanya": range(66688, 66736), - "Osage": range(66736, 66816), - "Elbasan": range(66816, 66864), - "Caucasian Albanian": range(66864, 66928), - "Vithkuqi": range(66928, 67008), - "Linear A": range(67072, 67456), - "Latin Extended-F": range(67456, 67520), - "Cypriot Syllabary": range(67584, 67648), - "Imperial Aramaic": range(67648, 67680), - "Palmyrene": range(67680, 67712), - "Nabataean": range(67712, 67760), - "Hatran": range(67808, 67840), - "Phoenician": range(67840, 67872), - "Lydian": range(67872, 67904), - "Meroitic Hieroglyphs": range(67968, 68000), - "Meroitic Cursive": range(68000, 68096), - "Kharoshthi": range(68096, 68192), - "Old South Arabian": range(68192, 68224), - "Old North Arabian": range(68224, 68256), - "Manichaean": range(68288, 68352), - "Avestan": range(68352, 68416), - "Inscriptional Parthian": range(68416, 68448), - "Inscriptional Pahlavi": range(68448, 68480), - "Psalter Pahlavi": range(68480, 68528), - "Old Turkic": range(68608, 68688), - "Old Hungarian": range(68736, 68864), - "Hanifi Rohingya": range(68864, 68928), - "Rumi Numeral Symbols": range(69216, 69248), - "Yezidi": range(69248, 69312), - "Arabic Extended-C": range(69312, 69376), - "Old Sogdian": range(69376, 69424), - "Sogdian": range(69424, 69488), - "Old Uyghur": range(69488, 69552), - "Chorasmian": range(69552, 69600), - "Elymaic": range(69600, 69632), - "Brahmi": range(69632, 69760), - "Kaithi": range(69760, 69840), - "Sora Sompeng": range(69840, 69888), - "Chakma": range(69888, 69968), - "Mahajani": range(69968, 70016), - "Sharada": range(70016, 70112), - "Sinhala Archaic Numbers": range(70112, 70144), - "Khojki": range(70144, 70224), - "Multani": range(70272, 70320), - "Khudawadi": range(70320, 70400), - "Grantha": range(70400, 70528), - "Newa": range(70656, 70784), - "Tirhuta": range(70784, 70880), - "Siddham": range(71040, 71168), - "Modi": range(71168, 71264), - "Mongolian Supplement": range(71264, 71296), - "Takri": range(71296, 71376), - "Ahom": range(71424, 71504), - "Dogra": range(71680, 71760), - "Warang Citi": range(71840, 71936), - "Dives Akuru": range(71936, 72032), - "Nandinagari": range(72096, 72192), - "Zanabazar Square": range(72192, 72272), - "Soyombo": range(72272, 72368), - "Unified Canadian Aboriginal Syllabics Extended-A": range(72368, 72384), - "Pau Cin Hau": range(72384, 72448), - "Devanagari Extended-A": range(72448, 72544), - "Bhaiksuki": range(72704, 72816), - "Marchen": range(72816, 72896), - "Masaram Gondi": range(72960, 73056), - "Gunjala Gondi": range(73056, 73136), - "Makasar": range(73440, 73472), - "Kawi": range(73472, 73568), - "Lisu Supplement": range(73648, 73664), - "Tamil Supplement": range(73664, 73728), - "Cuneiform": range(73728, 74752), - "Cuneiform Numbers and Punctuation": range(74752, 74880), - "Early Dynastic Cuneiform": range(74880, 75088), - "Cypro-Minoan": range(77712, 77824), - "Egyptian Hieroglyphs": range(77824, 78896), - "Egyptian Hieroglyph Format Controls": range(78896, 78944), - "Anatolian Hieroglyphs": range(82944, 83584), - "Bamum Supplement": range(92160, 92736), - "Mro": range(92736, 92784), - "Tangsa": range(92784, 92880), - "Bassa Vah": range(92880, 92928), - "Pahawh Hmong": range(92928, 93072), - "Medefaidrin": range(93760, 93856), - "Miao": range(93952, 94112), - "Ideographic Symbols and Punctuation": range(94176, 94208), - "Tangut": range(94208, 100352), - "Tangut Components": range(100352, 101120), - "Khitan Small Script": range(101120, 101632), - "Tangut Supplement": range(101632, 101760), - "Kana Extended-B": range(110576, 110592), - "Kana Supplement": range(110592, 110848), - "Kana Extended-A": range(110848, 110896), - "Small Kana Extension": range(110896, 110960), - "Nushu": range(110960, 111360), - "Duployan": range(113664, 113824), - "Shorthand Format Controls": range(113824, 113840), - "Znamenny Musical Notation": range(118528, 118736), - "Byzantine Musical Symbols": range(118784, 119040), - "Musical Symbols": range(119040, 119296), - "Ancient Greek Musical Notation": range(119296, 119376), - "Kaktovik Numerals": range(119488, 119520), - "Mayan Numerals": range(119520, 119552), - "Tai Xuan Jing Symbols": range(119552, 119648), - "Counting Rod Numerals": range(119648, 119680), - "Mathematical Alphanumeric Symbols": range(119808, 120832), - "Sutton SignWriting": range(120832, 121520), - "Latin Extended-G": range(122624, 122880), - "Glagolitic Supplement": range(122880, 122928), - "Cyrillic Extended-D": range(122928, 123024), - "Nyiakeng Puachue Hmong": range(123136, 123216), - "Toto": range(123536, 123584), - "Wancho": range(123584, 123648), - "Nag Mundari": range(124112, 124160), - "Ethiopic Extended-B": range(124896, 124928), - "Mende Kikakui": range(124928, 125152), - "Adlam": range(125184, 125280), - "Indic Siyaq Numbers": range(126064, 126144), - "Ottoman Siyaq Numbers": range(126208, 126288), - "Arabic Mathematical Alphabetic Symbols": range(126464, 126720), - "Mahjong Tiles": range(126976, 127024), - "Domino Tiles": range(127024, 127136), - "Playing Cards": range(127136, 127232), - "Enclosed Alphanumeric Supplement": range(127232, 127488), - "Enclosed Ideographic Supplement": range(127488, 127744), - "Miscellaneous Symbols and Pictographs": range(127744, 128512), - "Emoticons range(Emoji)": range(128512, 128592), - "Ornamental Dingbats": range(128592, 128640), - "Transport and Map Symbols": range(128640, 128768), - "Alchemical Symbols": range(128768, 128896), - "Geometric Shapes Extended": range(128896, 129024), - "Supplemental Arrows-C": range(129024, 129280), - "Supplemental Symbols and Pictographs": range(129280, 129536), - "Chess Symbols": range(129536, 129648), - "Symbols and Pictographs Extended-A": range(129648, 129792), - "Symbols for Legacy Computing": range(129792, 130048), - "CJK Unified Ideographs Extension B": range(131072, 173792), - "CJK Unified Ideographs Extension C": range(173824, 177984), - "CJK Unified Ideographs Extension D": range(177984, 178208), - "CJK Unified Ideographs Extension E": range(178208, 183984), - "CJK Unified Ideographs Extension F": range(183984, 191472), - "CJK Compatibility Ideographs Supplement": range(194560, 195104), - "CJK Unified Ideographs Extension G": range(196608, 201552), - "CJK Unified Ideographs Extension H": range(201552, 205744), - "Tags": range(917504, 917632), - "Variation Selectors Supplement": range(917760, 918000), - "Supplementary Private Use Area-A": range(983040, 1048576), - "Supplementary Private Use Area-B": range(1048576, 1114112), -} - - -UNICODE_SECONDARY_RANGE_KEYWORD: list[str] = [ - "Supplement", - "Extended", - "Extensions", - "Modifier", - "Marks", - "Punctuation", - "Symbols", - "Forms", - "Operators", - "Miscellaneous", - "Drawing", - "Block", - "Shapes", - "Supplemental", - "Tags", -] - -RE_POSSIBLE_ENCODING_INDICATION = re_compile( - r"(?:(?:encoding)|(?:charset)|(?:coding))(?:[\:= ]{1,10})(?:[\"\']?)([a-zA-Z0-9\-_]+)(?:[\"\']?)", - IGNORECASE, -) - -IANA_NO_ALIASES = [ - "cp720", - "cp737", - "cp856", - "cp874", - "cp875", - "cp1006", - "koi8_r", - "koi8_t", - "koi8_u", -] - -IANA_SUPPORTED: list[str] = sorted( - filter( - lambda x: x.endswith("_codec") is False - and x not in {"rot_13", "tactis", "mbcs"}, - list(set(aliases.values())) + IANA_NO_ALIASES, - ) -) - -IANA_SUPPORTED_COUNT: int = len(IANA_SUPPORTED) - -# pre-computed code page that are similar using the function cp_similarity. -IANA_SUPPORTED_SIMILAR: dict[str, list[str]] = { - "cp037": ["cp1026", "cp1140", "cp273", "cp500"], - "cp1026": ["cp037", "cp1140", "cp273", "cp500"], - "cp1125": ["cp866"], - "cp1140": ["cp037", "cp1026", "cp273", "cp500"], - "cp1250": ["iso8859_2"], - "cp1251": ["kz1048", "ptcp154"], - "cp1252": ["iso8859_15", "iso8859_9", "latin_1"], - "cp1253": ["iso8859_7"], - "cp1254": ["iso8859_15", "iso8859_9", "latin_1"], - "cp1257": ["iso8859_13"], - "cp273": ["cp037", "cp1026", "cp1140", "cp500"], - "cp437": ["cp850", "cp858", "cp860", "cp861", "cp862", "cp863", "cp865"], - "cp500": ["cp037", "cp1026", "cp1140", "cp273"], - "cp850": ["cp437", "cp857", "cp858", "cp865"], - "cp857": ["cp850", "cp858", "cp865"], - "cp858": ["cp437", "cp850", "cp857", "cp865"], - "cp860": ["cp437", "cp861", "cp862", "cp863", "cp865"], - "cp861": ["cp437", "cp860", "cp862", "cp863", "cp865"], - "cp862": ["cp437", "cp860", "cp861", "cp863", "cp865"], - "cp863": ["cp437", "cp860", "cp861", "cp862", "cp865"], - "cp865": ["cp437", "cp850", "cp857", "cp858", "cp860", "cp861", "cp862", "cp863"], - "cp866": ["cp1125"], - "iso8859_10": ["iso8859_14", "iso8859_15", "iso8859_4", "iso8859_9", "latin_1"], - "iso8859_11": ["tis_620"], - "iso8859_13": ["cp1257"], - "iso8859_14": [ - "iso8859_10", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_9", - "latin_1", - ], - "iso8859_15": [ - "cp1252", - "cp1254", - "iso8859_10", - "iso8859_14", - "iso8859_16", - "iso8859_3", - "iso8859_9", - "latin_1", - ], - "iso8859_16": [ - "iso8859_14", - "iso8859_15", - "iso8859_2", - "iso8859_3", - "iso8859_9", - "latin_1", - ], - "iso8859_2": ["cp1250", "iso8859_16", "iso8859_4"], - "iso8859_3": ["iso8859_14", "iso8859_15", "iso8859_16", "iso8859_9", "latin_1"], - "iso8859_4": ["iso8859_10", "iso8859_2", "iso8859_9", "latin_1"], - "iso8859_7": ["cp1253"], - "iso8859_9": [ - "cp1252", - "cp1254", - "cp1258", - "iso8859_10", - "iso8859_14", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_4", - "latin_1", - ], - "kz1048": ["cp1251", "ptcp154"], - "latin_1": [ - "cp1252", - "cp1254", - "cp1258", - "iso8859_10", - "iso8859_14", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_4", - "iso8859_9", - ], - "mac_iceland": ["mac_roman", "mac_turkish"], - "mac_roman": ["mac_iceland", "mac_turkish"], - "mac_turkish": ["mac_iceland", "mac_roman"], - "ptcp154": ["cp1251", "kz1048"], - "tis_620": ["iso8859_11"], -} - - -CHARDET_CORRESPONDENCE: dict[str, str] = { - "iso2022_kr": "ISO-2022-KR", - "iso2022_jp": "ISO-2022-JP", - "euc_kr": "EUC-KR", - "tis_620": "TIS-620", - "utf_32": "UTF-32", - "euc_jp": "EUC-JP", - "koi8_r": "KOI8-R", - "iso8859_1": "ISO-8859-1", - "iso8859_2": "ISO-8859-2", - "iso8859_5": "ISO-8859-5", - "iso8859_6": "ISO-8859-6", - "iso8859_7": "ISO-8859-7", - "iso8859_8": "ISO-8859-8", - "utf_16": "UTF-16", - "cp855": "IBM855", - "mac_cyrillic": "MacCyrillic", - "gb2312": "GB2312", - "gb18030": "GB18030", - "cp932": "CP932", - "cp866": "IBM866", - "utf_8": "utf-8", - "utf_8_sig": "UTF-8-SIG", - "shift_jis": "SHIFT_JIS", - "big5": "Big5", - "cp1250": "windows-1250", - "cp1251": "windows-1251", - "cp1252": "Windows-1252", - "cp1253": "windows-1253", - "cp1255": "windows-1255", - "cp1256": "windows-1256", - "cp1254": "Windows-1254", - "cp949": "CP949", -} - - -COMMON_SAFE_ASCII_CHARACTERS: set[str] = { - "<", - ">", - "=", - ":", - "/", - "&", - ";", - "{", - "}", - "[", - "]", - ",", - "|", - '"', - "-", - "(", - ")", -} - -# Sample character sets — replace with full lists if needed -COMMON_CHINESE_CHARACTERS = "的一是在不了有和人这中大为上个国我以要他时来用们生到作地于出就分对成会可主发年动同工也能下过子说产种面而方后多定行学法所民得经十三之进着等部度家电力里如水化高自二理起小物现实加量都两体制机当使点从业本去把性好应开它合还因由其些然前外天政四日那社义事平形相全表间样与关各重新线内数正心反你明看原又么利比或但质气第向道命此变条只没结解问意建月公无系军很情者最立代想已通并提直题党程展五果料象员革位入常文总次品式活设及管特件长求老头基资边流路级少图山统接知较将组见计别她手角期根论运农指几九区强放决西被干做必战先回则任取据处队南给色光门即保治北造百规热领七海口东导器压志世金增争济阶油思术极交受联什认六共权收证改清己美再采转更单风切打白教速花带安场身车例真务具万每目至达走积示议声报斗完类八离华名确才科张信马节话米整空元况今集温传土许步群广石记需段研界拉林律叫且究观越织装影算低持音众书布复容儿须际商非验连断深难近矿千周委素技备半办青省列习响约支般史感劳便团往酸历市克何除消构府太准精值号率族维划选标写存候毛亲快效斯院查江型眼王按格养易置派层片始却专状育厂京识适属圆包火住调满县局照参红细引听该铁价严龙飞" - -COMMON_JAPANESE_CHARACTERS = "日一国年大十二本中長出三時行見月分後前生五間上東四今金九入学高円子外八六下来気小七山話女北午百書先名川千水半男西電校語土木聞食車何南万毎白天母火右読友左休父雨" - -COMMON_KOREAN_CHARACTERS = "一二三四五六七八九十百千萬上下左右中人女子大小山川日月火水木金土父母天地國名年時文校學生" - -# Combine all into a set -COMMON_CJK_CHARACTERS = set( - "".join( - [ - COMMON_CHINESE_CHARACTERS, - COMMON_JAPANESE_CHARACTERS, - COMMON_KOREAN_CHARACTERS, - ] - ) -) - -KO_NAMES: set[str] = {"johab", "cp949", "euc_kr"} -ZH_NAMES: set[str] = {"big5", "cp950", "big5hkscs", "hz"} - -# Logging LEVEL below DEBUG -TRACE: int = 5 - - -# Language label that contain the em dash "—" -# character are to be considered alternative seq to origin -FREQUENCIES: dict[str, list[str]] = { - "English": [ - "e", - "a", - "t", - "i", - "o", - "n", - "s", - "r", - "h", - "l", - "d", - "c", - "u", - "m", - "f", - "p", - "g", - "w", - "y", - "b", - "v", - "k", - "x", - "j", - "z", - "q", - ], - "English—": [ - "e", - "a", - "t", - "i", - "o", - "n", - "s", - "r", - "h", - "l", - "d", - "c", - "m", - "u", - "f", - "p", - "g", - "w", - "b", - "y", - "v", - "k", - "j", - "x", - "z", - "q", - ], - "German": [ - "e", - "n", - "i", - "r", - "s", - "t", - "a", - "d", - "h", - "u", - "l", - "g", - "o", - "c", - "m", - "b", - "f", - "k", - "w", - "z", - "p", - "v", - "ü", - "ä", - "ö", - "j", - ], - "French": [ - "e", - "a", - "s", - "n", - "i", - "t", - "r", - "l", - "u", - "o", - "d", - "c", - "p", - "m", - "é", - "v", - "g", - "f", - "b", - "h", - "q", - "à", - "x", - "è", - "y", - "j", - ], - "Dutch": [ - "e", - "n", - "a", - "i", - "r", - "t", - "o", - "d", - "s", - "l", - "g", - "h", - "v", - "m", - "u", - "k", - "c", - "p", - "b", - "w", - "j", - "z", - "f", - "y", - "x", - "ë", - ], - "Italian": [ - "e", - "i", - "a", - "o", - "n", - "l", - "t", - "r", - "s", - "c", - "d", - "u", - "p", - "m", - "g", - "v", - "f", - "b", - "z", - "h", - "q", - "è", - "à", - "k", - "y", - "ò", - ], - "Polish": [ - "a", - "i", - "o", - "e", - "n", - "r", - "z", - "w", - "s", - "c", - "t", - "k", - "y", - "d", - "p", - "m", - "u", - "l", - "j", - "ł", - "g", - "b", - "h", - "ą", - "ę", - "ó", - ], - "Spanish": [ - "e", - "a", - "o", - "n", - "s", - "r", - "i", - "l", - "d", - "t", - "c", - "u", - "m", - "p", - "b", - "g", - "v", - "f", - "y", - "ó", - "h", - "q", - "í", - "j", - "z", - "á", - ], - "Russian": [ - "о", - "а", - "е", - "и", - "н", - "с", - "т", - "р", - "в", - "л", - "к", - "м", - "д", - "п", - "у", - "г", - "я", - "ы", - "з", - "б", - "й", - "ь", - "ч", - "х", - "ж", - "ц", - ], - # Jap-Kanji - "Japanese": [ - "人", - "一", - "大", - "亅", - "丁", - "丨", - "竹", - "笑", - "口", - "日", - "今", - "二", - "彳", - "行", - "十", - "土", - "丶", - "寸", - "寺", - "時", - "乙", - "丿", - "乂", - "气", - "気", - "冂", - "巾", - "亠", - "市", - "目", - "儿", - "見", - "八", - "小", - "凵", - "県", - "月", - "彐", - "門", - "間", - "木", - "東", - "山", - "出", - "本", - "中", - "刀", - "分", - "耳", - "又", - "取", - "最", - "言", - "田", - "心", - "思", - "刂", - "前", - "京", - "尹", - "事", - "生", - "厶", - "云", - "会", - "未", - "来", - "白", - "冫", - "楽", - "灬", - "馬", - "尸", - "尺", - "駅", - "明", - "耂", - "者", - "了", - "阝", - "都", - "高", - "卜", - "占", - "厂", - "广", - "店", - "子", - "申", - "奄", - "亻", - "俺", - "上", - "方", - "冖", - "学", - "衣", - "艮", - "食", - "自", - ], - # Jap-Katakana - "Japanese—": [ - "ー", - "ン", - "ス", - "・", - "ル", - "ト", - "リ", - "イ", - "ア", - "ラ", - "ッ", - "ク", - "ド", - "シ", - "レ", - "ジ", - "タ", - "フ", - "ロ", - "カ", - "テ", - "マ", - "ィ", - "グ", - "バ", - "ム", - "プ", - "オ", - "コ", - "デ", - "ニ", - "ウ", - "メ", - "サ", - "ビ", - "ナ", - "ブ", - "ャ", - "エ", - "ュ", - "チ", - "キ", - "ズ", - "ダ", - "パ", - "ミ", - "ェ", - "ョ", - "ハ", - "セ", - "ベ", - "ガ", - "モ", - "ツ", - "ネ", - "ボ", - "ソ", - "ノ", - "ァ", - "ヴ", - "ワ", - "ポ", - "ペ", - "ピ", - "ケ", - "ゴ", - "ギ", - "ザ", - "ホ", - "ゲ", - "ォ", - "ヤ", - "ヒ", - "ユ", - "ヨ", - "ヘ", - "ゼ", - "ヌ", - "ゥ", - "ゾ", - "ヶ", - "ヂ", - "ヲ", - "ヅ", - "ヵ", - "ヱ", - "ヰ", - "ヮ", - "ヽ", - "゠", - "ヾ", - "ヷ", - "ヿ", - "ヸ", - "ヹ", - "ヺ", - ], - # Jap-Hiragana - "Japanese——": [ - "の", - "に", - "る", - "た", - "と", - "は", - "し", - "い", - "を", - "で", - "て", - "が", - "な", - "れ", - "か", - "ら", - "さ", - "っ", - "り", - "す", - "あ", - "も", - "こ", - "ま", - "う", - "く", - "よ", - "き", - "ん", - "め", - "お", - "け", - "そ", - "つ", - "だ", - "や", - "え", - "ど", - "わ", - "ち", - "み", - "せ", - "じ", - "ば", - "へ", - "び", - "ず", - "ろ", - "ほ", - "げ", - "む", - "べ", - "ひ", - "ょ", - "ゆ", - "ぶ", - "ご", - "ゃ", - "ね", - "ふ", - "ぐ", - "ぎ", - "ぼ", - "ゅ", - "づ", - "ざ", - "ぞ", - "ぬ", - "ぜ", - "ぱ", - "ぽ", - "ぷ", - "ぴ", - "ぃ", - "ぁ", - "ぇ", - "ぺ", - "ゞ", - "ぢ", - "ぉ", - "ぅ", - "ゐ", - "ゝ", - "ゑ", - "゛", - "゜", - "ゎ", - "ゔ", - "゚", - "ゟ", - "゙", - "ゕ", - "ゖ", - ], - "Portuguese": [ - "a", - "e", - "o", - "s", - "i", - "r", - "d", - "n", - "t", - "m", - "u", - "c", - "l", - "p", - "g", - "v", - "b", - "f", - "h", - "ã", - "q", - "é", - "ç", - "á", - "z", - "í", - ], - "Swedish": [ - "e", - "a", - "n", - "r", - "t", - "s", - "i", - "l", - "d", - "o", - "m", - "k", - "g", - "v", - "h", - "f", - "u", - "p", - "ä", - "c", - "b", - "ö", - "å", - "y", - "j", - "x", - ], - "Chinese": [ - "的", - "一", - "是", - "不", - "了", - "在", - "人", - "有", - "我", - "他", - "这", - "个", - "们", - "中", - "来", - "上", - "大", - "为", - "和", - "国", - "地", - "到", - "以", - "说", - "时", - "要", - "就", - "出", - "会", - "可", - "也", - "你", - "对", - "生", - "能", - "而", - "子", - "那", - "得", - "于", - "着", - "下", - "自", - "之", - "年", - "过", - "发", - "后", - "作", - "里", - "用", - "道", - "行", - "所", - "然", - "家", - "种", - "事", - "成", - "方", - "多", - "经", - "么", - "去", - "法", - "学", - "如", - "都", - "同", - "现", - "当", - "没", - "动", - "面", - "起", - "看", - "定", - "天", - "分", - "还", - "进", - "好", - "小", - "部", - "其", - "些", - "主", - "样", - "理", - "心", - "她", - "本", - "前", - "开", - "但", - "因", - "只", - "从", - "想", - "实", - ], - "Ukrainian": [ - "о", - "а", - "н", - "і", - "и", - "р", - "в", - "т", - "е", - "с", - "к", - "л", - "у", - "д", - "м", - "п", - "з", - "я", - "ь", - "б", - "г", - "й", - "ч", - "х", - "ц", - "ї", - ], - "Norwegian": [ - "e", - "r", - "n", - "t", - "a", - "s", - "i", - "o", - "l", - "d", - "g", - "k", - "m", - "v", - "f", - "p", - "u", - "b", - "h", - "å", - "y", - "j", - "ø", - "c", - "æ", - "w", - ], - "Finnish": [ - "a", - "i", - "n", - "t", - "e", - "s", - "l", - "o", - "u", - "k", - "ä", - "m", - "r", - "v", - "j", - "h", - "p", - "y", - "d", - "ö", - "g", - "c", - "b", - "f", - "w", - "z", - ], - "Vietnamese": [ - "n", - "h", - "t", - "i", - "c", - "g", - "a", - "o", - "u", - "m", - "l", - "r", - "à", - "đ", - "s", - "e", - "v", - "p", - "b", - "y", - "ư", - "d", - "á", - "k", - "ộ", - "ế", - ], - "Czech": [ - "o", - "e", - "a", - "n", - "t", - "s", - "i", - "l", - "v", - "r", - "k", - "d", - "u", - "m", - "p", - "í", - "c", - "h", - "z", - "á", - "y", - "j", - "b", - "ě", - "é", - "ř", - ], - "Hungarian": [ - "e", - "a", - "t", - "l", - "s", - "n", - "k", - "r", - "i", - "o", - "z", - "á", - "é", - "g", - "m", - "b", - "y", - "v", - "d", - "h", - "u", - "p", - "j", - "ö", - "f", - "c", - ], - "Korean": [ - "이", - "다", - "에", - "의", - "는", - "로", - "하", - "을", - "가", - "고", - "지", - "서", - "한", - "은", - "기", - "으", - "년", - "대", - "사", - "시", - "를", - "리", - "도", - "인", - "스", - "일", - ], - "Indonesian": [ - "a", - "n", - "e", - "i", - "r", - "t", - "u", - "s", - "d", - "k", - "m", - "l", - "g", - "p", - "b", - "o", - "h", - "y", - "j", - "c", - "w", - "f", - "v", - "z", - "x", - "q", - ], - "Turkish": [ - "a", - "e", - "i", - "n", - "r", - "l", - "ı", - "k", - "d", - "t", - "s", - "m", - "y", - "u", - "o", - "b", - "ü", - "ş", - "v", - "g", - "z", - "h", - "c", - "p", - "ç", - "ğ", - ], - "Romanian": [ - "e", - "i", - "a", - "r", - "n", - "t", - "u", - "l", - "o", - "c", - "s", - "d", - "p", - "m", - "ă", - "f", - "v", - "î", - "g", - "b", - "ș", - "ț", - "z", - "h", - "â", - "j", - ], - "Farsi": [ - "ا", - "ی", - "ر", - "د", - "ن", - "ه", - "و", - "م", - "ت", - "ب", - "س", - "ل", - "ک", - "ش", - "ز", - "ف", - "گ", - "ع", - "خ", - "ق", - "ج", - "آ", - "پ", - "ح", - "ط", - "ص", - ], - "Arabic": [ - "ا", - "ل", - "ي", - "م", - "و", - "ن", - "ر", - "ت", - "ب", - "ة", - "ع", - "د", - "س", - "ف", - "ه", - "ك", - "ق", - "أ", - "ح", - "ج", - "ش", - "ط", - "ص", - "ى", - "خ", - "إ", - ], - "Danish": [ - "e", - "r", - "n", - "t", - "a", - "i", - "s", - "d", - "l", - "o", - "g", - "m", - "k", - "f", - "v", - "u", - "b", - "h", - "p", - "å", - "y", - "ø", - "æ", - "c", - "j", - "w", - ], - "Serbian": [ - "а", - "и", - "о", - "е", - "н", - "р", - "с", - "у", - "т", - "к", - "ј", - "в", - "д", - "м", - "п", - "л", - "г", - "з", - "б", - "a", - "i", - "e", - "o", - "n", - "ц", - "ш", - ], - "Lithuanian": [ - "i", - "a", - "s", - "o", - "r", - "e", - "t", - "n", - "u", - "k", - "m", - "l", - "p", - "v", - "d", - "j", - "g", - "ė", - "b", - "y", - "ų", - "š", - "ž", - "c", - "ą", - "į", - ], - "Slovene": [ - "e", - "a", - "i", - "o", - "n", - "r", - "s", - "l", - "t", - "j", - "v", - "k", - "d", - "p", - "m", - "u", - "z", - "b", - "g", - "h", - "č", - "c", - "š", - "ž", - "f", - "y", - ], - "Slovak": [ - "o", - "a", - "e", - "n", - "i", - "r", - "v", - "t", - "s", - "l", - "k", - "d", - "m", - "p", - "u", - "c", - "h", - "j", - "b", - "z", - "á", - "y", - "ý", - "í", - "č", - "é", - ], - "Hebrew": [ - "י", - "ו", - "ה", - "ל", - "ר", - "ב", - "ת", - "מ", - "א", - "ש", - "נ", - "ע", - "ם", - "ד", - "ק", - "ח", - "פ", - "ס", - "כ", - "ג", - "ט", - "צ", - "ן", - "ז", - "ך", - ], - "Bulgarian": [ - "а", - "и", - "о", - "е", - "н", - "т", - "р", - "с", - "в", - "л", - "к", - "д", - "п", - "м", - "з", - "г", - "я", - "ъ", - "у", - "б", - "ч", - "ц", - "й", - "ж", - "щ", - "х", - ], - "Croatian": [ - "a", - "i", - "o", - "e", - "n", - "r", - "j", - "s", - "t", - "u", - "k", - "l", - "v", - "d", - "m", - "p", - "g", - "z", - "b", - "c", - "č", - "h", - "š", - "ž", - "ć", - "f", - ], - "Hindi": [ - "क", - "र", - "स", - "न", - "त", - "म", - "ह", - "प", - "य", - "ल", - "व", - "ज", - "द", - "ग", - "ब", - "श", - "ट", - "अ", - "ए", - "थ", - "भ", - "ड", - "च", - "ध", - "ष", - "इ", - ], - "Estonian": [ - "a", - "i", - "e", - "s", - "t", - "l", - "u", - "n", - "o", - "k", - "r", - "d", - "m", - "v", - "g", - "p", - "j", - "h", - "ä", - "b", - "õ", - "ü", - "f", - "c", - "ö", - "y", - ], - "Thai": [ - "า", - "น", - "ร", - "อ", - "ก", - "เ", - "ง", - "ม", - "ย", - "ล", - "ว", - "ด", - "ท", - "ส", - "ต", - "ะ", - "ป", - "บ", - "ค", - "ห", - "แ", - "จ", - "พ", - "ช", - "ข", - "ใ", - ], - "Greek": [ - "α", - "τ", - "ο", - "ι", - "ε", - "ν", - "ρ", - "σ", - "κ", - "η", - "π", - "ς", - "υ", - "μ", - "λ", - "ί", - "ό", - "ά", - "γ", - "έ", - "δ", - "ή", - "ω", - "χ", - "θ", - "ύ", - ], - "Tamil": [ - "க", - "த", - "ப", - "ட", - "ர", - "ம", - "ல", - "ன", - "வ", - "ற", - "ய", - "ள", - "ச", - "ந", - "இ", - "ண", - "அ", - "ஆ", - "ழ", - "ங", - "எ", - "உ", - "ஒ", - "ஸ", - ], - "Kazakh": [ - "а", - "ы", - "е", - "н", - "т", - "р", - "л", - "і", - "д", - "с", - "м", - "қ", - "к", - "о", - "б", - "и", - "у", - "ғ", - "ж", - "ң", - "з", - "ш", - "й", - "п", - "г", - "ө", - ], -} - -LANGUAGE_SUPPORTED_COUNT: int = len(FREQUENCIES) diff --git a/venv/Lib/site-packages/charset_normalizer/legacy.py b/venv/Lib/site-packages/charset_normalizer/legacy.py deleted file mode 100644 index e221bec..0000000 --- a/venv/Lib/site-packages/charset_normalizer/legacy.py +++ /dev/null @@ -1,64 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any -from warnings import warn - -from .api import from_bytes -from .constant import CHARDET_CORRESPONDENCE - -# TODO: remove this check when dropping Python 3.7 support -if TYPE_CHECKING: - from typing_extensions import TypedDict - - class ResultDict(TypedDict): - encoding: str | None - language: str - confidence: float | None - - -def detect( - byte_str: bytes, should_rename_legacy: bool = False, **kwargs: Any -) -> ResultDict: - """ - chardet legacy method - Detect the encoding of the given byte string. It should be mostly backward-compatible. - Encoding name will match Chardet own writing whenever possible. (Not on encoding name unsupported by it) - This function is deprecated and should be used to migrate your project easily, consult the documentation for - further information. Not planned for removal. - - :param byte_str: The byte sequence to examine. - :param should_rename_legacy: Should we rename legacy encodings - to their more modern equivalents? - """ - if len(kwargs): - warn( - f"charset-normalizer disregard arguments '{','.join(list(kwargs.keys()))}' in legacy function detect()" - ) - - if not isinstance(byte_str, (bytearray, bytes)): - raise TypeError( # pragma: nocover - f"Expected object of type bytes or bytearray, got: {type(byte_str)}" - ) - - if isinstance(byte_str, bytearray): - byte_str = bytes(byte_str) - - r = from_bytes(byte_str).best() - - encoding = r.encoding if r is not None else None - language = r.language if r is not None and r.language != "Unknown" else "" - confidence = 1.0 - r.chaos if r is not None else None - - # Note: CharsetNormalizer does not return 'UTF-8-SIG' as the sig get stripped in the detection/normalization process - # but chardet does return 'utf-8-sig' and it is a valid codec name. - if r is not None and encoding == "utf_8" and r.bom: - encoding += "_sig" - - if should_rename_legacy is False and encoding in CHARDET_CORRESPONDENCE: - encoding = CHARDET_CORRESPONDENCE[encoding] - - return { - "encoding": encoding, - "language": language, - "confidence": confidence, - } diff --git a/venv/Lib/site-packages/charset_normalizer/md.cp310-win_amd64.pyd b/venv/Lib/site-packages/charset_normalizer/md.cp310-win_amd64.pyd deleted file mode 100644 index 2e61ae4..0000000 Binary files a/venv/Lib/site-packages/charset_normalizer/md.cp310-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/charset_normalizer/md.py b/venv/Lib/site-packages/charset_normalizer/md.py deleted file mode 100644 index 12ce024..0000000 --- a/venv/Lib/site-packages/charset_normalizer/md.py +++ /dev/null @@ -1,635 +0,0 @@ -from __future__ import annotations - -from functools import lru_cache -from logging import getLogger - -from .constant import ( - COMMON_SAFE_ASCII_CHARACTERS, - TRACE, - UNICODE_SECONDARY_RANGE_KEYWORD, -) -from .utils import ( - is_accentuated, - is_arabic, - is_arabic_isolated_form, - is_case_variable, - is_cjk, - is_emoticon, - is_hangul, - is_hiragana, - is_katakana, - is_latin, - is_punctuation, - is_separator, - is_symbol, - is_thai, - is_unprintable, - remove_accent, - unicode_range, - is_cjk_uncommon, -) - - -class MessDetectorPlugin: - """ - Base abstract class used for mess detection plugins. - All detectors MUST extend and implement given methods. - """ - - def eligible(self, character: str) -> bool: - """ - Determine if given character should be fed in. - """ - raise NotImplementedError # pragma: nocover - - def feed(self, character: str) -> None: - """ - The main routine to be executed upon character. - Insert the logic in witch the text would be considered chaotic. - """ - raise NotImplementedError # pragma: nocover - - def reset(self) -> None: # pragma: no cover - """ - Permit to reset the plugin to the initial state. - """ - raise NotImplementedError - - @property - def ratio(self) -> float: - """ - Compute the chaos ratio based on what your feed() has seen. - Must NOT be lower than 0.; No restriction gt 0. - """ - raise NotImplementedError # pragma: nocover - - -class TooManySymbolOrPunctuationPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._punctuation_count: int = 0 - self._symbol_count: int = 0 - self._character_count: int = 0 - - self._last_printable_char: str | None = None - self._frenzy_symbol_in_word: bool = False - - def eligible(self, character: str) -> bool: - return character.isprintable() - - def feed(self, character: str) -> None: - self._character_count += 1 - - if ( - character != self._last_printable_char - and character not in COMMON_SAFE_ASCII_CHARACTERS - ): - if is_punctuation(character): - self._punctuation_count += 1 - elif ( - character.isdigit() is False - and is_symbol(character) - and is_emoticon(character) is False - ): - self._symbol_count += 2 - - self._last_printable_char = character - - def reset(self) -> None: # Abstract - self._punctuation_count = 0 - self._character_count = 0 - self._symbol_count = 0 - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - ratio_of_punctuation: float = ( - self._punctuation_count + self._symbol_count - ) / self._character_count - - return ratio_of_punctuation if ratio_of_punctuation >= 0.3 else 0.0 - - -class TooManyAccentuatedPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._character_count: int = 0 - self._accentuated_count: int = 0 - - def eligible(self, character: str) -> bool: - return character.isalpha() - - def feed(self, character: str) -> None: - self._character_count += 1 - - if is_accentuated(character): - self._accentuated_count += 1 - - def reset(self) -> None: # Abstract - self._character_count = 0 - self._accentuated_count = 0 - - @property - def ratio(self) -> float: - if self._character_count < 8: - return 0.0 - - ratio_of_accentuation: float = self._accentuated_count / self._character_count - return ratio_of_accentuation if ratio_of_accentuation >= 0.35 else 0.0 - - -class UnprintablePlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._unprintable_count: int = 0 - self._character_count: int = 0 - - def eligible(self, character: str) -> bool: - return True - - def feed(self, character: str) -> None: - if is_unprintable(character): - self._unprintable_count += 1 - self._character_count += 1 - - def reset(self) -> None: # Abstract - self._unprintable_count = 0 - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - return (self._unprintable_count * 8) / self._character_count - - -class SuspiciousDuplicateAccentPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._successive_count: int = 0 - self._character_count: int = 0 - - self._last_latin_character: str | None = None - - def eligible(self, character: str) -> bool: - return character.isalpha() and is_latin(character) - - def feed(self, character: str) -> None: - self._character_count += 1 - if ( - self._last_latin_character is not None - and is_accentuated(character) - and is_accentuated(self._last_latin_character) - ): - if character.isupper() and self._last_latin_character.isupper(): - self._successive_count += 1 - # Worse if its the same char duplicated with different accent. - if remove_accent(character) == remove_accent(self._last_latin_character): - self._successive_count += 1 - self._last_latin_character = character - - def reset(self) -> None: # Abstract - self._successive_count = 0 - self._character_count = 0 - self._last_latin_character = None - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - return (self._successive_count * 2) / self._character_count - - -class SuspiciousRange(MessDetectorPlugin): - def __init__(self) -> None: - self._suspicious_successive_range_count: int = 0 - self._character_count: int = 0 - self._last_printable_seen: str | None = None - - def eligible(self, character: str) -> bool: - return character.isprintable() - - def feed(self, character: str) -> None: - self._character_count += 1 - - if ( - character.isspace() - or is_punctuation(character) - or character in COMMON_SAFE_ASCII_CHARACTERS - ): - self._last_printable_seen = None - return - - if self._last_printable_seen is None: - self._last_printable_seen = character - return - - unicode_range_a: str | None = unicode_range(self._last_printable_seen) - unicode_range_b: str | None = unicode_range(character) - - if is_suspiciously_successive_range(unicode_range_a, unicode_range_b): - self._suspicious_successive_range_count += 1 - - self._last_printable_seen = character - - def reset(self) -> None: # Abstract - self._character_count = 0 - self._suspicious_successive_range_count = 0 - self._last_printable_seen = None - - @property - def ratio(self) -> float: - if self._character_count <= 13: - return 0.0 - - ratio_of_suspicious_range_usage: float = ( - self._suspicious_successive_range_count * 2 - ) / self._character_count - - return ratio_of_suspicious_range_usage - - -class SuperWeirdWordPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._word_count: int = 0 - self._bad_word_count: int = 0 - self._foreign_long_count: int = 0 - - self._is_current_word_bad: bool = False - self._foreign_long_watch: bool = False - - self._character_count: int = 0 - self._bad_character_count: int = 0 - - self._buffer: str = "" - self._buffer_accent_count: int = 0 - self._buffer_glyph_count: int = 0 - - def eligible(self, character: str) -> bool: - return True - - def feed(self, character: str) -> None: - if character.isalpha(): - self._buffer += character - if is_accentuated(character): - self._buffer_accent_count += 1 - if ( - self._foreign_long_watch is False - and (is_latin(character) is False or is_accentuated(character)) - and is_cjk(character) is False - and is_hangul(character) is False - and is_katakana(character) is False - and is_hiragana(character) is False - and is_thai(character) is False - ): - self._foreign_long_watch = True - if ( - is_cjk(character) - or is_hangul(character) - or is_katakana(character) - or is_hiragana(character) - or is_thai(character) - ): - self._buffer_glyph_count += 1 - return - if not self._buffer: - return - if ( - character.isspace() or is_punctuation(character) or is_separator(character) - ) and self._buffer: - self._word_count += 1 - buffer_length: int = len(self._buffer) - - self._character_count += buffer_length - - if buffer_length >= 4: - if self._buffer_accent_count / buffer_length >= 0.5: - self._is_current_word_bad = True - # Word/Buffer ending with an upper case accentuated letter are so rare, - # that we will consider them all as suspicious. Same weight as foreign_long suspicious. - elif ( - is_accentuated(self._buffer[-1]) - and self._buffer[-1].isupper() - and all(_.isupper() for _ in self._buffer) is False - ): - self._foreign_long_count += 1 - self._is_current_word_bad = True - elif self._buffer_glyph_count == 1: - self._is_current_word_bad = True - self._foreign_long_count += 1 - if buffer_length >= 24 and self._foreign_long_watch: - camel_case_dst = [ - i - for c, i in zip(self._buffer, range(0, buffer_length)) - if c.isupper() - ] - probable_camel_cased: bool = False - - if camel_case_dst and (len(camel_case_dst) / buffer_length <= 0.3): - probable_camel_cased = True - - if not probable_camel_cased: - self._foreign_long_count += 1 - self._is_current_word_bad = True - - if self._is_current_word_bad: - self._bad_word_count += 1 - self._bad_character_count += len(self._buffer) - self._is_current_word_bad = False - - self._foreign_long_watch = False - self._buffer = "" - self._buffer_accent_count = 0 - self._buffer_glyph_count = 0 - elif ( - character not in {"<", ">", "-", "=", "~", "|", "_"} - and character.isdigit() is False - and is_symbol(character) - ): - self._is_current_word_bad = True - self._buffer += character - - def reset(self) -> None: # Abstract - self._buffer = "" - self._is_current_word_bad = False - self._foreign_long_watch = False - self._bad_word_count = 0 - self._word_count = 0 - self._character_count = 0 - self._bad_character_count = 0 - self._foreign_long_count = 0 - - @property - def ratio(self) -> float: - if self._word_count <= 10 and self._foreign_long_count == 0: - return 0.0 - - return self._bad_character_count / self._character_count - - -class CjkUncommonPlugin(MessDetectorPlugin): - """ - Detect messy CJK text that probably means nothing. - """ - - def __init__(self) -> None: - self._character_count: int = 0 - self._uncommon_count: int = 0 - - def eligible(self, character: str) -> bool: - return is_cjk(character) - - def feed(self, character: str) -> None: - self._character_count += 1 - - if is_cjk_uncommon(character): - self._uncommon_count += 1 - return - - def reset(self) -> None: # Abstract - self._character_count = 0 - self._uncommon_count = 0 - - @property - def ratio(self) -> float: - if self._character_count < 8: - return 0.0 - - uncommon_form_usage: float = self._uncommon_count / self._character_count - - # we can be pretty sure it's garbage when uncommon characters are widely - # used. otherwise it could just be traditional chinese for example. - return uncommon_form_usage / 10 if uncommon_form_usage > 0.5 else 0.0 - - -class ArchaicUpperLowerPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._buf: bool = False - - self._character_count_since_last_sep: int = 0 - - self._successive_upper_lower_count: int = 0 - self._successive_upper_lower_count_final: int = 0 - - self._character_count: int = 0 - - self._last_alpha_seen: str | None = None - self._current_ascii_only: bool = True - - def eligible(self, character: str) -> bool: - return True - - def feed(self, character: str) -> None: - is_concerned = character.isalpha() and is_case_variable(character) - chunk_sep = is_concerned is False - - if chunk_sep and self._character_count_since_last_sep > 0: - if ( - self._character_count_since_last_sep <= 64 - and character.isdigit() is False - and self._current_ascii_only is False - ): - self._successive_upper_lower_count_final += ( - self._successive_upper_lower_count - ) - - self._successive_upper_lower_count = 0 - self._character_count_since_last_sep = 0 - self._last_alpha_seen = None - self._buf = False - self._character_count += 1 - self._current_ascii_only = True - - return - - if self._current_ascii_only is True and character.isascii() is False: - self._current_ascii_only = False - - if self._last_alpha_seen is not None: - if (character.isupper() and self._last_alpha_seen.islower()) or ( - character.islower() and self._last_alpha_seen.isupper() - ): - if self._buf is True: - self._successive_upper_lower_count += 2 - self._buf = False - else: - self._buf = True - else: - self._buf = False - - self._character_count += 1 - self._character_count_since_last_sep += 1 - self._last_alpha_seen = character - - def reset(self) -> None: # Abstract - self._character_count = 0 - self._character_count_since_last_sep = 0 - self._successive_upper_lower_count = 0 - self._successive_upper_lower_count_final = 0 - self._last_alpha_seen = None - self._buf = False - self._current_ascii_only = True - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - return self._successive_upper_lower_count_final / self._character_count - - -class ArabicIsolatedFormPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._character_count: int = 0 - self._isolated_form_count: int = 0 - - def reset(self) -> None: # Abstract - self._character_count = 0 - self._isolated_form_count = 0 - - def eligible(self, character: str) -> bool: - return is_arabic(character) - - def feed(self, character: str) -> None: - self._character_count += 1 - - if is_arabic_isolated_form(character): - self._isolated_form_count += 1 - - @property - def ratio(self) -> float: - if self._character_count < 8: - return 0.0 - - isolated_form_usage: float = self._isolated_form_count / self._character_count - - return isolated_form_usage - - -@lru_cache(maxsize=1024) -def is_suspiciously_successive_range( - unicode_range_a: str | None, unicode_range_b: str | None -) -> bool: - """ - Determine if two Unicode range seen next to each other can be considered as suspicious. - """ - if unicode_range_a is None or unicode_range_b is None: - return True - - if unicode_range_a == unicode_range_b: - return False - - if "Latin" in unicode_range_a and "Latin" in unicode_range_b: - return False - - if "Emoticons" in unicode_range_a or "Emoticons" in unicode_range_b: - return False - - # Latin characters can be accompanied with a combining diacritical mark - # eg. Vietnamese. - if ("Latin" in unicode_range_a or "Latin" in unicode_range_b) and ( - "Combining" in unicode_range_a or "Combining" in unicode_range_b - ): - return False - - keywords_range_a, keywords_range_b = ( - unicode_range_a.split(" "), - unicode_range_b.split(" "), - ) - - for el in keywords_range_a: - if el in UNICODE_SECONDARY_RANGE_KEYWORD: - continue - if el in keywords_range_b: - return False - - # Japanese Exception - range_a_jp_chars, range_b_jp_chars = ( - unicode_range_a - in ( - "Hiragana", - "Katakana", - ), - unicode_range_b in ("Hiragana", "Katakana"), - ) - if (range_a_jp_chars or range_b_jp_chars) and ( - "CJK" in unicode_range_a or "CJK" in unicode_range_b - ): - return False - if range_a_jp_chars and range_b_jp_chars: - return False - - if "Hangul" in unicode_range_a or "Hangul" in unicode_range_b: - if "CJK" in unicode_range_a or "CJK" in unicode_range_b: - return False - if unicode_range_a == "Basic Latin" or unicode_range_b == "Basic Latin": - return False - - # Chinese/Japanese use dedicated range for punctuation and/or separators. - if ("CJK" in unicode_range_a or "CJK" in unicode_range_b) or ( - unicode_range_a in ["Katakana", "Hiragana"] - and unicode_range_b in ["Katakana", "Hiragana"] - ): - if "Punctuation" in unicode_range_a or "Punctuation" in unicode_range_b: - return False - if "Forms" in unicode_range_a or "Forms" in unicode_range_b: - return False - if unicode_range_a == "Basic Latin" or unicode_range_b == "Basic Latin": - return False - - return True - - -@lru_cache(maxsize=2048) -def mess_ratio( - decoded_sequence: str, maximum_threshold: float = 0.2, debug: bool = False -) -> float: - """ - Compute a mess ratio given a decoded bytes sequence. The maximum threshold does stop the computation earlier. - """ - - detectors: list[MessDetectorPlugin] = [ - md_class() for md_class in MessDetectorPlugin.__subclasses__() - ] - - length: int = len(decoded_sequence) + 1 - - mean_mess_ratio: float = 0.0 - - if length < 512: - intermediary_mean_mess_ratio_calc: int = 32 - elif length <= 1024: - intermediary_mean_mess_ratio_calc = 64 - else: - intermediary_mean_mess_ratio_calc = 128 - - for character, index in zip(decoded_sequence + "\n", range(length)): - for detector in detectors: - if detector.eligible(character): - detector.feed(character) - - if ( - index > 0 and index % intermediary_mean_mess_ratio_calc == 0 - ) or index == length - 1: - mean_mess_ratio = sum(dt.ratio for dt in detectors) - - if mean_mess_ratio >= maximum_threshold: - break - - if debug: - logger = getLogger("charset_normalizer") - - logger.log( - TRACE, - "Mess-detector extended-analysis start. " - f"intermediary_mean_mess_ratio_calc={intermediary_mean_mess_ratio_calc} mean_mess_ratio={mean_mess_ratio} " - f"maximum_threshold={maximum_threshold}", - ) - - if len(decoded_sequence) > 16: - logger.log(TRACE, f"Starting with: {decoded_sequence[:16]}") - logger.log(TRACE, f"Ending with: {decoded_sequence[-16::]}") - - for dt in detectors: - logger.log(TRACE, f"{dt.__class__}: {dt.ratio}") - - return round(mean_mess_ratio, 3) diff --git a/venv/Lib/site-packages/charset_normalizer/md__mypyc.cp310-win_amd64.pyd b/venv/Lib/site-packages/charset_normalizer/md__mypyc.cp310-win_amd64.pyd deleted file mode 100644 index cee5fb9..0000000 Binary files a/venv/Lib/site-packages/charset_normalizer/md__mypyc.cp310-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/charset_normalizer/models.py b/venv/Lib/site-packages/charset_normalizer/models.py deleted file mode 100644 index 1042758..0000000 --- a/venv/Lib/site-packages/charset_normalizer/models.py +++ /dev/null @@ -1,360 +0,0 @@ -from __future__ import annotations - -from encodings.aliases import aliases -from hashlib import sha256 -from json import dumps -from re import sub -from typing import Any, Iterator, List, Tuple - -from .constant import RE_POSSIBLE_ENCODING_INDICATION, TOO_BIG_SEQUENCE -from .utils import iana_name, is_multi_byte_encoding, unicode_range - - -class CharsetMatch: - def __init__( - self, - payload: bytes, - guessed_encoding: str, - mean_mess_ratio: float, - has_sig_or_bom: bool, - languages: CoherenceMatches, - decoded_payload: str | None = None, - preemptive_declaration: str | None = None, - ): - self._payload: bytes = payload - - self._encoding: str = guessed_encoding - self._mean_mess_ratio: float = mean_mess_ratio - self._languages: CoherenceMatches = languages - self._has_sig_or_bom: bool = has_sig_or_bom - self._unicode_ranges: list[str] | None = None - - self._leaves: list[CharsetMatch] = [] - self._mean_coherence_ratio: float = 0.0 - - self._output_payload: bytes | None = None - self._output_encoding: str | None = None - - self._string: str | None = decoded_payload - - self._preemptive_declaration: str | None = preemptive_declaration - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CharsetMatch): - if isinstance(other, str): - return iana_name(other) == self.encoding - return False - return self.encoding == other.encoding and self.fingerprint == other.fingerprint - - def __lt__(self, other: object) -> bool: - """ - Implemented to make sorted available upon CharsetMatches items. - """ - if not isinstance(other, CharsetMatch): - raise ValueError - - chaos_difference: float = abs(self.chaos - other.chaos) - coherence_difference: float = abs(self.coherence - other.coherence) - - # Below 1% difference --> Use Coherence - if chaos_difference < 0.01 and coherence_difference > 0.02: - return self.coherence > other.coherence - elif chaos_difference < 0.01 and coherence_difference <= 0.02: - # When having a difficult decision, use the result that decoded as many multi-byte as possible. - # preserve RAM usage! - if len(self._payload) >= TOO_BIG_SEQUENCE: - return self.chaos < other.chaos - return self.multi_byte_usage > other.multi_byte_usage - - return self.chaos < other.chaos - - @property - def multi_byte_usage(self) -> float: - return 1.0 - (len(str(self)) / len(self.raw)) - - def __str__(self) -> str: - # Lazy Str Loading - if self._string is None: - self._string = str(self._payload, self._encoding, "strict") - return self._string - - def __repr__(self) -> str: - return f"" - - def add_submatch(self, other: CharsetMatch) -> None: - if not isinstance(other, CharsetMatch) or other == self: - raise ValueError( - "Unable to add instance <{}> as a submatch of a CharsetMatch".format( - other.__class__ - ) - ) - - other._string = None # Unload RAM usage; dirty trick. - self._leaves.append(other) - - @property - def encoding(self) -> str: - return self._encoding - - @property - def encoding_aliases(self) -> list[str]: - """ - Encoding name are known by many name, using this could help when searching for IBM855 when it's listed as CP855. - """ - also_known_as: list[str] = [] - for u, p in aliases.items(): - if self.encoding == u: - also_known_as.append(p) - elif self.encoding == p: - also_known_as.append(u) - return also_known_as - - @property - def bom(self) -> bool: - return self._has_sig_or_bom - - @property - def byte_order_mark(self) -> bool: - return self._has_sig_or_bom - - @property - def languages(self) -> list[str]: - """ - Return the complete list of possible languages found in decoded sequence. - Usually not really useful. Returned list may be empty even if 'language' property return something != 'Unknown'. - """ - return [e[0] for e in self._languages] - - @property - def language(self) -> str: - """ - Most probable language found in decoded sequence. If none were detected or inferred, the property will return - "Unknown". - """ - if not self._languages: - # Trying to infer the language based on the given encoding - # Its either English or we should not pronounce ourselves in certain cases. - if "ascii" in self.could_be_from_charset: - return "English" - - # doing it there to avoid circular import - from charset_normalizer.cd import encoding_languages, mb_encoding_languages - - languages = ( - mb_encoding_languages(self.encoding) - if is_multi_byte_encoding(self.encoding) - else encoding_languages(self.encoding) - ) - - if len(languages) == 0 or "Latin Based" in languages: - return "Unknown" - - return languages[0] - - return self._languages[0][0] - - @property - def chaos(self) -> float: - return self._mean_mess_ratio - - @property - def coherence(self) -> float: - if not self._languages: - return 0.0 - return self._languages[0][1] - - @property - def percent_chaos(self) -> float: - return round(self.chaos * 100, ndigits=3) - - @property - def percent_coherence(self) -> float: - return round(self.coherence * 100, ndigits=3) - - @property - def raw(self) -> bytes: - """ - Original untouched bytes. - """ - return self._payload - - @property - def submatch(self) -> list[CharsetMatch]: - return self._leaves - - @property - def has_submatch(self) -> bool: - return len(self._leaves) > 0 - - @property - def alphabets(self) -> list[str]: - if self._unicode_ranges is not None: - return self._unicode_ranges - # list detected ranges - detected_ranges: list[str | None] = [unicode_range(char) for char in str(self)] - # filter and sort - self._unicode_ranges = sorted(list({r for r in detected_ranges if r})) - return self._unicode_ranges - - @property - def could_be_from_charset(self) -> list[str]: - """ - The complete list of encoding that output the exact SAME str result and therefore could be the originating - encoding. - This list does include the encoding available in property 'encoding'. - """ - return [self._encoding] + [m.encoding for m in self._leaves] - - def output(self, encoding: str = "utf_8") -> bytes: - """ - Method to get re-encoded bytes payload using given target encoding. Default to UTF-8. - Any errors will be simply ignored by the encoder NOT replaced. - """ - if self._output_encoding is None or self._output_encoding != encoding: - self._output_encoding = encoding - decoded_string = str(self) - if ( - self._preemptive_declaration is not None - and self._preemptive_declaration.lower() - not in ["utf-8", "utf8", "utf_8"] - ): - patched_header = sub( - RE_POSSIBLE_ENCODING_INDICATION, - lambda m: m.string[m.span()[0] : m.span()[1]].replace( - m.groups()[0], - iana_name(self._output_encoding).replace("_", "-"), # type: ignore[arg-type] - ), - decoded_string[:8192], - count=1, - ) - - decoded_string = patched_header + decoded_string[8192:] - - self._output_payload = decoded_string.encode(encoding, "replace") - - return self._output_payload # type: ignore - - @property - def fingerprint(self) -> str: - """ - Retrieve the unique SHA256 computed using the transformed (re-encoded) payload. Not the original one. - """ - return sha256(self.output()).hexdigest() - - -class CharsetMatches: - """ - Container with every CharsetMatch items ordered by default from most probable to the less one. - Act like a list(iterable) but does not implements all related methods. - """ - - def __init__(self, results: list[CharsetMatch] | None = None): - self._results: list[CharsetMatch] = sorted(results) if results else [] - - def __iter__(self) -> Iterator[CharsetMatch]: - yield from self._results - - def __getitem__(self, item: int | str) -> CharsetMatch: - """ - Retrieve a single item either by its position or encoding name (alias may be used here). - Raise KeyError upon invalid index or encoding not present in results. - """ - if isinstance(item, int): - return self._results[item] - if isinstance(item, str): - item = iana_name(item, False) - for result in self._results: - if item in result.could_be_from_charset: - return result - raise KeyError - - def __len__(self) -> int: - return len(self._results) - - def __bool__(self) -> bool: - return len(self._results) > 0 - - def append(self, item: CharsetMatch) -> None: - """ - Insert a single match. Will be inserted accordingly to preserve sort. - Can be inserted as a submatch. - """ - if not isinstance(item, CharsetMatch): - raise ValueError( - "Cannot append instance '{}' to CharsetMatches".format( - str(item.__class__) - ) - ) - # We should disable the submatch factoring when the input file is too heavy (conserve RAM usage) - if len(item.raw) < TOO_BIG_SEQUENCE: - for match in self._results: - if match.fingerprint == item.fingerprint and match.chaos == item.chaos: - match.add_submatch(item) - return - self._results.append(item) - self._results = sorted(self._results) - - def best(self) -> CharsetMatch | None: - """ - Simply return the first match. Strict equivalent to matches[0]. - """ - if not self._results: - return None - return self._results[0] - - def first(self) -> CharsetMatch | None: - """ - Redundant method, call the method best(). Kept for BC reasons. - """ - return self.best() - - -CoherenceMatch = Tuple[str, float] -CoherenceMatches = List[CoherenceMatch] - - -class CliDetectionResult: - def __init__( - self, - path: str, - encoding: str | None, - encoding_aliases: list[str], - alternative_encodings: list[str], - language: str, - alphabets: list[str], - has_sig_or_bom: bool, - chaos: float, - coherence: float, - unicode_path: str | None, - is_preferred: bool, - ): - self.path: str = path - self.unicode_path: str | None = unicode_path - self.encoding: str | None = encoding - self.encoding_aliases: list[str] = encoding_aliases - self.alternative_encodings: list[str] = alternative_encodings - self.language: str = language - self.alphabets: list[str] = alphabets - self.has_sig_or_bom: bool = has_sig_or_bom - self.chaos: float = chaos - self.coherence: float = coherence - self.is_preferred: bool = is_preferred - - @property - def __dict__(self) -> dict[str, Any]: # type: ignore - return { - "path": self.path, - "encoding": self.encoding, - "encoding_aliases": self.encoding_aliases, - "alternative_encodings": self.alternative_encodings, - "language": self.language, - "alphabets": self.alphabets, - "has_sig_or_bom": self.has_sig_or_bom, - "chaos": self.chaos, - "coherence": self.coherence, - "unicode_path": self.unicode_path, - "is_preferred": self.is_preferred, - } - - def to_json(self) -> str: - return dumps(self.__dict__, ensure_ascii=True, indent=4) diff --git a/venv/Lib/site-packages/charset_normalizer/py.typed b/venv/Lib/site-packages/charset_normalizer/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/venv/Lib/site-packages/charset_normalizer/utils.py b/venv/Lib/site-packages/charset_normalizer/utils.py deleted file mode 100644 index 6bf0384..0000000 --- a/venv/Lib/site-packages/charset_normalizer/utils.py +++ /dev/null @@ -1,414 +0,0 @@ -from __future__ import annotations - -import importlib -import logging -import unicodedata -from codecs import IncrementalDecoder -from encodings.aliases import aliases -from functools import lru_cache -from re import findall -from typing import Generator - -from _multibytecodec import ( # type: ignore[import-not-found,import] - MultibyteIncrementalDecoder, -) - -from .constant import ( - ENCODING_MARKS, - IANA_SUPPORTED_SIMILAR, - RE_POSSIBLE_ENCODING_INDICATION, - UNICODE_RANGES_COMBINED, - UNICODE_SECONDARY_RANGE_KEYWORD, - UTF8_MAXIMAL_ALLOCATION, - COMMON_CJK_CHARACTERS, -) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_accentuated(character: str) -> bool: - try: - description: str = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - return ( - "WITH GRAVE" in description - or "WITH ACUTE" in description - or "WITH CEDILLA" in description - or "WITH DIAERESIS" in description - or "WITH CIRCUMFLEX" in description - or "WITH TILDE" in description - or "WITH MACRON" in description - or "WITH RING ABOVE" in description - ) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def remove_accent(character: str) -> str: - decomposed: str = unicodedata.decomposition(character) - if not decomposed: - return character - - codes: list[str] = decomposed.split(" ") - - return chr(int(codes[0], 16)) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def unicode_range(character: str) -> str | None: - """ - Retrieve the Unicode range official name from a single character. - """ - character_ord: int = ord(character) - - for range_name, ord_range in UNICODE_RANGES_COMBINED.items(): - if character_ord in ord_range: - return range_name - - return None - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_latin(character: str) -> bool: - try: - description: str = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - return "LATIN" in description - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_punctuation(character: str) -> bool: - character_category: str = unicodedata.category(character) - - if "P" in character_category: - return True - - character_range: str | None = unicode_range(character) - - if character_range is None: - return False - - return "Punctuation" in character_range - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_symbol(character: str) -> bool: - character_category: str = unicodedata.category(character) - - if "S" in character_category or "N" in character_category: - return True - - character_range: str | None = unicode_range(character) - - if character_range is None: - return False - - return "Forms" in character_range and character_category != "Lo" - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_emoticon(character: str) -> bool: - character_range: str | None = unicode_range(character) - - if character_range is None: - return False - - return "Emoticons" in character_range or "Pictographs" in character_range - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_separator(character: str) -> bool: - if character.isspace() or character in {"|", "+", "<", ">"}: - return True - - character_category: str = unicodedata.category(character) - - return "Z" in character_category or character_category in {"Po", "Pd", "Pc"} - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_case_variable(character: str) -> bool: - return character.islower() != character.isupper() - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_cjk(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - - return "CJK" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_hiragana(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - - return "HIRAGANA" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_katakana(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - - return "KATAKANA" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_hangul(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - - return "HANGUL" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_thai(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - - return "THAI" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_arabic(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - - return "ARABIC" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_arabic_isolated_form(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - - return "ARABIC" in character_name and "ISOLATED FORM" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_cjk_uncommon(character: str) -> bool: - return character not in COMMON_CJK_CHARACTERS - - -@lru_cache(maxsize=len(UNICODE_RANGES_COMBINED)) -def is_unicode_range_secondary(range_name: str) -> bool: - return any(keyword in range_name for keyword in UNICODE_SECONDARY_RANGE_KEYWORD) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_unprintable(character: str) -> bool: - return ( - character.isspace() is False # includes \n \t \r \v - and character.isprintable() is False - and character != "\x1a" # Why? Its the ASCII substitute character. - and character != "\ufeff" # bug discovered in Python, - # Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space. - ) - - -def any_specified_encoding(sequence: bytes, search_zone: int = 8192) -> str | None: - """ - Extract using ASCII-only decoder any specified encoding in the first n-bytes. - """ - if not isinstance(sequence, bytes): - raise TypeError - - seq_len: int = len(sequence) - - results: list[str] = findall( - RE_POSSIBLE_ENCODING_INDICATION, - sequence[: min(seq_len, search_zone)].decode("ascii", errors="ignore"), - ) - - if len(results) == 0: - return None - - for specified_encoding in results: - specified_encoding = specified_encoding.lower().replace("-", "_") - - encoding_alias: str - encoding_iana: str - - for encoding_alias, encoding_iana in aliases.items(): - if encoding_alias == specified_encoding: - return encoding_iana - if encoding_iana == specified_encoding: - return encoding_iana - - return None - - -@lru_cache(maxsize=128) -def is_multi_byte_encoding(name: str) -> bool: - """ - Verify is a specific encoding is a multi byte one based on it IANA name - """ - return name in { - "utf_8", - "utf_8_sig", - "utf_16", - "utf_16_be", - "utf_16_le", - "utf_32", - "utf_32_le", - "utf_32_be", - "utf_7", - } or issubclass( - importlib.import_module(f"encodings.{name}").IncrementalDecoder, - MultibyteIncrementalDecoder, - ) - - -def identify_sig_or_bom(sequence: bytes) -> tuple[str | None, bytes]: - """ - Identify and extract SIG/BOM in given sequence. - """ - - for iana_encoding in ENCODING_MARKS: - marks: bytes | list[bytes] = ENCODING_MARKS[iana_encoding] - - if isinstance(marks, bytes): - marks = [marks] - - for mark in marks: - if sequence.startswith(mark): - return iana_encoding, mark - - return None, b"" - - -def should_strip_sig_or_bom(iana_encoding: str) -> bool: - return iana_encoding not in {"utf_16", "utf_32"} - - -def iana_name(cp_name: str, strict: bool = True) -> str: - """Returns the Python normalized encoding name (Not the IANA official name).""" - cp_name = cp_name.lower().replace("-", "_") - - encoding_alias: str - encoding_iana: str - - for encoding_alias, encoding_iana in aliases.items(): - if cp_name in [encoding_alias, encoding_iana]: - return encoding_iana - - if strict: - raise ValueError(f"Unable to retrieve IANA for '{cp_name}'") - - return cp_name - - -def cp_similarity(iana_name_a: str, iana_name_b: str) -> float: - if is_multi_byte_encoding(iana_name_a) or is_multi_byte_encoding(iana_name_b): - return 0.0 - - decoder_a = importlib.import_module(f"encodings.{iana_name_a}").IncrementalDecoder - decoder_b = importlib.import_module(f"encodings.{iana_name_b}").IncrementalDecoder - - id_a: IncrementalDecoder = decoder_a(errors="ignore") - id_b: IncrementalDecoder = decoder_b(errors="ignore") - - character_match_count: int = 0 - - for i in range(255): - to_be_decoded: bytes = bytes([i]) - if id_a.decode(to_be_decoded) == id_b.decode(to_be_decoded): - character_match_count += 1 - - return character_match_count / 254 - - -def is_cp_similar(iana_name_a: str, iana_name_b: str) -> bool: - """ - Determine if two code page are at least 80% similar. IANA_SUPPORTED_SIMILAR dict was generated using - the function cp_similarity. - """ - return ( - iana_name_a in IANA_SUPPORTED_SIMILAR - and iana_name_b in IANA_SUPPORTED_SIMILAR[iana_name_a] - ) - - -def set_logging_handler( - name: str = "charset_normalizer", - level: int = logging.INFO, - format_string: str = "%(asctime)s | %(levelname)s | %(message)s", -) -> None: - logger = logging.getLogger(name) - logger.setLevel(level) - - handler = logging.StreamHandler() - handler.setFormatter(logging.Formatter(format_string)) - logger.addHandler(handler) - - -def cut_sequence_chunks( - sequences: bytes, - encoding_iana: str, - offsets: range, - chunk_size: int, - bom_or_sig_available: bool, - strip_sig_or_bom: bool, - sig_payload: bytes, - is_multi_byte_decoder: bool, - decoded_payload: str | None = None, -) -> Generator[str, None, None]: - if decoded_payload and is_multi_byte_decoder is False: - for i in offsets: - chunk = decoded_payload[i : i + chunk_size] - if not chunk: - break - yield chunk - else: - for i in offsets: - chunk_end = i + chunk_size - if chunk_end > len(sequences) + 8: - continue - - cut_sequence = sequences[i : i + chunk_size] - - if bom_or_sig_available and strip_sig_or_bom is False: - cut_sequence = sig_payload + cut_sequence - - chunk = cut_sequence.decode( - encoding_iana, - errors="ignore" if is_multi_byte_decoder else "strict", - ) - - # multi-byte bad cutting detector and adjustment - # not the cleanest way to perform that fix but clever enough for now. - if is_multi_byte_decoder and i > 0: - chunk_partial_size_chk: int = min(chunk_size, 16) - - if ( - decoded_payload - and chunk[:chunk_partial_size_chk] not in decoded_payload - ): - for j in range(i, i - 4, -1): - cut_sequence = sequences[j:chunk_end] - - if bom_or_sig_available and strip_sig_or_bom is False: - cut_sequence = sig_payload + cut_sequence - - chunk = cut_sequence.decode(encoding_iana, errors="ignore") - - if chunk[:chunk_partial_size_chk] in decoded_payload: - break - - yield chunk diff --git a/venv/Lib/site-packages/charset_normalizer/version.py b/venv/Lib/site-packages/charset_normalizer/version.py deleted file mode 100644 index e5687e3..0000000 --- a/venv/Lib/site-packages/charset_normalizer/version.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -Expose version -""" - -from __future__ import annotations - -__version__ = "3.4.2" -VERSION = __version__.split(".") diff --git a/venv/Lib/site-packages/click-8.1.8.dist-info/INSTALLER b/venv/Lib/site-packages/click-8.1.8.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/Lib/site-packages/click-8.1.8.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/Lib/site-packages/click-8.1.8.dist-info/LICENSE.txt b/venv/Lib/site-packages/click-8.1.8.dist-info/LICENSE.txt deleted file mode 100644 index d12a849..0000000 --- a/venv/Lib/site-packages/click-8.1.8.dist-info/LICENSE.txt +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2014 Pallets - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/click-8.1.8.dist-info/METADATA b/venv/Lib/site-packages/click-8.1.8.dist-info/METADATA deleted file mode 100644 index 366d1a7..0000000 --- a/venv/Lib/site-packages/click-8.1.8.dist-info/METADATA +++ /dev/null @@ -1,74 +0,0 @@ -Metadata-Version: 2.3 -Name: click -Version: 8.1.8 -Summary: Composable command line interface toolkit -Maintainer-email: Pallets -Requires-Python: >=3.7 -Description-Content-Type: text/markdown -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Typing :: Typed -Requires-Dist: colorama; platform_system == 'Windows' -Requires-Dist: importlib-metadata; python_version < '3.8' -Project-URL: Changes, https://click.palletsprojects.com/changes/ -Project-URL: Chat, https://discord.gg/pallets -Project-URL: Documentation, https://click.palletsprojects.com/ -Project-URL: Donate, https://palletsprojects.com/donate -Project-URL: Source, https://github.com/pallets/click/ - -# $ click_ - -Click is a Python package for creating beautiful command line interfaces -in a composable way with as little code as necessary. It's the "Command -Line Interface Creation Kit". It's highly configurable but comes with -sensible defaults out of the box. - -It aims to make the process of writing command line tools quick and fun -while also preventing any frustration caused by the inability to -implement an intended CLI API. - -Click in three points: - -- Arbitrary nesting of commands -- Automatic help page generation -- Supports lazy loading of subcommands at runtime - - -## A Simple Example - -```python -import click - -@click.command() -@click.option("--count", default=1, help="Number of greetings.") -@click.option("--name", prompt="Your name", help="The person to greet.") -def hello(count, name): - """Simple program that greets NAME for a total of COUNT times.""" - for _ in range(count): - click.echo(f"Hello, {name}!") - -if __name__ == '__main__': - hello() -``` - -``` -$ python hello.py --count=3 -Your name: Click -Hello, Click! -Hello, Click! -Hello, Click! -``` - - -## Donate - -The Pallets organization develops and supports Click and other popular -packages. In order to grow the community of contributors and users, and -allow the maintainers to devote more time to the projects, [please -donate today][]. - -[please donate today]: https://palletsprojects.com/donate - diff --git a/venv/Lib/site-packages/click-8.1.8.dist-info/RECORD b/venv/Lib/site-packages/click-8.1.8.dist-info/RECORD deleted file mode 100644 index 312f26b..0000000 --- a/venv/Lib/site-packages/click-8.1.8.dist-info/RECORD +++ /dev/null @@ -1,38 +0,0 @@ -click-8.1.8.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -click-8.1.8.dist-info/LICENSE.txt,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 -click-8.1.8.dist-info/METADATA,sha256=WJtQ6uGS2ybLfvUE4vC0XIhIBr4yFGwjrMBR2fiCQ-Q,2263 -click-8.1.8.dist-info/RECORD,, -click-8.1.8.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82 -click/__init__.py,sha256=j1DJeCbga4ribkv5uyvIAzI0oFN13fW9mevDKShFelo,3188 -click/__pycache__/__init__.cpython-310.pyc,, -click/__pycache__/_compat.cpython-310.pyc,, -click/__pycache__/_termui_impl.cpython-310.pyc,, -click/__pycache__/_textwrap.cpython-310.pyc,, -click/__pycache__/_winconsole.cpython-310.pyc,, -click/__pycache__/core.cpython-310.pyc,, -click/__pycache__/decorators.cpython-310.pyc,, -click/__pycache__/exceptions.cpython-310.pyc,, -click/__pycache__/formatting.cpython-310.pyc,, -click/__pycache__/globals.cpython-310.pyc,, -click/__pycache__/parser.cpython-310.pyc,, -click/__pycache__/shell_completion.cpython-310.pyc,, -click/__pycache__/termui.cpython-310.pyc,, -click/__pycache__/testing.cpython-310.pyc,, -click/__pycache__/types.cpython-310.pyc,, -click/__pycache__/utils.cpython-310.pyc,, -click/_compat.py,sha256=IGKh_J5QdfKELitnRfTGHneejWxoCw_NX9tfMbdcg3w,18730 -click/_termui_impl.py,sha256=a5z7I9gOFeMmu7Gb6_RPyQ8GPuVP1EeblixcWSPSQPk,24783 -click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353 -click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 -click/core.py,sha256=Q1nEVdctZwvIPOlt4vfHko0TYnHCeE40UEEul8Wpyvs,114748 -click/decorators.py,sha256=7t6F-QWowtLh6F_6l-4YV4Y4yNTcqFQEu9i37zIz68s,18925 -click/exceptions.py,sha256=V7zDT6emqJ8iNl0kF1P5kpFmLMWQ1T1L7aNNKM4YR0w,9600 -click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706 -click/globals.py,sha256=cuJ6Bbo073lgEEmhjr394PeM-QFmXM-Ci-wmfsd7H5g,1954 -click/parser.py,sha256=h4sndcpF5OHrZQN8vD8IWb5OByvW7ABbhRToxovrqS8,19067 -click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -click/shell_completion.py,sha256=TR0dXEGcvWb9Eo3aaQEXGhnvNS3FF4H4QcuLnvAvYo4,18636 -click/termui.py,sha256=dLxiS70UOvIYBda_nEEZaPAFOVDVmRs1sEPMuLDowQo,28310 -click/testing.py,sha256=3RA8anCf7TZ8-5RAF5it2Te-aWXBAL5VLasQnMiC2ZQ,16282 -click/types.py,sha256=BD5Qqq4h-8kawBmOIzJlmq4xzThAf4wCvaOLZSBDNx0,36422 -click/utils.py,sha256=ce-IrO9ilII76LGkU354pOdHbepM8UftfNH7SfMU_28,20330 diff --git a/venv/Lib/site-packages/click-8.1.8.dist-info/WHEEL b/venv/Lib/site-packages/click-8.1.8.dist-info/WHEEL deleted file mode 100644 index e3c6fee..0000000 --- a/venv/Lib/site-packages/click-8.1.8.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: flit 3.10.1 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/venv/Lib/site-packages/click/__init__.py b/venv/Lib/site-packages/click/__init__.py deleted file mode 100644 index 2610d0e..0000000 --- a/venv/Lib/site-packages/click/__init__.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Click is a simple Python module inspired by the stdlib optparse to make -writing command line scripts fun. Unlike other modules, it's based -around a simple API that does not come with too much magic and is -composable. -""" - -from .core import Argument as Argument -from .core import BaseCommand as BaseCommand -from .core import Command as Command -from .core import CommandCollection as CommandCollection -from .core import Context as Context -from .core import Group as Group -from .core import MultiCommand as MultiCommand -from .core import Option as Option -from .core import Parameter as Parameter -from .decorators import argument as argument -from .decorators import command as command -from .decorators import confirmation_option as confirmation_option -from .decorators import group as group -from .decorators import help_option as help_option -from .decorators import HelpOption as HelpOption -from .decorators import make_pass_decorator as make_pass_decorator -from .decorators import option as option -from .decorators import pass_context as pass_context -from .decorators import pass_obj as pass_obj -from .decorators import password_option as password_option -from .decorators import version_option as version_option -from .exceptions import Abort as Abort -from .exceptions import BadArgumentUsage as BadArgumentUsage -from .exceptions import BadOptionUsage as BadOptionUsage -from .exceptions import BadParameter as BadParameter -from .exceptions import ClickException as ClickException -from .exceptions import FileError as FileError -from .exceptions import MissingParameter as MissingParameter -from .exceptions import NoSuchOption as NoSuchOption -from .exceptions import UsageError as UsageError -from .formatting import HelpFormatter as HelpFormatter -from .formatting import wrap_text as wrap_text -from .globals import get_current_context as get_current_context -from .parser import OptionParser as OptionParser -from .termui import clear as clear -from .termui import confirm as confirm -from .termui import echo_via_pager as echo_via_pager -from .termui import edit as edit -from .termui import getchar as getchar -from .termui import launch as launch -from .termui import pause as pause -from .termui import progressbar as progressbar -from .termui import prompt as prompt -from .termui import secho as secho -from .termui import style as style -from .termui import unstyle as unstyle -from .types import BOOL as BOOL -from .types import Choice as Choice -from .types import DateTime as DateTime -from .types import File as File -from .types import FLOAT as FLOAT -from .types import FloatRange as FloatRange -from .types import INT as INT -from .types import IntRange as IntRange -from .types import ParamType as ParamType -from .types import Path as Path -from .types import STRING as STRING -from .types import Tuple as Tuple -from .types import UNPROCESSED as UNPROCESSED -from .types import UUID as UUID -from .utils import echo as echo -from .utils import format_filename as format_filename -from .utils import get_app_dir as get_app_dir -from .utils import get_binary_stream as get_binary_stream -from .utils import get_text_stream as get_text_stream -from .utils import open_file as open_file - -__version__ = "8.1.8" diff --git a/venv/Lib/site-packages/click/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 69e2888..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/_compat.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/_compat.cpython-310.pyc deleted file mode 100644 index 3b7d0fe..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/_compat.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc deleted file mode 100644 index 1fb5577..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/_textwrap.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/_textwrap.cpython-310.pyc deleted file mode 100644 index 836fa30..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/_textwrap.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/_winconsole.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/_winconsole.cpython-310.pyc deleted file mode 100644 index 1a626f3..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/_winconsole.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/core.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/core.cpython-310.pyc deleted file mode 100644 index c5cd662..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/core.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/decorators.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/decorators.cpython-310.pyc deleted file mode 100644 index 6cde559..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/decorators.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/exceptions.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/exceptions.cpython-310.pyc deleted file mode 100644 index 08e2755..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/exceptions.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/formatting.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/formatting.cpython-310.pyc deleted file mode 100644 index 5defb42..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/formatting.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/globals.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/globals.cpython-310.pyc deleted file mode 100644 index 3f33943..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/globals.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/parser.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/parser.cpython-310.pyc deleted file mode 100644 index 8a0ba14..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/parser.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/shell_completion.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/shell_completion.cpython-310.pyc deleted file mode 100644 index 48507b0..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/shell_completion.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/termui.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/termui.cpython-310.pyc deleted file mode 100644 index b830492..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/termui.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/testing.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/testing.cpython-310.pyc deleted file mode 100644 index bb754d3..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/testing.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/types.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/types.cpython-310.pyc deleted file mode 100644 index a4a04ce..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/types.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/__pycache__/utils.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/utils.cpython-310.pyc deleted file mode 100644 index 5e8296f..0000000 Binary files a/venv/Lib/site-packages/click/__pycache__/utils.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/click/_compat.py b/venv/Lib/site-packages/click/_compat.py deleted file mode 100644 index 9153d15..0000000 --- a/venv/Lib/site-packages/click/_compat.py +++ /dev/null @@ -1,623 +0,0 @@ -import codecs -import io -import os -import re -import sys -import typing as t -from weakref import WeakKeyDictionary - -CYGWIN = sys.platform.startswith("cygwin") -WIN = sys.platform.startswith("win") -auto_wrap_for_ansi: t.Optional[t.Callable[[t.TextIO], t.TextIO]] = None -_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") - - -def _make_text_stream( - stream: t.BinaryIO, - encoding: t.Optional[str], - errors: t.Optional[str], - force_readable: bool = False, - force_writable: bool = False, -) -> t.TextIO: - if encoding is None: - encoding = get_best_encoding(stream) - if errors is None: - errors = "replace" - return _NonClosingTextIOWrapper( - stream, - encoding, - errors, - line_buffering=True, - force_readable=force_readable, - force_writable=force_writable, - ) - - -def is_ascii_encoding(encoding: str) -> bool: - """Checks if a given encoding is ascii.""" - try: - return codecs.lookup(encoding).name == "ascii" - except LookupError: - return False - - -def get_best_encoding(stream: t.IO[t.Any]) -> str: - """Returns the default stream encoding if not found.""" - rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() - if is_ascii_encoding(rv): - return "utf-8" - return rv - - -class _NonClosingTextIOWrapper(io.TextIOWrapper): - def __init__( - self, - stream: t.BinaryIO, - encoding: t.Optional[str], - errors: t.Optional[str], - force_readable: bool = False, - force_writable: bool = False, - **extra: t.Any, - ) -> None: - self._stream = stream = t.cast( - t.BinaryIO, _FixupStream(stream, force_readable, force_writable) - ) - super().__init__(stream, encoding, errors, **extra) - - def __del__(self) -> None: - try: - self.detach() - except Exception: - pass - - def isatty(self) -> bool: - # https://bitbucket.org/pypy/pypy/issue/1803 - return self._stream.isatty() - - -class _FixupStream: - """The new io interface needs more from streams than streams - traditionally implement. As such, this fix-up code is necessary in - some circumstances. - - The forcing of readable and writable flags are there because some tools - put badly patched objects on sys (one such offender are certain version - of jupyter notebook). - """ - - def __init__( - self, - stream: t.BinaryIO, - force_readable: bool = False, - force_writable: bool = False, - ): - self._stream = stream - self._force_readable = force_readable - self._force_writable = force_writable - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._stream, name) - - def read1(self, size: int) -> bytes: - f = getattr(self._stream, "read1", None) - - if f is not None: - return t.cast(bytes, f(size)) - - return self._stream.read(size) - - def readable(self) -> bool: - if self._force_readable: - return True - x = getattr(self._stream, "readable", None) - if x is not None: - return t.cast(bool, x()) - try: - self._stream.read(0) - except Exception: - return False - return True - - def writable(self) -> bool: - if self._force_writable: - return True - x = getattr(self._stream, "writable", None) - if x is not None: - return t.cast(bool, x()) - try: - self._stream.write("") # type: ignore - except Exception: - try: - self._stream.write(b"") - except Exception: - return False - return True - - def seekable(self) -> bool: - x = getattr(self._stream, "seekable", None) - if x is not None: - return t.cast(bool, x()) - try: - self._stream.seek(self._stream.tell()) - except Exception: - return False - return True - - -def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool: - try: - return isinstance(stream.read(0), bytes) - except Exception: - return default - # This happens in some cases where the stream was already - # closed. In this case, we assume the default. - - -def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool: - try: - stream.write(b"") - except Exception: - try: - stream.write("") - return False - except Exception: - pass - return default - return True - - -def _find_binary_reader(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: - # We need to figure out if the given stream is already binary. - # This can happen because the official docs recommend detaching - # the streams to get binary streams. Some code might do this, so - # we need to deal with this case explicitly. - if _is_binary_reader(stream, False): - return t.cast(t.BinaryIO, stream) - - buf = getattr(stream, "buffer", None) - - # Same situation here; this time we assume that the buffer is - # actually binary in case it's closed. - if buf is not None and _is_binary_reader(buf, True): - return t.cast(t.BinaryIO, buf) - - return None - - -def _find_binary_writer(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: - # We need to figure out if the given stream is already binary. - # This can happen because the official docs recommend detaching - # the streams to get binary streams. Some code might do this, so - # we need to deal with this case explicitly. - if _is_binary_writer(stream, False): - return t.cast(t.BinaryIO, stream) - - buf = getattr(stream, "buffer", None) - - # Same situation here; this time we assume that the buffer is - # actually binary in case it's closed. - if buf is not None and _is_binary_writer(buf, True): - return t.cast(t.BinaryIO, buf) - - return None - - -def _stream_is_misconfigured(stream: t.TextIO) -> bool: - """A stream is misconfigured if its encoding is ASCII.""" - # If the stream does not have an encoding set, we assume it's set - # to ASCII. This appears to happen in certain unittest - # environments. It's not quite clear what the correct behavior is - # but this at least will force Click to recover somehow. - return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") - - -def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: t.Optional[str]) -> bool: - """A stream attribute is compatible if it is equal to the - desired value or the desired value is unset and the attribute - has a value. - """ - stream_value = getattr(stream, attr, None) - return stream_value == value or (value is None and stream_value is not None) - - -def _is_compatible_text_stream( - stream: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] -) -> bool: - """Check if a stream's encoding and errors attributes are - compatible with the desired values. - """ - return _is_compat_stream_attr( - stream, "encoding", encoding - ) and _is_compat_stream_attr(stream, "errors", errors) - - -def _force_correct_text_stream( - text_stream: t.IO[t.Any], - encoding: t.Optional[str], - errors: t.Optional[str], - is_binary: t.Callable[[t.IO[t.Any], bool], bool], - find_binary: t.Callable[[t.IO[t.Any]], t.Optional[t.BinaryIO]], - force_readable: bool = False, - force_writable: bool = False, -) -> t.TextIO: - if is_binary(text_stream, False): - binary_reader = t.cast(t.BinaryIO, text_stream) - else: - text_stream = t.cast(t.TextIO, text_stream) - # If the stream looks compatible, and won't default to a - # misconfigured ascii encoding, return it as-is. - if _is_compatible_text_stream(text_stream, encoding, errors) and not ( - encoding is None and _stream_is_misconfigured(text_stream) - ): - return text_stream - - # Otherwise, get the underlying binary reader. - possible_binary_reader = find_binary(text_stream) - - # If that's not possible, silently use the original reader - # and get mojibake instead of exceptions. - if possible_binary_reader is None: - return text_stream - - binary_reader = possible_binary_reader - - # Default errors to replace instead of strict in order to get - # something that works. - if errors is None: - errors = "replace" - - # Wrap the binary stream in a text stream with the correct - # encoding parameters. - return _make_text_stream( - binary_reader, - encoding, - errors, - force_readable=force_readable, - force_writable=force_writable, - ) - - -def _force_correct_text_reader( - text_reader: t.IO[t.Any], - encoding: t.Optional[str], - errors: t.Optional[str], - force_readable: bool = False, -) -> t.TextIO: - return _force_correct_text_stream( - text_reader, - encoding, - errors, - _is_binary_reader, - _find_binary_reader, - force_readable=force_readable, - ) - - -def _force_correct_text_writer( - text_writer: t.IO[t.Any], - encoding: t.Optional[str], - errors: t.Optional[str], - force_writable: bool = False, -) -> t.TextIO: - return _force_correct_text_stream( - text_writer, - encoding, - errors, - _is_binary_writer, - _find_binary_writer, - force_writable=force_writable, - ) - - -def get_binary_stdin() -> t.BinaryIO: - reader = _find_binary_reader(sys.stdin) - if reader is None: - raise RuntimeError("Was not able to determine binary stream for sys.stdin.") - return reader - - -def get_binary_stdout() -> t.BinaryIO: - writer = _find_binary_writer(sys.stdout) - if writer is None: - raise RuntimeError("Was not able to determine binary stream for sys.stdout.") - return writer - - -def get_binary_stderr() -> t.BinaryIO: - writer = _find_binary_writer(sys.stderr) - if writer is None: - raise RuntimeError("Was not able to determine binary stream for sys.stderr.") - return writer - - -def get_text_stdin( - encoding: t.Optional[str] = None, errors: t.Optional[str] = None -) -> t.TextIO: - rv = _get_windows_console_stream(sys.stdin, encoding, errors) - if rv is not None: - return rv - return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) - - -def get_text_stdout( - encoding: t.Optional[str] = None, errors: t.Optional[str] = None -) -> t.TextIO: - rv = _get_windows_console_stream(sys.stdout, encoding, errors) - if rv is not None: - return rv - return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) - - -def get_text_stderr( - encoding: t.Optional[str] = None, errors: t.Optional[str] = None -) -> t.TextIO: - rv = _get_windows_console_stream(sys.stderr, encoding, errors) - if rv is not None: - return rv - return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) - - -def _wrap_io_open( - file: t.Union[str, "os.PathLike[str]", int], - mode: str, - encoding: t.Optional[str], - errors: t.Optional[str], -) -> t.IO[t.Any]: - """Handles not passing ``encoding`` and ``errors`` in binary mode.""" - if "b" in mode: - return open(file, mode) - - return open(file, mode, encoding=encoding, errors=errors) - - -def open_stream( - filename: "t.Union[str, os.PathLike[str]]", - mode: str = "r", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", - atomic: bool = False, -) -> t.Tuple[t.IO[t.Any], bool]: - binary = "b" in mode - filename = os.fspath(filename) - - # Standard streams first. These are simple because they ignore the - # atomic flag. Use fsdecode to handle Path("-"). - if os.fsdecode(filename) == "-": - if any(m in mode for m in ["w", "a", "x"]): - if binary: - return get_binary_stdout(), False - return get_text_stdout(encoding=encoding, errors=errors), False - if binary: - return get_binary_stdin(), False - return get_text_stdin(encoding=encoding, errors=errors), False - - # Non-atomic writes directly go out through the regular open functions. - if not atomic: - return _wrap_io_open(filename, mode, encoding, errors), True - - # Some usability stuff for atomic writes - if "a" in mode: - raise ValueError( - "Appending to an existing file is not supported, because that" - " would involve an expensive `copy`-operation to a temporary" - " file. Open the file in normal `w`-mode and copy explicitly" - " if that's what you're after." - ) - if "x" in mode: - raise ValueError("Use the `overwrite`-parameter instead.") - if "w" not in mode: - raise ValueError("Atomic writes only make sense with `w`-mode.") - - # Atomic writes are more complicated. They work by opening a file - # as a proxy in the same folder and then using the fdopen - # functionality to wrap it in a Python file. Then we wrap it in an - # atomic file that moves the file over on close. - import errno - import random - - try: - perm: t.Optional[int] = os.stat(filename).st_mode - except OSError: - perm = None - - flags = os.O_RDWR | os.O_CREAT | os.O_EXCL - - if binary: - flags |= getattr(os, "O_BINARY", 0) - - while True: - tmp_filename = os.path.join( - os.path.dirname(filename), - f".__atomic-write{random.randrange(1 << 32):08x}", - ) - try: - fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) - break - except OSError as e: - if e.errno == errno.EEXIST or ( - os.name == "nt" - and e.errno == errno.EACCES - and os.path.isdir(e.filename) - and os.access(e.filename, os.W_OK) - ): - continue - raise - - if perm is not None: - os.chmod(tmp_filename, perm) # in case perm includes bits in umask - - f = _wrap_io_open(fd, mode, encoding, errors) - af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) - return t.cast(t.IO[t.Any], af), True - - -class _AtomicFile: - def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None: - self._f = f - self._tmp_filename = tmp_filename - self._real_filename = real_filename - self.closed = False - - @property - def name(self) -> str: - return self._real_filename - - def close(self, delete: bool = False) -> None: - if self.closed: - return - self._f.close() - os.replace(self._tmp_filename, self._real_filename) - self.closed = True - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._f, name) - - def __enter__(self) -> "_AtomicFile": - return self - - def __exit__(self, exc_type: t.Optional[t.Type[BaseException]], *_: t.Any) -> None: - self.close(delete=exc_type is not None) - - def __repr__(self) -> str: - return repr(self._f) - - -def strip_ansi(value: str) -> str: - return _ansi_re.sub("", value) - - -def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool: - while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): - stream = stream._stream - - return stream.__class__.__module__.startswith("ipykernel.") - - -def should_strip_ansi( - stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None -) -> bool: - if color is None: - if stream is None: - stream = sys.stdin - return not isatty(stream) and not _is_jupyter_kernel_output(stream) - return not color - - -# On Windows, wrap the output streams with colorama to support ANSI -# color codes. -# NOTE: double check is needed so mypy does not analyze this on Linux -if sys.platform.startswith("win") and WIN: - from ._winconsole import _get_windows_console_stream - - def _get_argv_encoding() -> str: - import locale - - return locale.getpreferredencoding() - - _ansi_stream_wrappers: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() - - def auto_wrap_for_ansi( - stream: t.TextIO, color: t.Optional[bool] = None - ) -> t.TextIO: - """Support ANSI color and style codes on Windows by wrapping a - stream with colorama. - """ - try: - cached = _ansi_stream_wrappers.get(stream) - except Exception: - cached = None - - if cached is not None: - return cached - - import colorama - - strip = should_strip_ansi(stream, color) - ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) - rv = t.cast(t.TextIO, ansi_wrapper.stream) - _write = rv.write - - def _safe_write(s): - try: - return _write(s) - except BaseException: - ansi_wrapper.reset_all() - raise - - rv.write = _safe_write - - try: - _ansi_stream_wrappers[stream] = rv - except Exception: - pass - - return rv - -else: - - def _get_argv_encoding() -> str: - return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding() - - def _get_windows_console_stream( - f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] - ) -> t.Optional[t.TextIO]: - return None - - -def term_len(x: str) -> int: - return len(strip_ansi(x)) - - -def isatty(stream: t.IO[t.Any]) -> bool: - try: - return stream.isatty() - except Exception: - return False - - -def _make_cached_stream_func( - src_func: t.Callable[[], t.Optional[t.TextIO]], - wrapper_func: t.Callable[[], t.TextIO], -) -> t.Callable[[], t.Optional[t.TextIO]]: - cache: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() - - def func() -> t.Optional[t.TextIO]: - stream = src_func() - - if stream is None: - return None - - try: - rv = cache.get(stream) - except Exception: - rv = None - if rv is not None: - return rv - rv = wrapper_func() - try: - cache[stream] = rv - except Exception: - pass - return rv - - return func - - -_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) -_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) -_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) - - -binary_streams: t.Mapping[str, t.Callable[[], t.BinaryIO]] = { - "stdin": get_binary_stdin, - "stdout": get_binary_stdout, - "stderr": get_binary_stderr, -} - -text_streams: t.Mapping[ - str, t.Callable[[t.Optional[str], t.Optional[str]], t.TextIO] -] = { - "stdin": get_text_stdin, - "stdout": get_text_stdout, - "stderr": get_text_stderr, -} diff --git a/venv/Lib/site-packages/click/_termui_impl.py b/venv/Lib/site-packages/click/_termui_impl.py deleted file mode 100644 index ad9f8f6..0000000 --- a/venv/Lib/site-packages/click/_termui_impl.py +++ /dev/null @@ -1,788 +0,0 @@ -""" -This module contains implementations for the termui module. To keep the -import time of Click down, some infrequently used functionality is -placed in this module and only imported as needed. -""" - -import contextlib -import math -import os -import sys -import time -import typing as t -from gettext import gettext as _ -from io import StringIO -from shutil import which -from types import TracebackType - -from ._compat import _default_text_stdout -from ._compat import CYGWIN -from ._compat import get_best_encoding -from ._compat import isatty -from ._compat import open_stream -from ._compat import strip_ansi -from ._compat import term_len -from ._compat import WIN -from .exceptions import ClickException -from .utils import echo - -V = t.TypeVar("V") - -if os.name == "nt": - BEFORE_BAR = "\r" - AFTER_BAR = "\n" -else: - BEFORE_BAR = "\r\033[?25l" - AFTER_BAR = "\033[?25h\n" - - -class ProgressBar(t.Generic[V]): - def __init__( - self, - iterable: t.Optional[t.Iterable[V]], - length: t.Optional[int] = None, - fill_char: str = "#", - empty_char: str = " ", - bar_template: str = "%(bar)s", - info_sep: str = " ", - show_eta: bool = True, - show_percent: t.Optional[bool] = None, - show_pos: bool = False, - item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, - label: t.Optional[str] = None, - file: t.Optional[t.TextIO] = None, - color: t.Optional[bool] = None, - update_min_steps: int = 1, - width: int = 30, - ) -> None: - self.fill_char = fill_char - self.empty_char = empty_char - self.bar_template = bar_template - self.info_sep = info_sep - self.show_eta = show_eta - self.show_percent = show_percent - self.show_pos = show_pos - self.item_show_func = item_show_func - self.label: str = label or "" - - if file is None: - file = _default_text_stdout() - - # There are no standard streams attached to write to. For example, - # pythonw on Windows. - if file is None: - file = StringIO() - - self.file = file - self.color = color - self.update_min_steps = update_min_steps - self._completed_intervals = 0 - self.width: int = width - self.autowidth: bool = width == 0 - - if length is None: - from operator import length_hint - - length = length_hint(iterable, -1) - - if length == -1: - length = None - if iterable is None: - if length is None: - raise TypeError("iterable or length is required") - iterable = t.cast(t.Iterable[V], range(length)) - self.iter: t.Iterable[V] = iter(iterable) - self.length = length - self.pos = 0 - self.avg: t.List[float] = [] - self.last_eta: float - self.start: float - self.start = self.last_eta = time.time() - self.eta_known: bool = False - self.finished: bool = False - self.max_width: t.Optional[int] = None - self.entered: bool = False - self.current_item: t.Optional[V] = None - self.is_hidden: bool = not isatty(self.file) - self._last_line: t.Optional[str] = None - - def __enter__(self) -> "ProgressBar[V]": - self.entered = True - self.render_progress() - return self - - def __exit__( - self, - exc_type: t.Optional[t.Type[BaseException]], - exc_value: t.Optional[BaseException], - tb: t.Optional[TracebackType], - ) -> None: - self.render_finish() - - def __iter__(self) -> t.Iterator[V]: - if not self.entered: - raise RuntimeError("You need to use progress bars in a with block.") - self.render_progress() - return self.generator() - - def __next__(self) -> V: - # Iteration is defined in terms of a generator function, - # returned by iter(self); use that to define next(). This works - # because `self.iter` is an iterable consumed by that generator, - # so it is re-entry safe. Calling `next(self.generator())` - # twice works and does "what you want". - return next(iter(self)) - - def render_finish(self) -> None: - if self.is_hidden: - return - self.file.write(AFTER_BAR) - self.file.flush() - - @property - def pct(self) -> float: - if self.finished: - return 1.0 - return min(self.pos / (float(self.length or 1) or 1), 1.0) - - @property - def time_per_iteration(self) -> float: - if not self.avg: - return 0.0 - return sum(self.avg) / float(len(self.avg)) - - @property - def eta(self) -> float: - if self.length is not None and not self.finished: - return self.time_per_iteration * (self.length - self.pos) - return 0.0 - - def format_eta(self) -> str: - if self.eta_known: - t = int(self.eta) - seconds = t % 60 - t //= 60 - minutes = t % 60 - t //= 60 - hours = t % 24 - t //= 24 - if t > 0: - return f"{t}d {hours:02}:{minutes:02}:{seconds:02}" - else: - return f"{hours:02}:{minutes:02}:{seconds:02}" - return "" - - def format_pos(self) -> str: - pos = str(self.pos) - if self.length is not None: - pos += f"/{self.length}" - return pos - - def format_pct(self) -> str: - return f"{int(self.pct * 100): 4}%"[1:] - - def format_bar(self) -> str: - if self.length is not None: - bar_length = int(self.pct * self.width) - bar = self.fill_char * bar_length - bar += self.empty_char * (self.width - bar_length) - elif self.finished: - bar = self.fill_char * self.width - else: - chars = list(self.empty_char * (self.width or 1)) - if self.time_per_iteration != 0: - chars[ - int( - (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) - * self.width - ) - ] = self.fill_char - bar = "".join(chars) - return bar - - def format_progress_line(self) -> str: - show_percent = self.show_percent - - info_bits = [] - if self.length is not None and show_percent is None: - show_percent = not self.show_pos - - if self.show_pos: - info_bits.append(self.format_pos()) - if show_percent: - info_bits.append(self.format_pct()) - if self.show_eta and self.eta_known and not self.finished: - info_bits.append(self.format_eta()) - if self.item_show_func is not None: - item_info = self.item_show_func(self.current_item) - if item_info is not None: - info_bits.append(item_info) - - return ( - self.bar_template - % { - "label": self.label, - "bar": self.format_bar(), - "info": self.info_sep.join(info_bits), - } - ).rstrip() - - def render_progress(self) -> None: - import shutil - - if self.is_hidden: - # Only output the label as it changes if the output is not a - # TTY. Use file=stderr if you expect to be piping stdout. - if self._last_line != self.label: - self._last_line = self.label - echo(self.label, file=self.file, color=self.color) - - return - - buf = [] - # Update width in case the terminal has been resized - if self.autowidth: - old_width = self.width - self.width = 0 - clutter_length = term_len(self.format_progress_line()) - new_width = max(0, shutil.get_terminal_size().columns - clutter_length) - if new_width < old_width: - buf.append(BEFORE_BAR) - buf.append(" " * self.max_width) # type: ignore - self.max_width = new_width - self.width = new_width - - clear_width = self.width - if self.max_width is not None: - clear_width = self.max_width - - buf.append(BEFORE_BAR) - line = self.format_progress_line() - line_len = term_len(line) - if self.max_width is None or self.max_width < line_len: - self.max_width = line_len - - buf.append(line) - buf.append(" " * (clear_width - line_len)) - line = "".join(buf) - # Render the line only if it changed. - - if line != self._last_line: - self._last_line = line - echo(line, file=self.file, color=self.color, nl=False) - self.file.flush() - - def make_step(self, n_steps: int) -> None: - self.pos += n_steps - if self.length is not None and self.pos >= self.length: - self.finished = True - - if (time.time() - self.last_eta) < 1.0: - return - - self.last_eta = time.time() - - # self.avg is a rolling list of length <= 7 of steps where steps are - # defined as time elapsed divided by the total progress through - # self.length. - if self.pos: - step = (time.time() - self.start) / self.pos - else: - step = time.time() - self.start - - self.avg = self.avg[-6:] + [step] - - self.eta_known = self.length is not None - - def update(self, n_steps: int, current_item: t.Optional[V] = None) -> None: - """Update the progress bar by advancing a specified number of - steps, and optionally set the ``current_item`` for this new - position. - - :param n_steps: Number of steps to advance. - :param current_item: Optional item to set as ``current_item`` - for the updated position. - - .. versionchanged:: 8.0 - Added the ``current_item`` optional parameter. - - .. versionchanged:: 8.0 - Only render when the number of steps meets the - ``update_min_steps`` threshold. - """ - if current_item is not None: - self.current_item = current_item - - self._completed_intervals += n_steps - - if self._completed_intervals >= self.update_min_steps: - self.make_step(self._completed_intervals) - self.render_progress() - self._completed_intervals = 0 - - def finish(self) -> None: - self.eta_known = False - self.current_item = None - self.finished = True - - def generator(self) -> t.Iterator[V]: - """Return a generator which yields the items added to the bar - during construction, and updates the progress bar *after* the - yielded block returns. - """ - # WARNING: the iterator interface for `ProgressBar` relies on - # this and only works because this is a simple generator which - # doesn't create or manage additional state. If this function - # changes, the impact should be evaluated both against - # `iter(bar)` and `next(bar)`. `next()` in particular may call - # `self.generator()` repeatedly, and this must remain safe in - # order for that interface to work. - if not self.entered: - raise RuntimeError("You need to use progress bars in a with block.") - - if self.is_hidden: - yield from self.iter - else: - for rv in self.iter: - self.current_item = rv - - # This allows show_item_func to be updated before the - # item is processed. Only trigger at the beginning of - # the update interval. - if self._completed_intervals == 0: - self.render_progress() - - yield rv - self.update(1) - - self.finish() - self.render_progress() - - -def pager(generator: t.Iterable[str], color: t.Optional[bool] = None) -> None: - """Decide what method to use for paging through text.""" - stdout = _default_text_stdout() - - # There are no standard streams attached to write to. For example, - # pythonw on Windows. - if stdout is None: - stdout = StringIO() - - if not isatty(sys.stdin) or not isatty(stdout): - return _nullpager(stdout, generator, color) - pager_cmd = (os.environ.get("PAGER", None) or "").strip() - if pager_cmd: - if WIN: - if _tempfilepager(generator, pager_cmd, color): - return - elif _pipepager(generator, pager_cmd, color): - return - if os.environ.get("TERM") in ("dumb", "emacs"): - return _nullpager(stdout, generator, color) - if (WIN or sys.platform.startswith("os2")) and _tempfilepager( - generator, "more", color - ): - return - if _pipepager(generator, "less", color): - return - - import tempfile - - fd, filename = tempfile.mkstemp() - os.close(fd) - try: - if _pipepager(generator, "more", color): - return - return _nullpager(stdout, generator, color) - finally: - os.unlink(filename) - - -def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> bool: - """Page through text by feeding it to another program. Invoking a - pager through this might support colors. - - Returns True if the command was found, False otherwise and thus another - pager should be attempted. - """ - cmd_absolute = which(cmd) - if cmd_absolute is None: - return False - - import subprocess - - env = dict(os.environ) - - # If we're piping to less we might support colors under the - # condition that - cmd_detail = cmd.rsplit("/", 1)[-1].split() - if color is None and cmd_detail[0] == "less": - less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_detail[1:])}" - if not less_flags: - env["LESS"] = "-R" - color = True - elif "r" in less_flags or "R" in less_flags: - color = True - - c = subprocess.Popen( - [cmd_absolute], - shell=True, - stdin=subprocess.PIPE, - env=env, - errors="replace", - text=True, - ) - assert c.stdin is not None - try: - for text in generator: - if not color: - text = strip_ansi(text) - - c.stdin.write(text) - except (OSError, KeyboardInterrupt): - pass - else: - c.stdin.close() - - # Less doesn't respect ^C, but catches it for its own UI purposes (aborting - # search or other commands inside less). - # - # That means when the user hits ^C, the parent process (click) terminates, - # but less is still alive, paging the output and messing up the terminal. - # - # If the user wants to make the pager exit on ^C, they should set - # `LESS='-K'`. It's not our decision to make. - while True: - try: - c.wait() - except KeyboardInterrupt: - pass - else: - break - - return True - - -def _tempfilepager( - generator: t.Iterable[str], - cmd: str, - color: t.Optional[bool], -) -> bool: - """Page through text by invoking a program on a temporary file. - - Returns True if the command was found, False otherwise and thus another - pager should be attempted. - """ - # Which is necessary for Windows, it is also recommended in the Popen docs. - cmd_absolute = which(cmd) - if cmd_absolute is None: - return False - - import subprocess - import tempfile - - fd, filename = tempfile.mkstemp() - # TODO: This never terminates if the passed generator never terminates. - text = "".join(generator) - if not color: - text = strip_ansi(text) - encoding = get_best_encoding(sys.stdout) - with open_stream(filename, "wb")[0] as f: - f.write(text.encode(encoding)) - try: - subprocess.call([cmd_absolute, filename]) - except OSError: - # Command not found - pass - finally: - os.close(fd) - os.unlink(filename) - - return True - - -def _nullpager( - stream: t.TextIO, generator: t.Iterable[str], color: t.Optional[bool] -) -> None: - """Simply print unformatted text. This is the ultimate fallback.""" - for text in generator: - if not color: - text = strip_ansi(text) - stream.write(text) - - -class Editor: - def __init__( - self, - editor: t.Optional[str] = None, - env: t.Optional[t.Mapping[str, str]] = None, - require_save: bool = True, - extension: str = ".txt", - ) -> None: - self.editor = editor - self.env = env - self.require_save = require_save - self.extension = extension - - def get_editor(self) -> str: - if self.editor is not None: - return self.editor - for key in "VISUAL", "EDITOR": - rv = os.environ.get(key) - if rv: - return rv - if WIN: - return "notepad" - for editor in "sensible-editor", "vim", "nano": - if which(editor) is not None: - return editor - return "vi" - - def edit_file(self, filename: str) -> None: - import subprocess - - editor = self.get_editor() - environ: t.Optional[t.Dict[str, str]] = None - - if self.env: - environ = os.environ.copy() - environ.update(self.env) - - try: - c = subprocess.Popen(f'{editor} "{filename}"', env=environ, shell=True) - exit_code = c.wait() - if exit_code != 0: - raise ClickException( - _("{editor}: Editing failed").format(editor=editor) - ) - except OSError as e: - raise ClickException( - _("{editor}: Editing failed: {e}").format(editor=editor, e=e) - ) from e - - def edit(self, text: t.Optional[t.AnyStr]) -> t.Optional[t.AnyStr]: - import tempfile - - if not text: - data = b"" - elif isinstance(text, (bytes, bytearray)): - data = text - else: - if text and not text.endswith("\n"): - text += "\n" - - if WIN: - data = text.replace("\n", "\r\n").encode("utf-8-sig") - else: - data = text.encode("utf-8") - - fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) - f: t.BinaryIO - - try: - with os.fdopen(fd, "wb") as f: - f.write(data) - - # If the filesystem resolution is 1 second, like Mac OS - # 10.12 Extended, or 2 seconds, like FAT32, and the editor - # closes very fast, require_save can fail. Set the modified - # time to be 2 seconds in the past to work around this. - os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) - # Depending on the resolution, the exact value might not be - # recorded, so get the new recorded value. - timestamp = os.path.getmtime(name) - - self.edit_file(name) - - if self.require_save and os.path.getmtime(name) == timestamp: - return None - - with open(name, "rb") as f: - rv = f.read() - - if isinstance(text, (bytes, bytearray)): - return rv - - return rv.decode("utf-8-sig").replace("\r\n", "\n") # type: ignore - finally: - os.unlink(name) - - -def open_url(url: str, wait: bool = False, locate: bool = False) -> int: - import subprocess - - def _unquote_file(url: str) -> str: - from urllib.parse import unquote - - if url.startswith("file://"): - url = unquote(url[7:]) - - return url - - if sys.platform == "darwin": - args = ["open"] - if wait: - args.append("-W") - if locate: - args.append("-R") - args.append(_unquote_file(url)) - null = open("/dev/null", "w") - try: - return subprocess.Popen(args, stderr=null).wait() - finally: - null.close() - elif WIN: - if locate: - url = _unquote_file(url) - args = ["explorer", f"/select,{url}"] - else: - args = ["start"] - if wait: - args.append("/WAIT") - args.append("") - args.append(url) - try: - return subprocess.call(args) - except OSError: - # Command not found - return 127 - elif CYGWIN: - if locate: - url = _unquote_file(url) - args = ["cygstart", os.path.dirname(url)] - else: - args = ["cygstart"] - if wait: - args.append("-w") - args.append(url) - try: - return subprocess.call(args) - except OSError: - # Command not found - return 127 - - try: - if locate: - url = os.path.dirname(_unquote_file(url)) or "." - else: - url = _unquote_file(url) - c = subprocess.Popen(["xdg-open", url]) - if wait: - return c.wait() - return 0 - except OSError: - if url.startswith(("http://", "https://")) and not locate and not wait: - import webbrowser - - webbrowser.open(url) - return 0 - return 1 - - -def _translate_ch_to_exc(ch: str) -> t.Optional[BaseException]: - if ch == "\x03": - raise KeyboardInterrupt() - - if ch == "\x04" and not WIN: # Unix-like, Ctrl+D - raise EOFError() - - if ch == "\x1a" and WIN: # Windows, Ctrl+Z - raise EOFError() - - return None - - -if WIN: - import msvcrt - - @contextlib.contextmanager - def raw_terminal() -> t.Iterator[int]: - yield -1 - - def getchar(echo: bool) -> str: - # The function `getch` will return a bytes object corresponding to - # the pressed character. Since Windows 10 build 1803, it will also - # return \x00 when called a second time after pressing a regular key. - # - # `getwch` does not share this probably-bugged behavior. Moreover, it - # returns a Unicode object by default, which is what we want. - # - # Either of these functions will return \x00 or \xe0 to indicate - # a special key, and you need to call the same function again to get - # the "rest" of the code. The fun part is that \u00e0 is - # "latin small letter a with grave", so if you type that on a French - # keyboard, you _also_ get a \xe0. - # E.g., consider the Up arrow. This returns \xe0 and then \x48. The - # resulting Unicode string reads as "a with grave" + "capital H". - # This is indistinguishable from when the user actually types - # "a with grave" and then "capital H". - # - # When \xe0 is returned, we assume it's part of a special-key sequence - # and call `getwch` again, but that means that when the user types - # the \u00e0 character, `getchar` doesn't return until a second - # character is typed. - # The alternative is returning immediately, but that would mess up - # cross-platform handling of arrow keys and others that start with - # \xe0. Another option is using `getch`, but then we can't reliably - # read non-ASCII characters, because return values of `getch` are - # limited to the current 8-bit codepage. - # - # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` - # is doing the right thing in more situations than with `getch`. - func: t.Callable[[], str] - - if echo: - func = msvcrt.getwche # type: ignore - else: - func = msvcrt.getwch # type: ignore - - rv = func() - - if rv in ("\x00", "\xe0"): - # \x00 and \xe0 are control characters that indicate special key, - # see above. - rv += func() - - _translate_ch_to_exc(rv) - return rv - -else: - import termios - import tty - - @contextlib.contextmanager - def raw_terminal() -> t.Iterator[int]: - f: t.Optional[t.TextIO] - fd: int - - if not isatty(sys.stdin): - f = open("/dev/tty") - fd = f.fileno() - else: - fd = sys.stdin.fileno() - f = None - - try: - old_settings = termios.tcgetattr(fd) - - try: - tty.setraw(fd) - yield fd - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - sys.stdout.flush() - - if f is not None: - f.close() - except termios.error: - pass - - def getchar(echo: bool) -> str: - with raw_terminal() as fd: - ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") - - if echo and isatty(sys.stdout): - sys.stdout.write(ch) - - _translate_ch_to_exc(ch) - return ch diff --git a/venv/Lib/site-packages/click/_textwrap.py b/venv/Lib/site-packages/click/_textwrap.py deleted file mode 100644 index b47dcbd..0000000 --- a/venv/Lib/site-packages/click/_textwrap.py +++ /dev/null @@ -1,49 +0,0 @@ -import textwrap -import typing as t -from contextlib import contextmanager - - -class TextWrapper(textwrap.TextWrapper): - def _handle_long_word( - self, - reversed_chunks: t.List[str], - cur_line: t.List[str], - cur_len: int, - width: int, - ) -> None: - space_left = max(width - cur_len, 1) - - if self.break_long_words: - last = reversed_chunks[-1] - cut = last[:space_left] - res = last[space_left:] - cur_line.append(cut) - reversed_chunks[-1] = res - elif not cur_line: - cur_line.append(reversed_chunks.pop()) - - @contextmanager - def extra_indent(self, indent: str) -> t.Iterator[None]: - old_initial_indent = self.initial_indent - old_subsequent_indent = self.subsequent_indent - self.initial_indent += indent - self.subsequent_indent += indent - - try: - yield - finally: - self.initial_indent = old_initial_indent - self.subsequent_indent = old_subsequent_indent - - def indent_only(self, text: str) -> str: - rv = [] - - for idx, line in enumerate(text.splitlines()): - indent = self.initial_indent - - if idx > 0: - indent = self.subsequent_indent - - rv.append(f"{indent}{line}") - - return "\n".join(rv) diff --git a/venv/Lib/site-packages/click/_winconsole.py b/venv/Lib/site-packages/click/_winconsole.py deleted file mode 100644 index 6b20df3..0000000 --- a/venv/Lib/site-packages/click/_winconsole.py +++ /dev/null @@ -1,279 +0,0 @@ -# This module is based on the excellent work by Adam Bartoš who -# provided a lot of what went into the implementation here in -# the discussion to issue1602 in the Python bug tracker. -# -# There are some general differences in regards to how this works -# compared to the original patches as we do not need to patch -# the entire interpreter but just work in our little world of -# echo and prompt. -import io -import sys -import time -import typing as t -from ctypes import byref -from ctypes import c_char -from ctypes import c_char_p -from ctypes import c_int -from ctypes import c_ssize_t -from ctypes import c_ulong -from ctypes import c_void_p -from ctypes import POINTER -from ctypes import py_object -from ctypes import Structure -from ctypes.wintypes import DWORD -from ctypes.wintypes import HANDLE -from ctypes.wintypes import LPCWSTR -from ctypes.wintypes import LPWSTR - -from ._compat import _NonClosingTextIOWrapper - -assert sys.platform == "win32" -import msvcrt # noqa: E402 -from ctypes import windll # noqa: E402 -from ctypes import WINFUNCTYPE # noqa: E402 - -c_ssize_p = POINTER(c_ssize_t) - -kernel32 = windll.kernel32 -GetStdHandle = kernel32.GetStdHandle -ReadConsoleW = kernel32.ReadConsoleW -WriteConsoleW = kernel32.WriteConsoleW -GetConsoleMode = kernel32.GetConsoleMode -GetLastError = kernel32.GetLastError -GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) -CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( - ("CommandLineToArgvW", windll.shell32) -) -LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) - -STDIN_HANDLE = GetStdHandle(-10) -STDOUT_HANDLE = GetStdHandle(-11) -STDERR_HANDLE = GetStdHandle(-12) - -PyBUF_SIMPLE = 0 -PyBUF_WRITABLE = 1 - -ERROR_SUCCESS = 0 -ERROR_NOT_ENOUGH_MEMORY = 8 -ERROR_OPERATION_ABORTED = 995 - -STDIN_FILENO = 0 -STDOUT_FILENO = 1 -STDERR_FILENO = 2 - -EOF = b"\x1a" -MAX_BYTES_WRITTEN = 32767 - -try: - from ctypes import pythonapi -except ImportError: - # On PyPy we cannot get buffers so our ability to operate here is - # severely limited. - get_buffer = None -else: - - class Py_buffer(Structure): - _fields_ = [ - ("buf", c_void_p), - ("obj", py_object), - ("len", c_ssize_t), - ("itemsize", c_ssize_t), - ("readonly", c_int), - ("ndim", c_int), - ("format", c_char_p), - ("shape", c_ssize_p), - ("strides", c_ssize_p), - ("suboffsets", c_ssize_p), - ("internal", c_void_p), - ] - - PyObject_GetBuffer = pythonapi.PyObject_GetBuffer - PyBuffer_Release = pythonapi.PyBuffer_Release - - def get_buffer(obj, writable=False): - buf = Py_buffer() - flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE - PyObject_GetBuffer(py_object(obj), byref(buf), flags) - - try: - buffer_type = c_char * buf.len - return buffer_type.from_address(buf.buf) - finally: - PyBuffer_Release(byref(buf)) - - -class _WindowsConsoleRawIOBase(io.RawIOBase): - def __init__(self, handle): - self.handle = handle - - def isatty(self): - super().isatty() - return True - - -class _WindowsConsoleReader(_WindowsConsoleRawIOBase): - def readable(self): - return True - - def readinto(self, b): - bytes_to_be_read = len(b) - if not bytes_to_be_read: - return 0 - elif bytes_to_be_read % 2: - raise ValueError( - "cannot read odd number of bytes from UTF-16-LE encoded console" - ) - - buffer = get_buffer(b, writable=True) - code_units_to_be_read = bytes_to_be_read // 2 - code_units_read = c_ulong() - - rv = ReadConsoleW( - HANDLE(self.handle), - buffer, - code_units_to_be_read, - byref(code_units_read), - None, - ) - if GetLastError() == ERROR_OPERATION_ABORTED: - # wait for KeyboardInterrupt - time.sleep(0.1) - if not rv: - raise OSError(f"Windows error: {GetLastError()}") - - if buffer[0] == EOF: - return 0 - return 2 * code_units_read.value - - -class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): - def writable(self): - return True - - @staticmethod - def _get_error_message(errno): - if errno == ERROR_SUCCESS: - return "ERROR_SUCCESS" - elif errno == ERROR_NOT_ENOUGH_MEMORY: - return "ERROR_NOT_ENOUGH_MEMORY" - return f"Windows error {errno}" - - def write(self, b): - bytes_to_be_written = len(b) - buf = get_buffer(b) - code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 - code_units_written = c_ulong() - - WriteConsoleW( - HANDLE(self.handle), - buf, - code_units_to_be_written, - byref(code_units_written), - None, - ) - bytes_written = 2 * code_units_written.value - - if bytes_written == 0 and bytes_to_be_written > 0: - raise OSError(self._get_error_message(GetLastError())) - return bytes_written - - -class ConsoleStream: - def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: - self._text_stream = text_stream - self.buffer = byte_stream - - @property - def name(self) -> str: - return self.buffer.name - - def write(self, x: t.AnyStr) -> int: - if isinstance(x, str): - return self._text_stream.write(x) - try: - self.flush() - except Exception: - pass - return self.buffer.write(x) - - def writelines(self, lines: t.Iterable[t.AnyStr]) -> None: - for line in lines: - self.write(line) - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._text_stream, name) - - def isatty(self) -> bool: - return self.buffer.isatty() - - def __repr__(self): - return f"" - - -def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: - text_stream = _NonClosingTextIOWrapper( - io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), - "utf-16-le", - "strict", - line_buffering=True, - ) - return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) - - -def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: - text_stream = _NonClosingTextIOWrapper( - io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), - "utf-16-le", - "strict", - line_buffering=True, - ) - return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) - - -def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: - text_stream = _NonClosingTextIOWrapper( - io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), - "utf-16-le", - "strict", - line_buffering=True, - ) - return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) - - -_stream_factories: t.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { - 0: _get_text_stdin, - 1: _get_text_stdout, - 2: _get_text_stderr, -} - - -def _is_console(f: t.TextIO) -> bool: - if not hasattr(f, "fileno"): - return False - - try: - fileno = f.fileno() - except (OSError, io.UnsupportedOperation): - return False - - handle = msvcrt.get_osfhandle(fileno) - return bool(GetConsoleMode(handle, byref(DWORD()))) - - -def _get_windows_console_stream( - f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] -) -> t.Optional[t.TextIO]: - if ( - get_buffer is not None - and encoding in {"utf-16-le", None} - and errors in {"strict", None} - and _is_console(f) - ): - func = _stream_factories.get(f.fileno()) - if func is not None: - b = getattr(f, "buffer", None) - - if b is None: - return None - - return func(b) diff --git a/venv/Lib/site-packages/click/core.py b/venv/Lib/site-packages/click/core.py deleted file mode 100644 index e630501..0000000 --- a/venv/Lib/site-packages/click/core.py +++ /dev/null @@ -1,3047 +0,0 @@ -import enum -import errno -import inspect -import os -import sys -import typing as t -from collections import abc -from contextlib import contextmanager -from contextlib import ExitStack -from functools import update_wrapper -from gettext import gettext as _ -from gettext import ngettext -from itertools import repeat -from types import TracebackType - -from . import types -from .exceptions import Abort -from .exceptions import BadParameter -from .exceptions import ClickException -from .exceptions import Exit -from .exceptions import MissingParameter -from .exceptions import UsageError -from .formatting import HelpFormatter -from .formatting import join_options -from .globals import pop_context -from .globals import push_context -from .parser import _flag_needs_value -from .parser import OptionParser -from .parser import split_opt -from .termui import confirm -from .termui import prompt -from .termui import style -from .utils import _detect_program_name -from .utils import _expand_args -from .utils import echo -from .utils import make_default_short_help -from .utils import make_str -from .utils import PacifyFlushWrapper - -if t.TYPE_CHECKING: - import typing_extensions as te - - from .decorators import HelpOption - from .shell_completion import CompletionItem - -F = t.TypeVar("F", bound=t.Callable[..., t.Any]) -V = t.TypeVar("V") - - -def _complete_visible_commands( - ctx: "Context", incomplete: str -) -> t.Iterator[t.Tuple[str, "Command"]]: - """List all the subcommands of a group that start with the - incomplete value and aren't hidden. - - :param ctx: Invocation context for the group. - :param incomplete: Value being completed. May be empty. - """ - multi = t.cast(MultiCommand, ctx.command) - - for name in multi.list_commands(ctx): - if name.startswith(incomplete): - command = multi.get_command(ctx, name) - - if command is not None and not command.hidden: - yield name, command - - -def _check_multicommand( - base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False -) -> None: - if not base_command.chain or not isinstance(cmd, MultiCommand): - return - if register: - hint = ( - "It is not possible to add multi commands as children to" - " another multi command that is in chain mode." - ) - else: - hint = ( - "Found a multi command as subcommand to a multi command" - " that is in chain mode. This is not supported." - ) - raise RuntimeError( - f"{hint}. Command {base_command.name!r} is set to chain and" - f" {cmd_name!r} was added as a subcommand but it in itself is a" - f" multi command. ({cmd_name!r} is a {type(cmd).__name__}" - f" within a chained {type(base_command).__name__} named" - f" {base_command.name!r})." - ) - - -def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]: - return list(zip(*repeat(iter(iterable), batch_size))) - - -@contextmanager -def augment_usage_errors( - ctx: "Context", param: t.Optional["Parameter"] = None -) -> t.Iterator[None]: - """Context manager that attaches extra information to exceptions.""" - try: - yield - except BadParameter as e: - if e.ctx is None: - e.ctx = ctx - if param is not None and e.param is None: - e.param = param - raise - except UsageError as e: - if e.ctx is None: - e.ctx = ctx - raise - - -def iter_params_for_processing( - invocation_order: t.Sequence["Parameter"], - declaration_order: t.Sequence["Parameter"], -) -> t.List["Parameter"]: - """Returns all declared parameters in the order they should be processed. - - The declared parameters are re-shuffled depending on the order in which - they were invoked, as well as the eagerness of each parameters. - - The invocation order takes precedence over the declaration order. I.e. the - order in which the user provided them to the CLI is respected. - - This behavior and its effect on callback evaluation is detailed at: - https://click.palletsprojects.com/en/stable/advanced/#callback-evaluation-order - """ - - def sort_key(item: "Parameter") -> t.Tuple[bool, float]: - try: - idx: float = invocation_order.index(item) - except ValueError: - idx = float("inf") - - return not item.is_eager, idx - - return sorted(declaration_order, key=sort_key) - - -class ParameterSource(enum.Enum): - """This is an :class:`~enum.Enum` that indicates the source of a - parameter's value. - - Use :meth:`click.Context.get_parameter_source` to get the - source for a parameter by name. - - .. versionchanged:: 8.0 - Use :class:`~enum.Enum` and drop the ``validate`` method. - - .. versionchanged:: 8.0 - Added the ``PROMPT`` value. - """ - - COMMANDLINE = enum.auto() - """The value was provided by the command line args.""" - ENVIRONMENT = enum.auto() - """The value was provided with an environment variable.""" - DEFAULT = enum.auto() - """Used the default specified by the parameter.""" - DEFAULT_MAP = enum.auto() - """Used a default provided by :attr:`Context.default_map`.""" - PROMPT = enum.auto() - """Used a prompt to confirm a default or provide a value.""" - - -class Context: - """The context is a special internal object that holds state relevant - for the script execution at every single level. It's normally invisible - to commands unless they opt-in to getting access to it. - - The context is useful as it can pass internal objects around and can - control special execution features such as reading data from - environment variables. - - A context can be used as context manager in which case it will call - :meth:`close` on teardown. - - :param command: the command class for this context. - :param parent: the parent context. - :param info_name: the info name for this invocation. Generally this - is the most descriptive name for the script or - command. For the toplevel script it is usually - the name of the script, for commands below it it's - the name of the script. - :param obj: an arbitrary object of user data. - :param auto_envvar_prefix: the prefix to use for automatic environment - variables. If this is `None` then reading - from environment variables is disabled. This - does not affect manually set environment - variables which are always read. - :param default_map: a dictionary (like object) with default values - for parameters. - :param terminal_width: the width of the terminal. The default is - inherit from parent context. If no context - defines the terminal width then auto - detection will be applied. - :param max_content_width: the maximum width for content rendered by - Click (this currently only affects help - pages). This defaults to 80 characters if - not overridden. In other words: even if the - terminal is larger than that, Click will not - format things wider than 80 characters by - default. In addition to that, formatters might - add some safety mapping on the right. - :param resilient_parsing: if this flag is enabled then Click will - parse without any interactivity or callback - invocation. Default values will also be - ignored. This is useful for implementing - things such as completion support. - :param allow_extra_args: if this is set to `True` then extra arguments - at the end will not raise an error and will be - kept on the context. The default is to inherit - from the command. - :param allow_interspersed_args: if this is set to `False` then options - and arguments cannot be mixed. The - default is to inherit from the command. - :param ignore_unknown_options: instructs click to ignore options it does - not know and keeps them for later - processing. - :param help_option_names: optionally a list of strings that define how - the default help parameter is named. The - default is ``['--help']``. - :param token_normalize_func: an optional function that is used to - normalize tokens (options, choices, - etc.). This for instance can be used to - implement case insensitive behavior. - :param color: controls if the terminal supports ANSI colors or not. The - default is autodetection. This is only needed if ANSI - codes are used in texts that Click prints which is by - default not the case. This for instance would affect - help output. - :param show_default: Show the default value for commands. If this - value is not set, it defaults to the value from the parent - context. ``Command.show_default`` overrides this default for the - specific command. - - .. versionchanged:: 8.1 - The ``show_default`` parameter is overridden by - ``Command.show_default``, instead of the other way around. - - .. versionchanged:: 8.0 - The ``show_default`` parameter defaults to the value from the - parent context. - - .. versionchanged:: 7.1 - Added the ``show_default`` parameter. - - .. versionchanged:: 4.0 - Added the ``color``, ``ignore_unknown_options``, and - ``max_content_width`` parameters. - - .. versionchanged:: 3.0 - Added the ``allow_extra_args`` and ``allow_interspersed_args`` - parameters. - - .. versionchanged:: 2.0 - Added the ``resilient_parsing``, ``help_option_names``, and - ``token_normalize_func`` parameters. - """ - - #: The formatter class to create with :meth:`make_formatter`. - #: - #: .. versionadded:: 8.0 - formatter_class: t.Type["HelpFormatter"] = HelpFormatter - - def __init__( - self, - command: "Command", - parent: t.Optional["Context"] = None, - info_name: t.Optional[str] = None, - obj: t.Optional[t.Any] = None, - auto_envvar_prefix: t.Optional[str] = None, - default_map: t.Optional[t.MutableMapping[str, t.Any]] = None, - terminal_width: t.Optional[int] = None, - max_content_width: t.Optional[int] = None, - resilient_parsing: bool = False, - allow_extra_args: t.Optional[bool] = None, - allow_interspersed_args: t.Optional[bool] = None, - ignore_unknown_options: t.Optional[bool] = None, - help_option_names: t.Optional[t.List[str]] = None, - token_normalize_func: t.Optional[t.Callable[[str], str]] = None, - color: t.Optional[bool] = None, - show_default: t.Optional[bool] = None, - ) -> None: - #: the parent context or `None` if none exists. - self.parent = parent - #: the :class:`Command` for this context. - self.command = command - #: the descriptive information name - self.info_name = info_name - #: Map of parameter names to their parsed values. Parameters - #: with ``expose_value=False`` are not stored. - self.params: t.Dict[str, t.Any] = {} - #: the leftover arguments. - self.args: t.List[str] = [] - #: protected arguments. These are arguments that are prepended - #: to `args` when certain parsing scenarios are encountered but - #: must be never propagated to another arguments. This is used - #: to implement nested parsing. - self.protected_args: t.List[str] = [] - #: the collected prefixes of the command's options. - self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set() - - if obj is None and parent is not None: - obj = parent.obj - - #: the user object stored. - self.obj: t.Any = obj - self._meta: t.Dict[str, t.Any] = getattr(parent, "meta", {}) - - #: A dictionary (-like object) with defaults for parameters. - if ( - default_map is None - and info_name is not None - and parent is not None - and parent.default_map is not None - ): - default_map = parent.default_map.get(info_name) - - self.default_map: t.Optional[t.MutableMapping[str, t.Any]] = default_map - - #: This flag indicates if a subcommand is going to be executed. A - #: group callback can use this information to figure out if it's - #: being executed directly or because the execution flow passes - #: onwards to a subcommand. By default it's None, but it can be - #: the name of the subcommand to execute. - #: - #: If chaining is enabled this will be set to ``'*'`` in case - #: any commands are executed. It is however not possible to - #: figure out which ones. If you require this knowledge you - #: should use a :func:`result_callback`. - self.invoked_subcommand: t.Optional[str] = None - - if terminal_width is None and parent is not None: - terminal_width = parent.terminal_width - - #: The width of the terminal (None is autodetection). - self.terminal_width: t.Optional[int] = terminal_width - - if max_content_width is None and parent is not None: - max_content_width = parent.max_content_width - - #: The maximum width of formatted content (None implies a sensible - #: default which is 80 for most things). - self.max_content_width: t.Optional[int] = max_content_width - - if allow_extra_args is None: - allow_extra_args = command.allow_extra_args - - #: Indicates if the context allows extra args or if it should - #: fail on parsing. - #: - #: .. versionadded:: 3.0 - self.allow_extra_args = allow_extra_args - - if allow_interspersed_args is None: - allow_interspersed_args = command.allow_interspersed_args - - #: Indicates if the context allows mixing of arguments and - #: options or not. - #: - #: .. versionadded:: 3.0 - self.allow_interspersed_args: bool = allow_interspersed_args - - if ignore_unknown_options is None: - ignore_unknown_options = command.ignore_unknown_options - - #: Instructs click to ignore options that a command does not - #: understand and will store it on the context for later - #: processing. This is primarily useful for situations where you - #: want to call into external programs. Generally this pattern is - #: strongly discouraged because it's not possibly to losslessly - #: forward all arguments. - #: - #: .. versionadded:: 4.0 - self.ignore_unknown_options: bool = ignore_unknown_options - - if help_option_names is None: - if parent is not None: - help_option_names = parent.help_option_names - else: - help_option_names = ["--help"] - - #: The names for the help options. - self.help_option_names: t.List[str] = help_option_names - - if token_normalize_func is None and parent is not None: - token_normalize_func = parent.token_normalize_func - - #: An optional normalization function for tokens. This is - #: options, choices, commands etc. - self.token_normalize_func: t.Optional[t.Callable[[str], str]] = ( - token_normalize_func - ) - - #: Indicates if resilient parsing is enabled. In that case Click - #: will do its best to not cause any failures and default values - #: will be ignored. Useful for completion. - self.resilient_parsing: bool = resilient_parsing - - # If there is no envvar prefix yet, but the parent has one and - # the command on this level has a name, we can expand the envvar - # prefix automatically. - if auto_envvar_prefix is None: - if ( - parent is not None - and parent.auto_envvar_prefix is not None - and self.info_name is not None - ): - auto_envvar_prefix = ( - f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" - ) - else: - auto_envvar_prefix = auto_envvar_prefix.upper() - - if auto_envvar_prefix is not None: - auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") - - self.auto_envvar_prefix: t.Optional[str] = auto_envvar_prefix - - if color is None and parent is not None: - color = parent.color - - #: Controls if styling output is wanted or not. - self.color: t.Optional[bool] = color - - if show_default is None and parent is not None: - show_default = parent.show_default - - #: Show option default values when formatting help text. - self.show_default: t.Optional[bool] = show_default - - self._close_callbacks: t.List[t.Callable[[], t.Any]] = [] - self._depth = 0 - self._parameter_source: t.Dict[str, ParameterSource] = {} - self._exit_stack = ExitStack() - - def to_info_dict(self) -> t.Dict[str, t.Any]: - """Gather information that could be useful for a tool generating - user-facing documentation. This traverses the entire CLI - structure. - - .. code-block:: python - - with Context(cli) as ctx: - info = ctx.to_info_dict() - - .. versionadded:: 8.0 - """ - return { - "command": self.command.to_info_dict(self), - "info_name": self.info_name, - "allow_extra_args": self.allow_extra_args, - "allow_interspersed_args": self.allow_interspersed_args, - "ignore_unknown_options": self.ignore_unknown_options, - "auto_envvar_prefix": self.auto_envvar_prefix, - } - - def __enter__(self) -> "Context": - self._depth += 1 - push_context(self) - return self - - def __exit__( - self, - exc_type: t.Optional[t.Type[BaseException]], - exc_value: t.Optional[BaseException], - tb: t.Optional[TracebackType], - ) -> None: - self._depth -= 1 - if self._depth == 0: - self.close() - pop_context() - - @contextmanager - def scope(self, cleanup: bool = True) -> t.Iterator["Context"]: - """This helper method can be used with the context object to promote - it to the current thread local (see :func:`get_current_context`). - The default behavior of this is to invoke the cleanup functions which - can be disabled by setting `cleanup` to `False`. The cleanup - functions are typically used for things such as closing file handles. - - If the cleanup is intended the context object can also be directly - used as a context manager. - - Example usage:: - - with ctx.scope(): - assert get_current_context() is ctx - - This is equivalent:: - - with ctx: - assert get_current_context() is ctx - - .. versionadded:: 5.0 - - :param cleanup: controls if the cleanup functions should be run or - not. The default is to run these functions. In - some situations the context only wants to be - temporarily pushed in which case this can be disabled. - Nested pushes automatically defer the cleanup. - """ - if not cleanup: - self._depth += 1 - try: - with self as rv: - yield rv - finally: - if not cleanup: - self._depth -= 1 - - @property - def meta(self) -> t.Dict[str, t.Any]: - """This is a dictionary which is shared with all the contexts - that are nested. It exists so that click utilities can store some - state here if they need to. It is however the responsibility of - that code to manage this dictionary well. - - The keys are supposed to be unique dotted strings. For instance - module paths are a good choice for it. What is stored in there is - irrelevant for the operation of click. However what is important is - that code that places data here adheres to the general semantics of - the system. - - Example usage:: - - LANG_KEY = f'{__name__}.lang' - - def set_language(value): - ctx = get_current_context() - ctx.meta[LANG_KEY] = value - - def get_language(): - return get_current_context().meta.get(LANG_KEY, 'en_US') - - .. versionadded:: 5.0 - """ - return self._meta - - def make_formatter(self) -> HelpFormatter: - """Creates the :class:`~click.HelpFormatter` for the help and - usage output. - - To quickly customize the formatter class used without overriding - this method, set the :attr:`formatter_class` attribute. - - .. versionchanged:: 8.0 - Added the :attr:`formatter_class` attribute. - """ - return self.formatter_class( - width=self.terminal_width, max_width=self.max_content_width - ) - - def with_resource(self, context_manager: t.ContextManager[V]) -> V: - """Register a resource as if it were used in a ``with`` - statement. The resource will be cleaned up when the context is - popped. - - Uses :meth:`contextlib.ExitStack.enter_context`. It calls the - resource's ``__enter__()`` method and returns the result. When - the context is popped, it closes the stack, which calls the - resource's ``__exit__()`` method. - - To register a cleanup function for something that isn't a - context manager, use :meth:`call_on_close`. Or use something - from :mod:`contextlib` to turn it into a context manager first. - - .. code-block:: python - - @click.group() - @click.option("--name") - @click.pass_context - def cli(ctx): - ctx.obj = ctx.with_resource(connect_db(name)) - - :param context_manager: The context manager to enter. - :return: Whatever ``context_manager.__enter__()`` returns. - - .. versionadded:: 8.0 - """ - return self._exit_stack.enter_context(context_manager) - - def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: - """Register a function to be called when the context tears down. - - This can be used to close resources opened during the script - execution. Resources that support Python's context manager - protocol which would be used in a ``with`` statement should be - registered with :meth:`with_resource` instead. - - :param f: The function to execute on teardown. - """ - return self._exit_stack.callback(f) - - def close(self) -> None: - """Invoke all close callbacks registered with - :meth:`call_on_close`, and exit all context managers entered - with :meth:`with_resource`. - """ - self._exit_stack.close() - # In case the context is reused, create a new exit stack. - self._exit_stack = ExitStack() - - @property - def command_path(self) -> str: - """The computed command path. This is used for the ``usage`` - information on the help page. It's automatically created by - combining the info names of the chain of contexts to the root. - """ - rv = "" - if self.info_name is not None: - rv = self.info_name - if self.parent is not None: - parent_command_path = [self.parent.command_path] - - if isinstance(self.parent.command, Command): - for param in self.parent.command.get_params(self): - parent_command_path.extend(param.get_usage_pieces(self)) - - rv = f"{' '.join(parent_command_path)} {rv}" - return rv.lstrip() - - def find_root(self) -> "Context": - """Finds the outermost context.""" - node = self - while node.parent is not None: - node = node.parent - return node - - def find_object(self, object_type: t.Type[V]) -> t.Optional[V]: - """Finds the closest object of a given type.""" - node: t.Optional[Context] = self - - while node is not None: - if isinstance(node.obj, object_type): - return node.obj - - node = node.parent - - return None - - def ensure_object(self, object_type: t.Type[V]) -> V: - """Like :meth:`find_object` but sets the innermost object to a - new instance of `object_type` if it does not exist. - """ - rv = self.find_object(object_type) - if rv is None: - self.obj = rv = object_type() - return rv - - @t.overload - def lookup_default( - self, name: str, call: "te.Literal[True]" = True - ) -> t.Optional[t.Any]: ... - - @t.overload - def lookup_default( - self, name: str, call: "te.Literal[False]" = ... - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... - - def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]: - """Get the default for a parameter from :attr:`default_map`. - - :param name: Name of the parameter. - :param call: If the default is a callable, call it. Disable to - return the callable instead. - - .. versionchanged:: 8.0 - Added the ``call`` parameter. - """ - if self.default_map is not None: - value = self.default_map.get(name) - - if call and callable(value): - return value() - - return value - - return None - - def fail(self, message: str) -> "te.NoReturn": - """Aborts the execution of the program with a specific error - message. - - :param message: the error message to fail with. - """ - raise UsageError(message, self) - - def abort(self) -> "te.NoReturn": - """Aborts the script.""" - raise Abort() - - def exit(self, code: int = 0) -> "te.NoReturn": - """Exits the application with a given exit code.""" - raise Exit(code) - - def get_usage(self) -> str: - """Helper method to get formatted usage string for the current - context and command. - """ - return self.command.get_usage(self) - - def get_help(self) -> str: - """Helper method to get formatted help page for the current - context and command. - """ - return self.command.get_help(self) - - def _make_sub_context(self, command: "Command") -> "Context": - """Create a new context of the same type as this context, but - for a new command. - - :meta private: - """ - return type(self)(command, info_name=command.name, parent=self) - - @t.overload - def invoke( - __self, - __callback: "t.Callable[..., V]", - *args: t.Any, - **kwargs: t.Any, - ) -> V: ... - - @t.overload - def invoke( - __self, - __callback: "Command", - *args: t.Any, - **kwargs: t.Any, - ) -> t.Any: ... - - def invoke( - __self, - __callback: t.Union["Command", "t.Callable[..., V]"], - *args: t.Any, - **kwargs: t.Any, - ) -> t.Union[t.Any, V]: - """Invokes a command callback in exactly the way it expects. There - are two ways to invoke this method: - - 1. the first argument can be a callback and all other arguments and - keyword arguments are forwarded directly to the function. - 2. the first argument is a click command object. In that case all - arguments are forwarded as well but proper click parameters - (options and click arguments) must be keyword arguments and Click - will fill in defaults. - - Note that before Click 3.2 keyword arguments were not properly filled - in against the intention of this code and no context was created. For - more information about this change and why it was done in a bugfix - release see :ref:`upgrade-to-3.2`. - - .. versionchanged:: 8.0 - All ``kwargs`` are tracked in :attr:`params` so they will be - passed if :meth:`forward` is called at multiple levels. - """ - if isinstance(__callback, Command): - other_cmd = __callback - - if other_cmd.callback is None: - raise TypeError( - "The given command does not have a callback that can be invoked." - ) - else: - __callback = t.cast("t.Callable[..., V]", other_cmd.callback) - - ctx = __self._make_sub_context(other_cmd) - - for param in other_cmd.params: - if param.name not in kwargs and param.expose_value: - kwargs[param.name] = param.type_cast_value( # type: ignore - ctx, param.get_default(ctx) - ) - - # Track all kwargs as params, so that forward() will pass - # them on in subsequent calls. - ctx.params.update(kwargs) - else: - ctx = __self - - with augment_usage_errors(__self): - with ctx: - return __callback(*args, **kwargs) - - def forward(__self, __cmd: "Command", *args: t.Any, **kwargs: t.Any) -> t.Any: - """Similar to :meth:`invoke` but fills in default keyword - arguments from the current context if the other command expects - it. This cannot invoke callbacks directly, only other commands. - - .. versionchanged:: 8.0 - All ``kwargs`` are tracked in :attr:`params` so they will be - passed if ``forward`` is called at multiple levels. - """ - # Can only forward to other commands, not direct callbacks. - if not isinstance(__cmd, Command): - raise TypeError("Callback is not a command.") - - for param in __self.params: - if param not in kwargs: - kwargs[param] = __self.params[param] - - return __self.invoke(__cmd, *args, **kwargs) - - def set_parameter_source(self, name: str, source: ParameterSource) -> None: - """Set the source of a parameter. This indicates the location - from which the value of the parameter was obtained. - - :param name: The name of the parameter. - :param source: A member of :class:`~click.core.ParameterSource`. - """ - self._parameter_source[name] = source - - def get_parameter_source(self, name: str) -> t.Optional[ParameterSource]: - """Get the source of a parameter. This indicates the location - from which the value of the parameter was obtained. - - This can be useful for determining when a user specified a value - on the command line that is the same as the default value. It - will be :attr:`~click.core.ParameterSource.DEFAULT` only if the - value was actually taken from the default. - - :param name: The name of the parameter. - :rtype: ParameterSource - - .. versionchanged:: 8.0 - Returns ``None`` if the parameter was not provided from any - source. - """ - return self._parameter_source.get(name) - - -class BaseCommand: - """The base command implements the minimal API contract of commands. - Most code will never use this as it does not implement a lot of useful - functionality but it can act as the direct subclass of alternative - parsing methods that do not depend on the Click parser. - - For instance, this can be used to bridge Click and other systems like - argparse or docopt. - - Because base commands do not implement a lot of the API that other - parts of Click take for granted, they are not supported for all - operations. For instance, they cannot be used with the decorators - usually and they have no built-in callback system. - - .. versionchanged:: 2.0 - Added the `context_settings` parameter. - - :param name: the name of the command to use unless a group overrides it. - :param context_settings: an optional dictionary with defaults that are - passed to the context object. - """ - - #: The context class to create with :meth:`make_context`. - #: - #: .. versionadded:: 8.0 - context_class: t.Type[Context] = Context - #: the default for the :attr:`Context.allow_extra_args` flag. - allow_extra_args = False - #: the default for the :attr:`Context.allow_interspersed_args` flag. - allow_interspersed_args = True - #: the default for the :attr:`Context.ignore_unknown_options` flag. - ignore_unknown_options = False - - def __init__( - self, - name: t.Optional[str], - context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, - ) -> None: - #: the name the command thinks it has. Upon registering a command - #: on a :class:`Group` the group will default the command name - #: with this information. You should instead use the - #: :class:`Context`\'s :attr:`~Context.info_name` attribute. - self.name = name - - if context_settings is None: - context_settings = {} - - #: an optional dictionary with defaults passed to the context. - self.context_settings: t.MutableMapping[str, t.Any] = context_settings - - def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: - """Gather information that could be useful for a tool generating - user-facing documentation. This traverses the entire structure - below this command. - - Use :meth:`click.Context.to_info_dict` to traverse the entire - CLI structure. - - :param ctx: A :class:`Context` representing this command. - - .. versionadded:: 8.0 - """ - return {"name": self.name} - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} {self.name}>" - - def get_usage(self, ctx: Context) -> str: - raise NotImplementedError("Base commands cannot get usage") - - def get_help(self, ctx: Context) -> str: - raise NotImplementedError("Base commands cannot get help") - - def make_context( - self, - info_name: t.Optional[str], - args: t.List[str], - parent: t.Optional[Context] = None, - **extra: t.Any, - ) -> Context: - """This function when given an info name and arguments will kick - off the parsing and create a new :class:`Context`. It does not - invoke the actual command callback though. - - To quickly customize the context class used without overriding - this method, set the :attr:`context_class` attribute. - - :param info_name: the info name for this invocation. Generally this - is the most descriptive name for the script or - command. For the toplevel script it's usually - the name of the script, for commands below it's - the name of the command. - :param args: the arguments to parse as list of strings. - :param parent: the parent context if available. - :param extra: extra keyword arguments forwarded to the context - constructor. - - .. versionchanged:: 8.0 - Added the :attr:`context_class` attribute. - """ - for key, value in self.context_settings.items(): - if key not in extra: - extra[key] = value - - ctx = self.context_class( - self, # type: ignore[arg-type] - info_name=info_name, - parent=parent, - **extra, - ) - - with ctx.scope(cleanup=False): - self.parse_args(ctx, args) - return ctx - - def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: - """Given a context and a list of arguments this creates the parser - and parses the arguments, then modifies the context as necessary. - This is automatically invoked by :meth:`make_context`. - """ - raise NotImplementedError("Base commands do not know how to parse arguments.") - - def invoke(self, ctx: Context) -> t.Any: - """Given a context, this invokes the command. The default - implementation is raising a not implemented error. - """ - raise NotImplementedError("Base commands are not invocable by default") - - def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: - """Return a list of completions for the incomplete value. Looks - at the names of chained multi-commands. - - Any command could be part of a chained multi-command, so sibling - commands are valid at any point during command completion. Other - command classes will return more completions. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - results: t.List[CompletionItem] = [] - - while ctx.parent is not None: - ctx = ctx.parent - - if isinstance(ctx.command, MultiCommand) and ctx.command.chain: - results.extend( - CompletionItem(name, help=command.get_short_help_str()) - for name, command in _complete_visible_commands(ctx, incomplete) - if name not in ctx.protected_args - ) - - return results - - @t.overload - def main( - self, - args: t.Optional[t.Sequence[str]] = None, - prog_name: t.Optional[str] = None, - complete_var: t.Optional[str] = None, - standalone_mode: "te.Literal[True]" = True, - **extra: t.Any, - ) -> "te.NoReturn": ... - - @t.overload - def main( - self, - args: t.Optional[t.Sequence[str]] = None, - prog_name: t.Optional[str] = None, - complete_var: t.Optional[str] = None, - standalone_mode: bool = ..., - **extra: t.Any, - ) -> t.Any: ... - - def main( - self, - args: t.Optional[t.Sequence[str]] = None, - prog_name: t.Optional[str] = None, - complete_var: t.Optional[str] = None, - standalone_mode: bool = True, - windows_expand_args: bool = True, - **extra: t.Any, - ) -> t.Any: - """This is the way to invoke a script with all the bells and - whistles as a command line application. This will always terminate - the application after a call. If this is not wanted, ``SystemExit`` - needs to be caught. - - This method is also available by directly calling the instance of - a :class:`Command`. - - :param args: the arguments that should be used for parsing. If not - provided, ``sys.argv[1:]`` is used. - :param prog_name: the program name that should be used. By default - the program name is constructed by taking the file - name from ``sys.argv[0]``. - :param complete_var: the environment variable that controls the - bash completion support. The default is - ``"__COMPLETE"`` with prog_name in - uppercase. - :param standalone_mode: the default behavior is to invoke the script - in standalone mode. Click will then - handle exceptions and convert them into - error messages and the function will never - return but shut down the interpreter. If - this is set to `False` they will be - propagated to the caller and the return - value of this function is the return value - of :meth:`invoke`. - :param windows_expand_args: Expand glob patterns, user dir, and - env vars in command line args on Windows. - :param extra: extra keyword arguments are forwarded to the context - constructor. See :class:`Context` for more information. - - .. versionchanged:: 8.0.1 - Added the ``windows_expand_args`` parameter to allow - disabling command line arg expansion on Windows. - - .. versionchanged:: 8.0 - When taking arguments from ``sys.argv`` on Windows, glob - patterns, user dir, and env vars are expanded. - - .. versionchanged:: 3.0 - Added the ``standalone_mode`` parameter. - """ - if args is None: - args = sys.argv[1:] - - if os.name == "nt" and windows_expand_args: - args = _expand_args(args) - else: - args = list(args) - - if prog_name is None: - prog_name = _detect_program_name() - - # Process shell completion requests and exit early. - self._main_shell_completion(extra, prog_name, complete_var) - - try: - try: - with self.make_context(prog_name, args, **extra) as ctx: - rv = self.invoke(ctx) - if not standalone_mode: - return rv - # it's not safe to `ctx.exit(rv)` here! - # note that `rv` may actually contain data like "1" which - # has obvious effects - # more subtle case: `rv=[None, None]` can come out of - # chained commands which all returned `None` -- so it's not - # even always obvious that `rv` indicates success/failure - # by its truthiness/falsiness - ctx.exit() - except (EOFError, KeyboardInterrupt) as e: - echo(file=sys.stderr) - raise Abort() from e - except ClickException as e: - if not standalone_mode: - raise - e.show() - sys.exit(e.exit_code) - except OSError as e: - if e.errno == errno.EPIPE: - sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) - sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) - sys.exit(1) - else: - raise - except Exit as e: - if standalone_mode: - sys.exit(e.exit_code) - else: - # in non-standalone mode, return the exit code - # note that this is only reached if `self.invoke` above raises - # an Exit explicitly -- thus bypassing the check there which - # would return its result - # the results of non-standalone execution may therefore be - # somewhat ambiguous: if there are codepaths which lead to - # `ctx.exit(1)` and to `return 1`, the caller won't be able to - # tell the difference between the two - return e.exit_code - except Abort: - if not standalone_mode: - raise - echo(_("Aborted!"), file=sys.stderr) - sys.exit(1) - - def _main_shell_completion( - self, - ctx_args: t.MutableMapping[str, t.Any], - prog_name: str, - complete_var: t.Optional[str] = None, - ) -> None: - """Check if the shell is asking for tab completion, process - that, then exit early. Called from :meth:`main` before the - program is invoked. - - :param prog_name: Name of the executable in the shell. - :param complete_var: Name of the environment variable that holds - the completion instruction. Defaults to - ``_{PROG_NAME}_COMPLETE``. - - .. versionchanged:: 8.2.0 - Dots (``.``) in ``prog_name`` are replaced with underscores (``_``). - """ - if complete_var is None: - complete_name = prog_name.replace("-", "_").replace(".", "_") - complete_var = f"_{complete_name}_COMPLETE".upper() - - instruction = os.environ.get(complete_var) - - if not instruction: - return - - from .shell_completion import shell_complete - - rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) - sys.exit(rv) - - def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: - """Alias for :meth:`main`.""" - return self.main(*args, **kwargs) - - -class Command(BaseCommand): - """Commands are the basic building block of command line interfaces in - Click. A basic command handles command line parsing and might dispatch - more parsing to commands nested below it. - - :param name: the name of the command to use unless a group overrides it. - :param context_settings: an optional dictionary with defaults that are - passed to the context object. - :param callback: the callback to invoke. This is optional. - :param params: the parameters to register with this command. This can - be either :class:`Option` or :class:`Argument` objects. - :param help: the help string to use for this command. - :param epilog: like the help string but it's printed at the end of the - help page after everything else. - :param short_help: the short help to use for this command. This is - shown on the command listing of the parent command. - :param add_help_option: by default each command registers a ``--help`` - option. This can be disabled by this parameter. - :param no_args_is_help: this controls what happens if no arguments are - provided. This option is disabled by default. - If enabled this will add ``--help`` as argument - if no arguments are passed - :param hidden: hide this command from help outputs. - - :param deprecated: issues a message indicating that - the command is deprecated. - - .. versionchanged:: 8.1 - ``help``, ``epilog``, and ``short_help`` are stored unprocessed, - all formatting is done when outputting help text, not at init, - and is done even if not using the ``@command`` decorator. - - .. versionchanged:: 8.0 - Added a ``repr`` showing the command name. - - .. versionchanged:: 7.1 - Added the ``no_args_is_help`` parameter. - - .. versionchanged:: 2.0 - Added the ``context_settings`` parameter. - """ - - def __init__( - self, - name: t.Optional[str], - context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, - callback: t.Optional[t.Callable[..., t.Any]] = None, - params: t.Optional[t.List["Parameter"]] = None, - help: t.Optional[str] = None, - epilog: t.Optional[str] = None, - short_help: t.Optional[str] = None, - options_metavar: t.Optional[str] = "[OPTIONS]", - add_help_option: bool = True, - no_args_is_help: bool = False, - hidden: bool = False, - deprecated: bool = False, - ) -> None: - super().__init__(name, context_settings) - #: the callback to execute when the command fires. This might be - #: `None` in which case nothing happens. - self.callback = callback - #: the list of parameters for this command in the order they - #: should show up in the help page and execute. Eager parameters - #: will automatically be handled before non eager ones. - self.params: t.List[Parameter] = params or [] - self.help = help - self.epilog = epilog - self.options_metavar = options_metavar - self.short_help = short_help - self.add_help_option = add_help_option - self._help_option: t.Optional[HelpOption] = None - self.no_args_is_help = no_args_is_help - self.hidden = hidden - self.deprecated = deprecated - - def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict(ctx) - info_dict.update( - params=[param.to_info_dict() for param in self.get_params(ctx)], - help=self.help, - epilog=self.epilog, - short_help=self.short_help, - hidden=self.hidden, - deprecated=self.deprecated, - ) - return info_dict - - def get_usage(self, ctx: Context) -> str: - """Formats the usage line into a string and returns it. - - Calls :meth:`format_usage` internally. - """ - formatter = ctx.make_formatter() - self.format_usage(ctx, formatter) - return formatter.getvalue().rstrip("\n") - - def get_params(self, ctx: Context) -> t.List["Parameter"]: - rv = self.params - help_option = self.get_help_option(ctx) - - if help_option is not None: - rv = [*rv, help_option] - - return rv - - def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the usage line into the formatter. - - This is a low-level method called by :meth:`get_usage`. - """ - pieces = self.collect_usage_pieces(ctx) - formatter.write_usage(ctx.command_path, " ".join(pieces)) - - def collect_usage_pieces(self, ctx: Context) -> t.List[str]: - """Returns all the pieces that go into the usage line and returns - it as a list of strings. - """ - rv = [self.options_metavar] if self.options_metavar else [] - - for param in self.get_params(ctx): - rv.extend(param.get_usage_pieces(ctx)) - - return rv - - def get_help_option_names(self, ctx: Context) -> t.List[str]: - """Returns the names for the help option.""" - all_names = set(ctx.help_option_names) - for param in self.params: - all_names.difference_update(param.opts) - all_names.difference_update(param.secondary_opts) - return list(all_names) - - def get_help_option(self, ctx: Context) -> t.Optional["Option"]: - """Returns the help option object. - - Unless ``add_help_option`` is ``False``. - - .. versionchanged:: 8.1.8 - The help option is now cached to avoid creating it multiple times. - """ - help_options = self.get_help_option_names(ctx) - - if not help_options or not self.add_help_option: - return None - - # Cache the help option object in private _help_option attribute to - # avoid creating it multiple times. Not doing this will break the - # callback odering by iter_params_for_processing(), which relies on - # object comparison. - if self._help_option is None: - # Avoid circular import. - from .decorators import HelpOption - - self._help_option = HelpOption(help_options) - - return self._help_option - - def make_parser(self, ctx: Context) -> OptionParser: - """Creates the underlying option parser for this command.""" - parser = OptionParser(ctx) - for param in self.get_params(ctx): - param.add_to_parser(parser, ctx) - return parser - - def get_help(self, ctx: Context) -> str: - """Formats the help into a string and returns it. - - Calls :meth:`format_help` internally. - """ - formatter = ctx.make_formatter() - self.format_help(ctx, formatter) - return formatter.getvalue().rstrip("\n") - - def get_short_help_str(self, limit: int = 45) -> str: - """Gets short help for the command or makes it by shortening the - long help string. - """ - if self.short_help: - text = inspect.cleandoc(self.short_help) - elif self.help: - text = make_default_short_help(self.help, limit) - else: - text = "" - - if self.deprecated: - text = _("(Deprecated) {text}").format(text=text) - - return text.strip() - - def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the help into the formatter if it exists. - - This is a low-level method called by :meth:`get_help`. - - This calls the following methods: - - - :meth:`format_usage` - - :meth:`format_help_text` - - :meth:`format_options` - - :meth:`format_epilog` - """ - self.format_usage(ctx, formatter) - self.format_help_text(ctx, formatter) - self.format_options(ctx, formatter) - self.format_epilog(ctx, formatter) - - def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the help text to the formatter if it exists.""" - if self.help is not None: - # truncate the help text to the first form feed - text = inspect.cleandoc(self.help).partition("\f")[0] - else: - text = "" - - if self.deprecated: - text = _("(Deprecated) {text}").format(text=text) - - if text: - formatter.write_paragraph() - - with formatter.indentation(): - formatter.write_text(text) - - def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes all the options into the formatter if they exist.""" - opts = [] - for param in self.get_params(ctx): - rv = param.get_help_record(ctx) - if rv is not None: - opts.append(rv) - - if opts: - with formatter.section(_("Options")): - formatter.write_dl(opts) - - def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the epilog into the formatter if it exists.""" - if self.epilog: - epilog = inspect.cleandoc(self.epilog) - formatter.write_paragraph() - - with formatter.indentation(): - formatter.write_text(epilog) - - def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: - if not args and self.no_args_is_help and not ctx.resilient_parsing: - echo(ctx.get_help(), color=ctx.color) - ctx.exit() - - parser = self.make_parser(ctx) - opts, args, param_order = parser.parse_args(args=args) - - for param in iter_params_for_processing(param_order, self.get_params(ctx)): - value, args = param.handle_parse_result(ctx, opts, args) - - if args and not ctx.allow_extra_args and not ctx.resilient_parsing: - ctx.fail( - ngettext( - "Got unexpected extra argument ({args})", - "Got unexpected extra arguments ({args})", - len(args), - ).format(args=" ".join(map(str, args))) - ) - - ctx.args = args - ctx._opt_prefixes.update(parser._opt_prefixes) - return args - - def invoke(self, ctx: Context) -> t.Any: - """Given a context, this invokes the attached callback (if it exists) - in the right way. - """ - if self.deprecated: - message = _( - "DeprecationWarning: The command {name!r} is deprecated." - ).format(name=self.name) - echo(style(message, fg="red"), err=True) - - if self.callback is not None: - return ctx.invoke(self.callback, **ctx.params) - - def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: - """Return a list of completions for the incomplete value. Looks - at the names of options and chained multi-commands. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - results: t.List[CompletionItem] = [] - - if incomplete and not incomplete[0].isalnum(): - for param in self.get_params(ctx): - if ( - not isinstance(param, Option) - or param.hidden - or ( - not param.multiple - and ctx.get_parameter_source(param.name) # type: ignore - is ParameterSource.COMMANDLINE - ) - ): - continue - - results.extend( - CompletionItem(name, help=param.help) - for name in [*param.opts, *param.secondary_opts] - if name.startswith(incomplete) - ) - - results.extend(super().shell_complete(ctx, incomplete)) - return results - - -class MultiCommand(Command): - """A multi command is the basic implementation of a command that - dispatches to subcommands. The most common version is the - :class:`Group`. - - :param invoke_without_command: this controls how the multi command itself - is invoked. By default it's only invoked - if a subcommand is provided. - :param no_args_is_help: this controls what happens if no arguments are - provided. This option is enabled by default if - `invoke_without_command` is disabled or disabled - if it's enabled. If enabled this will add - ``--help`` as argument if no arguments are - passed. - :param subcommand_metavar: the string that is used in the documentation - to indicate the subcommand place. - :param chain: if this is set to `True` chaining of multiple subcommands - is enabled. This restricts the form of commands in that - they cannot have optional arguments but it allows - multiple commands to be chained together. - :param result_callback: The result callback to attach to this multi - command. This can be set or changed later with the - :meth:`result_callback` decorator. - :param attrs: Other command arguments described in :class:`Command`. - """ - - allow_extra_args = True - allow_interspersed_args = False - - def __init__( - self, - name: t.Optional[str] = None, - invoke_without_command: bool = False, - no_args_is_help: t.Optional[bool] = None, - subcommand_metavar: t.Optional[str] = None, - chain: bool = False, - result_callback: t.Optional[t.Callable[..., t.Any]] = None, - **attrs: t.Any, - ) -> None: - super().__init__(name, **attrs) - - if no_args_is_help is None: - no_args_is_help = not invoke_without_command - - self.no_args_is_help = no_args_is_help - self.invoke_without_command = invoke_without_command - - if subcommand_metavar is None: - if chain: - subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." - else: - subcommand_metavar = "COMMAND [ARGS]..." - - self.subcommand_metavar = subcommand_metavar - self.chain = chain - # The result callback that is stored. This can be set or - # overridden with the :func:`result_callback` decorator. - self._result_callback = result_callback - - if self.chain: - for param in self.params: - if isinstance(param, Argument) and not param.required: - raise RuntimeError( - "Multi commands in chain mode cannot have" - " optional arguments." - ) - - def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict(ctx) - commands = {} - - for name in self.list_commands(ctx): - command = self.get_command(ctx, name) - - if command is None: - continue - - sub_ctx = ctx._make_sub_context(command) - - with sub_ctx.scope(cleanup=False): - commands[name] = command.to_info_dict(sub_ctx) - - info_dict.update(commands=commands, chain=self.chain) - return info_dict - - def collect_usage_pieces(self, ctx: Context) -> t.List[str]: - rv = super().collect_usage_pieces(ctx) - rv.append(self.subcommand_metavar) - return rv - - def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: - super().format_options(ctx, formatter) - self.format_commands(ctx, formatter) - - def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: - """Adds a result callback to the command. By default if a - result callback is already registered this will chain them but - this can be disabled with the `replace` parameter. The result - callback is invoked with the return value of the subcommand - (or the list of return values from all subcommands if chaining - is enabled) as well as the parameters as they would be passed - to the main callback. - - Example:: - - @click.group() - @click.option('-i', '--input', default=23) - def cli(input): - return 42 - - @cli.result_callback() - def process_result(result, input): - return result + input - - :param replace: if set to `True` an already existing result - callback will be removed. - - .. versionchanged:: 8.0 - Renamed from ``resultcallback``. - - .. versionadded:: 3.0 - """ - - def decorator(f: F) -> F: - old_callback = self._result_callback - - if old_callback is None or replace: - self._result_callback = f - return f - - def function(__value, *args, **kwargs): # type: ignore - inner = old_callback(__value, *args, **kwargs) - return f(inner, *args, **kwargs) - - self._result_callback = rv = update_wrapper(t.cast(F, function), f) - return rv # type: ignore[return-value] - - return decorator - - def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: - """Extra format methods for multi methods that adds all the commands - after the options. - """ - commands = [] - for subcommand in self.list_commands(ctx): - cmd = self.get_command(ctx, subcommand) - # What is this, the tool lied about a command. Ignore it - if cmd is None: - continue - if cmd.hidden: - continue - - commands.append((subcommand, cmd)) - - # allow for 3 times the default spacing - if len(commands): - limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) - - rows = [] - for subcommand, cmd in commands: - help = cmd.get_short_help_str(limit) - rows.append((subcommand, help)) - - if rows: - with formatter.section(_("Commands")): - formatter.write_dl(rows) - - def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: - if not args and self.no_args_is_help and not ctx.resilient_parsing: - echo(ctx.get_help(), color=ctx.color) - ctx.exit() - - rest = super().parse_args(ctx, args) - - if self.chain: - ctx.protected_args = rest - ctx.args = [] - elif rest: - ctx.protected_args, ctx.args = rest[:1], rest[1:] - - return ctx.args - - def invoke(self, ctx: Context) -> t.Any: - def _process_result(value: t.Any) -> t.Any: - if self._result_callback is not None: - value = ctx.invoke(self._result_callback, value, **ctx.params) - return value - - if not ctx.protected_args: - if self.invoke_without_command: - # No subcommand was invoked, so the result callback is - # invoked with the group return value for regular - # groups, or an empty list for chained groups. - with ctx: - rv = super().invoke(ctx) - return _process_result([] if self.chain else rv) - ctx.fail(_("Missing command.")) - - # Fetch args back out - args = [*ctx.protected_args, *ctx.args] - ctx.args = [] - ctx.protected_args = [] - - # If we're not in chain mode, we only allow the invocation of a - # single command but we also inform the current context about the - # name of the command to invoke. - if not self.chain: - # Make sure the context is entered so we do not clean up - # resources until the result processor has worked. - with ctx: - cmd_name, cmd, args = self.resolve_command(ctx, args) - assert cmd is not None - ctx.invoked_subcommand = cmd_name - super().invoke(ctx) - sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) - with sub_ctx: - return _process_result(sub_ctx.command.invoke(sub_ctx)) - - # In chain mode we create the contexts step by step, but after the - # base command has been invoked. Because at that point we do not - # know the subcommands yet, the invoked subcommand attribute is - # set to ``*`` to inform the command that subcommands are executed - # but nothing else. - with ctx: - ctx.invoked_subcommand = "*" if args else None - super().invoke(ctx) - - # Otherwise we make every single context and invoke them in a - # chain. In that case the return value to the result processor - # is the list of all invoked subcommand's results. - contexts = [] - while args: - cmd_name, cmd, args = self.resolve_command(ctx, args) - assert cmd is not None - sub_ctx = cmd.make_context( - cmd_name, - args, - parent=ctx, - allow_extra_args=True, - allow_interspersed_args=False, - ) - contexts.append(sub_ctx) - args, sub_ctx.args = sub_ctx.args, [] - - rv = [] - for sub_ctx in contexts: - with sub_ctx: - rv.append(sub_ctx.command.invoke(sub_ctx)) - return _process_result(rv) - - def resolve_command( - self, ctx: Context, args: t.List[str] - ) -> t.Tuple[t.Optional[str], t.Optional[Command], t.List[str]]: - cmd_name = make_str(args[0]) - original_cmd_name = cmd_name - - # Get the command - cmd = self.get_command(ctx, cmd_name) - - # If we can't find the command but there is a normalization - # function available, we try with that one. - if cmd is None and ctx.token_normalize_func is not None: - cmd_name = ctx.token_normalize_func(cmd_name) - cmd = self.get_command(ctx, cmd_name) - - # If we don't find the command we want to show an error message - # to the user that it was not provided. However, there is - # something else we should do: if the first argument looks like - # an option we want to kick off parsing again for arguments to - # resolve things like --help which now should go to the main - # place. - if cmd is None and not ctx.resilient_parsing: - if split_opt(cmd_name)[0]: - self.parse_args(ctx, ctx.args) - ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) - return cmd_name if cmd else None, cmd, args[1:] - - def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: - """Given a context and a command name, this returns a - :class:`Command` object if it exists or returns `None`. - """ - raise NotImplementedError - - def list_commands(self, ctx: Context) -> t.List[str]: - """Returns a list of subcommand names in the order they should - appear. - """ - return [] - - def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: - """Return a list of completions for the incomplete value. Looks - at the names of options, subcommands, and chained - multi-commands. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - results = [ - CompletionItem(name, help=command.get_short_help_str()) - for name, command in _complete_visible_commands(ctx, incomplete) - ] - results.extend(super().shell_complete(ctx, incomplete)) - return results - - -class Group(MultiCommand): - """A group allows a command to have subcommands attached. This is - the most common way to implement nesting in Click. - - :param name: The name of the group command. - :param commands: A dict mapping names to :class:`Command` objects. - Can also be a list of :class:`Command`, which will use - :attr:`Command.name` to create the dict. - :param attrs: Other command arguments described in - :class:`MultiCommand`, :class:`Command`, and - :class:`BaseCommand`. - - .. versionchanged:: 8.0 - The ``commands`` argument can be a list of command objects. - """ - - #: If set, this is used by the group's :meth:`command` decorator - #: as the default :class:`Command` class. This is useful to make all - #: subcommands use a custom command class. - #: - #: .. versionadded:: 8.0 - command_class: t.Optional[t.Type[Command]] = None - - #: If set, this is used by the group's :meth:`group` decorator - #: as the default :class:`Group` class. This is useful to make all - #: subgroups use a custom group class. - #: - #: If set to the special value :class:`type` (literally - #: ``group_class = type``), this group's class will be used as the - #: default class. This makes a custom group class continue to make - #: custom groups. - #: - #: .. versionadded:: 8.0 - group_class: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None - # Literal[type] isn't valid, so use Type[type] - - def __init__( - self, - name: t.Optional[str] = None, - commands: t.Optional[ - t.Union[t.MutableMapping[str, Command], t.Sequence[Command]] - ] = None, - **attrs: t.Any, - ) -> None: - super().__init__(name, **attrs) - - if commands is None: - commands = {} - elif isinstance(commands, abc.Sequence): - commands = {c.name: c for c in commands if c.name is not None} - - #: The registered subcommands by their exported names. - self.commands: t.MutableMapping[str, Command] = commands - - def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None: - """Registers another :class:`Command` with this group. If the name - is not provided, the name of the command is used. - """ - name = name or cmd.name - if name is None: - raise TypeError("Command has no name.") - _check_multicommand(self, name, cmd, register=True) - self.commands[name] = cmd - - @t.overload - def command(self, __func: t.Callable[..., t.Any]) -> Command: ... - - @t.overload - def command( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[t.Callable[..., t.Any]], Command]: ... - - def command( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], Command], Command]: - """A shortcut decorator for declaring and attaching a command to - the group. This takes the same arguments as :func:`command` and - immediately registers the created command with this group by - calling :meth:`add_command`. - - To customize the command class used, set the - :attr:`command_class` attribute. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - - .. versionchanged:: 8.0 - Added the :attr:`command_class` attribute. - """ - from .decorators import command - - func: t.Optional[t.Callable[..., t.Any]] = None - - if args and callable(args[0]): - assert ( - len(args) == 1 and not kwargs - ), "Use 'command(**kwargs)(callable)' to provide arguments." - (func,) = args - args = () - - if self.command_class and kwargs.get("cls") is None: - kwargs["cls"] = self.command_class - - def decorator(f: t.Callable[..., t.Any]) -> Command: - cmd: Command = command(*args, **kwargs)(f) - self.add_command(cmd) - return cmd - - if func is not None: - return decorator(func) - - return decorator - - @t.overload - def group(self, __func: t.Callable[..., t.Any]) -> "Group": ... - - @t.overload - def group( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: ... - - def group( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], "Group"], "Group"]: - """A shortcut decorator for declaring and attaching a group to - the group. This takes the same arguments as :func:`group` and - immediately registers the created group with this group by - calling :meth:`add_command`. - - To customize the group class used, set the :attr:`group_class` - attribute. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - - .. versionchanged:: 8.0 - Added the :attr:`group_class` attribute. - """ - from .decorators import group - - func: t.Optional[t.Callable[..., t.Any]] = None - - if args and callable(args[0]): - assert ( - len(args) == 1 and not kwargs - ), "Use 'group(**kwargs)(callable)' to provide arguments." - (func,) = args - args = () - - if self.group_class is not None and kwargs.get("cls") is None: - if self.group_class is type: - kwargs["cls"] = type(self) - else: - kwargs["cls"] = self.group_class - - def decorator(f: t.Callable[..., t.Any]) -> "Group": - cmd: Group = group(*args, **kwargs)(f) - self.add_command(cmd) - return cmd - - if func is not None: - return decorator(func) - - return decorator - - def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: - return self.commands.get(cmd_name) - - def list_commands(self, ctx: Context) -> t.List[str]: - return sorted(self.commands) - - -class CommandCollection(MultiCommand): - """A command collection is a multi command that merges multiple multi - commands together into one. This is a straightforward implementation - that accepts a list of different multi commands as sources and - provides all the commands for each of them. - - See :class:`MultiCommand` and :class:`Command` for the description of - ``name`` and ``attrs``. - """ - - def __init__( - self, - name: t.Optional[str] = None, - sources: t.Optional[t.List[MultiCommand]] = None, - **attrs: t.Any, - ) -> None: - super().__init__(name, **attrs) - #: The list of registered multi commands. - self.sources: t.List[MultiCommand] = sources or [] - - def add_source(self, multi_cmd: MultiCommand) -> None: - """Adds a new multi command to the chain dispatcher.""" - self.sources.append(multi_cmd) - - def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: - for source in self.sources: - rv = source.get_command(ctx, cmd_name) - - if rv is not None: - if self.chain: - _check_multicommand(self, cmd_name, rv) - - return rv - - return None - - def list_commands(self, ctx: Context) -> t.List[str]: - rv: t.Set[str] = set() - - for source in self.sources: - rv.update(source.list_commands(ctx)) - - return sorted(rv) - - -def _check_iter(value: t.Any) -> t.Iterator[t.Any]: - """Check if the value is iterable but not a string. Raises a type - error, or return an iterator over the value. - """ - if isinstance(value, str): - raise TypeError - - return iter(value) - - -class Parameter: - r"""A parameter to a command comes in two versions: they are either - :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently - not supported by design as some of the internals for parsing are - intentionally not finalized. - - Some settings are supported by both options and arguments. - - :param param_decls: the parameter declarations for this option or - argument. This is a list of flags or argument - names. - :param type: the type that should be used. Either a :class:`ParamType` - or a Python type. The latter is converted into the former - automatically if supported. - :param required: controls if this is optional or not. - :param default: the default value if omitted. This can also be a callable, - in which case it's invoked when the default is needed - without any arguments. - :param callback: A function to further process or validate the value - after type conversion. It is called as ``f(ctx, param, value)`` - and must return the value. It is called for all sources, - including prompts. - :param nargs: the number of arguments to match. If not ``1`` the return - value is a tuple instead of single value. The default for - nargs is ``1`` (except if the type is a tuple, then it's - the arity of the tuple). If ``nargs=-1``, all remaining - parameters are collected. - :param metavar: how the value is represented in the help page. - :param expose_value: if this is `True` then the value is passed onwards - to the command callback and stored on the context, - otherwise it's skipped. - :param is_eager: eager values are processed before non eager ones. This - should not be set for arguments or it will inverse the - order of processing. - :param envvar: a string or list of strings that are environment variables - that should be checked. - :param shell_complete: A function that returns custom shell - completions. Used instead of the param's type completion if - given. Takes ``ctx, param, incomplete`` and must return a list - of :class:`~click.shell_completion.CompletionItem` or a list of - strings. - - .. versionchanged:: 8.0 - ``process_value`` validates required parameters and bounded - ``nargs``, and invokes the parameter callback before returning - the value. This allows the callback to validate prompts. - ``full_process_value`` is removed. - - .. versionchanged:: 8.0 - ``autocompletion`` is renamed to ``shell_complete`` and has new - semantics described above. The old name is deprecated and will - be removed in 8.1, until then it will be wrapped to match the - new requirements. - - .. versionchanged:: 8.0 - For ``multiple=True, nargs>1``, the default must be a list of - tuples. - - .. versionchanged:: 8.0 - Setting a default is no longer required for ``nargs>1``, it will - default to ``None``. ``multiple=True`` or ``nargs=-1`` will - default to ``()``. - - .. versionchanged:: 7.1 - Empty environment variables are ignored rather than taking the - empty string value. This makes it possible for scripts to clear - variables if they can't unset them. - - .. versionchanged:: 2.0 - Changed signature for parameter callback to also be passed the - parameter. The old callback format will still work, but it will - raise a warning to give you a chance to migrate the code easier. - """ - - param_type_name = "parameter" - - def __init__( - self, - param_decls: t.Optional[t.Sequence[str]] = None, - type: t.Optional[t.Union[types.ParamType, t.Any]] = None, - required: bool = False, - default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None, - callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None, - nargs: t.Optional[int] = None, - multiple: bool = False, - metavar: t.Optional[str] = None, - expose_value: bool = True, - is_eager: bool = False, - envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None, - shell_complete: t.Optional[ - t.Callable[ - [Context, "Parameter", str], - t.Union[t.List["CompletionItem"], t.List[str]], - ] - ] = None, - ) -> None: - self.name: t.Optional[str] - self.opts: t.List[str] - self.secondary_opts: t.List[str] - self.name, self.opts, self.secondary_opts = self._parse_decls( - param_decls or (), expose_value - ) - self.type: types.ParamType = types.convert_type(type, default) - - # Default nargs to what the type tells us if we have that - # information available. - if nargs is None: - if self.type.is_composite: - nargs = self.type.arity - else: - nargs = 1 - - self.required = required - self.callback = callback - self.nargs = nargs - self.multiple = multiple - self.expose_value = expose_value - self.default = default - self.is_eager = is_eager - self.metavar = metavar - self.envvar = envvar - self._custom_shell_complete = shell_complete - - if __debug__: - if self.type.is_composite and nargs != self.type.arity: - raise ValueError( - f"'nargs' must be {self.type.arity} (or None) for" - f" type {self.type!r}, but it was {nargs}." - ) - - # Skip no default or callable default. - check_default = default if not callable(default) else None - - if check_default is not None: - if multiple: - try: - # Only check the first value against nargs. - check_default = next(_check_iter(check_default), None) - except TypeError: - raise ValueError( - "'default' must be a list when 'multiple' is true." - ) from None - - # Can be None for multiple with empty default. - if nargs != 1 and check_default is not None: - try: - _check_iter(check_default) - except TypeError: - if multiple: - message = ( - "'default' must be a list of lists when 'multiple' is" - " true and 'nargs' != 1." - ) - else: - message = "'default' must be a list when 'nargs' != 1." - - raise ValueError(message) from None - - if nargs > 1 and len(check_default) != nargs: - subject = "item length" if multiple else "length" - raise ValueError( - f"'default' {subject} must match nargs={nargs}." - ) - - def to_info_dict(self) -> t.Dict[str, t.Any]: - """Gather information that could be useful for a tool generating - user-facing documentation. - - Use :meth:`click.Context.to_info_dict` to traverse the entire - CLI structure. - - .. versionadded:: 8.0 - """ - return { - "name": self.name, - "param_type_name": self.param_type_name, - "opts": self.opts, - "secondary_opts": self.secondary_opts, - "type": self.type.to_info_dict(), - "required": self.required, - "nargs": self.nargs, - "multiple": self.multiple, - "default": self.default, - "envvar": self.envvar, - } - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} {self.name}>" - - def _parse_decls( - self, decls: t.Sequence[str], expose_value: bool - ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: - raise NotImplementedError() - - @property - def human_readable_name(self) -> str: - """Returns the human readable name of this parameter. This is the - same as the name for options, but the metavar for arguments. - """ - return self.name # type: ignore - - def make_metavar(self) -> str: - if self.metavar is not None: - return self.metavar - - metavar = self.type.get_metavar(self) - - if metavar is None: - metavar = self.type.name.upper() - - if self.nargs != 1: - metavar += "..." - - return metavar - - @t.overload - def get_default( - self, ctx: Context, call: "te.Literal[True]" = True - ) -> t.Optional[t.Any]: ... - - @t.overload - def get_default( - self, ctx: Context, call: bool = ... - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... - - def get_default( - self, ctx: Context, call: bool = True - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: - """Get the default for the parameter. Tries - :meth:`Context.lookup_default` first, then the local default. - - :param ctx: Current context. - :param call: If the default is a callable, call it. Disable to - return the callable instead. - - .. versionchanged:: 8.0.2 - Type casting is no longer performed when getting a default. - - .. versionchanged:: 8.0.1 - Type casting can fail in resilient parsing mode. Invalid - defaults will not prevent showing help text. - - .. versionchanged:: 8.0 - Looks at ``ctx.default_map`` first. - - .. versionchanged:: 8.0 - Added the ``call`` parameter. - """ - value = ctx.lookup_default(self.name, call=False) # type: ignore - - if value is None: - value = self.default - - if call and callable(value): - value = value() - - return value - - def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: - raise NotImplementedError() - - def consume_value( - self, ctx: Context, opts: t.Mapping[str, t.Any] - ) -> t.Tuple[t.Any, ParameterSource]: - value = opts.get(self.name) # type: ignore - source = ParameterSource.COMMANDLINE - - if value is None: - value = self.value_from_envvar(ctx) - source = ParameterSource.ENVIRONMENT - - if value is None: - value = ctx.lookup_default(self.name) # type: ignore - source = ParameterSource.DEFAULT_MAP - - if value is None: - value = self.get_default(ctx) - source = ParameterSource.DEFAULT - - return value, source - - def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: - """Convert and validate a value against the option's - :attr:`type`, :attr:`multiple`, and :attr:`nargs`. - """ - if value is None: - return () if self.multiple or self.nargs == -1 else None - - def check_iter(value: t.Any) -> t.Iterator[t.Any]: - try: - return _check_iter(value) - except TypeError: - # This should only happen when passing in args manually, - # the parser should construct an iterable when parsing - # the command line. - raise BadParameter( - _("Value must be an iterable."), ctx=ctx, param=self - ) from None - - if self.nargs == 1 or self.type.is_composite: - - def convert(value: t.Any) -> t.Any: - return self.type(value, param=self, ctx=ctx) - - elif self.nargs == -1: - - def convert(value: t.Any) -> t.Any: # t.Tuple[t.Any, ...] - return tuple(self.type(x, self, ctx) for x in check_iter(value)) - - else: # nargs > 1 - - def convert(value: t.Any) -> t.Any: # t.Tuple[t.Any, ...] - value = tuple(check_iter(value)) - - if len(value) != self.nargs: - raise BadParameter( - ngettext( - "Takes {nargs} values but 1 was given.", - "Takes {nargs} values but {len} were given.", - len(value), - ).format(nargs=self.nargs, len=len(value)), - ctx=ctx, - param=self, - ) - - return tuple(self.type(x, self, ctx) for x in value) - - if self.multiple: - return tuple(convert(x) for x in check_iter(value)) - - return convert(value) - - def value_is_missing(self, value: t.Any) -> bool: - if value is None: - return True - - if (self.nargs != 1 or self.multiple) and value == (): - return True - - return False - - def process_value(self, ctx: Context, value: t.Any) -> t.Any: - value = self.type_cast_value(ctx, value) - - if self.required and self.value_is_missing(value): - raise MissingParameter(ctx=ctx, param=self) - - if self.callback is not None: - value = self.callback(ctx, self, value) - - return value - - def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: - if self.envvar is None: - return None - - if isinstance(self.envvar, str): - rv = os.environ.get(self.envvar) - - if rv: - return rv - else: - for envvar in self.envvar: - rv = os.environ.get(envvar) - - if rv: - return rv - - return None - - def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: - rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) - - if rv is not None and self.nargs != 1: - rv = self.type.split_envvar_value(rv) - - return rv - - def handle_parse_result( - self, ctx: Context, opts: t.Mapping[str, t.Any], args: t.List[str] - ) -> t.Tuple[t.Any, t.List[str]]: - with augment_usage_errors(ctx, param=self): - value, source = self.consume_value(ctx, opts) - ctx.set_parameter_source(self.name, source) # type: ignore - - try: - value = self.process_value(ctx, value) - except Exception: - if not ctx.resilient_parsing: - raise - - value = None - - if self.expose_value: - ctx.params[self.name] = value # type: ignore - - return value, args - - def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: - pass - - def get_usage_pieces(self, ctx: Context) -> t.List[str]: - return [] - - def get_error_hint(self, ctx: Context) -> str: - """Get a stringified version of the param for use in error messages to - indicate which param caused the error. - """ - hint_list = self.opts or [self.human_readable_name] - return " / ".join(f"'{x}'" for x in hint_list) - - def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: - """Return a list of completions for the incomplete value. If a - ``shell_complete`` function was given during init, it is used. - Otherwise, the :attr:`type` - :meth:`~click.types.ParamType.shell_complete` function is used. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - if self._custom_shell_complete is not None: - results = self._custom_shell_complete(ctx, self, incomplete) - - if results and isinstance(results[0], str): - from click.shell_completion import CompletionItem - - results = [CompletionItem(c) for c in results] - - return t.cast(t.List["CompletionItem"], results) - - return self.type.shell_complete(ctx, self, incomplete) - - -class Option(Parameter): - """Options are usually optional values on the command line and - have some extra features that arguments don't have. - - All other parameters are passed onwards to the parameter constructor. - - :param show_default: Show the default value for this option in its - help text. Values are not shown by default, unless - :attr:`Context.show_default` is ``True``. If this value is a - string, it shows that string in parentheses instead of the - actual value. This is particularly useful for dynamic options. - For single option boolean flags, the default remains hidden if - its value is ``False``. - :param show_envvar: Controls if an environment variable should be - shown on the help page. Normally, environment variables are not - shown. - :param prompt: If set to ``True`` or a non empty string then the - user will be prompted for input. If set to ``True`` the prompt - will be the option name capitalized. - :param confirmation_prompt: Prompt a second time to confirm the - value if it was prompted for. Can be set to a string instead of - ``True`` to customize the message. - :param prompt_required: If set to ``False``, the user will be - prompted for input only when the option was specified as a flag - without a value. - :param hide_input: If this is ``True`` then the input on the prompt - will be hidden from the user. This is useful for password input. - :param is_flag: forces this option to act as a flag. The default is - auto detection. - :param flag_value: which value should be used for this flag if it's - enabled. This is set to a boolean automatically if - the option string contains a slash to mark two options. - :param multiple: if this is set to `True` then the argument is accepted - multiple times and recorded. This is similar to ``nargs`` - in how it works but supports arbitrary number of - arguments. - :param count: this flag makes an option increment an integer. - :param allow_from_autoenv: if this is enabled then the value of this - parameter will be pulled from an environment - variable in case a prefix is defined on the - context. - :param help: the help string. - :param hidden: hide this option from help outputs. - :param attrs: Other command arguments described in :class:`Parameter`. - - .. versionchanged:: 8.1.0 - Help text indentation is cleaned here instead of only in the - ``@option`` decorator. - - .. versionchanged:: 8.1.0 - The ``show_default`` parameter overrides - ``Context.show_default``. - - .. versionchanged:: 8.1.0 - The default of a single option boolean flag is not shown if the - default value is ``False``. - - .. versionchanged:: 8.0.1 - ``type`` is detected from ``flag_value`` if given. - """ - - param_type_name = "option" - - def __init__( - self, - param_decls: t.Optional[t.Sequence[str]] = None, - show_default: t.Union[bool, str, None] = None, - prompt: t.Union[bool, str] = False, - confirmation_prompt: t.Union[bool, str] = False, - prompt_required: bool = True, - hide_input: bool = False, - is_flag: t.Optional[bool] = None, - flag_value: t.Optional[t.Any] = None, - multiple: bool = False, - count: bool = False, - allow_from_autoenv: bool = True, - type: t.Optional[t.Union[types.ParamType, t.Any]] = None, - help: t.Optional[str] = None, - hidden: bool = False, - show_choices: bool = True, - show_envvar: bool = False, - **attrs: t.Any, - ) -> None: - if help: - help = inspect.cleandoc(help) - - default_is_missing = "default" not in attrs - super().__init__(param_decls, type=type, multiple=multiple, **attrs) - - if prompt is True: - if self.name is None: - raise TypeError("'name' is required with 'prompt=True'.") - - prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize() - elif prompt is False: - prompt_text = None - else: - prompt_text = prompt - - self.prompt = prompt_text - self.confirmation_prompt = confirmation_prompt - self.prompt_required = prompt_required - self.hide_input = hide_input - self.hidden = hidden - - # If prompt is enabled but not required, then the option can be - # used as a flag to indicate using prompt or flag_value. - self._flag_needs_value = self.prompt is not None and not self.prompt_required - - if is_flag is None: - if flag_value is not None: - # Implicitly a flag because flag_value was set. - is_flag = True - elif self._flag_needs_value: - # Not a flag, but when used as a flag it shows a prompt. - is_flag = False - else: - # Implicitly a flag because flag options were given. - is_flag = bool(self.secondary_opts) - elif is_flag is False and not self._flag_needs_value: - # Not a flag, and prompt is not enabled, can be used as a - # flag if flag_value is set. - self._flag_needs_value = flag_value is not None - - self.default: t.Union[t.Any, t.Callable[[], t.Any]] - - if is_flag and default_is_missing and not self.required: - if multiple: - self.default = () - else: - self.default = False - - if flag_value is None: - flag_value = not self.default - - self.type: types.ParamType - if is_flag and type is None: - # Re-guess the type from the flag value instead of the - # default. - self.type = types.convert_type(None, flag_value) - - self.is_flag: bool = is_flag - self.is_bool_flag: bool = is_flag and isinstance(self.type, types.BoolParamType) - self.flag_value: t.Any = flag_value - - # Counting - self.count = count - if count: - if type is None: - self.type = types.IntRange(min=0) - if default_is_missing: - self.default = 0 - - self.allow_from_autoenv = allow_from_autoenv - self.help = help - self.show_default = show_default - self.show_choices = show_choices - self.show_envvar = show_envvar - - if __debug__: - if self.nargs == -1: - raise TypeError("nargs=-1 is not supported for options.") - - if self.prompt and self.is_flag and not self.is_bool_flag: - raise TypeError("'prompt' is not valid for non-boolean flag.") - - if not self.is_bool_flag and self.secondary_opts: - raise TypeError("Secondary flag is not valid for non-boolean flag.") - - if self.is_bool_flag and self.hide_input and self.prompt is not None: - raise TypeError( - "'prompt' with 'hide_input' is not valid for boolean flag." - ) - - if self.count: - if self.multiple: - raise TypeError("'count' is not valid with 'multiple'.") - - if self.is_flag: - raise TypeError("'count' is not valid with 'is_flag'.") - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict.update( - help=self.help, - prompt=self.prompt, - is_flag=self.is_flag, - flag_value=self.flag_value, - count=self.count, - hidden=self.hidden, - ) - return info_dict - - def _parse_decls( - self, decls: t.Sequence[str], expose_value: bool - ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: - opts = [] - secondary_opts = [] - name = None - possible_names = [] - - for decl in decls: - if decl.isidentifier(): - if name is not None: - raise TypeError(f"Name '{name}' defined twice") - name = decl - else: - split_char = ";" if decl[:1] == "/" else "/" - if split_char in decl: - first, second = decl.split(split_char, 1) - first = first.rstrip() - if first: - possible_names.append(split_opt(first)) - opts.append(first) - second = second.lstrip() - if second: - secondary_opts.append(second.lstrip()) - if first == second: - raise ValueError( - f"Boolean option {decl!r} cannot use the" - " same flag for true/false." - ) - else: - possible_names.append(split_opt(decl)) - opts.append(decl) - - if name is None and possible_names: - possible_names.sort(key=lambda x: -len(x[0])) # group long options first - name = possible_names[0][1].replace("-", "_").lower() - if not name.isidentifier(): - name = None - - if name is None: - if not expose_value: - return None, opts, secondary_opts - raise TypeError( - f"Could not determine name for option with declarations {decls!r}" - ) - - if not opts and not secondary_opts: - raise TypeError( - f"No options defined but a name was passed ({name})." - " Did you mean to declare an argument instead? Did" - f" you mean to pass '--{name}'?" - ) - - return name, opts, secondary_opts - - def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: - if self.multiple: - action = "append" - elif self.count: - action = "count" - else: - action = "store" - - if self.is_flag: - action = f"{action}_const" - - if self.is_bool_flag and self.secondary_opts: - parser.add_option( - obj=self, opts=self.opts, dest=self.name, action=action, const=True - ) - parser.add_option( - obj=self, - opts=self.secondary_opts, - dest=self.name, - action=action, - const=False, - ) - else: - parser.add_option( - obj=self, - opts=self.opts, - dest=self.name, - action=action, - const=self.flag_value, - ) - else: - parser.add_option( - obj=self, - opts=self.opts, - dest=self.name, - action=action, - nargs=self.nargs, - ) - - def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: - if self.hidden: - return None - - any_prefix_is_slash = False - - def _write_opts(opts: t.Sequence[str]) -> str: - nonlocal any_prefix_is_slash - - rv, any_slashes = join_options(opts) - - if any_slashes: - any_prefix_is_slash = True - - if not self.is_flag and not self.count: - rv += f" {self.make_metavar()}" - - return rv - - rv = [_write_opts(self.opts)] - - if self.secondary_opts: - rv.append(_write_opts(self.secondary_opts)) - - help = self.help or "" - extra = [] - - if self.show_envvar: - envvar = self.envvar - - if envvar is None: - if ( - self.allow_from_autoenv - and ctx.auto_envvar_prefix is not None - and self.name is not None - ): - envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" - - if envvar is not None: - var_str = ( - envvar - if isinstance(envvar, str) - else ", ".join(str(d) for d in envvar) - ) - extra.append(_("env var: {var}").format(var=var_str)) - - # Temporarily enable resilient parsing to avoid type casting - # failing for the default. Might be possible to extend this to - # help formatting in general. - resilient = ctx.resilient_parsing - ctx.resilient_parsing = True - - try: - default_value = self.get_default(ctx, call=False) - finally: - ctx.resilient_parsing = resilient - - show_default = False - show_default_is_str = False - - if self.show_default is not None: - if isinstance(self.show_default, str): - show_default_is_str = show_default = True - else: - show_default = self.show_default - elif ctx.show_default is not None: - show_default = ctx.show_default - - if show_default_is_str or (show_default and (default_value is not None)): - if show_default_is_str: - default_string = f"({self.show_default})" - elif isinstance(default_value, (list, tuple)): - default_string = ", ".join(str(d) for d in default_value) - elif inspect.isfunction(default_value): - default_string = _("(dynamic)") - elif self.is_bool_flag and self.secondary_opts: - # For boolean flags that have distinct True/False opts, - # use the opt without prefix instead of the value. - default_string = split_opt( - (self.opts if default_value else self.secondary_opts)[0] - )[1] - elif self.is_bool_flag and not self.secondary_opts and not default_value: - default_string = "" - elif default_value == "": - default_string = '""' - else: - default_string = str(default_value) - - if default_string: - extra.append(_("default: {default}").format(default=default_string)) - - if ( - isinstance(self.type, types._NumberRangeBase) - # skip count with default range type - and not (self.count and self.type.min == 0 and self.type.max is None) - ): - range_str = self.type._describe_range() - - if range_str: - extra.append(range_str) - - if self.required: - extra.append(_("required")) - - if extra: - extra_str = "; ".join(extra) - help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" - - return ("; " if any_prefix_is_slash else " / ").join(rv), help - - @t.overload - def get_default( - self, ctx: Context, call: "te.Literal[True]" = True - ) -> t.Optional[t.Any]: ... - - @t.overload - def get_default( - self, ctx: Context, call: bool = ... - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... - - def get_default( - self, ctx: Context, call: bool = True - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: - # If we're a non boolean flag our default is more complex because - # we need to look at all flags in the same group to figure out - # if we're the default one in which case we return the flag - # value as default. - if self.is_flag and not self.is_bool_flag: - for param in ctx.command.params: - if param.name == self.name and param.default: - return t.cast(Option, param).flag_value - - return None - - return super().get_default(ctx, call=call) - - def prompt_for_value(self, ctx: Context) -> t.Any: - """This is an alternative flow that can be activated in the full - value processing if a value does not exist. It will prompt the - user until a valid value exists and then returns the processed - value as result. - """ - assert self.prompt is not None - - # Calculate the default before prompting anything to be stable. - default = self.get_default(ctx) - - # If this is a prompt for a flag we need to handle this - # differently. - if self.is_bool_flag: - return confirm(self.prompt, default) - - return prompt( - self.prompt, - default=default, - type=self.type, - hide_input=self.hide_input, - show_choices=self.show_choices, - confirmation_prompt=self.confirmation_prompt, - value_proc=lambda x: self.process_value(ctx, x), - ) - - def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: - rv = super().resolve_envvar_value(ctx) - - if rv is not None: - return rv - - if ( - self.allow_from_autoenv - and ctx.auto_envvar_prefix is not None - and self.name is not None - ): - envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" - rv = os.environ.get(envvar) - - if rv: - return rv - - return None - - def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: - rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) - - if rv is None: - return None - - value_depth = (self.nargs != 1) + bool(self.multiple) - - if value_depth > 0: - rv = self.type.split_envvar_value(rv) - - if self.multiple and self.nargs != 1: - rv = batch(rv, self.nargs) - - return rv - - def consume_value( - self, ctx: Context, opts: t.Mapping[str, "Parameter"] - ) -> t.Tuple[t.Any, ParameterSource]: - value, source = super().consume_value(ctx, opts) - - # The parser will emit a sentinel value if the option can be - # given as a flag without a value. This is different from None - # to distinguish from the flag not being given at all. - if value is _flag_needs_value: - if self.prompt is not None and not ctx.resilient_parsing: - value = self.prompt_for_value(ctx) - source = ParameterSource.PROMPT - else: - value = self.flag_value - source = ParameterSource.COMMANDLINE - - elif ( - self.multiple - and value is not None - and any(v is _flag_needs_value for v in value) - ): - value = [self.flag_value if v is _flag_needs_value else v for v in value] - source = ParameterSource.COMMANDLINE - - # The value wasn't set, or used the param's default, prompt if - # prompting is enabled. - elif ( - source in {None, ParameterSource.DEFAULT} - and self.prompt is not None - and (self.required or self.prompt_required) - and not ctx.resilient_parsing - ): - value = self.prompt_for_value(ctx) - source = ParameterSource.PROMPT - - return value, source - - -class Argument(Parameter): - """Arguments are positional parameters to a command. They generally - provide fewer features than options but can have infinite ``nargs`` - and are required by default. - - All parameters are passed onwards to the constructor of :class:`Parameter`. - """ - - param_type_name = "argument" - - def __init__( - self, - param_decls: t.Sequence[str], - required: t.Optional[bool] = None, - **attrs: t.Any, - ) -> None: - if required is None: - if attrs.get("default") is not None: - required = False - else: - required = attrs.get("nargs", 1) > 0 - - if "multiple" in attrs: - raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") - - super().__init__(param_decls, required=required, **attrs) - - if __debug__: - if self.default is not None and self.nargs == -1: - raise TypeError("'default' is not supported for nargs=-1.") - - @property - def human_readable_name(self) -> str: - if self.metavar is not None: - return self.metavar - return self.name.upper() # type: ignore - - def make_metavar(self) -> str: - if self.metavar is not None: - return self.metavar - var = self.type.get_metavar(self) - if not var: - var = self.name.upper() # type: ignore - if not self.required: - var = f"[{var}]" - if self.nargs != 1: - var += "..." - return var - - def _parse_decls( - self, decls: t.Sequence[str], expose_value: bool - ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: - if not decls: - if not expose_value: - return None, [], [] - raise TypeError("Argument is marked as exposed, but does not have a name.") - if len(decls) == 1: - name = arg = decls[0] - name = name.replace("-", "_").lower() - else: - raise TypeError( - "Arguments take exactly one parameter declaration, got" - f" {len(decls)}." - ) - return name, [arg], [] - - def get_usage_pieces(self, ctx: Context) -> t.List[str]: - return [self.make_metavar()] - - def get_error_hint(self, ctx: Context) -> str: - return f"'{self.make_metavar()}'" - - def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: - parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) diff --git a/venv/Lib/site-packages/click/decorators.py b/venv/Lib/site-packages/click/decorators.py deleted file mode 100644 index bcf8906..0000000 --- a/venv/Lib/site-packages/click/decorators.py +++ /dev/null @@ -1,562 +0,0 @@ -import inspect -import types -import typing as t -from functools import update_wrapper -from gettext import gettext as _ - -from .core import Argument -from .core import Command -from .core import Context -from .core import Group -from .core import Option -from .core import Parameter -from .globals import get_current_context -from .utils import echo - -if t.TYPE_CHECKING: - import typing_extensions as te - - P = te.ParamSpec("P") - -R = t.TypeVar("R") -T = t.TypeVar("T") -_AnyCallable = t.Callable[..., t.Any] -FC = t.TypeVar("FC", bound=t.Union[_AnyCallable, Command]) - - -def pass_context(f: "t.Callable[te.Concatenate[Context, P], R]") -> "t.Callable[P, R]": - """Marks a callback as wanting to receive the current context - object as first argument. - """ - - def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": - return f(get_current_context(), *args, **kwargs) - - return update_wrapper(new_func, f) - - -def pass_obj(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": - """Similar to :func:`pass_context`, but only pass the object on the - context onwards (:attr:`Context.obj`). This is useful if that object - represents the state of a nested system. - """ - - def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": - return f(get_current_context().obj, *args, **kwargs) - - return update_wrapper(new_func, f) - - -def make_pass_decorator( - object_type: t.Type[T], ensure: bool = False -) -> t.Callable[["t.Callable[te.Concatenate[T, P], R]"], "t.Callable[P, R]"]: - """Given an object type this creates a decorator that will work - similar to :func:`pass_obj` but instead of passing the object of the - current context, it will find the innermost context of type - :func:`object_type`. - - This generates a decorator that works roughly like this:: - - from functools import update_wrapper - - def decorator(f): - @pass_context - def new_func(ctx, *args, **kwargs): - obj = ctx.find_object(object_type) - return ctx.invoke(f, obj, *args, **kwargs) - return update_wrapper(new_func, f) - return decorator - - :param object_type: the type of the object to pass. - :param ensure: if set to `True`, a new object will be created and - remembered on the context if it's not there yet. - """ - - def decorator(f: "t.Callable[te.Concatenate[T, P], R]") -> "t.Callable[P, R]": - def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": - ctx = get_current_context() - - obj: t.Optional[T] - if ensure: - obj = ctx.ensure_object(object_type) - else: - obj = ctx.find_object(object_type) - - if obj is None: - raise RuntimeError( - "Managed to invoke callback without a context" - f" object of type {object_type.__name__!r}" - " existing." - ) - - return ctx.invoke(f, obj, *args, **kwargs) - - return update_wrapper(new_func, f) - - return decorator - - -def pass_meta_key( - key: str, *, doc_description: t.Optional[str] = None -) -> "t.Callable[[t.Callable[te.Concatenate[t.Any, P], R]], t.Callable[P, R]]": - """Create a decorator that passes a key from - :attr:`click.Context.meta` as the first argument to the decorated - function. - - :param key: Key in ``Context.meta`` to pass. - :param doc_description: Description of the object being passed, - inserted into the decorator's docstring. Defaults to "the 'key' - key from Context.meta". - - .. versionadded:: 8.0 - """ - - def decorator(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": - def new_func(*args: "P.args", **kwargs: "P.kwargs") -> R: - ctx = get_current_context() - obj = ctx.meta[key] - return ctx.invoke(f, obj, *args, **kwargs) - - return update_wrapper(new_func, f) - - if doc_description is None: - doc_description = f"the {key!r} key from :attr:`click.Context.meta`" - - decorator.__doc__ = ( - f"Decorator that passes {doc_description} as the first argument" - " to the decorated function." - ) - return decorator - - -CmdType = t.TypeVar("CmdType", bound=Command) - - -# variant: no call, directly as decorator for a function. -@t.overload -def command(name: _AnyCallable) -> Command: ... - - -# variant: with positional name and with positional or keyword cls argument: -# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...) -@t.overload -def command( - name: t.Optional[str], - cls: t.Type[CmdType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], CmdType]: ... - - -# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) -@t.overload -def command( - name: None = None, - *, - cls: t.Type[CmdType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], CmdType]: ... - - -# variant: with optional string name, no cls argument provided. -@t.overload -def command( - name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any -) -> t.Callable[[_AnyCallable], Command]: ... - - -def command( - name: t.Union[t.Optional[str], _AnyCallable] = None, - cls: t.Optional[t.Type[CmdType]] = None, - **attrs: t.Any, -) -> t.Union[Command, t.Callable[[_AnyCallable], t.Union[Command, CmdType]]]: - r"""Creates a new :class:`Command` and uses the decorated function as - callback. This will also automatically attach all decorated - :func:`option`\s and :func:`argument`\s as parameters to the command. - - The name of the command defaults to the name of the function with - underscores replaced by dashes. If you want to change that, you can - pass the intended name as the first argument. - - All keyword arguments are forwarded to the underlying command class. - For the ``params`` argument, any decorated params are appended to - the end of the list. - - Once decorated the function turns into a :class:`Command` instance - that can be invoked as a command line utility or be attached to a - command :class:`Group`. - - :param name: the name of the command. This defaults to the function - name with underscores replaced by dashes. - :param cls: the command class to instantiate. This defaults to - :class:`Command`. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - - .. versionchanged:: 8.1 - The ``params`` argument can be used. Decorated params are - appended to the end of the list. - """ - - func: t.Optional[t.Callable[[_AnyCallable], t.Any]] = None - - if callable(name): - func = name - name = None - assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." - assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." - - if cls is None: - cls = t.cast(t.Type[CmdType], Command) - - def decorator(f: _AnyCallable) -> CmdType: - if isinstance(f, Command): - raise TypeError("Attempted to convert a callback into a command twice.") - - attr_params = attrs.pop("params", None) - params = attr_params if attr_params is not None else [] - - try: - decorator_params = f.__click_params__ # type: ignore - except AttributeError: - pass - else: - del f.__click_params__ # type: ignore - params.extend(reversed(decorator_params)) - - if attrs.get("help") is None: - attrs["help"] = f.__doc__ - - if t.TYPE_CHECKING: - assert cls is not None - assert not callable(name) - - cmd = cls( - name=name or f.__name__.lower().replace("_", "-"), - callback=f, - params=params, - **attrs, - ) - cmd.__doc__ = f.__doc__ - return cmd - - if func is not None: - return decorator(func) - - return decorator - - -GrpType = t.TypeVar("GrpType", bound=Group) - - -# variant: no call, directly as decorator for a function. -@t.overload -def group(name: _AnyCallable) -> Group: ... - - -# variant: with positional name and with positional or keyword cls argument: -# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...) -@t.overload -def group( - name: t.Optional[str], - cls: t.Type[GrpType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], GrpType]: ... - - -# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) -@t.overload -def group( - name: None = None, - *, - cls: t.Type[GrpType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], GrpType]: ... - - -# variant: with optional string name, no cls argument provided. -@t.overload -def group( - name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any -) -> t.Callable[[_AnyCallable], Group]: ... - - -def group( - name: t.Union[str, _AnyCallable, None] = None, - cls: t.Optional[t.Type[GrpType]] = None, - **attrs: t.Any, -) -> t.Union[Group, t.Callable[[_AnyCallable], t.Union[Group, GrpType]]]: - """Creates a new :class:`Group` with a function as callback. This - works otherwise the same as :func:`command` just that the `cls` - parameter is set to :class:`Group`. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - """ - if cls is None: - cls = t.cast(t.Type[GrpType], Group) - - if callable(name): - return command(cls=cls, **attrs)(name) - - return command(name, cls, **attrs) - - -def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None: - if isinstance(f, Command): - f.params.append(param) - else: - if not hasattr(f, "__click_params__"): - f.__click_params__ = [] # type: ignore - - f.__click_params__.append(param) # type: ignore - - -def argument( - *param_decls: str, cls: t.Optional[t.Type[Argument]] = None, **attrs: t.Any -) -> t.Callable[[FC], FC]: - """Attaches an argument to the command. All positional arguments are - passed as parameter declarations to :class:`Argument`; all keyword - arguments are forwarded unchanged (except ``cls``). - This is equivalent to creating an :class:`Argument` instance manually - and attaching it to the :attr:`Command.params` list. - - For the default argument class, refer to :class:`Argument` and - :class:`Parameter` for descriptions of parameters. - - :param cls: the argument class to instantiate. This defaults to - :class:`Argument`. - :param param_decls: Passed as positional arguments to the constructor of - ``cls``. - :param attrs: Passed as keyword arguments to the constructor of ``cls``. - """ - if cls is None: - cls = Argument - - def decorator(f: FC) -> FC: - _param_memo(f, cls(param_decls, **attrs)) - return f - - return decorator - - -def option( - *param_decls: str, cls: t.Optional[t.Type[Option]] = None, **attrs: t.Any -) -> t.Callable[[FC], FC]: - """Attaches an option to the command. All positional arguments are - passed as parameter declarations to :class:`Option`; all keyword - arguments are forwarded unchanged (except ``cls``). - This is equivalent to creating an :class:`Option` instance manually - and attaching it to the :attr:`Command.params` list. - - For the default option class, refer to :class:`Option` and - :class:`Parameter` for descriptions of parameters. - - :param cls: the option class to instantiate. This defaults to - :class:`Option`. - :param param_decls: Passed as positional arguments to the constructor of - ``cls``. - :param attrs: Passed as keyword arguments to the constructor of ``cls``. - """ - if cls is None: - cls = Option - - def decorator(f: FC) -> FC: - _param_memo(f, cls(param_decls, **attrs)) - return f - - return decorator - - -def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: - """Add a ``--yes`` option which shows a prompt before continuing if - not passed. If the prompt is declined, the program will exit. - - :param param_decls: One or more option names. Defaults to the single - value ``"--yes"``. - :param kwargs: Extra arguments are passed to :func:`option`. - """ - - def callback(ctx: Context, param: Parameter, value: bool) -> None: - if not value: - ctx.abort() - - if not param_decls: - param_decls = ("--yes",) - - kwargs.setdefault("is_flag", True) - kwargs.setdefault("callback", callback) - kwargs.setdefault("expose_value", False) - kwargs.setdefault("prompt", "Do you want to continue?") - kwargs.setdefault("help", "Confirm the action without prompting.") - return option(*param_decls, **kwargs) - - -def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: - """Add a ``--password`` option which prompts for a password, hiding - input and asking to enter the value again for confirmation. - - :param param_decls: One or more option names. Defaults to the single - value ``"--password"``. - :param kwargs: Extra arguments are passed to :func:`option`. - """ - if not param_decls: - param_decls = ("--password",) - - kwargs.setdefault("prompt", True) - kwargs.setdefault("confirmation_prompt", True) - kwargs.setdefault("hide_input", True) - return option(*param_decls, **kwargs) - - -def version_option( - version: t.Optional[str] = None, - *param_decls: str, - package_name: t.Optional[str] = None, - prog_name: t.Optional[str] = None, - message: t.Optional[str] = None, - **kwargs: t.Any, -) -> t.Callable[[FC], FC]: - """Add a ``--version`` option which immediately prints the version - number and exits the program. - - If ``version`` is not provided, Click will try to detect it using - :func:`importlib.metadata.version` to get the version for the - ``package_name``. On Python < 3.8, the ``importlib_metadata`` - backport must be installed. - - If ``package_name`` is not provided, Click will try to detect it by - inspecting the stack frames. This will be used to detect the - version, so it must match the name of the installed package. - - :param version: The version number to show. If not provided, Click - will try to detect it. - :param param_decls: One or more option names. Defaults to the single - value ``"--version"``. - :param package_name: The package name to detect the version from. If - not provided, Click will try to detect it. - :param prog_name: The name of the CLI to show in the message. If not - provided, it will be detected from the command. - :param message: The message to show. The values ``%(prog)s``, - ``%(package)s``, and ``%(version)s`` are available. Defaults to - ``"%(prog)s, version %(version)s"``. - :param kwargs: Extra arguments are passed to :func:`option`. - :raise RuntimeError: ``version`` could not be detected. - - .. versionchanged:: 8.0 - Add the ``package_name`` parameter, and the ``%(package)s`` - value for messages. - - .. versionchanged:: 8.0 - Use :mod:`importlib.metadata` instead of ``pkg_resources``. The - version is detected based on the package name, not the entry - point name. The Python package name must match the installed - package name, or be passed with ``package_name=``. - """ - if message is None: - message = _("%(prog)s, version %(version)s") - - if version is None and package_name is None: - frame = inspect.currentframe() - f_back = frame.f_back if frame is not None else None - f_globals = f_back.f_globals if f_back is not None else None - # break reference cycle - # https://docs.python.org/3/library/inspect.html#the-interpreter-stack - del frame - - if f_globals is not None: - package_name = f_globals.get("__name__") - - if package_name == "__main__": - package_name = f_globals.get("__package__") - - if package_name: - package_name = package_name.partition(".")[0] - - def callback(ctx: Context, param: Parameter, value: bool) -> None: - if not value or ctx.resilient_parsing: - return - - nonlocal prog_name - nonlocal version - - if prog_name is None: - prog_name = ctx.find_root().info_name - - if version is None and package_name is not None: - metadata: t.Optional[types.ModuleType] - - try: - from importlib import metadata - except ImportError: - # Python < 3.8 - import importlib_metadata as metadata # type: ignore - - try: - version = metadata.version(package_name) # type: ignore - except metadata.PackageNotFoundError: # type: ignore - raise RuntimeError( - f"{package_name!r} is not installed. Try passing" - " 'package_name' instead." - ) from None - - if version is None: - raise RuntimeError( - f"Could not determine the version for {package_name!r} automatically." - ) - - echo( - message % {"prog": prog_name, "package": package_name, "version": version}, - color=ctx.color, - ) - ctx.exit() - - if not param_decls: - param_decls = ("--version",) - - kwargs.setdefault("is_flag", True) - kwargs.setdefault("expose_value", False) - kwargs.setdefault("is_eager", True) - kwargs.setdefault("help", _("Show the version and exit.")) - kwargs["callback"] = callback - return option(*param_decls, **kwargs) - - -class HelpOption(Option): - """Pre-configured ``--help`` option which immediately prints the help page - and exits the program. - """ - - def __init__( - self, - param_decls: t.Optional[t.Sequence[str]] = None, - **kwargs: t.Any, - ) -> None: - if not param_decls: - param_decls = ("--help",) - - kwargs.setdefault("is_flag", True) - kwargs.setdefault("expose_value", False) - kwargs.setdefault("is_eager", True) - kwargs.setdefault("help", _("Show this message and exit.")) - kwargs.setdefault("callback", self.show_help) - - super().__init__(param_decls, **kwargs) - - @staticmethod - def show_help(ctx: Context, param: Parameter, value: bool) -> None: - """Callback that print the help page on ```` and exits.""" - if value and not ctx.resilient_parsing: - echo(ctx.get_help(), color=ctx.color) - ctx.exit() - - -def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: - """Decorator for the pre-configured ``--help`` option defined above. - - :param param_decls: One or more option names. Defaults to the single - value ``"--help"``. - :param kwargs: Extra arguments are passed to :func:`option`. - """ - kwargs.setdefault("cls", HelpOption) - return option(*param_decls, **kwargs) diff --git a/venv/Lib/site-packages/click/exceptions.py b/venv/Lib/site-packages/click/exceptions.py deleted file mode 100644 index 0b83151..0000000 --- a/venv/Lib/site-packages/click/exceptions.py +++ /dev/null @@ -1,296 +0,0 @@ -import typing as t -from gettext import gettext as _ -from gettext import ngettext - -from ._compat import get_text_stderr -from .globals import resolve_color_default -from .utils import echo -from .utils import format_filename - -if t.TYPE_CHECKING: - from .core import Command - from .core import Context - from .core import Parameter - - -def _join_param_hints( - param_hint: t.Optional[t.Union[t.Sequence[str], str]], -) -> t.Optional[str]: - if param_hint is not None and not isinstance(param_hint, str): - return " / ".join(repr(x) for x in param_hint) - - return param_hint - - -class ClickException(Exception): - """An exception that Click can handle and show to the user.""" - - #: The exit code for this exception. - exit_code = 1 - - def __init__(self, message: str) -> None: - super().__init__(message) - # The context will be removed by the time we print the message, so cache - # the color settings here to be used later on (in `show`) - self.show_color: t.Optional[bool] = resolve_color_default() - self.message = message - - def format_message(self) -> str: - return self.message - - def __str__(self) -> str: - return self.message - - def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: - if file is None: - file = get_text_stderr() - - echo( - _("Error: {message}").format(message=self.format_message()), - file=file, - color=self.show_color, - ) - - -class UsageError(ClickException): - """An internal exception that signals a usage error. This typically - aborts any further handling. - - :param message: the error message to display. - :param ctx: optionally the context that caused this error. Click will - fill in the context automatically in some situations. - """ - - exit_code = 2 - - def __init__(self, message: str, ctx: t.Optional["Context"] = None) -> None: - super().__init__(message) - self.ctx = ctx - self.cmd: t.Optional[Command] = self.ctx.command if self.ctx else None - - def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: - if file is None: - file = get_text_stderr() - color = None - hint = "" - if ( - self.ctx is not None - and self.ctx.command.get_help_option(self.ctx) is not None - ): - hint = _("Try '{command} {option}' for help.").format( - command=self.ctx.command_path, option=self.ctx.help_option_names[0] - ) - hint = f"{hint}\n" - if self.ctx is not None: - color = self.ctx.color - echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) - echo( - _("Error: {message}").format(message=self.format_message()), - file=file, - color=color, - ) - - -class BadParameter(UsageError): - """An exception that formats out a standardized error message for a - bad parameter. This is useful when thrown from a callback or type as - Click will attach contextual information to it (for instance, which - parameter it is). - - .. versionadded:: 2.0 - - :param param: the parameter object that caused this error. This can - be left out, and Click will attach this info itself - if possible. - :param param_hint: a string that shows up as parameter name. This - can be used as alternative to `param` in cases - where custom validation should happen. If it is - a string it's used as such, if it's a list then - each item is quoted and separated. - """ - - def __init__( - self, - message: str, - ctx: t.Optional["Context"] = None, - param: t.Optional["Parameter"] = None, - param_hint: t.Optional[str] = None, - ) -> None: - super().__init__(message, ctx) - self.param = param - self.param_hint = param_hint - - def format_message(self) -> str: - if self.param_hint is not None: - param_hint = self.param_hint - elif self.param is not None: - param_hint = self.param.get_error_hint(self.ctx) # type: ignore - else: - return _("Invalid value: {message}").format(message=self.message) - - return _("Invalid value for {param_hint}: {message}").format( - param_hint=_join_param_hints(param_hint), message=self.message - ) - - -class MissingParameter(BadParameter): - """Raised if click required an option or argument but it was not - provided when invoking the script. - - .. versionadded:: 4.0 - - :param param_type: a string that indicates the type of the parameter. - The default is to inherit the parameter type from - the given `param`. Valid values are ``'parameter'``, - ``'option'`` or ``'argument'``. - """ - - def __init__( - self, - message: t.Optional[str] = None, - ctx: t.Optional["Context"] = None, - param: t.Optional["Parameter"] = None, - param_hint: t.Optional[str] = None, - param_type: t.Optional[str] = None, - ) -> None: - super().__init__(message or "", ctx, param, param_hint) - self.param_type = param_type - - def format_message(self) -> str: - if self.param_hint is not None: - param_hint: t.Optional[str] = self.param_hint - elif self.param is not None: - param_hint = self.param.get_error_hint(self.ctx) # type: ignore - else: - param_hint = None - - param_hint = _join_param_hints(param_hint) - param_hint = f" {param_hint}" if param_hint else "" - - param_type = self.param_type - if param_type is None and self.param is not None: - param_type = self.param.param_type_name - - msg = self.message - if self.param is not None: - msg_extra = self.param.type.get_missing_message(self.param) - if msg_extra: - if msg: - msg += f". {msg_extra}" - else: - msg = msg_extra - - msg = f" {msg}" if msg else "" - - # Translate param_type for known types. - if param_type == "argument": - missing = _("Missing argument") - elif param_type == "option": - missing = _("Missing option") - elif param_type == "parameter": - missing = _("Missing parameter") - else: - missing = _("Missing {param_type}").format(param_type=param_type) - - return f"{missing}{param_hint}.{msg}" - - def __str__(self) -> str: - if not self.message: - param_name = self.param.name if self.param else None - return _("Missing parameter: {param_name}").format(param_name=param_name) - else: - return self.message - - -class NoSuchOption(UsageError): - """Raised if click attempted to handle an option that does not - exist. - - .. versionadded:: 4.0 - """ - - def __init__( - self, - option_name: str, - message: t.Optional[str] = None, - possibilities: t.Optional[t.Sequence[str]] = None, - ctx: t.Optional["Context"] = None, - ) -> None: - if message is None: - message = _("No such option: {name}").format(name=option_name) - - super().__init__(message, ctx) - self.option_name = option_name - self.possibilities = possibilities - - def format_message(self) -> str: - if not self.possibilities: - return self.message - - possibility_str = ", ".join(sorted(self.possibilities)) - suggest = ngettext( - "Did you mean {possibility}?", - "(Possible options: {possibilities})", - len(self.possibilities), - ).format(possibility=possibility_str, possibilities=possibility_str) - return f"{self.message} {suggest}" - - -class BadOptionUsage(UsageError): - """Raised if an option is generally supplied but the use of the option - was incorrect. This is for instance raised if the number of arguments - for an option is not correct. - - .. versionadded:: 4.0 - - :param option_name: the name of the option being used incorrectly. - """ - - def __init__( - self, option_name: str, message: str, ctx: t.Optional["Context"] = None - ) -> None: - super().__init__(message, ctx) - self.option_name = option_name - - -class BadArgumentUsage(UsageError): - """Raised if an argument is generally supplied but the use of the argument - was incorrect. This is for instance raised if the number of values - for an argument is not correct. - - .. versionadded:: 6.0 - """ - - -class FileError(ClickException): - """Raised if a file cannot be opened.""" - - def __init__(self, filename: str, hint: t.Optional[str] = None) -> None: - if hint is None: - hint = _("unknown error") - - super().__init__(hint) - self.ui_filename: str = format_filename(filename) - self.filename = filename - - def format_message(self) -> str: - return _("Could not open file {filename!r}: {message}").format( - filename=self.ui_filename, message=self.message - ) - - -class Abort(RuntimeError): - """An internal signalling exception that signals Click to abort.""" - - -class Exit(RuntimeError): - """An exception that indicates that the application should exit with some - status code. - - :param code: the status code to exit with. - """ - - __slots__ = ("exit_code",) - - def __init__(self, code: int = 0) -> None: - self.exit_code: int = code diff --git a/venv/Lib/site-packages/click/formatting.py b/venv/Lib/site-packages/click/formatting.py deleted file mode 100644 index ddd2a2f..0000000 --- a/venv/Lib/site-packages/click/formatting.py +++ /dev/null @@ -1,301 +0,0 @@ -import typing as t -from contextlib import contextmanager -from gettext import gettext as _ - -from ._compat import term_len -from .parser import split_opt - -# Can force a width. This is used by the test system -FORCED_WIDTH: t.Optional[int] = None - - -def measure_table(rows: t.Iterable[t.Tuple[str, str]]) -> t.Tuple[int, ...]: - widths: t.Dict[int, int] = {} - - for row in rows: - for idx, col in enumerate(row): - widths[idx] = max(widths.get(idx, 0), term_len(col)) - - return tuple(y for x, y in sorted(widths.items())) - - -def iter_rows( - rows: t.Iterable[t.Tuple[str, str]], col_count: int -) -> t.Iterator[t.Tuple[str, ...]]: - for row in rows: - yield row + ("",) * (col_count - len(row)) - - -def wrap_text( - text: str, - width: int = 78, - initial_indent: str = "", - subsequent_indent: str = "", - preserve_paragraphs: bool = False, -) -> str: - """A helper function that intelligently wraps text. By default, it - assumes that it operates on a single paragraph of text but if the - `preserve_paragraphs` parameter is provided it will intelligently - handle paragraphs (defined by two empty lines). - - If paragraphs are handled, a paragraph can be prefixed with an empty - line containing the ``\\b`` character (``\\x08``) to indicate that - no rewrapping should happen in that block. - - :param text: the text that should be rewrapped. - :param width: the maximum width for the text. - :param initial_indent: the initial indent that should be placed on the - first line as a string. - :param subsequent_indent: the indent string that should be placed on - each consecutive line. - :param preserve_paragraphs: if this flag is set then the wrapping will - intelligently handle paragraphs. - """ - from ._textwrap import TextWrapper - - text = text.expandtabs() - wrapper = TextWrapper( - width, - initial_indent=initial_indent, - subsequent_indent=subsequent_indent, - replace_whitespace=False, - ) - if not preserve_paragraphs: - return wrapper.fill(text) - - p: t.List[t.Tuple[int, bool, str]] = [] - buf: t.List[str] = [] - indent = None - - def _flush_par() -> None: - if not buf: - return - if buf[0].strip() == "\b": - p.append((indent or 0, True, "\n".join(buf[1:]))) - else: - p.append((indent or 0, False, " ".join(buf))) - del buf[:] - - for line in text.splitlines(): - if not line: - _flush_par() - indent = None - else: - if indent is None: - orig_len = term_len(line) - line = line.lstrip() - indent = orig_len - term_len(line) - buf.append(line) - _flush_par() - - rv = [] - for indent, raw, text in p: - with wrapper.extra_indent(" " * indent): - if raw: - rv.append(wrapper.indent_only(text)) - else: - rv.append(wrapper.fill(text)) - - return "\n\n".join(rv) - - -class HelpFormatter: - """This class helps with formatting text-based help pages. It's - usually just needed for very special internal cases, but it's also - exposed so that developers can write their own fancy outputs. - - At present, it always writes into memory. - - :param indent_increment: the additional increment for each level. - :param width: the width for the text. This defaults to the terminal - width clamped to a maximum of 78. - """ - - def __init__( - self, - indent_increment: int = 2, - width: t.Optional[int] = None, - max_width: t.Optional[int] = None, - ) -> None: - import shutil - - self.indent_increment = indent_increment - if max_width is None: - max_width = 80 - if width is None: - width = FORCED_WIDTH - if width is None: - width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) - self.width = width - self.current_indent = 0 - self.buffer: t.List[str] = [] - - def write(self, string: str) -> None: - """Writes a unicode string into the internal buffer.""" - self.buffer.append(string) - - def indent(self) -> None: - """Increases the indentation.""" - self.current_indent += self.indent_increment - - def dedent(self) -> None: - """Decreases the indentation.""" - self.current_indent -= self.indent_increment - - def write_usage( - self, prog: str, args: str = "", prefix: t.Optional[str] = None - ) -> None: - """Writes a usage line into the buffer. - - :param prog: the program name. - :param args: whitespace separated list of arguments. - :param prefix: The prefix for the first line. Defaults to - ``"Usage: "``. - """ - if prefix is None: - prefix = f"{_('Usage:')} " - - usage_prefix = f"{prefix:>{self.current_indent}}{prog} " - text_width = self.width - self.current_indent - - if text_width >= (term_len(usage_prefix) + 20): - # The arguments will fit to the right of the prefix. - indent = " " * term_len(usage_prefix) - self.write( - wrap_text( - args, - text_width, - initial_indent=usage_prefix, - subsequent_indent=indent, - ) - ) - else: - # The prefix is too long, put the arguments on the next line. - self.write(usage_prefix) - self.write("\n") - indent = " " * (max(self.current_indent, term_len(prefix)) + 4) - self.write( - wrap_text( - args, text_width, initial_indent=indent, subsequent_indent=indent - ) - ) - - self.write("\n") - - def write_heading(self, heading: str) -> None: - """Writes a heading into the buffer.""" - self.write(f"{'':>{self.current_indent}}{heading}:\n") - - def write_paragraph(self) -> None: - """Writes a paragraph into the buffer.""" - if self.buffer: - self.write("\n") - - def write_text(self, text: str) -> None: - """Writes re-indented text into the buffer. This rewraps and - preserves paragraphs. - """ - indent = " " * self.current_indent - self.write( - wrap_text( - text, - self.width, - initial_indent=indent, - subsequent_indent=indent, - preserve_paragraphs=True, - ) - ) - self.write("\n") - - def write_dl( - self, - rows: t.Sequence[t.Tuple[str, str]], - col_max: int = 30, - col_spacing: int = 2, - ) -> None: - """Writes a definition list into the buffer. This is how options - and commands are usually formatted. - - :param rows: a list of two item tuples for the terms and values. - :param col_max: the maximum width of the first column. - :param col_spacing: the number of spaces between the first and - second column. - """ - rows = list(rows) - widths = measure_table(rows) - if len(widths) != 2: - raise TypeError("Expected two columns for definition list") - - first_col = min(widths[0], col_max) + col_spacing - - for first, second in iter_rows(rows, len(widths)): - self.write(f"{'':>{self.current_indent}}{first}") - if not second: - self.write("\n") - continue - if term_len(first) <= first_col - col_spacing: - self.write(" " * (first_col - term_len(first))) - else: - self.write("\n") - self.write(" " * (first_col + self.current_indent)) - - text_width = max(self.width - first_col - 2, 10) - wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) - lines = wrapped_text.splitlines() - - if lines: - self.write(f"{lines[0]}\n") - - for line in lines[1:]: - self.write(f"{'':>{first_col + self.current_indent}}{line}\n") - else: - self.write("\n") - - @contextmanager - def section(self, name: str) -> t.Iterator[None]: - """Helpful context manager that writes a paragraph, a heading, - and the indents. - - :param name: the section name that is written as heading. - """ - self.write_paragraph() - self.write_heading(name) - self.indent() - try: - yield - finally: - self.dedent() - - @contextmanager - def indentation(self) -> t.Iterator[None]: - """A context manager that increases the indentation.""" - self.indent() - try: - yield - finally: - self.dedent() - - def getvalue(self) -> str: - """Returns the buffer contents.""" - return "".join(self.buffer) - - -def join_options(options: t.Sequence[str]) -> t.Tuple[str, bool]: - """Given a list of option strings this joins them in the most appropriate - way and returns them in the form ``(formatted_string, - any_prefix_is_slash)`` where the second item in the tuple is a flag that - indicates if any of the option prefixes was a slash. - """ - rv = [] - any_prefix_is_slash = False - - for opt in options: - prefix = split_opt(opt)[0] - - if prefix == "/": - any_prefix_is_slash = True - - rv.append((len(prefix), opt)) - - rv.sort(key=lambda x: x[0]) - return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/venv/Lib/site-packages/click/globals.py b/venv/Lib/site-packages/click/globals.py deleted file mode 100644 index 191e712..0000000 --- a/venv/Lib/site-packages/click/globals.py +++ /dev/null @@ -1,67 +0,0 @@ -import typing as t -from threading import local - -if t.TYPE_CHECKING: - import typing_extensions as te - - from .core import Context - -_local = local() - - -@t.overload -def get_current_context(silent: "te.Literal[False]" = False) -> "Context": ... - - -@t.overload -def get_current_context(silent: bool = ...) -> t.Optional["Context"]: ... - - -def get_current_context(silent: bool = False) -> t.Optional["Context"]: - """Returns the current click context. This can be used as a way to - access the current context object from anywhere. This is a more implicit - alternative to the :func:`pass_context` decorator. This function is - primarily useful for helpers such as :func:`echo` which might be - interested in changing its behavior based on the current context. - - To push the current context, :meth:`Context.scope` can be used. - - .. versionadded:: 5.0 - - :param silent: if set to `True` the return value is `None` if no context - is available. The default behavior is to raise a - :exc:`RuntimeError`. - """ - try: - return t.cast("Context", _local.stack[-1]) - except (AttributeError, IndexError) as e: - if not silent: - raise RuntimeError("There is no active click context.") from e - - return None - - -def push_context(ctx: "Context") -> None: - """Pushes a new context to the current stack.""" - _local.__dict__.setdefault("stack", []).append(ctx) - - -def pop_context() -> None: - """Removes the top level from the stack.""" - _local.stack.pop() - - -def resolve_color_default(color: t.Optional[bool] = None) -> t.Optional[bool]: - """Internal helper to get the default value of the color flag. If a - value is passed it's returned unchanged, otherwise it's looked up from - the current context. - """ - if color is not None: - return color - - ctx = get_current_context(silent=True) - - if ctx is not None: - return ctx.color - - return None diff --git a/venv/Lib/site-packages/click/parser.py b/venv/Lib/site-packages/click/parser.py deleted file mode 100644 index 600b843..0000000 --- a/venv/Lib/site-packages/click/parser.py +++ /dev/null @@ -1,531 +0,0 @@ -""" -This module started out as largely a copy paste from the stdlib's -optparse module with the features removed that we do not need from -optparse because we implement them in Click on a higher level (for -instance type handling, help formatting and a lot more). - -The plan is to remove more and more from here over time. - -The reason this is a different module and not optparse from the stdlib -is that there are differences in 2.x and 3.x about the error messages -generated and optparse in the stdlib uses gettext for no good reason -and might cause us issues. - -Click uses parts of optparse written by Gregory P. Ward and maintained -by the Python Software Foundation. This is limited to code in parser.py. - -Copyright 2001-2006 Gregory P. Ward. All rights reserved. -Copyright 2002-2006 Python Software Foundation. All rights reserved. -""" - -# This code uses parts of optparse written by Gregory P. Ward and -# maintained by the Python Software Foundation. -# Copyright 2001-2006 Gregory P. Ward -# Copyright 2002-2006 Python Software Foundation -import typing as t -from collections import deque -from gettext import gettext as _ -from gettext import ngettext - -from .exceptions import BadArgumentUsage -from .exceptions import BadOptionUsage -from .exceptions import NoSuchOption -from .exceptions import UsageError - -if t.TYPE_CHECKING: - import typing_extensions as te - - from .core import Argument as CoreArgument - from .core import Context - from .core import Option as CoreOption - from .core import Parameter as CoreParameter - -V = t.TypeVar("V") - -# Sentinel value that indicates an option was passed as a flag without a -# value but is not a flag option. Option.consume_value uses this to -# prompt or use the flag_value. -_flag_needs_value = object() - - -def _unpack_args( - args: t.Sequence[str], nargs_spec: t.Sequence[int] -) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.List[str]]: - """Given an iterable of arguments and an iterable of nargs specifications, - it returns a tuple with all the unpacked arguments at the first index - and all remaining arguments as the second. - - The nargs specification is the number of arguments that should be consumed - or `-1` to indicate that this position should eat up all the remainders. - - Missing items are filled with `None`. - """ - args = deque(args) - nargs_spec = deque(nargs_spec) - rv: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = [] - spos: t.Optional[int] = None - - def _fetch(c: "te.Deque[V]") -> t.Optional[V]: - try: - if spos is None: - return c.popleft() - else: - return c.pop() - except IndexError: - return None - - while nargs_spec: - nargs = _fetch(nargs_spec) - - if nargs is None: - continue - - if nargs == 1: - rv.append(_fetch(args)) - elif nargs > 1: - x = [_fetch(args) for _ in range(nargs)] - - # If we're reversed, we're pulling in the arguments in reverse, - # so we need to turn them around. - if spos is not None: - x.reverse() - - rv.append(tuple(x)) - elif nargs < 0: - if spos is not None: - raise TypeError("Cannot have two nargs < 0") - - spos = len(rv) - rv.append(None) - - # spos is the position of the wildcard (star). If it's not `None`, - # we fill it with the remainder. - if spos is not None: - rv[spos] = tuple(args) - args = [] - rv[spos + 1 :] = reversed(rv[spos + 1 :]) - - return tuple(rv), list(args) - - -def split_opt(opt: str) -> t.Tuple[str, str]: - first = opt[:1] - if first.isalnum(): - return "", opt - if opt[1:2] == first: - return opt[:2], opt[2:] - return first, opt[1:] - - -def normalize_opt(opt: str, ctx: t.Optional["Context"]) -> str: - if ctx is None or ctx.token_normalize_func is None: - return opt - prefix, opt = split_opt(opt) - return f"{prefix}{ctx.token_normalize_func(opt)}" - - -def split_arg_string(string: str) -> t.List[str]: - """Split an argument string as with :func:`shlex.split`, but don't - fail if the string is incomplete. Ignores a missing closing quote or - incomplete escape sequence and uses the partial token as-is. - - .. code-block:: python - - split_arg_string("example 'my file") - ["example", "my file"] - - split_arg_string("example my\\") - ["example", "my"] - - :param string: String to split. - """ - import shlex - - lex = shlex.shlex(string, posix=True) - lex.whitespace_split = True - lex.commenters = "" - out = [] - - try: - for token in lex: - out.append(token) - except ValueError: - # Raised when end-of-string is reached in an invalid state. Use - # the partial token as-is. The quote or escape character is in - # lex.state, not lex.token. - out.append(lex.token) - - return out - - -class Option: - def __init__( - self, - obj: "CoreOption", - opts: t.Sequence[str], - dest: t.Optional[str], - action: t.Optional[str] = None, - nargs: int = 1, - const: t.Optional[t.Any] = None, - ): - self._short_opts = [] - self._long_opts = [] - self.prefixes: t.Set[str] = set() - - for opt in opts: - prefix, value = split_opt(opt) - if not prefix: - raise ValueError(f"Invalid start character for option ({opt})") - self.prefixes.add(prefix[0]) - if len(prefix) == 1 and len(value) == 1: - self._short_opts.append(opt) - else: - self._long_opts.append(opt) - self.prefixes.add(prefix) - - if action is None: - action = "store" - - self.dest = dest - self.action = action - self.nargs = nargs - self.const = const - self.obj = obj - - @property - def takes_value(self) -> bool: - return self.action in ("store", "append") - - def process(self, value: t.Any, state: "ParsingState") -> None: - if self.action == "store": - state.opts[self.dest] = value # type: ignore - elif self.action == "store_const": - state.opts[self.dest] = self.const # type: ignore - elif self.action == "append": - state.opts.setdefault(self.dest, []).append(value) # type: ignore - elif self.action == "append_const": - state.opts.setdefault(self.dest, []).append(self.const) # type: ignore - elif self.action == "count": - state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore - else: - raise ValueError(f"unknown action '{self.action}'") - state.order.append(self.obj) - - -class Argument: - def __init__(self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1): - self.dest = dest - self.nargs = nargs - self.obj = obj - - def process( - self, - value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]], - state: "ParsingState", - ) -> None: - if self.nargs > 1: - assert value is not None - holes = sum(1 for x in value if x is None) - if holes == len(value): - value = None - elif holes != 0: - raise BadArgumentUsage( - _("Argument {name!r} takes {nargs} values.").format( - name=self.dest, nargs=self.nargs - ) - ) - - if self.nargs == -1 and self.obj.envvar is not None and value == (): - # Replace empty tuple with None so that a value from the - # environment may be tried. - value = None - - state.opts[self.dest] = value # type: ignore - state.order.append(self.obj) - - -class ParsingState: - def __init__(self, rargs: t.List[str]) -> None: - self.opts: t.Dict[str, t.Any] = {} - self.largs: t.List[str] = [] - self.rargs = rargs - self.order: t.List[CoreParameter] = [] - - -class OptionParser: - """The option parser is an internal class that is ultimately used to - parse options and arguments. It's modelled after optparse and brings - a similar but vastly simplified API. It should generally not be used - directly as the high level Click classes wrap it for you. - - It's not nearly as extensible as optparse or argparse as it does not - implement features that are implemented on a higher level (such as - types or defaults). - - :param ctx: optionally the :class:`~click.Context` where this parser - should go with. - """ - - def __init__(self, ctx: t.Optional["Context"] = None) -> None: - #: The :class:`~click.Context` for this parser. This might be - #: `None` for some advanced use cases. - self.ctx = ctx - #: This controls how the parser deals with interspersed arguments. - #: If this is set to `False`, the parser will stop on the first - #: non-option. Click uses this to implement nested subcommands - #: safely. - self.allow_interspersed_args: bool = True - #: This tells the parser how to deal with unknown options. By - #: default it will error out (which is sensible), but there is a - #: second mode where it will ignore it and continue processing - #: after shifting all the unknown options into the resulting args. - self.ignore_unknown_options: bool = False - - if ctx is not None: - self.allow_interspersed_args = ctx.allow_interspersed_args - self.ignore_unknown_options = ctx.ignore_unknown_options - - self._short_opt: t.Dict[str, Option] = {} - self._long_opt: t.Dict[str, Option] = {} - self._opt_prefixes = {"-", "--"} - self._args: t.List[Argument] = [] - - def add_option( - self, - obj: "CoreOption", - opts: t.Sequence[str], - dest: t.Optional[str], - action: t.Optional[str] = None, - nargs: int = 1, - const: t.Optional[t.Any] = None, - ) -> None: - """Adds a new option named `dest` to the parser. The destination - is not inferred (unlike with optparse) and needs to be explicitly - provided. Action can be any of ``store``, ``store_const``, - ``append``, ``append_const`` or ``count``. - - The `obj` can be used to identify the option in the order list - that is returned from the parser. - """ - opts = [normalize_opt(opt, self.ctx) for opt in opts] - option = Option(obj, opts, dest, action=action, nargs=nargs, const=const) - self._opt_prefixes.update(option.prefixes) - for opt in option._short_opts: - self._short_opt[opt] = option - for opt in option._long_opts: - self._long_opt[opt] = option - - def add_argument( - self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1 - ) -> None: - """Adds a positional argument named `dest` to the parser. - - The `obj` can be used to identify the option in the order list - that is returned from the parser. - """ - self._args.append(Argument(obj, dest=dest, nargs=nargs)) - - def parse_args( - self, args: t.List[str] - ) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.List["CoreParameter"]]: - """Parses positional arguments and returns ``(values, args, order)`` - for the parsed options and arguments as well as the leftover - arguments if there are any. The order is a list of objects as they - appear on the command line. If arguments appear multiple times they - will be memorized multiple times as well. - """ - state = ParsingState(args) - try: - self._process_args_for_options(state) - self._process_args_for_args(state) - except UsageError: - if self.ctx is None or not self.ctx.resilient_parsing: - raise - return state.opts, state.largs, state.order - - def _process_args_for_args(self, state: ParsingState) -> None: - pargs, args = _unpack_args( - state.largs + state.rargs, [x.nargs for x in self._args] - ) - - for idx, arg in enumerate(self._args): - arg.process(pargs[idx], state) - - state.largs = args - state.rargs = [] - - def _process_args_for_options(self, state: ParsingState) -> None: - while state.rargs: - arg = state.rargs.pop(0) - arglen = len(arg) - # Double dashes always handled explicitly regardless of what - # prefixes are valid. - if arg == "--": - return - elif arg[:1] in self._opt_prefixes and arglen > 1: - self._process_opts(arg, state) - elif self.allow_interspersed_args: - state.largs.append(arg) - else: - state.rargs.insert(0, arg) - return - - # Say this is the original argument list: - # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] - # ^ - # (we are about to process arg(i)). - # - # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of - # [arg0, ..., arg(i-1)] (any options and their arguments will have - # been removed from largs). - # - # The while loop will usually consume 1 or more arguments per pass. - # If it consumes 1 (eg. arg is an option that takes no arguments), - # then after _process_arg() is done the situation is: - # - # largs = subset of [arg0, ..., arg(i)] - # rargs = [arg(i+1), ..., arg(N-1)] - # - # If allow_interspersed_args is false, largs will always be - # *empty* -- still a subset of [arg0, ..., arg(i-1)], but - # not a very interesting subset! - - def _match_long_opt( - self, opt: str, explicit_value: t.Optional[str], state: ParsingState - ) -> None: - if opt not in self._long_opt: - from difflib import get_close_matches - - possibilities = get_close_matches(opt, self._long_opt) - raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) - - option = self._long_opt[opt] - if option.takes_value: - # At this point it's safe to modify rargs by injecting the - # explicit value, because no exception is raised in this - # branch. This means that the inserted value will be fully - # consumed. - if explicit_value is not None: - state.rargs.insert(0, explicit_value) - - value = self._get_value_from_state(opt, option, state) - - elif explicit_value is not None: - raise BadOptionUsage( - opt, _("Option {name!r} does not take a value.").format(name=opt) - ) - - else: - value = None - - option.process(value, state) - - def _match_short_opt(self, arg: str, state: ParsingState) -> None: - stop = False - i = 1 - prefix = arg[0] - unknown_options = [] - - for ch in arg[1:]: - opt = normalize_opt(f"{prefix}{ch}", self.ctx) - option = self._short_opt.get(opt) - i += 1 - - if not option: - if self.ignore_unknown_options: - unknown_options.append(ch) - continue - raise NoSuchOption(opt, ctx=self.ctx) - if option.takes_value: - # Any characters left in arg? Pretend they're the - # next arg, and stop consuming characters of arg. - if i < len(arg): - state.rargs.insert(0, arg[i:]) - stop = True - - value = self._get_value_from_state(opt, option, state) - - else: - value = None - - option.process(value, state) - - if stop: - break - - # If we got any unknown options we recombine the string of the - # remaining options and re-attach the prefix, then report that - # to the state as new larg. This way there is basic combinatorics - # that can be achieved while still ignoring unknown arguments. - if self.ignore_unknown_options and unknown_options: - state.largs.append(f"{prefix}{''.join(unknown_options)}") - - def _get_value_from_state( - self, option_name: str, option: Option, state: ParsingState - ) -> t.Any: - nargs = option.nargs - - if len(state.rargs) < nargs: - if option.obj._flag_needs_value: - # Option allows omitting the value. - value = _flag_needs_value - else: - raise BadOptionUsage( - option_name, - ngettext( - "Option {name!r} requires an argument.", - "Option {name!r} requires {nargs} arguments.", - nargs, - ).format(name=option_name, nargs=nargs), - ) - elif nargs == 1: - next_rarg = state.rargs[0] - - if ( - option.obj._flag_needs_value - and isinstance(next_rarg, str) - and next_rarg[:1] in self._opt_prefixes - and len(next_rarg) > 1 - ): - # The next arg looks like the start of an option, don't - # use it as the value if omitting the value is allowed. - value = _flag_needs_value - else: - value = state.rargs.pop(0) - else: - value = tuple(state.rargs[:nargs]) - del state.rargs[:nargs] - - return value - - def _process_opts(self, arg: str, state: ParsingState) -> None: - explicit_value = None - # Long option handling happens in two parts. The first part is - # supporting explicitly attached values. In any case, we will try - # to long match the option first. - if "=" in arg: - long_opt, explicit_value = arg.split("=", 1) - else: - long_opt = arg - norm_long_opt = normalize_opt(long_opt, self.ctx) - - # At this point we will match the (assumed) long option through - # the long option matching code. Note that this allows options - # like "-foo" to be matched as long options. - try: - self._match_long_opt(norm_long_opt, explicit_value, state) - except NoSuchOption: - # At this point the long option matching failed, and we need - # to try with short options. However there is a special rule - # which says, that if we have a two character options prefix - # (applies to "--foo" for instance), we do not dispatch to the - # short option code and will instead raise the no option - # error. - if arg[:2] not in self._opt_prefixes: - self._match_short_opt(arg, state) - return - - if not self.ignore_unknown_options: - raise - - state.largs.append(arg) diff --git a/venv/Lib/site-packages/click/py.typed b/venv/Lib/site-packages/click/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/venv/Lib/site-packages/click/shell_completion.py b/venv/Lib/site-packages/click/shell_completion.py deleted file mode 100644 index 07d0f09..0000000 --- a/venv/Lib/site-packages/click/shell_completion.py +++ /dev/null @@ -1,603 +0,0 @@ -import os -import re -import typing as t -from gettext import gettext as _ - -from .core import Argument -from .core import BaseCommand -from .core import Context -from .core import MultiCommand -from .core import Option -from .core import Parameter -from .core import ParameterSource -from .parser import split_arg_string -from .utils import echo - - -def shell_complete( - cli: BaseCommand, - ctx_args: t.MutableMapping[str, t.Any], - prog_name: str, - complete_var: str, - instruction: str, -) -> int: - """Perform shell completion for the given CLI program. - - :param cli: Command being called. - :param ctx_args: Extra arguments to pass to - ``cli.make_context``. - :param prog_name: Name of the executable in the shell. - :param complete_var: Name of the environment variable that holds - the completion instruction. - :param instruction: Value of ``complete_var`` with the completion - instruction and shell, in the form ``instruction_shell``. - :return: Status code to exit with. - """ - shell, _, instruction = instruction.partition("_") - comp_cls = get_completion_class(shell) - - if comp_cls is None: - return 1 - - comp = comp_cls(cli, ctx_args, prog_name, complete_var) - - if instruction == "source": - echo(comp.source()) - return 0 - - if instruction == "complete": - echo(comp.complete()) - return 0 - - return 1 - - -class CompletionItem: - """Represents a completion value and metadata about the value. The - default metadata is ``type`` to indicate special shell handling, - and ``help`` if a shell supports showing a help string next to the - value. - - Arbitrary parameters can be passed when creating the object, and - accessed using ``item.attr``. If an attribute wasn't passed, - accessing it returns ``None``. - - :param value: The completion suggestion. - :param type: Tells the shell script to provide special completion - support for the type. Click uses ``"dir"`` and ``"file"``. - :param help: String shown next to the value if supported. - :param kwargs: Arbitrary metadata. The built-in implementations - don't use this, but custom type completions paired with custom - shell support could use it. - """ - - __slots__ = ("value", "type", "help", "_info") - - def __init__( - self, - value: t.Any, - type: str = "plain", - help: t.Optional[str] = None, - **kwargs: t.Any, - ) -> None: - self.value: t.Any = value - self.type: str = type - self.help: t.Optional[str] = help - self._info = kwargs - - def __getattr__(self, name: str) -> t.Any: - return self._info.get(name) - - -# Only Bash >= 4.4 has the nosort option. -_SOURCE_BASH = """\ -%(complete_func)s() { - local IFS=$'\\n' - local response - - response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ -%(complete_var)s=bash_complete $1) - - for completion in $response; do - IFS=',' read type value <<< "$completion" - - if [[ $type == 'dir' ]]; then - COMPREPLY=() - compopt -o dirnames - elif [[ $type == 'file' ]]; then - COMPREPLY=() - compopt -o default - elif [[ $type == 'plain' ]]; then - COMPREPLY+=($value) - fi - done - - return 0 -} - -%(complete_func)s_setup() { - complete -o nosort -F %(complete_func)s %(prog_name)s -} - -%(complete_func)s_setup; -""" - -_SOURCE_ZSH = """\ -#compdef %(prog_name)s - -%(complete_func)s() { - local -a completions - local -a completions_with_descriptions - local -a response - (( ! $+commands[%(prog_name)s] )) && return 1 - - response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ -%(complete_var)s=zsh_complete %(prog_name)s)}") - - for type key descr in ${response}; do - if [[ "$type" == "plain" ]]; then - if [[ "$descr" == "_" ]]; then - completions+=("$key") - else - completions_with_descriptions+=("$key":"$descr") - fi - elif [[ "$type" == "dir" ]]; then - _path_files -/ - elif [[ "$type" == "file" ]]; then - _path_files -f - fi - done - - if [ -n "$completions_with_descriptions" ]; then - _describe -V unsorted completions_with_descriptions -U - fi - - if [ -n "$completions" ]; then - compadd -U -V unsorted -a completions - fi -} - -if [[ $zsh_eval_context[-1] == loadautofunc ]]; then - # autoload from fpath, call function directly - %(complete_func)s "$@" -else - # eval/source/. command, register function for later - compdef %(complete_func)s %(prog_name)s -fi -""" - -_SOURCE_FISH = """\ -function %(complete_func)s; - set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ -COMP_CWORD=(commandline -t) %(prog_name)s); - - for completion in $response; - set -l metadata (string split "," $completion); - - if test $metadata[1] = "dir"; - __fish_complete_directories $metadata[2]; - else if test $metadata[1] = "file"; - __fish_complete_path $metadata[2]; - else if test $metadata[1] = "plain"; - echo $metadata[2]; - end; - end; -end; - -complete --no-files --command %(prog_name)s --arguments \ -"(%(complete_func)s)"; -""" - - -class ShellComplete: - """Base class for providing shell completion support. A subclass for - a given shell will override attributes and methods to implement the - completion instructions (``source`` and ``complete``). - - :param cli: Command being called. - :param prog_name: Name of the executable in the shell. - :param complete_var: Name of the environment variable that holds - the completion instruction. - - .. versionadded:: 8.0 - """ - - name: t.ClassVar[str] - """Name to register the shell as with :func:`add_completion_class`. - This is used in completion instructions (``{name}_source`` and - ``{name}_complete``). - """ - - source_template: t.ClassVar[str] - """Completion script template formatted by :meth:`source`. This must - be provided by subclasses. - """ - - def __init__( - self, - cli: BaseCommand, - ctx_args: t.MutableMapping[str, t.Any], - prog_name: str, - complete_var: str, - ) -> None: - self.cli = cli - self.ctx_args = ctx_args - self.prog_name = prog_name - self.complete_var = complete_var - - @property - def func_name(self) -> str: - """The name of the shell function defined by the completion - script. - """ - safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII) - return f"_{safe_name}_completion" - - def source_vars(self) -> t.Dict[str, t.Any]: - """Vars for formatting :attr:`source_template`. - - By default this provides ``complete_func``, ``complete_var``, - and ``prog_name``. - """ - return { - "complete_func": self.func_name, - "complete_var": self.complete_var, - "prog_name": self.prog_name, - } - - def source(self) -> str: - """Produce the shell script that defines the completion - function. By default this ``%``-style formats - :attr:`source_template` with the dict returned by - :meth:`source_vars`. - """ - return self.source_template % self.source_vars() - - def get_completion_args(self) -> t.Tuple[t.List[str], str]: - """Use the env vars defined by the shell script to return a - tuple of ``args, incomplete``. This must be implemented by - subclasses. - """ - raise NotImplementedError - - def get_completions( - self, args: t.List[str], incomplete: str - ) -> t.List[CompletionItem]: - """Determine the context and last complete command or parameter - from the complete args. Call that object's ``shell_complete`` - method to get the completions for the incomplete value. - - :param args: List of complete args before the incomplete value. - :param incomplete: Value being completed. May be empty. - """ - ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) - obj, incomplete = _resolve_incomplete(ctx, args, incomplete) - return obj.shell_complete(ctx, incomplete) - - def format_completion(self, item: CompletionItem) -> str: - """Format a completion item into the form recognized by the - shell script. This must be implemented by subclasses. - - :param item: Completion item to format. - """ - raise NotImplementedError - - def complete(self) -> str: - """Produce the completion data to send back to the shell. - - By default this calls :meth:`get_completion_args`, gets the - completions, then calls :meth:`format_completion` for each - completion. - """ - args, incomplete = self.get_completion_args() - completions = self.get_completions(args, incomplete) - out = [self.format_completion(item) for item in completions] - return "\n".join(out) - - -class BashComplete(ShellComplete): - """Shell completion for Bash.""" - - name = "bash" - source_template = _SOURCE_BASH - - @staticmethod - def _check_version() -> None: - import shutil - import subprocess - - bash_exe = shutil.which("bash") - - if bash_exe is None: - match = None - else: - output = subprocess.run( - [bash_exe, "--norc", "-c", 'echo "${BASH_VERSION}"'], - stdout=subprocess.PIPE, - ) - match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) - - if match is not None: - major, minor = match.groups() - - if major < "4" or major == "4" and minor < "4": - echo( - _( - "Shell completion is not supported for Bash" - " versions older than 4.4." - ), - err=True, - ) - else: - echo( - _("Couldn't detect Bash version, shell completion is not supported."), - err=True, - ) - - def source(self) -> str: - self._check_version() - return super().source() - - def get_completion_args(self) -> t.Tuple[t.List[str], str]: - cwords = split_arg_string(os.environ["COMP_WORDS"]) - cword = int(os.environ["COMP_CWORD"]) - args = cwords[1:cword] - - try: - incomplete = cwords[cword] - except IndexError: - incomplete = "" - - return args, incomplete - - def format_completion(self, item: CompletionItem) -> str: - return f"{item.type},{item.value}" - - -class ZshComplete(ShellComplete): - """Shell completion for Zsh.""" - - name = "zsh" - source_template = _SOURCE_ZSH - - def get_completion_args(self) -> t.Tuple[t.List[str], str]: - cwords = split_arg_string(os.environ["COMP_WORDS"]) - cword = int(os.environ["COMP_CWORD"]) - args = cwords[1:cword] - - try: - incomplete = cwords[cword] - except IndexError: - incomplete = "" - - return args, incomplete - - def format_completion(self, item: CompletionItem) -> str: - return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}" - - -class FishComplete(ShellComplete): - """Shell completion for Fish.""" - - name = "fish" - source_template = _SOURCE_FISH - - def get_completion_args(self) -> t.Tuple[t.List[str], str]: - cwords = split_arg_string(os.environ["COMP_WORDS"]) - incomplete = os.environ["COMP_CWORD"] - args = cwords[1:] - - # Fish stores the partial word in both COMP_WORDS and - # COMP_CWORD, remove it from complete args. - if incomplete and args and args[-1] == incomplete: - args.pop() - - return args, incomplete - - def format_completion(self, item: CompletionItem) -> str: - if item.help: - return f"{item.type},{item.value}\t{item.help}" - - return f"{item.type},{item.value}" - - -ShellCompleteType = t.TypeVar("ShellCompleteType", bound=t.Type[ShellComplete]) - - -_available_shells: t.Dict[str, t.Type[ShellComplete]] = { - "bash": BashComplete, - "fish": FishComplete, - "zsh": ZshComplete, -} - - -def add_completion_class( - cls: ShellCompleteType, name: t.Optional[str] = None -) -> ShellCompleteType: - """Register a :class:`ShellComplete` subclass under the given name. - The name will be provided by the completion instruction environment - variable during completion. - - :param cls: The completion class that will handle completion for the - shell. - :param name: Name to register the class under. Defaults to the - class's ``name`` attribute. - """ - if name is None: - name = cls.name - - _available_shells[name] = cls - - return cls - - -def get_completion_class(shell: str) -> t.Optional[t.Type[ShellComplete]]: - """Look up a registered :class:`ShellComplete` subclass by the name - provided by the completion instruction environment variable. If the - name isn't registered, returns ``None``. - - :param shell: Name the class is registered under. - """ - return _available_shells.get(shell) - - -def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: - """Determine if the given parameter is an argument that can still - accept values. - - :param ctx: Invocation context for the command represented by the - parsed complete args. - :param param: Argument object being checked. - """ - if not isinstance(param, Argument): - return False - - assert param.name is not None - # Will be None if expose_value is False. - value = ctx.params.get(param.name) - return ( - param.nargs == -1 - or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE - or ( - param.nargs > 1 - and isinstance(value, (tuple, list)) - and len(value) < param.nargs - ) - ) - - -def _start_of_option(ctx: Context, value: str) -> bool: - """Check if the value looks like the start of an option.""" - if not value: - return False - - c = value[0] - return c in ctx._opt_prefixes - - -def _is_incomplete_option(ctx: Context, args: t.List[str], param: Parameter) -> bool: - """Determine if the given parameter is an option that needs a value. - - :param args: List of complete args before the incomplete value. - :param param: Option object being checked. - """ - if not isinstance(param, Option): - return False - - if param.is_flag or param.count: - return False - - last_option = None - - for index, arg in enumerate(reversed(args)): - if index + 1 > param.nargs: - break - - if _start_of_option(ctx, arg): - last_option = arg - - return last_option is not None and last_option in param.opts - - -def _resolve_context( - cli: BaseCommand, - ctx_args: t.MutableMapping[str, t.Any], - prog_name: str, - args: t.List[str], -) -> Context: - """Produce the context hierarchy starting with the command and - traversing the complete arguments. This only follows the commands, - it doesn't trigger input prompts or callbacks. - - :param cli: Command being called. - :param prog_name: Name of the executable in the shell. - :param args: List of complete args before the incomplete value. - """ - ctx_args["resilient_parsing"] = True - ctx = cli.make_context(prog_name, args.copy(), **ctx_args) - args = ctx.protected_args + ctx.args - - while args: - command = ctx.command - - if isinstance(command, MultiCommand): - if not command.chain: - name, cmd, args = command.resolve_command(ctx, args) - - if cmd is None: - return ctx - - ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True) - args = ctx.protected_args + ctx.args - else: - sub_ctx = ctx - - while args: - name, cmd, args = command.resolve_command(ctx, args) - - if cmd is None: - return ctx - - sub_ctx = cmd.make_context( - name, - args, - parent=ctx, - allow_extra_args=True, - allow_interspersed_args=False, - resilient_parsing=True, - ) - args = sub_ctx.args - - ctx = sub_ctx - args = [*sub_ctx.protected_args, *sub_ctx.args] - else: - break - - return ctx - - -def _resolve_incomplete( - ctx: Context, args: t.List[str], incomplete: str -) -> t.Tuple[t.Union[BaseCommand, Parameter], str]: - """Find the Click object that will handle the completion of the - incomplete value. Return the object and the incomplete value. - - :param ctx: Invocation context for the command represented by - the parsed complete args. - :param args: List of complete args before the incomplete value. - :param incomplete: Value being completed. May be empty. - """ - # Different shells treat an "=" between a long option name and - # value differently. Might keep the value joined, return the "=" - # as a separate item, or return the split name and value. Always - # split and discard the "=" to make completion easier. - if incomplete == "=": - incomplete = "" - elif "=" in incomplete and _start_of_option(ctx, incomplete): - name, _, incomplete = incomplete.partition("=") - args.append(name) - - # The "--" marker tells Click to stop treating values as options - # even if they start with the option character. If it hasn't been - # given and the incomplete arg looks like an option, the current - # command will provide option name completions. - if "--" not in args and _start_of_option(ctx, incomplete): - return ctx.command, incomplete - - params = ctx.command.get_params(ctx) - - # If the last complete arg is an option name with an incomplete - # value, the option will provide value completions. - for param in params: - if _is_incomplete_option(ctx, args, param): - return param, incomplete - - # It's not an option name or value. The first argument without a - # parsed value will provide value completions. - for param in params: - if _is_incomplete_argument(ctx, param): - return param, incomplete - - # There were no unparsed arguments, the command may be a group that - # will provide command name completions. - return ctx.command, incomplete diff --git a/venv/Lib/site-packages/click/termui.py b/venv/Lib/site-packages/click/termui.py deleted file mode 100644 index c084f19..0000000 --- a/venv/Lib/site-packages/click/termui.py +++ /dev/null @@ -1,784 +0,0 @@ -import inspect -import io -import itertools -import sys -import typing as t -from gettext import gettext as _ - -from ._compat import isatty -from ._compat import strip_ansi -from .exceptions import Abort -from .exceptions import UsageError -from .globals import resolve_color_default -from .types import Choice -from .types import convert_type -from .types import ParamType -from .utils import echo -from .utils import LazyFile - -if t.TYPE_CHECKING: - from ._termui_impl import ProgressBar - -V = t.TypeVar("V") - -# The prompt functions to use. The doc tools currently override these -# functions to customize how they work. -visible_prompt_func: t.Callable[[str], str] = input - -_ansi_colors = { - "black": 30, - "red": 31, - "green": 32, - "yellow": 33, - "blue": 34, - "magenta": 35, - "cyan": 36, - "white": 37, - "reset": 39, - "bright_black": 90, - "bright_red": 91, - "bright_green": 92, - "bright_yellow": 93, - "bright_blue": 94, - "bright_magenta": 95, - "bright_cyan": 96, - "bright_white": 97, -} -_ansi_reset_all = "\033[0m" - - -def hidden_prompt_func(prompt: str) -> str: - import getpass - - return getpass.getpass(prompt) - - -def _build_prompt( - text: str, - suffix: str, - show_default: bool = False, - default: t.Optional[t.Any] = None, - show_choices: bool = True, - type: t.Optional[ParamType] = None, -) -> str: - prompt = text - if type is not None and show_choices and isinstance(type, Choice): - prompt += f" ({', '.join(map(str, type.choices))})" - if default is not None and show_default: - prompt = f"{prompt} [{_format_default(default)}]" - return f"{prompt}{suffix}" - - -def _format_default(default: t.Any) -> t.Any: - if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): - return default.name - - return default - - -def prompt( - text: str, - default: t.Optional[t.Any] = None, - hide_input: bool = False, - confirmation_prompt: t.Union[bool, str] = False, - type: t.Optional[t.Union[ParamType, t.Any]] = None, - value_proc: t.Optional[t.Callable[[str], t.Any]] = None, - prompt_suffix: str = ": ", - show_default: bool = True, - err: bool = False, - show_choices: bool = True, -) -> t.Any: - """Prompts a user for input. This is a convenience function that can - be used to prompt a user for input later. - - If the user aborts the input by sending an interrupt signal, this - function will catch it and raise a :exc:`Abort` exception. - - :param text: the text to show for the prompt. - :param default: the default value to use if no input happens. If this - is not given it will prompt until it's aborted. - :param hide_input: if this is set to true then the input value will - be hidden. - :param confirmation_prompt: Prompt a second time to confirm the - value. Can be set to a string instead of ``True`` to customize - the message. - :param type: the type to use to check the value against. - :param value_proc: if this parameter is provided it's a function that - is invoked instead of the type conversion to - convert a value. - :param prompt_suffix: a suffix that should be added to the prompt. - :param show_default: shows or hides the default value in the prompt. - :param err: if set to true the file defaults to ``stderr`` instead of - ``stdout``, the same as with echo. - :param show_choices: Show or hide choices if the passed type is a Choice. - For example if type is a Choice of either day or week, - show_choices is true and text is "Group by" then the - prompt will be "Group by (day, week): ". - - .. versionadded:: 8.0 - ``confirmation_prompt`` can be a custom string. - - .. versionadded:: 7.0 - Added the ``show_choices`` parameter. - - .. versionadded:: 6.0 - Added unicode support for cmd.exe on Windows. - - .. versionadded:: 4.0 - Added the `err` parameter. - - """ - - def prompt_func(text: str) -> str: - f = hidden_prompt_func if hide_input else visible_prompt_func - try: - # Write the prompt separately so that we get nice - # coloring through colorama on Windows - echo(text.rstrip(" "), nl=False, err=err) - # Echo a space to stdout to work around an issue where - # readline causes backspace to clear the whole line. - return f(" ") - except (KeyboardInterrupt, EOFError): - # getpass doesn't print a newline if the user aborts input with ^C. - # Allegedly this behavior is inherited from getpass(3). - # A doc bug has been filed at https://bugs.python.org/issue24711 - if hide_input: - echo(None, err=err) - raise Abort() from None - - if value_proc is None: - value_proc = convert_type(type, default) - - prompt = _build_prompt( - text, prompt_suffix, show_default, default, show_choices, type - ) - - if confirmation_prompt: - if confirmation_prompt is True: - confirmation_prompt = _("Repeat for confirmation") - - confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) - - while True: - while True: - value = prompt_func(prompt) - if value: - break - elif default is not None: - value = default - break - try: - result = value_proc(value) - except UsageError as e: - if hide_input: - echo(_("Error: The value you entered was invalid."), err=err) - else: - echo(_("Error: {e.message}").format(e=e), err=err) - continue - if not confirmation_prompt: - return result - while True: - value2 = prompt_func(confirmation_prompt) - is_empty = not value and not value2 - if value2 or is_empty: - break - if value == value2: - return result - echo(_("Error: The two entered values do not match."), err=err) - - -def confirm( - text: str, - default: t.Optional[bool] = False, - abort: bool = False, - prompt_suffix: str = ": ", - show_default: bool = True, - err: bool = False, -) -> bool: - """Prompts for confirmation (yes/no question). - - If the user aborts the input by sending a interrupt signal this - function will catch it and raise a :exc:`Abort` exception. - - :param text: the question to ask. - :param default: The default value to use when no input is given. If - ``None``, repeat until input is given. - :param abort: if this is set to `True` a negative answer aborts the - exception by raising :exc:`Abort`. - :param prompt_suffix: a suffix that should be added to the prompt. - :param show_default: shows or hides the default value in the prompt. - :param err: if set to true the file defaults to ``stderr`` instead of - ``stdout``, the same as with echo. - - .. versionchanged:: 8.0 - Repeat until input is given if ``default`` is ``None``. - - .. versionadded:: 4.0 - Added the ``err`` parameter. - """ - prompt = _build_prompt( - text, - prompt_suffix, - show_default, - "y/n" if default is None else ("Y/n" if default else "y/N"), - ) - - while True: - try: - # Write the prompt separately so that we get nice - # coloring through colorama on Windows - echo(prompt.rstrip(" "), nl=False, err=err) - # Echo a space to stdout to work around an issue where - # readline causes backspace to clear the whole line. - value = visible_prompt_func(" ").lower().strip() - except (KeyboardInterrupt, EOFError): - raise Abort() from None - if value in ("y", "yes"): - rv = True - elif value in ("n", "no"): - rv = False - elif default is not None and value == "": - rv = default - else: - echo(_("Error: invalid input"), err=err) - continue - break - if abort and not rv: - raise Abort() - return rv - - -def echo_via_pager( - text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str], - color: t.Optional[bool] = None, -) -> None: - """This function takes a text and shows it via an environment specific - pager on stdout. - - .. versionchanged:: 3.0 - Added the `color` flag. - - :param text_or_generator: the text to page, or alternatively, a - generator emitting the text to page. - :param color: controls if the pager supports ANSI colors or not. The - default is autodetection. - """ - color = resolve_color_default(color) - - if inspect.isgeneratorfunction(text_or_generator): - i = t.cast(t.Callable[[], t.Iterable[str]], text_or_generator)() - elif isinstance(text_or_generator, str): - i = [text_or_generator] - else: - i = iter(t.cast(t.Iterable[str], text_or_generator)) - - # convert every element of i to a text type if necessary - text_generator = (el if isinstance(el, str) else str(el) for el in i) - - from ._termui_impl import pager - - return pager(itertools.chain(text_generator, "\n"), color) - - -def progressbar( - iterable: t.Optional[t.Iterable[V]] = None, - length: t.Optional[int] = None, - label: t.Optional[str] = None, - show_eta: bool = True, - show_percent: t.Optional[bool] = None, - show_pos: bool = False, - item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, - fill_char: str = "#", - empty_char: str = "-", - bar_template: str = "%(label)s [%(bar)s] %(info)s", - info_sep: str = " ", - width: int = 36, - file: t.Optional[t.TextIO] = None, - color: t.Optional[bool] = None, - update_min_steps: int = 1, -) -> "ProgressBar[V]": - """This function creates an iterable context manager that can be used - to iterate over something while showing a progress bar. It will - either iterate over the `iterable` or `length` items (that are counted - up). While iteration happens, this function will print a rendered - progress bar to the given `file` (defaults to stdout) and will attempt - to calculate remaining time and more. By default, this progress bar - will not be rendered if the file is not a terminal. - - The context manager creates the progress bar. When the context - manager is entered the progress bar is already created. With every - iteration over the progress bar, the iterable passed to the bar is - advanced and the bar is updated. When the context manager exits, - a newline is printed and the progress bar is finalized on screen. - - Note: The progress bar is currently designed for use cases where the - total progress can be expected to take at least several seconds. - Because of this, the ProgressBar class object won't display - progress that is considered too fast, and progress where the time - between steps is less than a second. - - No printing must happen or the progress bar will be unintentionally - destroyed. - - Example usage:: - - with progressbar(items) as bar: - for item in bar: - do_something_with(item) - - Alternatively, if no iterable is specified, one can manually update the - progress bar through the `update()` method instead of directly - iterating over the progress bar. The update method accepts the number - of steps to increment the bar with:: - - with progressbar(length=chunks.total_bytes) as bar: - for chunk in chunks: - process_chunk(chunk) - bar.update(chunks.bytes) - - The ``update()`` method also takes an optional value specifying the - ``current_item`` at the new position. This is useful when used - together with ``item_show_func`` to customize the output for each - manual step:: - - with click.progressbar( - length=total_size, - label='Unzipping archive', - item_show_func=lambda a: a.filename - ) as bar: - for archive in zip_file: - archive.extract() - bar.update(archive.size, archive) - - :param iterable: an iterable to iterate over. If not provided the length - is required. - :param length: the number of items to iterate over. By default the - progressbar will attempt to ask the iterator about its - length, which might or might not work. If an iterable is - also provided this parameter can be used to override the - length. If an iterable is not provided the progress bar - will iterate over a range of that length. - :param label: the label to show next to the progress bar. - :param show_eta: enables or disables the estimated time display. This is - automatically disabled if the length cannot be - determined. - :param show_percent: enables or disables the percentage display. The - default is `True` if the iterable has a length or - `False` if not. - :param show_pos: enables or disables the absolute position display. The - default is `False`. - :param item_show_func: A function called with the current item which - can return a string to show next to the progress bar. If the - function returns ``None`` nothing is shown. The current item can - be ``None``, such as when entering and exiting the bar. - :param fill_char: the character to use to show the filled part of the - progress bar. - :param empty_char: the character to use to show the non-filled part of - the progress bar. - :param bar_template: the format string to use as template for the bar. - The parameters in it are ``label`` for the label, - ``bar`` for the progress bar and ``info`` for the - info section. - :param info_sep: the separator between multiple info items (eta etc.) - :param width: the width of the progress bar in characters, 0 means full - terminal width - :param file: The file to write to. If this is not a terminal then - only the label is printed. - :param color: controls if the terminal supports ANSI colors or not. The - default is autodetection. This is only needed if ANSI - codes are included anywhere in the progress bar output - which is not the case by default. - :param update_min_steps: Render only when this many updates have - completed. This allows tuning for very fast iterators. - - .. versionchanged:: 8.0 - Output is shown even if execution time is less than 0.5 seconds. - - .. versionchanged:: 8.0 - ``item_show_func`` shows the current item, not the previous one. - - .. versionchanged:: 8.0 - Labels are echoed if the output is not a TTY. Reverts a change - in 7.0 that removed all output. - - .. versionadded:: 8.0 - Added the ``update_min_steps`` parameter. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. Added the ``update`` method to - the object. - - .. versionadded:: 2.0 - """ - from ._termui_impl import ProgressBar - - color = resolve_color_default(color) - return ProgressBar( - iterable=iterable, - length=length, - show_eta=show_eta, - show_percent=show_percent, - show_pos=show_pos, - item_show_func=item_show_func, - fill_char=fill_char, - empty_char=empty_char, - bar_template=bar_template, - info_sep=info_sep, - file=file, - label=label, - width=width, - color=color, - update_min_steps=update_min_steps, - ) - - -def clear() -> None: - """Clears the terminal screen. This will have the effect of clearing - the whole visible space of the terminal and moving the cursor to the - top left. This does not do anything if not connected to a terminal. - - .. versionadded:: 2.0 - """ - if not isatty(sys.stdout): - return - - # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor - echo("\033[2J\033[1;1H", nl=False) - - -def _interpret_color( - color: t.Union[int, t.Tuple[int, int, int], str], offset: int = 0 -) -> str: - if isinstance(color, int): - return f"{38 + offset};5;{color:d}" - - if isinstance(color, (tuple, list)): - r, g, b = color - return f"{38 + offset};2;{r:d};{g:d};{b:d}" - - return str(_ansi_colors[color] + offset) - - -def style( - text: t.Any, - fg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, - bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, - bold: t.Optional[bool] = None, - dim: t.Optional[bool] = None, - underline: t.Optional[bool] = None, - overline: t.Optional[bool] = None, - italic: t.Optional[bool] = None, - blink: t.Optional[bool] = None, - reverse: t.Optional[bool] = None, - strikethrough: t.Optional[bool] = None, - reset: bool = True, -) -> str: - """Styles a text with ANSI styles and returns the new string. By - default the styling is self contained which means that at the end - of the string a reset code is issued. This can be prevented by - passing ``reset=False``. - - Examples:: - - click.echo(click.style('Hello World!', fg='green')) - click.echo(click.style('ATTENTION!', blink=True)) - click.echo(click.style('Some things', reverse=True, fg='cyan')) - click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) - - Supported color names: - - * ``black`` (might be a gray) - * ``red`` - * ``green`` - * ``yellow`` (might be an orange) - * ``blue`` - * ``magenta`` - * ``cyan`` - * ``white`` (might be light gray) - * ``bright_black`` - * ``bright_red`` - * ``bright_green`` - * ``bright_yellow`` - * ``bright_blue`` - * ``bright_magenta`` - * ``bright_cyan`` - * ``bright_white`` - * ``reset`` (reset the color code only) - - If the terminal supports it, color may also be specified as: - - - An integer in the interval [0, 255]. The terminal must support - 8-bit/256-color mode. - - An RGB tuple of three integers in [0, 255]. The terminal must - support 24-bit/true-color mode. - - See https://en.wikipedia.org/wiki/ANSI_color and - https://gist.github.com/XVilka/8346728 for more information. - - :param text: the string to style with ansi codes. - :param fg: if provided this will become the foreground color. - :param bg: if provided this will become the background color. - :param bold: if provided this will enable or disable bold mode. - :param dim: if provided this will enable or disable dim mode. This is - badly supported. - :param underline: if provided this will enable or disable underline. - :param overline: if provided this will enable or disable overline. - :param italic: if provided this will enable or disable italic. - :param blink: if provided this will enable or disable blinking. - :param reverse: if provided this will enable or disable inverse - rendering (foreground becomes background and the - other way round). - :param strikethrough: if provided this will enable or disable - striking through text. - :param reset: by default a reset-all code is added at the end of the - string which means that styles do not carry over. This - can be disabled to compose styles. - - .. versionchanged:: 8.0 - A non-string ``message`` is converted to a string. - - .. versionchanged:: 8.0 - Added support for 256 and RGB color codes. - - .. versionchanged:: 8.0 - Added the ``strikethrough``, ``italic``, and ``overline`` - parameters. - - .. versionchanged:: 7.0 - Added support for bright colors. - - .. versionadded:: 2.0 - """ - if not isinstance(text, str): - text = str(text) - - bits = [] - - if fg: - try: - bits.append(f"\033[{_interpret_color(fg)}m") - except KeyError: - raise TypeError(f"Unknown color {fg!r}") from None - - if bg: - try: - bits.append(f"\033[{_interpret_color(bg, 10)}m") - except KeyError: - raise TypeError(f"Unknown color {bg!r}") from None - - if bold is not None: - bits.append(f"\033[{1 if bold else 22}m") - if dim is not None: - bits.append(f"\033[{2 if dim else 22}m") - if underline is not None: - bits.append(f"\033[{4 if underline else 24}m") - if overline is not None: - bits.append(f"\033[{53 if overline else 55}m") - if italic is not None: - bits.append(f"\033[{3 if italic else 23}m") - if blink is not None: - bits.append(f"\033[{5 if blink else 25}m") - if reverse is not None: - bits.append(f"\033[{7 if reverse else 27}m") - if strikethrough is not None: - bits.append(f"\033[{9 if strikethrough else 29}m") - bits.append(text) - if reset: - bits.append(_ansi_reset_all) - return "".join(bits) - - -def unstyle(text: str) -> str: - """Removes ANSI styling information from a string. Usually it's not - necessary to use this function as Click's echo function will - automatically remove styling if necessary. - - .. versionadded:: 2.0 - - :param text: the text to remove style information from. - """ - return strip_ansi(text) - - -def secho( - message: t.Optional[t.Any] = None, - file: t.Optional[t.IO[t.AnyStr]] = None, - nl: bool = True, - err: bool = False, - color: t.Optional[bool] = None, - **styles: t.Any, -) -> None: - """This function combines :func:`echo` and :func:`style` into one - call. As such the following two calls are the same:: - - click.secho('Hello World!', fg='green') - click.echo(click.style('Hello World!', fg='green')) - - All keyword arguments are forwarded to the underlying functions - depending on which one they go with. - - Non-string types will be converted to :class:`str`. However, - :class:`bytes` are passed directly to :meth:`echo` without applying - style. If you want to style bytes that represent text, call - :meth:`bytes.decode` first. - - .. versionchanged:: 8.0 - A non-string ``message`` is converted to a string. Bytes are - passed through without style applied. - - .. versionadded:: 2.0 - """ - if message is not None and not isinstance(message, (bytes, bytearray)): - message = style(message, **styles) - - return echo(message, file=file, nl=nl, err=err, color=color) - - -def edit( - text: t.Optional[t.AnyStr] = None, - editor: t.Optional[str] = None, - env: t.Optional[t.Mapping[str, str]] = None, - require_save: bool = True, - extension: str = ".txt", - filename: t.Optional[str] = None, -) -> t.Optional[t.AnyStr]: - r"""Edits the given text in the defined editor. If an editor is given - (should be the full path to the executable but the regular operating - system search path is used for finding the executable) it overrides - the detected editor. Optionally, some environment variables can be - used. If the editor is closed without changes, `None` is returned. In - case a file is edited directly the return value is always `None` and - `require_save` and `extension` are ignored. - - If the editor cannot be opened a :exc:`UsageError` is raised. - - Note for Windows: to simplify cross-platform usage, the newlines are - automatically converted from POSIX to Windows and vice versa. As such, - the message here will have ``\n`` as newline markers. - - :param text: the text to edit. - :param editor: optionally the editor to use. Defaults to automatic - detection. - :param env: environment variables to forward to the editor. - :param require_save: if this is true, then not saving in the editor - will make the return value become `None`. - :param extension: the extension to tell the editor about. This defaults - to `.txt` but changing this might change syntax - highlighting. - :param filename: if provided it will edit this file instead of the - provided text contents. It will not use a temporary - file as an indirection in that case. - """ - from ._termui_impl import Editor - - ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) - - if filename is None: - return ed.edit(text) - - ed.edit_file(filename) - return None - - -def launch(url: str, wait: bool = False, locate: bool = False) -> int: - """This function launches the given URL (or filename) in the default - viewer application for this file type. If this is an executable, it - might launch the executable in a new session. The return value is - the exit code of the launched application. Usually, ``0`` indicates - success. - - Examples:: - - click.launch('https://click.palletsprojects.com/') - click.launch('/my/downloaded/file', locate=True) - - .. versionadded:: 2.0 - - :param url: URL or filename of the thing to launch. - :param wait: Wait for the program to exit before returning. This - only works if the launched program blocks. In particular, - ``xdg-open`` on Linux does not block. - :param locate: if this is set to `True` then instead of launching the - application associated with the URL it will attempt to - launch a file manager with the file located. This - might have weird effects if the URL does not point to - the filesystem. - """ - from ._termui_impl import open_url - - return open_url(url, wait=wait, locate=locate) - - -# If this is provided, getchar() calls into this instead. This is used -# for unittesting purposes. -_getchar: t.Optional[t.Callable[[bool], str]] = None - - -def getchar(echo: bool = False) -> str: - """Fetches a single character from the terminal and returns it. This - will always return a unicode character and under certain rare - circumstances this might return more than one character. The - situations which more than one character is returned is when for - whatever reason multiple characters end up in the terminal buffer or - standard input was not actually a terminal. - - Note that this will always read from the terminal, even if something - is piped into the standard input. - - Note for Windows: in rare cases when typing non-ASCII characters, this - function might wait for a second character and then return both at once. - This is because certain Unicode characters look like special-key markers. - - .. versionadded:: 2.0 - - :param echo: if set to `True`, the character read will also show up on - the terminal. The default is to not show it. - """ - global _getchar - - if _getchar is None: - from ._termui_impl import getchar as f - - _getchar = f - - return _getchar(echo) - - -def raw_terminal() -> t.ContextManager[int]: - from ._termui_impl import raw_terminal as f - - return f() - - -def pause(info: t.Optional[str] = None, err: bool = False) -> None: - """This command stops execution and waits for the user to press any - key to continue. This is similar to the Windows batch "pause" - command. If the program is not run through a terminal, this command - will instead do nothing. - - .. versionadded:: 2.0 - - .. versionadded:: 4.0 - Added the `err` parameter. - - :param info: The message to print before pausing. Defaults to - ``"Press any key to continue..."``. - :param err: if set to message goes to ``stderr`` instead of - ``stdout``, the same as with echo. - """ - if not isatty(sys.stdin) or not isatty(sys.stdout): - return - - if info is None: - info = _("Press any key to continue...") - - try: - if info: - echo(info, nl=False, err=err) - try: - getchar() - except (KeyboardInterrupt, EOFError): - pass - finally: - if info: - echo(err=err) diff --git a/venv/Lib/site-packages/click/testing.py b/venv/Lib/site-packages/click/testing.py deleted file mode 100644 index 772b215..0000000 --- a/venv/Lib/site-packages/click/testing.py +++ /dev/null @@ -1,483 +0,0 @@ -import contextlib -import io -import os -import shlex -import shutil -import sys -import tempfile -import typing as t -from types import TracebackType - -from . import _compat -from . import formatting -from . import termui -from . import utils -from ._compat import _find_binary_reader - -if t.TYPE_CHECKING: - from .core import BaseCommand - - -class EchoingStdin: - def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: - self._input = input - self._output = output - self._paused = False - - def __getattr__(self, x: str) -> t.Any: - return getattr(self._input, x) - - def _echo(self, rv: bytes) -> bytes: - if not self._paused: - self._output.write(rv) - - return rv - - def read(self, n: int = -1) -> bytes: - return self._echo(self._input.read(n)) - - def read1(self, n: int = -1) -> bytes: - return self._echo(self._input.read1(n)) # type: ignore - - def readline(self, n: int = -1) -> bytes: - return self._echo(self._input.readline(n)) - - def readlines(self) -> t.List[bytes]: - return [self._echo(x) for x in self._input.readlines()] - - def __iter__(self) -> t.Iterator[bytes]: - return iter(self._echo(x) for x in self._input) - - def __repr__(self) -> str: - return repr(self._input) - - -@contextlib.contextmanager -def _pause_echo(stream: t.Optional[EchoingStdin]) -> t.Iterator[None]: - if stream is None: - yield - else: - stream._paused = True - yield - stream._paused = False - - -class _NamedTextIOWrapper(io.TextIOWrapper): - def __init__( - self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any - ) -> None: - super().__init__(buffer, **kwargs) - self._name = name - self._mode = mode - - @property - def name(self) -> str: - return self._name - - @property - def mode(self) -> str: - return self._mode - - -def make_input_stream( - input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]], charset: str -) -> t.BinaryIO: - # Is already an input stream. - if hasattr(input, "read"): - rv = _find_binary_reader(t.cast(t.IO[t.Any], input)) - - if rv is not None: - return rv - - raise TypeError("Could not find binary reader for input stream.") - - if input is None: - input = b"" - elif isinstance(input, str): - input = input.encode(charset) - - return io.BytesIO(input) - - -class Result: - """Holds the captured result of an invoked CLI script.""" - - def __init__( - self, - runner: "CliRunner", - stdout_bytes: bytes, - stderr_bytes: t.Optional[bytes], - return_value: t.Any, - exit_code: int, - exception: t.Optional[BaseException], - exc_info: t.Optional[ - t.Tuple[t.Type[BaseException], BaseException, TracebackType] - ] = None, - ): - #: The runner that created the result - self.runner = runner - #: The standard output as bytes. - self.stdout_bytes = stdout_bytes - #: The standard error as bytes, or None if not available - self.stderr_bytes = stderr_bytes - #: The value returned from the invoked command. - #: - #: .. versionadded:: 8.0 - self.return_value = return_value - #: The exit code as integer. - self.exit_code = exit_code - #: The exception that happened if one did. - self.exception = exception - #: The traceback - self.exc_info = exc_info - - @property - def output(self) -> str: - """The (standard) output as unicode string.""" - return self.stdout - - @property - def stdout(self) -> str: - """The standard output as unicode string.""" - return self.stdout_bytes.decode(self.runner.charset, "replace").replace( - "\r\n", "\n" - ) - - @property - def stderr(self) -> str: - """The standard error as unicode string.""" - if self.stderr_bytes is None: - raise ValueError("stderr not separately captured") - return self.stderr_bytes.decode(self.runner.charset, "replace").replace( - "\r\n", "\n" - ) - - def __repr__(self) -> str: - exc_str = repr(self.exception) if self.exception else "okay" - return f"<{type(self).__name__} {exc_str}>" - - -class CliRunner: - """The CLI runner provides functionality to invoke a Click command line - script for unittesting purposes in a isolated environment. This only - works in single-threaded systems without any concurrency as it changes the - global interpreter state. - - :param charset: the character set for the input and output data. - :param env: a dictionary with environment variables for overriding. - :param echo_stdin: if this is set to `True`, then reading from stdin writes - to stdout. This is useful for showing examples in - some circumstances. Note that regular prompts - will automatically echo the input. - :param mix_stderr: if this is set to `False`, then stdout and stderr are - preserved as independent streams. This is useful for - Unix-philosophy apps that have predictable stdout and - noisy stderr, such that each may be measured - independently - """ - - def __init__( - self, - charset: str = "utf-8", - env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, - echo_stdin: bool = False, - mix_stderr: bool = True, - ) -> None: - self.charset = charset - self.env: t.Mapping[str, t.Optional[str]] = env or {} - self.echo_stdin = echo_stdin - self.mix_stderr = mix_stderr - - def get_default_prog_name(self, cli: "BaseCommand") -> str: - """Given a command object it will return the default program name - for it. The default is the `name` attribute or ``"root"`` if not - set. - """ - return cli.name or "root" - - def make_env( - self, overrides: t.Optional[t.Mapping[str, t.Optional[str]]] = None - ) -> t.Mapping[str, t.Optional[str]]: - """Returns the environment overrides for invoking a script.""" - rv = dict(self.env) - if overrides: - rv.update(overrides) - return rv - - @contextlib.contextmanager - def isolation( - self, - input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, - env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, - color: bool = False, - ) -> t.Iterator[t.Tuple[io.BytesIO, t.Optional[io.BytesIO]]]: - """A context manager that sets up the isolation for invoking of a - command line tool. This sets up stdin with the given input data - and `os.environ` with the overrides from the given dictionary. - This also rebinds some internals in Click to be mocked (like the - prompt functionality). - - This is automatically done in the :meth:`invoke` method. - - :param input: the input stream to put into sys.stdin. - :param env: the environment overrides as dictionary. - :param color: whether the output should contain color codes. The - application can still override this explicitly. - - .. versionchanged:: 8.0 - ``stderr`` is opened with ``errors="backslashreplace"`` - instead of the default ``"strict"``. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. - """ - bytes_input = make_input_stream(input, self.charset) - echo_input = None - - old_stdin = sys.stdin - old_stdout = sys.stdout - old_stderr = sys.stderr - old_forced_width = formatting.FORCED_WIDTH - formatting.FORCED_WIDTH = 80 - - env = self.make_env(env) - - bytes_output = io.BytesIO() - - if self.echo_stdin: - bytes_input = echo_input = t.cast( - t.BinaryIO, EchoingStdin(bytes_input, bytes_output) - ) - - sys.stdin = text_input = _NamedTextIOWrapper( - bytes_input, encoding=self.charset, name="", mode="r" - ) - - if self.echo_stdin: - # Force unbuffered reads, otherwise TextIOWrapper reads a - # large chunk which is echoed early. - text_input._CHUNK_SIZE = 1 # type: ignore - - sys.stdout = _NamedTextIOWrapper( - bytes_output, encoding=self.charset, name="", mode="w" - ) - - bytes_error = None - if self.mix_stderr: - sys.stderr = sys.stdout - else: - bytes_error = io.BytesIO() - sys.stderr = _NamedTextIOWrapper( - bytes_error, - encoding=self.charset, - name="", - mode="w", - errors="backslashreplace", - ) - - @_pause_echo(echo_input) # type: ignore - def visible_input(prompt: t.Optional[str] = None) -> str: - sys.stdout.write(prompt or "") - val = text_input.readline().rstrip("\r\n") - sys.stdout.write(f"{val}\n") - sys.stdout.flush() - return val - - @_pause_echo(echo_input) # type: ignore - def hidden_input(prompt: t.Optional[str] = None) -> str: - sys.stdout.write(f"{prompt or ''}\n") - sys.stdout.flush() - return text_input.readline().rstrip("\r\n") - - @_pause_echo(echo_input) # type: ignore - def _getchar(echo: bool) -> str: - char = sys.stdin.read(1) - - if echo: - sys.stdout.write(char) - - sys.stdout.flush() - return char - - default_color = color - - def should_strip_ansi( - stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None - ) -> bool: - if color is None: - return not default_color - return not color - - old_visible_prompt_func = termui.visible_prompt_func - old_hidden_prompt_func = termui.hidden_prompt_func - old__getchar_func = termui._getchar - old_should_strip_ansi = utils.should_strip_ansi # type: ignore - old__compat_should_strip_ansi = _compat.should_strip_ansi - termui.visible_prompt_func = visible_input - termui.hidden_prompt_func = hidden_input - termui._getchar = _getchar - utils.should_strip_ansi = should_strip_ansi # type: ignore - _compat.should_strip_ansi = should_strip_ansi - - old_env = {} - try: - for key, value in env.items(): - old_env[key] = os.environ.get(key) - if value is None: - try: - del os.environ[key] - except Exception: - pass - else: - os.environ[key] = value - yield (bytes_output, bytes_error) - finally: - for key, value in old_env.items(): - if value is None: - try: - del os.environ[key] - except Exception: - pass - else: - os.environ[key] = value - sys.stdout = old_stdout - sys.stderr = old_stderr - sys.stdin = old_stdin - termui.visible_prompt_func = old_visible_prompt_func - termui.hidden_prompt_func = old_hidden_prompt_func - termui._getchar = old__getchar_func - utils.should_strip_ansi = old_should_strip_ansi # type: ignore - _compat.should_strip_ansi = old__compat_should_strip_ansi - formatting.FORCED_WIDTH = old_forced_width - - def invoke( - self, - cli: "BaseCommand", - args: t.Optional[t.Union[str, t.Sequence[str]]] = None, - input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, - env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, - catch_exceptions: bool = True, - color: bool = False, - **extra: t.Any, - ) -> Result: - """Invokes a command in an isolated environment. The arguments are - forwarded directly to the command line script, the `extra` keyword - arguments are passed to the :meth:`~clickpkg.Command.main` function of - the command. - - This returns a :class:`Result` object. - - :param cli: the command to invoke - :param args: the arguments to invoke. It may be given as an iterable - or a string. When given as string it will be interpreted - as a Unix shell command. More details at - :func:`shlex.split`. - :param input: the input data for `sys.stdin`. - :param env: the environment overrides. - :param catch_exceptions: Whether to catch any other exceptions than - ``SystemExit``. - :param extra: the keyword arguments to pass to :meth:`main`. - :param color: whether the output should contain color codes. The - application can still override this explicitly. - - .. versionchanged:: 8.0 - The result object has the ``return_value`` attribute with - the value returned from the invoked command. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. - - .. versionchanged:: 3.0 - Added the ``catch_exceptions`` parameter. - - .. versionchanged:: 3.0 - The result object has the ``exc_info`` attribute with the - traceback if available. - """ - exc_info = None - with self.isolation(input=input, env=env, color=color) as outstreams: - return_value = None - exception: t.Optional[BaseException] = None - exit_code = 0 - - if isinstance(args, str): - args = shlex.split(args) - - try: - prog_name = extra.pop("prog_name") - except KeyError: - prog_name = self.get_default_prog_name(cli) - - try: - return_value = cli.main(args=args or (), prog_name=prog_name, **extra) - except SystemExit as e: - exc_info = sys.exc_info() - e_code = t.cast(t.Optional[t.Union[int, t.Any]], e.code) - - if e_code is None: - e_code = 0 - - if e_code != 0: - exception = e - - if not isinstance(e_code, int): - sys.stdout.write(str(e_code)) - sys.stdout.write("\n") - e_code = 1 - - exit_code = e_code - - except Exception as e: - if not catch_exceptions: - raise - exception = e - exit_code = 1 - exc_info = sys.exc_info() - finally: - sys.stdout.flush() - stdout = outstreams[0].getvalue() - if self.mix_stderr: - stderr = None - else: - stderr = outstreams[1].getvalue() # type: ignore - - return Result( - runner=self, - stdout_bytes=stdout, - stderr_bytes=stderr, - return_value=return_value, - exit_code=exit_code, - exception=exception, - exc_info=exc_info, # type: ignore - ) - - @contextlib.contextmanager - def isolated_filesystem( - self, temp_dir: t.Optional[t.Union[str, "os.PathLike[str]"]] = None - ) -> t.Iterator[str]: - """A context manager that creates a temporary directory and - changes the current working directory to it. This isolates tests - that affect the contents of the CWD to prevent them from - interfering with each other. - - :param temp_dir: Create the temporary directory under this - directory. If given, the created directory is not removed - when exiting. - - .. versionchanged:: 8.0 - Added the ``temp_dir`` parameter. - """ - cwd = os.getcwd() - dt = tempfile.mkdtemp(dir=temp_dir) - os.chdir(dt) - - try: - yield dt - finally: - os.chdir(cwd) - - if temp_dir is None: - try: - shutil.rmtree(dt) - except OSError: - pass diff --git a/venv/Lib/site-packages/click/types.py b/venv/Lib/site-packages/click/types.py deleted file mode 100644 index a70fd58..0000000 --- a/venv/Lib/site-packages/click/types.py +++ /dev/null @@ -1,1093 +0,0 @@ -import os -import stat -import sys -import typing as t -from datetime import datetime -from gettext import gettext as _ -from gettext import ngettext - -from ._compat import _get_argv_encoding -from ._compat import open_stream -from .exceptions import BadParameter -from .utils import format_filename -from .utils import LazyFile -from .utils import safecall - -if t.TYPE_CHECKING: - import typing_extensions as te - - from .core import Context - from .core import Parameter - from .shell_completion import CompletionItem - - -class ParamType: - """Represents the type of a parameter. Validates and converts values - from the command line or Python into the correct type. - - To implement a custom type, subclass and implement at least the - following: - - - The :attr:`name` class attribute must be set. - - Calling an instance of the type with ``None`` must return - ``None``. This is already implemented by default. - - :meth:`convert` must convert string values to the correct type. - - :meth:`convert` must accept values that are already the correct - type. - - It must be able to convert a value if the ``ctx`` and ``param`` - arguments are ``None``. This can occur when converting prompt - input. - """ - - is_composite: t.ClassVar[bool] = False - arity: t.ClassVar[int] = 1 - - #: the descriptive name of this type - name: str - - #: if a list of this type is expected and the value is pulled from a - #: string environment variable, this is what splits it up. `None` - #: means any whitespace. For all parameters the general rule is that - #: whitespace splits them up. The exception are paths and files which - #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on - #: Windows). - envvar_list_splitter: t.ClassVar[t.Optional[str]] = None - - def to_info_dict(self) -> t.Dict[str, t.Any]: - """Gather information that could be useful for a tool generating - user-facing documentation. - - Use :meth:`click.Context.to_info_dict` to traverse the entire - CLI structure. - - .. versionadded:: 8.0 - """ - # The class name without the "ParamType" suffix. - param_type = type(self).__name__.partition("ParamType")[0] - param_type = param_type.partition("ParameterType")[0] - - # Custom subclasses might not remember to set a name. - if hasattr(self, "name"): - name = self.name - else: - name = param_type - - return {"param_type": param_type, "name": name} - - def __call__( - self, - value: t.Any, - param: t.Optional["Parameter"] = None, - ctx: t.Optional["Context"] = None, - ) -> t.Any: - if value is not None: - return self.convert(value, param, ctx) - - def get_metavar(self, param: "Parameter") -> t.Optional[str]: - """Returns the metavar default for this param if it provides one.""" - - def get_missing_message(self, param: "Parameter") -> t.Optional[str]: - """Optionally might return extra information about a missing - parameter. - - .. versionadded:: 2.0 - """ - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - """Convert the value to the correct type. This is not called if - the value is ``None`` (the missing value). - - This must accept string values from the command line, as well as - values that are already the correct type. It may also convert - other compatible types. - - The ``param`` and ``ctx`` arguments may be ``None`` in certain - situations, such as when converting prompt input. - - If the value cannot be converted, call :meth:`fail` with a - descriptive message. - - :param value: The value to convert. - :param param: The parameter that is using this type to convert - its value. May be ``None``. - :param ctx: The current context that arrived at this value. May - be ``None``. - """ - return value - - def split_envvar_value(self, rv: str) -> t.Sequence[str]: - """Given a value from an environment variable this splits it up - into small chunks depending on the defined envvar list splitter. - - If the splitter is set to `None`, which means that whitespace splits, - then leading and trailing whitespace is ignored. Otherwise, leading - and trailing splitters usually lead to empty items being included. - """ - return (rv or "").split(self.envvar_list_splitter) - - def fail( - self, - message: str, - param: t.Optional["Parameter"] = None, - ctx: t.Optional["Context"] = None, - ) -> "t.NoReturn": - """Helper method to fail with an invalid value message.""" - raise BadParameter(message, ctx=ctx, param=param) - - def shell_complete( - self, ctx: "Context", param: "Parameter", incomplete: str - ) -> t.List["CompletionItem"]: - """Return a list of - :class:`~click.shell_completion.CompletionItem` objects for the - incomplete value. Most types do not provide completions, but - some do, and this allows custom types to provide custom - completions as well. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - return [] - - -class CompositeParamType(ParamType): - is_composite = True - - @property - def arity(self) -> int: # type: ignore - raise NotImplementedError() - - -class FuncParamType(ParamType): - def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: - self.name: str = func.__name__ - self.func = func - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict["func"] = self.func - return info_dict - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - try: - return self.func(value) - except ValueError: - try: - value = str(value) - except UnicodeError: - value = value.decode("utf-8", "replace") - - self.fail(value, param, ctx) - - -class UnprocessedParamType(ParamType): - name = "text" - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - return value - - def __repr__(self) -> str: - return "UNPROCESSED" - - -class StringParamType(ParamType): - name = "text" - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - if isinstance(value, bytes): - enc = _get_argv_encoding() - try: - value = value.decode(enc) - except UnicodeError: - fs_enc = sys.getfilesystemencoding() - if fs_enc != enc: - try: - value = value.decode(fs_enc) - except UnicodeError: - value = value.decode("utf-8", "replace") - else: - value = value.decode("utf-8", "replace") - return value - return str(value) - - def __repr__(self) -> str: - return "STRING" - - -class Choice(ParamType): - """The choice type allows a value to be checked against a fixed set - of supported values. All of these values have to be strings. - - You should only pass a list or tuple of choices. Other iterables - (like generators) may lead to surprising results. - - The resulting value will always be one of the originally passed choices - regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` - being specified. - - See :ref:`choice-opts` for an example. - - :param case_sensitive: Set to false to make choices case - insensitive. Defaults to true. - """ - - name = "choice" - - def __init__(self, choices: t.Sequence[str], case_sensitive: bool = True) -> None: - self.choices = choices - self.case_sensitive = case_sensitive - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict["choices"] = self.choices - info_dict["case_sensitive"] = self.case_sensitive - return info_dict - - def get_metavar(self, param: "Parameter") -> str: - choices_str = "|".join(self.choices) - - # Use curly braces to indicate a required argument. - if param.required and param.param_type_name == "argument": - return f"{{{choices_str}}}" - - # Use square braces to indicate an option or optional argument. - return f"[{choices_str}]" - - def get_missing_message(self, param: "Parameter") -> str: - return _("Choose from:\n\t{choices}").format(choices=",\n\t".join(self.choices)) - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - # Match through normalization and case sensitivity - # first do token_normalize_func, then lowercase - # preserve original `value` to produce an accurate message in - # `self.fail` - normed_value = value - normed_choices = {choice: choice for choice in self.choices} - - if ctx is not None and ctx.token_normalize_func is not None: - normed_value = ctx.token_normalize_func(value) - normed_choices = { - ctx.token_normalize_func(normed_choice): original - for normed_choice, original in normed_choices.items() - } - - if not self.case_sensitive: - normed_value = normed_value.casefold() - normed_choices = { - normed_choice.casefold(): original - for normed_choice, original in normed_choices.items() - } - - if normed_value in normed_choices: - return normed_choices[normed_value] - - choices_str = ", ".join(map(repr, self.choices)) - self.fail( - ngettext( - "{value!r} is not {choice}.", - "{value!r} is not one of {choices}.", - len(self.choices), - ).format(value=value, choice=choices_str, choices=choices_str), - param, - ctx, - ) - - def __repr__(self) -> str: - return f"Choice({list(self.choices)})" - - def shell_complete( - self, ctx: "Context", param: "Parameter", incomplete: str - ) -> t.List["CompletionItem"]: - """Complete choices that start with the incomplete value. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - str_choices = map(str, self.choices) - - if self.case_sensitive: - matched = (c for c in str_choices if c.startswith(incomplete)) - else: - incomplete = incomplete.lower() - matched = (c for c in str_choices if c.lower().startswith(incomplete)) - - return [CompletionItem(c) for c in matched] - - -class DateTime(ParamType): - """The DateTime type converts date strings into `datetime` objects. - - The format strings which are checked are configurable, but default to some - common (non-timezone aware) ISO 8601 formats. - - When specifying *DateTime* formats, you should only pass a list or a tuple. - Other iterables, like generators, may lead to surprising results. - - The format strings are processed using ``datetime.strptime``, and this - consequently defines the format strings which are allowed. - - Parsing is tried using each format, in order, and the first format which - parses successfully is used. - - :param formats: A list or tuple of date format strings, in the order in - which they should be tried. Defaults to - ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, - ``'%Y-%m-%d %H:%M:%S'``. - """ - - name = "datetime" - - def __init__(self, formats: t.Optional[t.Sequence[str]] = None): - self.formats: t.Sequence[str] = formats or [ - "%Y-%m-%d", - "%Y-%m-%dT%H:%M:%S", - "%Y-%m-%d %H:%M:%S", - ] - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict["formats"] = self.formats - return info_dict - - def get_metavar(self, param: "Parameter") -> str: - return f"[{'|'.join(self.formats)}]" - - def _try_to_convert_date(self, value: t.Any, format: str) -> t.Optional[datetime]: - try: - return datetime.strptime(value, format) - except ValueError: - return None - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - if isinstance(value, datetime): - return value - - for format in self.formats: - converted = self._try_to_convert_date(value, format) - - if converted is not None: - return converted - - formats_str = ", ".join(map(repr, self.formats)) - self.fail( - ngettext( - "{value!r} does not match the format {format}.", - "{value!r} does not match the formats {formats}.", - len(self.formats), - ).format(value=value, format=formats_str, formats=formats_str), - param, - ctx, - ) - - def __repr__(self) -> str: - return "DateTime" - - -class _NumberParamTypeBase(ParamType): - _number_class: t.ClassVar[t.Type[t.Any]] - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - try: - return self._number_class(value) - except ValueError: - self.fail( - _("{value!r} is not a valid {number_type}.").format( - value=value, number_type=self.name - ), - param, - ctx, - ) - - -class _NumberRangeBase(_NumberParamTypeBase): - def __init__( - self, - min: t.Optional[float] = None, - max: t.Optional[float] = None, - min_open: bool = False, - max_open: bool = False, - clamp: bool = False, - ) -> None: - self.min = min - self.max = max - self.min_open = min_open - self.max_open = max_open - self.clamp = clamp - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict.update( - min=self.min, - max=self.max, - min_open=self.min_open, - max_open=self.max_open, - clamp=self.clamp, - ) - return info_dict - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - import operator - - rv = super().convert(value, param, ctx) - lt_min: bool = self.min is not None and ( - operator.le if self.min_open else operator.lt - )(rv, self.min) - gt_max: bool = self.max is not None and ( - operator.ge if self.max_open else operator.gt - )(rv, self.max) - - if self.clamp: - if lt_min: - return self._clamp(self.min, 1, self.min_open) # type: ignore - - if gt_max: - return self._clamp(self.max, -1, self.max_open) # type: ignore - - if lt_min or gt_max: - self.fail( - _("{value} is not in the range {range}.").format( - value=rv, range=self._describe_range() - ), - param, - ctx, - ) - - return rv - - def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: - """Find the valid value to clamp to bound in the given - direction. - - :param bound: The boundary value. - :param dir: 1 or -1 indicating the direction to move. - :param open: If true, the range does not include the bound. - """ - raise NotImplementedError - - def _describe_range(self) -> str: - """Describe the range for use in help text.""" - if self.min is None: - op = "<" if self.max_open else "<=" - return f"x{op}{self.max}" - - if self.max is None: - op = ">" if self.min_open else ">=" - return f"x{op}{self.min}" - - lop = "<" if self.min_open else "<=" - rop = "<" if self.max_open else "<=" - return f"{self.min}{lop}x{rop}{self.max}" - - def __repr__(self) -> str: - clamp = " clamped" if self.clamp else "" - return f"<{type(self).__name__} {self._describe_range()}{clamp}>" - - -class IntParamType(_NumberParamTypeBase): - name = "integer" - _number_class = int - - def __repr__(self) -> str: - return "INT" - - -class IntRange(_NumberRangeBase, IntParamType): - """Restrict an :data:`click.INT` value to a range of accepted - values. See :ref:`ranges`. - - If ``min`` or ``max`` are not passed, any value is accepted in that - direction. If ``min_open`` or ``max_open`` are enabled, the - corresponding boundary is not included in the range. - - If ``clamp`` is enabled, a value outside the range is clamped to the - boundary instead of failing. - - .. versionchanged:: 8.0 - Added the ``min_open`` and ``max_open`` parameters. - """ - - name = "integer range" - - def _clamp( # type: ignore - self, bound: int, dir: "te.Literal[1, -1]", open: bool - ) -> int: - if not open: - return bound - - return bound + dir - - -class FloatParamType(_NumberParamTypeBase): - name = "float" - _number_class = float - - def __repr__(self) -> str: - return "FLOAT" - - -class FloatRange(_NumberRangeBase, FloatParamType): - """Restrict a :data:`click.FLOAT` value to a range of accepted - values. See :ref:`ranges`. - - If ``min`` or ``max`` are not passed, any value is accepted in that - direction. If ``min_open`` or ``max_open`` are enabled, the - corresponding boundary is not included in the range. - - If ``clamp`` is enabled, a value outside the range is clamped to the - boundary instead of failing. This is not supported if either - boundary is marked ``open``. - - .. versionchanged:: 8.0 - Added the ``min_open`` and ``max_open`` parameters. - """ - - name = "float range" - - def __init__( - self, - min: t.Optional[float] = None, - max: t.Optional[float] = None, - min_open: bool = False, - max_open: bool = False, - clamp: bool = False, - ) -> None: - super().__init__( - min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp - ) - - if (min_open or max_open) and clamp: - raise TypeError("Clamping is not supported for open bounds.") - - def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: - if not open: - return bound - - # Could use Python 3.9's math.nextafter here, but clamping an - # open float range doesn't seem to be particularly useful. It's - # left up to the user to write a callback to do it if needed. - raise RuntimeError("Clamping is not supported for open bounds.") - - -class BoolParamType(ParamType): - name = "boolean" - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - if value in {False, True}: - return bool(value) - - norm = value.strip().lower() - - if norm in {"1", "true", "t", "yes", "y", "on"}: - return True - - if norm in {"0", "false", "f", "no", "n", "off"}: - return False - - self.fail( - _("{value!r} is not a valid boolean.").format(value=value), param, ctx - ) - - def __repr__(self) -> str: - return "BOOL" - - -class UUIDParameterType(ParamType): - name = "uuid" - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - import uuid - - if isinstance(value, uuid.UUID): - return value - - value = value.strip() - - try: - return uuid.UUID(value) - except ValueError: - self.fail( - _("{value!r} is not a valid UUID.").format(value=value), param, ctx - ) - - def __repr__(self) -> str: - return "UUID" - - -class File(ParamType): - """Declares a parameter to be a file for reading or writing. The file - is automatically closed once the context tears down (after the command - finished working). - - Files can be opened for reading or writing. The special value ``-`` - indicates stdin or stdout depending on the mode. - - By default, the file is opened for reading text data, but it can also be - opened in binary mode or for writing. The encoding parameter can be used - to force a specific encoding. - - The `lazy` flag controls if the file should be opened immediately or upon - first IO. The default is to be non-lazy for standard input and output - streams as well as files opened for reading, `lazy` otherwise. When opening a - file lazily for reading, it is still opened temporarily for validation, but - will not be held open until first IO. lazy is mainly useful when opening - for writing to avoid creating the file until it is needed. - - Files can also be opened atomically in which case all writes go into a - separate file in the same folder and upon completion the file will - be moved over to the original location. This is useful if a file - regularly read by other users is modified. - - See :ref:`file-args` for more information. - - .. versionchanged:: 2.0 - Added the ``atomic`` parameter. - """ - - name = "filename" - envvar_list_splitter: t.ClassVar[str] = os.path.pathsep - - def __init__( - self, - mode: str = "r", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", - lazy: t.Optional[bool] = None, - atomic: bool = False, - ) -> None: - self.mode = mode - self.encoding = encoding - self.errors = errors - self.lazy = lazy - self.atomic = atomic - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict.update(mode=self.mode, encoding=self.encoding) - return info_dict - - def resolve_lazy_flag(self, value: "t.Union[str, os.PathLike[str]]") -> bool: - if self.lazy is not None: - return self.lazy - if os.fspath(value) == "-": - return False - elif "w" in self.mode: - return True - return False - - def convert( - self, - value: t.Union[str, "os.PathLike[str]", t.IO[t.Any]], - param: t.Optional["Parameter"], - ctx: t.Optional["Context"], - ) -> t.IO[t.Any]: - if _is_file_like(value): - return value - - value = t.cast("t.Union[str, os.PathLike[str]]", value) - - try: - lazy = self.resolve_lazy_flag(value) - - if lazy: - lf = LazyFile( - value, self.mode, self.encoding, self.errors, atomic=self.atomic - ) - - if ctx is not None: - ctx.call_on_close(lf.close_intelligently) - - return t.cast(t.IO[t.Any], lf) - - f, should_close = open_stream( - value, self.mode, self.encoding, self.errors, atomic=self.atomic - ) - - # If a context is provided, we automatically close the file - # at the end of the context execution (or flush out). If a - # context does not exist, it's the caller's responsibility to - # properly close the file. This for instance happens when the - # type is used with prompts. - if ctx is not None: - if should_close: - ctx.call_on_close(safecall(f.close)) - else: - ctx.call_on_close(safecall(f.flush)) - - return f - except OSError as e: - self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx) - - def shell_complete( - self, ctx: "Context", param: "Parameter", incomplete: str - ) -> t.List["CompletionItem"]: - """Return a special completion marker that tells the completion - system to use the shell to provide file path completions. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - return [CompletionItem(incomplete, type="file")] - - -def _is_file_like(value: t.Any) -> "te.TypeGuard[t.IO[t.Any]]": - return hasattr(value, "read") or hasattr(value, "write") - - -class Path(ParamType): - """The ``Path`` type is similar to the :class:`File` type, but - returns the filename instead of an open file. Various checks can be - enabled to validate the type of file and permissions. - - :param exists: The file or directory needs to exist for the value to - be valid. If this is not set to ``True``, and the file does not - exist, then all further checks are silently skipped. - :param file_okay: Allow a file as a value. - :param dir_okay: Allow a directory as a value. - :param readable: if true, a readable check is performed. - :param writable: if true, a writable check is performed. - :param executable: if true, an executable check is performed. - :param resolve_path: Make the value absolute and resolve any - symlinks. A ``~`` is not expanded, as this is supposed to be - done by the shell only. - :param allow_dash: Allow a single dash as a value, which indicates - a standard stream (but does not open it). Use - :func:`~click.open_file` to handle opening this value. - :param path_type: Convert the incoming path value to this type. If - ``None``, keep Python's default, which is ``str``. Useful to - convert to :class:`pathlib.Path`. - - .. versionchanged:: 8.1 - Added the ``executable`` parameter. - - .. versionchanged:: 8.0 - Allow passing ``path_type=pathlib.Path``. - - .. versionchanged:: 6.0 - Added the ``allow_dash`` parameter. - """ - - envvar_list_splitter: t.ClassVar[str] = os.path.pathsep - - def __init__( - self, - exists: bool = False, - file_okay: bool = True, - dir_okay: bool = True, - writable: bool = False, - readable: bool = True, - resolve_path: bool = False, - allow_dash: bool = False, - path_type: t.Optional[t.Type[t.Any]] = None, - executable: bool = False, - ): - self.exists = exists - self.file_okay = file_okay - self.dir_okay = dir_okay - self.readable = readable - self.writable = writable - self.executable = executable - self.resolve_path = resolve_path - self.allow_dash = allow_dash - self.type = path_type - - if self.file_okay and not self.dir_okay: - self.name: str = _("file") - elif self.dir_okay and not self.file_okay: - self.name = _("directory") - else: - self.name = _("path") - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict.update( - exists=self.exists, - file_okay=self.file_okay, - dir_okay=self.dir_okay, - writable=self.writable, - readable=self.readable, - allow_dash=self.allow_dash, - ) - return info_dict - - def coerce_path_result( - self, value: "t.Union[str, os.PathLike[str]]" - ) -> "t.Union[str, bytes, os.PathLike[str]]": - if self.type is not None and not isinstance(value, self.type): - if self.type is str: - return os.fsdecode(value) - elif self.type is bytes: - return os.fsencode(value) - else: - return t.cast("os.PathLike[str]", self.type(value)) - - return value - - def convert( - self, - value: "t.Union[str, os.PathLike[str]]", - param: t.Optional["Parameter"], - ctx: t.Optional["Context"], - ) -> "t.Union[str, bytes, os.PathLike[str]]": - rv = value - - is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") - - if not is_dash: - if self.resolve_path: - # os.path.realpath doesn't resolve symlinks on Windows - # until Python 3.8. Use pathlib for now. - import pathlib - - rv = os.fsdecode(pathlib.Path(rv).resolve()) - - try: - st = os.stat(rv) - except OSError: - if not self.exists: - return self.coerce_path_result(rv) - self.fail( - _("{name} {filename!r} does not exist.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if not self.file_okay and stat.S_ISREG(st.st_mode): - self.fail( - _("{name} {filename!r} is a file.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - if not self.dir_okay and stat.S_ISDIR(st.st_mode): - self.fail( - _("{name} {filename!r} is a directory.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if self.readable and not os.access(rv, os.R_OK): - self.fail( - _("{name} {filename!r} is not readable.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if self.writable and not os.access(rv, os.W_OK): - self.fail( - _("{name} {filename!r} is not writable.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if self.executable and not os.access(value, os.X_OK): - self.fail( - _("{name} {filename!r} is not executable.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - return self.coerce_path_result(rv) - - def shell_complete( - self, ctx: "Context", param: "Parameter", incomplete: str - ) -> t.List["CompletionItem"]: - """Return a special completion marker that tells the completion - system to use the shell to provide path completions for only - directories or any paths. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - type = "dir" if self.dir_okay and not self.file_okay else "file" - return [CompletionItem(incomplete, type=type)] - - -class Tuple(CompositeParamType): - """The default behavior of Click is to apply a type on a value directly. - This works well in most cases, except for when `nargs` is set to a fixed - count and different types should be used for different items. In this - case the :class:`Tuple` type can be used. This type can only be used - if `nargs` is set to a fixed number. - - For more information see :ref:`tuple-type`. - - This can be selected by using a Python tuple literal as a type. - - :param types: a list of types that should be used for the tuple items. - """ - - def __init__(self, types: t.Sequence[t.Union[t.Type[t.Any], ParamType]]) -> None: - self.types: t.Sequence[ParamType] = [convert_type(ty) for ty in types] - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict["types"] = [t.to_info_dict() for t in self.types] - return info_dict - - @property - def name(self) -> str: # type: ignore - return f"<{' '.join(ty.name for ty in self.types)}>" - - @property - def arity(self) -> int: # type: ignore - return len(self.types) - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - len_type = len(self.types) - len_value = len(value) - - if len_value != len_type: - self.fail( - ngettext( - "{len_type} values are required, but {len_value} was given.", - "{len_type} values are required, but {len_value} were given.", - len_value, - ).format(len_type=len_type, len_value=len_value), - param=param, - ctx=ctx, - ) - - return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) - - -def convert_type(ty: t.Optional[t.Any], default: t.Optional[t.Any] = None) -> ParamType: - """Find the most appropriate :class:`ParamType` for the given Python - type. If the type isn't provided, it can be inferred from a default - value. - """ - guessed_type = False - - if ty is None and default is not None: - if isinstance(default, (tuple, list)): - # If the default is empty, ty will remain None and will - # return STRING. - if default: - item = default[0] - - # A tuple of tuples needs to detect the inner types. - # Can't call convert recursively because that would - # incorrectly unwind the tuple to a single type. - if isinstance(item, (tuple, list)): - ty = tuple(map(type, item)) - else: - ty = type(item) - else: - ty = type(default) - - guessed_type = True - - if isinstance(ty, tuple): - return Tuple(ty) - - if isinstance(ty, ParamType): - return ty - - if ty is str or ty is None: - return STRING - - if ty is int: - return INT - - if ty is float: - return FLOAT - - if ty is bool: - return BOOL - - if guessed_type: - return STRING - - if __debug__: - try: - if issubclass(ty, ParamType): - raise AssertionError( - f"Attempted to use an uninstantiated parameter type ({ty})." - ) - except TypeError: - # ty is an instance (correct), so issubclass fails. - pass - - return FuncParamType(ty) - - -#: A dummy parameter type that just does nothing. From a user's -#: perspective this appears to just be the same as `STRING` but -#: internally no string conversion takes place if the input was bytes. -#: This is usually useful when working with file paths as they can -#: appear in bytes and unicode. -#: -#: For path related uses the :class:`Path` type is a better choice but -#: there are situations where an unprocessed type is useful which is why -#: it is is provided. -#: -#: .. versionadded:: 4.0 -UNPROCESSED = UnprocessedParamType() - -#: A unicode string parameter type which is the implicit default. This -#: can also be selected by using ``str`` as type. -STRING = StringParamType() - -#: An integer parameter. This can also be selected by using ``int`` as -#: type. -INT = IntParamType() - -#: A floating point value parameter. This can also be selected by using -#: ``float`` as type. -FLOAT = FloatParamType() - -#: A boolean parameter. This is the default for boolean flags. This can -#: also be selected by using ``bool`` as a type. -BOOL = BoolParamType() - -#: A UUID parameter. -UUID = UUIDParameterType() diff --git a/venv/Lib/site-packages/click/utils.py b/venv/Lib/site-packages/click/utils.py deleted file mode 100644 index 836c6f2..0000000 --- a/venv/Lib/site-packages/click/utils.py +++ /dev/null @@ -1,624 +0,0 @@ -import os -import re -import sys -import typing as t -from functools import update_wrapper -from types import ModuleType -from types import TracebackType - -from ._compat import _default_text_stderr -from ._compat import _default_text_stdout -from ._compat import _find_binary_writer -from ._compat import auto_wrap_for_ansi -from ._compat import binary_streams -from ._compat import open_stream -from ._compat import should_strip_ansi -from ._compat import strip_ansi -from ._compat import text_streams -from ._compat import WIN -from .globals import resolve_color_default - -if t.TYPE_CHECKING: - import typing_extensions as te - - P = te.ParamSpec("P") - -R = t.TypeVar("R") - - -def _posixify(name: str) -> str: - return "-".join(name.split()).lower() - - -def safecall(func: "t.Callable[P, R]") -> "t.Callable[P, t.Optional[R]]": - """Wraps a function so that it swallows exceptions.""" - - def wrapper(*args: "P.args", **kwargs: "P.kwargs") -> t.Optional[R]: - try: - return func(*args, **kwargs) - except Exception: - pass - return None - - return update_wrapper(wrapper, func) - - -def make_str(value: t.Any) -> str: - """Converts a value into a valid string.""" - if isinstance(value, bytes): - try: - return value.decode(sys.getfilesystemencoding()) - except UnicodeError: - return value.decode("utf-8", "replace") - return str(value) - - -def make_default_short_help(help: str, max_length: int = 45) -> str: - """Returns a condensed version of help string.""" - # Consider only the first paragraph. - paragraph_end = help.find("\n\n") - - if paragraph_end != -1: - help = help[:paragraph_end] - - # Collapse newlines, tabs, and spaces. - words = help.split() - - if not words: - return "" - - # The first paragraph started with a "no rewrap" marker, ignore it. - if words[0] == "\b": - words = words[1:] - - total_length = 0 - last_index = len(words) - 1 - - for i, word in enumerate(words): - total_length += len(word) + (i > 0) - - if total_length > max_length: # too long, truncate - break - - if word[-1] == ".": # sentence end, truncate without "..." - return " ".join(words[: i + 1]) - - if total_length == max_length and i != last_index: - break # not at sentence end, truncate with "..." - else: - return " ".join(words) # no truncation needed - - # Account for the length of the suffix. - total_length += len("...") - - # remove words until the length is short enough - while i > 0: - total_length -= len(words[i]) + (i > 0) - - if total_length <= max_length: - break - - i -= 1 - - return " ".join(words[:i]) + "..." - - -class LazyFile: - """A lazy file works like a regular file but it does not fully open - the file but it does perform some basic checks early to see if the - filename parameter does make sense. This is useful for safely opening - files for writing. - """ - - def __init__( - self, - filename: t.Union[str, "os.PathLike[str]"], - mode: str = "r", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", - atomic: bool = False, - ): - self.name: str = os.fspath(filename) - self.mode = mode - self.encoding = encoding - self.errors = errors - self.atomic = atomic - self._f: t.Optional[t.IO[t.Any]] - self.should_close: bool - - if self.name == "-": - self._f, self.should_close = open_stream(filename, mode, encoding, errors) - else: - if "r" in mode: - # Open and close the file in case we're opening it for - # reading so that we can catch at least some errors in - # some cases early. - open(filename, mode).close() - self._f = None - self.should_close = True - - def __getattr__(self, name: str) -> t.Any: - return getattr(self.open(), name) - - def __repr__(self) -> str: - if self._f is not None: - return repr(self._f) - return f"" - - def open(self) -> t.IO[t.Any]: - """Opens the file if it's not yet open. This call might fail with - a :exc:`FileError`. Not handling this error will produce an error - that Click shows. - """ - if self._f is not None: - return self._f - try: - rv, self.should_close = open_stream( - self.name, self.mode, self.encoding, self.errors, atomic=self.atomic - ) - except OSError as e: - from .exceptions import FileError - - raise FileError(self.name, hint=e.strerror) from e - self._f = rv - return rv - - def close(self) -> None: - """Closes the underlying file, no matter what.""" - if self._f is not None: - self._f.close() - - def close_intelligently(self) -> None: - """This function only closes the file if it was opened by the lazy - file wrapper. For instance this will never close stdin. - """ - if self.should_close: - self.close() - - def __enter__(self) -> "LazyFile": - return self - - def __exit__( - self, - exc_type: t.Optional[t.Type[BaseException]], - exc_value: t.Optional[BaseException], - tb: t.Optional[TracebackType], - ) -> None: - self.close_intelligently() - - def __iter__(self) -> t.Iterator[t.AnyStr]: - self.open() - return iter(self._f) # type: ignore - - -class KeepOpenFile: - def __init__(self, file: t.IO[t.Any]) -> None: - self._file: t.IO[t.Any] = file - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._file, name) - - def __enter__(self) -> "KeepOpenFile": - return self - - def __exit__( - self, - exc_type: t.Optional[t.Type[BaseException]], - exc_value: t.Optional[BaseException], - tb: t.Optional[TracebackType], - ) -> None: - pass - - def __repr__(self) -> str: - return repr(self._file) - - def __iter__(self) -> t.Iterator[t.AnyStr]: - return iter(self._file) - - -def echo( - message: t.Optional[t.Any] = None, - file: t.Optional[t.IO[t.Any]] = None, - nl: bool = True, - err: bool = False, - color: t.Optional[bool] = None, -) -> None: - """Print a message and newline to stdout or a file. This should be - used instead of :func:`print` because it provides better support - for different data, files, and environments. - - Compared to :func:`print`, this does the following: - - - Ensures that the output encoding is not misconfigured on Linux. - - Supports Unicode in the Windows console. - - Supports writing to binary outputs, and supports writing bytes - to text outputs. - - Supports colors and styles on Windows. - - Removes ANSI color and style codes if the output does not look - like an interactive terminal. - - Always flushes the output. - - :param message: The string or bytes to output. Other objects are - converted to strings. - :param file: The file to write to. Defaults to ``stdout``. - :param err: Write to ``stderr`` instead of ``stdout``. - :param nl: Print a newline after the message. Enabled by default. - :param color: Force showing or hiding colors and other styles. By - default Click will remove color if the output does not look like - an interactive terminal. - - .. versionchanged:: 6.0 - Support Unicode output on the Windows console. Click does not - modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` - will still not support Unicode. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. - - .. versionadded:: 3.0 - Added the ``err`` parameter. - - .. versionchanged:: 2.0 - Support colors on Windows if colorama is installed. - """ - if file is None: - if err: - file = _default_text_stderr() - else: - file = _default_text_stdout() - - # There are no standard streams attached to write to. For example, - # pythonw on Windows. - if file is None: - return - - # Convert non bytes/text into the native string type. - if message is not None and not isinstance(message, (str, bytes, bytearray)): - out: t.Optional[t.Union[str, bytes]] = str(message) - else: - out = message - - if nl: - out = out or "" - if isinstance(out, str): - out += "\n" - else: - out += b"\n" - - if not out: - file.flush() - return - - # If there is a message and the value looks like bytes, we manually - # need to find the binary stream and write the message in there. - # This is done separately so that most stream types will work as you - # would expect. Eg: you can write to StringIO for other cases. - if isinstance(out, (bytes, bytearray)): - binary_file = _find_binary_writer(file) - - if binary_file is not None: - file.flush() - binary_file.write(out) - binary_file.flush() - return - - # ANSI style code support. For no message or bytes, nothing happens. - # When outputting to a file instead of a terminal, strip codes. - else: - color = resolve_color_default(color) - - if should_strip_ansi(file, color): - out = strip_ansi(out) - elif WIN: - if auto_wrap_for_ansi is not None: - file = auto_wrap_for_ansi(file, color) # type: ignore - elif not color: - out = strip_ansi(out) - - file.write(out) # type: ignore - file.flush() - - -def get_binary_stream(name: "te.Literal['stdin', 'stdout', 'stderr']") -> t.BinaryIO: - """Returns a system stream for byte processing. - - :param name: the name of the stream to open. Valid names are ``'stdin'``, - ``'stdout'`` and ``'stderr'`` - """ - opener = binary_streams.get(name) - if opener is None: - raise TypeError(f"Unknown standard stream '{name}'") - return opener() - - -def get_text_stream( - name: "te.Literal['stdin', 'stdout', 'stderr']", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", -) -> t.TextIO: - """Returns a system stream for text processing. This usually returns - a wrapped stream around a binary stream returned from - :func:`get_binary_stream` but it also can take shortcuts for already - correctly configured streams. - - :param name: the name of the stream to open. Valid names are ``'stdin'``, - ``'stdout'`` and ``'stderr'`` - :param encoding: overrides the detected default encoding. - :param errors: overrides the default error mode. - """ - opener = text_streams.get(name) - if opener is None: - raise TypeError(f"Unknown standard stream '{name}'") - return opener(encoding, errors) - - -def open_file( - filename: t.Union[str, "os.PathLike[str]"], - mode: str = "r", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", - lazy: bool = False, - atomic: bool = False, -) -> t.IO[t.Any]: - """Open a file, with extra behavior to handle ``'-'`` to indicate - a standard stream, lazy open on write, and atomic write. Similar to - the behavior of the :class:`~click.File` param type. - - If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is - wrapped so that using it in a context manager will not close it. - This makes it possible to use the function without accidentally - closing a standard stream: - - .. code-block:: python - - with open_file(filename) as f: - ... - - :param filename: The name or Path of the file to open, or ``'-'`` for - ``stdin``/``stdout``. - :param mode: The mode in which to open the file. - :param encoding: The encoding to decode or encode a file opened in - text mode. - :param errors: The error handling mode. - :param lazy: Wait to open the file until it is accessed. For read - mode, the file is temporarily opened to raise access errors - early, then closed until it is read again. - :param atomic: Write to a temporary file and replace the given file - on close. - - .. versionadded:: 3.0 - """ - if lazy: - return t.cast( - t.IO[t.Any], LazyFile(filename, mode, encoding, errors, atomic=atomic) - ) - - f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) - - if not should_close: - f = t.cast(t.IO[t.Any], KeepOpenFile(f)) - - return f - - -def format_filename( - filename: "t.Union[str, bytes, os.PathLike[str], os.PathLike[bytes]]", - shorten: bool = False, -) -> str: - """Format a filename as a string for display. Ensures the filename can be - displayed by replacing any invalid bytes or surrogate escapes in the name - with the replacement character ``�``. - - Invalid bytes or surrogate escapes will raise an error when written to a - stream with ``errors="strict"``. This will typically happen with ``stdout`` - when the locale is something like ``en_GB.UTF-8``. - - Many scenarios *are* safe to write surrogates though, due to PEP 538 and - PEP 540, including: - - - Writing to ``stderr``, which uses ``errors="backslashreplace"``. - - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens - stdout and stderr with ``errors="surrogateescape"``. - - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``. - - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``. - Python opens stdout and stderr with ``errors="surrogateescape"``. - - :param filename: formats a filename for UI display. This will also convert - the filename into unicode without failing. - :param shorten: this optionally shortens the filename to strip of the - path that leads up to it. - """ - if shorten: - filename = os.path.basename(filename) - else: - filename = os.fspath(filename) - - if isinstance(filename, bytes): - filename = filename.decode(sys.getfilesystemencoding(), "replace") - else: - filename = filename.encode("utf-8", "surrogateescape").decode( - "utf-8", "replace" - ) - - return filename - - -def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: - r"""Returns the config folder for the application. The default behavior - is to return whatever is most appropriate for the operating system. - - To give you an idea, for an app called ``"Foo Bar"``, something like - the following folders could be returned: - - Mac OS X: - ``~/Library/Application Support/Foo Bar`` - Mac OS X (POSIX): - ``~/.foo-bar`` - Unix: - ``~/.config/foo-bar`` - Unix (POSIX): - ``~/.foo-bar`` - Windows (roaming): - ``C:\Users\\AppData\Roaming\Foo Bar`` - Windows (not roaming): - ``C:\Users\\AppData\Local\Foo Bar`` - - .. versionadded:: 2.0 - - :param app_name: the application name. This should be properly capitalized - and can contain whitespace. - :param roaming: controls if the folder should be roaming or not on Windows. - Has no effect otherwise. - :param force_posix: if this is set to `True` then on any POSIX system the - folder will be stored in the home folder with a leading - dot instead of the XDG config home or darwin's - application support folder. - """ - if WIN: - key = "APPDATA" if roaming else "LOCALAPPDATA" - folder = os.environ.get(key) - if folder is None: - folder = os.path.expanduser("~") - return os.path.join(folder, app_name) - if force_posix: - return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) - if sys.platform == "darwin": - return os.path.join( - os.path.expanduser("~/Library/Application Support"), app_name - ) - return os.path.join( - os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), - _posixify(app_name), - ) - - -class PacifyFlushWrapper: - """This wrapper is used to catch and suppress BrokenPipeErrors resulting - from ``.flush()`` being called on broken pipe during the shutdown/final-GC - of the Python interpreter. Notably ``.flush()`` is always called on - ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any - other cleanup code, and the case where the underlying file is not a broken - pipe, all calls and attributes are proxied. - """ - - def __init__(self, wrapped: t.IO[t.Any]) -> None: - self.wrapped = wrapped - - def flush(self) -> None: - try: - self.wrapped.flush() - except OSError as e: - import errno - - if e.errno != errno.EPIPE: - raise - - def __getattr__(self, attr: str) -> t.Any: - return getattr(self.wrapped, attr) - - -def _detect_program_name( - path: t.Optional[str] = None, _main: t.Optional[ModuleType] = None -) -> str: - """Determine the command used to run the program, for use in help - text. If a file or entry point was executed, the file name is - returned. If ``python -m`` was used to execute a module or package, - ``python -m name`` is returned. - - This doesn't try to be too precise, the goal is to give a concise - name for help text. Files are only shown as their name without the - path. ``python`` is only shown for modules, and the full path to - ``sys.executable`` is not shown. - - :param path: The Python file being executed. Python puts this in - ``sys.argv[0]``, which is used by default. - :param _main: The ``__main__`` module. This should only be passed - during internal testing. - - .. versionadded:: 8.0 - Based on command args detection in the Werkzeug reloader. - - :meta private: - """ - if _main is None: - _main = sys.modules["__main__"] - - if not path: - path = sys.argv[0] - - # The value of __package__ indicates how Python was called. It may - # not exist if a setuptools script is installed as an egg. It may be - # set incorrectly for entry points created with pip on Windows. - # It is set to "" inside a Shiv or PEX zipapp. - if getattr(_main, "__package__", None) in {None, ""} or ( - os.name == "nt" - and _main.__package__ == "" - and not os.path.exists(path) - and os.path.exists(f"{path}.exe") - ): - # Executed a file, like "python app.py". - return os.path.basename(path) - - # Executed a module, like "python -m example". - # Rewritten by Python from "-m script" to "/path/to/script.py". - # Need to look at main module to determine how it was executed. - py_module = t.cast(str, _main.__package__) - name = os.path.splitext(os.path.basename(path))[0] - - # A submodule like "example.cli". - if name != "__main__": - py_module = f"{py_module}.{name}" - - return f"python -m {py_module.lstrip('.')}" - - -def _expand_args( - args: t.Iterable[str], - *, - user: bool = True, - env: bool = True, - glob_recursive: bool = True, -) -> t.List[str]: - """Simulate Unix shell expansion with Python functions. - - See :func:`glob.glob`, :func:`os.path.expanduser`, and - :func:`os.path.expandvars`. - - This is intended for use on Windows, where the shell does not do any - expansion. It may not exactly match what a Unix shell would do. - - :param args: List of command line arguments to expand. - :param user: Expand user home directory. - :param env: Expand environment variables. - :param glob_recursive: ``**`` matches directories recursively. - - .. versionchanged:: 8.1 - Invalid glob patterns are treated as empty expansions rather - than raising an error. - - .. versionadded:: 8.0 - - :meta private: - """ - from glob import glob - - out = [] - - for arg in args: - if user: - arg = os.path.expanduser(arg) - - if env: - arg = os.path.expandvars(arg) - - try: - matches = glob(arg, recursive=glob_recursive) - except re.error: - matches = [] - - if not matches: - out.append(arg) - else: - out.extend(matches) - - return out diff --git a/venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER b/venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA b/venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA deleted file mode 100644 index a1b5c57..0000000 --- a/venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA +++ /dev/null @@ -1,441 +0,0 @@ -Metadata-Version: 2.1 -Name: colorama -Version: 0.4.6 -Summary: Cross-platform colored terminal text. -Project-URL: Homepage, https://github.com/tartley/colorama -Author-email: Jonathan Hartley -License-File: LICENSE.txt -Keywords: ansi,color,colour,crossplatform,terminal,text,windows,xplatform -Classifier: Development Status :: 5 - Production/Stable -Classifier: Environment :: Console -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Terminals -Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7 -Description-Content-Type: text/x-rst - -.. image:: https://img.shields.io/pypi/v/colorama.svg - :target: https://pypi.org/project/colorama/ - :alt: Latest Version - -.. image:: https://img.shields.io/pypi/pyversions/colorama.svg - :target: https://pypi.org/project/colorama/ - :alt: Supported Python versions - -.. image:: https://github.com/tartley/colorama/actions/workflows/test.yml/badge.svg - :target: https://github.com/tartley/colorama/actions/workflows/test.yml - :alt: Build Status - -Colorama -======== - -Makes ANSI escape character sequences (for producing colored terminal text and -cursor positioning) work under MS Windows. - -.. |donate| image:: https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif - :target: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2MZ9D2GMLYCUJ&item_name=Colorama¤cy_code=USD - :alt: Donate with Paypal - -`PyPI for releases `_ | -`Github for source `_ | -`Colorama for enterprise on Tidelift `_ - -If you find Colorama useful, please |donate| to the authors. Thank you! - -Installation ------------- - -Tested on CPython 2.7, 3.7, 3.8, 3.9 and 3.10 and Pypy 2.7 and 3.8. - -No requirements other than the standard library. - -.. code-block:: bash - - pip install colorama - # or - conda install -c anaconda colorama - -Description ------------ - -ANSI escape character sequences have long been used to produce colored terminal -text and cursor positioning on Unix and Macs. Colorama makes this work on -Windows, too, by wrapping ``stdout``, stripping ANSI sequences it finds (which -would appear as gobbledygook in the output), and converting them into the -appropriate win32 calls to modify the state of the terminal. On other platforms, -Colorama does nothing. - -This has the upshot of providing a simple cross-platform API for printing -colored terminal text from Python, and has the happy side-effect that existing -applications or libraries which use ANSI sequences to produce colored output on -Linux or Macs can now also work on Windows, simply by calling -``colorama.just_fix_windows_console()`` (since v0.4.6) or ``colorama.init()`` -(all versions, but may have other side-effects – see below). - -An alternative approach is to install ``ansi.sys`` on Windows machines, which -provides the same behaviour for all applications running in terminals. Colorama -is intended for situations where that isn't easy (e.g., maybe your app doesn't -have an installer.) - -Demo scripts in the source code repository print some colored text using -ANSI sequences. Compare their output under Gnome-terminal's built in ANSI -handling, versus on Windows Command-Prompt using Colorama: - -.. image:: https://github.com/tartley/colorama/raw/master/screenshots/ubuntu-demo.png - :width: 661 - :height: 357 - :alt: ANSI sequences on Ubuntu under gnome-terminal. - -.. image:: https://github.com/tartley/colorama/raw/master/screenshots/windows-demo.png - :width: 668 - :height: 325 - :alt: Same ANSI sequences on Windows, using Colorama. - -These screenshots show that, on Windows, Colorama does not support ANSI 'dim -text'; it looks the same as 'normal text'. - -Usage ------ - -Initialisation -.............. - -If the only thing you want from Colorama is to get ANSI escapes to work on -Windows, then run: - -.. code-block:: python - - from colorama import just_fix_windows_console - just_fix_windows_console() - -If you're on a recent version of Windows 10 or better, and your stdout/stderr -are pointing to a Windows console, then this will flip the magic configuration -switch to enable Windows' built-in ANSI support. - -If you're on an older version of Windows, and your stdout/stderr are pointing to -a Windows console, then this will wrap ``sys.stdout`` and/or ``sys.stderr`` in a -magic file object that intercepts ANSI escape sequences and issues the -appropriate Win32 calls to emulate them. - -In all other circumstances, it does nothing whatsoever. Basically the idea is -that this makes Windows act like Unix with respect to ANSI escape handling. - -It's safe to call this function multiple times. It's safe to call this function -on non-Windows platforms, but it won't do anything. It's safe to call this -function when one or both of your stdout/stderr are redirected to a file – it -won't do anything to those streams. - -Alternatively, you can use the older interface with more features (but also more -potential footguns): - -.. code-block:: python - - from colorama import init - init() - -This does the same thing as ``just_fix_windows_console``, except for the -following differences: - -- It's not safe to call ``init`` multiple times; you can end up with multiple - layers of wrapping and broken ANSI support. - -- Colorama will apply a heuristic to guess whether stdout/stderr support ANSI, - and if it thinks they don't, then it will wrap ``sys.stdout`` and - ``sys.stderr`` in a magic file object that strips out ANSI escape sequences - before printing them. This happens on all platforms, and can be convenient if - you want to write your code to emit ANSI escape sequences unconditionally, and - let Colorama decide whether they should actually be output. But note that - Colorama's heuristic is not particularly clever. - -- ``init`` also accepts explicit keyword args to enable/disable various - functionality – see below. - -To stop using Colorama before your program exits, simply call ``deinit()``. -This will restore ``stdout`` and ``stderr`` to their original values, so that -Colorama is disabled. To resume using Colorama again, call ``reinit()``; it is -cheaper than calling ``init()`` again (but does the same thing). - -Most users should depend on ``colorama >= 0.4.6``, and use -``just_fix_windows_console``. The old ``init`` interface will be supported -indefinitely for backwards compatibility, but we don't plan to fix any issues -with it, also for backwards compatibility. - -Colored Output -.............. - -Cross-platform printing of colored text can then be done using Colorama's -constant shorthand for ANSI escape sequences. These are deliberately -rudimentary, see below. - -.. code-block:: python - - from colorama import Fore, Back, Style - print(Fore.RED + 'some red text') - print(Back.GREEN + 'and with a green background') - print(Style.DIM + 'and in dim text') - print(Style.RESET_ALL) - print('back to normal now') - -...or simply by manually printing ANSI sequences from your own code: - -.. code-block:: python - - print('\033[31m' + 'some red text') - print('\033[39m') # and reset to default color - -...or, Colorama can be used in conjunction with existing ANSI libraries -such as the venerable `Termcolor `_ -the fabulous `Blessings `_, -or the incredible `_Rich `_. - -If you wish Colorama's Fore, Back and Style constants were more capable, -then consider using one of the above highly capable libraries to generate -colors, etc, and use Colorama just for its primary purpose: to convert -those ANSI sequences to also work on Windows: - -SIMILARLY, do not send PRs adding the generation of new ANSI types to Colorama. -We are only interested in converting ANSI codes to win32 API calls, not -shortcuts like the above to generate ANSI characters. - -.. code-block:: python - - from colorama import just_fix_windows_console - from termcolor import colored - - # use Colorama to make Termcolor work on Windows too - just_fix_windows_console() - - # then use Termcolor for all colored text output - print(colored('Hello, World!', 'green', 'on_red')) - -Available formatting constants are:: - - Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. - Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. - Style: DIM, NORMAL, BRIGHT, RESET_ALL - -``Style.RESET_ALL`` resets foreground, background, and brightness. Colorama will -perform this reset automatically on program exit. - -These are fairly well supported, but not part of the standard:: - - Fore: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX - Back: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX - -Cursor Positioning -.................. - -ANSI codes to reposition the cursor are supported. See ``demos/demo06.py`` for -an example of how to generate them. - -Init Keyword Args -................. - -``init()`` accepts some ``**kwargs`` to override default behaviour. - -init(autoreset=False): - If you find yourself repeatedly sending reset sequences to turn off color - changes at the end of every print, then ``init(autoreset=True)`` will - automate that: - - .. code-block:: python - - from colorama import init - init(autoreset=True) - print(Fore.RED + 'some red text') - print('automatically back to default color again') - -init(strip=None): - Pass ``True`` or ``False`` to override whether ANSI codes should be - stripped from the output. The default behaviour is to strip if on Windows - or if output is redirected (not a tty). - -init(convert=None): - Pass ``True`` or ``False`` to override whether to convert ANSI codes in the - output into win32 calls. The default behaviour is to convert if on Windows - and output is to a tty (terminal). - -init(wrap=True): - On Windows, Colorama works by replacing ``sys.stdout`` and ``sys.stderr`` - with proxy objects, which override the ``.write()`` method to do their work. - If this wrapping causes you problems, then this can be disabled by passing - ``init(wrap=False)``. The default behaviour is to wrap if ``autoreset`` or - ``strip`` or ``convert`` are True. - - When wrapping is disabled, colored printing on non-Windows platforms will - continue to work as normal. To do cross-platform colored output, you can - use Colorama's ``AnsiToWin32`` proxy directly: - - .. code-block:: python - - import sys - from colorama import init, AnsiToWin32 - init(wrap=False) - stream = AnsiToWin32(sys.stderr).stream - - # Python 2 - print >>stream, Fore.BLUE + 'blue text on stderr' - - # Python 3 - print(Fore.BLUE + 'blue text on stderr', file=stream) - -Recognised ANSI Sequences -......................... - -ANSI sequences generally take the form:: - - ESC [ ; ... - -Where ```` is an integer, and ```` is a single letter. Zero or -more params are passed to a ````. If no params are passed, it is -generally synonymous with passing a single zero. No spaces exist in the -sequence; they have been inserted here simply to read more easily. - -The only ANSI sequences that Colorama converts into win32 calls are:: - - ESC [ 0 m # reset all (colors and brightness) - ESC [ 1 m # bright - ESC [ 2 m # dim (looks same as normal brightness) - ESC [ 22 m # normal brightness - - # FOREGROUND: - ESC [ 30 m # black - ESC [ 31 m # red - ESC [ 32 m # green - ESC [ 33 m # yellow - ESC [ 34 m # blue - ESC [ 35 m # magenta - ESC [ 36 m # cyan - ESC [ 37 m # white - ESC [ 39 m # reset - - # BACKGROUND - ESC [ 40 m # black - ESC [ 41 m # red - ESC [ 42 m # green - ESC [ 43 m # yellow - ESC [ 44 m # blue - ESC [ 45 m # magenta - ESC [ 46 m # cyan - ESC [ 47 m # white - ESC [ 49 m # reset - - # cursor positioning - ESC [ y;x H # position cursor at x across, y down - ESC [ y;x f # position cursor at x across, y down - ESC [ n A # move cursor n lines up - ESC [ n B # move cursor n lines down - ESC [ n C # move cursor n characters forward - ESC [ n D # move cursor n characters backward - - # clear the screen - ESC [ mode J # clear the screen - - # clear the line - ESC [ mode K # clear the line - -Multiple numeric params to the ``'m'`` command can be combined into a single -sequence:: - - ESC [ 36 ; 45 ; 1 m # bright cyan text on magenta background - -All other ANSI sequences of the form ``ESC [ ; ... `` -are silently stripped from the output on Windows. - -Any other form of ANSI sequence, such as single-character codes or alternative -initial characters, are not recognised or stripped. It would be cool to add -them though. Let me know if it would be useful for you, via the Issues on -GitHub. - -Status & Known Problems ------------------------ - -I've personally only tested it on Windows XP (CMD, Console2), Ubuntu -(gnome-terminal, xterm), and OS X. - -Some valid ANSI sequences aren't recognised. - -If you're hacking on the code, see `README-hacking.md`_. ESPECIALLY, see the -explanation there of why we do not want PRs that allow Colorama to generate new -types of ANSI codes. - -See outstanding issues and wish-list: -https://github.com/tartley/colorama/issues - -If anything doesn't work for you, or doesn't do what you expected or hoped for, -I'd love to hear about it on that issues list, would be delighted by patches, -and would be happy to grant commit access to anyone who submits a working patch -or two. - -.. _README-hacking.md: README-hacking.md - -License -------- - -Copyright Jonathan Hartley & Arnon Yaari, 2013-2020. BSD 3-Clause license; see -LICENSE file. - -Professional support --------------------- - -.. |tideliftlogo| image:: https://cdn2.hubspot.net/hubfs/4008838/website/logos/logos_for_download/Tidelift_primary-shorthand-logo.png - :alt: Tidelift - :target: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme - -.. list-table:: - :widths: 10 100 - - * - |tideliftlogo| - - Professional support for colorama is available as part of the - `Tidelift Subscription`_. - Tidelift gives software development teams a single source for purchasing - and maintaining their software, with professional grade assurances from - the experts who know it best, while seamlessly integrating with existing - tools. - -.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme - -Thanks ------- - -See the CHANGELOG for more thanks! - -* Marc Schlaich (schlamar) for a ``setup.py`` fix for Python2.5. -* Marc Abramowitz, reported & fixed a crash on exit with closed ``stdout``, - providing a solution to issue #7's setuptools/distutils debate, - and other fixes. -* User 'eryksun', for guidance on correctly instantiating ``ctypes.windll``. -* Matthew McCormick for politely pointing out a longstanding crash on non-Win. -* Ben Hoyt, for a magnificent fix under 64-bit Windows. -* Jesse at Empty Square for submitting a fix for examples in the README. -* User 'jamessp', an observant documentation fix for cursor positioning. -* User 'vaal1239', Dave Mckee & Lackner Kristof for a tiny but much-needed Win7 - fix. -* Julien Stuyck, for wisely suggesting Python3 compatible updates to README. -* Daniel Griffith for multiple fabulous patches. -* Oscar Lesta for a valuable fix to stop ANSI chars being sent to non-tty - output. -* Roger Binns, for many suggestions, valuable feedback, & bug reports. -* Tim Golden for thought and much appreciated feedback on the initial idea. -* User 'Zearin' for updates to the README file. -* John Szakmeister for adding support for light colors -* Charles Merriam for adding documentation to demos -* Jurko for a fix on 64-bit Windows CPython2.5 w/o ctypes -* Florian Bruhin for a fix when stdout or stderr are None -* Thomas Weininger for fixing ValueError on Windows -* Remi Rampin for better Github integration and fixes to the README file -* Simeon Visser for closing a file handle using 'with' and updating classifiers - to include Python 3.3 and 3.4 -* Andy Neff for fixing RESET of LIGHT_EX colors. -* Jonathan Hartley for the initial idea and implementation. diff --git a/venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD b/venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD deleted file mode 100644 index 8c5f12d..0000000 --- a/venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD +++ /dev/null @@ -1,31 +0,0 @@ -colorama-0.4.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -colorama-0.4.6.dist-info/METADATA,sha256=e67SnrUMOym9sz_4TjF3vxvAV4T3aF7NyqRHHH3YEMw,17158 -colorama-0.4.6.dist-info/RECORD,, -colorama-0.4.6.dist-info/WHEEL,sha256=cdcF4Fbd0FPtw2EMIOwH-3rSOTUdTCeOSXRMD1iLUb8,105 -colorama-0.4.6.dist-info/licenses/LICENSE.txt,sha256=ysNcAmhuXQSlpxQL-zs25zrtSWZW6JEQLkKIhteTAxg,1491 -colorama/__init__.py,sha256=wePQA4U20tKgYARySLEC047ucNX-g8pRLpYBuiHlLb8,266 -colorama/__pycache__/__init__.cpython-310.pyc,, -colorama/__pycache__/ansi.cpython-310.pyc,, -colorama/__pycache__/ansitowin32.cpython-310.pyc,, -colorama/__pycache__/initialise.cpython-310.pyc,, -colorama/__pycache__/win32.cpython-310.pyc,, -colorama/__pycache__/winterm.cpython-310.pyc,, -colorama/ansi.py,sha256=Top4EeEuaQdBWdteKMEcGOTeKeF19Q-Wo_6_Cj5kOzQ,2522 -colorama/ansitowin32.py,sha256=vPNYa3OZbxjbuFyaVo0Tmhmy1FZ1lKMWCnT7odXpItk,11128 -colorama/initialise.py,sha256=-hIny86ClXo39ixh5iSCfUIa2f_h_bgKRDW7gqs-KLU,3325 -colorama/tests/__init__.py,sha256=MkgPAEzGQd-Rq0w0PZXSX2LadRWhUECcisJY8lSrm4Q,75 -colorama/tests/__pycache__/__init__.cpython-310.pyc,, -colorama/tests/__pycache__/ansi_test.cpython-310.pyc,, -colorama/tests/__pycache__/ansitowin32_test.cpython-310.pyc,, -colorama/tests/__pycache__/initialise_test.cpython-310.pyc,, -colorama/tests/__pycache__/isatty_test.cpython-310.pyc,, -colorama/tests/__pycache__/utils.cpython-310.pyc,, -colorama/tests/__pycache__/winterm_test.cpython-310.pyc,, -colorama/tests/ansi_test.py,sha256=FeViDrUINIZcr505PAxvU4AjXz1asEiALs9GXMhwRaE,2839 -colorama/tests/ansitowin32_test.py,sha256=RN7AIhMJ5EqDsYaCjVo-o4u8JzDD4ukJbmevWKS70rY,10678 -colorama/tests/initialise_test.py,sha256=BbPy-XfyHwJ6zKozuQOvNvQZzsx9vdb_0bYXn7hsBTc,6741 -colorama/tests/isatty_test.py,sha256=Pg26LRpv0yQDB5Ac-sxgVXG7hsA1NYvapFgApZfYzZg,1866 -colorama/tests/utils.py,sha256=1IIRylG39z5-dzq09R_ngufxyPZxgldNbrxKxUGwGKE,1079 -colorama/tests/winterm_test.py,sha256=qoWFPEjym5gm2RuMwpf3pOis3a5r_PJZFCzK254JL8A,3709 -colorama/win32.py,sha256=YQOKwMTwtGBbsY4dL5HYTvwTeP9wIQra5MvPNddpxZs,6181 -colorama/winterm.py,sha256=XCQFDHjPi6AHYNdZwy0tA02H-Jh48Jp-HvCjeLeLp3U,7134 diff --git a/venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL b/venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL deleted file mode 100644 index d79189f..0000000 --- a/venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: hatchling 1.11.1 -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any diff --git a/venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt b/venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt deleted file mode 100644 index 3105888..0000000 --- a/venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2010 Jonathan Hartley -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the copyright holders, nor those of its contributors - may be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/colorama/__init__.py b/venv/Lib/site-packages/colorama/__init__.py deleted file mode 100644 index 383101c..0000000 --- a/venv/Lib/site-packages/colorama/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -from .initialise import init, deinit, reinit, colorama_text, just_fix_windows_console -from .ansi import Fore, Back, Style, Cursor -from .ansitowin32 import AnsiToWin32 - -__version__ = '0.4.6' - diff --git a/venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 4900407..0000000 Binary files a/venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-310.pyc b/venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-310.pyc deleted file mode 100644 index de2b103..0000000 Binary files a/venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-310.pyc b/venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-310.pyc deleted file mode 100644 index dce73bd..0000000 Binary files a/venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/colorama/__pycache__/initialise.cpython-310.pyc b/venv/Lib/site-packages/colorama/__pycache__/initialise.cpython-310.pyc deleted file mode 100644 index 52bc854..0000000 Binary files a/venv/Lib/site-packages/colorama/__pycache__/initialise.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/colorama/__pycache__/win32.cpython-310.pyc b/venv/Lib/site-packages/colorama/__pycache__/win32.cpython-310.pyc deleted file mode 100644 index 8d11706..0000000 Binary files a/venv/Lib/site-packages/colorama/__pycache__/win32.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/colorama/__pycache__/winterm.cpython-310.pyc b/venv/Lib/site-packages/colorama/__pycache__/winterm.cpython-310.pyc deleted file mode 100644 index 44c36f9..0000000 Binary files a/venv/Lib/site-packages/colorama/__pycache__/winterm.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/colorama/ansi.py b/venv/Lib/site-packages/colorama/ansi.py deleted file mode 100644 index 11ec695..0000000 --- a/venv/Lib/site-packages/colorama/ansi.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -''' -This module generates ANSI character codes to printing colors to terminals. -See: http://en.wikipedia.org/wiki/ANSI_escape_code -''' - -CSI = '\033[' -OSC = '\033]' -BEL = '\a' - - -def code_to_chars(code): - return CSI + str(code) + 'm' - -def set_title(title): - return OSC + '2;' + title + BEL - -def clear_screen(mode=2): - return CSI + str(mode) + 'J' - -def clear_line(mode=2): - return CSI + str(mode) + 'K' - - -class AnsiCodes(object): - def __init__(self): - # the subclasses declare class attributes which are numbers. - # Upon instantiation we define instance attributes, which are the same - # as the class attributes but wrapped with the ANSI escape sequence - for name in dir(self): - if not name.startswith('_'): - value = getattr(self, name) - setattr(self, name, code_to_chars(value)) - - -class AnsiCursor(object): - def UP(self, n=1): - return CSI + str(n) + 'A' - def DOWN(self, n=1): - return CSI + str(n) + 'B' - def FORWARD(self, n=1): - return CSI + str(n) + 'C' - def BACK(self, n=1): - return CSI + str(n) + 'D' - def POS(self, x=1, y=1): - return CSI + str(y) + ';' + str(x) + 'H' - - -class AnsiFore(AnsiCodes): - BLACK = 30 - RED = 31 - GREEN = 32 - YELLOW = 33 - BLUE = 34 - MAGENTA = 35 - CYAN = 36 - WHITE = 37 - RESET = 39 - - # These are fairly well supported, but not part of the standard. - LIGHTBLACK_EX = 90 - LIGHTRED_EX = 91 - LIGHTGREEN_EX = 92 - LIGHTYELLOW_EX = 93 - LIGHTBLUE_EX = 94 - LIGHTMAGENTA_EX = 95 - LIGHTCYAN_EX = 96 - LIGHTWHITE_EX = 97 - - -class AnsiBack(AnsiCodes): - BLACK = 40 - RED = 41 - GREEN = 42 - YELLOW = 43 - BLUE = 44 - MAGENTA = 45 - CYAN = 46 - WHITE = 47 - RESET = 49 - - # These are fairly well supported, but not part of the standard. - LIGHTBLACK_EX = 100 - LIGHTRED_EX = 101 - LIGHTGREEN_EX = 102 - LIGHTYELLOW_EX = 103 - LIGHTBLUE_EX = 104 - LIGHTMAGENTA_EX = 105 - LIGHTCYAN_EX = 106 - LIGHTWHITE_EX = 107 - - -class AnsiStyle(AnsiCodes): - BRIGHT = 1 - DIM = 2 - NORMAL = 22 - RESET_ALL = 0 - -Fore = AnsiFore() -Back = AnsiBack() -Style = AnsiStyle() -Cursor = AnsiCursor() diff --git a/venv/Lib/site-packages/colorama/ansitowin32.py b/venv/Lib/site-packages/colorama/ansitowin32.py deleted file mode 100644 index abf209e..0000000 --- a/venv/Lib/site-packages/colorama/ansitowin32.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -import re -import sys -import os - -from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL -from .winterm import enable_vt_processing, WinTerm, WinColor, WinStyle -from .win32 import windll, winapi_test - - -winterm = None -if windll is not None: - winterm = WinTerm() - - -class StreamWrapper(object): - ''' - Wraps a stream (such as stdout), acting as a transparent proxy for all - attribute access apart from method 'write()', which is delegated to our - Converter instance. - ''' - def __init__(self, wrapped, converter): - # double-underscore everything to prevent clashes with names of - # attributes on the wrapped stream object. - self.__wrapped = wrapped - self.__convertor = converter - - def __getattr__(self, name): - return getattr(self.__wrapped, name) - - def __enter__(self, *args, **kwargs): - # special method lookup bypasses __getattr__/__getattribute__, see - # https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit - # thus, contextlib magic methods are not proxied via __getattr__ - return self.__wrapped.__enter__(*args, **kwargs) - - def __exit__(self, *args, **kwargs): - return self.__wrapped.__exit__(*args, **kwargs) - - def __setstate__(self, state): - self.__dict__ = state - - def __getstate__(self): - return self.__dict__ - - def write(self, text): - self.__convertor.write(text) - - def isatty(self): - stream = self.__wrapped - if 'PYCHARM_HOSTED' in os.environ: - if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__): - return True - try: - stream_isatty = stream.isatty - except AttributeError: - return False - else: - return stream_isatty() - - @property - def closed(self): - stream = self.__wrapped - try: - return stream.closed - # AttributeError in the case that the stream doesn't support being closed - # ValueError for the case that the stream has already been detached when atexit runs - except (AttributeError, ValueError): - return True - - -class AnsiToWin32(object): - ''' - Implements a 'write()' method which, on Windows, will strip ANSI character - sequences from the text, and if outputting to a tty, will convert them into - win32 function calls. - ''' - ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer - ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command - - def __init__(self, wrapped, convert=None, strip=None, autoreset=False): - # The wrapped stream (normally sys.stdout or sys.stderr) - self.wrapped = wrapped - - # should we reset colors to defaults after every .write() - self.autoreset = autoreset - - # create the proxy wrapping our output stream - self.stream = StreamWrapper(wrapped, self) - - on_windows = os.name == 'nt' - # We test if the WinAPI works, because even if we are on Windows - # we may be using a terminal that doesn't support the WinAPI - # (e.g. Cygwin Terminal). In this case it's up to the terminal - # to support the ANSI codes. - conversion_supported = on_windows and winapi_test() - try: - fd = wrapped.fileno() - except Exception: - fd = -1 - system_has_native_ansi = not on_windows or enable_vt_processing(fd) - have_tty = not self.stream.closed and self.stream.isatty() - need_conversion = conversion_supported and not system_has_native_ansi - - # should we strip ANSI sequences from our output? - if strip is None: - strip = need_conversion or not have_tty - self.strip = strip - - # should we should convert ANSI sequences into win32 calls? - if convert is None: - convert = need_conversion and have_tty - self.convert = convert - - # dict of ansi codes to win32 functions and parameters - self.win32_calls = self.get_win32_calls() - - # are we wrapping stderr? - self.on_stderr = self.wrapped is sys.stderr - - def should_wrap(self): - ''' - True if this class is actually needed. If false, then the output - stream will not be affected, nor will win32 calls be issued, so - wrapping stdout is not actually required. This will generally be - False on non-Windows platforms, unless optional functionality like - autoreset has been requested using kwargs to init() - ''' - return self.convert or self.strip or self.autoreset - - def get_win32_calls(self): - if self.convert and winterm: - return { - AnsiStyle.RESET_ALL: (winterm.reset_all, ), - AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), - AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), - AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), - AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), - AnsiFore.RED: (winterm.fore, WinColor.RED), - AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), - AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), - AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), - AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), - AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), - AnsiFore.WHITE: (winterm.fore, WinColor.GREY), - AnsiFore.RESET: (winterm.fore, ), - AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), - AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), - AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), - AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True), - AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True), - AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True), - AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True), - AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True), - AnsiBack.BLACK: (winterm.back, WinColor.BLACK), - AnsiBack.RED: (winterm.back, WinColor.RED), - AnsiBack.GREEN: (winterm.back, WinColor.GREEN), - AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), - AnsiBack.BLUE: (winterm.back, WinColor.BLUE), - AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), - AnsiBack.CYAN: (winterm.back, WinColor.CYAN), - AnsiBack.WHITE: (winterm.back, WinColor.GREY), - AnsiBack.RESET: (winterm.back, ), - AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), - AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), - AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), - AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True), - AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True), - AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True), - AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True), - AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True), - } - return dict() - - def write(self, text): - if self.strip or self.convert: - self.write_and_convert(text) - else: - self.wrapped.write(text) - self.wrapped.flush() - if self.autoreset: - self.reset_all() - - - def reset_all(self): - if self.convert: - self.call_win32('m', (0,)) - elif not self.strip and not self.stream.closed: - self.wrapped.write(Style.RESET_ALL) - - - def write_and_convert(self, text): - ''' - Write the given text to our wrapped stream, stripping any ANSI - sequences from the text, and optionally converting them into win32 - calls. - ''' - cursor = 0 - text = self.convert_osc(text) - for match in self.ANSI_CSI_RE.finditer(text): - start, end = match.span() - self.write_plain_text(text, cursor, start) - self.convert_ansi(*match.groups()) - cursor = end - self.write_plain_text(text, cursor, len(text)) - - - def write_plain_text(self, text, start, end): - if start < end: - self.wrapped.write(text[start:end]) - self.wrapped.flush() - - - def convert_ansi(self, paramstring, command): - if self.convert: - params = self.extract_params(command, paramstring) - self.call_win32(command, params) - - - def extract_params(self, command, paramstring): - if command in 'Hf': - params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) - while len(params) < 2: - # defaults: - params = params + (1,) - else: - params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0) - if len(params) == 0: - # defaults: - if command in 'JKm': - params = (0,) - elif command in 'ABCD': - params = (1,) - - return params - - - def call_win32(self, command, params): - if command == 'm': - for param in params: - if param in self.win32_calls: - func_args = self.win32_calls[param] - func = func_args[0] - args = func_args[1:] - kwargs = dict(on_stderr=self.on_stderr) - func(*args, **kwargs) - elif command in 'J': - winterm.erase_screen(params[0], on_stderr=self.on_stderr) - elif command in 'K': - winterm.erase_line(params[0], on_stderr=self.on_stderr) - elif command in 'Hf': # cursor position - absolute - winterm.set_cursor_position(params, on_stderr=self.on_stderr) - elif command in 'ABCD': # cursor position - relative - n = params[0] - # A - up, B - down, C - forward, D - back - x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] - winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) - - - def convert_osc(self, text): - for match in self.ANSI_OSC_RE.finditer(text): - start, end = match.span() - text = text[:start] + text[end:] - paramstring, command = match.groups() - if command == BEL: - if paramstring.count(";") == 1: - params = paramstring.split(";") - # 0 - change title and icon (we will only change title) - # 1 - change icon (we don't support this) - # 2 - change title - if params[0] in '02': - winterm.set_title(params[1]) - return text - - - def flush(self): - self.wrapped.flush() diff --git a/venv/Lib/site-packages/colorama/initialise.py b/venv/Lib/site-packages/colorama/initialise.py deleted file mode 100644 index d5fd4b7..0000000 --- a/venv/Lib/site-packages/colorama/initialise.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -import atexit -import contextlib -import sys - -from .ansitowin32 import AnsiToWin32 - - -def _wipe_internal_state_for_tests(): - global orig_stdout, orig_stderr - orig_stdout = None - orig_stderr = None - - global wrapped_stdout, wrapped_stderr - wrapped_stdout = None - wrapped_stderr = None - - global atexit_done - atexit_done = False - - global fixed_windows_console - fixed_windows_console = False - - try: - # no-op if it wasn't registered - atexit.unregister(reset_all) - except AttributeError: - # python 2: no atexit.unregister. Oh well, we did our best. - pass - - -def reset_all(): - if AnsiToWin32 is not None: # Issue #74: objects might become None at exit - AnsiToWin32(orig_stdout).reset_all() - - -def init(autoreset=False, convert=None, strip=None, wrap=True): - - if not wrap and any([autoreset, convert, strip]): - raise ValueError('wrap=False conflicts with any other arg=True') - - global wrapped_stdout, wrapped_stderr - global orig_stdout, orig_stderr - - orig_stdout = sys.stdout - orig_stderr = sys.stderr - - if sys.stdout is None: - wrapped_stdout = None - else: - sys.stdout = wrapped_stdout = \ - wrap_stream(orig_stdout, convert, strip, autoreset, wrap) - if sys.stderr is None: - wrapped_stderr = None - else: - sys.stderr = wrapped_stderr = \ - wrap_stream(orig_stderr, convert, strip, autoreset, wrap) - - global atexit_done - if not atexit_done: - atexit.register(reset_all) - atexit_done = True - - -def deinit(): - if orig_stdout is not None: - sys.stdout = orig_stdout - if orig_stderr is not None: - sys.stderr = orig_stderr - - -def just_fix_windows_console(): - global fixed_windows_console - - if sys.platform != "win32": - return - if fixed_windows_console: - return - if wrapped_stdout is not None or wrapped_stderr is not None: - # Someone already ran init() and it did stuff, so we won't second-guess them - return - - # On newer versions of Windows, AnsiToWin32.__init__ will implicitly enable the - # native ANSI support in the console as a side-effect. We only need to actually - # replace sys.stdout/stderr if we're in the old-style conversion mode. - new_stdout = AnsiToWin32(sys.stdout, convert=None, strip=None, autoreset=False) - if new_stdout.convert: - sys.stdout = new_stdout - new_stderr = AnsiToWin32(sys.stderr, convert=None, strip=None, autoreset=False) - if new_stderr.convert: - sys.stderr = new_stderr - - fixed_windows_console = True - -@contextlib.contextmanager -def colorama_text(*args, **kwargs): - init(*args, **kwargs) - try: - yield - finally: - deinit() - - -def reinit(): - if wrapped_stdout is not None: - sys.stdout = wrapped_stdout - if wrapped_stderr is not None: - sys.stderr = wrapped_stderr - - -def wrap_stream(stream, convert, strip, autoreset, wrap): - if wrap: - wrapper = AnsiToWin32(stream, - convert=convert, strip=strip, autoreset=autoreset) - if wrapper.should_wrap(): - stream = wrapper.stream - return stream - - -# Use this for initial setup as well, to reduce code duplication -_wipe_internal_state_for_tests() diff --git a/venv/Lib/site-packages/colorama/tests/__init__.py b/venv/Lib/site-packages/colorama/tests/__init__.py deleted file mode 100644 index 8c5661e..0000000 --- a/venv/Lib/site-packages/colorama/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 635fe78..0000000 Binary files a/venv/Lib/site-packages/colorama/tests/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/ansi_test.cpython-310.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/ansi_test.cpython-310.pyc deleted file mode 100644 index 3431f10..0000000 Binary files a/venv/Lib/site-packages/colorama/tests/__pycache__/ansi_test.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-310.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-310.pyc deleted file mode 100644 index 1183caf..0000000 Binary files a/venv/Lib/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/initialise_test.cpython-310.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/initialise_test.cpython-310.pyc deleted file mode 100644 index 9ef751e..0000000 Binary files a/venv/Lib/site-packages/colorama/tests/__pycache__/initialise_test.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/isatty_test.cpython-310.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/isatty_test.cpython-310.pyc deleted file mode 100644 index e7ca3cb..0000000 Binary files a/venv/Lib/site-packages/colorama/tests/__pycache__/isatty_test.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/utils.cpython-310.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/utils.cpython-310.pyc deleted file mode 100644 index 9d998fb..0000000 Binary files a/venv/Lib/site-packages/colorama/tests/__pycache__/utils.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/winterm_test.cpython-310.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/winterm_test.cpython-310.pyc deleted file mode 100644 index fc1cd1b..0000000 Binary files a/venv/Lib/site-packages/colorama/tests/__pycache__/winterm_test.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/colorama/tests/ansi_test.py b/venv/Lib/site-packages/colorama/tests/ansi_test.py deleted file mode 100644 index 0a20c80..0000000 --- a/venv/Lib/site-packages/colorama/tests/ansi_test.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -import sys -from unittest import TestCase, main - -from ..ansi import Back, Fore, Style -from ..ansitowin32 import AnsiToWin32 - -stdout_orig = sys.stdout -stderr_orig = sys.stderr - - -class AnsiTest(TestCase): - - def setUp(self): - # sanity check: stdout should be a file or StringIO object. - # It will only be AnsiToWin32 if init() has previously wrapped it - self.assertNotEqual(type(sys.stdout), AnsiToWin32) - self.assertNotEqual(type(sys.stderr), AnsiToWin32) - - def tearDown(self): - sys.stdout = stdout_orig - sys.stderr = stderr_orig - - - def testForeAttributes(self): - self.assertEqual(Fore.BLACK, '\033[30m') - self.assertEqual(Fore.RED, '\033[31m') - self.assertEqual(Fore.GREEN, '\033[32m') - self.assertEqual(Fore.YELLOW, '\033[33m') - self.assertEqual(Fore.BLUE, '\033[34m') - self.assertEqual(Fore.MAGENTA, '\033[35m') - self.assertEqual(Fore.CYAN, '\033[36m') - self.assertEqual(Fore.WHITE, '\033[37m') - self.assertEqual(Fore.RESET, '\033[39m') - - # Check the light, extended versions. - self.assertEqual(Fore.LIGHTBLACK_EX, '\033[90m') - self.assertEqual(Fore.LIGHTRED_EX, '\033[91m') - self.assertEqual(Fore.LIGHTGREEN_EX, '\033[92m') - self.assertEqual(Fore.LIGHTYELLOW_EX, '\033[93m') - self.assertEqual(Fore.LIGHTBLUE_EX, '\033[94m') - self.assertEqual(Fore.LIGHTMAGENTA_EX, '\033[95m') - self.assertEqual(Fore.LIGHTCYAN_EX, '\033[96m') - self.assertEqual(Fore.LIGHTWHITE_EX, '\033[97m') - - - def testBackAttributes(self): - self.assertEqual(Back.BLACK, '\033[40m') - self.assertEqual(Back.RED, '\033[41m') - self.assertEqual(Back.GREEN, '\033[42m') - self.assertEqual(Back.YELLOW, '\033[43m') - self.assertEqual(Back.BLUE, '\033[44m') - self.assertEqual(Back.MAGENTA, '\033[45m') - self.assertEqual(Back.CYAN, '\033[46m') - self.assertEqual(Back.WHITE, '\033[47m') - self.assertEqual(Back.RESET, '\033[49m') - - # Check the light, extended versions. - self.assertEqual(Back.LIGHTBLACK_EX, '\033[100m') - self.assertEqual(Back.LIGHTRED_EX, '\033[101m') - self.assertEqual(Back.LIGHTGREEN_EX, '\033[102m') - self.assertEqual(Back.LIGHTYELLOW_EX, '\033[103m') - self.assertEqual(Back.LIGHTBLUE_EX, '\033[104m') - self.assertEqual(Back.LIGHTMAGENTA_EX, '\033[105m') - self.assertEqual(Back.LIGHTCYAN_EX, '\033[106m') - self.assertEqual(Back.LIGHTWHITE_EX, '\033[107m') - - - def testStyleAttributes(self): - self.assertEqual(Style.DIM, '\033[2m') - self.assertEqual(Style.NORMAL, '\033[22m') - self.assertEqual(Style.BRIGHT, '\033[1m') - - -if __name__ == '__main__': - main() diff --git a/venv/Lib/site-packages/colorama/tests/ansitowin32_test.py b/venv/Lib/site-packages/colorama/tests/ansitowin32_test.py deleted file mode 100644 index 91ca551..0000000 --- a/venv/Lib/site-packages/colorama/tests/ansitowin32_test.py +++ /dev/null @@ -1,294 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -from io import StringIO, TextIOWrapper -from unittest import TestCase, main -try: - from contextlib import ExitStack -except ImportError: - # python 2 - from contextlib2 import ExitStack - -try: - from unittest.mock import MagicMock, Mock, patch -except ImportError: - from mock import MagicMock, Mock, patch - -from ..ansitowin32 import AnsiToWin32, StreamWrapper -from ..win32 import ENABLE_VIRTUAL_TERMINAL_PROCESSING -from .utils import osname - - -class StreamWrapperTest(TestCase): - - def testIsAProxy(self): - mockStream = Mock() - wrapper = StreamWrapper(mockStream, None) - self.assertTrue( wrapper.random_attr is mockStream.random_attr ) - - def testDelegatesWrite(self): - mockStream = Mock() - mockConverter = Mock() - wrapper = StreamWrapper(mockStream, mockConverter) - wrapper.write('hello') - self.assertTrue(mockConverter.write.call_args, (('hello',), {})) - - def testDelegatesContext(self): - mockConverter = Mock() - s = StringIO() - with StreamWrapper(s, mockConverter) as fp: - fp.write(u'hello') - self.assertTrue(s.closed) - - def testProxyNoContextManager(self): - mockStream = MagicMock() - mockStream.__enter__.side_effect = AttributeError() - mockConverter = Mock() - with self.assertRaises(AttributeError) as excinfo: - with StreamWrapper(mockStream, mockConverter) as wrapper: - wrapper.write('hello') - - def test_closed_shouldnt_raise_on_closed_stream(self): - stream = StringIO() - stream.close() - wrapper = StreamWrapper(stream, None) - self.assertEqual(wrapper.closed, True) - - def test_closed_shouldnt_raise_on_detached_stream(self): - stream = TextIOWrapper(StringIO()) - stream.detach() - wrapper = StreamWrapper(stream, None) - self.assertEqual(wrapper.closed, True) - -class AnsiToWin32Test(TestCase): - - def testInit(self): - mockStdout = Mock() - auto = Mock() - stream = AnsiToWin32(mockStdout, autoreset=auto) - self.assertEqual(stream.wrapped, mockStdout) - self.assertEqual(stream.autoreset, auto) - - @patch('colorama.ansitowin32.winterm', None) - @patch('colorama.ansitowin32.winapi_test', lambda *_: True) - def testStripIsTrueOnWindows(self): - with osname('nt'): - mockStdout = Mock() - stream = AnsiToWin32(mockStdout) - self.assertTrue(stream.strip) - - def testStripIsFalseOffWindows(self): - with osname('posix'): - mockStdout = Mock(closed=False) - stream = AnsiToWin32(mockStdout) - self.assertFalse(stream.strip) - - def testWriteStripsAnsi(self): - mockStdout = Mock() - stream = AnsiToWin32(mockStdout) - stream.wrapped = Mock() - stream.write_and_convert = Mock() - stream.strip = True - - stream.write('abc') - - self.assertFalse(stream.wrapped.write.called) - self.assertEqual(stream.write_and_convert.call_args, (('abc',), {})) - - def testWriteDoesNotStripAnsi(self): - mockStdout = Mock() - stream = AnsiToWin32(mockStdout) - stream.wrapped = Mock() - stream.write_and_convert = Mock() - stream.strip = False - stream.convert = False - - stream.write('abc') - - self.assertFalse(stream.write_and_convert.called) - self.assertEqual(stream.wrapped.write.call_args, (('abc',), {})) - - def assert_autoresets(self, convert, autoreset=True): - stream = AnsiToWin32(Mock()) - stream.convert = convert - stream.reset_all = Mock() - stream.autoreset = autoreset - stream.winterm = Mock() - - stream.write('abc') - - self.assertEqual(stream.reset_all.called, autoreset) - - def testWriteAutoresets(self): - self.assert_autoresets(convert=True) - self.assert_autoresets(convert=False) - self.assert_autoresets(convert=True, autoreset=False) - self.assert_autoresets(convert=False, autoreset=False) - - def testWriteAndConvertWritesPlainText(self): - stream = AnsiToWin32(Mock()) - stream.write_and_convert( 'abc' ) - self.assertEqual( stream.wrapped.write.call_args, (('abc',), {}) ) - - def testWriteAndConvertStripsAllValidAnsi(self): - stream = AnsiToWin32(Mock()) - stream.call_win32 = Mock() - data = [ - 'abc\033[mdef', - 'abc\033[0mdef', - 'abc\033[2mdef', - 'abc\033[02mdef', - 'abc\033[002mdef', - 'abc\033[40mdef', - 'abc\033[040mdef', - 'abc\033[0;1mdef', - 'abc\033[40;50mdef', - 'abc\033[50;30;40mdef', - 'abc\033[Adef', - 'abc\033[0Gdef', - 'abc\033[1;20;128Hdef', - ] - for datum in data: - stream.wrapped.write.reset_mock() - stream.write_and_convert( datum ) - self.assertEqual( - [args[0] for args in stream.wrapped.write.call_args_list], - [ ('abc',), ('def',) ] - ) - - def testWriteAndConvertSkipsEmptySnippets(self): - stream = AnsiToWin32(Mock()) - stream.call_win32 = Mock() - stream.write_and_convert( '\033[40m\033[41m' ) - self.assertFalse( stream.wrapped.write.called ) - - def testWriteAndConvertCallsWin32WithParamsAndCommand(self): - stream = AnsiToWin32(Mock()) - stream.convert = True - stream.call_win32 = Mock() - stream.extract_params = Mock(return_value='params') - data = { - 'abc\033[adef': ('a', 'params'), - 'abc\033[;;bdef': ('b', 'params'), - 'abc\033[0cdef': ('c', 'params'), - 'abc\033[;;0;;Gdef': ('G', 'params'), - 'abc\033[1;20;128Hdef': ('H', 'params'), - } - for datum, expected in data.items(): - stream.call_win32.reset_mock() - stream.write_and_convert( datum ) - self.assertEqual( stream.call_win32.call_args[0], expected ) - - def test_reset_all_shouldnt_raise_on_closed_orig_stdout(self): - stream = StringIO() - converter = AnsiToWin32(stream) - stream.close() - - converter.reset_all() - - def test_wrap_shouldnt_raise_on_closed_orig_stdout(self): - stream = StringIO() - stream.close() - with \ - patch("colorama.ansitowin32.os.name", "nt"), \ - patch("colorama.ansitowin32.winapi_test", lambda: True): - converter = AnsiToWin32(stream) - self.assertTrue(converter.strip) - self.assertFalse(converter.convert) - - def test_wrap_shouldnt_raise_on_missing_closed_attr(self): - with \ - patch("colorama.ansitowin32.os.name", "nt"), \ - patch("colorama.ansitowin32.winapi_test", lambda: True): - converter = AnsiToWin32(object()) - self.assertTrue(converter.strip) - self.assertFalse(converter.convert) - - def testExtractParams(self): - stream = AnsiToWin32(Mock()) - data = { - '': (0,), - ';;': (0,), - '2': (2,), - ';;002;;': (2,), - '0;1': (0, 1), - ';;003;;456;;': (3, 456), - '11;22;33;44;55': (11, 22, 33, 44, 55), - } - for datum, expected in data.items(): - self.assertEqual(stream.extract_params('m', datum), expected) - - def testCallWin32UsesLookup(self): - listener = Mock() - stream = AnsiToWin32(listener) - stream.win32_calls = { - 1: (lambda *_, **__: listener(11),), - 2: (lambda *_, **__: listener(22),), - 3: (lambda *_, **__: listener(33),), - } - stream.call_win32('m', (3, 1, 99, 2)) - self.assertEqual( - [a[0][0] for a in listener.call_args_list], - [33, 11, 22] ) - - def test_osc_codes(self): - mockStdout = Mock() - stream = AnsiToWin32(mockStdout, convert=True) - with patch('colorama.ansitowin32.winterm') as winterm: - data = [ - '\033]0\x07', # missing arguments - '\033]0;foo\x08', # wrong OSC command - '\033]0;colorama_test_title\x07', # should work - '\033]1;colorama_test_title\x07', # wrong set command - '\033]2;colorama_test_title\x07', # should work - '\033]' + ';' * 64 + '\x08', # see issue #247 - ] - for code in data: - stream.write(code) - self.assertEqual(winterm.set_title.call_count, 2) - - def test_native_windows_ansi(self): - with ExitStack() as stack: - def p(a, b): - stack.enter_context(patch(a, b, create=True)) - # Pretend to be on Windows - p("colorama.ansitowin32.os.name", "nt") - p("colorama.ansitowin32.winapi_test", lambda: True) - p("colorama.win32.winapi_test", lambda: True) - p("colorama.winterm.win32.windll", "non-None") - p("colorama.winterm.get_osfhandle", lambda _: 1234) - - # Pretend that our mock stream has native ANSI support - p( - "colorama.winterm.win32.GetConsoleMode", - lambda _: ENABLE_VIRTUAL_TERMINAL_PROCESSING, - ) - SetConsoleMode = Mock() - p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) - - stdout = Mock() - stdout.closed = False - stdout.isatty.return_value = True - stdout.fileno.return_value = 1 - - # Our fake console says it has native vt support, so AnsiToWin32 should - # enable that support and do nothing else. - stream = AnsiToWin32(stdout) - SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) - self.assertFalse(stream.strip) - self.assertFalse(stream.convert) - self.assertFalse(stream.should_wrap()) - - # Now let's pretend we're on an old Windows console, that doesn't have - # native ANSI support. - p("colorama.winterm.win32.GetConsoleMode", lambda _: 0) - SetConsoleMode = Mock() - p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) - - stream = AnsiToWin32(stdout) - SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) - self.assertTrue(stream.strip) - self.assertTrue(stream.convert) - self.assertTrue(stream.should_wrap()) - - -if __name__ == '__main__': - main() diff --git a/venv/Lib/site-packages/colorama/tests/initialise_test.py b/venv/Lib/site-packages/colorama/tests/initialise_test.py deleted file mode 100644 index 89f9b07..0000000 --- a/venv/Lib/site-packages/colorama/tests/initialise_test.py +++ /dev/null @@ -1,189 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -import sys -from unittest import TestCase, main, skipUnless - -try: - from unittest.mock import patch, Mock -except ImportError: - from mock import patch, Mock - -from ..ansitowin32 import StreamWrapper -from ..initialise import init, just_fix_windows_console, _wipe_internal_state_for_tests -from .utils import osname, replace_by - -orig_stdout = sys.stdout -orig_stderr = sys.stderr - - -class InitTest(TestCase): - - @skipUnless(sys.stdout.isatty(), "sys.stdout is not a tty") - def setUp(self): - # sanity check - self.assertNotWrapped() - - def tearDown(self): - _wipe_internal_state_for_tests() - sys.stdout = orig_stdout - sys.stderr = orig_stderr - - def assertWrapped(self): - self.assertIsNot(sys.stdout, orig_stdout, 'stdout should be wrapped') - self.assertIsNot(sys.stderr, orig_stderr, 'stderr should be wrapped') - self.assertTrue(isinstance(sys.stdout, StreamWrapper), - 'bad stdout wrapper') - self.assertTrue(isinstance(sys.stderr, StreamWrapper), - 'bad stderr wrapper') - - def assertNotWrapped(self): - self.assertIs(sys.stdout, orig_stdout, 'stdout should not be wrapped') - self.assertIs(sys.stderr, orig_stderr, 'stderr should not be wrapped') - - @patch('colorama.initialise.reset_all') - @patch('colorama.ansitowin32.winapi_test', lambda *_: True) - @patch('colorama.ansitowin32.enable_vt_processing', lambda *_: False) - def testInitWrapsOnWindows(self, _): - with osname("nt"): - init() - self.assertWrapped() - - @patch('colorama.initialise.reset_all') - @patch('colorama.ansitowin32.winapi_test', lambda *_: False) - def testInitDoesntWrapOnEmulatedWindows(self, _): - with osname("nt"): - init() - self.assertNotWrapped() - - def testInitDoesntWrapOnNonWindows(self): - with osname("posix"): - init() - self.assertNotWrapped() - - def testInitDoesntWrapIfNone(self): - with replace_by(None): - init() - # We can't use assertNotWrapped here because replace_by(None) - # changes stdout/stderr already. - self.assertIsNone(sys.stdout) - self.assertIsNone(sys.stderr) - - def testInitAutoresetOnWrapsOnAllPlatforms(self): - with osname("posix"): - init(autoreset=True) - self.assertWrapped() - - def testInitWrapOffDoesntWrapOnWindows(self): - with osname("nt"): - init(wrap=False) - self.assertNotWrapped() - - def testInitWrapOffIncompatibleWithAutoresetOn(self): - self.assertRaises(ValueError, lambda: init(autoreset=True, wrap=False)) - - @patch('colorama.win32.SetConsoleTextAttribute') - @patch('colorama.initialise.AnsiToWin32') - def testAutoResetPassedOn(self, mockATW32, _): - with osname("nt"): - init(autoreset=True) - self.assertEqual(len(mockATW32.call_args_list), 2) - self.assertEqual(mockATW32.call_args_list[1][1]['autoreset'], True) - self.assertEqual(mockATW32.call_args_list[0][1]['autoreset'], True) - - @patch('colorama.initialise.AnsiToWin32') - def testAutoResetChangeable(self, mockATW32): - with osname("nt"): - init() - - init(autoreset=True) - self.assertEqual(len(mockATW32.call_args_list), 4) - self.assertEqual(mockATW32.call_args_list[2][1]['autoreset'], True) - self.assertEqual(mockATW32.call_args_list[3][1]['autoreset'], True) - - init() - self.assertEqual(len(mockATW32.call_args_list), 6) - self.assertEqual( - mockATW32.call_args_list[4][1]['autoreset'], False) - self.assertEqual( - mockATW32.call_args_list[5][1]['autoreset'], False) - - - @patch('colorama.initialise.atexit.register') - def testAtexitRegisteredOnlyOnce(self, mockRegister): - init() - self.assertTrue(mockRegister.called) - mockRegister.reset_mock() - init() - self.assertFalse(mockRegister.called) - - -class JustFixWindowsConsoleTest(TestCase): - def _reset(self): - _wipe_internal_state_for_tests() - sys.stdout = orig_stdout - sys.stderr = orig_stderr - - def tearDown(self): - self._reset() - - @patch("colorama.ansitowin32.winapi_test", lambda: True) - def testJustFixWindowsConsole(self): - if sys.platform != "win32": - # just_fix_windows_console should be a no-op - just_fix_windows_console() - self.assertIs(sys.stdout, orig_stdout) - self.assertIs(sys.stderr, orig_stderr) - else: - def fake_std(): - # Emulate stdout=not a tty, stderr=tty - # to check that we handle both cases correctly - stdout = Mock() - stdout.closed = False - stdout.isatty.return_value = False - stdout.fileno.return_value = 1 - sys.stdout = stdout - - stderr = Mock() - stderr.closed = False - stderr.isatty.return_value = True - stderr.fileno.return_value = 2 - sys.stderr = stderr - - for native_ansi in [False, True]: - with patch( - 'colorama.ansitowin32.enable_vt_processing', - lambda *_: native_ansi - ): - self._reset() - fake_std() - - # Regular single-call test - prev_stdout = sys.stdout - prev_stderr = sys.stderr - just_fix_windows_console() - self.assertIs(sys.stdout, prev_stdout) - if native_ansi: - self.assertIs(sys.stderr, prev_stderr) - else: - self.assertIsNot(sys.stderr, prev_stderr) - - # second call without resetting is always a no-op - prev_stdout = sys.stdout - prev_stderr = sys.stderr - just_fix_windows_console() - self.assertIs(sys.stdout, prev_stdout) - self.assertIs(sys.stderr, prev_stderr) - - self._reset() - fake_std() - - # If init() runs first, just_fix_windows_console should be a no-op - init() - prev_stdout = sys.stdout - prev_stderr = sys.stderr - just_fix_windows_console() - self.assertIs(prev_stdout, sys.stdout) - self.assertIs(prev_stderr, sys.stderr) - - -if __name__ == '__main__': - main() diff --git a/venv/Lib/site-packages/colorama/tests/isatty_test.py b/venv/Lib/site-packages/colorama/tests/isatty_test.py deleted file mode 100644 index 0f84e4b..0000000 --- a/venv/Lib/site-packages/colorama/tests/isatty_test.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -import sys -from unittest import TestCase, main - -from ..ansitowin32 import StreamWrapper, AnsiToWin32 -from .utils import pycharm, replace_by, replace_original_by, StreamTTY, StreamNonTTY - - -def is_a_tty(stream): - return StreamWrapper(stream, None).isatty() - -class IsattyTest(TestCase): - - def test_TTY(self): - tty = StreamTTY() - self.assertTrue(is_a_tty(tty)) - with pycharm(): - self.assertTrue(is_a_tty(tty)) - - def test_nonTTY(self): - non_tty = StreamNonTTY() - self.assertFalse(is_a_tty(non_tty)) - with pycharm(): - self.assertFalse(is_a_tty(non_tty)) - - def test_withPycharm(self): - with pycharm(): - self.assertTrue(is_a_tty(sys.stderr)) - self.assertTrue(is_a_tty(sys.stdout)) - - def test_withPycharmTTYOverride(self): - tty = StreamTTY() - with pycharm(), replace_by(tty): - self.assertTrue(is_a_tty(tty)) - - def test_withPycharmNonTTYOverride(self): - non_tty = StreamNonTTY() - with pycharm(), replace_by(non_tty): - self.assertFalse(is_a_tty(non_tty)) - - def test_withPycharmNoneOverride(self): - with pycharm(): - with replace_by(None), replace_original_by(None): - self.assertFalse(is_a_tty(None)) - self.assertFalse(is_a_tty(StreamNonTTY())) - self.assertTrue(is_a_tty(StreamTTY())) - - def test_withPycharmStreamWrapped(self): - with pycharm(): - self.assertTrue(AnsiToWin32(StreamTTY()).stream.isatty()) - self.assertFalse(AnsiToWin32(StreamNonTTY()).stream.isatty()) - self.assertTrue(AnsiToWin32(sys.stdout).stream.isatty()) - self.assertTrue(AnsiToWin32(sys.stderr).stream.isatty()) - - -if __name__ == '__main__': - main() diff --git a/venv/Lib/site-packages/colorama/tests/utils.py b/venv/Lib/site-packages/colorama/tests/utils.py deleted file mode 100644 index 472fafb..0000000 --- a/venv/Lib/site-packages/colorama/tests/utils.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -from contextlib import contextmanager -from io import StringIO -import sys -import os - - -class StreamTTY(StringIO): - def isatty(self): - return True - -class StreamNonTTY(StringIO): - def isatty(self): - return False - -@contextmanager -def osname(name): - orig = os.name - os.name = name - yield - os.name = orig - -@contextmanager -def replace_by(stream): - orig_stdout = sys.stdout - orig_stderr = sys.stderr - sys.stdout = stream - sys.stderr = stream - yield - sys.stdout = orig_stdout - sys.stderr = orig_stderr - -@contextmanager -def replace_original_by(stream): - orig_stdout = sys.__stdout__ - orig_stderr = sys.__stderr__ - sys.__stdout__ = stream - sys.__stderr__ = stream - yield - sys.__stdout__ = orig_stdout - sys.__stderr__ = orig_stderr - -@contextmanager -def pycharm(): - os.environ["PYCHARM_HOSTED"] = "1" - non_tty = StreamNonTTY() - with replace_by(non_tty), replace_original_by(non_tty): - yield - del os.environ["PYCHARM_HOSTED"] diff --git a/venv/Lib/site-packages/colorama/tests/winterm_test.py b/venv/Lib/site-packages/colorama/tests/winterm_test.py deleted file mode 100644 index d0955f9..0000000 --- a/venv/Lib/site-packages/colorama/tests/winterm_test.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -import sys -from unittest import TestCase, main, skipUnless - -try: - from unittest.mock import Mock, patch -except ImportError: - from mock import Mock, patch - -from ..winterm import WinColor, WinStyle, WinTerm - - -class WinTermTest(TestCase): - - @patch('colorama.winterm.win32') - def testInit(self, mockWin32): - mockAttr = Mock() - mockAttr.wAttributes = 7 + 6 * 16 + 8 - mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr - term = WinTerm() - self.assertEqual(term._fore, 7) - self.assertEqual(term._back, 6) - self.assertEqual(term._style, 8) - - @skipUnless(sys.platform.startswith("win"), "requires Windows") - def testGetAttrs(self): - term = WinTerm() - - term._fore = 0 - term._back = 0 - term._style = 0 - self.assertEqual(term.get_attrs(), 0) - - term._fore = WinColor.YELLOW - self.assertEqual(term.get_attrs(), WinColor.YELLOW) - - term._back = WinColor.MAGENTA - self.assertEqual( - term.get_attrs(), - WinColor.YELLOW + WinColor.MAGENTA * 16) - - term._style = WinStyle.BRIGHT - self.assertEqual( - term.get_attrs(), - WinColor.YELLOW + WinColor.MAGENTA * 16 + WinStyle.BRIGHT) - - @patch('colorama.winterm.win32') - def testResetAll(self, mockWin32): - mockAttr = Mock() - mockAttr.wAttributes = 1 + 2 * 16 + 8 - mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr - term = WinTerm() - - term.set_console = Mock() - term._fore = -1 - term._back = -1 - term._style = -1 - - term.reset_all() - - self.assertEqual(term._fore, 1) - self.assertEqual(term._back, 2) - self.assertEqual(term._style, 8) - self.assertEqual(term.set_console.called, True) - - @skipUnless(sys.platform.startswith("win"), "requires Windows") - def testFore(self): - term = WinTerm() - term.set_console = Mock() - term._fore = 0 - - term.fore(5) - - self.assertEqual(term._fore, 5) - self.assertEqual(term.set_console.called, True) - - @skipUnless(sys.platform.startswith("win"), "requires Windows") - def testBack(self): - term = WinTerm() - term.set_console = Mock() - term._back = 0 - - term.back(5) - - self.assertEqual(term._back, 5) - self.assertEqual(term.set_console.called, True) - - @skipUnless(sys.platform.startswith("win"), "requires Windows") - def testStyle(self): - term = WinTerm() - term.set_console = Mock() - term._style = 0 - - term.style(22) - - self.assertEqual(term._style, 22) - self.assertEqual(term.set_console.called, True) - - @patch('colorama.winterm.win32') - def testSetConsole(self, mockWin32): - mockAttr = Mock() - mockAttr.wAttributes = 0 - mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr - term = WinTerm() - term.windll = Mock() - - term.set_console() - - self.assertEqual( - mockWin32.SetConsoleTextAttribute.call_args, - ((mockWin32.STDOUT, term.get_attrs()), {}) - ) - - @patch('colorama.winterm.win32') - def testSetConsoleOnStderr(self, mockWin32): - mockAttr = Mock() - mockAttr.wAttributes = 0 - mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr - term = WinTerm() - term.windll = Mock() - - term.set_console(on_stderr=True) - - self.assertEqual( - mockWin32.SetConsoleTextAttribute.call_args, - ((mockWin32.STDERR, term.get_attrs()), {}) - ) - - -if __name__ == '__main__': - main() diff --git a/venv/Lib/site-packages/colorama/win32.py b/venv/Lib/site-packages/colorama/win32.py deleted file mode 100644 index 841b0e2..0000000 --- a/venv/Lib/site-packages/colorama/win32.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. - -# from winbase.h -STDOUT = -11 -STDERR = -12 - -ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 - -try: - import ctypes - from ctypes import LibraryLoader - windll = LibraryLoader(ctypes.WinDLL) - from ctypes import wintypes -except (AttributeError, ImportError): - windll = None - SetConsoleTextAttribute = lambda *_: None - winapi_test = lambda *_: None -else: - from ctypes import byref, Structure, c_char, POINTER - - COORD = wintypes._COORD - - class CONSOLE_SCREEN_BUFFER_INFO(Structure): - """struct in wincon.h.""" - _fields_ = [ - ("dwSize", COORD), - ("dwCursorPosition", COORD), - ("wAttributes", wintypes.WORD), - ("srWindow", wintypes.SMALL_RECT), - ("dwMaximumWindowSize", COORD), - ] - def __str__(self): - return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( - self.dwSize.Y, self.dwSize.X - , self.dwCursorPosition.Y, self.dwCursorPosition.X - , self.wAttributes - , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right - , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X - ) - - _GetStdHandle = windll.kernel32.GetStdHandle - _GetStdHandle.argtypes = [ - wintypes.DWORD, - ] - _GetStdHandle.restype = wintypes.HANDLE - - _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo - _GetConsoleScreenBufferInfo.argtypes = [ - wintypes.HANDLE, - POINTER(CONSOLE_SCREEN_BUFFER_INFO), - ] - _GetConsoleScreenBufferInfo.restype = wintypes.BOOL - - _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute - _SetConsoleTextAttribute.argtypes = [ - wintypes.HANDLE, - wintypes.WORD, - ] - _SetConsoleTextAttribute.restype = wintypes.BOOL - - _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition - _SetConsoleCursorPosition.argtypes = [ - wintypes.HANDLE, - COORD, - ] - _SetConsoleCursorPosition.restype = wintypes.BOOL - - _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA - _FillConsoleOutputCharacterA.argtypes = [ - wintypes.HANDLE, - c_char, - wintypes.DWORD, - COORD, - POINTER(wintypes.DWORD), - ] - _FillConsoleOutputCharacterA.restype = wintypes.BOOL - - _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute - _FillConsoleOutputAttribute.argtypes = [ - wintypes.HANDLE, - wintypes.WORD, - wintypes.DWORD, - COORD, - POINTER(wintypes.DWORD), - ] - _FillConsoleOutputAttribute.restype = wintypes.BOOL - - _SetConsoleTitleW = windll.kernel32.SetConsoleTitleW - _SetConsoleTitleW.argtypes = [ - wintypes.LPCWSTR - ] - _SetConsoleTitleW.restype = wintypes.BOOL - - _GetConsoleMode = windll.kernel32.GetConsoleMode - _GetConsoleMode.argtypes = [ - wintypes.HANDLE, - POINTER(wintypes.DWORD) - ] - _GetConsoleMode.restype = wintypes.BOOL - - _SetConsoleMode = windll.kernel32.SetConsoleMode - _SetConsoleMode.argtypes = [ - wintypes.HANDLE, - wintypes.DWORD - ] - _SetConsoleMode.restype = wintypes.BOOL - - def _winapi_test(handle): - csbi = CONSOLE_SCREEN_BUFFER_INFO() - success = _GetConsoleScreenBufferInfo( - handle, byref(csbi)) - return bool(success) - - def winapi_test(): - return any(_winapi_test(h) for h in - (_GetStdHandle(STDOUT), _GetStdHandle(STDERR))) - - def GetConsoleScreenBufferInfo(stream_id=STDOUT): - handle = _GetStdHandle(stream_id) - csbi = CONSOLE_SCREEN_BUFFER_INFO() - success = _GetConsoleScreenBufferInfo( - handle, byref(csbi)) - return csbi - - def SetConsoleTextAttribute(stream_id, attrs): - handle = _GetStdHandle(stream_id) - return _SetConsoleTextAttribute(handle, attrs) - - def SetConsoleCursorPosition(stream_id, position, adjust=True): - position = COORD(*position) - # If the position is out of range, do nothing. - if position.Y <= 0 or position.X <= 0: - return - # Adjust for Windows' SetConsoleCursorPosition: - # 1. being 0-based, while ANSI is 1-based. - # 2. expecting (x,y), while ANSI uses (y,x). - adjusted_position = COORD(position.Y - 1, position.X - 1) - if adjust: - # Adjust for viewport's scroll position - sr = GetConsoleScreenBufferInfo(STDOUT).srWindow - adjusted_position.Y += sr.Top - adjusted_position.X += sr.Left - # Resume normal processing - handle = _GetStdHandle(stream_id) - return _SetConsoleCursorPosition(handle, adjusted_position) - - def FillConsoleOutputCharacter(stream_id, char, length, start): - handle = _GetStdHandle(stream_id) - char = c_char(char.encode()) - length = wintypes.DWORD(length) - num_written = wintypes.DWORD(0) - # Note that this is hard-coded for ANSI (vs wide) bytes. - success = _FillConsoleOutputCharacterA( - handle, char, length, start, byref(num_written)) - return num_written.value - - def FillConsoleOutputAttribute(stream_id, attr, length, start): - ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' - handle = _GetStdHandle(stream_id) - attribute = wintypes.WORD(attr) - length = wintypes.DWORD(length) - num_written = wintypes.DWORD(0) - # Note that this is hard-coded for ANSI (vs wide) bytes. - return _FillConsoleOutputAttribute( - handle, attribute, length, start, byref(num_written)) - - def SetConsoleTitle(title): - return _SetConsoleTitleW(title) - - def GetConsoleMode(handle): - mode = wintypes.DWORD() - success = _GetConsoleMode(handle, byref(mode)) - if not success: - raise ctypes.WinError() - return mode.value - - def SetConsoleMode(handle, mode): - success = _SetConsoleMode(handle, mode) - if not success: - raise ctypes.WinError() diff --git a/venv/Lib/site-packages/colorama/winterm.py b/venv/Lib/site-packages/colorama/winterm.py deleted file mode 100644 index aad867e..0000000 --- a/venv/Lib/site-packages/colorama/winterm.py +++ /dev/null @@ -1,195 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -try: - from msvcrt import get_osfhandle -except ImportError: - def get_osfhandle(_): - raise OSError("This isn't windows!") - - -from . import win32 - -# from wincon.h -class WinColor(object): - BLACK = 0 - BLUE = 1 - GREEN = 2 - CYAN = 3 - RED = 4 - MAGENTA = 5 - YELLOW = 6 - GREY = 7 - -# from wincon.h -class WinStyle(object): - NORMAL = 0x00 # dim text, dim background - BRIGHT = 0x08 # bright text, dim background - BRIGHT_BACKGROUND = 0x80 # dim text, bright background - -class WinTerm(object): - - def __init__(self): - self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes - self.set_attrs(self._default) - self._default_fore = self._fore - self._default_back = self._back - self._default_style = self._style - # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style. - # So that LIGHT_EX colors and BRIGHT style do not clobber each other, - # we track them separately, since LIGHT_EX is overwritten by Fore/Back - # and BRIGHT is overwritten by Style codes. - self._light = 0 - - def get_attrs(self): - return self._fore + self._back * 16 + (self._style | self._light) - - def set_attrs(self, value): - self._fore = value & 7 - self._back = (value >> 4) & 7 - self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND) - - def reset_all(self, on_stderr=None): - self.set_attrs(self._default) - self.set_console(attrs=self._default) - self._light = 0 - - def fore(self, fore=None, light=False, on_stderr=False): - if fore is None: - fore = self._default_fore - self._fore = fore - # Emulate LIGHT_EX with BRIGHT Style - if light: - self._light |= WinStyle.BRIGHT - else: - self._light &= ~WinStyle.BRIGHT - self.set_console(on_stderr=on_stderr) - - def back(self, back=None, light=False, on_stderr=False): - if back is None: - back = self._default_back - self._back = back - # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style - if light: - self._light |= WinStyle.BRIGHT_BACKGROUND - else: - self._light &= ~WinStyle.BRIGHT_BACKGROUND - self.set_console(on_stderr=on_stderr) - - def style(self, style=None, on_stderr=False): - if style is None: - style = self._default_style - self._style = style - self.set_console(on_stderr=on_stderr) - - def set_console(self, attrs=None, on_stderr=False): - if attrs is None: - attrs = self.get_attrs() - handle = win32.STDOUT - if on_stderr: - handle = win32.STDERR - win32.SetConsoleTextAttribute(handle, attrs) - - def get_position(self, handle): - position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition - # Because Windows coordinates are 0-based, - # and win32.SetConsoleCursorPosition expects 1-based. - position.X += 1 - position.Y += 1 - return position - - def set_cursor_position(self, position=None, on_stderr=False): - if position is None: - # I'm not currently tracking the position, so there is no default. - # position = self.get_position() - return - handle = win32.STDOUT - if on_stderr: - handle = win32.STDERR - win32.SetConsoleCursorPosition(handle, position) - - def cursor_adjust(self, x, y, on_stderr=False): - handle = win32.STDOUT - if on_stderr: - handle = win32.STDERR - position = self.get_position(handle) - adjusted_position = (position.Y + y, position.X + x) - win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False) - - def erase_screen(self, mode=0, on_stderr=False): - # 0 should clear from the cursor to the end of the screen. - # 1 should clear from the cursor to the beginning of the screen. - # 2 should clear the entire screen, and move cursor to (1,1) - handle = win32.STDOUT - if on_stderr: - handle = win32.STDERR - csbi = win32.GetConsoleScreenBufferInfo(handle) - # get the number of character cells in the current buffer - cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y - # get number of character cells before current cursor position - cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X - if mode == 0: - from_coord = csbi.dwCursorPosition - cells_to_erase = cells_in_screen - cells_before_cursor - elif mode == 1: - from_coord = win32.COORD(0, 0) - cells_to_erase = cells_before_cursor - elif mode == 2: - from_coord = win32.COORD(0, 0) - cells_to_erase = cells_in_screen - else: - # invalid mode - return - # fill the entire screen with blanks - win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) - # now set the buffer's attributes accordingly - win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) - if mode == 2: - # put the cursor where needed - win32.SetConsoleCursorPosition(handle, (1, 1)) - - def erase_line(self, mode=0, on_stderr=False): - # 0 should clear from the cursor to the end of the line. - # 1 should clear from the cursor to the beginning of the line. - # 2 should clear the entire line. - handle = win32.STDOUT - if on_stderr: - handle = win32.STDERR - csbi = win32.GetConsoleScreenBufferInfo(handle) - if mode == 0: - from_coord = csbi.dwCursorPosition - cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X - elif mode == 1: - from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) - cells_to_erase = csbi.dwCursorPosition.X - elif mode == 2: - from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) - cells_to_erase = csbi.dwSize.X - else: - # invalid mode - return - # fill the entire screen with blanks - win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) - # now set the buffer's attributes accordingly - win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) - - def set_title(self, title): - win32.SetConsoleTitle(title) - - -def enable_vt_processing(fd): - if win32.windll is None or not win32.winapi_test(): - return False - - try: - handle = get_osfhandle(fd) - mode = win32.GetConsoleMode(handle) - win32.SetConsoleMode( - handle, - mode | win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING, - ) - - mode = win32.GetConsoleMode(handle) - if mode & win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING: - return True - # Can get TypeError in testsuite where 'fd' is a Mock() - except (OSError, TypeError): - return False diff --git a/venv/Lib/site-packages/distutils-precedence.pth b/venv/Lib/site-packages/distutils-precedence.pth deleted file mode 100644 index 7f009fe..0000000 --- a/venv/Lib/site-packages/distutils-precedence.pth +++ /dev/null @@ -1 +0,0 @@ -import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'local') == 'local'; enabled and __import__('_distutils_hack').add_shim(); diff --git a/venv/Lib/site-packages/dns/__init__.py b/venv/Lib/site-packages/dns/__init__.py deleted file mode 100644 index a4249b9..0000000 --- a/venv/Lib/site-packages/dns/__init__.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""dnspython DNS toolkit""" - -__all__ = [ - "asyncbackend", - "asyncquery", - "asyncresolver", - "dnssec", - "dnssecalgs", - "dnssectypes", - "e164", - "edns", - "entropy", - "exception", - "flags", - "immutable", - "inet", - "ipv4", - "ipv6", - "message", - "name", - "namedict", - "node", - "opcode", - "query", - "quic", - "rcode", - "rdata", - "rdataclass", - "rdataset", - "rdatatype", - "renderer", - "resolver", - "reversename", - "rrset", - "serial", - "set", - "tokenizer", - "transaction", - "tsig", - "tsigkeyring", - "ttl", - "rdtypes", - "update", - "version", - "versioned", - "wire", - "xfr", - "zone", - "zonetypes", - "zonefile", -] - -from dns.version import version as __version__ # noqa diff --git a/venv/Lib/site-packages/dns/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index cb2ebbb..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/_asyncbackend.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/_asyncbackend.cpython-310.pyc deleted file mode 100644 index d5f9b66..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/_asyncbackend.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/_asyncio_backend.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/_asyncio_backend.cpython-310.pyc deleted file mode 100644 index b74a972..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/_asyncio_backend.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/_ddr.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/_ddr.cpython-310.pyc deleted file mode 100644 index cac164d..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/_ddr.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/_features.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/_features.cpython-310.pyc deleted file mode 100644 index 70d0e02..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/_features.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/_immutable_ctx.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/_immutable_ctx.cpython-310.pyc deleted file mode 100644 index 7fd37f8..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/_immutable_ctx.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/_trio_backend.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/_trio_backend.cpython-310.pyc deleted file mode 100644 index 5ccfb6d..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/_trio_backend.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/asyncbackend.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/asyncbackend.cpython-310.pyc deleted file mode 100644 index 451e7cd..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/asyncbackend.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/asyncquery.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/asyncquery.cpython-310.pyc deleted file mode 100644 index 5683fa8..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/asyncquery.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/asyncresolver.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/asyncresolver.cpython-310.pyc deleted file mode 100644 index 862cdad..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/asyncresolver.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/dnssec.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/dnssec.cpython-310.pyc deleted file mode 100644 index efee713..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/dnssec.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/dnssectypes.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/dnssectypes.cpython-310.pyc deleted file mode 100644 index a4be971..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/dnssectypes.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/e164.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/e164.cpython-310.pyc deleted file mode 100644 index 6748352..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/e164.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/edns.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/edns.cpython-310.pyc deleted file mode 100644 index 9da5817..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/edns.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/entropy.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/entropy.cpython-310.pyc deleted file mode 100644 index b022e21..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/entropy.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/enum.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/enum.cpython-310.pyc deleted file mode 100644 index 769c0f7..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/enum.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/exception.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/exception.cpython-310.pyc deleted file mode 100644 index 64b3a50..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/exception.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/flags.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/flags.cpython-310.pyc deleted file mode 100644 index a6a3877..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/flags.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/grange.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/grange.cpython-310.pyc deleted file mode 100644 index 7861052..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/grange.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/immutable.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/immutable.cpython-310.pyc deleted file mode 100644 index d4489cb..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/immutable.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/inet.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/inet.cpython-310.pyc deleted file mode 100644 index 40ce2a3..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/inet.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/ipv4.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/ipv4.cpython-310.pyc deleted file mode 100644 index 5c4c5e4..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/ipv4.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/ipv6.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/ipv6.cpython-310.pyc deleted file mode 100644 index 290caaa..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/ipv6.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/message.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/message.cpython-310.pyc deleted file mode 100644 index 61a0028..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/message.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/name.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/name.cpython-310.pyc deleted file mode 100644 index 05f92a0..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/name.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/namedict.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/namedict.cpython-310.pyc deleted file mode 100644 index f206b39..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/namedict.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/nameserver.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/nameserver.cpython-310.pyc deleted file mode 100644 index 22ef5fa..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/nameserver.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/node.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/node.cpython-310.pyc deleted file mode 100644 index 039a86b..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/node.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/opcode.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/opcode.cpython-310.pyc deleted file mode 100644 index 9614e4a..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/opcode.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/query.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/query.cpython-310.pyc deleted file mode 100644 index f6fc167..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/query.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/rcode.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/rcode.cpython-310.pyc deleted file mode 100644 index 9844529..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/rcode.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/rdata.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/rdata.cpython-310.pyc deleted file mode 100644 index 9156da4..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/rdata.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/rdataclass.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/rdataclass.cpython-310.pyc deleted file mode 100644 index aeb4d1a..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/rdataclass.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/rdataset.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/rdataset.cpython-310.pyc deleted file mode 100644 index f528cc0..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/rdataset.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/rdatatype.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/rdatatype.cpython-310.pyc deleted file mode 100644 index e1eea2d..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/rdatatype.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/renderer.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/renderer.cpython-310.pyc deleted file mode 100644 index cf8cb46..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/renderer.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/resolver.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/resolver.cpython-310.pyc deleted file mode 100644 index 9826cb9..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/resolver.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/reversename.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/reversename.cpython-310.pyc deleted file mode 100644 index e5e944f..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/reversename.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/rrset.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/rrset.cpython-310.pyc deleted file mode 100644 index 5182c0c..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/rrset.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/serial.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/serial.cpython-310.pyc deleted file mode 100644 index 3a66ff5..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/serial.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/set.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/set.cpython-310.pyc deleted file mode 100644 index 3c2b2d4..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/set.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/tokenizer.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/tokenizer.cpython-310.pyc deleted file mode 100644 index 88e251a..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/tokenizer.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/transaction.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/transaction.cpython-310.pyc deleted file mode 100644 index ab7f43a..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/transaction.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/tsig.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/tsig.cpython-310.pyc deleted file mode 100644 index 321d8e8..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/tsig.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/tsigkeyring.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/tsigkeyring.cpython-310.pyc deleted file mode 100644 index 06bb6ad..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/tsigkeyring.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/ttl.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/ttl.cpython-310.pyc deleted file mode 100644 index 4e6f51d..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/ttl.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/update.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/update.cpython-310.pyc deleted file mode 100644 index df87fb0..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/update.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/version.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/version.cpython-310.pyc deleted file mode 100644 index 5da5362..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/version.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/versioned.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/versioned.cpython-310.pyc deleted file mode 100644 index f8a567b..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/versioned.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/win32util.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/win32util.cpython-310.pyc deleted file mode 100644 index 62106b3..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/win32util.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/wire.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/wire.cpython-310.pyc deleted file mode 100644 index bdeb8ff..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/wire.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/xfr.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/xfr.cpython-310.pyc deleted file mode 100644 index 6a55790..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/xfr.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/zone.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/zone.cpython-310.pyc deleted file mode 100644 index dea5b84..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/zone.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/zonefile.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/zonefile.cpython-310.pyc deleted file mode 100644 index 02858df..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/zonefile.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/__pycache__/zonetypes.cpython-310.pyc b/venv/Lib/site-packages/dns/__pycache__/zonetypes.cpython-310.pyc deleted file mode 100644 index 59afd37..0000000 Binary files a/venv/Lib/site-packages/dns/__pycache__/zonetypes.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/_asyncbackend.py b/venv/Lib/site-packages/dns/_asyncbackend.py deleted file mode 100644 index f6760fd..0000000 --- a/venv/Lib/site-packages/dns/_asyncbackend.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# This is a nullcontext for both sync and async. 3.7 has a nullcontext, -# but it is only for sync use. - - -class NullContext: - def __init__(self, enter_result=None): - self.enter_result = enter_result - - def __enter__(self): - return self.enter_result - - def __exit__(self, exc_type, exc_value, traceback): - pass - - async def __aenter__(self): - return self.enter_result - - async def __aexit__(self, exc_type, exc_value, traceback): - pass - - -# These are declared here so backends can import them without creating -# circular dependencies with dns.asyncbackend. - - -class Socket: # pragma: no cover - def __init__(self, family: int, type: int): - self.family = family - self.type = type - - async def close(self): - pass - - async def getpeername(self): - raise NotImplementedError - - async def getsockname(self): - raise NotImplementedError - - async def getpeercert(self, timeout): - raise NotImplementedError - - async def __aenter__(self): - return self - - async def __aexit__(self, exc_type, exc_value, traceback): - await self.close() - - -class DatagramSocket(Socket): # pragma: no cover - async def sendto(self, what, destination, timeout): - raise NotImplementedError - - async def recvfrom(self, size, timeout): - raise NotImplementedError - - -class StreamSocket(Socket): # pragma: no cover - async def sendall(self, what, timeout): - raise NotImplementedError - - async def recv(self, size, timeout): - raise NotImplementedError - - -class NullTransport: - async def connect_tcp(self, host, port, timeout, local_address): - raise NotImplementedError - - -class Backend: # pragma: no cover - def name(self): - return "unknown" - - async def make_socket( - self, - af, - socktype, - proto=0, - source=None, - destination=None, - timeout=None, - ssl_context=None, - server_hostname=None, - ): - raise NotImplementedError - - def datagram_connection_required(self): - return False - - async def sleep(self, interval): - raise NotImplementedError - - def get_transport_class(self): - raise NotImplementedError - - async def wait_for(self, awaitable, timeout): - raise NotImplementedError diff --git a/venv/Lib/site-packages/dns/_asyncio_backend.py b/venv/Lib/site-packages/dns/_asyncio_backend.py deleted file mode 100644 index 6ab168d..0000000 --- a/venv/Lib/site-packages/dns/_asyncio_backend.py +++ /dev/null @@ -1,275 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -"""asyncio library query support""" - -import asyncio -import socket -import sys - -import dns._asyncbackend -import dns._features -import dns.exception -import dns.inet - -_is_win32 = sys.platform == "win32" - - -def _get_running_loop(): - try: - return asyncio.get_running_loop() - except AttributeError: # pragma: no cover - return asyncio.get_event_loop() - - -class _DatagramProtocol: - def __init__(self): - self.transport = None - self.recvfrom = None - - def connection_made(self, transport): - self.transport = transport - - def datagram_received(self, data, addr): - if self.recvfrom and not self.recvfrom.done(): - self.recvfrom.set_result((data, addr)) - - def error_received(self, exc): # pragma: no cover - if self.recvfrom and not self.recvfrom.done(): - self.recvfrom.set_exception(exc) - - def connection_lost(self, exc): - if self.recvfrom and not self.recvfrom.done(): - if exc is None: - # EOF we triggered. Is there a better way to do this? - try: - raise EOFError("EOF") - except EOFError as e: - self.recvfrom.set_exception(e) - else: - self.recvfrom.set_exception(exc) - - def close(self): - self.transport.close() - - -async def _maybe_wait_for(awaitable, timeout): - if timeout is not None: - try: - return await asyncio.wait_for(awaitable, timeout) - except asyncio.TimeoutError: - raise dns.exception.Timeout(timeout=timeout) - else: - return await awaitable - - -class DatagramSocket(dns._asyncbackend.DatagramSocket): - def __init__(self, family, transport, protocol): - super().__init__(family, socket.SOCK_DGRAM) - self.transport = transport - self.protocol = protocol - - async def sendto(self, what, destination, timeout): # pragma: no cover - # no timeout for asyncio sendto - self.transport.sendto(what, destination) - return len(what) - - async def recvfrom(self, size, timeout): - # ignore size as there's no way I know to tell protocol about it - done = _get_running_loop().create_future() - try: - assert self.protocol.recvfrom is None - self.protocol.recvfrom = done - await _maybe_wait_for(done, timeout) - return done.result() - finally: - self.protocol.recvfrom = None - - async def close(self): - self.protocol.close() - - async def getpeername(self): - return self.transport.get_extra_info("peername") - - async def getsockname(self): - return self.transport.get_extra_info("sockname") - - async def getpeercert(self, timeout): - raise NotImplementedError - - -class StreamSocket(dns._asyncbackend.StreamSocket): - def __init__(self, af, reader, writer): - super().__init__(af, socket.SOCK_STREAM) - self.reader = reader - self.writer = writer - - async def sendall(self, what, timeout): - self.writer.write(what) - return await _maybe_wait_for(self.writer.drain(), timeout) - - async def recv(self, size, timeout): - return await _maybe_wait_for(self.reader.read(size), timeout) - - async def close(self): - self.writer.close() - - async def getpeername(self): - return self.writer.get_extra_info("peername") - - async def getsockname(self): - return self.writer.get_extra_info("sockname") - - async def getpeercert(self, timeout): - return self.writer.get_extra_info("peercert") - - -if dns._features.have("doh"): - import anyio - import httpcore - import httpcore._backends.anyio - import httpx - - _CoreAsyncNetworkBackend = httpcore.AsyncNetworkBackend - _CoreAnyIOStream = httpcore._backends.anyio.AnyIOStream - - from dns.query import _compute_times, _expiration_for_this_attempt, _remaining - - class _NetworkBackend(_CoreAsyncNetworkBackend): - def __init__(self, resolver, local_port, bootstrap_address, family): - super().__init__() - self._local_port = local_port - self._resolver = resolver - self._bootstrap_address = bootstrap_address - self._family = family - if local_port != 0: - raise NotImplementedError( - "the asyncio transport for HTTPX cannot set the local port" - ) - - async def connect_tcp( - self, host, port, timeout, local_address, socket_options=None - ): # pylint: disable=signature-differs - addresses = [] - _, expiration = _compute_times(timeout) - if dns.inet.is_address(host): - addresses.append(host) - elif self._bootstrap_address is not None: - addresses.append(self._bootstrap_address) - else: - timeout = _remaining(expiration) - family = self._family - if local_address: - family = dns.inet.af_for_address(local_address) - answers = await self._resolver.resolve_name( - host, family=family, lifetime=timeout - ) - addresses = answers.addresses() - for address in addresses: - try: - attempt_expiration = _expiration_for_this_attempt(2.0, expiration) - timeout = _remaining(attempt_expiration) - with anyio.fail_after(timeout): - stream = await anyio.connect_tcp( - remote_host=address, - remote_port=port, - local_host=local_address, - ) - return _CoreAnyIOStream(stream) - except Exception: - pass - raise httpcore.ConnectError - - async def connect_unix_socket( - self, path, timeout, socket_options=None - ): # pylint: disable=signature-differs - raise NotImplementedError - - async def sleep(self, seconds): # pylint: disable=signature-differs - await anyio.sleep(seconds) - - class _HTTPTransport(httpx.AsyncHTTPTransport): - def __init__( - self, - *args, - local_port=0, - bootstrap_address=None, - resolver=None, - family=socket.AF_UNSPEC, - **kwargs, - ): - if resolver is None and bootstrap_address is None: - # pylint: disable=import-outside-toplevel,redefined-outer-name - import dns.asyncresolver - - resolver = dns.asyncresolver.Resolver() - super().__init__(*args, **kwargs) - self._pool._network_backend = _NetworkBackend( - resolver, local_port, bootstrap_address, family - ) - -else: - _HTTPTransport = dns._asyncbackend.NullTransport # type: ignore - - -class Backend(dns._asyncbackend.Backend): - def name(self): - return "asyncio" - - async def make_socket( - self, - af, - socktype, - proto=0, - source=None, - destination=None, - timeout=None, - ssl_context=None, - server_hostname=None, - ): - loop = _get_running_loop() - if socktype == socket.SOCK_DGRAM: - if _is_win32 and source is None: - # Win32 wants explicit binding before recvfrom(). This is the - # proper fix for [#637]. - source = (dns.inet.any_for_af(af), 0) - transport, protocol = await loop.create_datagram_endpoint( - _DatagramProtocol, - source, - family=af, - proto=proto, - remote_addr=destination, - ) - return DatagramSocket(af, transport, protocol) - elif socktype == socket.SOCK_STREAM: - if destination is None: - # This shouldn't happen, but we check to make code analysis software - # happier. - raise ValueError("destination required for stream sockets") - (r, w) = await _maybe_wait_for( - asyncio.open_connection( - destination[0], - destination[1], - ssl=ssl_context, - family=af, - proto=proto, - local_addr=source, - server_hostname=server_hostname, - ), - timeout, - ) - return StreamSocket(af, r, w) - raise NotImplementedError( - "unsupported socket " + f"type {socktype}" - ) # pragma: no cover - - async def sleep(self, interval): - await asyncio.sleep(interval) - - def datagram_connection_required(self): - return False - - def get_transport_class(self): - return _HTTPTransport - - async def wait_for(self, awaitable, timeout): - return await _maybe_wait_for(awaitable, timeout) diff --git a/venv/Lib/site-packages/dns/_ddr.py b/venv/Lib/site-packages/dns/_ddr.py deleted file mode 100644 index bf5c11e..0000000 --- a/venv/Lib/site-packages/dns/_ddr.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license -# -# Support for Discovery of Designated Resolvers - -import socket -import time -from urllib.parse import urlparse - -import dns.asyncbackend -import dns.inet -import dns.name -import dns.nameserver -import dns.query -import dns.rdtypes.svcbbase - -# The special name of the local resolver when using DDR -_local_resolver_name = dns.name.from_text("_dns.resolver.arpa") - - -# -# Processing is split up into I/O independent and I/O dependent parts to -# make supporting sync and async versions easy. -# - - -class _SVCBInfo: - def __init__(self, bootstrap_address, port, hostname, nameservers): - self.bootstrap_address = bootstrap_address - self.port = port - self.hostname = hostname - self.nameservers = nameservers - - def ddr_check_certificate(self, cert): - """Verify that the _SVCBInfo's address is in the cert's subjectAltName (SAN)""" - for name, value in cert["subjectAltName"]: - if name == "IP Address" and value == self.bootstrap_address: - return True - return False - - def make_tls_context(self): - ssl = dns.query.ssl - ctx = ssl.create_default_context() - ctx.minimum_version = ssl.TLSVersion.TLSv1_2 - return ctx - - def ddr_tls_check_sync(self, lifetime): - ctx = self.make_tls_context() - expiration = time.time() + lifetime - with socket.create_connection( - (self.bootstrap_address, self.port), lifetime - ) as s: - with ctx.wrap_socket(s, server_hostname=self.hostname) as ts: - ts.settimeout(dns.query._remaining(expiration)) - ts.do_handshake() - cert = ts.getpeercert() - return self.ddr_check_certificate(cert) - - async def ddr_tls_check_async(self, lifetime, backend=None): - if backend is None: - backend = dns.asyncbackend.get_default_backend() - ctx = self.make_tls_context() - expiration = time.time() + lifetime - async with await backend.make_socket( - dns.inet.af_for_address(self.bootstrap_address), - socket.SOCK_STREAM, - 0, - None, - (self.bootstrap_address, self.port), - lifetime, - ctx, - self.hostname, - ) as ts: - cert = await ts.getpeercert(dns.query._remaining(expiration)) - return self.ddr_check_certificate(cert) - - -def _extract_nameservers_from_svcb(answer): - bootstrap_address = answer.nameserver - if not dns.inet.is_address(bootstrap_address): - return [] - infos = [] - for rr in answer.rrset.processing_order(): - nameservers = [] - param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.ALPN) - if param is None: - continue - alpns = set(param.ids) - host = rr.target.to_text(omit_final_dot=True) - port = None - param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.PORT) - if param is not None: - port = param.port - # For now we ignore address hints and address resolution and always use the - # bootstrap address - if b"h2" in alpns: - param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.DOHPATH) - if param is None or not param.value.endswith(b"{?dns}"): - continue - path = param.value[:-6].decode() - if not path.startswith("/"): - path = "/" + path - if port is None: - port = 443 - url = f"https://{host}:{port}{path}" - # check the URL - try: - urlparse(url) - nameservers.append(dns.nameserver.DoHNameserver(url, bootstrap_address)) - except Exception: - # continue processing other ALPN types - pass - if b"dot" in alpns: - if port is None: - port = 853 - nameservers.append( - dns.nameserver.DoTNameserver(bootstrap_address, port, host) - ) - if b"doq" in alpns: - if port is None: - port = 853 - nameservers.append( - dns.nameserver.DoQNameserver(bootstrap_address, port, True, host) - ) - if len(nameservers) > 0: - infos.append(_SVCBInfo(bootstrap_address, port, host, nameservers)) - return infos - - -def _get_nameservers_sync(answer, lifetime): - """Return a list of TLS-validated resolver nameservers extracted from an SVCB - answer.""" - nameservers = [] - infos = _extract_nameservers_from_svcb(answer) - for info in infos: - try: - if info.ddr_tls_check_sync(lifetime): - nameservers.extend(info.nameservers) - except Exception: - pass - return nameservers - - -async def _get_nameservers_async(answer, lifetime): - """Return a list of TLS-validated resolver nameservers extracted from an SVCB - answer.""" - nameservers = [] - infos = _extract_nameservers_from_svcb(answer) - for info in infos: - try: - if await info.ddr_tls_check_async(lifetime): - nameservers.extend(info.nameservers) - except Exception: - pass - return nameservers diff --git a/venv/Lib/site-packages/dns/_features.py b/venv/Lib/site-packages/dns/_features.py deleted file mode 100644 index fa6d495..0000000 --- a/venv/Lib/site-packages/dns/_features.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import importlib.metadata -import itertools -import string -from typing import Dict, List, Tuple - - -def _tuple_from_text(version: str) -> Tuple: - text_parts = version.split(".") - int_parts = [] - for text_part in text_parts: - digit_prefix = "".join( - itertools.takewhile(lambda x: x in string.digits, text_part) - ) - try: - int_parts.append(int(digit_prefix)) - except Exception: - break - return tuple(int_parts) - - -def _version_check( - requirement: str, -) -> bool: - """Is the requirement fulfilled? - - The requirement must be of the form - - package>=version - """ - package, minimum = requirement.split(">=") - try: - version = importlib.metadata.version(package) - # This shouldn't happen, but it apparently can. - if version is None: - return False - except Exception: - return False - t_version = _tuple_from_text(version) - t_minimum = _tuple_from_text(minimum) - if t_version < t_minimum: - return False - return True - - -_cache: Dict[str, bool] = {} - - -def have(feature: str) -> bool: - """Is *feature* available? - - This tests if all optional packages needed for the - feature are available and recent enough. - - Returns ``True`` if the feature is available, - and ``False`` if it is not or if metadata is - missing. - """ - value = _cache.get(feature) - if value is not None: - return value - requirements = _requirements.get(feature) - if requirements is None: - # we make a cache entry here for consistency not performance - _cache[feature] = False - return False - ok = True - for requirement in requirements: - if not _version_check(requirement): - ok = False - break - _cache[feature] = ok - return ok - - -def force(feature: str, enabled: bool) -> None: - """Force the status of *feature* to be *enabled*. - - This method is provided as a workaround for any cases - where importlib.metadata is ineffective, or for testing. - """ - _cache[feature] = enabled - - -_requirements: Dict[str, List[str]] = { - ### BEGIN generated requirements - "dnssec": ["cryptography>=43"], - "doh": ["httpcore>=1.0.0", "httpx>=0.26.0", "h2>=4.1.0"], - "doq": ["aioquic>=1.0.0"], - "idna": ["idna>=3.7"], - "trio": ["trio>=0.23"], - "wmi": ["wmi>=1.5.1"], - ### END generated requirements -} diff --git a/venv/Lib/site-packages/dns/_immutable_ctx.py b/venv/Lib/site-packages/dns/_immutable_ctx.py deleted file mode 100644 index ae7a33b..0000000 --- a/venv/Lib/site-packages/dns/_immutable_ctx.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# This implementation of the immutable decorator requires python >= -# 3.7, and is significantly more storage efficient when making classes -# with slots immutable. It's also faster. - -import contextvars -import inspect - -_in__init__ = contextvars.ContextVar("_immutable_in__init__", default=False) - - -class _Immutable: - """Immutable mixin class""" - - # We set slots to the empty list to say "we don't have any attributes". - # We do this so that if we're mixed in with a class with __slots__, we - # don't cause a __dict__ to be added which would waste space. - - __slots__ = () - - def __setattr__(self, name, value): - if _in__init__.get() is not self: - raise TypeError("object doesn't support attribute assignment") - else: - super().__setattr__(name, value) - - def __delattr__(self, name): - if _in__init__.get() is not self: - raise TypeError("object doesn't support attribute assignment") - else: - super().__delattr__(name) - - -def _immutable_init(f): - def nf(*args, **kwargs): - previous = _in__init__.set(args[0]) - try: - # call the actual __init__ - f(*args, **kwargs) - finally: - _in__init__.reset(previous) - - nf.__signature__ = inspect.signature(f) - return nf - - -def immutable(cls): - if _Immutable in cls.__mro__: - # Some ancestor already has the mixin, so just make sure we keep - # following the __init__ protocol. - cls.__init__ = _immutable_init(cls.__init__) - if hasattr(cls, "__setstate__"): - cls.__setstate__ = _immutable_init(cls.__setstate__) - ncls = cls - else: - # Mixin the Immutable class and follow the __init__ protocol. - class ncls(_Immutable, cls): - # We have to do the __slots__ declaration here too! - __slots__ = () - - @_immutable_init - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - if hasattr(cls, "__setstate__"): - - @_immutable_init - def __setstate__(self, *args, **kwargs): - super().__setstate__(*args, **kwargs) - - # make ncls have the same name and module as cls - ncls.__name__ = cls.__name__ - ncls.__qualname__ = cls.__qualname__ - ncls.__module__ = cls.__module__ - return ncls diff --git a/venv/Lib/site-packages/dns/_trio_backend.py b/venv/Lib/site-packages/dns/_trio_backend.py deleted file mode 100644 index 0ed904d..0000000 --- a/venv/Lib/site-packages/dns/_trio_backend.py +++ /dev/null @@ -1,253 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -"""trio async I/O library query support""" - -import socket - -import trio -import trio.socket # type: ignore - -import dns._asyncbackend -import dns._features -import dns.exception -import dns.inet - -if not dns._features.have("trio"): - raise ImportError("trio not found or too old") - - -def _maybe_timeout(timeout): - if timeout is not None: - return trio.move_on_after(timeout) - else: - return dns._asyncbackend.NullContext() - - -# for brevity -_lltuple = dns.inet.low_level_address_tuple - -# pylint: disable=redefined-outer-name - - -class DatagramSocket(dns._asyncbackend.DatagramSocket): - def __init__(self, sock): - super().__init__(sock.family, socket.SOCK_DGRAM) - self.socket = sock - - async def sendto(self, what, destination, timeout): - with _maybe_timeout(timeout): - if destination is None: - return await self.socket.send(what) - else: - return await self.socket.sendto(what, destination) - raise dns.exception.Timeout( - timeout=timeout - ) # pragma: no cover lgtm[py/unreachable-statement] - - async def recvfrom(self, size, timeout): - with _maybe_timeout(timeout): - return await self.socket.recvfrom(size) - raise dns.exception.Timeout(timeout=timeout) # lgtm[py/unreachable-statement] - - async def close(self): - self.socket.close() - - async def getpeername(self): - return self.socket.getpeername() - - async def getsockname(self): - return self.socket.getsockname() - - async def getpeercert(self, timeout): - raise NotImplementedError - - -class StreamSocket(dns._asyncbackend.StreamSocket): - def __init__(self, family, stream, tls=False): - super().__init__(family, socket.SOCK_STREAM) - self.stream = stream - self.tls = tls - - async def sendall(self, what, timeout): - with _maybe_timeout(timeout): - return await self.stream.send_all(what) - raise dns.exception.Timeout(timeout=timeout) # lgtm[py/unreachable-statement] - - async def recv(self, size, timeout): - with _maybe_timeout(timeout): - return await self.stream.receive_some(size) - raise dns.exception.Timeout(timeout=timeout) # lgtm[py/unreachable-statement] - - async def close(self): - await self.stream.aclose() - - async def getpeername(self): - if self.tls: - return self.stream.transport_stream.socket.getpeername() - else: - return self.stream.socket.getpeername() - - async def getsockname(self): - if self.tls: - return self.stream.transport_stream.socket.getsockname() - else: - return self.stream.socket.getsockname() - - async def getpeercert(self, timeout): - if self.tls: - with _maybe_timeout(timeout): - await self.stream.do_handshake() - return self.stream.getpeercert() - else: - raise NotImplementedError - - -if dns._features.have("doh"): - import httpcore - import httpcore._backends.trio - import httpx - - _CoreAsyncNetworkBackend = httpcore.AsyncNetworkBackend - _CoreTrioStream = httpcore._backends.trio.TrioStream - - from dns.query import _compute_times, _expiration_for_this_attempt, _remaining - - class _NetworkBackend(_CoreAsyncNetworkBackend): - def __init__(self, resolver, local_port, bootstrap_address, family): - super().__init__() - self._local_port = local_port - self._resolver = resolver - self._bootstrap_address = bootstrap_address - self._family = family - - async def connect_tcp( - self, host, port, timeout, local_address, socket_options=None - ): # pylint: disable=signature-differs - addresses = [] - _, expiration = _compute_times(timeout) - if dns.inet.is_address(host): - addresses.append(host) - elif self._bootstrap_address is not None: - addresses.append(self._bootstrap_address) - else: - timeout = _remaining(expiration) - family = self._family - if local_address: - family = dns.inet.af_for_address(local_address) - answers = await self._resolver.resolve_name( - host, family=family, lifetime=timeout - ) - addresses = answers.addresses() - for address in addresses: - try: - af = dns.inet.af_for_address(address) - if local_address is not None or self._local_port != 0: - source = (local_address, self._local_port) - else: - source = None - destination = (address, port) - attempt_expiration = _expiration_for_this_attempt(2.0, expiration) - timeout = _remaining(attempt_expiration) - sock = await Backend().make_socket( - af, socket.SOCK_STREAM, 0, source, destination, timeout - ) - return _CoreTrioStream(sock.stream) - except Exception: - continue - raise httpcore.ConnectError - - async def connect_unix_socket( - self, path, timeout, socket_options=None - ): # pylint: disable=signature-differs - raise NotImplementedError - - async def sleep(self, seconds): # pylint: disable=signature-differs - await trio.sleep(seconds) - - class _HTTPTransport(httpx.AsyncHTTPTransport): - def __init__( - self, - *args, - local_port=0, - bootstrap_address=None, - resolver=None, - family=socket.AF_UNSPEC, - **kwargs, - ): - if resolver is None and bootstrap_address is None: - # pylint: disable=import-outside-toplevel,redefined-outer-name - import dns.asyncresolver - - resolver = dns.asyncresolver.Resolver() - super().__init__(*args, **kwargs) - self._pool._network_backend = _NetworkBackend( - resolver, local_port, bootstrap_address, family - ) - -else: - _HTTPTransport = dns._asyncbackend.NullTransport # type: ignore - - -class Backend(dns._asyncbackend.Backend): - def name(self): - return "trio" - - async def make_socket( - self, - af, - socktype, - proto=0, - source=None, - destination=None, - timeout=None, - ssl_context=None, - server_hostname=None, - ): - s = trio.socket.socket(af, socktype, proto) - stream = None - try: - if source: - await s.bind(_lltuple(source, af)) - if socktype == socket.SOCK_STREAM or destination is not None: - connected = False - with _maybe_timeout(timeout): - await s.connect(_lltuple(destination, af)) - connected = True - if not connected: - raise dns.exception.Timeout( - timeout=timeout - ) # lgtm[py/unreachable-statement] - except Exception: # pragma: no cover - s.close() - raise - if socktype == socket.SOCK_DGRAM: - return DatagramSocket(s) - elif socktype == socket.SOCK_STREAM: - stream = trio.SocketStream(s) - tls = False - if ssl_context: - tls = True - try: - stream = trio.SSLStream( - stream, ssl_context, server_hostname=server_hostname - ) - except Exception: # pragma: no cover - await stream.aclose() - raise - return StreamSocket(af, stream, tls) - raise NotImplementedError( - "unsupported socket " + f"type {socktype}" - ) # pragma: no cover - - async def sleep(self, interval): - await trio.sleep(interval) - - def get_transport_class(self): - return _HTTPTransport - - async def wait_for(self, awaitable, timeout): - with _maybe_timeout(timeout): - return await awaitable - raise dns.exception.Timeout( - timeout=timeout - ) # pragma: no cover lgtm[py/unreachable-statement] diff --git a/venv/Lib/site-packages/dns/asyncbackend.py b/venv/Lib/site-packages/dns/asyncbackend.py deleted file mode 100644 index 0ec58b0..0000000 --- a/venv/Lib/site-packages/dns/asyncbackend.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -from typing import Dict - -import dns.exception - -# pylint: disable=unused-import -from dns._asyncbackend import ( # noqa: F401 lgtm[py/unused-import] - Backend, - DatagramSocket, - Socket, - StreamSocket, -) - -# pylint: enable=unused-import - -_default_backend = None - -_backends: Dict[str, Backend] = {} - -# Allow sniffio import to be disabled for testing purposes -_no_sniffio = False - - -class AsyncLibraryNotFoundError(dns.exception.DNSException): - pass - - -def get_backend(name: str) -> Backend: - """Get the specified asynchronous backend. - - *name*, a ``str``, the name of the backend. Currently the "trio" - and "asyncio" backends are available. - - Raises NotImplementedError if an unknown backend name is specified. - """ - # pylint: disable=import-outside-toplevel,redefined-outer-name - backend = _backends.get(name) - if backend: - return backend - if name == "trio": - import dns._trio_backend - - backend = dns._trio_backend.Backend() - elif name == "asyncio": - import dns._asyncio_backend - - backend = dns._asyncio_backend.Backend() - else: - raise NotImplementedError(f"unimplemented async backend {name}") - _backends[name] = backend - return backend - - -def sniff() -> str: - """Attempt to determine the in-use asynchronous I/O library by using - the ``sniffio`` module if it is available. - - Returns the name of the library, or raises AsyncLibraryNotFoundError - if the library cannot be determined. - """ - # pylint: disable=import-outside-toplevel - try: - if _no_sniffio: - raise ImportError - import sniffio - - try: - return sniffio.current_async_library() - except sniffio.AsyncLibraryNotFoundError: - raise AsyncLibraryNotFoundError("sniffio cannot determine async library") - except ImportError: - import asyncio - - try: - asyncio.get_running_loop() - return "asyncio" - except RuntimeError: - raise AsyncLibraryNotFoundError("no async library detected") - - -def get_default_backend() -> Backend: - """Get the default backend, initializing it if necessary.""" - if _default_backend: - return _default_backend - - return set_default_backend(sniff()) - - -def set_default_backend(name: str) -> Backend: - """Set the default backend. - - It's not normally necessary to call this method, as - ``get_default_backend()`` will initialize the backend - appropriately in many cases. If ``sniffio`` is not installed, or - in testing situations, this function allows the backend to be set - explicitly. - """ - global _default_backend - _default_backend = get_backend(name) - return _default_backend diff --git a/venv/Lib/site-packages/dns/asyncquery.py b/venv/Lib/site-packages/dns/asyncquery.py deleted file mode 100644 index efad0fd..0000000 --- a/venv/Lib/site-packages/dns/asyncquery.py +++ /dev/null @@ -1,913 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Talk to a DNS server.""" - -import base64 -import contextlib -import random -import socket -import struct -import time -import urllib.parse -from typing import Any, Dict, Optional, Tuple, Union, cast - -import dns.asyncbackend -import dns.exception -import dns.inet -import dns.message -import dns.name -import dns.quic -import dns.rcode -import dns.rdataclass -import dns.rdatatype -import dns.transaction -from dns._asyncbackend import NullContext -from dns.query import ( - BadResponse, - HTTPVersion, - NoDOH, - NoDOQ, - UDPMode, - _check_status, - _compute_times, - _make_dot_ssl_context, - _matches_destination, - _remaining, - have_doh, - ssl, -) - -if have_doh: - import httpx - -# for brevity -_lltuple = dns.inet.low_level_address_tuple - - -def _source_tuple(af, address, port): - # Make a high level source tuple, or return None if address and port - # are both None - if address or port: - if address is None: - if af == socket.AF_INET: - address = "0.0.0.0" - elif af == socket.AF_INET6: - address = "::" - else: - raise NotImplementedError(f"unknown address family {af}") - return (address, port) - else: - return None - - -def _timeout(expiration, now=None): - if expiration is not None: - if not now: - now = time.time() - return max(expiration - now, 0) - else: - return None - - -async def send_udp( - sock: dns.asyncbackend.DatagramSocket, - what: Union[dns.message.Message, bytes], - destination: Any, - expiration: Optional[float] = None, -) -> Tuple[int, float]: - """Send a DNS message to the specified UDP socket. - - *sock*, a ``dns.asyncbackend.DatagramSocket``. - - *what*, a ``bytes`` or ``dns.message.Message``, the message to send. - - *destination*, a destination tuple appropriate for the address family - of the socket, specifying where to send the query. - - *expiration*, a ``float`` or ``None``, the absolute time at which - a timeout exception should be raised. If ``None``, no timeout will - occur. The expiration value is meaningless for the asyncio backend, as - asyncio's transport sendto() never blocks. - - Returns an ``(int, float)`` tuple of bytes sent and the sent time. - """ - - if isinstance(what, dns.message.Message): - what = what.to_wire() - sent_time = time.time() - n = await sock.sendto(what, destination, _timeout(expiration, sent_time)) - return (n, sent_time) - - -async def receive_udp( - sock: dns.asyncbackend.DatagramSocket, - destination: Optional[Any] = None, - expiration: Optional[float] = None, - ignore_unexpected: bool = False, - one_rr_per_rrset: bool = False, - keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, - request_mac: Optional[bytes] = b"", - ignore_trailing: bool = False, - raise_on_truncation: bool = False, - ignore_errors: bool = False, - query: Optional[dns.message.Message] = None, -) -> Any: - """Read a DNS message from a UDP socket. - - *sock*, a ``dns.asyncbackend.DatagramSocket``. - - See :py:func:`dns.query.receive_udp()` for the documentation of the other - parameters, and exceptions. - - Returns a ``(dns.message.Message, float, tuple)`` tuple of the received message, the - received time, and the address where the message arrived from. - """ - - wire = b"" - while True: - (wire, from_address) = await sock.recvfrom(65535, _timeout(expiration)) - if not _matches_destination( - sock.family, from_address, destination, ignore_unexpected - ): - continue - received_time = time.time() - try: - r = dns.message.from_wire( - wire, - keyring=keyring, - request_mac=request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - raise_on_truncation=raise_on_truncation, - ) - except dns.message.Truncated as e: - # See the comment in query.py for details. - if ( - ignore_errors - and query is not None - and not query.is_response(e.message()) - ): - continue - else: - raise - except Exception: - if ignore_errors: - continue - else: - raise - if ignore_errors and query is not None and not query.is_response(r): - continue - return (r, received_time, from_address) - - -async def udp( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 53, - source: Optional[str] = None, - source_port: int = 0, - ignore_unexpected: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - raise_on_truncation: bool = False, - sock: Optional[dns.asyncbackend.DatagramSocket] = None, - backend: Optional[dns.asyncbackend.Backend] = None, - ignore_errors: bool = False, -) -> dns.message.Message: - """Return the response obtained after sending a query via UDP. - - *sock*, a ``dns.asyncbackend.DatagramSocket``, or ``None``, - the socket to use for the query. If ``None``, the default, a - socket is created. Note that if a socket is provided, the - *source*, *source_port*, and *backend* are ignored. - - *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, - the default, then dnspython will use the default backend. - - See :py:func:`dns.query.udp()` for the documentation of the other - parameters, exceptions, and return type of this method. - """ - wire = q.to_wire() - (begin_time, expiration) = _compute_times(timeout) - af = dns.inet.af_for_address(where) - destination = _lltuple((where, port), af) - if sock: - cm: contextlib.AbstractAsyncContextManager = NullContext(sock) - else: - if not backend: - backend = dns.asyncbackend.get_default_backend() - stuple = _source_tuple(af, source, source_port) - if backend.datagram_connection_required(): - dtuple = (where, port) - else: - dtuple = None - cm = await backend.make_socket(af, socket.SOCK_DGRAM, 0, stuple, dtuple) - async with cm as s: - await send_udp(s, wire, destination, expiration) - (r, received_time, _) = await receive_udp( - s, - destination, - expiration, - ignore_unexpected, - one_rr_per_rrset, - q.keyring, - q.mac, - ignore_trailing, - raise_on_truncation, - ignore_errors, - q, - ) - r.time = received_time - begin_time - # We don't need to check q.is_response() if we are in ignore_errors mode - # as receive_udp() will have checked it. - if not (ignore_errors or q.is_response(r)): - raise BadResponse - return r - - -async def udp_with_fallback( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 53, - source: Optional[str] = None, - source_port: int = 0, - ignore_unexpected: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - udp_sock: Optional[dns.asyncbackend.DatagramSocket] = None, - tcp_sock: Optional[dns.asyncbackend.StreamSocket] = None, - backend: Optional[dns.asyncbackend.Backend] = None, - ignore_errors: bool = False, -) -> Tuple[dns.message.Message, bool]: - """Return the response to the query, trying UDP first and falling back - to TCP if UDP results in a truncated response. - - *udp_sock*, a ``dns.asyncbackend.DatagramSocket``, or ``None``, - the socket to use for the UDP query. If ``None``, the default, a - socket is created. Note that if a socket is provided the *source*, - *source_port*, and *backend* are ignored for the UDP query. - - *tcp_sock*, a ``dns.asyncbackend.StreamSocket``, or ``None``, the - socket to use for the TCP query. If ``None``, the default, a - socket is created. Note that if a socket is provided *where*, - *source*, *source_port*, and *backend* are ignored for the TCP query. - - *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, - the default, then dnspython will use the default backend. - - See :py:func:`dns.query.udp_with_fallback()` for the documentation - of the other parameters, exceptions, and return type of this - method. - """ - try: - response = await udp( - q, - where, - timeout, - port, - source, - source_port, - ignore_unexpected, - one_rr_per_rrset, - ignore_trailing, - True, - udp_sock, - backend, - ignore_errors, - ) - return (response, False) - except dns.message.Truncated: - response = await tcp( - q, - where, - timeout, - port, - source, - source_port, - one_rr_per_rrset, - ignore_trailing, - tcp_sock, - backend, - ) - return (response, True) - - -async def send_tcp( - sock: dns.asyncbackend.StreamSocket, - what: Union[dns.message.Message, bytes], - expiration: Optional[float] = None, -) -> Tuple[int, float]: - """Send a DNS message to the specified TCP socket. - - *sock*, a ``dns.asyncbackend.StreamSocket``. - - See :py:func:`dns.query.send_tcp()` for the documentation of the other - parameters, exceptions, and return type of this method. - """ - - if isinstance(what, dns.message.Message): - tcpmsg = what.to_wire(prepend_length=True) - else: - # copying the wire into tcpmsg is inefficient, but lets us - # avoid writev() or doing a short write that would get pushed - # onto the net - tcpmsg = len(what).to_bytes(2, "big") + what - sent_time = time.time() - await sock.sendall(tcpmsg, _timeout(expiration, sent_time)) - return (len(tcpmsg), sent_time) - - -async def _read_exactly(sock, count, expiration): - """Read the specified number of bytes from stream. Keep trying until we - either get the desired amount, or we hit EOF. - """ - s = b"" - while count > 0: - n = await sock.recv(count, _timeout(expiration)) - if n == b"": - raise EOFError("EOF") - count = count - len(n) - s = s + n - return s - - -async def receive_tcp( - sock: dns.asyncbackend.StreamSocket, - expiration: Optional[float] = None, - one_rr_per_rrset: bool = False, - keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, - request_mac: Optional[bytes] = b"", - ignore_trailing: bool = False, -) -> Tuple[dns.message.Message, float]: - """Read a DNS message from a TCP socket. - - *sock*, a ``dns.asyncbackend.StreamSocket``. - - See :py:func:`dns.query.receive_tcp()` for the documentation of the other - parameters, exceptions, and return type of this method. - """ - - ldata = await _read_exactly(sock, 2, expiration) - (l,) = struct.unpack("!H", ldata) - wire = await _read_exactly(sock, l, expiration) - received_time = time.time() - r = dns.message.from_wire( - wire, - keyring=keyring, - request_mac=request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - return (r, received_time) - - -async def tcp( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 53, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - sock: Optional[dns.asyncbackend.StreamSocket] = None, - backend: Optional[dns.asyncbackend.Backend] = None, -) -> dns.message.Message: - """Return the response obtained after sending a query via TCP. - - *sock*, a ``dns.asyncbacket.StreamSocket``, or ``None``, the - socket to use for the query. If ``None``, the default, a socket - is created. Note that if a socket is provided - *where*, *port*, *source*, *source_port*, and *backend* are ignored. - - *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, - the default, then dnspython will use the default backend. - - See :py:func:`dns.query.tcp()` for the documentation of the other - parameters, exceptions, and return type of this method. - """ - - wire = q.to_wire() - (begin_time, expiration) = _compute_times(timeout) - if sock: - # Verify that the socket is connected, as if it's not connected, - # it's not writable, and the polling in send_tcp() will time out or - # hang forever. - await sock.getpeername() - cm: contextlib.AbstractAsyncContextManager = NullContext(sock) - else: - # These are simple (address, port) pairs, not family-dependent tuples - # you pass to low-level socket code. - af = dns.inet.af_for_address(where) - stuple = _source_tuple(af, source, source_port) - dtuple = (where, port) - if not backend: - backend = dns.asyncbackend.get_default_backend() - cm = await backend.make_socket( - af, socket.SOCK_STREAM, 0, stuple, dtuple, timeout - ) - async with cm as s: - await send_tcp(s, wire, expiration) - (r, received_time) = await receive_tcp( - s, expiration, one_rr_per_rrset, q.keyring, q.mac, ignore_trailing - ) - r.time = received_time - begin_time - if not q.is_response(r): - raise BadResponse - return r - - -async def tls( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 853, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - sock: Optional[dns.asyncbackend.StreamSocket] = None, - backend: Optional[dns.asyncbackend.Backend] = None, - ssl_context: Optional[ssl.SSLContext] = None, - server_hostname: Optional[str] = None, - verify: Union[bool, str] = True, -) -> dns.message.Message: - """Return the response obtained after sending a query via TLS. - - *sock*, an ``asyncbackend.StreamSocket``, or ``None``, the socket - to use for the query. If ``None``, the default, a socket is - created. Note that if a socket is provided, it must be a - connected SSL stream socket, and *where*, *port*, - *source*, *source_port*, *backend*, *ssl_context*, and *server_hostname* - are ignored. - - *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, - the default, then dnspython will use the default backend. - - See :py:func:`dns.query.tls()` for the documentation of the other - parameters, exceptions, and return type of this method. - """ - (begin_time, expiration) = _compute_times(timeout) - if sock: - cm: contextlib.AbstractAsyncContextManager = NullContext(sock) - else: - if ssl_context is None: - ssl_context = _make_dot_ssl_context(server_hostname, verify) - af = dns.inet.af_for_address(where) - stuple = _source_tuple(af, source, source_port) - dtuple = (where, port) - if not backend: - backend = dns.asyncbackend.get_default_backend() - cm = await backend.make_socket( - af, - socket.SOCK_STREAM, - 0, - stuple, - dtuple, - timeout, - ssl_context, - server_hostname, - ) - async with cm as s: - timeout = _timeout(expiration) - response = await tcp( - q, - where, - timeout, - port, - source, - source_port, - one_rr_per_rrset, - ignore_trailing, - s, - backend, - ) - end_time = time.time() - response.time = end_time - begin_time - return response - - -def _maybe_get_resolver( - resolver: Optional["dns.asyncresolver.Resolver"], -) -> "dns.asyncresolver.Resolver": - # We need a separate method for this to avoid overriding the global - # variable "dns" with the as-yet undefined local variable "dns" - # in https(). - if resolver is None: - # pylint: disable=import-outside-toplevel,redefined-outer-name - import dns.asyncresolver - - resolver = dns.asyncresolver.Resolver() - return resolver - - -async def https( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 443, - source: Optional[str] = None, - source_port: int = 0, # pylint: disable=W0613 - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - client: Optional["httpx.AsyncClient"] = None, - path: str = "/dns-query", - post: bool = True, - verify: Union[bool, str] = True, - bootstrap_address: Optional[str] = None, - resolver: Optional["dns.asyncresolver.Resolver"] = None, - family: int = socket.AF_UNSPEC, - http_version: HTTPVersion = HTTPVersion.DEFAULT, -) -> dns.message.Message: - """Return the response obtained after sending a query via DNS-over-HTTPS. - - *client*, a ``httpx.AsyncClient``. If provided, the client to use for - the query. - - Unlike the other dnspython async functions, a backend cannot be provided - in this function because httpx always auto-detects the async backend. - - See :py:func:`dns.query.https()` for the documentation of the other - parameters, exceptions, and return type of this method. - """ - - try: - af = dns.inet.af_for_address(where) - except ValueError: - af = None - if af is not None and dns.inet.is_address(where): - if af == socket.AF_INET: - url = f"https://{where}:{port}{path}" - elif af == socket.AF_INET6: - url = f"https://[{where}]:{port}{path}" - else: - url = where - - extensions = {} - if bootstrap_address is None: - # pylint: disable=possibly-used-before-assignment - parsed = urllib.parse.urlparse(url) - if parsed.hostname is None: - raise ValueError("no hostname in URL") - if dns.inet.is_address(parsed.hostname): - bootstrap_address = parsed.hostname - extensions["sni_hostname"] = parsed.hostname - if parsed.port is not None: - port = parsed.port - - if http_version == HTTPVersion.H3 or ( - http_version == HTTPVersion.DEFAULT and not have_doh - ): - if bootstrap_address is None: - resolver = _maybe_get_resolver(resolver) - assert parsed.hostname is not None # for mypy - answers = await resolver.resolve_name(parsed.hostname, family) - bootstrap_address = random.choice(list(answers.addresses())) - return await _http3( - q, - bootstrap_address, - url, - timeout, - port, - source, - source_port, - one_rr_per_rrset, - ignore_trailing, - verify=verify, - post=post, - ) - - if not have_doh: - raise NoDOH # pragma: no cover - # pylint: disable=possibly-used-before-assignment - if client and not isinstance(client, httpx.AsyncClient): - raise ValueError("session parameter must be an httpx.AsyncClient") - # pylint: enable=possibly-used-before-assignment - - wire = q.to_wire() - headers = {"accept": "application/dns-message"} - - h1 = http_version in (HTTPVersion.H1, HTTPVersion.DEFAULT) - h2 = http_version in (HTTPVersion.H2, HTTPVersion.DEFAULT) - - backend = dns.asyncbackend.get_default_backend() - - if source is None: - local_address = None - local_port = 0 - else: - local_address = source - local_port = source_port - - if client: - cm: contextlib.AbstractAsyncContextManager = NullContext(client) - else: - transport = backend.get_transport_class()( - local_address=local_address, - http1=h1, - http2=h2, - verify=verify, - local_port=local_port, - bootstrap_address=bootstrap_address, - resolver=resolver, - family=family, - ) - - cm = httpx.AsyncClient(http1=h1, http2=h2, verify=verify, transport=transport) - - async with cm as the_client: - # see https://tools.ietf.org/html/rfc8484#section-4.1.1 for DoH - # GET and POST examples - if post: - headers.update( - { - "content-type": "application/dns-message", - "content-length": str(len(wire)), - } - ) - response = await backend.wait_for( - the_client.post( - url, - headers=headers, - content=wire, - extensions=extensions, - ), - timeout, - ) - else: - wire = base64.urlsafe_b64encode(wire).rstrip(b"=") - twire = wire.decode() # httpx does a repr() if we give it bytes - response = await backend.wait_for( - the_client.get( - url, - headers=headers, - params={"dns": twire}, - extensions=extensions, - ), - timeout, - ) - - # see https://tools.ietf.org/html/rfc8484#section-4.2.1 for info about DoH - # status codes - if response.status_code < 200 or response.status_code > 299: - raise ValueError( - f"{where} responded with status code {response.status_code}" - f"\nResponse body: {response.content!r}" - ) - r = dns.message.from_wire( - response.content, - keyring=q.keyring, - request_mac=q.request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - r.time = response.elapsed.total_seconds() - if not q.is_response(r): - raise BadResponse - return r - - -async def _http3( - q: dns.message.Message, - where: str, - url: str, - timeout: Optional[float] = None, - port: int = 853, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - verify: Union[bool, str] = True, - backend: Optional[dns.asyncbackend.Backend] = None, - hostname: Optional[str] = None, - post: bool = True, -) -> dns.message.Message: - if not dns.quic.have_quic: - raise NoDOH("DNS-over-HTTP3 is not available.") # pragma: no cover - - url_parts = urllib.parse.urlparse(url) - hostname = url_parts.hostname - if url_parts.port is not None: - port = url_parts.port - - q.id = 0 - wire = q.to_wire() - (cfactory, mfactory) = dns.quic.factories_for_backend(backend) - - async with cfactory() as context: - async with mfactory( - context, verify_mode=verify, server_name=hostname, h3=True - ) as the_manager: - the_connection = the_manager.connect(where, port, source, source_port) - (start, expiration) = _compute_times(timeout) - stream = await the_connection.make_stream(timeout) - async with stream: - # note that send_h3() does not need await - stream.send_h3(url, wire, post) - wire = await stream.receive(_remaining(expiration)) - _check_status(stream.headers(), where, wire) - finish = time.time() - r = dns.message.from_wire( - wire, - keyring=q.keyring, - request_mac=q.request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - r.time = max(finish - start, 0.0) - if not q.is_response(r): - raise BadResponse - return r - - -async def quic( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 853, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - connection: Optional[dns.quic.AsyncQuicConnection] = None, - verify: Union[bool, str] = True, - backend: Optional[dns.asyncbackend.Backend] = None, - hostname: Optional[str] = None, - server_hostname: Optional[str] = None, -) -> dns.message.Message: - """Return the response obtained after sending an asynchronous query via - DNS-over-QUIC. - - *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, - the default, then dnspython will use the default backend. - - See :py:func:`dns.query.quic()` for the documentation of the other - parameters, exceptions, and return type of this method. - """ - - if not dns.quic.have_quic: - raise NoDOQ("DNS-over-QUIC is not available.") # pragma: no cover - - if server_hostname is not None and hostname is None: - hostname = server_hostname - - q.id = 0 - wire = q.to_wire() - the_connection: dns.quic.AsyncQuicConnection - if connection: - cfactory = dns.quic.null_factory - mfactory = dns.quic.null_factory - the_connection = connection - else: - (cfactory, mfactory) = dns.quic.factories_for_backend(backend) - - async with cfactory() as context: - async with mfactory( - context, - verify_mode=verify, - server_name=server_hostname, - ) as the_manager: - if not connection: - the_connection = the_manager.connect(where, port, source, source_port) - (start, expiration) = _compute_times(timeout) - stream = await the_connection.make_stream(timeout) - async with stream: - await stream.send(wire, True) - wire = await stream.receive(_remaining(expiration)) - finish = time.time() - r = dns.message.from_wire( - wire, - keyring=q.keyring, - request_mac=q.request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - r.time = max(finish - start, 0.0) - if not q.is_response(r): - raise BadResponse - return r - - -async def _inbound_xfr( - txn_manager: dns.transaction.TransactionManager, - s: dns.asyncbackend.Socket, - query: dns.message.Message, - serial: Optional[int], - timeout: Optional[float], - expiration: float, -) -> Any: - """Given a socket, does the zone transfer.""" - rdtype = query.question[0].rdtype - is_ixfr = rdtype == dns.rdatatype.IXFR - origin = txn_manager.from_wire_origin() - wire = query.to_wire() - is_udp = s.type == socket.SOCK_DGRAM - if is_udp: - udp_sock = cast(dns.asyncbackend.DatagramSocket, s) - await udp_sock.sendto(wire, None, _timeout(expiration)) - else: - tcp_sock = cast(dns.asyncbackend.StreamSocket, s) - tcpmsg = struct.pack("!H", len(wire)) + wire - await tcp_sock.sendall(tcpmsg, expiration) - with dns.xfr.Inbound(txn_manager, rdtype, serial, is_udp) as inbound: - done = False - tsig_ctx = None - while not done: - (_, mexpiration) = _compute_times(timeout) - if mexpiration is None or ( - expiration is not None and mexpiration > expiration - ): - mexpiration = expiration - if is_udp: - timeout = _timeout(mexpiration) - (rwire, _) = await udp_sock.recvfrom(65535, timeout) - else: - ldata = await _read_exactly(tcp_sock, 2, mexpiration) - (l,) = struct.unpack("!H", ldata) - rwire = await _read_exactly(tcp_sock, l, mexpiration) - r = dns.message.from_wire( - rwire, - keyring=query.keyring, - request_mac=query.mac, - xfr=True, - origin=origin, - tsig_ctx=tsig_ctx, - multi=(not is_udp), - one_rr_per_rrset=is_ixfr, - ) - done = inbound.process_message(r) - yield r - tsig_ctx = r.tsig_ctx - if query.keyring and not r.had_tsig: - raise dns.exception.FormError("missing TSIG") - - -async def inbound_xfr( - where: str, - txn_manager: dns.transaction.TransactionManager, - query: Optional[dns.message.Message] = None, - port: int = 53, - timeout: Optional[float] = None, - lifetime: Optional[float] = None, - source: Optional[str] = None, - source_port: int = 0, - udp_mode: UDPMode = UDPMode.NEVER, - backend: Optional[dns.asyncbackend.Backend] = None, -) -> None: - """Conduct an inbound transfer and apply it via a transaction from the - txn_manager. - - *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, - the default, then dnspython will use the default backend. - - See :py:func:`dns.query.inbound_xfr()` for the documentation of - the other parameters, exceptions, and return type of this method. - """ - if query is None: - (query, serial) = dns.xfr.make_query(txn_manager) - else: - serial = dns.xfr.extract_serial_from_query(query) - af = dns.inet.af_for_address(where) - stuple = _source_tuple(af, source, source_port) - dtuple = (where, port) - if not backend: - backend = dns.asyncbackend.get_default_backend() - (_, expiration) = _compute_times(lifetime) - if query.question[0].rdtype == dns.rdatatype.IXFR and udp_mode != UDPMode.NEVER: - s = await backend.make_socket( - af, socket.SOCK_DGRAM, 0, stuple, dtuple, _timeout(expiration) - ) - async with s: - try: - async for _ in _inbound_xfr( - txn_manager, s, query, serial, timeout, expiration - ): - pass - return - except dns.xfr.UseTCP: - if udp_mode == UDPMode.ONLY: - raise - - s = await backend.make_socket( - af, socket.SOCK_STREAM, 0, stuple, dtuple, _timeout(expiration) - ) - async with s: - async for _ in _inbound_xfr(txn_manager, s, query, serial, timeout, expiration): - pass diff --git a/venv/Lib/site-packages/dns/asyncresolver.py b/venv/Lib/site-packages/dns/asyncresolver.py deleted file mode 100644 index 8f5e062..0000000 --- a/venv/Lib/site-packages/dns/asyncresolver.py +++ /dev/null @@ -1,475 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Asynchronous DNS stub resolver.""" - -import socket -import time -from typing import Any, Dict, List, Optional, Union - -import dns._ddr -import dns.asyncbackend -import dns.asyncquery -import dns.exception -import dns.name -import dns.query -import dns.rdataclass -import dns.rdatatype -import dns.resolver # lgtm[py/import-and-import-from] - -# import some resolver symbols for brevity -from dns.resolver import NXDOMAIN, NoAnswer, NoRootSOA, NotAbsolute - -# for indentation purposes below -_udp = dns.asyncquery.udp -_tcp = dns.asyncquery.tcp - - -class Resolver(dns.resolver.BaseResolver): - """Asynchronous DNS stub resolver.""" - - async def resolve( - self, - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, - search: Optional[bool] = None, - backend: Optional[dns.asyncbackend.Backend] = None, - ) -> dns.resolver.Answer: - """Query nameservers asynchronously to find the answer to the question. - - *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, - the default, then dnspython will use the default backend. - - See :py:func:`dns.resolver.Resolver.resolve()` for the - documentation of the other parameters, exceptions, and return - type of this method. - """ - - resolution = dns.resolver._Resolution( - self, qname, rdtype, rdclass, tcp, raise_on_no_answer, search - ) - if not backend: - backend = dns.asyncbackend.get_default_backend() - start = time.time() - while True: - (request, answer) = resolution.next_request() - # Note we need to say "if answer is not None" and not just - # "if answer" because answer implements __len__, and python - # will call that. We want to return if we have an answer - # object, including in cases where its length is 0. - if answer is not None: - # cache hit! - return answer - assert request is not None # needed for type checking - done = False - while not done: - (nameserver, tcp, backoff) = resolution.next_nameserver() - if backoff: - await backend.sleep(backoff) - timeout = self._compute_timeout(start, lifetime, resolution.errors) - try: - response = await nameserver.async_query( - request, - timeout=timeout, - source=source, - source_port=source_port, - max_size=tcp, - backend=backend, - ) - except Exception as ex: - (_, done) = resolution.query_result(None, ex) - continue - (answer, done) = resolution.query_result(response, None) - # Note we need to say "if answer is not None" and not just - # "if answer" because answer implements __len__, and python - # will call that. We want to return if we have an answer - # object, including in cases where its length is 0. - if answer is not None: - return answer - - async def resolve_address( - self, ipaddr: str, *args: Any, **kwargs: Any - ) -> dns.resolver.Answer: - """Use an asynchronous resolver to run a reverse query for PTR - records. - - This utilizes the resolve() method to perform a PTR lookup on the - specified IP address. - - *ipaddr*, a ``str``, the IPv4 or IPv6 address you want to get - the PTR record for. - - All other arguments that can be passed to the resolve() function - except for rdtype and rdclass are also supported by this - function. - - """ - # We make a modified kwargs for type checking happiness, as otherwise - # we get a legit warning about possibly having rdtype and rdclass - # in the kwargs more than once. - modified_kwargs: Dict[str, Any] = {} - modified_kwargs.update(kwargs) - modified_kwargs["rdtype"] = dns.rdatatype.PTR - modified_kwargs["rdclass"] = dns.rdataclass.IN - return await self.resolve( - dns.reversename.from_address(ipaddr), *args, **modified_kwargs - ) - - async def resolve_name( - self, - name: Union[dns.name.Name, str], - family: int = socket.AF_UNSPEC, - **kwargs: Any, - ) -> dns.resolver.HostAnswers: - """Use an asynchronous resolver to query for address records. - - This utilizes the resolve() method to perform A and/or AAAA lookups on - the specified name. - - *qname*, a ``dns.name.Name`` or ``str``, the name to resolve. - - *family*, an ``int``, the address family. If socket.AF_UNSPEC - (the default), both A and AAAA records will be retrieved. - - All other arguments that can be passed to the resolve() function - except for rdtype and rdclass are also supported by this - function. - """ - # We make a modified kwargs for type checking happiness, as otherwise - # we get a legit warning about possibly having rdtype and rdclass - # in the kwargs more than once. - modified_kwargs: Dict[str, Any] = {} - modified_kwargs.update(kwargs) - modified_kwargs.pop("rdtype", None) - modified_kwargs["rdclass"] = dns.rdataclass.IN - - if family == socket.AF_INET: - v4 = await self.resolve(name, dns.rdatatype.A, **modified_kwargs) - return dns.resolver.HostAnswers.make(v4=v4) - elif family == socket.AF_INET6: - v6 = await self.resolve(name, dns.rdatatype.AAAA, **modified_kwargs) - return dns.resolver.HostAnswers.make(v6=v6) - elif family != socket.AF_UNSPEC: - raise NotImplementedError(f"unknown address family {family}") - - raise_on_no_answer = modified_kwargs.pop("raise_on_no_answer", True) - lifetime = modified_kwargs.pop("lifetime", None) - start = time.time() - v6 = await self.resolve( - name, - dns.rdatatype.AAAA, - raise_on_no_answer=False, - lifetime=self._compute_timeout(start, lifetime), - **modified_kwargs, - ) - # Note that setting name ensures we query the same name - # for A as we did for AAAA. (This is just in case search lists - # are active by default in the resolver configuration and - # we might be talking to a server that says NXDOMAIN when it - # wants to say NOERROR no data. - name = v6.qname - v4 = await self.resolve( - name, - dns.rdatatype.A, - raise_on_no_answer=False, - lifetime=self._compute_timeout(start, lifetime), - **modified_kwargs, - ) - answers = dns.resolver.HostAnswers.make( - v6=v6, v4=v4, add_empty=not raise_on_no_answer - ) - if not answers: - raise NoAnswer(response=v6.response) - return answers - - # pylint: disable=redefined-outer-name - - async def canonical_name(self, name: Union[dns.name.Name, str]) -> dns.name.Name: - """Determine the canonical name of *name*. - - The canonical name is the name the resolver uses for queries - after all CNAME and DNAME renamings have been applied. - - *name*, a ``dns.name.Name`` or ``str``, the query name. - - This method can raise any exception that ``resolve()`` can - raise, other than ``dns.resolver.NoAnswer`` and - ``dns.resolver.NXDOMAIN``. - - Returns a ``dns.name.Name``. - """ - try: - answer = await self.resolve(name, raise_on_no_answer=False) - canonical_name = answer.canonical_name - except dns.resolver.NXDOMAIN as e: - canonical_name = e.canonical_name - return canonical_name - - async def try_ddr(self, lifetime: float = 5.0) -> None: - """Try to update the resolver's nameservers using Discovery of Designated - Resolvers (DDR). If successful, the resolver will subsequently use - DNS-over-HTTPS or DNS-over-TLS for future queries. - - *lifetime*, a float, is the maximum time to spend attempting DDR. The default - is 5 seconds. - - If the SVCB query is successful and results in a non-empty list of nameservers, - then the resolver's nameservers are set to the returned servers in priority - order. - - The current implementation does not use any address hints from the SVCB record, - nor does it resolve addresses for the SCVB target name, rather it assumes that - the bootstrap nameserver will always be one of the addresses and uses it. - A future revision to the code may offer fuller support. The code verifies that - the bootstrap nameserver is in the Subject Alternative Name field of the - TLS certficate. - """ - try: - expiration = time.time() + lifetime - answer = await self.resolve( - dns._ddr._local_resolver_name, "svcb", lifetime=lifetime - ) - timeout = dns.query._remaining(expiration) - nameservers = await dns._ddr._get_nameservers_async(answer, timeout) - if len(nameservers) > 0: - self.nameservers = nameservers - except Exception: - pass - - -default_resolver = None - - -def get_default_resolver() -> Resolver: - """Get the default asynchronous resolver, initializing it if necessary.""" - if default_resolver is None: - reset_default_resolver() - assert default_resolver is not None - return default_resolver - - -def reset_default_resolver() -> None: - """Re-initialize default asynchronous resolver. - - Note that the resolver configuration (i.e. /etc/resolv.conf on UNIX - systems) will be re-read immediately. - """ - - global default_resolver - default_resolver = Resolver() - - -async def resolve( - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, - search: Optional[bool] = None, - backend: Optional[dns.asyncbackend.Backend] = None, -) -> dns.resolver.Answer: - """Query nameservers asynchronously to find the answer to the question. - - This is a convenience function that uses the default resolver - object to make the query. - - See :py:func:`dns.asyncresolver.Resolver.resolve` for more - information on the parameters. - """ - - return await get_default_resolver().resolve( - qname, - rdtype, - rdclass, - tcp, - source, - raise_on_no_answer, - source_port, - lifetime, - search, - backend, - ) - - -async def resolve_address( - ipaddr: str, *args: Any, **kwargs: Any -) -> dns.resolver.Answer: - """Use a resolver to run a reverse query for PTR records. - - See :py:func:`dns.asyncresolver.Resolver.resolve_address` for more - information on the parameters. - """ - - return await get_default_resolver().resolve_address(ipaddr, *args, **kwargs) - - -async def resolve_name( - name: Union[dns.name.Name, str], family: int = socket.AF_UNSPEC, **kwargs: Any -) -> dns.resolver.HostAnswers: - """Use a resolver to asynchronously query for address records. - - See :py:func:`dns.asyncresolver.Resolver.resolve_name` for more - information on the parameters. - """ - - return await get_default_resolver().resolve_name(name, family, **kwargs) - - -async def canonical_name(name: Union[dns.name.Name, str]) -> dns.name.Name: - """Determine the canonical name of *name*. - - See :py:func:`dns.resolver.Resolver.canonical_name` for more - information on the parameters and possible exceptions. - """ - - return await get_default_resolver().canonical_name(name) - - -async def try_ddr(timeout: float = 5.0) -> None: - """Try to update the default resolver's nameservers using Discovery of Designated - Resolvers (DDR). If successful, the resolver will subsequently use - DNS-over-HTTPS or DNS-over-TLS for future queries. - - See :py:func:`dns.resolver.Resolver.try_ddr` for more information. - """ - return await get_default_resolver().try_ddr(timeout) - - -async def zone_for_name( - name: Union[dns.name.Name, str], - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - tcp: bool = False, - resolver: Optional[Resolver] = None, - backend: Optional[dns.asyncbackend.Backend] = None, -) -> dns.name.Name: - """Find the name of the zone which contains the specified name. - - See :py:func:`dns.resolver.Resolver.zone_for_name` for more - information on the parameters and possible exceptions. - """ - - if isinstance(name, str): - name = dns.name.from_text(name, dns.name.root) - if resolver is None: - resolver = get_default_resolver() - if not name.is_absolute(): - raise NotAbsolute(name) - while True: - try: - answer = await resolver.resolve( - name, dns.rdatatype.SOA, rdclass, tcp, backend=backend - ) - assert answer.rrset is not None - if answer.rrset.name == name: - return name - # otherwise we were CNAMEd or DNAMEd and need to look higher - except (NXDOMAIN, NoAnswer): - pass - try: - name = name.parent() - except dns.name.NoParent: # pragma: no cover - raise NoRootSOA - - -async def make_resolver_at( - where: Union[dns.name.Name, str], - port: int = 53, - family: int = socket.AF_UNSPEC, - resolver: Optional[Resolver] = None, -) -> Resolver: - """Make a stub resolver using the specified destination as the full resolver. - - *where*, a ``dns.name.Name`` or ``str`` the domain name or IP address of the - full resolver. - - *port*, an ``int``, the port to use. If not specified, the default is 53. - - *family*, an ``int``, the address family to use. This parameter is used if - *where* is not an address. The default is ``socket.AF_UNSPEC`` in which case - the first address returned by ``resolve_name()`` will be used, otherwise the - first address of the specified family will be used. - - *resolver*, a ``dns.asyncresolver.Resolver`` or ``None``, the resolver to use for - resolution of hostnames. If not specified, the default resolver will be used. - - Returns a ``dns.resolver.Resolver`` or raises an exception. - """ - if resolver is None: - resolver = get_default_resolver() - nameservers: List[Union[str, dns.nameserver.Nameserver]] = [] - if isinstance(where, str) and dns.inet.is_address(where): - nameservers.append(dns.nameserver.Do53Nameserver(where, port)) - else: - answers = await resolver.resolve_name(where, family) - for address in answers.addresses(): - nameservers.append(dns.nameserver.Do53Nameserver(address, port)) - res = dns.asyncresolver.Resolver(configure=False) - res.nameservers = nameservers - return res - - -async def resolve_at( - where: Union[dns.name.Name, str], - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, - search: Optional[bool] = None, - backend: Optional[dns.asyncbackend.Backend] = None, - port: int = 53, - family: int = socket.AF_UNSPEC, - resolver: Optional[Resolver] = None, -) -> dns.resolver.Answer: - """Query nameservers to find the answer to the question. - - This is a convenience function that calls ``dns.asyncresolver.make_resolver_at()`` - to make a resolver, and then uses it to resolve the query. - - See ``dns.asyncresolver.Resolver.resolve`` for more information on the resolution - parameters, and ``dns.asyncresolver.make_resolver_at`` for information about the - resolver parameters *where*, *port*, *family*, and *resolver*. - - If making more than one query, it is more efficient to call - ``dns.asyncresolver.make_resolver_at()`` and then use that resolver for the queries - instead of calling ``resolve_at()`` multiple times. - """ - res = await make_resolver_at(where, port, family, resolver) - return await res.resolve( - qname, - rdtype, - rdclass, - tcp, - source, - raise_on_no_answer, - source_port, - lifetime, - search, - backend, - ) diff --git a/venv/Lib/site-packages/dns/dnssec.py b/venv/Lib/site-packages/dns/dnssec.py deleted file mode 100644 index b69d0a1..0000000 --- a/venv/Lib/site-packages/dns/dnssec.py +++ /dev/null @@ -1,1247 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Common DNSSEC-related functions and constants.""" - - -import base64 -import contextlib -import functools -import hashlib -import struct -import time -from datetime import datetime -from typing import Callable, Dict, List, Optional, Set, Tuple, Union, cast - -import dns._features -import dns.exception -import dns.name -import dns.node -import dns.rdata -import dns.rdataclass -import dns.rdataset -import dns.rdatatype -import dns.rrset -import dns.transaction -import dns.zone -from dns.dnssectypes import Algorithm, DSDigest, NSEC3Hash -from dns.exception import ( # pylint: disable=W0611 - AlgorithmKeyMismatch, - DeniedByPolicy, - UnsupportedAlgorithm, - ValidationFailure, -) -from dns.rdtypes.ANY.CDNSKEY import CDNSKEY -from dns.rdtypes.ANY.CDS import CDS -from dns.rdtypes.ANY.DNSKEY import DNSKEY -from dns.rdtypes.ANY.DS import DS -from dns.rdtypes.ANY.NSEC import NSEC, Bitmap -from dns.rdtypes.ANY.NSEC3PARAM import NSEC3PARAM -from dns.rdtypes.ANY.RRSIG import RRSIG, sigtime_to_posixtime -from dns.rdtypes.dnskeybase import Flag - -PublicKey = Union[ - "GenericPublicKey", - "rsa.RSAPublicKey", - "ec.EllipticCurvePublicKey", - "ed25519.Ed25519PublicKey", - "ed448.Ed448PublicKey", -] - -PrivateKey = Union[ - "GenericPrivateKey", - "rsa.RSAPrivateKey", - "ec.EllipticCurvePrivateKey", - "ed25519.Ed25519PrivateKey", - "ed448.Ed448PrivateKey", -] - -RRsetSigner = Callable[[dns.transaction.Transaction, dns.rrset.RRset], None] - - -def algorithm_from_text(text: str) -> Algorithm: - """Convert text into a DNSSEC algorithm value. - - *text*, a ``str``, the text to convert to into an algorithm value. - - Returns an ``int``. - """ - - return Algorithm.from_text(text) - - -def algorithm_to_text(value: Union[Algorithm, int]) -> str: - """Convert a DNSSEC algorithm value to text - - *value*, a ``dns.dnssec.Algorithm``. - - Returns a ``str``, the name of a DNSSEC algorithm. - """ - - return Algorithm.to_text(value) - - -def to_timestamp(value: Union[datetime, str, float, int]) -> int: - """Convert various format to a timestamp""" - if isinstance(value, datetime): - return int(value.timestamp()) - elif isinstance(value, str): - return sigtime_to_posixtime(value) - elif isinstance(value, float): - return int(value) - elif isinstance(value, int): - return value - else: - raise TypeError("Unsupported timestamp type") - - -def key_id(key: Union[DNSKEY, CDNSKEY]) -> int: - """Return the key id (a 16-bit number) for the specified key. - - *key*, a ``dns.rdtypes.ANY.DNSKEY.DNSKEY`` - - Returns an ``int`` between 0 and 65535 - """ - - rdata = key.to_wire() - assert rdata is not None # for mypy - if key.algorithm == Algorithm.RSAMD5: - return (rdata[-3] << 8) + rdata[-2] - else: - total = 0 - for i in range(len(rdata) // 2): - total += (rdata[2 * i] << 8) + rdata[2 * i + 1] - if len(rdata) % 2 != 0: - total += rdata[len(rdata) - 1] << 8 - total += (total >> 16) & 0xFFFF - return total & 0xFFFF - - -class Policy: - def __init__(self): - pass - - def ok_to_sign(self, _: DNSKEY) -> bool: # pragma: no cover - return False - - def ok_to_validate(self, _: DNSKEY) -> bool: # pragma: no cover - return False - - def ok_to_create_ds(self, _: DSDigest) -> bool: # pragma: no cover - return False - - def ok_to_validate_ds(self, _: DSDigest) -> bool: # pragma: no cover - return False - - -class SimpleDeny(Policy): - def __init__(self, deny_sign, deny_validate, deny_create_ds, deny_validate_ds): - super().__init__() - self._deny_sign = deny_sign - self._deny_validate = deny_validate - self._deny_create_ds = deny_create_ds - self._deny_validate_ds = deny_validate_ds - - def ok_to_sign(self, key: DNSKEY) -> bool: - return key.algorithm not in self._deny_sign - - def ok_to_validate(self, key: DNSKEY) -> bool: - return key.algorithm not in self._deny_validate - - def ok_to_create_ds(self, algorithm: DSDigest) -> bool: - return algorithm not in self._deny_create_ds - - def ok_to_validate_ds(self, algorithm: DSDigest) -> bool: - return algorithm not in self._deny_validate_ds - - -rfc_8624_policy = SimpleDeny( - {Algorithm.RSAMD5, Algorithm.DSA, Algorithm.DSANSEC3SHA1, Algorithm.ECCGOST}, - {Algorithm.RSAMD5, Algorithm.DSA, Algorithm.DSANSEC3SHA1}, - {DSDigest.NULL, DSDigest.SHA1, DSDigest.GOST}, - {DSDigest.NULL}, -) - -allow_all_policy = SimpleDeny(set(), set(), set(), set()) - - -default_policy = rfc_8624_policy - - -def make_ds( - name: Union[dns.name.Name, str], - key: dns.rdata.Rdata, - algorithm: Union[DSDigest, str], - origin: Optional[dns.name.Name] = None, - policy: Optional[Policy] = None, - validating: bool = False, -) -> DS: - """Create a DS record for a DNSSEC key. - - *name*, a ``dns.name.Name`` or ``str``, the owner name of the DS record. - - *key*, a ``dns.rdtypes.ANY.DNSKEY.DNSKEY`` or ``dns.rdtypes.ANY.DNSKEY.CDNSKEY``, - the key the DS is about. - - *algorithm*, a ``str`` or ``int`` specifying the hash algorithm. - The currently supported hashes are "SHA1", "SHA256", and "SHA384". Case - does not matter for these strings. - - *origin*, a ``dns.name.Name`` or ``None``. If *key* is a relative name, - then it will be made absolute using the specified origin. - - *policy*, a ``dns.dnssec.Policy`` or ``None``. If ``None``, the default policy, - ``dns.dnssec.default_policy`` is used; this policy defaults to that of RFC 8624. - - *validating*, a ``bool``. If ``True``, then policy is checked in - validating mode, i.e. "Is it ok to validate using this digest algorithm?". - Otherwise the policy is checked in creating mode, i.e. "Is it ok to create a DS with - this digest algorithm?". - - Raises ``UnsupportedAlgorithm`` if the algorithm is unknown. - - Raises ``DeniedByPolicy`` if the algorithm is denied by policy. - - Returns a ``dns.rdtypes.ANY.DS.DS`` - """ - - if policy is None: - policy = default_policy - try: - if isinstance(algorithm, str): - algorithm = DSDigest[algorithm.upper()] - except Exception: - raise UnsupportedAlgorithm(f'unsupported algorithm "{algorithm}"') - if validating: - check = policy.ok_to_validate_ds - else: - check = policy.ok_to_create_ds - if not check(algorithm): - raise DeniedByPolicy - if not isinstance(key, (DNSKEY, CDNSKEY)): - raise ValueError("key is not a DNSKEY/CDNSKEY") - if algorithm == DSDigest.SHA1: - dshash = hashlib.sha1() - elif algorithm == DSDigest.SHA256: - dshash = hashlib.sha256() - elif algorithm == DSDigest.SHA384: - dshash = hashlib.sha384() - else: - raise UnsupportedAlgorithm(f'unsupported algorithm "{algorithm}"') - - if isinstance(name, str): - name = dns.name.from_text(name, origin) - wire = name.canonicalize().to_wire() - kwire = key.to_wire(origin=origin) - assert wire is not None and kwire is not None # for mypy - dshash.update(wire) - dshash.update(kwire) - digest = dshash.digest() - - dsrdata = struct.pack("!HBB", key_id(key), key.algorithm, algorithm) + digest - ds = dns.rdata.from_wire( - dns.rdataclass.IN, dns.rdatatype.DS, dsrdata, 0, len(dsrdata) - ) - return cast(DS, ds) - - -def make_cds( - name: Union[dns.name.Name, str], - key: dns.rdata.Rdata, - algorithm: Union[DSDigest, str], - origin: Optional[dns.name.Name] = None, -) -> CDS: - """Create a CDS record for a DNSSEC key. - - *name*, a ``dns.name.Name`` or ``str``, the owner name of the DS record. - - *key*, a ``dns.rdtypes.ANY.DNSKEY.DNSKEY`` or ``dns.rdtypes.ANY.DNSKEY.CDNSKEY``, - the key the DS is about. - - *algorithm*, a ``str`` or ``int`` specifying the hash algorithm. - The currently supported hashes are "SHA1", "SHA256", and "SHA384". Case - does not matter for these strings. - - *origin*, a ``dns.name.Name`` or ``None``. If *key* is a relative name, - then it will be made absolute using the specified origin. - - Raises ``UnsupportedAlgorithm`` if the algorithm is unknown. - - Returns a ``dns.rdtypes.ANY.DS.CDS`` - """ - - ds = make_ds(name, key, algorithm, origin) - return CDS( - rdclass=ds.rdclass, - rdtype=dns.rdatatype.CDS, - key_tag=ds.key_tag, - algorithm=ds.algorithm, - digest_type=ds.digest_type, - digest=ds.digest, - ) - - -def _find_candidate_keys( - keys: Dict[dns.name.Name, Union[dns.rdataset.Rdataset, dns.node.Node]], rrsig: RRSIG -) -> Optional[List[DNSKEY]]: - value = keys.get(rrsig.signer) - if isinstance(value, dns.node.Node): - rdataset = value.get_rdataset(dns.rdataclass.IN, dns.rdatatype.DNSKEY) - else: - rdataset = value - if rdataset is None: - return None - return [ - cast(DNSKEY, rd) - for rd in rdataset - if rd.algorithm == rrsig.algorithm - and key_id(rd) == rrsig.key_tag - and (rd.flags & Flag.ZONE) == Flag.ZONE # RFC 4034 2.1.1 - and rd.protocol == 3 # RFC 4034 2.1.2 - ] - - -def _get_rrname_rdataset( - rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], -) -> Tuple[dns.name.Name, dns.rdataset.Rdataset]: - if isinstance(rrset, tuple): - return rrset[0], rrset[1] - else: - return rrset.name, rrset - - -def _validate_signature(sig: bytes, data: bytes, key: DNSKEY) -> None: - # pylint: disable=possibly-used-before-assignment - public_cls = get_algorithm_cls_from_dnskey(key).public_cls - try: - public_key = public_cls.from_dnskey(key) - except ValueError: - raise ValidationFailure("invalid public key") - public_key.verify(sig, data) - - -def _validate_rrsig( - rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], - rrsig: RRSIG, - keys: Dict[dns.name.Name, Union[dns.node.Node, dns.rdataset.Rdataset]], - origin: Optional[dns.name.Name] = None, - now: Optional[float] = None, - policy: Optional[Policy] = None, -) -> None: - """Validate an RRset against a single signature rdata, throwing an - exception if validation is not successful. - - *rrset*, the RRset to validate. This can be a - ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) - tuple. - - *rrsig*, a ``dns.rdata.Rdata``, the signature to validate. - - *keys*, the key dictionary, used to find the DNSKEY associated - with a given name. The dictionary is keyed by a - ``dns.name.Name``, and has ``dns.node.Node`` or - ``dns.rdataset.Rdataset`` values. - - *origin*, a ``dns.name.Name`` or ``None``, the origin to use for relative - names. - - *now*, a ``float`` or ``None``, the time, in seconds since the epoch, to - use as the current time when validating. If ``None``, the actual current - time is used. - - *policy*, a ``dns.dnssec.Policy`` or ``None``. If ``None``, the default policy, - ``dns.dnssec.default_policy`` is used; this policy defaults to that of RFC 8624. - - Raises ``ValidationFailure`` if the signature is expired, not yet valid, - the public key is invalid, the algorithm is unknown, the verification - fails, etc. - - Raises ``UnsupportedAlgorithm`` if the algorithm is recognized by - dnspython but not implemented. - """ - - if policy is None: - policy = default_policy - - candidate_keys = _find_candidate_keys(keys, rrsig) - if candidate_keys is None: - raise ValidationFailure("unknown key") - - if now is None: - now = time.time() - if rrsig.expiration < now: - raise ValidationFailure("expired") - if rrsig.inception > now: - raise ValidationFailure("not yet valid") - - data = _make_rrsig_signature_data(rrset, rrsig, origin) - - # pylint: disable=possibly-used-before-assignment - for candidate_key in candidate_keys: - if not policy.ok_to_validate(candidate_key): - continue - try: - _validate_signature(rrsig.signature, data, candidate_key) - return - except (InvalidSignature, ValidationFailure): - # this happens on an individual validation failure - continue - # nothing verified -- raise failure: - raise ValidationFailure("verify failure") - - -def _validate( - rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], - rrsigset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], - keys: Dict[dns.name.Name, Union[dns.node.Node, dns.rdataset.Rdataset]], - origin: Optional[dns.name.Name] = None, - now: Optional[float] = None, - policy: Optional[Policy] = None, -) -> None: - """Validate an RRset against a signature RRset, throwing an exception - if none of the signatures validate. - - *rrset*, the RRset to validate. This can be a - ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) - tuple. - - *rrsigset*, the signature RRset. This can be a - ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) - tuple. - - *keys*, the key dictionary, used to find the DNSKEY associated - with a given name. The dictionary is keyed by a - ``dns.name.Name``, and has ``dns.node.Node`` or - ``dns.rdataset.Rdataset`` values. - - *origin*, a ``dns.name.Name``, the origin to use for relative names; - defaults to None. - - *now*, an ``int`` or ``None``, the time, in seconds since the epoch, to - use as the current time when validating. If ``None``, the actual current - time is used. - - *policy*, a ``dns.dnssec.Policy`` or ``None``. If ``None``, the default policy, - ``dns.dnssec.default_policy`` is used; this policy defaults to that of RFC 8624. - - Raises ``ValidationFailure`` if the signature is expired, not yet valid, - the public key is invalid, the algorithm is unknown, the verification - fails, etc. - """ - - if policy is None: - policy = default_policy - - if isinstance(origin, str): - origin = dns.name.from_text(origin, dns.name.root) - - if isinstance(rrset, tuple): - rrname = rrset[0] - else: - rrname = rrset.name - - if isinstance(rrsigset, tuple): - rrsigname = rrsigset[0] - rrsigrdataset = rrsigset[1] - else: - rrsigname = rrsigset.name - rrsigrdataset = rrsigset - - rrname = rrname.choose_relativity(origin) - rrsigname = rrsigname.choose_relativity(origin) - if rrname != rrsigname: - raise ValidationFailure("owner names do not match") - - for rrsig in rrsigrdataset: - if not isinstance(rrsig, RRSIG): - raise ValidationFailure("expected an RRSIG") - try: - _validate_rrsig(rrset, rrsig, keys, origin, now, policy) - return - except (ValidationFailure, UnsupportedAlgorithm): - pass - raise ValidationFailure("no RRSIGs validated") - - -def _sign( - rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], - private_key: PrivateKey, - signer: dns.name.Name, - dnskey: DNSKEY, - inception: Optional[Union[datetime, str, int, float]] = None, - expiration: Optional[Union[datetime, str, int, float]] = None, - lifetime: Optional[int] = None, - verify: bool = False, - policy: Optional[Policy] = None, - origin: Optional[dns.name.Name] = None, - deterministic: bool = True, -) -> RRSIG: - """Sign RRset using private key. - - *rrset*, the RRset to validate. This can be a - ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) - tuple. - - *private_key*, the private key to use for signing, a - ``cryptography.hazmat.primitives.asymmetric`` private key class applicable - for DNSSEC. - - *signer*, a ``dns.name.Name``, the Signer's name. - - *dnskey*, a ``DNSKEY`` matching ``private_key``. - - *inception*, a ``datetime``, ``str``, ``int``, ``float`` or ``None``, the - signature inception time. If ``None``, the current time is used. If a ``str``, the - format is "YYYYMMDDHHMMSS" or alternatively the number of seconds since the UNIX - epoch in text form; this is the same the RRSIG rdata's text form. - Values of type `int` or `float` are interpreted as seconds since the UNIX epoch. - - *expiration*, a ``datetime``, ``str``, ``int``, ``float`` or ``None``, the signature - expiration time. If ``None``, the expiration time will be the inception time plus - the value of the *lifetime* parameter. See the description of *inception* above - for how the various parameter types are interpreted. - - *lifetime*, an ``int`` or ``None``, the signature lifetime in seconds. This - parameter is only meaningful if *expiration* is ``None``. - - *verify*, a ``bool``. If set to ``True``, the signer will verify signatures - after they are created; the default is ``False``. - - *policy*, a ``dns.dnssec.Policy`` or ``None``. If ``None``, the default policy, - ``dns.dnssec.default_policy`` is used; this policy defaults to that of RFC 8624. - - *origin*, a ``dns.name.Name`` or ``None``. If ``None``, the default, then all - names in the rrset (including its owner name) must be absolute; otherwise the - specified origin will be used to make names absolute when signing. - - *deterministic*, a ``bool``. If ``True``, the default, use deterministic - (reproducible) signatures when supported by the algorithm used for signing. - Currently, this only affects ECDSA. - - Raises ``DeniedByPolicy`` if the signature is denied by policy. - """ - - if policy is None: - policy = default_policy - if not policy.ok_to_sign(dnskey): - raise DeniedByPolicy - - if isinstance(rrset, tuple): - rdclass = rrset[1].rdclass - rdtype = rrset[1].rdtype - rrname = rrset[0] - original_ttl = rrset[1].ttl - else: - rdclass = rrset.rdclass - rdtype = rrset.rdtype - rrname = rrset.name - original_ttl = rrset.ttl - - if inception is not None: - rrsig_inception = to_timestamp(inception) - else: - rrsig_inception = int(time.time()) - - if expiration is not None: - rrsig_expiration = to_timestamp(expiration) - elif lifetime is not None: - rrsig_expiration = rrsig_inception + lifetime - else: - raise ValueError("expiration or lifetime must be specified") - - # Derelativize now because we need a correct labels length for the - # rrsig_template. - if origin is not None: - rrname = rrname.derelativize(origin) - labels = len(rrname) - 1 - - # Adjust labels appropriately for wildcards. - if rrname.is_wild(): - labels -= 1 - - rrsig_template = RRSIG( - rdclass=rdclass, - rdtype=dns.rdatatype.RRSIG, - type_covered=rdtype, - algorithm=dnskey.algorithm, - labels=labels, - original_ttl=original_ttl, - expiration=rrsig_expiration, - inception=rrsig_inception, - key_tag=key_id(dnskey), - signer=signer, - signature=b"", - ) - - data = dns.dnssec._make_rrsig_signature_data(rrset, rrsig_template, origin) - - # pylint: disable=possibly-used-before-assignment - if isinstance(private_key, GenericPrivateKey): - signing_key = private_key - else: - try: - private_cls = get_algorithm_cls_from_dnskey(dnskey) - signing_key = private_cls(key=private_key) - except UnsupportedAlgorithm: - raise TypeError("Unsupported key algorithm") - - signature = signing_key.sign(data, verify, deterministic) - - return cast(RRSIG, rrsig_template.replace(signature=signature)) - - -def _make_rrsig_signature_data( - rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], - rrsig: RRSIG, - origin: Optional[dns.name.Name] = None, -) -> bytes: - """Create signature rdata. - - *rrset*, the RRset to sign/validate. This can be a - ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) - tuple. - - *rrsig*, a ``dns.rdata.Rdata``, the signature to validate, or the - signature template used when signing. - - *origin*, a ``dns.name.Name`` or ``None``, the origin to use for relative - names. - - Raises ``UnsupportedAlgorithm`` if the algorithm is recognized by - dnspython but not implemented. - """ - - if isinstance(origin, str): - origin = dns.name.from_text(origin, dns.name.root) - - signer = rrsig.signer - if not signer.is_absolute(): - if origin is None: - raise ValidationFailure("relative RR name without an origin specified") - signer = signer.derelativize(origin) - - # For convenience, allow the rrset to be specified as a (name, - # rdataset) tuple as well as a proper rrset - rrname, rdataset = _get_rrname_rdataset(rrset) - - data = b"" - wire = rrsig.to_wire(origin=signer) - assert wire is not None # for mypy - data += wire[:18] - data += rrsig.signer.to_digestable(signer) - - # Derelativize the name before considering labels. - if not rrname.is_absolute(): - if origin is None: - raise ValidationFailure("relative RR name without an origin specified") - rrname = rrname.derelativize(origin) - - name_len = len(rrname) - if rrname.is_wild() and rrsig.labels != name_len - 2: - raise ValidationFailure("wild owner name has wrong label length") - if name_len - 1 < rrsig.labels: - raise ValidationFailure("owner name longer than RRSIG labels") - elif rrsig.labels < name_len - 1: - suffix = rrname.split(rrsig.labels + 1)[1] - rrname = dns.name.from_text("*", suffix) - rrnamebuf = rrname.to_digestable() - rrfixed = struct.pack("!HHI", rdataset.rdtype, rdataset.rdclass, rrsig.original_ttl) - rdatas = [rdata.to_digestable(origin) for rdata in rdataset] - for rdata in sorted(rdatas): - data += rrnamebuf - data += rrfixed - rrlen = struct.pack("!H", len(rdata)) - data += rrlen - data += rdata - - return data - - -def _make_dnskey( - public_key: PublicKey, - algorithm: Union[int, str], - flags: int = Flag.ZONE, - protocol: int = 3, -) -> DNSKEY: - """Convert a public key to DNSKEY Rdata - - *public_key*, a ``PublicKey`` (``GenericPublicKey`` or - ``cryptography.hazmat.primitives.asymmetric``) to convert. - - *algorithm*, a ``str`` or ``int`` specifying the DNSKEY algorithm. - - *flags*: DNSKEY flags field as an integer. - - *protocol*: DNSKEY protocol field as an integer. - - Raises ``ValueError`` if the specified key algorithm parameters are not - unsupported, ``TypeError`` if the key type is unsupported, - `UnsupportedAlgorithm` if the algorithm is unknown and - `AlgorithmKeyMismatch` if the algorithm does not match the key type. - - Return DNSKEY ``Rdata``. - """ - - algorithm = Algorithm.make(algorithm) - - # pylint: disable=possibly-used-before-assignment - if isinstance(public_key, GenericPublicKey): - return public_key.to_dnskey(flags=flags, protocol=protocol) - else: - public_cls = get_algorithm_cls(algorithm).public_cls - return public_cls(key=public_key).to_dnskey(flags=flags, protocol=protocol) - - -def _make_cdnskey( - public_key: PublicKey, - algorithm: Union[int, str], - flags: int = Flag.ZONE, - protocol: int = 3, -) -> CDNSKEY: - """Convert a public key to CDNSKEY Rdata - - *public_key*, the public key to convert, a - ``cryptography.hazmat.primitives.asymmetric`` public key class applicable - for DNSSEC. - - *algorithm*, a ``str`` or ``int`` specifying the DNSKEY algorithm. - - *flags*: DNSKEY flags field as an integer. - - *protocol*: DNSKEY protocol field as an integer. - - Raises ``ValueError`` if the specified key algorithm parameters are not - unsupported, ``TypeError`` if the key type is unsupported, - `UnsupportedAlgorithm` if the algorithm is unknown and - `AlgorithmKeyMismatch` if the algorithm does not match the key type. - - Return CDNSKEY ``Rdata``. - """ - - dnskey = _make_dnskey(public_key, algorithm, flags, protocol) - - return CDNSKEY( - rdclass=dnskey.rdclass, - rdtype=dns.rdatatype.CDNSKEY, - flags=dnskey.flags, - protocol=dnskey.protocol, - algorithm=dnskey.algorithm, - key=dnskey.key, - ) - - -def nsec3_hash( - domain: Union[dns.name.Name, str], - salt: Optional[Union[str, bytes]], - iterations: int, - algorithm: Union[int, str], -) -> str: - """ - Calculate the NSEC3 hash, according to - https://tools.ietf.org/html/rfc5155#section-5 - - *domain*, a ``dns.name.Name`` or ``str``, the name to hash. - - *salt*, a ``str``, ``bytes``, or ``None``, the hash salt. If a - string, it is decoded as a hex string. - - *iterations*, an ``int``, the number of iterations. - - *algorithm*, a ``str`` or ``int``, the hash algorithm. - The only defined algorithm is SHA1. - - Returns a ``str``, the encoded NSEC3 hash. - """ - - b32_conversion = str.maketrans( - "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", "0123456789ABCDEFGHIJKLMNOPQRSTUV" - ) - - try: - if isinstance(algorithm, str): - algorithm = NSEC3Hash[algorithm.upper()] - except Exception: - raise ValueError("Wrong hash algorithm (only SHA1 is supported)") - - if algorithm != NSEC3Hash.SHA1: - raise ValueError("Wrong hash algorithm (only SHA1 is supported)") - - if salt is None: - salt_encoded = b"" - elif isinstance(salt, str): - if len(salt) % 2 == 0: - salt_encoded = bytes.fromhex(salt) - else: - raise ValueError("Invalid salt length") - else: - salt_encoded = salt - - if not isinstance(domain, dns.name.Name): - domain = dns.name.from_text(domain) - domain_encoded = domain.canonicalize().to_wire() - assert domain_encoded is not None - - digest = hashlib.sha1(domain_encoded + salt_encoded).digest() - for _ in range(iterations): - digest = hashlib.sha1(digest + salt_encoded).digest() - - output = base64.b32encode(digest).decode("utf-8") - output = output.translate(b32_conversion) - - return output - - -def make_ds_rdataset( - rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], - algorithms: Set[Union[DSDigest, str]], - origin: Optional[dns.name.Name] = None, -) -> dns.rdataset.Rdataset: - """Create a DS record from DNSKEY/CDNSKEY/CDS. - - *rrset*, the RRset to create DS Rdataset for. This can be a - ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) - tuple. - - *algorithms*, a set of ``str`` or ``int`` specifying the hash algorithms. - The currently supported hashes are "SHA1", "SHA256", and "SHA384". Case - does not matter for these strings. If the RRset is a CDS, only digest - algorithms matching algorithms are accepted. - - *origin*, a ``dns.name.Name`` or ``None``. If `key` is a relative name, - then it will be made absolute using the specified origin. - - Raises ``UnsupportedAlgorithm`` if any of the algorithms are unknown and - ``ValueError`` if the given RRset is not usable. - - Returns a ``dns.rdataset.Rdataset`` - """ - - rrname, rdataset = _get_rrname_rdataset(rrset) - - if rdataset.rdtype not in ( - dns.rdatatype.DNSKEY, - dns.rdatatype.CDNSKEY, - dns.rdatatype.CDS, - ): - raise ValueError("rrset not a DNSKEY/CDNSKEY/CDS") - - _algorithms = set() - for algorithm in algorithms: - try: - if isinstance(algorithm, str): - algorithm = DSDigest[algorithm.upper()] - except Exception: - raise UnsupportedAlgorithm(f'unsupported algorithm "{algorithm}"') - _algorithms.add(algorithm) - - if rdataset.rdtype == dns.rdatatype.CDS: - res = [] - for rdata in cds_rdataset_to_ds_rdataset(rdataset): - if rdata.digest_type in _algorithms: - res.append(rdata) - if len(res) == 0: - raise ValueError("no acceptable CDS rdata found") - return dns.rdataset.from_rdata_list(rdataset.ttl, res) - - res = [] - for algorithm in _algorithms: - res.extend(dnskey_rdataset_to_cds_rdataset(rrname, rdataset, algorithm, origin)) - return dns.rdataset.from_rdata_list(rdataset.ttl, res) - - -def cds_rdataset_to_ds_rdataset( - rdataset: dns.rdataset.Rdataset, -) -> dns.rdataset.Rdataset: - """Create a CDS record from DS. - - *rdataset*, a ``dns.rdataset.Rdataset``, to create DS Rdataset for. - - Raises ``ValueError`` if the rdataset is not CDS. - - Returns a ``dns.rdataset.Rdataset`` - """ - - if rdataset.rdtype != dns.rdatatype.CDS: - raise ValueError("rdataset not a CDS") - res = [] - for rdata in rdataset: - res.append( - CDS( - rdclass=rdata.rdclass, - rdtype=dns.rdatatype.DS, - key_tag=rdata.key_tag, - algorithm=rdata.algorithm, - digest_type=rdata.digest_type, - digest=rdata.digest, - ) - ) - return dns.rdataset.from_rdata_list(rdataset.ttl, res) - - -def dnskey_rdataset_to_cds_rdataset( - name: Union[dns.name.Name, str], - rdataset: dns.rdataset.Rdataset, - algorithm: Union[DSDigest, str], - origin: Optional[dns.name.Name] = None, -) -> dns.rdataset.Rdataset: - """Create a CDS record from DNSKEY/CDNSKEY. - - *name*, a ``dns.name.Name`` or ``str``, the owner name of the CDS record. - - *rdataset*, a ``dns.rdataset.Rdataset``, to create DS Rdataset for. - - *algorithm*, a ``str`` or ``int`` specifying the hash algorithm. - The currently supported hashes are "SHA1", "SHA256", and "SHA384". Case - does not matter for these strings. - - *origin*, a ``dns.name.Name`` or ``None``. If `key` is a relative name, - then it will be made absolute using the specified origin. - - Raises ``UnsupportedAlgorithm`` if the algorithm is unknown or - ``ValueError`` if the rdataset is not DNSKEY/CDNSKEY. - - Returns a ``dns.rdataset.Rdataset`` - """ - - if rdataset.rdtype not in (dns.rdatatype.DNSKEY, dns.rdatatype.CDNSKEY): - raise ValueError("rdataset not a DNSKEY/CDNSKEY") - res = [] - for rdata in rdataset: - res.append(make_cds(name, rdata, algorithm, origin)) - return dns.rdataset.from_rdata_list(rdataset.ttl, res) - - -def dnskey_rdataset_to_cdnskey_rdataset( - rdataset: dns.rdataset.Rdataset, -) -> dns.rdataset.Rdataset: - """Create a CDNSKEY record from DNSKEY. - - *rdataset*, a ``dns.rdataset.Rdataset``, to create CDNSKEY Rdataset for. - - Returns a ``dns.rdataset.Rdataset`` - """ - - if rdataset.rdtype != dns.rdatatype.DNSKEY: - raise ValueError("rdataset not a DNSKEY") - res = [] - for rdata in rdataset: - res.append( - CDNSKEY( - rdclass=rdataset.rdclass, - rdtype=rdataset.rdtype, - flags=rdata.flags, - protocol=rdata.protocol, - algorithm=rdata.algorithm, - key=rdata.key, - ) - ) - return dns.rdataset.from_rdata_list(rdataset.ttl, res) - - -def default_rrset_signer( - txn: dns.transaction.Transaction, - rrset: dns.rrset.RRset, - signer: dns.name.Name, - ksks: List[Tuple[PrivateKey, DNSKEY]], - zsks: List[Tuple[PrivateKey, DNSKEY]], - inception: Optional[Union[datetime, str, int, float]] = None, - expiration: Optional[Union[datetime, str, int, float]] = None, - lifetime: Optional[int] = None, - policy: Optional[Policy] = None, - origin: Optional[dns.name.Name] = None, - deterministic: bool = True, -) -> None: - """Default RRset signer""" - - if rrset.rdtype in set( - [ - dns.rdatatype.RdataType.DNSKEY, - dns.rdatatype.RdataType.CDS, - dns.rdatatype.RdataType.CDNSKEY, - ] - ): - keys = ksks - else: - keys = zsks - - for private_key, dnskey in keys: - rrsig = dns.dnssec.sign( - rrset=rrset, - private_key=private_key, - dnskey=dnskey, - inception=inception, - expiration=expiration, - lifetime=lifetime, - signer=signer, - policy=policy, - origin=origin, - deterministic=deterministic, - ) - txn.add(rrset.name, rrset.ttl, rrsig) - - -def sign_zone( - zone: dns.zone.Zone, - txn: Optional[dns.transaction.Transaction] = None, - keys: Optional[List[Tuple[PrivateKey, DNSKEY]]] = None, - add_dnskey: bool = True, - dnskey_ttl: Optional[int] = None, - inception: Optional[Union[datetime, str, int, float]] = None, - expiration: Optional[Union[datetime, str, int, float]] = None, - lifetime: Optional[int] = None, - nsec3: Optional[NSEC3PARAM] = None, - rrset_signer: Optional[RRsetSigner] = None, - policy: Optional[Policy] = None, - deterministic: bool = True, -) -> None: - """Sign zone. - - *zone*, a ``dns.zone.Zone``, the zone to sign. - - *txn*, a ``dns.transaction.Transaction``, an optional transaction to use for - signing. - - *keys*, a list of (``PrivateKey``, ``DNSKEY``) tuples, to use for signing. KSK/ZSK - roles are assigned automatically if the SEP flag is used, otherwise all RRsets are - signed by all keys. - - *add_dnskey*, a ``bool``. If ``True``, the default, all specified DNSKEYs are - automatically added to the zone on signing. - - *dnskey_ttl*, a``int``, specifies the TTL for DNSKEY RRs. If not specified the TTL - of the existing DNSKEY RRset used or the TTL of the SOA RRset. - - *inception*, a ``datetime``, ``str``, ``int``, ``float`` or ``None``, the signature - inception time. If ``None``, the current time is used. If a ``str``, the format is - "YYYYMMDDHHMMSS" or alternatively the number of seconds since the UNIX epoch in text - form; this is the same the RRSIG rdata's text form. Values of type `int` or `float` - are interpreted as seconds since the UNIX epoch. - - *expiration*, a ``datetime``, ``str``, ``int``, ``float`` or ``None``, the signature - expiration time. If ``None``, the expiration time will be the inception time plus - the value of the *lifetime* parameter. See the description of *inception* above for - how the various parameter types are interpreted. - - *lifetime*, an ``int`` or ``None``, the signature lifetime in seconds. This - parameter is only meaningful if *expiration* is ``None``. - - *nsec3*, a ``NSEC3PARAM`` Rdata, configures signing using NSEC3. Not yet - implemented. - - *rrset_signer*, a ``Callable``, an optional function for signing RRsets. The - function requires two arguments: transaction and RRset. If the not specified, - ``dns.dnssec.default_rrset_signer`` will be used. - - *deterministic*, a ``bool``. If ``True``, the default, use deterministic - (reproducible) signatures when supported by the algorithm used for signing. - Currently, this only affects ECDSA. - - Returns ``None``. - """ - - ksks = [] - zsks = [] - - # if we have both KSKs and ZSKs, split by SEP flag. if not, sign all - # records with all keys - if keys: - for key in keys: - if key[1].flags & Flag.SEP: - ksks.append(key) - else: - zsks.append(key) - if not ksks: - ksks = keys - if not zsks: - zsks = keys - else: - keys = [] - - if txn: - cm: contextlib.AbstractContextManager = contextlib.nullcontext(txn) - else: - cm = zone.writer() - - if zone.origin is None: - raise ValueError("no zone origin") - - with cm as _txn: - if add_dnskey: - if dnskey_ttl is None: - dnskey = _txn.get(zone.origin, dns.rdatatype.DNSKEY) - if dnskey: - dnskey_ttl = dnskey.ttl - else: - soa = _txn.get(zone.origin, dns.rdatatype.SOA) - dnskey_ttl = soa.ttl - for _, dnskey in keys: - _txn.add(zone.origin, dnskey_ttl, dnskey) - - if nsec3: - raise NotImplementedError("Signing with NSEC3 not yet implemented") - else: - _rrset_signer = rrset_signer or functools.partial( - default_rrset_signer, - signer=zone.origin, - ksks=ksks, - zsks=zsks, - inception=inception, - expiration=expiration, - lifetime=lifetime, - policy=policy, - origin=zone.origin, - deterministic=deterministic, - ) - return _sign_zone_nsec(zone, _txn, _rrset_signer) - - -def _sign_zone_nsec( - zone: dns.zone.Zone, - txn: dns.transaction.Transaction, - rrset_signer: Optional[RRsetSigner] = None, -) -> None: - """NSEC zone signer""" - - def _txn_add_nsec( - txn: dns.transaction.Transaction, - name: dns.name.Name, - next_secure: Optional[dns.name.Name], - rdclass: dns.rdataclass.RdataClass, - ttl: int, - rrset_signer: Optional[RRsetSigner] = None, - ) -> None: - """NSEC zone signer helper""" - mandatory_types = set( - [dns.rdatatype.RdataType.RRSIG, dns.rdatatype.RdataType.NSEC] - ) - node = txn.get_node(name) - if node and next_secure: - types = ( - set([rdataset.rdtype for rdataset in node.rdatasets]) | mandatory_types - ) - windows = Bitmap.from_rdtypes(list(types)) - rrset = dns.rrset.from_rdata( - name, - ttl, - NSEC( - rdclass=rdclass, - rdtype=dns.rdatatype.RdataType.NSEC, - next=next_secure, - windows=windows, - ), - ) - txn.add(rrset) - if rrset_signer: - rrset_signer(txn, rrset) - - rrsig_ttl = zone.get_soa().minimum - delegation = None - last_secure = None - - for name in sorted(txn.iterate_names()): - if delegation and name.is_subdomain(delegation): - # names below delegations are not secure - continue - elif txn.get(name, dns.rdatatype.NS) and name != zone.origin: - # inside delegation - delegation = name - else: - # outside delegation - delegation = None - - if rrset_signer: - node = txn.get_node(name) - if node: - for rdataset in node.rdatasets: - if rdataset.rdtype == dns.rdatatype.RRSIG: - # do not sign RRSIGs - continue - elif delegation and rdataset.rdtype != dns.rdatatype.DS: - # do not sign delegations except DS records - continue - else: - rrset = dns.rrset.from_rdata(name, rdataset.ttl, *rdataset) - rrset_signer(txn, rrset) - - # We need "is not None" as the empty name is False because its length is 0. - if last_secure is not None: - _txn_add_nsec(txn, last_secure, name, zone.rdclass, rrsig_ttl, rrset_signer) - last_secure = name - - if last_secure: - _txn_add_nsec( - txn, last_secure, zone.origin, zone.rdclass, rrsig_ttl, rrset_signer - ) - - -def _need_pyca(*args, **kwargs): - raise ImportError( - "DNSSEC validation requires python cryptography" - ) # pragma: no cover - - -if dns._features.have("dnssec"): - from cryptography.exceptions import InvalidSignature - from cryptography.hazmat.primitives.asymmetric import dsa # pylint: disable=W0611 - from cryptography.hazmat.primitives.asymmetric import ec # pylint: disable=W0611 - from cryptography.hazmat.primitives.asymmetric import ed448 # pylint: disable=W0611 - from cryptography.hazmat.primitives.asymmetric import rsa # pylint: disable=W0611 - from cryptography.hazmat.primitives.asymmetric import ( # pylint: disable=W0611 - ed25519, - ) - - from dns.dnssecalgs import ( # pylint: disable=C0412 - get_algorithm_cls, - get_algorithm_cls_from_dnskey, - ) - from dns.dnssecalgs.base import GenericPrivateKey, GenericPublicKey - - validate = _validate # type: ignore - validate_rrsig = _validate_rrsig # type: ignore - sign = _sign - make_dnskey = _make_dnskey - make_cdnskey = _make_cdnskey - _have_pyca = True -else: # pragma: no cover - validate = _need_pyca - validate_rrsig = _need_pyca - sign = _need_pyca - make_dnskey = _need_pyca - make_cdnskey = _need_pyca - _have_pyca = False - -### BEGIN generated Algorithm constants - -RSAMD5 = Algorithm.RSAMD5 -DH = Algorithm.DH -DSA = Algorithm.DSA -ECC = Algorithm.ECC -RSASHA1 = Algorithm.RSASHA1 -DSANSEC3SHA1 = Algorithm.DSANSEC3SHA1 -RSASHA1NSEC3SHA1 = Algorithm.RSASHA1NSEC3SHA1 -RSASHA256 = Algorithm.RSASHA256 -RSASHA512 = Algorithm.RSASHA512 -ECCGOST = Algorithm.ECCGOST -ECDSAP256SHA256 = Algorithm.ECDSAP256SHA256 -ECDSAP384SHA384 = Algorithm.ECDSAP384SHA384 -ED25519 = Algorithm.ED25519 -ED448 = Algorithm.ED448 -INDIRECT = Algorithm.INDIRECT -PRIVATEDNS = Algorithm.PRIVATEDNS -PRIVATEOID = Algorithm.PRIVATEOID - -### END generated Algorithm constants diff --git a/venv/Lib/site-packages/dns/dnssecalgs/__init__.py b/venv/Lib/site-packages/dns/dnssecalgs/__init__.py deleted file mode 100644 index 602367e..0000000 --- a/venv/Lib/site-packages/dns/dnssecalgs/__init__.py +++ /dev/null @@ -1,121 +0,0 @@ -from typing import Dict, Optional, Tuple, Type, Union - -import dns.name -from dns.dnssecalgs.base import GenericPrivateKey -from dns.dnssectypes import Algorithm -from dns.exception import UnsupportedAlgorithm -from dns.rdtypes.ANY.DNSKEY import DNSKEY - -if dns._features.have("dnssec"): - from dns.dnssecalgs.dsa import PrivateDSA, PrivateDSANSEC3SHA1 - from dns.dnssecalgs.ecdsa import PrivateECDSAP256SHA256, PrivateECDSAP384SHA384 - from dns.dnssecalgs.eddsa import PrivateED448, PrivateED25519 - from dns.dnssecalgs.rsa import ( - PrivateRSAMD5, - PrivateRSASHA1, - PrivateRSASHA1NSEC3SHA1, - PrivateRSASHA256, - PrivateRSASHA512, - ) - - _have_cryptography = True -else: - _have_cryptography = False - -AlgorithmPrefix = Optional[Union[bytes, dns.name.Name]] - -algorithms: Dict[Tuple[Algorithm, AlgorithmPrefix], Type[GenericPrivateKey]] = {} -if _have_cryptography: - # pylint: disable=possibly-used-before-assignment - algorithms.update( - { - (Algorithm.RSAMD5, None): PrivateRSAMD5, - (Algorithm.DSA, None): PrivateDSA, - (Algorithm.RSASHA1, None): PrivateRSASHA1, - (Algorithm.DSANSEC3SHA1, None): PrivateDSANSEC3SHA1, - (Algorithm.RSASHA1NSEC3SHA1, None): PrivateRSASHA1NSEC3SHA1, - (Algorithm.RSASHA256, None): PrivateRSASHA256, - (Algorithm.RSASHA512, None): PrivateRSASHA512, - (Algorithm.ECDSAP256SHA256, None): PrivateECDSAP256SHA256, - (Algorithm.ECDSAP384SHA384, None): PrivateECDSAP384SHA384, - (Algorithm.ED25519, None): PrivateED25519, - (Algorithm.ED448, None): PrivateED448, - } - ) - - -def get_algorithm_cls( - algorithm: Union[int, str], prefix: AlgorithmPrefix = None -) -> Type[GenericPrivateKey]: - """Get Private Key class from Algorithm. - - *algorithm*, a ``str`` or ``int`` specifying the DNSKEY algorithm. - - Raises ``UnsupportedAlgorithm`` if the algorithm is unknown. - - Returns a ``dns.dnssecalgs.GenericPrivateKey`` - """ - algorithm = Algorithm.make(algorithm) - cls = algorithms.get((algorithm, prefix)) - if cls: - return cls - raise UnsupportedAlgorithm( - f'algorithm "{Algorithm.to_text(algorithm)}" not supported by dnspython' - ) - - -def get_algorithm_cls_from_dnskey(dnskey: DNSKEY) -> Type[GenericPrivateKey]: - """Get Private Key class from DNSKEY. - - *dnskey*, a ``DNSKEY`` to get Algorithm class for. - - Raises ``UnsupportedAlgorithm`` if the algorithm is unknown. - - Returns a ``dns.dnssecalgs.GenericPrivateKey`` - """ - prefix: AlgorithmPrefix = None - if dnskey.algorithm == Algorithm.PRIVATEDNS: - prefix, _ = dns.name.from_wire(dnskey.key, 0) - elif dnskey.algorithm == Algorithm.PRIVATEOID: - length = int(dnskey.key[0]) - prefix = dnskey.key[0 : length + 1] - return get_algorithm_cls(dnskey.algorithm, prefix) - - -def register_algorithm_cls( - algorithm: Union[int, str], - algorithm_cls: Type[GenericPrivateKey], - name: Optional[Union[dns.name.Name, str]] = None, - oid: Optional[bytes] = None, -) -> None: - """Register Algorithm Private Key class. - - *algorithm*, a ``str`` or ``int`` specifying the DNSKEY algorithm. - - *algorithm_cls*: A `GenericPrivateKey` class. - - *name*, an optional ``dns.name.Name`` or ``str``, for for PRIVATEDNS algorithms. - - *oid*: an optional BER-encoded `bytes` for PRIVATEOID algorithms. - - Raises ``ValueError`` if a name or oid is specified incorrectly. - """ - if not issubclass(algorithm_cls, GenericPrivateKey): - raise TypeError("Invalid algorithm class") - algorithm = Algorithm.make(algorithm) - prefix: AlgorithmPrefix = None - if algorithm == Algorithm.PRIVATEDNS: - if name is None: - raise ValueError("Name required for PRIVATEDNS algorithms") - if isinstance(name, str): - name = dns.name.from_text(name) - prefix = name - elif algorithm == Algorithm.PRIVATEOID: - if oid is None: - raise ValueError("OID required for PRIVATEOID algorithms") - prefix = bytes([len(oid)]) + oid - elif name: - raise ValueError("Name only supported for PRIVATEDNS algorithm") - elif oid: - raise ValueError("OID only supported for PRIVATEOID algorithm") - algorithms[(algorithm, prefix)] = algorithm_cls diff --git a/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 49a69a9..0000000 Binary files a/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/base.cpython-310.pyc b/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/base.cpython-310.pyc deleted file mode 100644 index 4fc0c06..0000000 Binary files a/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/base.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/cryptography.cpython-310.pyc b/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/cryptography.cpython-310.pyc deleted file mode 100644 index 1a424af..0000000 Binary files a/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/cryptography.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/dsa.cpython-310.pyc b/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/dsa.cpython-310.pyc deleted file mode 100644 index 5a89837..0000000 Binary files a/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/dsa.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/ecdsa.cpython-310.pyc b/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/ecdsa.cpython-310.pyc deleted file mode 100644 index 67d9f27..0000000 Binary files a/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/ecdsa.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/eddsa.cpython-310.pyc b/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/eddsa.cpython-310.pyc deleted file mode 100644 index 9495920..0000000 Binary files a/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/eddsa.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/rsa.cpython-310.pyc b/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/rsa.cpython-310.pyc deleted file mode 100644 index 2226395..0000000 Binary files a/venv/Lib/site-packages/dns/dnssecalgs/__pycache__/rsa.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/dnssecalgs/base.py b/venv/Lib/site-packages/dns/dnssecalgs/base.py deleted file mode 100644 index 752ee48..0000000 --- a/venv/Lib/site-packages/dns/dnssecalgs/base.py +++ /dev/null @@ -1,89 +0,0 @@ -from abc import ABC, abstractmethod # pylint: disable=no-name-in-module -from typing import Any, Optional, Type - -import dns.rdataclass -import dns.rdatatype -from dns.dnssectypes import Algorithm -from dns.exception import AlgorithmKeyMismatch -from dns.rdtypes.ANY.DNSKEY import DNSKEY -from dns.rdtypes.dnskeybase import Flag - - -class GenericPublicKey(ABC): - algorithm: Algorithm - - @abstractmethod - def __init__(self, key: Any) -> None: - pass - - @abstractmethod - def verify(self, signature: bytes, data: bytes) -> None: - """Verify signed DNSSEC data""" - - @abstractmethod - def encode_key_bytes(self) -> bytes: - """Encode key as bytes for DNSKEY""" - - @classmethod - def _ensure_algorithm_key_combination(cls, key: DNSKEY) -> None: - if key.algorithm != cls.algorithm: - raise AlgorithmKeyMismatch - - def to_dnskey(self, flags: int = Flag.ZONE, protocol: int = 3) -> DNSKEY: - """Return public key as DNSKEY""" - return DNSKEY( - rdclass=dns.rdataclass.IN, - rdtype=dns.rdatatype.DNSKEY, - flags=flags, - protocol=protocol, - algorithm=self.algorithm, - key=self.encode_key_bytes(), - ) - - @classmethod - @abstractmethod - def from_dnskey(cls, key: DNSKEY) -> "GenericPublicKey": - """Create public key from DNSKEY""" - - @classmethod - @abstractmethod - def from_pem(cls, public_pem: bytes) -> "GenericPublicKey": - """Create public key from PEM-encoded SubjectPublicKeyInfo as specified - in RFC 5280""" - - @abstractmethod - def to_pem(self) -> bytes: - """Return public-key as PEM-encoded SubjectPublicKeyInfo as specified - in RFC 5280""" - - -class GenericPrivateKey(ABC): - public_cls: Type[GenericPublicKey] - - @abstractmethod - def __init__(self, key: Any) -> None: - pass - - @abstractmethod - def sign( - self, - data: bytes, - verify: bool = False, - deterministic: bool = True, - ) -> bytes: - """Sign DNSSEC data""" - - @abstractmethod - def public_key(self) -> "GenericPublicKey": - """Return public key instance""" - - @classmethod - @abstractmethod - def from_pem( - cls, private_pem: bytes, password: Optional[bytes] = None - ) -> "GenericPrivateKey": - """Create private key from PEM-encoded PKCS#8""" - - @abstractmethod - def to_pem(self, password: Optional[bytes] = None) -> bytes: - """Return private key as PEM-encoded PKCS#8""" diff --git a/venv/Lib/site-packages/dns/dnssecalgs/cryptography.py b/venv/Lib/site-packages/dns/dnssecalgs/cryptography.py deleted file mode 100644 index 5a31a81..0000000 --- a/venv/Lib/site-packages/dns/dnssecalgs/cryptography.py +++ /dev/null @@ -1,68 +0,0 @@ -from typing import Any, Optional, Type - -from cryptography.hazmat.primitives import serialization - -from dns.dnssecalgs.base import GenericPrivateKey, GenericPublicKey -from dns.exception import AlgorithmKeyMismatch - - -class CryptographyPublicKey(GenericPublicKey): - key: Any = None - key_cls: Any = None - - def __init__(self, key: Any) -> None: # pylint: disable=super-init-not-called - if self.key_cls is None: - raise TypeError("Undefined private key class") - if not isinstance( # pylint: disable=isinstance-second-argument-not-valid-type - key, self.key_cls - ): - raise AlgorithmKeyMismatch - self.key = key - - @classmethod - def from_pem(cls, public_pem: bytes) -> "GenericPublicKey": - key = serialization.load_pem_public_key(public_pem) - return cls(key=key) - - def to_pem(self) -> bytes: - return self.key.public_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PublicFormat.SubjectPublicKeyInfo, - ) - - -class CryptographyPrivateKey(GenericPrivateKey): - key: Any = None - key_cls: Any = None - public_cls: Type[CryptographyPublicKey] - - def __init__(self, key: Any) -> None: # pylint: disable=super-init-not-called - if self.key_cls is None: - raise TypeError("Undefined private key class") - if not isinstance( # pylint: disable=isinstance-second-argument-not-valid-type - key, self.key_cls - ): - raise AlgorithmKeyMismatch - self.key = key - - def public_key(self) -> "CryptographyPublicKey": - return self.public_cls(key=self.key.public_key()) - - @classmethod - def from_pem( - cls, private_pem: bytes, password: Optional[bytes] = None - ) -> "GenericPrivateKey": - key = serialization.load_pem_private_key(private_pem, password=password) - return cls(key=key) - - def to_pem(self, password: Optional[bytes] = None) -> bytes: - encryption_algorithm: serialization.KeySerializationEncryption - if password: - encryption_algorithm = serialization.BestAvailableEncryption(password) - else: - encryption_algorithm = serialization.NoEncryption() - return self.key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=encryption_algorithm, - ) diff --git a/venv/Lib/site-packages/dns/dnssecalgs/dsa.py b/venv/Lib/site-packages/dns/dnssecalgs/dsa.py deleted file mode 100644 index adca3de..0000000 --- a/venv/Lib/site-packages/dns/dnssecalgs/dsa.py +++ /dev/null @@ -1,106 +0,0 @@ -import struct - -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import dsa, utils - -from dns.dnssecalgs.cryptography import CryptographyPrivateKey, CryptographyPublicKey -from dns.dnssectypes import Algorithm -from dns.rdtypes.ANY.DNSKEY import DNSKEY - - -class PublicDSA(CryptographyPublicKey): - key: dsa.DSAPublicKey - key_cls = dsa.DSAPublicKey - algorithm = Algorithm.DSA - chosen_hash = hashes.SHA1() - - def verify(self, signature: bytes, data: bytes) -> None: - sig_r = signature[1:21] - sig_s = signature[21:] - sig = utils.encode_dss_signature( - int.from_bytes(sig_r, "big"), int.from_bytes(sig_s, "big") - ) - self.key.verify(sig, data, self.chosen_hash) - - def encode_key_bytes(self) -> bytes: - """Encode a public key per RFC 2536, section 2.""" - pn = self.key.public_numbers() - dsa_t = (self.key.key_size // 8 - 64) // 8 - if dsa_t > 8: - raise ValueError("unsupported DSA key size") - octets = 64 + dsa_t * 8 - res = struct.pack("!B", dsa_t) - res += pn.parameter_numbers.q.to_bytes(20, "big") - res += pn.parameter_numbers.p.to_bytes(octets, "big") - res += pn.parameter_numbers.g.to_bytes(octets, "big") - res += pn.y.to_bytes(octets, "big") - return res - - @classmethod - def from_dnskey(cls, key: DNSKEY) -> "PublicDSA": - cls._ensure_algorithm_key_combination(key) - keyptr = key.key - (t,) = struct.unpack("!B", keyptr[0:1]) - keyptr = keyptr[1:] - octets = 64 + t * 8 - dsa_q = keyptr[0:20] - keyptr = keyptr[20:] - dsa_p = keyptr[0:octets] - keyptr = keyptr[octets:] - dsa_g = keyptr[0:octets] - keyptr = keyptr[octets:] - dsa_y = keyptr[0:octets] - return cls( - key=dsa.DSAPublicNumbers( # type: ignore - int.from_bytes(dsa_y, "big"), - dsa.DSAParameterNumbers( - int.from_bytes(dsa_p, "big"), - int.from_bytes(dsa_q, "big"), - int.from_bytes(dsa_g, "big"), - ), - ).public_key(default_backend()), - ) - - -class PrivateDSA(CryptographyPrivateKey): - key: dsa.DSAPrivateKey - key_cls = dsa.DSAPrivateKey - public_cls = PublicDSA - - def sign( - self, - data: bytes, - verify: bool = False, - deterministic: bool = True, - ) -> bytes: - """Sign using a private key per RFC 2536, section 3.""" - public_dsa_key = self.key.public_key() - if public_dsa_key.key_size > 1024: - raise ValueError("DSA key size overflow") - der_signature = self.key.sign(data, self.public_cls.chosen_hash) - dsa_r, dsa_s = utils.decode_dss_signature(der_signature) - dsa_t = (public_dsa_key.key_size // 8 - 64) // 8 - octets = 20 - signature = ( - struct.pack("!B", dsa_t) - + int.to_bytes(dsa_r, length=octets, byteorder="big") - + int.to_bytes(dsa_s, length=octets, byteorder="big") - ) - if verify: - self.public_key().verify(signature, data) - return signature - - @classmethod - def generate(cls, key_size: int) -> "PrivateDSA": - return cls( - key=dsa.generate_private_key(key_size=key_size), - ) - - -class PublicDSANSEC3SHA1(PublicDSA): - algorithm = Algorithm.DSANSEC3SHA1 - - -class PrivateDSANSEC3SHA1(PrivateDSA): - public_cls = PublicDSANSEC3SHA1 diff --git a/venv/Lib/site-packages/dns/dnssecalgs/ecdsa.py b/venv/Lib/site-packages/dns/dnssecalgs/ecdsa.py deleted file mode 100644 index 86d5764..0000000 --- a/venv/Lib/site-packages/dns/dnssecalgs/ecdsa.py +++ /dev/null @@ -1,97 +0,0 @@ -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import ec, utils - -from dns.dnssecalgs.cryptography import CryptographyPrivateKey, CryptographyPublicKey -from dns.dnssectypes import Algorithm -from dns.rdtypes.ANY.DNSKEY import DNSKEY - - -class PublicECDSA(CryptographyPublicKey): - key: ec.EllipticCurvePublicKey - key_cls = ec.EllipticCurvePublicKey - algorithm: Algorithm - chosen_hash: hashes.HashAlgorithm - curve: ec.EllipticCurve - octets: int - - def verify(self, signature: bytes, data: bytes) -> None: - sig_r = signature[0 : self.octets] - sig_s = signature[self.octets :] - sig = utils.encode_dss_signature( - int.from_bytes(sig_r, "big"), int.from_bytes(sig_s, "big") - ) - self.key.verify(sig, data, ec.ECDSA(self.chosen_hash)) - - def encode_key_bytes(self) -> bytes: - """Encode a public key per RFC 6605, section 4.""" - pn = self.key.public_numbers() - return pn.x.to_bytes(self.octets, "big") + pn.y.to_bytes(self.octets, "big") - - @classmethod - def from_dnskey(cls, key: DNSKEY) -> "PublicECDSA": - cls._ensure_algorithm_key_combination(key) - ecdsa_x = key.key[0 : cls.octets] - ecdsa_y = key.key[cls.octets : cls.octets * 2] - return cls( - key=ec.EllipticCurvePublicNumbers( - curve=cls.curve, - x=int.from_bytes(ecdsa_x, "big"), - y=int.from_bytes(ecdsa_y, "big"), - ).public_key(default_backend()), - ) - - -class PrivateECDSA(CryptographyPrivateKey): - key: ec.EllipticCurvePrivateKey - key_cls = ec.EllipticCurvePrivateKey - public_cls = PublicECDSA - - def sign( - self, - data: bytes, - verify: bool = False, - deterministic: bool = True, - ) -> bytes: - """Sign using a private key per RFC 6605, section 4.""" - algorithm = ec.ECDSA( - self.public_cls.chosen_hash, deterministic_signing=deterministic - ) - der_signature = self.key.sign(data, algorithm) - dsa_r, dsa_s = utils.decode_dss_signature(der_signature) - signature = int.to_bytes( - dsa_r, length=self.public_cls.octets, byteorder="big" - ) + int.to_bytes(dsa_s, length=self.public_cls.octets, byteorder="big") - if verify: - self.public_key().verify(signature, data) - return signature - - @classmethod - def generate(cls) -> "PrivateECDSA": - return cls( - key=ec.generate_private_key( - curve=cls.public_cls.curve, backend=default_backend() - ), - ) - - -class PublicECDSAP256SHA256(PublicECDSA): - algorithm = Algorithm.ECDSAP256SHA256 - chosen_hash = hashes.SHA256() - curve = ec.SECP256R1() - octets = 32 - - -class PrivateECDSAP256SHA256(PrivateECDSA): - public_cls = PublicECDSAP256SHA256 - - -class PublicECDSAP384SHA384(PublicECDSA): - algorithm = Algorithm.ECDSAP384SHA384 - chosen_hash = hashes.SHA384() - curve = ec.SECP384R1() - octets = 48 - - -class PrivateECDSAP384SHA384(PrivateECDSA): - public_cls = PublicECDSAP384SHA384 diff --git a/venv/Lib/site-packages/dns/dnssecalgs/eddsa.py b/venv/Lib/site-packages/dns/dnssecalgs/eddsa.py deleted file mode 100644 index 604bcbf..0000000 --- a/venv/Lib/site-packages/dns/dnssecalgs/eddsa.py +++ /dev/null @@ -1,70 +0,0 @@ -from typing import Type - -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import ed448, ed25519 - -from dns.dnssecalgs.cryptography import CryptographyPrivateKey, CryptographyPublicKey -from dns.dnssectypes import Algorithm -from dns.rdtypes.ANY.DNSKEY import DNSKEY - - -class PublicEDDSA(CryptographyPublicKey): - def verify(self, signature: bytes, data: bytes) -> None: - self.key.verify(signature, data) - - def encode_key_bytes(self) -> bytes: - """Encode a public key per RFC 8080, section 3.""" - return self.key.public_bytes( - encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw - ) - - @classmethod - def from_dnskey(cls, key: DNSKEY) -> "PublicEDDSA": - cls._ensure_algorithm_key_combination(key) - return cls( - key=cls.key_cls.from_public_bytes(key.key), - ) - - -class PrivateEDDSA(CryptographyPrivateKey): - public_cls: Type[PublicEDDSA] - - def sign( - self, - data: bytes, - verify: bool = False, - deterministic: bool = True, - ) -> bytes: - """Sign using a private key per RFC 8080, section 4.""" - signature = self.key.sign(data) - if verify: - self.public_key().verify(signature, data) - return signature - - @classmethod - def generate(cls) -> "PrivateEDDSA": - return cls(key=cls.key_cls.generate()) - - -class PublicED25519(PublicEDDSA): - key: ed25519.Ed25519PublicKey - key_cls = ed25519.Ed25519PublicKey - algorithm = Algorithm.ED25519 - - -class PrivateED25519(PrivateEDDSA): - key: ed25519.Ed25519PrivateKey - key_cls = ed25519.Ed25519PrivateKey - public_cls = PublicED25519 - - -class PublicED448(PublicEDDSA): - key: ed448.Ed448PublicKey - key_cls = ed448.Ed448PublicKey - algorithm = Algorithm.ED448 - - -class PrivateED448(PrivateEDDSA): - key: ed448.Ed448PrivateKey - key_cls = ed448.Ed448PrivateKey - public_cls = PublicED448 diff --git a/venv/Lib/site-packages/dns/dnssecalgs/rsa.py b/venv/Lib/site-packages/dns/dnssecalgs/rsa.py deleted file mode 100644 index 27537aa..0000000 --- a/venv/Lib/site-packages/dns/dnssecalgs/rsa.py +++ /dev/null @@ -1,124 +0,0 @@ -import math -import struct - -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import padding, rsa - -from dns.dnssecalgs.cryptography import CryptographyPrivateKey, CryptographyPublicKey -from dns.dnssectypes import Algorithm -from dns.rdtypes.ANY.DNSKEY import DNSKEY - - -class PublicRSA(CryptographyPublicKey): - key: rsa.RSAPublicKey - key_cls = rsa.RSAPublicKey - algorithm: Algorithm - chosen_hash: hashes.HashAlgorithm - - def verify(self, signature: bytes, data: bytes) -> None: - self.key.verify(signature, data, padding.PKCS1v15(), self.chosen_hash) - - def encode_key_bytes(self) -> bytes: - """Encode a public key per RFC 3110, section 2.""" - pn = self.key.public_numbers() - _exp_len = math.ceil(int.bit_length(pn.e) / 8) - exp = int.to_bytes(pn.e, length=_exp_len, byteorder="big") - if _exp_len > 255: - exp_header = b"\0" + struct.pack("!H", _exp_len) - else: - exp_header = struct.pack("!B", _exp_len) - if pn.n.bit_length() < 512 or pn.n.bit_length() > 4096: - raise ValueError("unsupported RSA key length") - return exp_header + exp + pn.n.to_bytes((pn.n.bit_length() + 7) // 8, "big") - - @classmethod - def from_dnskey(cls, key: DNSKEY) -> "PublicRSA": - cls._ensure_algorithm_key_combination(key) - keyptr = key.key - (bytes_,) = struct.unpack("!B", keyptr[0:1]) - keyptr = keyptr[1:] - if bytes_ == 0: - (bytes_,) = struct.unpack("!H", keyptr[0:2]) - keyptr = keyptr[2:] - rsa_e = keyptr[0:bytes_] - rsa_n = keyptr[bytes_:] - return cls( - key=rsa.RSAPublicNumbers( - int.from_bytes(rsa_e, "big"), int.from_bytes(rsa_n, "big") - ).public_key(default_backend()) - ) - - -class PrivateRSA(CryptographyPrivateKey): - key: rsa.RSAPrivateKey - key_cls = rsa.RSAPrivateKey - public_cls = PublicRSA - default_public_exponent = 65537 - - def sign( - self, - data: bytes, - verify: bool = False, - deterministic: bool = True, - ) -> bytes: - """Sign using a private key per RFC 3110, section 3.""" - signature = self.key.sign(data, padding.PKCS1v15(), self.public_cls.chosen_hash) - if verify: - self.public_key().verify(signature, data) - return signature - - @classmethod - def generate(cls, key_size: int) -> "PrivateRSA": - return cls( - key=rsa.generate_private_key( - public_exponent=cls.default_public_exponent, - key_size=key_size, - backend=default_backend(), - ) - ) - - -class PublicRSAMD5(PublicRSA): - algorithm = Algorithm.RSAMD5 - chosen_hash = hashes.MD5() - - -class PrivateRSAMD5(PrivateRSA): - public_cls = PublicRSAMD5 - - -class PublicRSASHA1(PublicRSA): - algorithm = Algorithm.RSASHA1 - chosen_hash = hashes.SHA1() - - -class PrivateRSASHA1(PrivateRSA): - public_cls = PublicRSASHA1 - - -class PublicRSASHA1NSEC3SHA1(PublicRSA): - algorithm = Algorithm.RSASHA1NSEC3SHA1 - chosen_hash = hashes.SHA1() - - -class PrivateRSASHA1NSEC3SHA1(PrivateRSA): - public_cls = PublicRSASHA1NSEC3SHA1 - - -class PublicRSASHA256(PublicRSA): - algorithm = Algorithm.RSASHA256 - chosen_hash = hashes.SHA256() - - -class PrivateRSASHA256(PrivateRSA): - public_cls = PublicRSASHA256 - - -class PublicRSASHA512(PublicRSA): - algorithm = Algorithm.RSASHA512 - chosen_hash = hashes.SHA512() - - -class PrivateRSASHA512(PrivateRSA): - public_cls = PublicRSASHA512 diff --git a/venv/Lib/site-packages/dns/dnssectypes.py b/venv/Lib/site-packages/dns/dnssectypes.py deleted file mode 100644 index 02131e0..0000000 --- a/venv/Lib/site-packages/dns/dnssectypes.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Common DNSSEC-related types.""" - -# This is a separate file to avoid import circularity between dns.dnssec and -# the implementations of the DS and DNSKEY types. - -import dns.enum - - -class Algorithm(dns.enum.IntEnum): - RSAMD5 = 1 - DH = 2 - DSA = 3 - ECC = 4 - RSASHA1 = 5 - DSANSEC3SHA1 = 6 - RSASHA1NSEC3SHA1 = 7 - RSASHA256 = 8 - RSASHA512 = 10 - ECCGOST = 12 - ECDSAP256SHA256 = 13 - ECDSAP384SHA384 = 14 - ED25519 = 15 - ED448 = 16 - INDIRECT = 252 - PRIVATEDNS = 253 - PRIVATEOID = 254 - - @classmethod - def _maximum(cls): - return 255 - - -class DSDigest(dns.enum.IntEnum): - """DNSSEC Delegation Signer Digest Algorithm""" - - NULL = 0 - SHA1 = 1 - SHA256 = 2 - GOST = 3 - SHA384 = 4 - - @classmethod - def _maximum(cls): - return 255 - - -class NSEC3Hash(dns.enum.IntEnum): - """NSEC3 hash algorithm""" - - SHA1 = 1 - - @classmethod - def _maximum(cls): - return 255 diff --git a/venv/Lib/site-packages/dns/e164.py b/venv/Lib/site-packages/dns/e164.py deleted file mode 100644 index 453736d..0000000 --- a/venv/Lib/site-packages/dns/e164.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS E.164 helpers.""" - -from typing import Iterable, Optional, Union - -import dns.exception -import dns.name -import dns.resolver - -#: The public E.164 domain. -public_enum_domain = dns.name.from_text("e164.arpa.") - - -def from_e164( - text: str, origin: Optional[dns.name.Name] = public_enum_domain -) -> dns.name.Name: - """Convert an E.164 number in textual form into a Name object whose - value is the ENUM domain name for that number. - - Non-digits in the text are ignored, i.e. "16505551212", - "+1.650.555.1212" and "1 (650) 555-1212" are all the same. - - *text*, a ``str``, is an E.164 number in textual form. - - *origin*, a ``dns.name.Name``, the domain in which the number - should be constructed. The default is ``e164.arpa.``. - - Returns a ``dns.name.Name``. - """ - - parts = [d for d in text if d.isdigit()] - parts.reverse() - return dns.name.from_text(".".join(parts), origin=origin) - - -def to_e164( - name: dns.name.Name, - origin: Optional[dns.name.Name] = public_enum_domain, - want_plus_prefix: bool = True, -) -> str: - """Convert an ENUM domain name into an E.164 number. - - Note that dnspython does not have any information about preferred - number formats within national numbering plans, so all numbers are - emitted as a simple string of digits, prefixed by a '+' (unless - *want_plus_prefix* is ``False``). - - *name* is a ``dns.name.Name``, the ENUM domain name. - - *origin* is a ``dns.name.Name``, a domain containing the ENUM - domain name. The name is relativized to this domain before being - converted to text. If ``None``, no relativization is done. - - *want_plus_prefix* is a ``bool``. If True, add a '+' to the beginning of - the returned number. - - Returns a ``str``. - - """ - if origin is not None: - name = name.relativize(origin) - dlabels = [d for d in name.labels if d.isdigit() and len(d) == 1] - if len(dlabels) != len(name.labels): - raise dns.exception.SyntaxError("non-digit labels in ENUM domain name") - dlabels.reverse() - text = b"".join(dlabels) - if want_plus_prefix: - text = b"+" + text - return text.decode() - - -def query( - number: str, - domains: Iterable[Union[dns.name.Name, str]], - resolver: Optional[dns.resolver.Resolver] = None, -) -> dns.resolver.Answer: - """Look for NAPTR RRs for the specified number in the specified domains. - - e.g. lookup('16505551212', ['e164.dnspython.org.', 'e164.arpa.']) - - *number*, a ``str`` is the number to look for. - - *domains* is an iterable containing ``dns.name.Name`` values. - - *resolver*, a ``dns.resolver.Resolver``, is the resolver to use. If - ``None``, the default resolver is used. - """ - - if resolver is None: - resolver = dns.resolver.get_default_resolver() - e_nx = dns.resolver.NXDOMAIN() - for domain in domains: - if isinstance(domain, str): - domain = dns.name.from_text(domain) - qname = dns.e164.from_e164(number, domain) - try: - return resolver.resolve(qname, "NAPTR") - except dns.resolver.NXDOMAIN as e: - e_nx += e - raise e_nx diff --git a/venv/Lib/site-packages/dns/edns.py b/venv/Lib/site-packages/dns/edns.py deleted file mode 100644 index f7d9ff9..0000000 --- a/venv/Lib/site-packages/dns/edns.py +++ /dev/null @@ -1,572 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2009-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""EDNS Options""" - -import binascii -import math -import socket -import struct -from typing import Any, Dict, Optional, Union - -import dns.enum -import dns.inet -import dns.rdata -import dns.wire - - -class OptionType(dns.enum.IntEnum): - #: NSID - NSID = 3 - #: DAU - DAU = 5 - #: DHU - DHU = 6 - #: N3U - N3U = 7 - #: ECS (client-subnet) - ECS = 8 - #: EXPIRE - EXPIRE = 9 - #: COOKIE - COOKIE = 10 - #: KEEPALIVE - KEEPALIVE = 11 - #: PADDING - PADDING = 12 - #: CHAIN - CHAIN = 13 - #: EDE (extended-dns-error) - EDE = 15 - #: REPORTCHANNEL - REPORTCHANNEL = 18 - - @classmethod - def _maximum(cls): - return 65535 - - -class Option: - """Base class for all EDNS option types.""" - - def __init__(self, otype: Union[OptionType, str]): - """Initialize an option. - - *otype*, a ``dns.edns.OptionType``, is the option type. - """ - self.otype = OptionType.make(otype) - - def to_wire(self, file: Optional[Any] = None) -> Optional[bytes]: - """Convert an option to wire format. - - Returns a ``bytes`` or ``None``. - - """ - raise NotImplementedError # pragma: no cover - - def to_text(self) -> str: - raise NotImplementedError # pragma: no cover - - @classmethod - def from_wire_parser(cls, otype: OptionType, parser: "dns.wire.Parser") -> "Option": - """Build an EDNS option object from wire format. - - *otype*, a ``dns.edns.OptionType``, is the option type. - - *parser*, a ``dns.wire.Parser``, the parser, which should be - restructed to the option length. - - Returns a ``dns.edns.Option``. - """ - raise NotImplementedError # pragma: no cover - - def _cmp(self, other): - """Compare an EDNS option with another option of the same type. - - Returns < 0 if < *other*, 0 if == *other*, and > 0 if > *other*. - """ - wire = self.to_wire() - owire = other.to_wire() - if wire == owire: - return 0 - if wire > owire: - return 1 - return -1 - - def __eq__(self, other): - if not isinstance(other, Option): - return False - if self.otype != other.otype: - return False - return self._cmp(other) == 0 - - def __ne__(self, other): - if not isinstance(other, Option): - return True - if self.otype != other.otype: - return True - return self._cmp(other) != 0 - - def __lt__(self, other): - if not isinstance(other, Option) or self.otype != other.otype: - return NotImplemented - return self._cmp(other) < 0 - - def __le__(self, other): - if not isinstance(other, Option) or self.otype != other.otype: - return NotImplemented - return self._cmp(other) <= 0 - - def __ge__(self, other): - if not isinstance(other, Option) or self.otype != other.otype: - return NotImplemented - return self._cmp(other) >= 0 - - def __gt__(self, other): - if not isinstance(other, Option) or self.otype != other.otype: - return NotImplemented - return self._cmp(other) > 0 - - def __str__(self): - return self.to_text() - - -class GenericOption(Option): # lgtm[py/missing-equals] - """Generic Option Class - - This class is used for EDNS option types for which we have no better - implementation. - """ - - def __init__(self, otype: Union[OptionType, str], data: Union[bytes, str]): - super().__init__(otype) - self.data = dns.rdata.Rdata._as_bytes(data, True) - - def to_wire(self, file: Optional[Any] = None) -> Optional[bytes]: - if file: - file.write(self.data) - return None - else: - return self.data - - def to_text(self) -> str: - return "Generic %d" % self.otype - - @classmethod - def from_wire_parser( - cls, otype: Union[OptionType, str], parser: "dns.wire.Parser" - ) -> Option: - return cls(otype, parser.get_remaining()) - - -class ECSOption(Option): # lgtm[py/missing-equals] - """EDNS Client Subnet (ECS, RFC7871)""" - - def __init__(self, address: str, srclen: Optional[int] = None, scopelen: int = 0): - """*address*, a ``str``, is the client address information. - - *srclen*, an ``int``, the source prefix length, which is the - leftmost number of bits of the address to be used for the - lookup. The default is 24 for IPv4 and 56 for IPv6. - - *scopelen*, an ``int``, the scope prefix length. This value - must be 0 in queries, and should be set in responses. - """ - - super().__init__(OptionType.ECS) - af = dns.inet.af_for_address(address) - - if af == socket.AF_INET6: - self.family = 2 - if srclen is None: - srclen = 56 - address = dns.rdata.Rdata._as_ipv6_address(address) - srclen = dns.rdata.Rdata._as_int(srclen, 0, 128) - scopelen = dns.rdata.Rdata._as_int(scopelen, 0, 128) - elif af == socket.AF_INET: - self.family = 1 - if srclen is None: - srclen = 24 - address = dns.rdata.Rdata._as_ipv4_address(address) - srclen = dns.rdata.Rdata._as_int(srclen, 0, 32) - scopelen = dns.rdata.Rdata._as_int(scopelen, 0, 32) - else: # pragma: no cover (this will never happen) - raise ValueError("Bad address family") - - assert srclen is not None - self.address = address - self.srclen = srclen - self.scopelen = scopelen - - addrdata = dns.inet.inet_pton(af, address) - nbytes = int(math.ceil(srclen / 8.0)) - - # Truncate to srclen and pad to the end of the last octet needed - # See RFC section 6 - self.addrdata = addrdata[:nbytes] - nbits = srclen % 8 - if nbits != 0: - last = struct.pack("B", ord(self.addrdata[-1:]) & (0xFF << (8 - nbits))) - self.addrdata = self.addrdata[:-1] + last - - def to_text(self) -> str: - return f"ECS {self.address}/{self.srclen} scope/{self.scopelen}" - - @staticmethod - def from_text(text: str) -> Option: - """Convert a string into a `dns.edns.ECSOption` - - *text*, a `str`, the text form of the option. - - Returns a `dns.edns.ECSOption`. - - Examples: - - >>> import dns.edns - >>> - >>> # basic example - >>> dns.edns.ECSOption.from_text('1.2.3.4/24') - >>> - >>> # also understands scope - >>> dns.edns.ECSOption.from_text('1.2.3.4/24/32') - >>> - >>> # IPv6 - >>> dns.edns.ECSOption.from_text('2001:4b98::1/64/64') - >>> - >>> # it understands results from `dns.edns.ECSOption.to_text()` - >>> dns.edns.ECSOption.from_text('ECS 1.2.3.4/24/32') - """ - optional_prefix = "ECS" - tokens = text.split() - ecs_text = None - if len(tokens) == 1: - ecs_text = tokens[0] - elif len(tokens) == 2: - if tokens[0] != optional_prefix: - raise ValueError(f'could not parse ECS from "{text}"') - ecs_text = tokens[1] - else: - raise ValueError(f'could not parse ECS from "{text}"') - n_slashes = ecs_text.count("/") - if n_slashes == 1: - address, tsrclen = ecs_text.split("/") - tscope = "0" - elif n_slashes == 2: - address, tsrclen, tscope = ecs_text.split("/") - else: - raise ValueError(f'could not parse ECS from "{text}"') - try: - scope = int(tscope) - except ValueError: - raise ValueError("invalid scope " + f'"{tscope}": scope must be an integer') - try: - srclen = int(tsrclen) - except ValueError: - raise ValueError( - "invalid srclen " + f'"{tsrclen}": srclen must be an integer' - ) - return ECSOption(address, srclen, scope) - - def to_wire(self, file: Optional[Any] = None) -> Optional[bytes]: - value = ( - struct.pack("!HBB", self.family, self.srclen, self.scopelen) + self.addrdata - ) - if file: - file.write(value) - return None - else: - return value - - @classmethod - def from_wire_parser( - cls, otype: Union[OptionType, str], parser: "dns.wire.Parser" - ) -> Option: - family, src, scope = parser.get_struct("!HBB") - addrlen = int(math.ceil(src / 8.0)) - prefix = parser.get_bytes(addrlen) - if family == 1: - pad = 4 - addrlen - addr = dns.ipv4.inet_ntoa(prefix + b"\x00" * pad) - elif family == 2: - pad = 16 - addrlen - addr = dns.ipv6.inet_ntoa(prefix + b"\x00" * pad) - else: - raise ValueError("unsupported family") - - return cls(addr, src, scope) - - -class EDECode(dns.enum.IntEnum): - OTHER = 0 - UNSUPPORTED_DNSKEY_ALGORITHM = 1 - UNSUPPORTED_DS_DIGEST_TYPE = 2 - STALE_ANSWER = 3 - FORGED_ANSWER = 4 - DNSSEC_INDETERMINATE = 5 - DNSSEC_BOGUS = 6 - SIGNATURE_EXPIRED = 7 - SIGNATURE_NOT_YET_VALID = 8 - DNSKEY_MISSING = 9 - RRSIGS_MISSING = 10 - NO_ZONE_KEY_BIT_SET = 11 - NSEC_MISSING = 12 - CACHED_ERROR = 13 - NOT_READY = 14 - BLOCKED = 15 - CENSORED = 16 - FILTERED = 17 - PROHIBITED = 18 - STALE_NXDOMAIN_ANSWER = 19 - NOT_AUTHORITATIVE = 20 - NOT_SUPPORTED = 21 - NO_REACHABLE_AUTHORITY = 22 - NETWORK_ERROR = 23 - INVALID_DATA = 24 - - @classmethod - def _maximum(cls): - return 65535 - - -class EDEOption(Option): # lgtm[py/missing-equals] - """Extended DNS Error (EDE, RFC8914)""" - - _preserve_case = {"DNSKEY", "DS", "DNSSEC", "RRSIGs", "NSEC", "NXDOMAIN"} - - def __init__(self, code: Union[EDECode, str], text: Optional[str] = None): - """*code*, a ``dns.edns.EDECode`` or ``str``, the info code of the - extended error. - - *text*, a ``str`` or ``None``, specifying additional information about - the error. - """ - - super().__init__(OptionType.EDE) - - self.code = EDECode.make(code) - if text is not None and not isinstance(text, str): - raise ValueError("text must be string or None") - self.text = text - - def to_text(self) -> str: - output = f"EDE {self.code}" - if self.code in EDECode: - desc = EDECode.to_text(self.code) - desc = " ".join( - word if word in self._preserve_case else word.title() - for word in desc.split("_") - ) - output += f" ({desc})" - if self.text is not None: - output += f": {self.text}" - return output - - def to_wire(self, file: Optional[Any] = None) -> Optional[bytes]: - value = struct.pack("!H", self.code) - if self.text is not None: - value += self.text.encode("utf8") - - if file: - file.write(value) - return None - else: - return value - - @classmethod - def from_wire_parser( - cls, otype: Union[OptionType, str], parser: "dns.wire.Parser" - ) -> Option: - code = EDECode.make(parser.get_uint16()) - text = parser.get_remaining() - - if text: - if text[-1] == 0: # text MAY be null-terminated - text = text[:-1] - btext = text.decode("utf8") - else: - btext = None - - return cls(code, btext) - - -class NSIDOption(Option): - def __init__(self, nsid: bytes): - super().__init__(OptionType.NSID) - self.nsid = nsid - - def to_wire(self, file: Any = None) -> Optional[bytes]: - if file: - file.write(self.nsid) - return None - else: - return self.nsid - - def to_text(self) -> str: - if all(c >= 0x20 and c <= 0x7E for c in self.nsid): - # All ASCII printable, so it's probably a string. - value = self.nsid.decode() - else: - value = binascii.hexlify(self.nsid).decode() - return f"NSID {value}" - - @classmethod - def from_wire_parser( - cls, otype: Union[OptionType, str], parser: dns.wire.Parser - ) -> Option: - return cls(parser.get_remaining()) - - -class CookieOption(Option): - def __init__(self, client: bytes, server: bytes): - super().__init__(dns.edns.OptionType.COOKIE) - self.client = client - self.server = server - if len(client) != 8: - raise ValueError("client cookie must be 8 bytes") - if len(server) != 0 and (len(server) < 8 or len(server) > 32): - raise ValueError("server cookie must be empty or between 8 and 32 bytes") - - def to_wire(self, file: Any = None) -> Optional[bytes]: - if file: - file.write(self.client) - if len(self.server) > 0: - file.write(self.server) - return None - else: - return self.client + self.server - - def to_text(self) -> str: - client = binascii.hexlify(self.client).decode() - if len(self.server) > 0: - server = binascii.hexlify(self.server).decode() - else: - server = "" - return f"COOKIE {client}{server}" - - @classmethod - def from_wire_parser( - cls, otype: Union[OptionType, str], parser: dns.wire.Parser - ) -> Option: - return cls(parser.get_bytes(8), parser.get_remaining()) - - -class ReportChannelOption(Option): - # RFC 9567 - def __init__(self, agent_domain: dns.name.Name): - super().__init__(OptionType.REPORTCHANNEL) - self.agent_domain = agent_domain - - def to_wire(self, file: Any = None) -> Optional[bytes]: - return self.agent_domain.to_wire(file) - - def to_text(self) -> str: - return "REPORTCHANNEL " + self.agent_domain.to_text() - - @classmethod - def from_wire_parser( - cls, otype: Union[OptionType, str], parser: dns.wire.Parser - ) -> Option: - return cls(parser.get_name()) - - -_type_to_class: Dict[OptionType, Any] = { - OptionType.ECS: ECSOption, - OptionType.EDE: EDEOption, - OptionType.NSID: NSIDOption, - OptionType.COOKIE: CookieOption, - OptionType.REPORTCHANNEL: ReportChannelOption, -} - - -def get_option_class(otype: OptionType) -> Any: - """Return the class for the specified option type. - - The GenericOption class is used if a more specific class is not - known. - """ - - cls = _type_to_class.get(otype) - if cls is None: - cls = GenericOption - return cls - - -def option_from_wire_parser( - otype: Union[OptionType, str], parser: "dns.wire.Parser" -) -> Option: - """Build an EDNS option object from wire format. - - *otype*, an ``int``, is the option type. - - *parser*, a ``dns.wire.Parser``, the parser, which should be - restricted to the option length. - - Returns an instance of a subclass of ``dns.edns.Option``. - """ - otype = OptionType.make(otype) - cls = get_option_class(otype) - return cls.from_wire_parser(otype, parser) - - -def option_from_wire( - otype: Union[OptionType, str], wire: bytes, current: int, olen: int -) -> Option: - """Build an EDNS option object from wire format. - - *otype*, an ``int``, is the option type. - - *wire*, a ``bytes``, is the wire-format message. - - *current*, an ``int``, is the offset in *wire* of the beginning - of the rdata. - - *olen*, an ``int``, is the length of the wire-format option data - - Returns an instance of a subclass of ``dns.edns.Option``. - """ - parser = dns.wire.Parser(wire, current) - with parser.restrict_to(olen): - return option_from_wire_parser(otype, parser) - - -def register_type(implementation: Any, otype: OptionType) -> None: - """Register the implementation of an option type. - - *implementation*, a ``class``, is a subclass of ``dns.edns.Option``. - - *otype*, an ``int``, is the option type. - """ - - _type_to_class[otype] = implementation - - -### BEGIN generated OptionType constants - -NSID = OptionType.NSID -DAU = OptionType.DAU -DHU = OptionType.DHU -N3U = OptionType.N3U -ECS = OptionType.ECS -EXPIRE = OptionType.EXPIRE -COOKIE = OptionType.COOKIE -KEEPALIVE = OptionType.KEEPALIVE -PADDING = OptionType.PADDING -CHAIN = OptionType.CHAIN -EDE = OptionType.EDE -REPORTCHANNEL = OptionType.REPORTCHANNEL - -### END generated OptionType constants diff --git a/venv/Lib/site-packages/dns/entropy.py b/venv/Lib/site-packages/dns/entropy.py deleted file mode 100644 index 4dcdc62..0000000 --- a/venv/Lib/site-packages/dns/entropy.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2009-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import hashlib -import os -import random -import threading -import time -from typing import Any, Optional - - -class EntropyPool: - # This is an entropy pool for Python implementations that do not - # have a working SystemRandom. I'm not sure there are any, but - # leaving this code doesn't hurt anything as the library code - # is used if present. - - def __init__(self, seed: Optional[bytes] = None): - self.pool_index = 0 - self.digest: Optional[bytearray] = None - self.next_byte = 0 - self.lock = threading.Lock() - self.hash = hashlib.sha1() - self.hash_len = 20 - self.pool = bytearray(b"\0" * self.hash_len) - if seed is not None: - self._stir(seed) - self.seeded = True - self.seed_pid = os.getpid() - else: - self.seeded = False - self.seed_pid = 0 - - def _stir(self, entropy: bytes) -> None: - for c in entropy: - if self.pool_index == self.hash_len: - self.pool_index = 0 - b = c & 0xFF - self.pool[self.pool_index] ^= b - self.pool_index += 1 - - def stir(self, entropy: bytes) -> None: - with self.lock: - self._stir(entropy) - - def _maybe_seed(self) -> None: - if not self.seeded or self.seed_pid != os.getpid(): - try: - seed = os.urandom(16) - except Exception: # pragma: no cover - try: - with open("/dev/urandom", "rb", 0) as r: - seed = r.read(16) - except Exception: - seed = str(time.time()).encode() - self.seeded = True - self.seed_pid = os.getpid() - self.digest = None - seed = bytearray(seed) - self._stir(seed) - - def random_8(self) -> int: - with self.lock: - self._maybe_seed() - if self.digest is None or self.next_byte == self.hash_len: - self.hash.update(bytes(self.pool)) - self.digest = bytearray(self.hash.digest()) - self._stir(self.digest) - self.next_byte = 0 - value = self.digest[self.next_byte] - self.next_byte += 1 - return value - - def random_16(self) -> int: - return self.random_8() * 256 + self.random_8() - - def random_32(self) -> int: - return self.random_16() * 65536 + self.random_16() - - def random_between(self, first: int, last: int) -> int: - size = last - first + 1 - if size > 4294967296: - raise ValueError("too big") - if size > 65536: - rand = self.random_32 - max = 4294967295 - elif size > 256: - rand = self.random_16 - max = 65535 - else: - rand = self.random_8 - max = 255 - return first + size * rand() // (max + 1) - - -pool = EntropyPool() - -system_random: Optional[Any] -try: - system_random = random.SystemRandom() -except Exception: # pragma: no cover - system_random = None - - -def random_16() -> int: - if system_random is not None: - return system_random.randrange(0, 65536) - else: - return pool.random_16() - - -def between(first: int, last: int) -> int: - if system_random is not None: - return system_random.randrange(first, last + 1) - else: - return pool.random_between(first, last) diff --git a/venv/Lib/site-packages/dns/enum.py b/venv/Lib/site-packages/dns/enum.py deleted file mode 100644 index 71461f1..0000000 --- a/venv/Lib/site-packages/dns/enum.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import enum -from typing import Type, TypeVar, Union - -TIntEnum = TypeVar("TIntEnum", bound="IntEnum") - - -class IntEnum(enum.IntEnum): - @classmethod - def _missing_(cls, value): - cls._check_value(value) - val = int.__new__(cls, value) - val._name_ = cls._extra_to_text(value, None) or f"{cls._prefix()}{value}" - val._value_ = value - return val - - @classmethod - def _check_value(cls, value): - max = cls._maximum() - if not isinstance(value, int): - raise TypeError - if value < 0 or value > max: - name = cls._short_name() - raise ValueError(f"{name} must be an int between >= 0 and <= {max}") - - @classmethod - def from_text(cls: Type[TIntEnum], text: str) -> TIntEnum: - text = text.upper() - try: - return cls[text] - except KeyError: - pass - value = cls._extra_from_text(text) - if value: - return value - prefix = cls._prefix() - if text.startswith(prefix) and text[len(prefix) :].isdigit(): - value = int(text[len(prefix) :]) - cls._check_value(value) - try: - return cls(value) - except ValueError: - return value - raise cls._unknown_exception_class() - - @classmethod - def to_text(cls: Type[TIntEnum], value: int) -> str: - cls._check_value(value) - try: - text = cls(value).name - except ValueError: - text = None - text = cls._extra_to_text(value, text) - if text is None: - text = f"{cls._prefix()}{value}" - return text - - @classmethod - def make(cls: Type[TIntEnum], value: Union[int, str]) -> TIntEnum: - """Convert text or a value into an enumerated type, if possible. - - *value*, the ``int`` or ``str`` to convert. - - Raises a class-specific exception if a ``str`` is provided that - cannot be converted. - - Raises ``ValueError`` if the value is out of range. - - Returns an enumeration from the calling class corresponding to the - value, if one is defined, or an ``int`` otherwise. - """ - - if isinstance(value, str): - return cls.from_text(value) - cls._check_value(value) - return cls(value) - - @classmethod - def _maximum(cls): - raise NotImplementedError # pragma: no cover - - @classmethod - def _short_name(cls): - return cls.__name__.lower() - - @classmethod - def _prefix(cls): - return "" - - @classmethod - def _extra_from_text(cls, text): # pylint: disable=W0613 - return None - - @classmethod - def _extra_to_text(cls, value, current_text): # pylint: disable=W0613 - return current_text - - @classmethod - def _unknown_exception_class(cls): - return ValueError diff --git a/venv/Lib/site-packages/dns/exception.py b/venv/Lib/site-packages/dns/exception.py deleted file mode 100644 index 223f2d6..0000000 --- a/venv/Lib/site-packages/dns/exception.py +++ /dev/null @@ -1,169 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Common DNS Exceptions. - -Dnspython modules may also define their own exceptions, which will -always be subclasses of ``DNSException``. -""" - - -from typing import Optional, Set - - -class DNSException(Exception): - """Abstract base class shared by all dnspython exceptions. - - It supports two basic modes of operation: - - a) Old/compatible mode is used if ``__init__`` was called with - empty *kwargs*. In compatible mode all *args* are passed - to the standard Python Exception class as before and all *args* are - printed by the standard ``__str__`` implementation. Class variable - ``msg`` (or doc string if ``msg`` is ``None``) is returned from ``str()`` - if *args* is empty. - - b) New/parametrized mode is used if ``__init__`` was called with - non-empty *kwargs*. - In the new mode *args* must be empty and all kwargs must match - those set in class variable ``supp_kwargs``. All kwargs are stored inside - ``self.kwargs`` and used in a new ``__str__`` implementation to construct - a formatted message based on the ``fmt`` class variable, a ``string``. - - In the simplest case it is enough to override the ``supp_kwargs`` - and ``fmt`` class variables to get nice parametrized messages. - """ - - msg: Optional[str] = None # non-parametrized message - supp_kwargs: Set[str] = set() # accepted parameters for _fmt_kwargs (sanity check) - fmt: Optional[str] = None # message parametrized with results from _fmt_kwargs - - def __init__(self, *args, **kwargs): - self._check_params(*args, **kwargs) - if kwargs: - # This call to a virtual method from __init__ is ok in our usage - self.kwargs = self._check_kwargs(**kwargs) # lgtm[py/init-calls-subclass] - self.msg = str(self) - else: - self.kwargs = dict() # defined but empty for old mode exceptions - if self.msg is None: - # doc string is better implicit message than empty string - self.msg = self.__doc__ - if args: - super().__init__(*args) - else: - super().__init__(self.msg) - - def _check_params(self, *args, **kwargs): - """Old exceptions supported only args and not kwargs. - - For sanity we do not allow to mix old and new behavior.""" - if args or kwargs: - assert bool(args) != bool( - kwargs - ), "keyword arguments are mutually exclusive with positional args" - - def _check_kwargs(self, **kwargs): - if kwargs: - assert ( - set(kwargs.keys()) == self.supp_kwargs - ), f"following set of keyword args is required: {self.supp_kwargs}" - return kwargs - - def _fmt_kwargs(self, **kwargs): - """Format kwargs before printing them. - - Resulting dictionary has to have keys necessary for str.format call - on fmt class variable. - """ - fmtargs = {} - for kw, data in kwargs.items(): - if isinstance(data, (list, set)): - # convert list of to list of str() - fmtargs[kw] = list(map(str, data)) - if len(fmtargs[kw]) == 1: - # remove list brackets [] from single-item lists - fmtargs[kw] = fmtargs[kw].pop() - else: - fmtargs[kw] = data - return fmtargs - - def __str__(self): - if self.kwargs and self.fmt: - # provide custom message constructed from keyword arguments - fmtargs = self._fmt_kwargs(**self.kwargs) - return self.fmt.format(**fmtargs) - else: - # print *args directly in the same way as old DNSException - return super().__str__() - - -class FormError(DNSException): - """DNS message is malformed.""" - - -class SyntaxError(DNSException): - """Text input is malformed.""" - - -class UnexpectedEnd(SyntaxError): - """Text input ended unexpectedly.""" - - -class TooBig(DNSException): - """The DNS message is too big.""" - - -class Timeout(DNSException): - """The DNS operation timed out.""" - - supp_kwargs = {"timeout"} - fmt = "The DNS operation timed out after {timeout:.3f} seconds" - - # We do this as otherwise mypy complains about unexpected keyword argument - # idna_exception - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - -class UnsupportedAlgorithm(DNSException): - """The DNSSEC algorithm is not supported.""" - - -class AlgorithmKeyMismatch(UnsupportedAlgorithm): - """The DNSSEC algorithm is not supported for the given key type.""" - - -class ValidationFailure(DNSException): - """The DNSSEC signature is invalid.""" - - -class DeniedByPolicy(DNSException): - """Denied by DNSSEC policy.""" - - -class ExceptionWrapper: - def __init__(self, exception_class): - self.exception_class = exception_class - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - if exc_type is not None and not isinstance(exc_val, self.exception_class): - raise self.exception_class(str(exc_val)) from exc_val - return False diff --git a/venv/Lib/site-packages/dns/flags.py b/venv/Lib/site-packages/dns/flags.py deleted file mode 100644 index 4c60be1..0000000 --- a/venv/Lib/site-packages/dns/flags.py +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Message Flags.""" - -import enum -from typing import Any - -# Standard DNS flags - - -class Flag(enum.IntFlag): - #: Query Response - QR = 0x8000 - #: Authoritative Answer - AA = 0x0400 - #: Truncated Response - TC = 0x0200 - #: Recursion Desired - RD = 0x0100 - #: Recursion Available - RA = 0x0080 - #: Authentic Data - AD = 0x0020 - #: Checking Disabled - CD = 0x0010 - - -# EDNS flags - - -class EDNSFlag(enum.IntFlag): - #: DNSSEC answer OK - DO = 0x8000 - - -def _from_text(text: str, enum_class: Any) -> int: - flags = 0 - tokens = text.split() - for t in tokens: - flags |= enum_class[t.upper()] - return flags - - -def _to_text(flags: int, enum_class: Any) -> str: - text_flags = [] - for k, v in enum_class.__members__.items(): - if flags & v != 0: - text_flags.append(k) - return " ".join(text_flags) - - -def from_text(text: str) -> int: - """Convert a space-separated list of flag text values into a flags - value. - - Returns an ``int`` - """ - - return _from_text(text, Flag) - - -def to_text(flags: int) -> str: - """Convert a flags value into a space-separated list of flag text - values. - - Returns a ``str``. - """ - - return _to_text(flags, Flag) - - -def edns_from_text(text: str) -> int: - """Convert a space-separated list of EDNS flag text values into a EDNS - flags value. - - Returns an ``int`` - """ - - return _from_text(text, EDNSFlag) - - -def edns_to_text(flags: int) -> str: - """Convert an EDNS flags value into a space-separated list of EDNS flag - text values. - - Returns a ``str``. - """ - - return _to_text(flags, EDNSFlag) - - -### BEGIN generated Flag constants - -QR = Flag.QR -AA = Flag.AA -TC = Flag.TC -RD = Flag.RD -RA = Flag.RA -AD = Flag.AD -CD = Flag.CD - -### END generated Flag constants - -### BEGIN generated EDNSFlag constants - -DO = EDNSFlag.DO - -### END generated EDNSFlag constants diff --git a/venv/Lib/site-packages/dns/grange.py b/venv/Lib/site-packages/dns/grange.py deleted file mode 100644 index a967ca4..0000000 --- a/venv/Lib/site-packages/dns/grange.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2012-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS GENERATE range conversion.""" - -from typing import Tuple - -import dns - - -def from_text(text: str) -> Tuple[int, int, int]: - """Convert the text form of a range in a ``$GENERATE`` statement to an - integer. - - *text*, a ``str``, the textual range in ``$GENERATE`` form. - - Returns a tuple of three ``int`` values ``(start, stop, step)``. - """ - - start = -1 - stop = -1 - step = 1 - cur = "" - state = 0 - # state 0 1 2 - # x - y / z - - if text and text[0] == "-": - raise dns.exception.SyntaxError("Start cannot be a negative number") - - for c in text: - if c == "-" and state == 0: - start = int(cur) - cur = "" - state = 1 - elif c == "/": - stop = int(cur) - cur = "" - state = 2 - elif c.isdigit(): - cur += c - else: - raise dns.exception.SyntaxError(f"Could not parse {c}") - - if state == 0: - raise dns.exception.SyntaxError("no stop value specified") - elif state == 1: - stop = int(cur) - else: - assert state == 2 - step = int(cur) - - assert step >= 1 - assert start >= 0 - if start > stop: - raise dns.exception.SyntaxError("start must be <= stop") - - return (start, stop, step) diff --git a/venv/Lib/site-packages/dns/immutable.py b/venv/Lib/site-packages/dns/immutable.py deleted file mode 100644 index 36b0362..0000000 --- a/venv/Lib/site-packages/dns/immutable.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import collections.abc -from typing import Any, Callable - -from dns._immutable_ctx import immutable - - -@immutable -class Dict(collections.abc.Mapping): # lgtm[py/missing-equals] - def __init__( - self, - dictionary: Any, - no_copy: bool = False, - map_factory: Callable[[], collections.abc.MutableMapping] = dict, - ): - """Make an immutable dictionary from the specified dictionary. - - If *no_copy* is `True`, then *dictionary* will be wrapped instead - of copied. Only set this if you are sure there will be no external - references to the dictionary. - """ - if no_copy and isinstance(dictionary, collections.abc.MutableMapping): - self._odict = dictionary - else: - self._odict = map_factory() - self._odict.update(dictionary) - self._hash = None - - def __getitem__(self, key): - return self._odict.__getitem__(key) - - def __hash__(self): # pylint: disable=invalid-hash-returned - if self._hash is None: - h = 0 - for key in sorted(self._odict.keys()): - h ^= hash(key) - object.__setattr__(self, "_hash", h) - # this does return an int, but pylint doesn't figure that out - return self._hash - - def __len__(self): - return len(self._odict) - - def __iter__(self): - return iter(self._odict) - - -def constify(o: Any) -> Any: - """ - Convert mutable types to immutable types. - """ - if isinstance(o, bytearray): - return bytes(o) - if isinstance(o, tuple): - try: - hash(o) - return o - except Exception: - return tuple(constify(elt) for elt in o) - if isinstance(o, list): - return tuple(constify(elt) for elt in o) - if isinstance(o, dict): - cdict = dict() - for k, v in o.items(): - cdict[k] = constify(v) - return Dict(cdict, True) - return o diff --git a/venv/Lib/site-packages/dns/inet.py b/venv/Lib/site-packages/dns/inet.py deleted file mode 100644 index 4a03f99..0000000 --- a/venv/Lib/site-packages/dns/inet.py +++ /dev/null @@ -1,197 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Generic Internet address helper functions.""" - -import socket -from typing import Any, Optional, Tuple - -import dns.ipv4 -import dns.ipv6 - -# We assume that AF_INET and AF_INET6 are always defined. We keep -# these here for the benefit of any old code (unlikely though that -# is!). -AF_INET = socket.AF_INET -AF_INET6 = socket.AF_INET6 - - -def inet_pton(family: int, text: str) -> bytes: - """Convert the textual form of a network address into its binary form. - - *family* is an ``int``, the address family. - - *text* is a ``str``, the textual address. - - Raises ``NotImplementedError`` if the address family specified is not - implemented. - - Returns a ``bytes``. - """ - - if family == AF_INET: - return dns.ipv4.inet_aton(text) - elif family == AF_INET6: - return dns.ipv6.inet_aton(text, True) - else: - raise NotImplementedError - - -def inet_ntop(family: int, address: bytes) -> str: - """Convert the binary form of a network address into its textual form. - - *family* is an ``int``, the address family. - - *address* is a ``bytes``, the network address in binary form. - - Raises ``NotImplementedError`` if the address family specified is not - implemented. - - Returns a ``str``. - """ - - if family == AF_INET: - return dns.ipv4.inet_ntoa(address) - elif family == AF_INET6: - return dns.ipv6.inet_ntoa(address) - else: - raise NotImplementedError - - -def af_for_address(text: str) -> int: - """Determine the address family of a textual-form network address. - - *text*, a ``str``, the textual address. - - Raises ``ValueError`` if the address family cannot be determined - from the input. - - Returns an ``int``. - """ - - try: - dns.ipv4.inet_aton(text) - return AF_INET - except Exception: - try: - dns.ipv6.inet_aton(text, True) - return AF_INET6 - except Exception: - raise ValueError - - -def is_multicast(text: str) -> bool: - """Is the textual-form network address a multicast address? - - *text*, a ``str``, the textual address. - - Raises ``ValueError`` if the address family cannot be determined - from the input. - - Returns a ``bool``. - """ - - try: - first = dns.ipv4.inet_aton(text)[0] - return first >= 224 and first <= 239 - except Exception: - try: - first = dns.ipv6.inet_aton(text, True)[0] - return first == 255 - except Exception: - raise ValueError - - -def is_address(text: str) -> bool: - """Is the specified string an IPv4 or IPv6 address? - - *text*, a ``str``, the textual address. - - Returns a ``bool``. - """ - - try: - dns.ipv4.inet_aton(text) - return True - except Exception: - try: - dns.ipv6.inet_aton(text, True) - return True - except Exception: - return False - - -def low_level_address_tuple( - high_tuple: Tuple[str, int], af: Optional[int] = None -) -> Any: - """Given a "high-level" address tuple, i.e. - an (address, port) return the appropriate "low-level" address tuple - suitable for use in socket calls. - - If an *af* other than ``None`` is provided, it is assumed the - address in the high-level tuple is valid and has that af. If af - is ``None``, then af_for_address will be called. - """ - address, port = high_tuple - if af is None: - af = af_for_address(address) - if af == AF_INET: - return (address, port) - elif af == AF_INET6: - i = address.find("%") - if i < 0: - # no scope, shortcut! - return (address, port, 0, 0) - # try to avoid getaddrinfo() - addrpart = address[:i] - scope = address[i + 1 :] - if scope.isdigit(): - return (addrpart, port, 0, int(scope)) - try: - return (addrpart, port, 0, socket.if_nametoindex(scope)) - except AttributeError: # pragma: no cover (we can't really test this) - ai_flags = socket.AI_NUMERICHOST - ((*_, tup), *_) = socket.getaddrinfo(address, port, flags=ai_flags) - return tup - else: - raise NotImplementedError(f"unknown address family {af}") - - -def any_for_af(af): - """Return the 'any' address for the specified address family.""" - if af == socket.AF_INET: - return "0.0.0.0" - elif af == socket.AF_INET6: - return "::" - raise NotImplementedError(f"unknown address family {af}") - - -def canonicalize(text: str) -> str: - """Verify that *address* is a valid text form IPv4 or IPv6 address and return its - canonical text form. IPv6 addresses with scopes are rejected. - - *text*, a ``str``, the address in textual form. - - Raises ``ValueError`` if the text is not valid. - """ - try: - return dns.ipv6.canonicalize(text) - except Exception: - try: - return dns.ipv4.canonicalize(text) - except Exception: - raise ValueError diff --git a/venv/Lib/site-packages/dns/ipv4.py b/venv/Lib/site-packages/dns/ipv4.py deleted file mode 100644 index 65ee69c..0000000 --- a/venv/Lib/site-packages/dns/ipv4.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""IPv4 helper functions.""" - -import struct -from typing import Union - -import dns.exception - - -def inet_ntoa(address: bytes) -> str: - """Convert an IPv4 address in binary form to text form. - - *address*, a ``bytes``, the IPv4 address in binary form. - - Returns a ``str``. - """ - - if len(address) != 4: - raise dns.exception.SyntaxError - return "%u.%u.%u.%u" % (address[0], address[1], address[2], address[3]) - - -def inet_aton(text: Union[str, bytes]) -> bytes: - """Convert an IPv4 address in text form to binary form. - - *text*, a ``str`` or ``bytes``, the IPv4 address in textual form. - - Returns a ``bytes``. - """ - - if not isinstance(text, bytes): - btext = text.encode() - else: - btext = text - parts = btext.split(b".") - if len(parts) != 4: - raise dns.exception.SyntaxError - for part in parts: - if not part.isdigit(): - raise dns.exception.SyntaxError - if len(part) > 1 and part[0] == ord("0"): - # No leading zeros - raise dns.exception.SyntaxError - try: - b = [int(part) for part in parts] - return struct.pack("BBBB", *b) - except Exception: - raise dns.exception.SyntaxError - - -def canonicalize(text: Union[str, bytes]) -> str: - """Verify that *address* is a valid text form IPv4 address and return its - canonical text form. - - *text*, a ``str`` or ``bytes``, the IPv4 address in textual form. - - Raises ``dns.exception.SyntaxError`` if the text is not valid. - """ - # Note that inet_aton() only accepts canonial form, but we still run through - # inet_ntoa() to ensure the output is a str. - return dns.ipv4.inet_ntoa(dns.ipv4.inet_aton(text)) diff --git a/venv/Lib/site-packages/dns/ipv6.py b/venv/Lib/site-packages/dns/ipv6.py deleted file mode 100644 index 4dd1d1c..0000000 --- a/venv/Lib/site-packages/dns/ipv6.py +++ /dev/null @@ -1,217 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""IPv6 helper functions.""" - -import binascii -import re -from typing import List, Union - -import dns.exception -import dns.ipv4 - -_leading_zero = re.compile(r"0+([0-9a-f]+)") - - -def inet_ntoa(address: bytes) -> str: - """Convert an IPv6 address in binary form to text form. - - *address*, a ``bytes``, the IPv6 address in binary form. - - Raises ``ValueError`` if the address isn't 16 bytes long. - Returns a ``str``. - """ - - if len(address) != 16: - raise ValueError("IPv6 addresses are 16 bytes long") - hex = binascii.hexlify(address) - chunks = [] - i = 0 - l = len(hex) - while i < l: - chunk = hex[i : i + 4].decode() - # strip leading zeros. we do this with an re instead of - # with lstrip() because lstrip() didn't support chars until - # python 2.2.2 - m = _leading_zero.match(chunk) - if m is not None: - chunk = m.group(1) - chunks.append(chunk) - i += 4 - # - # Compress the longest subsequence of 0-value chunks to :: - # - best_start = 0 - best_len = 0 - start = -1 - last_was_zero = False - for i in range(8): - if chunks[i] != "0": - if last_was_zero: - end = i - current_len = end - start - if current_len > best_len: - best_start = start - best_len = current_len - last_was_zero = False - elif not last_was_zero: - start = i - last_was_zero = True - if last_was_zero: - end = 8 - current_len = end - start - if current_len > best_len: - best_start = start - best_len = current_len - if best_len > 1: - if best_start == 0 and (best_len == 6 or best_len == 5 and chunks[5] == "ffff"): - # We have an embedded IPv4 address - if best_len == 6: - prefix = "::" - else: - prefix = "::ffff:" - thex = prefix + dns.ipv4.inet_ntoa(address[12:]) - else: - thex = ( - ":".join(chunks[:best_start]) - + "::" - + ":".join(chunks[best_start + best_len :]) - ) - else: - thex = ":".join(chunks) - return thex - - -_v4_ending = re.compile(rb"(.*):(\d+\.\d+\.\d+\.\d+)$") -_colon_colon_start = re.compile(rb"::.*") -_colon_colon_end = re.compile(rb".*::$") - - -def inet_aton(text: Union[str, bytes], ignore_scope: bool = False) -> bytes: - """Convert an IPv6 address in text form to binary form. - - *text*, a ``str`` or ``bytes``, the IPv6 address in textual form. - - *ignore_scope*, a ``bool``. If ``True``, a scope will be ignored. - If ``False``, the default, it is an error for a scope to be present. - - Returns a ``bytes``. - """ - - # - # Our aim here is not something fast; we just want something that works. - # - if not isinstance(text, bytes): - btext = text.encode() - else: - btext = text - - if ignore_scope: - parts = btext.split(b"%") - l = len(parts) - if l == 2: - btext = parts[0] - elif l > 2: - raise dns.exception.SyntaxError - - if btext == b"": - raise dns.exception.SyntaxError - elif btext.endswith(b":") and not btext.endswith(b"::"): - raise dns.exception.SyntaxError - elif btext.startswith(b":") and not btext.startswith(b"::"): - raise dns.exception.SyntaxError - elif btext == b"::": - btext = b"0::" - # - # Get rid of the icky dot-quad syntax if we have it. - # - m = _v4_ending.match(btext) - if m is not None: - b = dns.ipv4.inet_aton(m.group(2)) - btext = ( - f"{m.group(1).decode()}:{b[0]:02x}{b[1]:02x}:{b[2]:02x}{b[3]:02x}" - ).encode() - # - # Try to turn '::' into ':'; if no match try to - # turn '::' into ':' - # - m = _colon_colon_start.match(btext) - if m is not None: - btext = btext[1:] - else: - m = _colon_colon_end.match(btext) - if m is not None: - btext = btext[:-1] - # - # Now canonicalize into 8 chunks of 4 hex digits each - # - chunks = btext.split(b":") - l = len(chunks) - if l > 8: - raise dns.exception.SyntaxError - seen_empty = False - canonical: List[bytes] = [] - for c in chunks: - if c == b"": - if seen_empty: - raise dns.exception.SyntaxError - seen_empty = True - for _ in range(0, 8 - l + 1): - canonical.append(b"0000") - else: - lc = len(c) - if lc > 4: - raise dns.exception.SyntaxError - if lc != 4: - c = (b"0" * (4 - lc)) + c - canonical.append(c) - if l < 8 and not seen_empty: - raise dns.exception.SyntaxError - btext = b"".join(canonical) - - # - # Finally we can go to binary. - # - try: - return binascii.unhexlify(btext) - except (binascii.Error, TypeError): - raise dns.exception.SyntaxError - - -_mapped_prefix = b"\x00" * 10 + b"\xff\xff" - - -def is_mapped(address: bytes) -> bool: - """Is the specified address a mapped IPv4 address? - - *address*, a ``bytes`` is an IPv6 address in binary form. - - Returns a ``bool``. - """ - - return address.startswith(_mapped_prefix) - - -def canonicalize(text: Union[str, bytes]) -> str: - """Verify that *address* is a valid text form IPv6 address and return its - canonical text form. Addresses with scopes are rejected. - - *text*, a ``str`` or ``bytes``, the IPv6 address in textual form. - - Raises ``dns.exception.SyntaxError`` if the text is not valid. - """ - return dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(text)) diff --git a/venv/Lib/site-packages/dns/message.py b/venv/Lib/site-packages/dns/message.py deleted file mode 100644 index e978a0a..0000000 --- a/venv/Lib/site-packages/dns/message.py +++ /dev/null @@ -1,1933 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Messages""" - -import contextlib -import enum -import io -import time -from typing import Any, Dict, List, Optional, Tuple, Union, cast - -import dns.edns -import dns.entropy -import dns.enum -import dns.exception -import dns.flags -import dns.name -import dns.opcode -import dns.rcode -import dns.rdata -import dns.rdataclass -import dns.rdatatype -import dns.rdtypes.ANY.OPT -import dns.rdtypes.ANY.TSIG -import dns.renderer -import dns.rrset -import dns.tsig -import dns.ttl -import dns.wire - - -class ShortHeader(dns.exception.FormError): - """The DNS packet passed to from_wire() is too short.""" - - -class TrailingJunk(dns.exception.FormError): - """The DNS packet passed to from_wire() has extra junk at the end of it.""" - - -class UnknownHeaderField(dns.exception.DNSException): - """The header field name was not recognized when converting from text - into a message.""" - - -class BadEDNS(dns.exception.FormError): - """An OPT record occurred somewhere other than - the additional data section.""" - - -class BadTSIG(dns.exception.FormError): - """A TSIG record occurred somewhere other than the end of - the additional data section.""" - - -class UnknownTSIGKey(dns.exception.DNSException): - """A TSIG with an unknown key was received.""" - - -class Truncated(dns.exception.DNSException): - """The truncated flag is set.""" - - supp_kwargs = {"message"} - - # We do this as otherwise mypy complains about unexpected keyword argument - # idna_exception - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def message(self): - """As much of the message as could be processed. - - Returns a ``dns.message.Message``. - """ - return self.kwargs["message"] - - -class NotQueryResponse(dns.exception.DNSException): - """Message is not a response to a query.""" - - -class ChainTooLong(dns.exception.DNSException): - """The CNAME chain is too long.""" - - -class AnswerForNXDOMAIN(dns.exception.DNSException): - """The rcode is NXDOMAIN but an answer was found.""" - - -class NoPreviousName(dns.exception.SyntaxError): - """No previous name was known.""" - - -class MessageSection(dns.enum.IntEnum): - """Message sections""" - - QUESTION = 0 - ANSWER = 1 - AUTHORITY = 2 - ADDITIONAL = 3 - - @classmethod - def _maximum(cls): - return 3 - - -class MessageError: - def __init__(self, exception: Exception, offset: int): - self.exception = exception - self.offset = offset - - -DEFAULT_EDNS_PAYLOAD = 1232 -MAX_CHAIN = 16 - -IndexKeyType = Tuple[ - int, - dns.name.Name, - dns.rdataclass.RdataClass, - dns.rdatatype.RdataType, - Optional[dns.rdatatype.RdataType], - Optional[dns.rdataclass.RdataClass], -] -IndexType = Dict[IndexKeyType, dns.rrset.RRset] -SectionType = Union[int, str, List[dns.rrset.RRset]] - - -class Message: - """A DNS message.""" - - _section_enum = MessageSection - - def __init__(self, id: Optional[int] = None): - if id is None: - self.id = dns.entropy.random_16() - else: - self.id = id - self.flags = 0 - self.sections: List[List[dns.rrset.RRset]] = [[], [], [], []] - self.opt: Optional[dns.rrset.RRset] = None - self.request_payload = 0 - self.pad = 0 - self.keyring: Any = None - self.tsig: Optional[dns.rrset.RRset] = None - self.request_mac = b"" - self.xfr = False - self.origin: Optional[dns.name.Name] = None - self.tsig_ctx: Optional[Any] = None - self.index: IndexType = {} - self.errors: List[MessageError] = [] - self.time = 0.0 - self.wire: Optional[bytes] = None - - @property - def question(self) -> List[dns.rrset.RRset]: - """The question section.""" - return self.sections[0] - - @question.setter - def question(self, v): - self.sections[0] = v - - @property - def answer(self) -> List[dns.rrset.RRset]: - """The answer section.""" - return self.sections[1] - - @answer.setter - def answer(self, v): - self.sections[1] = v - - @property - def authority(self) -> List[dns.rrset.RRset]: - """The authority section.""" - return self.sections[2] - - @authority.setter - def authority(self, v): - self.sections[2] = v - - @property - def additional(self) -> List[dns.rrset.RRset]: - """The additional data section.""" - return self.sections[3] - - @additional.setter - def additional(self, v): - self.sections[3] = v - - def __repr__(self): - return "" - - def __str__(self): - return self.to_text() - - def to_text( - self, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - **kw: Dict[str, Any], - ) -> str: - """Convert the message to text. - - The *origin*, *relativize*, and any other keyword - arguments are passed to the RRset ``to_wire()`` method. - - Returns a ``str``. - """ - - s = io.StringIO() - s.write("id %d\n" % self.id) - s.write(f"opcode {dns.opcode.to_text(self.opcode())}\n") - s.write(f"rcode {dns.rcode.to_text(self.rcode())}\n") - s.write(f"flags {dns.flags.to_text(self.flags)}\n") - if self.edns >= 0: - s.write(f"edns {self.edns}\n") - if self.ednsflags != 0: - s.write(f"eflags {dns.flags.edns_to_text(self.ednsflags)}\n") - s.write("payload %d\n" % self.payload) - for opt in self.options: - s.write(f"option {opt.to_text()}\n") - for name, which in self._section_enum.__members__.items(): - s.write(f";{name}\n") - for rrset in self.section_from_number(which): - s.write(rrset.to_text(origin, relativize, **kw)) - s.write("\n") - # - # We strip off the final \n so the caller can print the result without - # doing weird things to get around eccentricities in Python print - # formatting - # - return s.getvalue()[:-1] - - def __eq__(self, other): - """Two messages are equal if they have the same content in the - header, question, answer, and authority sections. - - Returns a ``bool``. - """ - - if not isinstance(other, Message): - return False - if self.id != other.id: - return False - if self.flags != other.flags: - return False - for i, section in enumerate(self.sections): - other_section = other.sections[i] - for n in section: - if n not in other_section: - return False - for n in other_section: - if n not in section: - return False - return True - - def __ne__(self, other): - return not self.__eq__(other) - - def is_response(self, other: "Message") -> bool: - """Is *other*, also a ``dns.message.Message``, a response to this - message? - - Returns a ``bool``. - """ - - if ( - other.flags & dns.flags.QR == 0 - or self.id != other.id - or dns.opcode.from_flags(self.flags) != dns.opcode.from_flags(other.flags) - ): - return False - if other.rcode() in { - dns.rcode.FORMERR, - dns.rcode.SERVFAIL, - dns.rcode.NOTIMP, - dns.rcode.REFUSED, - }: - # We don't check the question section in these cases if - # the other question section is empty, even though they - # still really ought to have a question section. - if len(other.question) == 0: - return True - if dns.opcode.is_update(self.flags): - # This is assuming the "sender doesn't include anything - # from the update", but we don't care to check the other - # case, which is that all the sections are returned and - # identical. - return True - for n in self.question: - if n not in other.question: - return False - for n in other.question: - if n not in self.question: - return False - return True - - def section_number(self, section: List[dns.rrset.RRset]) -> int: - """Return the "section number" of the specified section for use - in indexing. - - *section* is one of the section attributes of this message. - - Raises ``ValueError`` if the section isn't known. - - Returns an ``int``. - """ - - for i, our_section in enumerate(self.sections): - if section is our_section: - return self._section_enum(i) - raise ValueError("unknown section") - - def section_from_number(self, number: int) -> List[dns.rrset.RRset]: - """Return the section list associated with the specified section - number. - - *number* is a section number `int` or the text form of a section - name. - - Raises ``ValueError`` if the section isn't known. - - Returns a ``list``. - """ - - section = self._section_enum.make(number) - return self.sections[section] - - def find_rrset( - self, - section: SectionType, - name: dns.name.Name, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - deleting: Optional[dns.rdataclass.RdataClass] = None, - create: bool = False, - force_unique: bool = False, - idna_codec: Optional[dns.name.IDNACodec] = None, - ) -> dns.rrset.RRset: - """Find the RRset with the given attributes in the specified section. - - *section*, an ``int`` section number, a ``str`` section name, or one of - the section attributes of this message. This specifies the - the section of the message to search. For example:: - - my_message.find_rrset(my_message.answer, name, rdclass, rdtype) - my_message.find_rrset(dns.message.ANSWER, name, rdclass, rdtype) - my_message.find_rrset("ANSWER", name, rdclass, rdtype) - - *name*, a ``dns.name.Name`` or ``str``, the name of the RRset. - - *rdclass*, an ``int`` or ``str``, the class of the RRset. - - *rdtype*, an ``int`` or ``str``, the type of the RRset. - - *covers*, an ``int`` or ``str``, the covers value of the RRset. - The default is ``dns.rdatatype.NONE``. - - *deleting*, an ``int``, ``str``, or ``None``, the deleting value of the - RRset. The default is ``None``. - - *create*, a ``bool``. If ``True``, create the RRset if it is not found. - The created RRset is appended to *section*. - - *force_unique*, a ``bool``. If ``True`` and *create* is also ``True``, - create a new RRset regardless of whether a matching RRset exists - already. The default is ``False``. This is useful when creating - DDNS Update messages, as order matters for them. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - Raises ``KeyError`` if the RRset was not found and create was - ``False``. - - Returns a ``dns.rrset.RRset object``. - """ - - if isinstance(section, int): - section_number = section - section = self.section_from_number(section_number) - elif isinstance(section, str): - section_number = self._section_enum.from_text(section) - section = self.section_from_number(section_number) - else: - section_number = self.section_number(section) - if isinstance(name, str): - name = dns.name.from_text(name, idna_codec=idna_codec) - rdtype = dns.rdatatype.RdataType.make(rdtype) - rdclass = dns.rdataclass.RdataClass.make(rdclass) - covers = dns.rdatatype.RdataType.make(covers) - if deleting is not None: - deleting = dns.rdataclass.RdataClass.make(deleting) - key = (section_number, name, rdclass, rdtype, covers, deleting) - if not force_unique: - if self.index is not None: - rrset = self.index.get(key) - if rrset is not None: - return rrset - else: - for rrset in section: - if rrset.full_match(name, rdclass, rdtype, covers, deleting): - return rrset - if not create: - raise KeyError - rrset = dns.rrset.RRset(name, rdclass, rdtype, covers, deleting) - section.append(rrset) - if self.index is not None: - self.index[key] = rrset - return rrset - - def get_rrset( - self, - section: SectionType, - name: dns.name.Name, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - deleting: Optional[dns.rdataclass.RdataClass] = None, - create: bool = False, - force_unique: bool = False, - idna_codec: Optional[dns.name.IDNACodec] = None, - ) -> Optional[dns.rrset.RRset]: - """Get the RRset with the given attributes in the specified section. - - If the RRset is not found, None is returned. - - *section*, an ``int`` section number, a ``str`` section name, or one of - the section attributes of this message. This specifies the - the section of the message to search. For example:: - - my_message.get_rrset(my_message.answer, name, rdclass, rdtype) - my_message.get_rrset(dns.message.ANSWER, name, rdclass, rdtype) - my_message.get_rrset("ANSWER", name, rdclass, rdtype) - - *name*, a ``dns.name.Name`` or ``str``, the name of the RRset. - - *rdclass*, an ``int`` or ``str``, the class of the RRset. - - *rdtype*, an ``int`` or ``str``, the type of the RRset. - - *covers*, an ``int`` or ``str``, the covers value of the RRset. - The default is ``dns.rdatatype.NONE``. - - *deleting*, an ``int``, ``str``, or ``None``, the deleting value of the - RRset. The default is ``None``. - - *create*, a ``bool``. If ``True``, create the RRset if it is not found. - The created RRset is appended to *section*. - - *force_unique*, a ``bool``. If ``True`` and *create* is also ``True``, - create a new RRset regardless of whether a matching RRset exists - already. The default is ``False``. This is useful when creating - DDNS Update messages, as order matters for them. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - Returns a ``dns.rrset.RRset object`` or ``None``. - """ - - try: - rrset = self.find_rrset( - section, - name, - rdclass, - rdtype, - covers, - deleting, - create, - force_unique, - idna_codec, - ) - except KeyError: - rrset = None - return rrset - - def section_count(self, section: SectionType) -> int: - """Returns the number of records in the specified section. - - *section*, an ``int`` section number, a ``str`` section name, or one of - the section attributes of this message. This specifies the - the section of the message to count. For example:: - - my_message.section_count(my_message.answer) - my_message.section_count(dns.message.ANSWER) - my_message.section_count("ANSWER") - """ - - if isinstance(section, int): - section_number = section - section = self.section_from_number(section_number) - elif isinstance(section, str): - section_number = self._section_enum.from_text(section) - section = self.section_from_number(section_number) - else: - section_number = self.section_number(section) - count = sum(max(1, len(rrs)) for rrs in section) - if section_number == MessageSection.ADDITIONAL: - if self.opt is not None: - count += 1 - if self.tsig is not None: - count += 1 - return count - - def _compute_opt_reserve(self) -> int: - """Compute the size required for the OPT RR, padding excluded""" - if not self.opt: - return 0 - # 1 byte for the root name, 10 for the standard RR fields - size = 11 - # This would be more efficient if options had a size() method, but we won't - # worry about that for now. We also don't worry if there is an existing padding - # option, as it is unlikely and probably harmless, as the worst case is that we - # may add another, and this seems to be legal. - for option in self.opt[0].options: - wire = option.to_wire() - # We add 4 here to account for the option type and length - size += len(wire) + 4 - if self.pad: - # Padding will be added, so again add the option type and length. - size += 4 - return size - - def _compute_tsig_reserve(self) -> int: - """Compute the size required for the TSIG RR""" - # This would be more efficient if TSIGs had a size method, but we won't - # worry about for now. Also, we can't really cope with the potential - # compressibility of the TSIG owner name, so we estimate with the uncompressed - # size. We will disable compression when TSIG and padding are both is active - # so that the padding comes out right. - if not self.tsig: - return 0 - f = io.BytesIO() - self.tsig.to_wire(f) - return len(f.getvalue()) - - def to_wire( - self, - origin: Optional[dns.name.Name] = None, - max_size: int = 0, - multi: bool = False, - tsig_ctx: Optional[Any] = None, - prepend_length: bool = False, - prefer_truncation: bool = False, - **kw: Dict[str, Any], - ) -> bytes: - """Return a string containing the message in DNS compressed wire - format. - - Additional keyword arguments are passed to the RRset ``to_wire()`` - method. - - *origin*, a ``dns.name.Name`` or ``None``, the origin to be appended - to any relative names. If ``None``, and the message has an origin - attribute that is not ``None``, then it will be used. - - *max_size*, an ``int``, the maximum size of the wire format - output; default is 0, which means "the message's request - payload, if nonzero, or 65535". - - *multi*, a ``bool``, should be set to ``True`` if this message is - part of a multiple message sequence. - - *tsig_ctx*, a ``dns.tsig.HMACTSig`` or ``dns.tsig.GSSTSig`` object, the - ongoing TSIG context, used when signing zone transfers. - - *prepend_length*, a ``bool``, should be set to ``True`` if the caller - wants the message length prepended to the message itself. This is - useful for messages sent over TCP, TLS (DoT), or QUIC (DoQ). - - *prefer_truncation*, a ``bool``, should be set to ``True`` if the caller - wants the message to be truncated if it would otherwise exceed the - maximum length. If the truncation occurs before the additional section, - the TC bit will be set. - - Raises ``dns.exception.TooBig`` if *max_size* was exceeded. - - Returns a ``bytes``. - """ - - if origin is None and self.origin is not None: - origin = self.origin - if max_size == 0: - if self.request_payload != 0: - max_size = self.request_payload - else: - max_size = 65535 - if max_size < 512: - max_size = 512 - elif max_size > 65535: - max_size = 65535 - r = dns.renderer.Renderer(self.id, self.flags, max_size, origin) - opt_reserve = self._compute_opt_reserve() - r.reserve(opt_reserve) - tsig_reserve = self._compute_tsig_reserve() - r.reserve(tsig_reserve) - try: - for rrset in self.question: - r.add_question(rrset.name, rrset.rdtype, rrset.rdclass) - for rrset in self.answer: - r.add_rrset(dns.renderer.ANSWER, rrset, **kw) - for rrset in self.authority: - r.add_rrset(dns.renderer.AUTHORITY, rrset, **kw) - for rrset in self.additional: - r.add_rrset(dns.renderer.ADDITIONAL, rrset, **kw) - except dns.exception.TooBig: - if prefer_truncation: - if r.section < dns.renderer.ADDITIONAL: - r.flags |= dns.flags.TC - else: - raise - r.release_reserved() - if self.opt is not None: - r.add_opt(self.opt, self.pad, opt_reserve, tsig_reserve) - r.write_header() - if self.tsig is not None: - (new_tsig, ctx) = dns.tsig.sign( - r.get_wire(), - self.keyring, - self.tsig[0], - int(time.time()), - self.request_mac, - tsig_ctx, - multi, - ) - self.tsig.clear() - self.tsig.add(new_tsig) - r.add_rrset(dns.renderer.ADDITIONAL, self.tsig) - r.write_header() - if multi: - self.tsig_ctx = ctx - wire = r.get_wire() - self.wire = wire - if prepend_length: - wire = len(wire).to_bytes(2, "big") + wire - return wire - - @staticmethod - def _make_tsig( - keyname, algorithm, time_signed, fudge, mac, original_id, error, other - ): - tsig = dns.rdtypes.ANY.TSIG.TSIG( - dns.rdataclass.ANY, - dns.rdatatype.TSIG, - algorithm, - time_signed, - fudge, - mac, - original_id, - error, - other, - ) - return dns.rrset.from_rdata(keyname, 0, tsig) - - def use_tsig( - self, - keyring: Any, - keyname: Optional[Union[dns.name.Name, str]] = None, - fudge: int = 300, - original_id: Optional[int] = None, - tsig_error: int = 0, - other_data: bytes = b"", - algorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, - ) -> None: - """When sending, a TSIG signature using the specified key - should be added. - - *key*, a ``dns.tsig.Key`` is the key to use. If a key is specified, - the *keyring* and *algorithm* fields are not used. - - *keyring*, a ``dict``, ``callable`` or ``dns.tsig.Key``, is either - the TSIG keyring or key to use. - - The format of a keyring dict is a mapping from TSIG key name, as - ``dns.name.Name`` to ``dns.tsig.Key`` or a TSIG secret, a ``bytes``. - If a ``dict`` *keyring* is specified but a *keyname* is not, the key - used will be the first key in the *keyring*. Note that the order of - keys in a dictionary is not defined, so applications should supply a - keyname when a ``dict`` keyring is used, unless they know the keyring - contains only one key. If a ``callable`` keyring is specified, the - callable will be called with the message and the keyname, and is - expected to return a key. - - *keyname*, a ``dns.name.Name``, ``str`` or ``None``, the name of - this TSIG key to use; defaults to ``None``. If *keyring* is a - ``dict``, the key must be defined in it. If *keyring* is a - ``dns.tsig.Key``, this is ignored. - - *fudge*, an ``int``, the TSIG time fudge. - - *original_id*, an ``int``, the TSIG original id. If ``None``, - the message's id is used. - - *tsig_error*, an ``int``, the TSIG error code. - - *other_data*, a ``bytes``, the TSIG other data. - - *algorithm*, a ``dns.name.Name`` or ``str``, the TSIG algorithm to use. This is - only used if *keyring* is a ``dict``, and the key entry is a ``bytes``. - """ - - if isinstance(keyring, dns.tsig.Key): - key = keyring - keyname = key.name - elif callable(keyring): - key = keyring(self, keyname) - else: - if isinstance(keyname, str): - keyname = dns.name.from_text(keyname) - if keyname is None: - keyname = next(iter(keyring)) - key = keyring[keyname] - if isinstance(key, bytes): - key = dns.tsig.Key(keyname, key, algorithm) - self.keyring = key - if original_id is None: - original_id = self.id - self.tsig = self._make_tsig( - keyname, - self.keyring.algorithm, - 0, - fudge, - b"\x00" * dns.tsig.mac_sizes[self.keyring.algorithm], - original_id, - tsig_error, - other_data, - ) - - @property - def keyname(self) -> Optional[dns.name.Name]: - if self.tsig: - return self.tsig.name - else: - return None - - @property - def keyalgorithm(self) -> Optional[dns.name.Name]: - if self.tsig: - return self.tsig[0].algorithm - else: - return None - - @property - def mac(self) -> Optional[bytes]: - if self.tsig: - return self.tsig[0].mac - else: - return None - - @property - def tsig_error(self) -> Optional[int]: - if self.tsig: - return self.tsig[0].error - else: - return None - - @property - def had_tsig(self) -> bool: - return bool(self.tsig) - - @staticmethod - def _make_opt(flags=0, payload=DEFAULT_EDNS_PAYLOAD, options=None): - opt = dns.rdtypes.ANY.OPT.OPT(payload, dns.rdatatype.OPT, options or ()) - return dns.rrset.from_rdata(dns.name.root, int(flags), opt) - - def use_edns( - self, - edns: Optional[Union[int, bool]] = 0, - ednsflags: int = 0, - payload: int = DEFAULT_EDNS_PAYLOAD, - request_payload: Optional[int] = None, - options: Optional[List[dns.edns.Option]] = None, - pad: int = 0, - ) -> None: - """Configure EDNS behavior. - - *edns*, an ``int``, is the EDNS level to use. Specifying ``None``, ``False``, - or ``-1`` means "do not use EDNS", and in this case the other parameters are - ignored. Specifying ``True`` is equivalent to specifying 0, i.e. "use EDNS0". - - *ednsflags*, an ``int``, the EDNS flag values. - - *payload*, an ``int``, is the EDNS sender's payload field, which is the maximum - size of UDP datagram the sender can handle. I.e. how big a response to this - message can be. - - *request_payload*, an ``int``, is the EDNS payload size to use when sending this - message. If not specified, defaults to the value of *payload*. - - *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS options. - - *pad*, a non-negative ``int``. If 0, the default, do not pad; otherwise add - padding bytes to make the message size a multiple of *pad*. Note that if - padding is non-zero, an EDNS PADDING option will always be added to the - message. - """ - - if edns is None or edns is False: - edns = -1 - elif edns is True: - edns = 0 - if edns < 0: - self.opt = None - self.request_payload = 0 - else: - # make sure the EDNS version in ednsflags agrees with edns - ednsflags &= 0xFF00FFFF - ednsflags |= edns << 16 - if options is None: - options = [] - self.opt = self._make_opt(ednsflags, payload, options) - if request_payload is None: - request_payload = payload - self.request_payload = request_payload - if pad < 0: - raise ValueError("pad must be non-negative") - self.pad = pad - - @property - def edns(self) -> int: - if self.opt: - return (self.ednsflags & 0xFF0000) >> 16 - else: - return -1 - - @property - def ednsflags(self) -> int: - if self.opt: - return self.opt.ttl - else: - return 0 - - @ednsflags.setter - def ednsflags(self, v): - if self.opt: - self.opt.ttl = v - elif v: - self.opt = self._make_opt(v) - - @property - def payload(self) -> int: - if self.opt: - return self.opt[0].payload - else: - return 0 - - @property - def options(self) -> Tuple: - if self.opt: - return self.opt[0].options - else: - return () - - def want_dnssec(self, wanted: bool = True) -> None: - """Enable or disable 'DNSSEC desired' flag in requests. - - *wanted*, a ``bool``. If ``True``, then DNSSEC data is - desired in the response, EDNS is enabled if required, and then - the DO bit is set. If ``False``, the DO bit is cleared if - EDNS is enabled. - """ - - if wanted: - self.ednsflags |= dns.flags.DO - elif self.opt: - self.ednsflags &= ~int(dns.flags.DO) - - def rcode(self) -> dns.rcode.Rcode: - """Return the rcode. - - Returns a ``dns.rcode.Rcode``. - """ - return dns.rcode.from_flags(int(self.flags), int(self.ednsflags)) - - def set_rcode(self, rcode: dns.rcode.Rcode) -> None: - """Set the rcode. - - *rcode*, a ``dns.rcode.Rcode``, is the rcode to set. - """ - (value, evalue) = dns.rcode.to_flags(rcode) - self.flags &= 0xFFF0 - self.flags |= value - self.ednsflags &= 0x00FFFFFF - self.ednsflags |= evalue - - def opcode(self) -> dns.opcode.Opcode: - """Return the opcode. - - Returns a ``dns.opcode.Opcode``. - """ - return dns.opcode.from_flags(int(self.flags)) - - def set_opcode(self, opcode: dns.opcode.Opcode) -> None: - """Set the opcode. - - *opcode*, a ``dns.opcode.Opcode``, is the opcode to set. - """ - self.flags &= 0x87FF - self.flags |= dns.opcode.to_flags(opcode) - - def get_options(self, otype: dns.edns.OptionType) -> List[dns.edns.Option]: - """Return the list of options of the specified type.""" - return [option for option in self.options if option.otype == otype] - - def extended_errors(self) -> List[dns.edns.EDEOption]: - """Return the list of Extended DNS Error (EDE) options in the message""" - return cast(List[dns.edns.EDEOption], self.get_options(dns.edns.OptionType.EDE)) - - def _get_one_rr_per_rrset(self, value): - # What the caller picked is fine. - return value - - # pylint: disable=unused-argument - - def _parse_rr_header(self, section, name, rdclass, rdtype): - return (rdclass, rdtype, None, False) - - # pylint: enable=unused-argument - - def _parse_special_rr_header(self, section, count, position, name, rdclass, rdtype): - if rdtype == dns.rdatatype.OPT: - if ( - section != MessageSection.ADDITIONAL - or self.opt - or name != dns.name.root - ): - raise BadEDNS - elif rdtype == dns.rdatatype.TSIG: - if ( - section != MessageSection.ADDITIONAL - or rdclass != dns.rdatatype.ANY - or position != count - 1 - ): - raise BadTSIG - return (rdclass, rdtype, None, False) - - -class ChainingResult: - """The result of a call to dns.message.QueryMessage.resolve_chaining(). - - The ``answer`` attribute is the answer RRSet, or ``None`` if it doesn't - exist. - - The ``canonical_name`` attribute is the canonical name after all - chaining has been applied (this is the same name as ``rrset.name`` in cases - where rrset is not ``None``). - - The ``minimum_ttl`` attribute is the minimum TTL, i.e. the TTL to - use if caching the data. It is the smallest of all the CNAME TTLs - and either the answer TTL if it exists or the SOA TTL and SOA - minimum values for negative answers. - - The ``cnames`` attribute is a list of all the CNAME RRSets followed to - get to the canonical name. - """ - - def __init__( - self, - canonical_name: dns.name.Name, - answer: Optional[dns.rrset.RRset], - minimum_ttl: int, - cnames: List[dns.rrset.RRset], - ): - self.canonical_name = canonical_name - self.answer = answer - self.minimum_ttl = minimum_ttl - self.cnames = cnames - - -class QueryMessage(Message): - def resolve_chaining(self) -> ChainingResult: - """Follow the CNAME chain in the response to determine the answer - RRset. - - Raises ``dns.message.NotQueryResponse`` if the message is not - a response. - - Raises ``dns.message.ChainTooLong`` if the CNAME chain is too long. - - Raises ``dns.message.AnswerForNXDOMAIN`` if the rcode is NXDOMAIN - but an answer was found. - - Raises ``dns.exception.FormError`` if the question count is not 1. - - Returns a ChainingResult object. - """ - if self.flags & dns.flags.QR == 0: - raise NotQueryResponse - if len(self.question) != 1: - raise dns.exception.FormError - question = self.question[0] - qname = question.name - min_ttl = dns.ttl.MAX_TTL - answer = None - count = 0 - cnames = [] - while count < MAX_CHAIN: - try: - answer = self.find_rrset( - self.answer, qname, question.rdclass, question.rdtype - ) - min_ttl = min(min_ttl, answer.ttl) - break - except KeyError: - if question.rdtype != dns.rdatatype.CNAME: - try: - crrset = self.find_rrset( - self.answer, qname, question.rdclass, dns.rdatatype.CNAME - ) - cnames.append(crrset) - min_ttl = min(min_ttl, crrset.ttl) - for rd in crrset: - qname = rd.target - break - count += 1 - continue - except KeyError: - # Exit the chaining loop - break - else: - # Exit the chaining loop - break - if count >= MAX_CHAIN: - raise ChainTooLong - if self.rcode() == dns.rcode.NXDOMAIN and answer is not None: - raise AnswerForNXDOMAIN - if answer is None: - # Further minimize the TTL with NCACHE. - auname = qname - while True: - # Look for an SOA RR whose owner name is a superdomain - # of qname. - try: - srrset = self.find_rrset( - self.authority, auname, question.rdclass, dns.rdatatype.SOA - ) - min_ttl = min(min_ttl, srrset.ttl, srrset[0].minimum) - break - except KeyError: - try: - auname = auname.parent() - except dns.name.NoParent: - break - return ChainingResult(qname, answer, min_ttl, cnames) - - def canonical_name(self) -> dns.name.Name: - """Return the canonical name of the first name in the question - section. - - Raises ``dns.message.NotQueryResponse`` if the message is not - a response. - - Raises ``dns.message.ChainTooLong`` if the CNAME chain is too long. - - Raises ``dns.message.AnswerForNXDOMAIN`` if the rcode is NXDOMAIN - but an answer was found. - - Raises ``dns.exception.FormError`` if the question count is not 1. - """ - return self.resolve_chaining().canonical_name - - -def _maybe_import_update(): - # We avoid circular imports by doing this here. We do it in another - # function as doing it in _message_factory_from_opcode() makes "dns" - # a local symbol, and the first line fails :) - - # pylint: disable=redefined-outer-name,import-outside-toplevel,unused-import - import dns.update # noqa: F401 - - -def _message_factory_from_opcode(opcode): - if opcode == dns.opcode.QUERY: - return QueryMessage - elif opcode == dns.opcode.UPDATE: - _maybe_import_update() - return dns.update.UpdateMessage - else: - return Message - - -class _WireReader: - """Wire format reader. - - parser: the binary parser - message: The message object being built - initialize_message: Callback to set message parsing options - question_only: Are we only reading the question? - one_rr_per_rrset: Put each RR into its own RRset? - keyring: TSIG keyring - ignore_trailing: Ignore trailing junk at end of request? - multi: Is this message part of a multi-message sequence? - DNS dynamic updates. - continue_on_error: try to extract as much information as possible from - the message, accumulating MessageErrors in the *errors* attribute instead of - raising them. - """ - - def __init__( - self, - wire, - initialize_message, - question_only=False, - one_rr_per_rrset=False, - ignore_trailing=False, - keyring=None, - multi=False, - continue_on_error=False, - ): - self.parser = dns.wire.Parser(wire) - self.message = None - self.initialize_message = initialize_message - self.question_only = question_only - self.one_rr_per_rrset = one_rr_per_rrset - self.ignore_trailing = ignore_trailing - self.keyring = keyring - self.multi = multi - self.continue_on_error = continue_on_error - self.errors = [] - - def _get_question(self, section_number, qcount): - """Read the next *qcount* records from the wire data and add them to - the question section. - """ - assert self.message is not None - section = self.message.sections[section_number] - for _ in range(qcount): - qname = self.parser.get_name(self.message.origin) - (rdtype, rdclass) = self.parser.get_struct("!HH") - (rdclass, rdtype, _, _) = self.message._parse_rr_header( - section_number, qname, rdclass, rdtype - ) - self.message.find_rrset( - section, qname, rdclass, rdtype, create=True, force_unique=True - ) - - def _add_error(self, e): - self.errors.append(MessageError(e, self.parser.current)) - - def _get_section(self, section_number, count): - """Read the next I{count} records from the wire data and add them to - the specified section. - - section_number: the section of the message to which to add records - count: the number of records to read - """ - assert self.message is not None - section = self.message.sections[section_number] - force_unique = self.one_rr_per_rrset - for i in range(count): - rr_start = self.parser.current - absolute_name = self.parser.get_name() - if self.message.origin is not None: - name = absolute_name.relativize(self.message.origin) - else: - name = absolute_name - (rdtype, rdclass, ttl, rdlen) = self.parser.get_struct("!HHIH") - if rdtype in (dns.rdatatype.OPT, dns.rdatatype.TSIG): - ( - rdclass, - rdtype, - deleting, - empty, - ) = self.message._parse_special_rr_header( - section_number, count, i, name, rdclass, rdtype - ) - else: - (rdclass, rdtype, deleting, empty) = self.message._parse_rr_header( - section_number, name, rdclass, rdtype - ) - rdata_start = self.parser.current - try: - if empty: - if rdlen > 0: - raise dns.exception.FormError - rd = None - covers = dns.rdatatype.NONE - else: - with self.parser.restrict_to(rdlen): - rd = dns.rdata.from_wire_parser( - rdclass, rdtype, self.parser, self.message.origin - ) - covers = rd.covers() - if self.message.xfr and rdtype == dns.rdatatype.SOA: - force_unique = True - if rdtype == dns.rdatatype.OPT: - self.message.opt = dns.rrset.from_rdata(name, ttl, rd) - elif rdtype == dns.rdatatype.TSIG: - if self.keyring is None or self.keyring is True: - raise UnknownTSIGKey("got signed message without keyring") - elif isinstance(self.keyring, dict): - key = self.keyring.get(absolute_name) - if isinstance(key, bytes): - key = dns.tsig.Key(absolute_name, key, rd.algorithm) - elif callable(self.keyring): - key = self.keyring(self.message, absolute_name) - else: - key = self.keyring - if key is None: - raise UnknownTSIGKey(f"key '{name}' unknown") - if key: - self.message.keyring = key - self.message.tsig_ctx = dns.tsig.validate( - self.parser.wire, - key, - absolute_name, - rd, - int(time.time()), - self.message.request_mac, - rr_start, - self.message.tsig_ctx, - self.multi, - ) - self.message.tsig = dns.rrset.from_rdata(absolute_name, 0, rd) - else: - rrset = self.message.find_rrset( - section, - name, - rdclass, - rdtype, - covers, - deleting, - True, - force_unique, - ) - if rd is not None: - if ttl > 0x7FFFFFFF: - ttl = 0 - rrset.add(rd, ttl) - except Exception as e: - if self.continue_on_error: - self._add_error(e) - self.parser.seek(rdata_start + rdlen) - else: - raise - - def read(self): - """Read a wire format DNS message and build a dns.message.Message - object.""" - - if self.parser.remaining() < 12: - raise ShortHeader - (id, flags, qcount, ancount, aucount, adcount) = self.parser.get_struct( - "!HHHHHH" - ) - factory = _message_factory_from_opcode(dns.opcode.from_flags(flags)) - self.message = factory(id=id) - self.message.flags = dns.flags.Flag(flags) - self.message.wire = self.parser.wire - self.initialize_message(self.message) - self.one_rr_per_rrset = self.message._get_one_rr_per_rrset( - self.one_rr_per_rrset - ) - try: - self._get_question(MessageSection.QUESTION, qcount) - if self.question_only: - return self.message - self._get_section(MessageSection.ANSWER, ancount) - self._get_section(MessageSection.AUTHORITY, aucount) - self._get_section(MessageSection.ADDITIONAL, adcount) - if not self.ignore_trailing and self.parser.remaining() != 0: - raise TrailingJunk - if self.multi and self.message.tsig_ctx and not self.message.had_tsig: - self.message.tsig_ctx.update(self.parser.wire) - except Exception as e: - if self.continue_on_error: - self._add_error(e) - else: - raise - return self.message - - -def from_wire( - wire: bytes, - keyring: Optional[Any] = None, - request_mac: Optional[bytes] = b"", - xfr: bool = False, - origin: Optional[dns.name.Name] = None, - tsig_ctx: Optional[Union[dns.tsig.HMACTSig, dns.tsig.GSSTSig]] = None, - multi: bool = False, - question_only: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - raise_on_truncation: bool = False, - continue_on_error: bool = False, -) -> Message: - """Convert a DNS wire format message into a message object. - - *keyring*, a ``dns.tsig.Key``, ``dict``, ``bool``, or ``None``, the key or keyring - to use if the message is signed. If ``None`` or ``True``, then trying to decode - a message with a TSIG will fail as it cannot be validated. If ``False``, then - TSIG validation is disabled. - - *request_mac*, a ``bytes`` or ``None``. If the message is a response to a - TSIG-signed request, *request_mac* should be set to the MAC of that request. - - *xfr*, a ``bool``, should be set to ``True`` if this message is part of a zone - transfer. - - *origin*, a ``dns.name.Name`` or ``None``. If the message is part of a zone - transfer, *origin* should be the origin name of the zone. If not ``None``, names - will be relativized to the origin. - - *tsig_ctx*, a ``dns.tsig.HMACTSig`` or ``dns.tsig.GSSTSig`` object, the ongoing TSIG - context, used when validating zone transfers. - - *multi*, a ``bool``, should be set to ``True`` if this message is part of a multiple - message sequence. - - *question_only*, a ``bool``. If ``True``, read only up to the end of the question - section. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the - message. - - *raise_on_truncation*, a ``bool``. If ``True``, raise an exception if the TC bit is - set. - - *continue_on_error*, a ``bool``. If ``True``, try to continue parsing even if - errors occur. Erroneous rdata will be ignored. Errors will be accumulated as a - list of MessageError objects in the message's ``errors`` attribute. This option is - recommended only for DNS analysis tools, or for use in a server as part of an error - handling path. The default is ``False``. - - Raises ``dns.message.ShortHeader`` if the message is less than 12 octets long. - - Raises ``dns.message.TrailingJunk`` if there were octets in the message past the end - of the proper DNS message, and *ignore_trailing* is ``False``. - - Raises ``dns.message.BadEDNS`` if an OPT record was in the wrong section, or - occurred more than once. - - Raises ``dns.message.BadTSIG`` if a TSIG record was not the last record of the - additional data section. - - Raises ``dns.message.Truncated`` if the TC flag is set and *raise_on_truncation* is - ``True``. - - Returns a ``dns.message.Message``. - """ - - # We permit None for request_mac solely for backwards compatibility - if request_mac is None: - request_mac = b"" - - def initialize_message(message): - message.request_mac = request_mac - message.xfr = xfr - message.origin = origin - message.tsig_ctx = tsig_ctx - - reader = _WireReader( - wire, - initialize_message, - question_only, - one_rr_per_rrset, - ignore_trailing, - keyring, - multi, - continue_on_error, - ) - try: - m = reader.read() - except dns.exception.FormError: - if ( - reader.message - and (reader.message.flags & dns.flags.TC) - and raise_on_truncation - ): - raise Truncated(message=reader.message) - else: - raise - # Reading a truncated message might not have any errors, so we - # have to do this check here too. - if m.flags & dns.flags.TC and raise_on_truncation: - raise Truncated(message=m) - if continue_on_error: - m.errors = reader.errors - - return m - - -class _TextReader: - """Text format reader. - - tok: the tokenizer. - message: The message object being built. - DNS dynamic updates. - last_name: The most recently read name when building a message object. - one_rr_per_rrset: Put each RR into its own RRset? - origin: The origin for relative names - relativize: relativize names? - relativize_to: the origin to relativize to. - """ - - def __init__( - self, - text, - idna_codec, - one_rr_per_rrset=False, - origin=None, - relativize=True, - relativize_to=None, - ): - self.message = None - self.tok = dns.tokenizer.Tokenizer(text, idna_codec=idna_codec) - self.last_name = None - self.one_rr_per_rrset = one_rr_per_rrset - self.origin = origin - self.relativize = relativize - self.relativize_to = relativize_to - self.id = None - self.edns = -1 - self.ednsflags = 0 - self.payload = DEFAULT_EDNS_PAYLOAD - self.rcode = None - self.opcode = dns.opcode.QUERY - self.flags = 0 - - def _header_line(self, _): - """Process one line from the text format header section.""" - - token = self.tok.get() - what = token.value - if what == "id": - self.id = self.tok.get_int() - elif what == "flags": - while True: - token = self.tok.get() - if not token.is_identifier(): - self.tok.unget(token) - break - self.flags = self.flags | dns.flags.from_text(token.value) - elif what == "edns": - self.edns = self.tok.get_int() - self.ednsflags = self.ednsflags | (self.edns << 16) - elif what == "eflags": - if self.edns < 0: - self.edns = 0 - while True: - token = self.tok.get() - if not token.is_identifier(): - self.tok.unget(token) - break - self.ednsflags = self.ednsflags | dns.flags.edns_from_text(token.value) - elif what == "payload": - self.payload = self.tok.get_int() - if self.edns < 0: - self.edns = 0 - elif what == "opcode": - text = self.tok.get_string() - self.opcode = dns.opcode.from_text(text) - self.flags = self.flags | dns.opcode.to_flags(self.opcode) - elif what == "rcode": - text = self.tok.get_string() - self.rcode = dns.rcode.from_text(text) - else: - raise UnknownHeaderField - self.tok.get_eol() - - def _question_line(self, section_number): - """Process one line from the text format question section.""" - - section = self.message.sections[section_number] - token = self.tok.get(want_leading=True) - if not token.is_whitespace(): - self.last_name = self.tok.as_name( - token, self.message.origin, self.relativize, self.relativize_to - ) - name = self.last_name - if name is None: - raise NoPreviousName - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - # Class - try: - rdclass = dns.rdataclass.from_text(token.value) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except dns.exception.SyntaxError: - raise dns.exception.SyntaxError - except Exception: - rdclass = dns.rdataclass.IN - # Type - rdtype = dns.rdatatype.from_text(token.value) - (rdclass, rdtype, _, _) = self.message._parse_rr_header( - section_number, name, rdclass, rdtype - ) - self.message.find_rrset( - section, name, rdclass, rdtype, create=True, force_unique=True - ) - self.tok.get_eol() - - def _rr_line(self, section_number): - """Process one line from the text format answer, authority, or - additional data sections. - """ - - section = self.message.sections[section_number] - # Name - token = self.tok.get(want_leading=True) - if not token.is_whitespace(): - self.last_name = self.tok.as_name( - token, self.message.origin, self.relativize, self.relativize_to - ) - name = self.last_name - if name is None: - raise NoPreviousName - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - # TTL - try: - ttl = int(token.value, 0) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except dns.exception.SyntaxError: - raise dns.exception.SyntaxError - except Exception: - ttl = 0 - # Class - try: - rdclass = dns.rdataclass.from_text(token.value) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except dns.exception.SyntaxError: - raise dns.exception.SyntaxError - except Exception: - rdclass = dns.rdataclass.IN - # Type - rdtype = dns.rdatatype.from_text(token.value) - (rdclass, rdtype, deleting, empty) = self.message._parse_rr_header( - section_number, name, rdclass, rdtype - ) - token = self.tok.get() - if empty and not token.is_eol_or_eof(): - raise dns.exception.SyntaxError - if not empty and token.is_eol_or_eof(): - raise dns.exception.UnexpectedEnd - if not token.is_eol_or_eof(): - self.tok.unget(token) - rd = dns.rdata.from_text( - rdclass, - rdtype, - self.tok, - self.message.origin, - self.relativize, - self.relativize_to, - ) - covers = rd.covers() - else: - rd = None - covers = dns.rdatatype.NONE - rrset = self.message.find_rrset( - section, - name, - rdclass, - rdtype, - covers, - deleting, - True, - self.one_rr_per_rrset, - ) - if rd is not None: - rrset.add(rd, ttl) - - def _make_message(self): - factory = _message_factory_from_opcode(self.opcode) - message = factory(id=self.id) - message.flags = self.flags - if self.edns >= 0: - message.use_edns(self.edns, self.ednsflags, self.payload) - if self.rcode: - message.set_rcode(self.rcode) - if self.origin: - message.origin = self.origin - return message - - def read(self): - """Read a text format DNS message and build a dns.message.Message - object.""" - - line_method = self._header_line - section_number = None - while 1: - token = self.tok.get(True, True) - if token.is_eol_or_eof(): - break - if token.is_comment(): - u = token.value.upper() - if u == "HEADER": - line_method = self._header_line - - if self.message: - message = self.message - else: - # If we don't have a message, create one with the current - # opcode, so that we know which section names to parse. - message = self._make_message() - try: - section_number = message._section_enum.from_text(u) - # We found a section name. If we don't have a message, - # use the one we just created. - if not self.message: - self.message = message - self.one_rr_per_rrset = message._get_one_rr_per_rrset( - self.one_rr_per_rrset - ) - if section_number == MessageSection.QUESTION: - line_method = self._question_line - else: - line_method = self._rr_line - except Exception: - # It's just a comment. - pass - self.tok.get_eol() - continue - self.tok.unget(token) - line_method(section_number) - if not self.message: - self.message = self._make_message() - return self.message - - -def from_text( - text: str, - idna_codec: Optional[dns.name.IDNACodec] = None, - one_rr_per_rrset: bool = False, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - relativize_to: Optional[dns.name.Name] = None, -) -> Message: - """Convert the text format message into a message object. - - The reader stops after reading the first blank line in the input to - facilitate reading multiple messages from a single file with - ``dns.message.from_file()``. - - *text*, a ``str``, the text format message. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - *one_rr_per_rrset*, a ``bool``. If ``True``, then each RR is put - into its own rrset. The default is ``False``. - - *origin*, a ``dns.name.Name`` (or ``None``), the - origin to use for relative names. - - *relativize*, a ``bool``. If true, name will be relativized. - - *relativize_to*, a ``dns.name.Name`` (or ``None``), the origin to use - when relativizing names. If not set, the *origin* value will be used. - - Raises ``dns.message.UnknownHeaderField`` if a header is unknown. - - Raises ``dns.exception.SyntaxError`` if the text is badly formed. - - Returns a ``dns.message.Message object`` - """ - - # 'text' can also be a file, but we don't publish that fact - # since it's an implementation detail. The official file - # interface is from_file(). - - reader = _TextReader( - text, idna_codec, one_rr_per_rrset, origin, relativize, relativize_to - ) - return reader.read() - - -def from_file( - f: Any, - idna_codec: Optional[dns.name.IDNACodec] = None, - one_rr_per_rrset: bool = False, -) -> Message: - """Read the next text format message from the specified file. - - Message blocks are separated by a single blank line. - - *f*, a ``file`` or ``str``. If *f* is text, it is treated as the - pathname of a file to open. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - *one_rr_per_rrset*, a ``bool``. If ``True``, then each RR is put - into its own rrset. The default is ``False``. - - Raises ``dns.message.UnknownHeaderField`` if a header is unknown. - - Raises ``dns.exception.SyntaxError`` if the text is badly formed. - - Returns a ``dns.message.Message object`` - """ - - if isinstance(f, str): - cm: contextlib.AbstractContextManager = open(f) - else: - cm = contextlib.nullcontext(f) - with cm as f: - return from_text(f, idna_codec, one_rr_per_rrset) - assert False # for mypy lgtm[py/unreachable-statement] - - -def make_query( - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - use_edns: Optional[Union[int, bool]] = None, - want_dnssec: bool = False, - ednsflags: Optional[int] = None, - payload: Optional[int] = None, - request_payload: Optional[int] = None, - options: Optional[List[dns.edns.Option]] = None, - idna_codec: Optional[dns.name.IDNACodec] = None, - id: Optional[int] = None, - flags: int = dns.flags.RD, - pad: int = 0, -) -> QueryMessage: - """Make a query message. - - The query name, type, and class may all be specified either - as objects of the appropriate type, or as strings. - - The query will have a randomly chosen query id, and its DNS flags - will be set to dns.flags.RD. - - qname, a ``dns.name.Name`` or ``str``, the query name. - - *rdtype*, an ``int`` or ``str``, the desired rdata type. - - *rdclass*, an ``int`` or ``str``, the desired rdata class; the default - is class IN. - - *use_edns*, an ``int``, ``bool`` or ``None``. The EDNS level to use; the - default is ``None``. If ``None``, EDNS will be enabled only if other - parameters (*ednsflags*, *payload*, *request_payload*, or *options*) are - set. - See the description of dns.message.Message.use_edns() for the possible - values for use_edns and their meanings. - - *want_dnssec*, a ``bool``. If ``True``, DNSSEC data is desired. - - *ednsflags*, an ``int``, the EDNS flag values. - - *payload*, an ``int``, is the EDNS sender's payload field, which is the - maximum size of UDP datagram the sender can handle. I.e. how big - a response to this message can be. - - *request_payload*, an ``int``, is the EDNS payload size to use when - sending this message. If not specified, defaults to the value of - *payload*. - - *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS - options. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - *id*, an ``int`` or ``None``, the desired query id. The default is - ``None``, which generates a random query id. - - *flags*, an ``int``, the desired query flags. The default is - ``dns.flags.RD``. - - *pad*, a non-negative ``int``. If 0, the default, do not pad; otherwise add - padding bytes to make the message size a multiple of *pad*. Note that if - padding is non-zero, an EDNS PADDING option will always be added to the - message. - - Returns a ``dns.message.QueryMessage`` - """ - - if isinstance(qname, str): - qname = dns.name.from_text(qname, idna_codec=idna_codec) - rdtype = dns.rdatatype.RdataType.make(rdtype) - rdclass = dns.rdataclass.RdataClass.make(rdclass) - m = QueryMessage(id=id) - m.flags = dns.flags.Flag(flags) - m.find_rrset(m.question, qname, rdclass, rdtype, create=True, force_unique=True) - # only pass keywords on to use_edns if they have been set to a - # non-None value. Setting a field will turn EDNS on if it hasn't - # been configured. - kwargs: Dict[str, Any] = {} - if ednsflags is not None: - kwargs["ednsflags"] = ednsflags - if payload is not None: - kwargs["payload"] = payload - if request_payload is not None: - kwargs["request_payload"] = request_payload - if options is not None: - kwargs["options"] = options - if kwargs and use_edns is None: - use_edns = 0 - kwargs["edns"] = use_edns - kwargs["pad"] = pad - m.use_edns(**kwargs) - m.want_dnssec(want_dnssec) - return m - - -class CopyMode(enum.Enum): - """ - How should sections be copied when making an update response? - """ - - NOTHING = 0 - QUESTION = 1 - EVERYTHING = 2 - - -def make_response( - query: Message, - recursion_available: bool = False, - our_payload: int = 8192, - fudge: int = 300, - tsig_error: int = 0, - pad: Optional[int] = None, - copy_mode: Optional[CopyMode] = None, -) -> Message: - """Make a message which is a response for the specified query. - The message returned is really a response skeleton; it has all of the infrastructure - required of a response, but none of the content. - - Response section(s) which are copied are shallow copies of the matching section(s) - in the query, so the query's RRsets should not be changed. - - *query*, a ``dns.message.Message``, the query to respond to. - - *recursion_available*, a ``bool``, should RA be set in the response? - - *our_payload*, an ``int``, the payload size to advertise in EDNS responses. - - *fudge*, an ``int``, the TSIG time fudge. - - *tsig_error*, an ``int``, the TSIG error. - - *pad*, a non-negative ``int`` or ``None``. If 0, the default, do not pad; otherwise - if not ``None`` add padding bytes to make the message size a multiple of *pad*. Note - that if padding is non-zero, an EDNS PADDING option will always be added to the - message. If ``None``, add padding following RFC 8467, namely if the request is - padded, pad the response to 468 otherwise do not pad. - - *copy_mode*, a ``dns.message.CopyMode`` or ``None``, determines how sections are - copied. The default, ``None`` copies sections according to the default for the - message's opcode, which is currently ``dns.message.CopyMode.QUESTION`` for all - opcodes. ``dns.message.CopyMode.QUESTION`` copies only the question section. - ``dns.message.CopyMode.EVERYTHING`` copies all sections other than OPT or TSIG - records, which are created appropriately if needed. ``dns.message.CopyMode.NOTHING`` - copies no sections; note that this mode is for server testing purposes and is - otherwise not recommended for use. In particular, ``dns.message.is_response()`` - will be ``False`` if you create a response this way and the rcode is not - ``FORMERR``, ``SERVFAIL``, ``NOTIMP``, or ``REFUSED``. - - Returns a ``dns.message.Message`` object whose specific class is appropriate for the - query. For example, if query is a ``dns.update.UpdateMessage``, the response will - be one too. - """ - - if query.flags & dns.flags.QR: - raise dns.exception.FormError("specified query message is not a query") - opcode = query.opcode() - factory = _message_factory_from_opcode(opcode) - response = factory(id=query.id) - response.flags = dns.flags.QR | (query.flags & dns.flags.RD) - if recursion_available: - response.flags |= dns.flags.RA - response.set_opcode(opcode) - if copy_mode is None: - copy_mode = CopyMode.QUESTION - if copy_mode != CopyMode.NOTHING: - response.question = list(query.question) - if copy_mode == CopyMode.EVERYTHING: - response.answer = list(query.answer) - response.authority = list(query.authority) - response.additional = list(query.additional) - if query.edns >= 0: - if pad is None: - # Set response padding per RFC 8467 - pad = 0 - for option in query.options: - if option.otype == dns.edns.OptionType.PADDING: - pad = 468 - response.use_edns(0, 0, our_payload, query.payload, pad=pad) - if query.had_tsig: - response.use_tsig( - query.keyring, - query.keyname, - fudge, - None, - tsig_error, - b"", - query.keyalgorithm, - ) - response.request_mac = query.mac - return response - - -### BEGIN generated MessageSection constants - -QUESTION = MessageSection.QUESTION -ANSWER = MessageSection.ANSWER -AUTHORITY = MessageSection.AUTHORITY -ADDITIONAL = MessageSection.ADDITIONAL - -### END generated MessageSection constants diff --git a/venv/Lib/site-packages/dns/name.py b/venv/Lib/site-packages/dns/name.py deleted file mode 100644 index f79f0d0..0000000 --- a/venv/Lib/site-packages/dns/name.py +++ /dev/null @@ -1,1284 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Names. -""" - -import copy -import encodings.idna # type: ignore -import functools -import struct -from typing import Any, Callable, Dict, Iterable, Optional, Tuple, Union - -import dns._features -import dns.enum -import dns.exception -import dns.immutable -import dns.wire - -if dns._features.have("idna"): - import idna # type: ignore - - have_idna_2008 = True -else: # pragma: no cover - have_idna_2008 = False - -CompressType = Dict["Name", int] - - -class NameRelation(dns.enum.IntEnum): - """Name relation result from fullcompare().""" - - # This is an IntEnum for backwards compatibility in case anyone - # has hardwired the constants. - - #: The compared names have no relationship to each other. - NONE = 0 - #: the first name is a superdomain of the second. - SUPERDOMAIN = 1 - #: The first name is a subdomain of the second. - SUBDOMAIN = 2 - #: The compared names are equal. - EQUAL = 3 - #: The compared names have a common ancestor. - COMMONANCESTOR = 4 - - @classmethod - def _maximum(cls): - return cls.COMMONANCESTOR # pragma: no cover - - @classmethod - def _short_name(cls): - return cls.__name__ # pragma: no cover - - -# Backwards compatibility -NAMERELN_NONE = NameRelation.NONE -NAMERELN_SUPERDOMAIN = NameRelation.SUPERDOMAIN -NAMERELN_SUBDOMAIN = NameRelation.SUBDOMAIN -NAMERELN_EQUAL = NameRelation.EQUAL -NAMERELN_COMMONANCESTOR = NameRelation.COMMONANCESTOR - - -class EmptyLabel(dns.exception.SyntaxError): - """A DNS label is empty.""" - - -class BadEscape(dns.exception.SyntaxError): - """An escaped code in a text format of DNS name is invalid.""" - - -class BadPointer(dns.exception.FormError): - """A DNS compression pointer points forward instead of backward.""" - - -class BadLabelType(dns.exception.FormError): - """The label type in DNS name wire format is unknown.""" - - -class NeedAbsoluteNameOrOrigin(dns.exception.DNSException): - """An attempt was made to convert a non-absolute name to - wire when there was also a non-absolute (or missing) origin.""" - - -class NameTooLong(dns.exception.FormError): - """A DNS name is > 255 octets long.""" - - -class LabelTooLong(dns.exception.SyntaxError): - """A DNS label is > 63 octets long.""" - - -class AbsoluteConcatenation(dns.exception.DNSException): - """An attempt was made to append anything other than the - empty name to an absolute DNS name.""" - - -class NoParent(dns.exception.DNSException): - """An attempt was made to get the parent of the root name - or the empty name.""" - - -class NoIDNA2008(dns.exception.DNSException): - """IDNA 2008 processing was requested but the idna module is not - available.""" - - -class IDNAException(dns.exception.DNSException): - """IDNA processing raised an exception.""" - - supp_kwargs = {"idna_exception"} - fmt = "IDNA processing exception: {idna_exception}" - - # We do this as otherwise mypy complains about unexpected keyword argument - # idna_exception - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - -class NeedSubdomainOfOrigin(dns.exception.DNSException): - """An absolute name was provided that is not a subdomain of the specified origin.""" - - -_escaped = b'"().;\\@$' -_escaped_text = '"().;\\@$' - - -def _escapify(label: Union[bytes, str]) -> str: - """Escape the characters in label which need it. - @returns: the escaped string - @rtype: string""" - if isinstance(label, bytes): - # Ordinary DNS label mode. Escape special characters and values - # < 0x20 or > 0x7f. - text = "" - for c in label: - if c in _escaped: - text += "\\" + chr(c) - elif c > 0x20 and c < 0x7F: - text += chr(c) - else: - text += "\\%03d" % c - return text - - # Unicode label mode. Escape only special characters and values < 0x20 - text = "" - for uc in label: - if uc in _escaped_text: - text += "\\" + uc - elif uc <= "\x20": - text += "\\%03d" % ord(uc) - else: - text += uc - return text - - -class IDNACodec: - """Abstract base class for IDNA encoder/decoders.""" - - def __init__(self): - pass - - def is_idna(self, label: bytes) -> bool: - return label.lower().startswith(b"xn--") - - def encode(self, label: str) -> bytes: - raise NotImplementedError # pragma: no cover - - def decode(self, label: bytes) -> str: - # We do not apply any IDNA policy on decode. - if self.is_idna(label): - try: - slabel = label[4:].decode("punycode") - return _escapify(slabel) - except Exception as e: - raise IDNAException(idna_exception=e) - else: - return _escapify(label) - - -class IDNA2003Codec(IDNACodec): - """IDNA 2003 encoder/decoder.""" - - def __init__(self, strict_decode: bool = False): - """Initialize the IDNA 2003 encoder/decoder. - - *strict_decode* is a ``bool``. If `True`, then IDNA2003 checking - is done when decoding. This can cause failures if the name - was encoded with IDNA2008. The default is `False`. - """ - - super().__init__() - self.strict_decode = strict_decode - - def encode(self, label: str) -> bytes: - """Encode *label*.""" - - if label == "": - return b"" - try: - return encodings.idna.ToASCII(label) - except UnicodeError: - raise LabelTooLong - - def decode(self, label: bytes) -> str: - """Decode *label*.""" - if not self.strict_decode: - return super().decode(label) - if label == b"": - return "" - try: - return _escapify(encodings.idna.ToUnicode(label)) - except Exception as e: - raise IDNAException(idna_exception=e) - - -class IDNA2008Codec(IDNACodec): - """IDNA 2008 encoder/decoder.""" - - def __init__( - self, - uts_46: bool = False, - transitional: bool = False, - allow_pure_ascii: bool = False, - strict_decode: bool = False, - ): - """Initialize the IDNA 2008 encoder/decoder. - - *uts_46* is a ``bool``. If True, apply Unicode IDNA - compatibility processing as described in Unicode Technical - Standard #46 (https://unicode.org/reports/tr46/). - If False, do not apply the mapping. The default is False. - - *transitional* is a ``bool``: If True, use the - "transitional" mode described in Unicode Technical Standard - #46. The default is False. - - *allow_pure_ascii* is a ``bool``. If True, then a label which - consists of only ASCII characters is allowed. This is less - strict than regular IDNA 2008, but is also necessary for mixed - names, e.g. a name with starting with "_sip._tcp." and ending - in an IDN suffix which would otherwise be disallowed. The - default is False. - - *strict_decode* is a ``bool``: If True, then IDNA2008 checking - is done when decoding. This can cause failures if the name - was encoded with IDNA2003. The default is False. - """ - super().__init__() - self.uts_46 = uts_46 - self.transitional = transitional - self.allow_pure_ascii = allow_pure_ascii - self.strict_decode = strict_decode - - def encode(self, label: str) -> bytes: - if label == "": - return b"" - if self.allow_pure_ascii and is_all_ascii(label): - encoded = label.encode("ascii") - if len(encoded) > 63: - raise LabelTooLong - return encoded - if not have_idna_2008: - raise NoIDNA2008 - try: - if self.uts_46: - # pylint: disable=possibly-used-before-assignment - label = idna.uts46_remap(label, False, self.transitional) - return idna.alabel(label) - except idna.IDNAError as e: - if e.args[0] == "Label too long": - raise LabelTooLong - else: - raise IDNAException(idna_exception=e) - - def decode(self, label: bytes) -> str: - if not self.strict_decode: - return super().decode(label) - if label == b"": - return "" - if not have_idna_2008: - raise NoIDNA2008 - try: - ulabel = idna.ulabel(label) - if self.uts_46: - ulabel = idna.uts46_remap(ulabel, False, self.transitional) - return _escapify(ulabel) - except (idna.IDNAError, UnicodeError) as e: - raise IDNAException(idna_exception=e) - - -IDNA_2003_Practical = IDNA2003Codec(False) -IDNA_2003_Strict = IDNA2003Codec(True) -IDNA_2003 = IDNA_2003_Practical -IDNA_2008_Practical = IDNA2008Codec(True, False, True, False) -IDNA_2008_UTS_46 = IDNA2008Codec(True, False, False, False) -IDNA_2008_Strict = IDNA2008Codec(False, False, False, True) -IDNA_2008_Transitional = IDNA2008Codec(True, True, False, False) -IDNA_2008 = IDNA_2008_Practical - - -def _validate_labels(labels: Tuple[bytes, ...]) -> None: - """Check for empty labels in the middle of a label sequence, - labels that are too long, and for too many labels. - - Raises ``dns.name.NameTooLong`` if the name as a whole is too long. - - Raises ``dns.name.EmptyLabel`` if a label is empty (i.e. the root - label) and appears in a position other than the end of the label - sequence - - """ - - l = len(labels) - total = 0 - i = -1 - j = 0 - for label in labels: - ll = len(label) - total += ll + 1 - if ll > 63: - raise LabelTooLong - if i < 0 and label == b"": - i = j - j += 1 - if total > 255: - raise NameTooLong - if i >= 0 and i != l - 1: - raise EmptyLabel - - -def _maybe_convert_to_binary(label: Union[bytes, str]) -> bytes: - """If label is ``str``, convert it to ``bytes``. If it is already - ``bytes`` just return it. - - """ - - if isinstance(label, bytes): - return label - if isinstance(label, str): - return label.encode() - raise ValueError # pragma: no cover - - -@dns.immutable.immutable -class Name: - """A DNS name. - - The dns.name.Name class represents a DNS name as a tuple of - labels. Each label is a ``bytes`` in DNS wire format. Instances - of the class are immutable. - """ - - __slots__ = ["labels"] - - def __init__(self, labels: Iterable[Union[bytes, str]]): - """*labels* is any iterable whose values are ``str`` or ``bytes``.""" - - blabels = [_maybe_convert_to_binary(x) for x in labels] - self.labels = tuple(blabels) - _validate_labels(self.labels) - - def __copy__(self): - return Name(self.labels) - - def __deepcopy__(self, memo): - return Name(copy.deepcopy(self.labels, memo)) - - def __getstate__(self): - # Names can be pickled - return {"labels": self.labels} - - def __setstate__(self, state): - super().__setattr__("labels", state["labels"]) - _validate_labels(self.labels) - - def is_absolute(self) -> bool: - """Is the most significant label of this name the root label? - - Returns a ``bool``. - """ - - return len(self.labels) > 0 and self.labels[-1] == b"" - - def is_wild(self) -> bool: - """Is this name wild? (I.e. Is the least significant label '*'?) - - Returns a ``bool``. - """ - - return len(self.labels) > 0 and self.labels[0] == b"*" - - def __hash__(self) -> int: - """Return a case-insensitive hash of the name. - - Returns an ``int``. - """ - - h = 0 - for label in self.labels: - for c in label.lower(): - h += (h << 3) + c - return h - - def fullcompare(self, other: "Name") -> Tuple[NameRelation, int, int]: - """Compare two names, returning a 3-tuple - ``(relation, order, nlabels)``. - - *relation* describes the relation ship between the names, - and is one of: ``dns.name.NameRelation.NONE``, - ``dns.name.NameRelation.SUPERDOMAIN``, ``dns.name.NameRelation.SUBDOMAIN``, - ``dns.name.NameRelation.EQUAL``, or ``dns.name.NameRelation.COMMONANCESTOR``. - - *order* is < 0 if *self* < *other*, > 0 if *self* > *other*, and == - 0 if *self* == *other*. A relative name is always less than an - absolute name. If both names have the same relativity, then - the DNSSEC order relation is used to order them. - - *nlabels* is the number of significant labels that the two names - have in common. - - Here are some examples. Names ending in "." are absolute names, - those not ending in "." are relative names. - - ============= ============= =========== ===== ======= - self other relation order nlabels - ============= ============= =========== ===== ======= - www.example. www.example. equal 0 3 - www.example. example. subdomain > 0 2 - example. www.example. superdomain < 0 2 - example1.com. example2.com. common anc. < 0 2 - example1 example2. none < 0 0 - example1. example2 none > 0 0 - ============= ============= =========== ===== ======= - """ - - sabs = self.is_absolute() - oabs = other.is_absolute() - if sabs != oabs: - if sabs: - return (NameRelation.NONE, 1, 0) - else: - return (NameRelation.NONE, -1, 0) - l1 = len(self.labels) - l2 = len(other.labels) - ldiff = l1 - l2 - if ldiff < 0: - l = l1 - else: - l = l2 - - order = 0 - nlabels = 0 - namereln = NameRelation.NONE - while l > 0: - l -= 1 - l1 -= 1 - l2 -= 1 - label1 = self.labels[l1].lower() - label2 = other.labels[l2].lower() - if label1 < label2: - order = -1 - if nlabels > 0: - namereln = NameRelation.COMMONANCESTOR - return (namereln, order, nlabels) - elif label1 > label2: - order = 1 - if nlabels > 0: - namereln = NameRelation.COMMONANCESTOR - return (namereln, order, nlabels) - nlabels += 1 - order = ldiff - if ldiff < 0: - namereln = NameRelation.SUPERDOMAIN - elif ldiff > 0: - namereln = NameRelation.SUBDOMAIN - else: - namereln = NameRelation.EQUAL - return (namereln, order, nlabels) - - def is_subdomain(self, other: "Name") -> bool: - """Is self a subdomain of other? - - Note that the notion of subdomain includes equality, e.g. - "dnspython.org" is a subdomain of itself. - - Returns a ``bool``. - """ - - (nr, _, _) = self.fullcompare(other) - if nr == NameRelation.SUBDOMAIN or nr == NameRelation.EQUAL: - return True - return False - - def is_superdomain(self, other: "Name") -> bool: - """Is self a superdomain of other? - - Note that the notion of superdomain includes equality, e.g. - "dnspython.org" is a superdomain of itself. - - Returns a ``bool``. - """ - - (nr, _, _) = self.fullcompare(other) - if nr == NameRelation.SUPERDOMAIN or nr == NameRelation.EQUAL: - return True - return False - - def canonicalize(self) -> "Name": - """Return a name which is equal to the current name, but is in - DNSSEC canonical form. - """ - - return Name([x.lower() for x in self.labels]) - - def __eq__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] == 0 - else: - return False - - def __ne__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] != 0 - else: - return True - - def __lt__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] < 0 - else: - return NotImplemented - - def __le__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] <= 0 - else: - return NotImplemented - - def __ge__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] >= 0 - else: - return NotImplemented - - def __gt__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] > 0 - else: - return NotImplemented - - def __repr__(self): - return "" - - def __str__(self): - return self.to_text(False) - - def to_text(self, omit_final_dot: bool = False) -> str: - """Convert name to DNS text format. - - *omit_final_dot* is a ``bool``. If True, don't emit the final - dot (denoting the root label) for absolute names. The default - is False. - - Returns a ``str``. - """ - - if len(self.labels) == 0: - return "@" - if len(self.labels) == 1 and self.labels[0] == b"": - return "." - if omit_final_dot and self.is_absolute(): - l = self.labels[:-1] - else: - l = self.labels - s = ".".join(map(_escapify, l)) - return s - - def to_unicode( - self, omit_final_dot: bool = False, idna_codec: Optional[IDNACodec] = None - ) -> str: - """Convert name to Unicode text format. - - IDN ACE labels are converted to Unicode. - - *omit_final_dot* is a ``bool``. If True, don't emit the final - dot (denoting the root label) for absolute names. The default - is False. - *idna_codec* specifies the IDNA encoder/decoder. If None, the - dns.name.IDNA_2003_Practical encoder/decoder is used. - The IDNA_2003_Practical decoder does - not impose any policy, it just decodes punycode, so if you - don't want checking for compliance, you can use this decoder - for IDNA2008 as well. - - Returns a ``str``. - """ - - if len(self.labels) == 0: - return "@" - if len(self.labels) == 1 and self.labels[0] == b"": - return "." - if omit_final_dot and self.is_absolute(): - l = self.labels[:-1] - else: - l = self.labels - if idna_codec is None: - idna_codec = IDNA_2003_Practical - return ".".join([idna_codec.decode(x) for x in l]) - - def to_digestable(self, origin: Optional["Name"] = None) -> bytes: - """Convert name to a format suitable for digesting in hashes. - - The name is canonicalized and converted to uncompressed wire - format. All names in wire format are absolute. If the name - is a relative name, then an origin must be supplied. - - *origin* is a ``dns.name.Name`` or ``None``. If the name is - relative and origin is not ``None``, then origin will be appended - to the name. - - Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is - relative and no origin was provided. - - Returns a ``bytes``. - """ - - digest = self.to_wire(origin=origin, canonicalize=True) - assert digest is not None - return digest - - def to_wire( - self, - file: Optional[Any] = None, - compress: Optional[CompressType] = None, - origin: Optional["Name"] = None, - canonicalize: bool = False, - ) -> Optional[bytes]: - """Convert name to wire format, possibly compressing it. - - *file* is the file where the name is emitted (typically an - io.BytesIO file). If ``None`` (the default), a ``bytes`` - containing the wire name will be returned. - - *compress*, a ``dict``, is the compression table to use. If - ``None`` (the default), names will not be compressed. Note that - the compression code assumes that compression offset 0 is the - start of *file*, and thus compression will not be correct - if this is not the case. - - *origin* is a ``dns.name.Name`` or ``None``. If the name is - relative and origin is not ``None``, then *origin* will be appended - to it. - - *canonicalize*, a ``bool``, indicates whether the name should - be canonicalized; that is, converted to a format suitable for - digesting in hashes. - - Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is - relative and no origin was provided. - - Returns a ``bytes`` or ``None``. - """ - - if file is None: - out = bytearray() - for label in self.labels: - out.append(len(label)) - if canonicalize: - out += label.lower() - else: - out += label - if not self.is_absolute(): - if origin is None or not origin.is_absolute(): - raise NeedAbsoluteNameOrOrigin - for label in origin.labels: - out.append(len(label)) - if canonicalize: - out += label.lower() - else: - out += label - return bytes(out) - - labels: Iterable[bytes] - if not self.is_absolute(): - if origin is None or not origin.is_absolute(): - raise NeedAbsoluteNameOrOrigin - labels = list(self.labels) - labels.extend(list(origin.labels)) - else: - labels = self.labels - i = 0 - for label in labels: - n = Name(labels[i:]) - i += 1 - if compress is not None: - pos = compress.get(n) - else: - pos = None - if pos is not None: - value = 0xC000 + pos - s = struct.pack("!H", value) - file.write(s) - break - else: - if compress is not None and len(n) > 1: - pos = file.tell() - if pos <= 0x3FFF: - compress[n] = pos - l = len(label) - file.write(struct.pack("!B", l)) - if l > 0: - if canonicalize: - file.write(label.lower()) - else: - file.write(label) - return None - - def __len__(self) -> int: - """The length of the name (in labels). - - Returns an ``int``. - """ - - return len(self.labels) - - def __getitem__(self, index): - return self.labels[index] - - def __add__(self, other): - return self.concatenate(other) - - def __sub__(self, other): - return self.relativize(other) - - def split(self, depth: int) -> Tuple["Name", "Name"]: - """Split a name into a prefix and suffix names at the specified depth. - - *depth* is an ``int`` specifying the number of labels in the suffix - - Raises ``ValueError`` if *depth* was not >= 0 and <= the length of the - name. - - Returns the tuple ``(prefix, suffix)``. - """ - - l = len(self.labels) - if depth == 0: - return (self, dns.name.empty) - elif depth == l: - return (dns.name.empty, self) - elif depth < 0 or depth > l: - raise ValueError("depth must be >= 0 and <= the length of the name") - return (Name(self[:-depth]), Name(self[-depth:])) - - def concatenate(self, other: "Name") -> "Name": - """Return a new name which is the concatenation of self and other. - - Raises ``dns.name.AbsoluteConcatenation`` if the name is - absolute and *other* is not the empty name. - - Returns a ``dns.name.Name``. - """ - - if self.is_absolute() and len(other) > 0: - raise AbsoluteConcatenation - labels = list(self.labels) - labels.extend(list(other.labels)) - return Name(labels) - - def relativize(self, origin: "Name") -> "Name": - """If the name is a subdomain of *origin*, return a new name which is - the name relative to origin. Otherwise return the name. - - For example, relativizing ``www.dnspython.org.`` to origin - ``dnspython.org.`` returns the name ``www``. Relativizing ``example.`` - to origin ``dnspython.org.`` returns ``example.``. - - Returns a ``dns.name.Name``. - """ - - if origin is not None and self.is_subdomain(origin): - return Name(self[: -len(origin)]) - else: - return self - - def derelativize(self, origin: "Name") -> "Name": - """If the name is a relative name, return a new name which is the - concatenation of the name and origin. Otherwise return the name. - - For example, derelativizing ``www`` to origin ``dnspython.org.`` - returns the name ``www.dnspython.org.``. Derelativizing ``example.`` - to origin ``dnspython.org.`` returns ``example.``. - - Returns a ``dns.name.Name``. - """ - - if not self.is_absolute(): - return self.concatenate(origin) - else: - return self - - def choose_relativity( - self, origin: Optional["Name"] = None, relativize: bool = True - ) -> "Name": - """Return a name with the relativity desired by the caller. - - If *origin* is ``None``, then the name is returned. - Otherwise, if *relativize* is ``True`` the name is - relativized, and if *relativize* is ``False`` the name is - derelativized. - - Returns a ``dns.name.Name``. - """ - - if origin: - if relativize: - return self.relativize(origin) - else: - return self.derelativize(origin) - else: - return self - - def parent(self) -> "Name": - """Return the parent of the name. - - For example, the parent of ``www.dnspython.org.`` is ``dnspython.org``. - - Raises ``dns.name.NoParent`` if the name is either the root name or the - empty name, and thus has no parent. - - Returns a ``dns.name.Name``. - """ - - if self == root or self == empty: - raise NoParent - return Name(self.labels[1:]) - - def predecessor(self, origin: "Name", prefix_ok: bool = True) -> "Name": - """Return the maximal predecessor of *name* in the DNSSEC ordering in the zone - whose origin is *origin*, or return the longest name under *origin* if the - name is origin (i.e. wrap around to the longest name, which may still be - *origin* due to length considerations. - - The relativity of the name is preserved, so if this name is relative - then the method will return a relative name, and likewise if this name - is absolute then the predecessor will be absolute. - - *prefix_ok* indicates if prefixing labels is allowed, and - defaults to ``True``. Normally it is good to allow this, but if computing - a maximal predecessor at a zone cut point then ``False`` must be specified. - """ - return _handle_relativity_and_call( - _absolute_predecessor, self, origin, prefix_ok - ) - - def successor(self, origin: "Name", prefix_ok: bool = True) -> "Name": - """Return the minimal successor of *name* in the DNSSEC ordering in the zone - whose origin is *origin*, or return *origin* if the successor cannot be - computed due to name length limitations. - - Note that *origin* is returned in the "too long" cases because wrapping - around to the origin is how NSEC records express "end of the zone". - - The relativity of the name is preserved, so if this name is relative - then the method will return a relative name, and likewise if this name - is absolute then the successor will be absolute. - - *prefix_ok* indicates if prefixing a new minimal label is allowed, and - defaults to ``True``. Normally it is good to allow this, but if computing - a minimal successor at a zone cut point then ``False`` must be specified. - """ - return _handle_relativity_and_call(_absolute_successor, self, origin, prefix_ok) - - -#: The root name, '.' -root = Name([b""]) - -#: The empty name. -empty = Name([]) - - -def from_unicode( - text: str, origin: Optional[Name] = root, idna_codec: Optional[IDNACodec] = None -) -> Name: - """Convert unicode text into a Name object. - - Labels are encoded in IDN ACE form according to rules specified by - the IDNA codec. - - *text*, a ``str``, is the text to convert into a name. - - *origin*, a ``dns.name.Name``, specifies the origin to - append to non-absolute names. The default is the root name. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - Returns a ``dns.name.Name``. - """ - - if not isinstance(text, str): - raise ValueError("input to from_unicode() must be a unicode string") - if not (origin is None or isinstance(origin, Name)): - raise ValueError("origin must be a Name or None") - labels = [] - label = "" - escaping = False - edigits = 0 - total = 0 - if idna_codec is None: - idna_codec = IDNA_2003 - if text == "@": - text = "" - if text: - if text in [".", "\u3002", "\uff0e", "\uff61"]: - return Name([b""]) # no Unicode "u" on this constant! - for c in text: - if escaping: - if edigits == 0: - if c.isdigit(): - total = int(c) - edigits += 1 - else: - label += c - escaping = False - else: - if not c.isdigit(): - raise BadEscape - total *= 10 - total += int(c) - edigits += 1 - if edigits == 3: - escaping = False - label += chr(total) - elif c in [".", "\u3002", "\uff0e", "\uff61"]: - if len(label) == 0: - raise EmptyLabel - labels.append(idna_codec.encode(label)) - label = "" - elif c == "\\": - escaping = True - edigits = 0 - total = 0 - else: - label += c - if escaping: - raise BadEscape - if len(label) > 0: - labels.append(idna_codec.encode(label)) - else: - labels.append(b"") - - if (len(labels) == 0 or labels[-1] != b"") and origin is not None: - labels.extend(list(origin.labels)) - return Name(labels) - - -def is_all_ascii(text: str) -> bool: - for c in text: - if ord(c) > 0x7F: - return False - return True - - -def from_text( - text: Union[bytes, str], - origin: Optional[Name] = root, - idna_codec: Optional[IDNACodec] = None, -) -> Name: - """Convert text into a Name object. - - *text*, a ``bytes`` or ``str``, is the text to convert into a name. - - *origin*, a ``dns.name.Name``, specifies the origin to - append to non-absolute names. The default is the root name. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - Returns a ``dns.name.Name``. - """ - - if isinstance(text, str): - if not is_all_ascii(text): - # Some codepoint in the input text is > 127, so IDNA applies. - return from_unicode(text, origin, idna_codec) - # The input is all ASCII, so treat this like an ordinary non-IDNA - # domain name. Note that "all ASCII" is about the input text, - # not the codepoints in the domain name. E.g. if text has value - # - # r'\150\151\152\153\154\155\156\157\158\159' - # - # then it's still "all ASCII" even though the domain name has - # codepoints > 127. - text = text.encode("ascii") - if not isinstance(text, bytes): - raise ValueError("input to from_text() must be a string") - if not (origin is None or isinstance(origin, Name)): - raise ValueError("origin must be a Name or None") - labels = [] - label = b"" - escaping = False - edigits = 0 - total = 0 - if text == b"@": - text = b"" - if text: - if text == b".": - return Name([b""]) - for c in text: - byte_ = struct.pack("!B", c) - if escaping: - if edigits == 0: - if byte_.isdigit(): - total = int(byte_) - edigits += 1 - else: - label += byte_ - escaping = False - else: - if not byte_.isdigit(): - raise BadEscape - total *= 10 - total += int(byte_) - edigits += 1 - if edigits == 3: - escaping = False - label += struct.pack("!B", total) - elif byte_ == b".": - if len(label) == 0: - raise EmptyLabel - labels.append(label) - label = b"" - elif byte_ == b"\\": - escaping = True - edigits = 0 - total = 0 - else: - label += byte_ - if escaping: - raise BadEscape - if len(label) > 0: - labels.append(label) - else: - labels.append(b"") - if (len(labels) == 0 or labels[-1] != b"") and origin is not None: - labels.extend(list(origin.labels)) - return Name(labels) - - -# we need 'dns.wire.Parser' quoted as dns.name and dns.wire depend on each other. - - -def from_wire_parser(parser: "dns.wire.Parser") -> Name: - """Convert possibly compressed wire format into a Name. - - *parser* is a dns.wire.Parser. - - Raises ``dns.name.BadPointer`` if a compression pointer did not - point backwards in the message. - - Raises ``dns.name.BadLabelType`` if an invalid label type was encountered. - - Returns a ``dns.name.Name`` - """ - - labels = [] - biggest_pointer = parser.current - with parser.restore_furthest(): - count = parser.get_uint8() - while count != 0: - if count < 64: - labels.append(parser.get_bytes(count)) - elif count >= 192: - current = (count & 0x3F) * 256 + parser.get_uint8() - if current >= biggest_pointer: - raise BadPointer - biggest_pointer = current - parser.seek(current) - else: - raise BadLabelType - count = parser.get_uint8() - labels.append(b"") - return Name(labels) - - -def from_wire(message: bytes, current: int) -> Tuple[Name, int]: - """Convert possibly compressed wire format into a Name. - - *message* is a ``bytes`` containing an entire DNS message in DNS - wire form. - - *current*, an ``int``, is the offset of the beginning of the name - from the start of the message - - Raises ``dns.name.BadPointer`` if a compression pointer did not - point backwards in the message. - - Raises ``dns.name.BadLabelType`` if an invalid label type was encountered. - - Returns a ``(dns.name.Name, int)`` tuple consisting of the name - that was read and the number of bytes of the wire format message - which were consumed reading it. - """ - - if not isinstance(message, bytes): - raise ValueError("input to from_wire() must be a byte string") - parser = dns.wire.Parser(message, current) - name = from_wire_parser(parser) - return (name, parser.current - current) - - -# RFC 4471 Support - -_MINIMAL_OCTET = b"\x00" -_MINIMAL_OCTET_VALUE = ord(_MINIMAL_OCTET) -_SUCCESSOR_PREFIX = Name([_MINIMAL_OCTET]) -_MAXIMAL_OCTET = b"\xff" -_MAXIMAL_OCTET_VALUE = ord(_MAXIMAL_OCTET) -_AT_SIGN_VALUE = ord("@") -_LEFT_SQUARE_BRACKET_VALUE = ord("[") - - -def _wire_length(labels): - return functools.reduce(lambda v, x: v + len(x) + 1, labels, 0) - - -def _pad_to_max_name(name): - needed = 255 - _wire_length(name.labels) - new_labels = [] - while needed > 64: - new_labels.append(_MAXIMAL_OCTET * 63) - needed -= 64 - if needed >= 2: - new_labels.append(_MAXIMAL_OCTET * (needed - 1)) - # Note we're already maximal in the needed == 1 case as while we'd like - # to add one more byte as a new label, we can't, as adding a new non-empty - # label requires at least 2 bytes. - new_labels = list(reversed(new_labels)) - new_labels.extend(name.labels) - return Name(new_labels) - - -def _pad_to_max_label(label, suffix_labels): - length = len(label) - # We have to subtract one here to account for the length byte of label. - remaining = 255 - _wire_length(suffix_labels) - length - 1 - if remaining <= 0: - # Shouldn't happen! - return label - needed = min(63 - length, remaining) - return label + _MAXIMAL_OCTET * needed - - -def _absolute_predecessor(name: Name, origin: Name, prefix_ok: bool) -> Name: - # This is the RFC 4471 predecessor algorithm using the "absolute method" of section - # 3.1.1. - # - # Our caller must ensure that the name and origin are absolute, and that name is a - # subdomain of origin. - if name == origin: - return _pad_to_max_name(name) - least_significant_label = name[0] - if least_significant_label == _MINIMAL_OCTET: - return name.parent() - least_octet = least_significant_label[-1] - suffix_labels = name.labels[1:] - if least_octet == _MINIMAL_OCTET_VALUE: - new_labels = [least_significant_label[:-1]] - else: - octets = bytearray(least_significant_label) - octet = octets[-1] - if octet == _LEFT_SQUARE_BRACKET_VALUE: - octet = _AT_SIGN_VALUE - else: - octet -= 1 - octets[-1] = octet - least_significant_label = bytes(octets) - new_labels = [_pad_to_max_label(least_significant_label, suffix_labels)] - new_labels.extend(suffix_labels) - name = Name(new_labels) - if prefix_ok: - return _pad_to_max_name(name) - else: - return name - - -def _absolute_successor(name: Name, origin: Name, prefix_ok: bool) -> Name: - # This is the RFC 4471 successor algorithm using the "absolute method" of section - # 3.1.2. - # - # Our caller must ensure that the name and origin are absolute, and that name is a - # subdomain of origin. - if prefix_ok: - # Try prefixing \000 as new label - try: - return _SUCCESSOR_PREFIX.concatenate(name) - except NameTooLong: - pass - while name != origin: - # Try extending the least significant label. - least_significant_label = name[0] - if len(least_significant_label) < 63: - # We may be able to extend the least label with a minimal additional byte. - # This is only "may" because we could have a maximal length name even though - # the least significant label isn't maximally long. - new_labels = [least_significant_label + _MINIMAL_OCTET] - new_labels.extend(name.labels[1:]) - try: - return dns.name.Name(new_labels) - except dns.name.NameTooLong: - pass - # We can't extend the label either, so we'll try to increment the least - # signficant non-maximal byte in it. - octets = bytearray(least_significant_label) - # We do this reversed iteration with an explicit indexing variable because - # if we find something to increment, we're going to want to truncate everything - # to the right of it. - for i in range(len(octets) - 1, -1, -1): - octet = octets[i] - if octet == _MAXIMAL_OCTET_VALUE: - # We can't increment this, so keep looking. - continue - # Finally, something we can increment. We have to apply a special rule for - # incrementing "@", sending it to "[", because RFC 4034 6.1 says that when - # comparing names, uppercase letters compare as if they were their - # lower-case equivalents. If we increment "@" to "A", then it would compare - # as "a", which is after "[", "\", "]", "^", "_", and "`", so we would have - # skipped the most minimal successor, namely "[". - if octet == _AT_SIGN_VALUE: - octet = _LEFT_SQUARE_BRACKET_VALUE - else: - octet += 1 - octets[i] = octet - # We can now truncate all of the maximal values we skipped (if any) - new_labels = [bytes(octets[: i + 1])] - new_labels.extend(name.labels[1:]) - # We haven't changed the length of the name, so the Name constructor will - # always work. - return Name(new_labels) - # We couldn't increment, so chop off the least significant label and try - # again. - name = name.parent() - - # We couldn't increment at all, so return the origin, as wrapping around is the - # DNSSEC way. - return origin - - -def _handle_relativity_and_call( - function: Callable[[Name, Name, bool], Name], - name: Name, - origin: Name, - prefix_ok: bool, -) -> Name: - # Make "name" absolute if needed, ensure that the origin is absolute, - # call function(), and then relativize the result if needed. - if not origin.is_absolute(): - raise NeedAbsoluteNameOrOrigin - relative = not name.is_absolute() - if relative: - name = name.derelativize(origin) - elif not name.is_subdomain(origin): - raise NeedSubdomainOfOrigin - result_name = function(name, origin, prefix_ok) - if relative: - result_name = result_name.relativize(origin) - return result_name diff --git a/venv/Lib/site-packages/dns/namedict.py b/venv/Lib/site-packages/dns/namedict.py deleted file mode 100644 index ca8b197..0000000 --- a/venv/Lib/site-packages/dns/namedict.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# Copyright (C) 2016 Coresec Systems AB -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND CORESEC SYSTEMS AB DISCLAIMS ALL -# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CORESEC -# SYSTEMS AB BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR -# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS -# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS name dictionary""" - -# pylint seems to be confused about this one! -from collections.abc import MutableMapping # pylint: disable=no-name-in-module - -import dns.name - - -class NameDict(MutableMapping): - """A dictionary whose keys are dns.name.Name objects. - - In addition to being like a regular Python dictionary, this - dictionary can also get the deepest match for a given key. - """ - - __slots__ = ["max_depth", "max_depth_items", "__store"] - - def __init__(self, *args, **kwargs): - super().__init__() - self.__store = dict() - #: the maximum depth of the keys that have ever been added - self.max_depth = 0 - #: the number of items of maximum depth - self.max_depth_items = 0 - self.update(dict(*args, **kwargs)) - - def __update_max_depth(self, key): - if len(key) == self.max_depth: - self.max_depth_items = self.max_depth_items + 1 - elif len(key) > self.max_depth: - self.max_depth = len(key) - self.max_depth_items = 1 - - def __getitem__(self, key): - return self.__store[key] - - def __setitem__(self, key, value): - if not isinstance(key, dns.name.Name): - raise ValueError("NameDict key must be a name") - self.__store[key] = value - self.__update_max_depth(key) - - def __delitem__(self, key): - self.__store.pop(key) - if len(key) == self.max_depth: - self.max_depth_items = self.max_depth_items - 1 - if self.max_depth_items == 0: - self.max_depth = 0 - for k in self.__store: - self.__update_max_depth(k) - - def __iter__(self): - return iter(self.__store) - - def __len__(self): - return len(self.__store) - - def has_key(self, key): - return key in self.__store - - def get_deepest_match(self, name): - """Find the deepest match to *name* in the dictionary. - - The deepest match is the longest name in the dictionary which is - a superdomain of *name*. Note that *superdomain* includes matching - *name* itself. - - *name*, a ``dns.name.Name``, the name to find. - - Returns a ``(key, value)`` where *key* is the deepest - ``dns.name.Name``, and *value* is the value associated with *key*. - """ - - depth = len(name) - if depth > self.max_depth: - depth = self.max_depth - for i in range(-depth, 0): - n = dns.name.Name(name[i:]) - if n in self: - return (n, self[n]) - v = self[dns.name.empty] - return (dns.name.empty, v) diff --git a/venv/Lib/site-packages/dns/nameserver.py b/venv/Lib/site-packages/dns/nameserver.py deleted file mode 100644 index b02a239..0000000 --- a/venv/Lib/site-packages/dns/nameserver.py +++ /dev/null @@ -1,363 +0,0 @@ -from typing import Optional, Union -from urllib.parse import urlparse - -import dns.asyncbackend -import dns.asyncquery -import dns.inet -import dns.message -import dns.query - - -class Nameserver: - def __init__(self): - pass - - def __str__(self): - raise NotImplementedError - - def kind(self) -> str: - raise NotImplementedError - - def is_always_max_size(self) -> bool: - raise NotImplementedError - - def answer_nameserver(self) -> str: - raise NotImplementedError - - def answer_port(self) -> int: - raise NotImplementedError - - def query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - raise NotImplementedError - - async def async_query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool, - backend: dns.asyncbackend.Backend, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - raise NotImplementedError - - -class AddressAndPortNameserver(Nameserver): - def __init__(self, address: str, port: int): - super().__init__() - self.address = address - self.port = port - - def kind(self) -> str: - raise NotImplementedError - - def is_always_max_size(self) -> bool: - return False - - def __str__(self): - ns_kind = self.kind() - return f"{ns_kind}:{self.address}@{self.port}" - - def answer_nameserver(self) -> str: - return self.address - - def answer_port(self) -> int: - return self.port - - -class Do53Nameserver(AddressAndPortNameserver): - def __init__(self, address: str, port: int = 53): - super().__init__(address, port) - - def kind(self): - return "Do53" - - def query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - if max_size: - response = dns.query.tcp( - request, - self.address, - timeout=timeout, - port=self.port, - source=source, - source_port=source_port, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - else: - response = dns.query.udp( - request, - self.address, - timeout=timeout, - port=self.port, - source=source, - source_port=source_port, - raise_on_truncation=True, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ignore_errors=True, - ignore_unexpected=True, - ) - return response - - async def async_query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool, - backend: dns.asyncbackend.Backend, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - if max_size: - response = await dns.asyncquery.tcp( - request, - self.address, - timeout=timeout, - port=self.port, - source=source, - source_port=source_port, - backend=backend, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - else: - response = await dns.asyncquery.udp( - request, - self.address, - timeout=timeout, - port=self.port, - source=source, - source_port=source_port, - raise_on_truncation=True, - backend=backend, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ignore_errors=True, - ignore_unexpected=True, - ) - return response - - -class DoHNameserver(Nameserver): - def __init__( - self, - url: str, - bootstrap_address: Optional[str] = None, - verify: Union[bool, str] = True, - want_get: bool = False, - http_version: dns.query.HTTPVersion = dns.query.HTTPVersion.DEFAULT, - ): - super().__init__() - self.url = url - self.bootstrap_address = bootstrap_address - self.verify = verify - self.want_get = want_get - self.http_version = http_version - - def kind(self): - return "DoH" - - def is_always_max_size(self) -> bool: - return True - - def __str__(self): - return self.url - - def answer_nameserver(self) -> str: - return self.url - - def answer_port(self) -> int: - port = urlparse(self.url).port - if port is None: - port = 443 - return port - - def query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - return dns.query.https( - request, - self.url, - timeout=timeout, - source=source, - source_port=source_port, - bootstrap_address=self.bootstrap_address, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - verify=self.verify, - post=(not self.want_get), - http_version=self.http_version, - ) - - async def async_query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool, - backend: dns.asyncbackend.Backend, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - return await dns.asyncquery.https( - request, - self.url, - timeout=timeout, - source=source, - source_port=source_port, - bootstrap_address=self.bootstrap_address, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - verify=self.verify, - post=(not self.want_get), - http_version=self.http_version, - ) - - -class DoTNameserver(AddressAndPortNameserver): - def __init__( - self, - address: str, - port: int = 853, - hostname: Optional[str] = None, - verify: Union[bool, str] = True, - ): - super().__init__(address, port) - self.hostname = hostname - self.verify = verify - - def kind(self): - return "DoT" - - def query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - return dns.query.tls( - request, - self.address, - port=self.port, - timeout=timeout, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - server_hostname=self.hostname, - verify=self.verify, - ) - - async def async_query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool, - backend: dns.asyncbackend.Backend, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - return await dns.asyncquery.tls( - request, - self.address, - port=self.port, - timeout=timeout, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - server_hostname=self.hostname, - verify=self.verify, - ) - - -class DoQNameserver(AddressAndPortNameserver): - def __init__( - self, - address: str, - port: int = 853, - verify: Union[bool, str] = True, - server_hostname: Optional[str] = None, - ): - super().__init__(address, port) - self.verify = verify - self.server_hostname = server_hostname - - def kind(self): - return "DoQ" - - def query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - return dns.query.quic( - request, - self.address, - port=self.port, - timeout=timeout, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - verify=self.verify, - server_hostname=self.server_hostname, - ) - - async def async_query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool, - backend: dns.asyncbackend.Backend, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - return await dns.asyncquery.quic( - request, - self.address, - port=self.port, - timeout=timeout, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - verify=self.verify, - server_hostname=self.server_hostname, - ) diff --git a/venv/Lib/site-packages/dns/node.py b/venv/Lib/site-packages/dns/node.py deleted file mode 100644 index de85a82..0000000 --- a/venv/Lib/site-packages/dns/node.py +++ /dev/null @@ -1,359 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS nodes. A node is a set of rdatasets.""" - -import enum -import io -from typing import Any, Dict, Optional - -import dns.immutable -import dns.name -import dns.rdataclass -import dns.rdataset -import dns.rdatatype -import dns.renderer -import dns.rrset - -_cname_types = { - dns.rdatatype.CNAME, -} - -# "neutral" types can coexist with a CNAME and thus are not "other data" -_neutral_types = { - dns.rdatatype.NSEC, # RFC 4035 section 2.5 - dns.rdatatype.NSEC3, # This is not likely to happen, but not impossible! - dns.rdatatype.KEY, # RFC 4035 section 2.5, RFC 3007 -} - - -def _matches_type_or_its_signature(rdtypes, rdtype, covers): - return rdtype in rdtypes or (rdtype == dns.rdatatype.RRSIG and covers in rdtypes) - - -@enum.unique -class NodeKind(enum.Enum): - """Rdatasets in nodes""" - - REGULAR = 0 # a.k.a "other data" - NEUTRAL = 1 - CNAME = 2 - - @classmethod - def classify( - cls, rdtype: dns.rdatatype.RdataType, covers: dns.rdatatype.RdataType - ) -> "NodeKind": - if _matches_type_or_its_signature(_cname_types, rdtype, covers): - return NodeKind.CNAME - elif _matches_type_or_its_signature(_neutral_types, rdtype, covers): - return NodeKind.NEUTRAL - else: - return NodeKind.REGULAR - - @classmethod - def classify_rdataset(cls, rdataset: dns.rdataset.Rdataset) -> "NodeKind": - return cls.classify(rdataset.rdtype, rdataset.covers) - - -class Node: - """A Node is a set of rdatasets. - - A node is either a CNAME node or an "other data" node. A CNAME - node contains only CNAME, KEY, NSEC, and NSEC3 rdatasets along with their - covering RRSIG rdatasets. An "other data" node contains any - rdataset other than a CNAME or RRSIG(CNAME) rdataset. When - changes are made to a node, the CNAME or "other data" state is - always consistent with the update, i.e. the most recent change - wins. For example, if you have a node which contains a CNAME - rdataset, and then add an MX rdataset to it, then the CNAME - rdataset will be deleted. Likewise if you have a node containing - an MX rdataset and add a CNAME rdataset, the MX rdataset will be - deleted. - """ - - __slots__ = ["rdatasets"] - - def __init__(self): - # the set of rdatasets, represented as a list. - self.rdatasets = [] - - def to_text(self, name: dns.name.Name, **kw: Dict[str, Any]) -> str: - """Convert a node to text format. - - Each rdataset at the node is printed. Any keyword arguments - to this method are passed on to the rdataset's to_text() method. - - *name*, a ``dns.name.Name``, the owner name of the - rdatasets. - - Returns a ``str``. - - """ - - s = io.StringIO() - for rds in self.rdatasets: - if len(rds) > 0: - s.write(rds.to_text(name, **kw)) # type: ignore[arg-type] - s.write("\n") - return s.getvalue()[:-1] - - def __repr__(self): - return "" - - def __eq__(self, other): - # - # This is inefficient. Good thing we don't need to do it much. - # - for rd in self.rdatasets: - if rd not in other.rdatasets: - return False - for rd in other.rdatasets: - if rd not in self.rdatasets: - return False - return True - - def __ne__(self, other): - return not self.__eq__(other) - - def __len__(self): - return len(self.rdatasets) - - def __iter__(self): - return iter(self.rdatasets) - - def _append_rdataset(self, rdataset): - """Append rdataset to the node with special handling for CNAME and - other data conditions. - - Specifically, if the rdataset being appended has ``NodeKind.CNAME``, - then all rdatasets other than KEY, NSEC, NSEC3, and their covering - RRSIGs are deleted. If the rdataset being appended has - ``NodeKind.REGULAR`` then CNAME and RRSIG(CNAME) are deleted. - """ - # Make having just one rdataset at the node fast. - if len(self.rdatasets) > 0: - kind = NodeKind.classify_rdataset(rdataset) - if kind == NodeKind.CNAME: - self.rdatasets = [ - rds - for rds in self.rdatasets - if NodeKind.classify_rdataset(rds) != NodeKind.REGULAR - ] - elif kind == NodeKind.REGULAR: - self.rdatasets = [ - rds - for rds in self.rdatasets - if NodeKind.classify_rdataset(rds) != NodeKind.CNAME - ] - # Otherwise the rdataset is NodeKind.NEUTRAL and we do not need to - # edit self.rdatasets. - self.rdatasets.append(rdataset) - - def find_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - create: bool = False, - ) -> dns.rdataset.Rdataset: - """Find an rdataset matching the specified properties in the - current node. - - *rdclass*, a ``dns.rdataclass.RdataClass``, the class of the rdataset. - - *rdtype*, a ``dns.rdatatype.RdataType``, the type of the rdataset. - - *covers*, a ``dns.rdatatype.RdataType``, the covered type. - Usually this value is ``dns.rdatatype.NONE``, but if the - rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, - then the covers value will be the rdata type the SIG/RRSIG - covers. The library treats the SIG and RRSIG types as if they - were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). - This makes RRSIGs much easier to work with than if RRSIGs - covering different rdata types were aggregated into a single - RRSIG rdataset. - - *create*, a ``bool``. If True, create the rdataset if it is not found. - - Raises ``KeyError`` if an rdataset of the desired type and class does - not exist and *create* is not ``True``. - - Returns a ``dns.rdataset.Rdataset``. - """ - - for rds in self.rdatasets: - if rds.match(rdclass, rdtype, covers): - return rds - if not create: - raise KeyError - rds = dns.rdataset.Rdataset(rdclass, rdtype, covers) - self._append_rdataset(rds) - return rds - - def get_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - create: bool = False, - ) -> Optional[dns.rdataset.Rdataset]: - """Get an rdataset matching the specified properties in the - current node. - - None is returned if an rdataset of the specified type and - class does not exist and *create* is not ``True``. - - *rdclass*, an ``int``, the class of the rdataset. - - *rdtype*, an ``int``, the type of the rdataset. - - *covers*, an ``int``, the covered type. Usually this value is - dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or - dns.rdatatype.RRSIG, then the covers value will be the rdata - type the SIG/RRSIG covers. The library treats the SIG and RRSIG - types as if they were a family of - types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much - easier to work with than if RRSIGs covering different rdata - types were aggregated into a single RRSIG rdataset. - - *create*, a ``bool``. If True, create the rdataset if it is not found. - - Returns a ``dns.rdataset.Rdataset`` or ``None``. - """ - - try: - rds = self.find_rdataset(rdclass, rdtype, covers, create) - except KeyError: - rds = None - return rds - - def delete_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - ) -> None: - """Delete the rdataset matching the specified properties in the - current node. - - If a matching rdataset does not exist, it is not an error. - - *rdclass*, an ``int``, the class of the rdataset. - - *rdtype*, an ``int``, the type of the rdataset. - - *covers*, an ``int``, the covered type. - """ - - rds = self.get_rdataset(rdclass, rdtype, covers) - if rds is not None: - self.rdatasets.remove(rds) - - def replace_rdataset(self, replacement: dns.rdataset.Rdataset) -> None: - """Replace an rdataset. - - It is not an error if there is no rdataset matching *replacement*. - - Ownership of the *replacement* object is transferred to the node; - in other words, this method does not store a copy of *replacement* - at the node, it stores *replacement* itself. - - *replacement*, a ``dns.rdataset.Rdataset``. - - Raises ``ValueError`` if *replacement* is not a - ``dns.rdataset.Rdataset``. - """ - - if not isinstance(replacement, dns.rdataset.Rdataset): - raise ValueError("replacement is not an rdataset") - if isinstance(replacement, dns.rrset.RRset): - # RRsets are not good replacements as the match() method - # is not compatible. - replacement = replacement.to_rdataset() - self.delete_rdataset( - replacement.rdclass, replacement.rdtype, replacement.covers - ) - self._append_rdataset(replacement) - - def classify(self) -> NodeKind: - """Classify a node. - - A node which contains a CNAME or RRSIG(CNAME) is a - ``NodeKind.CNAME`` node. - - A node which contains only "neutral" types, i.e. types allowed to - co-exist with a CNAME, is a ``NodeKind.NEUTRAL`` node. The neutral - types are NSEC, NSEC3, KEY, and their associated RRSIGS. An empty node - is also considered neutral. - - A node which contains some rdataset which is not a CNAME, RRSIG(CNAME), - or a neutral type is a a ``NodeKind.REGULAR`` node. Regular nodes are - also commonly referred to as "other data". - """ - for rdataset in self.rdatasets: - kind = NodeKind.classify(rdataset.rdtype, rdataset.covers) - if kind != NodeKind.NEUTRAL: - return kind - return NodeKind.NEUTRAL - - def is_immutable(self) -> bool: - return False - - -@dns.immutable.immutable -class ImmutableNode(Node): - def __init__(self, node): - super().__init__() - self.rdatasets = tuple( - [dns.rdataset.ImmutableRdataset(rds) for rds in node.rdatasets] - ) - - def find_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - create: bool = False, - ) -> dns.rdataset.Rdataset: - if create: - raise TypeError("immutable") - return super().find_rdataset(rdclass, rdtype, covers, False) - - def get_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - create: bool = False, - ) -> Optional[dns.rdataset.Rdataset]: - if create: - raise TypeError("immutable") - return super().get_rdataset(rdclass, rdtype, covers, False) - - def delete_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - ) -> None: - raise TypeError("immutable") - - def replace_rdataset(self, replacement: dns.rdataset.Rdataset) -> None: - raise TypeError("immutable") - - def is_immutable(self) -> bool: - return True diff --git a/venv/Lib/site-packages/dns/opcode.py b/venv/Lib/site-packages/dns/opcode.py deleted file mode 100644 index 78b43d2..0000000 --- a/venv/Lib/site-packages/dns/opcode.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Opcodes.""" - -import dns.enum -import dns.exception - - -class Opcode(dns.enum.IntEnum): - #: Query - QUERY = 0 - #: Inverse Query (historical) - IQUERY = 1 - #: Server Status (unspecified and unimplemented anywhere) - STATUS = 2 - #: Notify - NOTIFY = 4 - #: Dynamic Update - UPDATE = 5 - - @classmethod - def _maximum(cls): - return 15 - - @classmethod - def _unknown_exception_class(cls): - return UnknownOpcode - - -class UnknownOpcode(dns.exception.DNSException): - """An DNS opcode is unknown.""" - - -def from_text(text: str) -> Opcode: - """Convert text into an opcode. - - *text*, a ``str``, the textual opcode - - Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown. - - Returns an ``int``. - """ - - return Opcode.from_text(text) - - -def from_flags(flags: int) -> Opcode: - """Extract an opcode from DNS message flags. - - *flags*, an ``int``, the DNS flags. - - Returns an ``int``. - """ - - return Opcode((flags & 0x7800) >> 11) - - -def to_flags(value: Opcode) -> int: - """Convert an opcode to a value suitable for ORing into DNS message - flags. - - *value*, an ``int``, the DNS opcode value. - - Returns an ``int``. - """ - - return (value << 11) & 0x7800 - - -def to_text(value: Opcode) -> str: - """Convert an opcode to text. - - *value*, an ``int`` the opcode value, - - Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown. - - Returns a ``str``. - """ - - return Opcode.to_text(value) - - -def is_update(flags: int) -> bool: - """Is the opcode in flags UPDATE? - - *flags*, an ``int``, the DNS message flags. - - Returns a ``bool``. - """ - - return from_flags(flags) == Opcode.UPDATE - - -### BEGIN generated Opcode constants - -QUERY = Opcode.QUERY -IQUERY = Opcode.IQUERY -STATUS = Opcode.STATUS -NOTIFY = Opcode.NOTIFY -UPDATE = Opcode.UPDATE - -### END generated Opcode constants diff --git a/venv/Lib/site-packages/dns/py.typed b/venv/Lib/site-packages/dns/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/venv/Lib/site-packages/dns/query.py b/venv/Lib/site-packages/dns/query.py deleted file mode 100644 index 0d8a977..0000000 --- a/venv/Lib/site-packages/dns/query.py +++ /dev/null @@ -1,1665 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Talk to a DNS server.""" - -import base64 -import contextlib -import enum -import errno -import os -import os.path -import random -import selectors -import socket -import struct -import time -import urllib.parse -from typing import Any, Dict, Optional, Tuple, Union, cast - -import dns._features -import dns.exception -import dns.inet -import dns.message -import dns.name -import dns.quic -import dns.rcode -import dns.rdataclass -import dns.rdatatype -import dns.serial -import dns.transaction -import dns.tsig -import dns.xfr - - -def _remaining(expiration): - if expiration is None: - return None - timeout = expiration - time.time() - if timeout <= 0.0: - raise dns.exception.Timeout - return timeout - - -def _expiration_for_this_attempt(timeout, expiration): - if expiration is None: - return None - return min(time.time() + timeout, expiration) - - -_have_httpx = dns._features.have("doh") -if _have_httpx: - import httpcore._backends.sync - import httpx - - _CoreNetworkBackend = httpcore.NetworkBackend - _CoreSyncStream = httpcore._backends.sync.SyncStream - - class _NetworkBackend(_CoreNetworkBackend): - def __init__(self, resolver, local_port, bootstrap_address, family): - super().__init__() - self._local_port = local_port - self._resolver = resolver - self._bootstrap_address = bootstrap_address - self._family = family - - def connect_tcp( - self, host, port, timeout, local_address, socket_options=None - ): # pylint: disable=signature-differs - addresses = [] - _, expiration = _compute_times(timeout) - if dns.inet.is_address(host): - addresses.append(host) - elif self._bootstrap_address is not None: - addresses.append(self._bootstrap_address) - else: - timeout = _remaining(expiration) - family = self._family - if local_address: - family = dns.inet.af_for_address(local_address) - answers = self._resolver.resolve_name( - host, family=family, lifetime=timeout - ) - addresses = answers.addresses() - for address in addresses: - af = dns.inet.af_for_address(address) - if local_address is not None or self._local_port != 0: - source = dns.inet.low_level_address_tuple( - (local_address, self._local_port), af - ) - else: - source = None - sock = _make_socket(af, socket.SOCK_STREAM, source) - attempt_expiration = _expiration_for_this_attempt(2.0, expiration) - try: - _connect( - sock, - dns.inet.low_level_address_tuple((address, port), af), - attempt_expiration, - ) - return _CoreSyncStream(sock) - except Exception: - pass - raise httpcore.ConnectError - - def connect_unix_socket( - self, path, timeout, socket_options=None - ): # pylint: disable=signature-differs - raise NotImplementedError - - class _HTTPTransport(httpx.HTTPTransport): - def __init__( - self, - *args, - local_port=0, - bootstrap_address=None, - resolver=None, - family=socket.AF_UNSPEC, - **kwargs, - ): - if resolver is None and bootstrap_address is None: - # pylint: disable=import-outside-toplevel,redefined-outer-name - import dns.resolver - - resolver = dns.resolver.Resolver() - super().__init__(*args, **kwargs) - self._pool._network_backend = _NetworkBackend( - resolver, local_port, bootstrap_address, family - ) - -else: - - class _HTTPTransport: # type: ignore - def connect_tcp(self, host, port, timeout, local_address): - raise NotImplementedError - - -have_doh = _have_httpx - -try: - import ssl -except ImportError: # pragma: no cover - - class ssl: # type: ignore - CERT_NONE = 0 - - class WantReadException(Exception): - pass - - class WantWriteException(Exception): - pass - - class SSLContext: - pass - - class SSLSocket: - pass - - @classmethod - def create_default_context(cls, *args, **kwargs): - raise Exception("no ssl support") # pylint: disable=broad-exception-raised - - -# Function used to create a socket. Can be overridden if needed in special -# situations. -socket_factory = socket.socket - - -class UnexpectedSource(dns.exception.DNSException): - """A DNS query response came from an unexpected address or port.""" - - -class BadResponse(dns.exception.FormError): - """A DNS query response does not respond to the question asked.""" - - -class NoDOH(dns.exception.DNSException): - """DNS over HTTPS (DOH) was requested but the httpx module is not - available.""" - - -class NoDOQ(dns.exception.DNSException): - """DNS over QUIC (DOQ) was requested but the aioquic module is not - available.""" - - -# for backwards compatibility -TransferError = dns.xfr.TransferError - - -def _compute_times(timeout): - now = time.time() - if timeout is None: - return (now, None) - else: - return (now, now + timeout) - - -def _wait_for(fd, readable, writable, _, expiration): - # Use the selected selector class to wait for any of the specified - # events. An "expiration" absolute time is converted into a relative - # timeout. - # - # The unused parameter is 'error', which is always set when - # selecting for read or write, and we have no error-only selects. - - if readable and isinstance(fd, ssl.SSLSocket) and fd.pending() > 0: - return True - sel = selectors.DefaultSelector() - events = 0 - if readable: - events |= selectors.EVENT_READ - if writable: - events |= selectors.EVENT_WRITE - if events: - sel.register(fd, events) - if expiration is None: - timeout = None - else: - timeout = expiration - time.time() - if timeout <= 0.0: - raise dns.exception.Timeout - if not sel.select(timeout): - raise dns.exception.Timeout - - -def _wait_for_readable(s, expiration): - _wait_for(s, True, False, True, expiration) - - -def _wait_for_writable(s, expiration): - _wait_for(s, False, True, True, expiration) - - -def _addresses_equal(af, a1, a2): - # Convert the first value of the tuple, which is a textual format - # address into binary form, so that we are not confused by different - # textual representations of the same address - try: - n1 = dns.inet.inet_pton(af, a1[0]) - n2 = dns.inet.inet_pton(af, a2[0]) - except dns.exception.SyntaxError: - return False - return n1 == n2 and a1[1:] == a2[1:] - - -def _matches_destination(af, from_address, destination, ignore_unexpected): - # Check that from_address is appropriate for a response to a query - # sent to destination. - if not destination: - return True - if _addresses_equal(af, from_address, destination) or ( - dns.inet.is_multicast(destination[0]) and from_address[1:] == destination[1:] - ): - return True - elif ignore_unexpected: - return False - raise UnexpectedSource( - f"got a response from {from_address} instead of " f"{destination}" - ) - - -def _destination_and_source( - where, port, source, source_port, where_must_be_address=True -): - # Apply defaults and compute destination and source tuples - # suitable for use in connect(), sendto(), or bind(). - af = None - destination = None - try: - af = dns.inet.af_for_address(where) - destination = where - except Exception: - if where_must_be_address: - raise - # URLs are ok so eat the exception - if source: - saf = dns.inet.af_for_address(source) - if af: - # We know the destination af, so source had better agree! - if saf != af: - raise ValueError( - "different address families for source and destination" - ) - else: - # We didn't know the destination af, but we know the source, - # so that's our af. - af = saf - if source_port and not source: - # Caller has specified a source_port but not an address, so we - # need to return a source, and we need to use the appropriate - # wildcard address as the address. - try: - source = dns.inet.any_for_af(af) - except Exception: - # we catch this and raise ValueError for backwards compatibility - raise ValueError("source_port specified but address family is unknown") - # Convert high-level (address, port) tuples into low-level address - # tuples. - if destination: - destination = dns.inet.low_level_address_tuple((destination, port), af) - if source: - source = dns.inet.low_level_address_tuple((source, source_port), af) - return (af, destination, source) - - -def _make_socket(af, type, source, ssl_context=None, server_hostname=None): - s = socket_factory(af, type) - try: - s.setblocking(False) - if source is not None: - s.bind(source) - if ssl_context: - # LGTM gets a false positive here, as our default context is OK - return ssl_context.wrap_socket( - s, - do_handshake_on_connect=False, # lgtm[py/insecure-protocol] - server_hostname=server_hostname, - ) - else: - return s - except Exception: - s.close() - raise - - -def _maybe_get_resolver( - resolver: Optional["dns.resolver.Resolver"], -) -> "dns.resolver.Resolver": - # We need a separate method for this to avoid overriding the global - # variable "dns" with the as-yet undefined local variable "dns" - # in https(). - if resolver is None: - # pylint: disable=import-outside-toplevel,redefined-outer-name - import dns.resolver - - resolver = dns.resolver.Resolver() - return resolver - - -class HTTPVersion(enum.IntEnum): - """Which version of HTTP should be used? - - DEFAULT will select the first version from the list [2, 1.1, 3] that - is available. - """ - - DEFAULT = 0 - HTTP_1 = 1 - H1 = 1 - HTTP_2 = 2 - H2 = 2 - HTTP_3 = 3 - H3 = 3 - - -def https( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 443, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - session: Optional[Any] = None, - path: str = "/dns-query", - post: bool = True, - bootstrap_address: Optional[str] = None, - verify: Union[bool, str] = True, - resolver: Optional["dns.resolver.Resolver"] = None, - family: int = socket.AF_UNSPEC, - http_version: HTTPVersion = HTTPVersion.DEFAULT, -) -> dns.message.Message: - """Return the response obtained after sending a query via DNS-over-HTTPS. - - *q*, a ``dns.message.Message``, the query to send. - - *where*, a ``str``, the nameserver IP address or the full URL. If an IP address is - given, the URL will be constructed using the following schema: - https://:/. - - *timeout*, a ``float`` or ``None``, the number of seconds to wait before the query - times out. If ``None``, the default, wait forever. - - *port*, a ``int``, the port to send the query to. The default is 443. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying the source - address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. The default is - 0. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the - received message. - - *session*, an ``httpx.Client``. If provided, the client session to use to send the - queries. - - *path*, a ``str``. If *where* is an IP address, then *path* will be used to - construct the URL to send the DNS query to. - - *post*, a ``bool``. If ``True``, the default, POST method will be used. - - *bootstrap_address*, a ``str``, the IP address to use to bypass resolution. - - *verify*, a ``bool`` or ``str``. If a ``True``, then TLS certificate verification - of the server is done using the default CA bundle; if ``False``, then no - verification is done; if a `str` then it specifies the path to a certificate file or - directory which will be used for verification. - - *resolver*, a ``dns.resolver.Resolver`` or ``None``, the resolver to use for - resolution of hostnames in URLs. If not specified, a new resolver with a default - configuration will be used; note this is *not* the default resolver as that resolver - might have been configured to use DoH causing a chicken-and-egg problem. This - parameter only has an effect if the HTTP library is httpx. - - *family*, an ``int``, the address family. If socket.AF_UNSPEC (the default), both A - and AAAA records will be retrieved. - - *http_version*, a ``dns.query.HTTPVersion``, indicating which HTTP version to use. - - Returns a ``dns.message.Message``. - """ - - (af, _, the_source) = _destination_and_source( - where, port, source, source_port, False - ) - if af is not None and dns.inet.is_address(where): - if af == socket.AF_INET: - url = f"https://{where}:{port}{path}" - elif af == socket.AF_INET6: - url = f"https://[{where}]:{port}{path}" - else: - url = where - - extensions = {} - if bootstrap_address is None: - # pylint: disable=possibly-used-before-assignment - parsed = urllib.parse.urlparse(url) - if parsed.hostname is None: - raise ValueError("no hostname in URL") - if dns.inet.is_address(parsed.hostname): - bootstrap_address = parsed.hostname - extensions["sni_hostname"] = parsed.hostname - if parsed.port is not None: - port = parsed.port - - if http_version == HTTPVersion.H3 or ( - http_version == HTTPVersion.DEFAULT and not have_doh - ): - if bootstrap_address is None: - resolver = _maybe_get_resolver(resolver) - assert parsed.hostname is not None # for mypy - answers = resolver.resolve_name(parsed.hostname, family) - bootstrap_address = random.choice(list(answers.addresses())) - return _http3( - q, - bootstrap_address, - url, - timeout, - port, - source, - source_port, - one_rr_per_rrset, - ignore_trailing, - verify=verify, - post=post, - ) - - if not have_doh: - raise NoDOH # pragma: no cover - if session and not isinstance(session, httpx.Client): - raise ValueError("session parameter must be an httpx.Client") - - wire = q.to_wire() - headers = {"accept": "application/dns-message"} - - h1 = http_version in (HTTPVersion.H1, HTTPVersion.DEFAULT) - h2 = http_version in (HTTPVersion.H2, HTTPVersion.DEFAULT) - - # set source port and source address - - if the_source is None: - local_address = None - local_port = 0 - else: - local_address = the_source[0] - local_port = the_source[1] - - if session: - cm: contextlib.AbstractContextManager = contextlib.nullcontext(session) - else: - transport = _HTTPTransport( - local_address=local_address, - http1=h1, - http2=h2, - verify=verify, - local_port=local_port, - bootstrap_address=bootstrap_address, - resolver=resolver, - family=family, - ) - - cm = httpx.Client(http1=h1, http2=h2, verify=verify, transport=transport) - with cm as session: - # see https://tools.ietf.org/html/rfc8484#section-4.1.1 for DoH - # GET and POST examples - if post: - headers.update( - { - "content-type": "application/dns-message", - "content-length": str(len(wire)), - } - ) - response = session.post( - url, - headers=headers, - content=wire, - timeout=timeout, - extensions=extensions, - ) - else: - wire = base64.urlsafe_b64encode(wire).rstrip(b"=") - twire = wire.decode() # httpx does a repr() if we give it bytes - response = session.get( - url, - headers=headers, - timeout=timeout, - params={"dns": twire}, - extensions=extensions, - ) - - # see https://tools.ietf.org/html/rfc8484#section-4.2.1 for info about DoH - # status codes - if response.status_code < 200 or response.status_code > 299: - raise ValueError( - f"{where} responded with status code {response.status_code}" - f"\nResponse body: {response.content}" - ) - r = dns.message.from_wire( - response.content, - keyring=q.keyring, - request_mac=q.request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - r.time = response.elapsed.total_seconds() - if not q.is_response(r): - raise BadResponse - return r - - -def _find_header(headers: dns.quic.Headers, name: bytes) -> bytes: - if headers is None: - raise KeyError - for header, value in headers: - if header == name: - return value - raise KeyError - - -def _check_status(headers: dns.quic.Headers, peer: str, wire: bytes) -> None: - value = _find_header(headers, b":status") - if value is None: - raise SyntaxError("no :status header in response") - status = int(value) - if status < 0: - raise SyntaxError("status is negative") - if status < 200 or status > 299: - error = "" - if len(wire) > 0: - try: - error = ": " + wire.decode() - except Exception: - pass - raise ValueError(f"{peer} responded with status code {status}{error}") - - -def _http3( - q: dns.message.Message, - where: str, - url: str, - timeout: Optional[float] = None, - port: int = 853, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - verify: Union[bool, str] = True, - hostname: Optional[str] = None, - post: bool = True, -) -> dns.message.Message: - if not dns.quic.have_quic: - raise NoDOH("DNS-over-HTTP3 is not available.") # pragma: no cover - - url_parts = urllib.parse.urlparse(url) - hostname = url_parts.hostname - if url_parts.port is not None: - port = url_parts.port - - q.id = 0 - wire = q.to_wire() - manager = dns.quic.SyncQuicManager( - verify_mode=verify, server_name=hostname, h3=True - ) - - with manager: - connection = manager.connect(where, port, source, source_port) - (start, expiration) = _compute_times(timeout) - with connection.make_stream(timeout) as stream: - stream.send_h3(url, wire, post) - wire = stream.receive(_remaining(expiration)) - _check_status(stream.headers(), where, wire) - finish = time.time() - r = dns.message.from_wire( - wire, - keyring=q.keyring, - request_mac=q.request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - r.time = max(finish - start, 0.0) - if not q.is_response(r): - raise BadResponse - return r - - -def _udp_recv(sock, max_size, expiration): - """Reads a datagram from the socket. - A Timeout exception will be raised if the operation is not completed - by the expiration time. - """ - while True: - try: - return sock.recvfrom(max_size) - except BlockingIOError: - _wait_for_readable(sock, expiration) - - -def _udp_send(sock, data, destination, expiration): - """Sends the specified datagram to destination over the socket. - A Timeout exception will be raised if the operation is not completed - by the expiration time. - """ - while True: - try: - if destination: - return sock.sendto(data, destination) - else: - return sock.send(data) - except BlockingIOError: # pragma: no cover - _wait_for_writable(sock, expiration) - - -def send_udp( - sock: Any, - what: Union[dns.message.Message, bytes], - destination: Any, - expiration: Optional[float] = None, -) -> Tuple[int, float]: - """Send a DNS message to the specified UDP socket. - - *sock*, a ``socket``. - - *what*, a ``bytes`` or ``dns.message.Message``, the message to send. - - *destination*, a destination tuple appropriate for the address family - of the socket, specifying where to send the query. - - *expiration*, a ``float`` or ``None``, the absolute time at which - a timeout exception should be raised. If ``None``, no timeout will - occur. - - Returns an ``(int, float)`` tuple of bytes sent and the sent time. - """ - - if isinstance(what, dns.message.Message): - what = what.to_wire() - sent_time = time.time() - n = _udp_send(sock, what, destination, expiration) - return (n, sent_time) - - -def receive_udp( - sock: Any, - destination: Optional[Any] = None, - expiration: Optional[float] = None, - ignore_unexpected: bool = False, - one_rr_per_rrset: bool = False, - keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, - request_mac: Optional[bytes] = b"", - ignore_trailing: bool = False, - raise_on_truncation: bool = False, - ignore_errors: bool = False, - query: Optional[dns.message.Message] = None, -) -> Any: - """Read a DNS message from a UDP socket. - - *sock*, a ``socket``. - - *destination*, a destination tuple appropriate for the address family - of the socket, specifying where the message is expected to arrive from. - When receiving a response, this would be where the associated query was - sent. - - *expiration*, a ``float`` or ``None``, the absolute time at which - a timeout exception should be raised. If ``None``, no timeout will - occur. - - *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from - unexpected sources. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own - RRset. - - *keyring*, a ``dict``, the keyring to use for TSIG. - - *request_mac*, a ``bytes`` or ``None``, the MAC of the request (for TSIG). - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing - junk at end of the received message. - - *raise_on_truncation*, a ``bool``. If ``True``, raise an exception if - the TC bit is set. - - Raises if the message is malformed, if network errors occur, of if - there is a timeout. - - If *destination* is not ``None``, returns a ``(dns.message.Message, float)`` - tuple of the received message and the received time. - - If *destination* is ``None``, returns a - ``(dns.message.Message, float, tuple)`` - tuple of the received message, the received time, and the address where - the message arrived from. - - *ignore_errors*, a ``bool``. If various format errors or response - mismatches occur, ignore them and keep listening for a valid response. - The default is ``False``. - - *query*, a ``dns.message.Message`` or ``None``. If not ``None`` and - *ignore_errors* is ``True``, check that the received message is a response - to this query, and if not keep listening for a valid response. - """ - - wire = b"" - while True: - (wire, from_address) = _udp_recv(sock, 65535, expiration) - if not _matches_destination( - sock.family, from_address, destination, ignore_unexpected - ): - continue - received_time = time.time() - try: - r = dns.message.from_wire( - wire, - keyring=keyring, - request_mac=request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - raise_on_truncation=raise_on_truncation, - ) - except dns.message.Truncated as e: - # If we got Truncated and not FORMERR, we at least got the header with TC - # set, and very likely the question section, so we'll re-raise if the - # message seems to be a response as we need to know when truncation happens. - # We need to check that it seems to be a response as we don't want a random - # injected message with TC set to cause us to bail out. - if ( - ignore_errors - and query is not None - and not query.is_response(e.message()) - ): - continue - else: - raise - except Exception: - if ignore_errors: - continue - else: - raise - if ignore_errors and query is not None and not query.is_response(r): - continue - if destination: - return (r, received_time) - else: - return (r, received_time, from_address) - - -def udp( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 53, - source: Optional[str] = None, - source_port: int = 0, - ignore_unexpected: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - raise_on_truncation: bool = False, - sock: Optional[Any] = None, - ignore_errors: bool = False, -) -> dns.message.Message: - """Return the response obtained after sending a query via UDP. - - *q*, a ``dns.message.Message``, the query to send - - *where*, a ``str`` containing an IPv4 or IPv6 address, where - to send the message. - - *timeout*, a ``float`` or ``None``, the number of seconds to wait before the - query times out. If ``None``, the default, wait forever. - - *port*, an ``int``, the port send the message to. The default is 53. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying - the source address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. - The default is 0. - - *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from - unexpected sources. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own - RRset. - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing - junk at end of the received message. - - *raise_on_truncation*, a ``bool``. If ``True``, raise an exception if - the TC bit is set. - - *sock*, a ``socket.socket``, or ``None``, the socket to use for the - query. If ``None``, the default, a socket is created. Note that - if a socket is provided, it must be a nonblocking datagram socket, - and the *source* and *source_port* are ignored. - - *ignore_errors*, a ``bool``. If various format errors or response - mismatches occur, ignore them and keep listening for a valid response. - The default is ``False``. - - Returns a ``dns.message.Message``. - """ - - wire = q.to_wire() - (af, destination, source) = _destination_and_source( - where, port, source, source_port - ) - (begin_time, expiration) = _compute_times(timeout) - if sock: - cm: contextlib.AbstractContextManager = contextlib.nullcontext(sock) - else: - cm = _make_socket(af, socket.SOCK_DGRAM, source) - with cm as s: - send_udp(s, wire, destination, expiration) - (r, received_time) = receive_udp( - s, - destination, - expiration, - ignore_unexpected, - one_rr_per_rrset, - q.keyring, - q.mac, - ignore_trailing, - raise_on_truncation, - ignore_errors, - q, - ) - r.time = received_time - begin_time - # We don't need to check q.is_response() if we are in ignore_errors mode - # as receive_udp() will have checked it. - if not (ignore_errors or q.is_response(r)): - raise BadResponse - return r - assert ( - False # help mypy figure out we can't get here lgtm[py/unreachable-statement] - ) - - -def udp_with_fallback( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 53, - source: Optional[str] = None, - source_port: int = 0, - ignore_unexpected: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - udp_sock: Optional[Any] = None, - tcp_sock: Optional[Any] = None, - ignore_errors: bool = False, -) -> Tuple[dns.message.Message, bool]: - """Return the response to the query, trying UDP first and falling back - to TCP if UDP results in a truncated response. - - *q*, a ``dns.message.Message``, the query to send - - *where*, a ``str`` containing an IPv4 or IPv6 address, where to send the message. - - *timeout*, a ``float`` or ``None``, the number of seconds to wait before the query - times out. If ``None``, the default, wait forever. - - *port*, an ``int``, the port send the message to. The default is 53. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying the source - address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. The default is - 0. - - *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from unexpected - sources. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the - received message. - - *udp_sock*, a ``socket.socket``, or ``None``, the socket to use for the UDP query. - If ``None``, the default, a socket is created. Note that if a socket is provided, - it must be a nonblocking datagram socket, and the *source* and *source_port* are - ignored for the UDP query. - - *tcp_sock*, a ``socket.socket``, or ``None``, the connected socket to use for the - TCP query. If ``None``, the default, a socket is created. Note that if a socket is - provided, it must be a nonblocking connected stream socket, and *where*, *source* - and *source_port* are ignored for the TCP query. - - *ignore_errors*, a ``bool``. If various format errors or response mismatches occur - while listening for UDP, ignore them and keep listening for a valid response. The - default is ``False``. - - Returns a (``dns.message.Message``, tcp) tuple where tcp is ``True`` if and only if - TCP was used. - """ - try: - response = udp( - q, - where, - timeout, - port, - source, - source_port, - ignore_unexpected, - one_rr_per_rrset, - ignore_trailing, - True, - udp_sock, - ignore_errors, - ) - return (response, False) - except dns.message.Truncated: - response = tcp( - q, - where, - timeout, - port, - source, - source_port, - one_rr_per_rrset, - ignore_trailing, - tcp_sock, - ) - return (response, True) - - -def _net_read(sock, count, expiration): - """Read the specified number of bytes from sock. Keep trying until we - either get the desired amount, or we hit EOF. - A Timeout exception will be raised if the operation is not completed - by the expiration time. - """ - s = b"" - while count > 0: - try: - n = sock.recv(count) - if n == b"": - raise EOFError("EOF") - count -= len(n) - s += n - except (BlockingIOError, ssl.SSLWantReadError): - _wait_for_readable(sock, expiration) - except ssl.SSLWantWriteError: # pragma: no cover - _wait_for_writable(sock, expiration) - return s - - -def _net_write(sock, data, expiration): - """Write the specified data to the socket. - A Timeout exception will be raised if the operation is not completed - by the expiration time. - """ - current = 0 - l = len(data) - while current < l: - try: - current += sock.send(data[current:]) - except (BlockingIOError, ssl.SSLWantWriteError): - _wait_for_writable(sock, expiration) - except ssl.SSLWantReadError: # pragma: no cover - _wait_for_readable(sock, expiration) - - -def send_tcp( - sock: Any, - what: Union[dns.message.Message, bytes], - expiration: Optional[float] = None, -) -> Tuple[int, float]: - """Send a DNS message to the specified TCP socket. - - *sock*, a ``socket``. - - *what*, a ``bytes`` or ``dns.message.Message``, the message to send. - - *expiration*, a ``float`` or ``None``, the absolute time at which - a timeout exception should be raised. If ``None``, no timeout will - occur. - - Returns an ``(int, float)`` tuple of bytes sent and the sent time. - """ - - if isinstance(what, dns.message.Message): - tcpmsg = what.to_wire(prepend_length=True) - else: - # copying the wire into tcpmsg is inefficient, but lets us - # avoid writev() or doing a short write that would get pushed - # onto the net - tcpmsg = len(what).to_bytes(2, "big") + what - sent_time = time.time() - _net_write(sock, tcpmsg, expiration) - return (len(tcpmsg), sent_time) - - -def receive_tcp( - sock: Any, - expiration: Optional[float] = None, - one_rr_per_rrset: bool = False, - keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, - request_mac: Optional[bytes] = b"", - ignore_trailing: bool = False, -) -> Tuple[dns.message.Message, float]: - """Read a DNS message from a TCP socket. - - *sock*, a ``socket``. - - *expiration*, a ``float`` or ``None``, the absolute time at which - a timeout exception should be raised. If ``None``, no timeout will - occur. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own - RRset. - - *keyring*, a ``dict``, the keyring to use for TSIG. - - *request_mac*, a ``bytes`` or ``None``, the MAC of the request (for TSIG). - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing - junk at end of the received message. - - Raises if the message is malformed, if network errors occur, of if - there is a timeout. - - Returns a ``(dns.message.Message, float)`` tuple of the received message - and the received time. - """ - - ldata = _net_read(sock, 2, expiration) - (l,) = struct.unpack("!H", ldata) - wire = _net_read(sock, l, expiration) - received_time = time.time() - r = dns.message.from_wire( - wire, - keyring=keyring, - request_mac=request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - return (r, received_time) - - -def _connect(s, address, expiration): - err = s.connect_ex(address) - if err == 0: - return - if err in (errno.EINPROGRESS, errno.EWOULDBLOCK, errno.EALREADY): - _wait_for_writable(s, expiration) - err = s.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) - if err != 0: - raise OSError(err, os.strerror(err)) - - -def tcp( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 53, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - sock: Optional[Any] = None, -) -> dns.message.Message: - """Return the response obtained after sending a query via TCP. - - *q*, a ``dns.message.Message``, the query to send - - *where*, a ``str`` containing an IPv4 or IPv6 address, where - to send the message. - - *timeout*, a ``float`` or ``None``, the number of seconds to wait before the - query times out. If ``None``, the default, wait forever. - - *port*, an ``int``, the port send the message to. The default is 53. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying - the source address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. - The default is 0. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own - RRset. - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing - junk at end of the received message. - - *sock*, a ``socket.socket``, or ``None``, the connected socket to use for the - query. If ``None``, the default, a socket is created. Note that - if a socket is provided, it must be a nonblocking connected stream - socket, and *where*, *port*, *source* and *source_port* are ignored. - - Returns a ``dns.message.Message``. - """ - - wire = q.to_wire() - (begin_time, expiration) = _compute_times(timeout) - if sock: - cm: contextlib.AbstractContextManager = contextlib.nullcontext(sock) - else: - (af, destination, source) = _destination_and_source( - where, port, source, source_port - ) - cm = _make_socket(af, socket.SOCK_STREAM, source) - with cm as s: - if not sock: - # pylint: disable=possibly-used-before-assignment - _connect(s, destination, expiration) - send_tcp(s, wire, expiration) - (r, received_time) = receive_tcp( - s, expiration, one_rr_per_rrset, q.keyring, q.mac, ignore_trailing - ) - r.time = received_time - begin_time - if not q.is_response(r): - raise BadResponse - return r - assert ( - False # help mypy figure out we can't get here lgtm[py/unreachable-statement] - ) - - -def _tls_handshake(s, expiration): - while True: - try: - s.do_handshake() - return - except ssl.SSLWantReadError: - _wait_for_readable(s, expiration) - except ssl.SSLWantWriteError: # pragma: no cover - _wait_for_writable(s, expiration) - - -def _make_dot_ssl_context( - server_hostname: Optional[str], verify: Union[bool, str] -) -> ssl.SSLContext: - cafile: Optional[str] = None - capath: Optional[str] = None - if isinstance(verify, str): - if os.path.isfile(verify): - cafile = verify - elif os.path.isdir(verify): - capath = verify - else: - raise ValueError("invalid verify string") - ssl_context = ssl.create_default_context(cafile=cafile, capath=capath) - ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2 - if server_hostname is None: - ssl_context.check_hostname = False - ssl_context.set_alpn_protocols(["dot"]) - if verify is False: - ssl_context.verify_mode = ssl.CERT_NONE - return ssl_context - - -def tls( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 853, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - sock: Optional[ssl.SSLSocket] = None, - ssl_context: Optional[ssl.SSLContext] = None, - server_hostname: Optional[str] = None, - verify: Union[bool, str] = True, -) -> dns.message.Message: - """Return the response obtained after sending a query via TLS. - - *q*, a ``dns.message.Message``, the query to send - - *where*, a ``str`` containing an IPv4 or IPv6 address, where - to send the message. - - *timeout*, a ``float`` or ``None``, the number of seconds to wait before the - query times out. If ``None``, the default, wait forever. - - *port*, an ``int``, the port send the message to. The default is 853. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying - the source address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. - The default is 0. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own - RRset. - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing - junk at end of the received message. - - *sock*, an ``ssl.SSLSocket``, or ``None``, the socket to use for - the query. If ``None``, the default, a socket is created. Note - that if a socket is provided, it must be a nonblocking connected - SSL stream socket, and *where*, *port*, *source*, *source_port*, - and *ssl_context* are ignored. - - *ssl_context*, an ``ssl.SSLContext``, the context to use when establishing - a TLS connection. If ``None``, the default, creates one with the default - configuration. - - *server_hostname*, a ``str`` containing the server's hostname. The - default is ``None``, which means that no hostname is known, and if an - SSL context is created, hostname checking will be disabled. - - *verify*, a ``bool`` or ``str``. If a ``True``, then TLS certificate verification - of the server is done using the default CA bundle; if ``False``, then no - verification is done; if a `str` then it specifies the path to a certificate file or - directory which will be used for verification. - - Returns a ``dns.message.Message``. - - """ - - if sock: - # - # If a socket was provided, there's no special TLS handling needed. - # - return tcp( - q, - where, - timeout, - port, - source, - source_port, - one_rr_per_rrset, - ignore_trailing, - sock, - ) - - wire = q.to_wire() - (begin_time, expiration) = _compute_times(timeout) - (af, destination, source) = _destination_and_source( - where, port, source, source_port - ) - if ssl_context is None and not sock: - ssl_context = _make_dot_ssl_context(server_hostname, verify) - - with _make_socket( - af, - socket.SOCK_STREAM, - source, - ssl_context=ssl_context, - server_hostname=server_hostname, - ) as s: - _connect(s, destination, expiration) - _tls_handshake(s, expiration) - send_tcp(s, wire, expiration) - (r, received_time) = receive_tcp( - s, expiration, one_rr_per_rrset, q.keyring, q.mac, ignore_trailing - ) - r.time = received_time - begin_time - if not q.is_response(r): - raise BadResponse - return r - assert ( - False # help mypy figure out we can't get here lgtm[py/unreachable-statement] - ) - - -def quic( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 853, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - connection: Optional[dns.quic.SyncQuicConnection] = None, - verify: Union[bool, str] = True, - hostname: Optional[str] = None, - server_hostname: Optional[str] = None, -) -> dns.message.Message: - """Return the response obtained after sending a query via DNS-over-QUIC. - - *q*, a ``dns.message.Message``, the query to send. - - *where*, a ``str``, the nameserver IP address. - - *timeout*, a ``float`` or ``None``, the number of seconds to wait before the query - times out. If ``None``, the default, wait forever. - - *port*, a ``int``, the port to send the query to. The default is 853. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying the source - address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. The default is - 0. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the - received message. - - *connection*, a ``dns.quic.SyncQuicConnection``. If provided, the connection to use - to send the query. - - *verify*, a ``bool`` or ``str``. If a ``True``, then TLS certificate verification - of the server is done using the default CA bundle; if ``False``, then no - verification is done; if a `str` then it specifies the path to a certificate file or - directory which will be used for verification. - - *hostname*, a ``str`` containing the server's hostname or ``None``. The default is - ``None``, which means that no hostname is known, and if an SSL context is created, - hostname checking will be disabled. This value is ignored if *url* is not - ``None``. - - *server_hostname*, a ``str`` or ``None``. This item is for backwards compatibility - only, and has the same meaning as *hostname*. - - Returns a ``dns.message.Message``. - """ - - if not dns.quic.have_quic: - raise NoDOQ("DNS-over-QUIC is not available.") # pragma: no cover - - if server_hostname is not None and hostname is None: - hostname = server_hostname - - q.id = 0 - wire = q.to_wire() - the_connection: dns.quic.SyncQuicConnection - the_manager: dns.quic.SyncQuicManager - if connection: - manager: contextlib.AbstractContextManager = contextlib.nullcontext(None) - the_connection = connection - else: - manager = dns.quic.SyncQuicManager(verify_mode=verify, server_name=hostname) - the_manager = manager # for type checking happiness - - with manager: - if not connection: - the_connection = the_manager.connect(where, port, source, source_port) - (start, expiration) = _compute_times(timeout) - with the_connection.make_stream(timeout) as stream: - stream.send(wire, True) - wire = stream.receive(_remaining(expiration)) - finish = time.time() - r = dns.message.from_wire( - wire, - keyring=q.keyring, - request_mac=q.request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - r.time = max(finish - start, 0.0) - if not q.is_response(r): - raise BadResponse - return r - - -class UDPMode(enum.IntEnum): - """How should UDP be used in an IXFR from :py:func:`inbound_xfr()`? - - NEVER means "never use UDP; always use TCP" - TRY_FIRST means "try to use UDP but fall back to TCP if needed" - ONLY means "raise ``dns.xfr.UseTCP`` if trying UDP does not succeed" - """ - - NEVER = 0 - TRY_FIRST = 1 - ONLY = 2 - - -def _inbound_xfr( - txn_manager: dns.transaction.TransactionManager, - s: socket.socket, - query: dns.message.Message, - serial: Optional[int], - timeout: Optional[float], - expiration: float, -) -> Any: - """Given a socket, does the zone transfer.""" - rdtype = query.question[0].rdtype - is_ixfr = rdtype == dns.rdatatype.IXFR - origin = txn_manager.from_wire_origin() - wire = query.to_wire() - is_udp = s.type == socket.SOCK_DGRAM - if is_udp: - _udp_send(s, wire, None, expiration) - else: - tcpmsg = struct.pack("!H", len(wire)) + wire - _net_write(s, tcpmsg, expiration) - with dns.xfr.Inbound(txn_manager, rdtype, serial, is_udp) as inbound: - done = False - tsig_ctx = None - while not done: - (_, mexpiration) = _compute_times(timeout) - if mexpiration is None or ( - expiration is not None and mexpiration > expiration - ): - mexpiration = expiration - if is_udp: - (rwire, _) = _udp_recv(s, 65535, mexpiration) - else: - ldata = _net_read(s, 2, mexpiration) - (l,) = struct.unpack("!H", ldata) - rwire = _net_read(s, l, mexpiration) - r = dns.message.from_wire( - rwire, - keyring=query.keyring, - request_mac=query.mac, - xfr=True, - origin=origin, - tsig_ctx=tsig_ctx, - multi=(not is_udp), - one_rr_per_rrset=is_ixfr, - ) - done = inbound.process_message(r) - yield r - tsig_ctx = r.tsig_ctx - if query.keyring and not r.had_tsig: - raise dns.exception.FormError("missing TSIG") - - -def xfr( - where: str, - zone: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.AXFR, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - timeout: Optional[float] = None, - port: int = 53, - keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, - keyname: Optional[Union[dns.name.Name, str]] = None, - relativize: bool = True, - lifetime: Optional[float] = None, - source: Optional[str] = None, - source_port: int = 0, - serial: int = 0, - use_udp: bool = False, - keyalgorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, -) -> Any: - """Return a generator for the responses to a zone transfer. - - *where*, a ``str`` containing an IPv4 or IPv6 address, where - to send the message. - - *zone*, a ``dns.name.Name`` or ``str``, the name of the zone to transfer. - - *rdtype*, an ``int`` or ``str``, the type of zone transfer. The - default is ``dns.rdatatype.AXFR``. ``dns.rdatatype.IXFR`` can be - used to do an incremental transfer instead. - - *rdclass*, an ``int`` or ``str``, the class of the zone transfer. - The default is ``dns.rdataclass.IN``. - - *timeout*, a ``float``, the number of seconds to wait for each - response message. If None, the default, wait forever. - - *port*, an ``int``, the port send the message to. The default is 53. - - *keyring*, a ``dict``, the keyring to use for TSIG. - - *keyname*, a ``dns.name.Name`` or ``str``, the name of the TSIG - key to use. - - *relativize*, a ``bool``. If ``True``, all names in the zone will be - relativized to the zone origin. It is essential that the - relativize setting matches the one specified to - ``dns.zone.from_xfr()`` if using this generator to make a zone. - - *lifetime*, a ``float``, the total number of seconds to spend - doing the transfer. If ``None``, the default, then there is no - limit on the time the transfer may take. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying - the source address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. - The default is 0. - - *serial*, an ``int``, the SOA serial number to use as the base for - an IXFR diff sequence (only meaningful if *rdtype* is - ``dns.rdatatype.IXFR``). - - *use_udp*, a ``bool``. If ``True``, use UDP (only meaningful for IXFR). - - *keyalgorithm*, a ``dns.name.Name`` or ``str``, the TSIG algorithm to use. - - Raises on errors, and so does the generator. - - Returns a generator of ``dns.message.Message`` objects. - """ - - class DummyTransactionManager(dns.transaction.TransactionManager): - def __init__(self, origin, relativize): - self.info = (origin, relativize, dns.name.empty if relativize else origin) - - def origin_information(self): - return self.info - - def get_class(self) -> dns.rdataclass.RdataClass: - raise NotImplementedError # pragma: no cover - - def reader(self): - raise NotImplementedError # pragma: no cover - - def writer(self, replacement: bool = False) -> dns.transaction.Transaction: - class DummyTransaction: - def nop(self, *args, **kw): - pass - - def __getattr__(self, _): - return self.nop - - return cast(dns.transaction.Transaction, DummyTransaction()) - - if isinstance(zone, str): - zone = dns.name.from_text(zone) - rdtype = dns.rdatatype.RdataType.make(rdtype) - q = dns.message.make_query(zone, rdtype, rdclass) - if rdtype == dns.rdatatype.IXFR: - rrset = q.find_rrset( - q.authority, zone, dns.rdataclass.IN, dns.rdatatype.SOA, create=True - ) - soa = dns.rdata.from_text("IN", "SOA", ". . %u 0 0 0 0" % serial) - rrset.add(soa, 0) - if keyring is not None: - q.use_tsig(keyring, keyname, algorithm=keyalgorithm) - (af, destination, source) = _destination_and_source( - where, port, source, source_port - ) - (_, expiration) = _compute_times(lifetime) - tm = DummyTransactionManager(zone, relativize) - if use_udp and rdtype != dns.rdatatype.IXFR: - raise ValueError("cannot do a UDP AXFR") - sock_type = socket.SOCK_DGRAM if use_udp else socket.SOCK_STREAM - with _make_socket(af, sock_type, source) as s: - _connect(s, destination, expiration) - yield from _inbound_xfr(tm, s, q, serial, timeout, expiration) - - -def inbound_xfr( - where: str, - txn_manager: dns.transaction.TransactionManager, - query: Optional[dns.message.Message] = None, - port: int = 53, - timeout: Optional[float] = None, - lifetime: Optional[float] = None, - source: Optional[str] = None, - source_port: int = 0, - udp_mode: UDPMode = UDPMode.NEVER, -) -> None: - """Conduct an inbound transfer and apply it via a transaction from the - txn_manager. - - *where*, a ``str`` containing an IPv4 or IPv6 address, where - to send the message. - - *txn_manager*, a ``dns.transaction.TransactionManager``, the txn_manager - for this transfer (typically a ``dns.zone.Zone``). - - *query*, the query to send. If not supplied, a default query is - constructed using information from the *txn_manager*. - - *port*, an ``int``, the port send the message to. The default is 53. - - *timeout*, a ``float``, the number of seconds to wait for each - response message. If None, the default, wait forever. - - *lifetime*, a ``float``, the total number of seconds to spend - doing the transfer. If ``None``, the default, then there is no - limit on the time the transfer may take. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying - the source address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. - The default is 0. - - *udp_mode*, a ``dns.query.UDPMode``, determines how UDP is used - for IXFRs. The default is ``dns.UDPMode.NEVER``, i.e. only use - TCP. Other possibilities are ``dns.UDPMode.TRY_FIRST``, which - means "try UDP but fallback to TCP if needed", and - ``dns.UDPMode.ONLY``, which means "try UDP and raise - ``dns.xfr.UseTCP`` if it does not succeed. - - Raises on errors. - """ - if query is None: - (query, serial) = dns.xfr.make_query(txn_manager) - else: - serial = dns.xfr.extract_serial_from_query(query) - - (af, destination, source) = _destination_and_source( - where, port, source, source_port - ) - (_, expiration) = _compute_times(lifetime) - if query.question[0].rdtype == dns.rdatatype.IXFR and udp_mode != UDPMode.NEVER: - with _make_socket(af, socket.SOCK_DGRAM, source) as s: - _connect(s, destination, expiration) - try: - for _ in _inbound_xfr( - txn_manager, s, query, serial, timeout, expiration - ): - pass - return - except dns.xfr.UseTCP: - if udp_mode == UDPMode.ONLY: - raise - - with _make_socket(af, socket.SOCK_STREAM, source) as s: - _connect(s, destination, expiration) - for _ in _inbound_xfr(txn_manager, s, query, serial, timeout, expiration): - pass diff --git a/venv/Lib/site-packages/dns/quic/__init__.py b/venv/Lib/site-packages/dns/quic/__init__.py deleted file mode 100644 index 0750e72..0000000 --- a/venv/Lib/site-packages/dns/quic/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -from typing import List, Tuple - -import dns._features -import dns.asyncbackend - -if dns._features.have("doq"): - import aioquic.quic.configuration # type: ignore - - from dns._asyncbackend import NullContext - from dns.quic._asyncio import ( - AsyncioQuicConnection, - AsyncioQuicManager, - AsyncioQuicStream, - ) - from dns.quic._common import AsyncQuicConnection, AsyncQuicManager - from dns.quic._sync import SyncQuicConnection, SyncQuicManager, SyncQuicStream - - have_quic = True - - def null_factory( - *args, # pylint: disable=unused-argument - **kwargs, # pylint: disable=unused-argument - ): - return NullContext(None) - - def _asyncio_manager_factory( - context, *args, **kwargs # pylint: disable=unused-argument - ): - return AsyncioQuicManager(*args, **kwargs) - - # We have a context factory and a manager factory as for trio we need to have - # a nursery. - - _async_factories = {"asyncio": (null_factory, _asyncio_manager_factory)} - - if dns._features.have("trio"): - import trio - - from dns.quic._trio import ( # pylint: disable=ungrouped-imports - TrioQuicConnection, - TrioQuicManager, - TrioQuicStream, - ) - - def _trio_context_factory(): - return trio.open_nursery() - - def _trio_manager_factory(context, *args, **kwargs): - return TrioQuicManager(context, *args, **kwargs) - - _async_factories["trio"] = (_trio_context_factory, _trio_manager_factory) - - def factories_for_backend(backend=None): - if backend is None: - backend = dns.asyncbackend.get_default_backend() - return _async_factories[backend.name()] - -else: # pragma: no cover - have_quic = False - - from typing import Any - - class AsyncQuicStream: # type: ignore - pass - - class AsyncQuicConnection: # type: ignore - async def make_stream(self) -> Any: - raise NotImplementedError - - class SyncQuicStream: # type: ignore - pass - - class SyncQuicConnection: # type: ignore - def make_stream(self) -> Any: - raise NotImplementedError - - -Headers = List[Tuple[bytes, bytes]] diff --git a/venv/Lib/site-packages/dns/quic/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/dns/quic/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index e9c7bdd..0000000 Binary files a/venv/Lib/site-packages/dns/quic/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/quic/__pycache__/_asyncio.cpython-310.pyc b/venv/Lib/site-packages/dns/quic/__pycache__/_asyncio.cpython-310.pyc deleted file mode 100644 index c0ddb11..0000000 Binary files a/venv/Lib/site-packages/dns/quic/__pycache__/_asyncio.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/quic/__pycache__/_common.cpython-310.pyc b/venv/Lib/site-packages/dns/quic/__pycache__/_common.cpython-310.pyc deleted file mode 100644 index d0ca700..0000000 Binary files a/venv/Lib/site-packages/dns/quic/__pycache__/_common.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/quic/__pycache__/_sync.cpython-310.pyc b/venv/Lib/site-packages/dns/quic/__pycache__/_sync.cpython-310.pyc deleted file mode 100644 index b4f4d01..0000000 Binary files a/venv/Lib/site-packages/dns/quic/__pycache__/_sync.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/quic/__pycache__/_trio.cpython-310.pyc b/venv/Lib/site-packages/dns/quic/__pycache__/_trio.cpython-310.pyc deleted file mode 100644 index 9a5ed4b..0000000 Binary files a/venv/Lib/site-packages/dns/quic/__pycache__/_trio.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/quic/_asyncio.py b/venv/Lib/site-packages/dns/quic/_asyncio.py deleted file mode 100644 index f87515d..0000000 --- a/venv/Lib/site-packages/dns/quic/_asyncio.py +++ /dev/null @@ -1,267 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import asyncio -import socket -import ssl -import struct -import time - -import aioquic.quic.configuration # type: ignore -import aioquic.quic.connection # type: ignore -import aioquic.quic.events # type: ignore - -import dns.asyncbackend -import dns.exception -import dns.inet -from dns.quic._common import ( - QUIC_MAX_DATAGRAM, - AsyncQuicConnection, - AsyncQuicManager, - BaseQuicStream, - UnexpectedEOF, -) - - -class AsyncioQuicStream(BaseQuicStream): - def __init__(self, connection, stream_id): - super().__init__(connection, stream_id) - self._wake_up = asyncio.Condition() - - async def _wait_for_wake_up(self): - async with self._wake_up: - await self._wake_up.wait() - - async def wait_for(self, amount, expiration): - while True: - timeout = self._timeout_from_expiration(expiration) - if self._buffer.have(amount): - return - self._expecting = amount - try: - await asyncio.wait_for(self._wait_for_wake_up(), timeout) - except TimeoutError: - raise dns.exception.Timeout - self._expecting = 0 - - async def wait_for_end(self, expiration): - while True: - timeout = self._timeout_from_expiration(expiration) - if self._buffer.seen_end(): - return - try: - await asyncio.wait_for(self._wait_for_wake_up(), timeout) - except TimeoutError: - raise dns.exception.Timeout - - async def receive(self, timeout=None): - expiration = self._expiration_from_timeout(timeout) - if self._connection.is_h3(): - await self.wait_for_end(expiration) - return self._buffer.get_all() - else: - await self.wait_for(2, expiration) - (size,) = struct.unpack("!H", self._buffer.get(2)) - await self.wait_for(size, expiration) - return self._buffer.get(size) - - async def send(self, datagram, is_end=False): - data = self._encapsulate(datagram) - await self._connection.write(self._stream_id, data, is_end) - - async def _add_input(self, data, is_end): - if self._common_add_input(data, is_end): - async with self._wake_up: - self._wake_up.notify() - - async def close(self): - self._close() - - # Streams are async context managers - - async def __aenter__(self): - return self - - async def __aexit__(self, exc_type, exc_val, exc_tb): - await self.close() - async with self._wake_up: - self._wake_up.notify() - return False - - -class AsyncioQuicConnection(AsyncQuicConnection): - def __init__(self, connection, address, port, source, source_port, manager=None): - super().__init__(connection, address, port, source, source_port, manager) - self._socket = None - self._handshake_complete = asyncio.Event() - self._socket_created = asyncio.Event() - self._wake_timer = asyncio.Condition() - self._receiver_task = None - self._sender_task = None - self._wake_pending = False - - async def _receiver(self): - try: - af = dns.inet.af_for_address(self._address) - backend = dns.asyncbackend.get_backend("asyncio") - # Note that peer is a low-level address tuple, but make_socket() wants - # a high-level address tuple, so we convert. - self._socket = await backend.make_socket( - af, socket.SOCK_DGRAM, 0, self._source, (self._peer[0], self._peer[1]) - ) - self._socket_created.set() - async with self._socket: - while not self._done: - (datagram, address) = await self._socket.recvfrom( - QUIC_MAX_DATAGRAM, None - ) - if address[0] != self._peer[0] or address[1] != self._peer[1]: - continue - self._connection.receive_datagram(datagram, address, time.time()) - # Wake up the timer in case the sender is sleeping, as there may be - # stuff to send now. - await self._wakeup() - except Exception: - pass - finally: - self._done = True - await self._wakeup() - self._handshake_complete.set() - - async def _wakeup(self): - self._wake_pending = True - async with self._wake_timer: - self._wake_timer.notify_all() - - async def _wait_for_wake_timer(self): - async with self._wake_timer: - if not self._wake_pending: - await self._wake_timer.wait() - self._wake_pending = False - - async def _sender(self): - await self._socket_created.wait() - while not self._done: - datagrams = self._connection.datagrams_to_send(time.time()) - for datagram, address in datagrams: - assert address == self._peer - await self._socket.sendto(datagram, self._peer, None) - (expiration, interval) = self._get_timer_values() - try: - await asyncio.wait_for(self._wait_for_wake_timer(), interval) - except Exception: - pass - self._handle_timer(expiration) - await self._handle_events() - - async def _handle_events(self): - count = 0 - while True: - event = self._connection.next_event() - if event is None: - return - if isinstance(event, aioquic.quic.events.StreamDataReceived): - if self.is_h3(): - h3_events = self._h3_conn.handle_event(event) - for h3_event in h3_events: - if isinstance(h3_event, aioquic.h3.events.HeadersReceived): - stream = self._streams.get(event.stream_id) - if stream: - if stream._headers is None: - stream._headers = h3_event.headers - elif stream._trailers is None: - stream._trailers = h3_event.headers - if h3_event.stream_ended: - await stream._add_input(b"", True) - elif isinstance(h3_event, aioquic.h3.events.DataReceived): - stream = self._streams.get(event.stream_id) - if stream: - await stream._add_input( - h3_event.data, h3_event.stream_ended - ) - else: - stream = self._streams.get(event.stream_id) - if stream: - await stream._add_input(event.data, event.end_stream) - elif isinstance(event, aioquic.quic.events.HandshakeCompleted): - self._handshake_complete.set() - elif isinstance(event, aioquic.quic.events.ConnectionTerminated): - self._done = True - self._receiver_task.cancel() - elif isinstance(event, aioquic.quic.events.StreamReset): - stream = self._streams.get(event.stream_id) - if stream: - await stream._add_input(b"", True) - - count += 1 - if count > 10: - # yield - count = 0 - await asyncio.sleep(0) - - async def write(self, stream, data, is_end=False): - self._connection.send_stream_data(stream, data, is_end) - await self._wakeup() - - def run(self): - if self._closed: - return - self._receiver_task = asyncio.Task(self._receiver()) - self._sender_task = asyncio.Task(self._sender()) - - async def make_stream(self, timeout=None): - try: - await asyncio.wait_for(self._handshake_complete.wait(), timeout) - except TimeoutError: - raise dns.exception.Timeout - if self._done: - raise UnexpectedEOF - stream_id = self._connection.get_next_available_stream_id(False) - stream = AsyncioQuicStream(self, stream_id) - self._streams[stream_id] = stream - return stream - - async def close(self): - if not self._closed: - self._manager.closed(self._peer[0], self._peer[1]) - self._closed = True - self._connection.close() - # sender might be blocked on this, so set it - self._socket_created.set() - await self._wakeup() - try: - await self._receiver_task - except asyncio.CancelledError: - pass - try: - await self._sender_task - except asyncio.CancelledError: - pass - await self._socket.close() - - -class AsyncioQuicManager(AsyncQuicManager): - def __init__( - self, conf=None, verify_mode=ssl.CERT_REQUIRED, server_name=None, h3=False - ): - super().__init__(conf, verify_mode, AsyncioQuicConnection, server_name, h3) - - def connect( - self, address, port=853, source=None, source_port=0, want_session_ticket=True - ): - (connection, start) = self._connect( - address, port, source, source_port, want_session_ticket - ) - if start: - connection.run() - return connection - - async def __aenter__(self): - return self - - async def __aexit__(self, exc_type, exc_val, exc_tb): - # Copy the iterator into a list as exiting things will mutate the connections - # table. - connections = list(self._connections.values()) - for connection in connections: - await connection.close() - return False diff --git a/venv/Lib/site-packages/dns/quic/_common.py b/venv/Lib/site-packages/dns/quic/_common.py deleted file mode 100644 index ce575b0..0000000 --- a/venv/Lib/site-packages/dns/quic/_common.py +++ /dev/null @@ -1,339 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import base64 -import copy -import functools -import socket -import struct -import time -import urllib -from typing import Any, Optional - -import aioquic.h3.connection # type: ignore -import aioquic.h3.events # type: ignore -import aioquic.quic.configuration # type: ignore -import aioquic.quic.connection # type: ignore - -import dns.inet - -QUIC_MAX_DATAGRAM = 2048 -MAX_SESSION_TICKETS = 8 -# If we hit the max sessions limit we will delete this many of the oldest connections. -# The value must be a integer > 0 and <= MAX_SESSION_TICKETS. -SESSIONS_TO_DELETE = MAX_SESSION_TICKETS // 4 - - -class UnexpectedEOF(Exception): - pass - - -class Buffer: - def __init__(self): - self._buffer = b"" - self._seen_end = False - - def put(self, data, is_end): - if self._seen_end: - return - self._buffer += data - if is_end: - self._seen_end = True - - def have(self, amount): - if len(self._buffer) >= amount: - return True - if self._seen_end: - raise UnexpectedEOF - return False - - def seen_end(self): - return self._seen_end - - def get(self, amount): - assert self.have(amount) - data = self._buffer[:amount] - self._buffer = self._buffer[amount:] - return data - - def get_all(self): - assert self.seen_end() - data = self._buffer - self._buffer = b"" - return data - - -class BaseQuicStream: - def __init__(self, connection, stream_id): - self._connection = connection - self._stream_id = stream_id - self._buffer = Buffer() - self._expecting = 0 - self._headers = None - self._trailers = None - - def id(self): - return self._stream_id - - def headers(self): - return self._headers - - def trailers(self): - return self._trailers - - def _expiration_from_timeout(self, timeout): - if timeout is not None: - expiration = time.time() + timeout - else: - expiration = None - return expiration - - def _timeout_from_expiration(self, expiration): - if expiration is not None: - timeout = max(expiration - time.time(), 0.0) - else: - timeout = None - return timeout - - # Subclass must implement receive() as sync / async and which returns a message - # or raises. - - # Subclass must implement send() as sync / async and which takes a message and - # an EOF indicator. - - def send_h3(self, url, datagram, post=True): - if not self._connection.is_h3(): - raise SyntaxError("cannot send H3 to a non-H3 connection") - url_parts = urllib.parse.urlparse(url) - path = url_parts.path.encode() - if post: - method = b"POST" - else: - method = b"GET" - path += b"?dns=" + base64.urlsafe_b64encode(datagram).rstrip(b"=") - headers = [ - (b":method", method), - (b":scheme", url_parts.scheme.encode()), - (b":authority", url_parts.netloc.encode()), - (b":path", path), - (b"accept", b"application/dns-message"), - ] - if post: - headers.extend( - [ - (b"content-type", b"application/dns-message"), - (b"content-length", str(len(datagram)).encode()), - ] - ) - self._connection.send_headers(self._stream_id, headers, not post) - if post: - self._connection.send_data(self._stream_id, datagram, True) - - def _encapsulate(self, datagram): - if self._connection.is_h3(): - return datagram - l = len(datagram) - return struct.pack("!H", l) + datagram - - def _common_add_input(self, data, is_end): - self._buffer.put(data, is_end) - try: - return ( - self._expecting > 0 and self._buffer.have(self._expecting) - ) or self._buffer.seen_end - except UnexpectedEOF: - return True - - def _close(self): - self._connection.close_stream(self._stream_id) - self._buffer.put(b"", True) # send EOF in case we haven't seen it. - - -class BaseQuicConnection: - def __init__( - self, - connection, - address, - port, - source=None, - source_port=0, - manager=None, - ): - self._done = False - self._connection = connection - self._address = address - self._port = port - self._closed = False - self._manager = manager - self._streams = {} - if manager.is_h3(): - self._h3_conn = aioquic.h3.connection.H3Connection(connection, False) - else: - self._h3_conn = None - self._af = dns.inet.af_for_address(address) - self._peer = dns.inet.low_level_address_tuple((address, port)) - if source is None and source_port != 0: - if self._af == socket.AF_INET: - source = "0.0.0.0" - elif self._af == socket.AF_INET6: - source = "::" - else: - raise NotImplementedError - if source: - self._source = (source, source_port) - else: - self._source = None - - def is_h3(self): - return self._h3_conn is not None - - def close_stream(self, stream_id): - del self._streams[stream_id] - - def send_headers(self, stream_id, headers, is_end=False): - self._h3_conn.send_headers(stream_id, headers, is_end) - - def send_data(self, stream_id, data, is_end=False): - self._h3_conn.send_data(stream_id, data, is_end) - - def _get_timer_values(self, closed_is_special=True): - now = time.time() - expiration = self._connection.get_timer() - if expiration is None: - expiration = now + 3600 # arbitrary "big" value - interval = max(expiration - now, 0) - if self._closed and closed_is_special: - # lower sleep interval to avoid a race in the closing process - # which can lead to higher latency closing due to sleeping when - # we have events. - interval = min(interval, 0.05) - return (expiration, interval) - - def _handle_timer(self, expiration): - now = time.time() - if expiration <= now: - self._connection.handle_timer(now) - - -class AsyncQuicConnection(BaseQuicConnection): - async def make_stream(self, timeout: Optional[float] = None) -> Any: - pass - - -class BaseQuicManager: - def __init__( - self, conf, verify_mode, connection_factory, server_name=None, h3=False - ): - self._connections = {} - self._connection_factory = connection_factory - self._session_tickets = {} - self._tokens = {} - self._h3 = h3 - if conf is None: - verify_path = None - if isinstance(verify_mode, str): - verify_path = verify_mode - verify_mode = True - if h3: - alpn_protocols = ["h3"] - else: - alpn_protocols = ["doq", "doq-i03"] - conf = aioquic.quic.configuration.QuicConfiguration( - alpn_protocols=alpn_protocols, - verify_mode=verify_mode, - server_name=server_name, - ) - if verify_path is not None: - conf.load_verify_locations(verify_path) - self._conf = conf - - def _connect( - self, - address, - port=853, - source=None, - source_port=0, - want_session_ticket=True, - want_token=True, - ): - connection = self._connections.get((address, port)) - if connection is not None: - return (connection, False) - conf = self._conf - if want_session_ticket: - try: - session_ticket = self._session_tickets.pop((address, port)) - # We found a session ticket, so make a configuration that uses it. - conf = copy.copy(conf) - conf.session_ticket = session_ticket - except KeyError: - # No session ticket. - pass - # Whether or not we found a session ticket, we want a handler to save - # one. - session_ticket_handler = functools.partial( - self.save_session_ticket, address, port - ) - else: - session_ticket_handler = None - if want_token: - try: - token = self._tokens.pop((address, port)) - # We found a token, so make a configuration that uses it. - conf = copy.copy(conf) - conf.token = token - except KeyError: - # No token - pass - # Whether or not we found a token, we want a handler to save # one. - token_handler = functools.partial(self.save_token, address, port) - else: - token_handler = None - - qconn = aioquic.quic.connection.QuicConnection( - configuration=conf, - session_ticket_handler=session_ticket_handler, - token_handler=token_handler, - ) - lladdress = dns.inet.low_level_address_tuple((address, port)) - qconn.connect(lladdress, time.time()) - connection = self._connection_factory( - qconn, address, port, source, source_port, self - ) - self._connections[(address, port)] = connection - return (connection, True) - - def closed(self, address, port): - try: - del self._connections[(address, port)] - except KeyError: - pass - - def is_h3(self): - return self._h3 - - def save_session_ticket(self, address, port, ticket): - # We rely on dictionaries keys() being in insertion order here. We - # can't just popitem() as that would be LIFO which is the opposite of - # what we want. - l = len(self._session_tickets) - if l >= MAX_SESSION_TICKETS: - keys_to_delete = list(self._session_tickets.keys())[0:SESSIONS_TO_DELETE] - for key in keys_to_delete: - del self._session_tickets[key] - self._session_tickets[(address, port)] = ticket - - def save_token(self, address, port, token): - # We rely on dictionaries keys() being in insertion order here. We - # can't just popitem() as that would be LIFO which is the opposite of - # what we want. - l = len(self._tokens) - if l >= MAX_SESSION_TICKETS: - keys_to_delete = list(self._tokens.keys())[0:SESSIONS_TO_DELETE] - for key in keys_to_delete: - del self._tokens[key] - self._tokens[(address, port)] = token - - -class AsyncQuicManager(BaseQuicManager): - def connect(self, address, port=853, source=None, source_port=0): - raise NotImplementedError diff --git a/venv/Lib/site-packages/dns/quic/_sync.py b/venv/Lib/site-packages/dns/quic/_sync.py deleted file mode 100644 index 473d1f4..0000000 --- a/venv/Lib/site-packages/dns/quic/_sync.py +++ /dev/null @@ -1,295 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import selectors -import socket -import ssl -import struct -import threading -import time - -import aioquic.quic.configuration # type: ignore -import aioquic.quic.connection # type: ignore -import aioquic.quic.events # type: ignore - -import dns.exception -import dns.inet -from dns.quic._common import ( - QUIC_MAX_DATAGRAM, - BaseQuicConnection, - BaseQuicManager, - BaseQuicStream, - UnexpectedEOF, -) - -# Function used to create a socket. Can be overridden if needed in special -# situations. -socket_factory = socket.socket - - -class SyncQuicStream(BaseQuicStream): - def __init__(self, connection, stream_id): - super().__init__(connection, stream_id) - self._wake_up = threading.Condition() - self._lock = threading.Lock() - - def wait_for(self, amount, expiration): - while True: - timeout = self._timeout_from_expiration(expiration) - with self._lock: - if self._buffer.have(amount): - return - self._expecting = amount - with self._wake_up: - if not self._wake_up.wait(timeout): - raise dns.exception.Timeout - self._expecting = 0 - - def wait_for_end(self, expiration): - while True: - timeout = self._timeout_from_expiration(expiration) - with self._lock: - if self._buffer.seen_end(): - return - with self._wake_up: - if not self._wake_up.wait(timeout): - raise dns.exception.Timeout - - def receive(self, timeout=None): - expiration = self._expiration_from_timeout(timeout) - if self._connection.is_h3(): - self.wait_for_end(expiration) - with self._lock: - return self._buffer.get_all() - else: - self.wait_for(2, expiration) - with self._lock: - (size,) = struct.unpack("!H", self._buffer.get(2)) - self.wait_for(size, expiration) - with self._lock: - return self._buffer.get(size) - - def send(self, datagram, is_end=False): - data = self._encapsulate(datagram) - self._connection.write(self._stream_id, data, is_end) - - def _add_input(self, data, is_end): - if self._common_add_input(data, is_end): - with self._wake_up: - self._wake_up.notify() - - def close(self): - with self._lock: - self._close() - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - with self._wake_up: - self._wake_up.notify() - return False - - -class SyncQuicConnection(BaseQuicConnection): - def __init__(self, connection, address, port, source, source_port, manager): - super().__init__(connection, address, port, source, source_port, manager) - self._socket = socket_factory(self._af, socket.SOCK_DGRAM, 0) - if self._source is not None: - try: - self._socket.bind( - dns.inet.low_level_address_tuple(self._source, self._af) - ) - except Exception: - self._socket.close() - raise - self._socket.connect(self._peer) - (self._send_wakeup, self._receive_wakeup) = socket.socketpair() - self._receive_wakeup.setblocking(False) - self._socket.setblocking(False) - self._handshake_complete = threading.Event() - self._worker_thread = None - self._lock = threading.Lock() - - def _read(self): - count = 0 - while count < 10: - count += 1 - try: - datagram = self._socket.recv(QUIC_MAX_DATAGRAM) - except BlockingIOError: - return - with self._lock: - self._connection.receive_datagram(datagram, self._peer, time.time()) - - def _drain_wakeup(self): - while True: - try: - self._receive_wakeup.recv(32) - except BlockingIOError: - return - - def _worker(self): - try: - sel = selectors.DefaultSelector() - sel.register(self._socket, selectors.EVENT_READ, self._read) - sel.register(self._receive_wakeup, selectors.EVENT_READ, self._drain_wakeup) - while not self._done: - (expiration, interval) = self._get_timer_values(False) - items = sel.select(interval) - for key, _ in items: - key.data() - with self._lock: - self._handle_timer(expiration) - self._handle_events() - with self._lock: - datagrams = self._connection.datagrams_to_send(time.time()) - for datagram, _ in datagrams: - try: - self._socket.send(datagram) - except BlockingIOError: - # we let QUIC handle any lossage - pass - finally: - with self._lock: - self._done = True - self._socket.close() - # Ensure anyone waiting for this gets woken up. - self._handshake_complete.set() - - def _handle_events(self): - while True: - with self._lock: - event = self._connection.next_event() - if event is None: - return - if isinstance(event, aioquic.quic.events.StreamDataReceived): - if self.is_h3(): - h3_events = self._h3_conn.handle_event(event) - for h3_event in h3_events: - if isinstance(h3_event, aioquic.h3.events.HeadersReceived): - with self._lock: - stream = self._streams.get(event.stream_id) - if stream: - if stream._headers is None: - stream._headers = h3_event.headers - elif stream._trailers is None: - stream._trailers = h3_event.headers - if h3_event.stream_ended: - stream._add_input(b"", True) - elif isinstance(h3_event, aioquic.h3.events.DataReceived): - with self._lock: - stream = self._streams.get(event.stream_id) - if stream: - stream._add_input(h3_event.data, h3_event.stream_ended) - else: - with self._lock: - stream = self._streams.get(event.stream_id) - if stream: - stream._add_input(event.data, event.end_stream) - elif isinstance(event, aioquic.quic.events.HandshakeCompleted): - self._handshake_complete.set() - elif isinstance(event, aioquic.quic.events.ConnectionTerminated): - with self._lock: - self._done = True - elif isinstance(event, aioquic.quic.events.StreamReset): - with self._lock: - stream = self._streams.get(event.stream_id) - if stream: - stream._add_input(b"", True) - - def write(self, stream, data, is_end=False): - with self._lock: - self._connection.send_stream_data(stream, data, is_end) - self._send_wakeup.send(b"\x01") - - def send_headers(self, stream_id, headers, is_end=False): - with self._lock: - super().send_headers(stream_id, headers, is_end) - if is_end: - self._send_wakeup.send(b"\x01") - - def send_data(self, stream_id, data, is_end=False): - with self._lock: - super().send_data(stream_id, data, is_end) - if is_end: - self._send_wakeup.send(b"\x01") - - def run(self): - if self._closed: - return - self._worker_thread = threading.Thread(target=self._worker) - self._worker_thread.start() - - def make_stream(self, timeout=None): - if not self._handshake_complete.wait(timeout): - raise dns.exception.Timeout - with self._lock: - if self._done: - raise UnexpectedEOF - stream_id = self._connection.get_next_available_stream_id(False) - stream = SyncQuicStream(self, stream_id) - self._streams[stream_id] = stream - return stream - - def close_stream(self, stream_id): - with self._lock: - super().close_stream(stream_id) - - def close(self): - with self._lock: - if self._closed: - return - self._manager.closed(self._peer[0], self._peer[1]) - self._closed = True - self._connection.close() - self._send_wakeup.send(b"\x01") - self._worker_thread.join() - - -class SyncQuicManager(BaseQuicManager): - def __init__( - self, conf=None, verify_mode=ssl.CERT_REQUIRED, server_name=None, h3=False - ): - super().__init__(conf, verify_mode, SyncQuicConnection, server_name, h3) - self._lock = threading.Lock() - - def connect( - self, - address, - port=853, - source=None, - source_port=0, - want_session_ticket=True, - want_token=True, - ): - with self._lock: - (connection, start) = self._connect( - address, port, source, source_port, want_session_ticket, want_token - ) - if start: - connection.run() - return connection - - def closed(self, address, port): - with self._lock: - super().closed(address, port) - - def save_session_ticket(self, address, port, ticket): - with self._lock: - super().save_session_ticket(address, port, ticket) - - def save_token(self, address, port, token): - with self._lock: - super().save_token(address, port, token) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - # Copy the iterator into a list as exiting things will mutate the connections - # table. - connections = list(self._connections.values()) - for connection in connections: - connection.close() - return False diff --git a/venv/Lib/site-packages/dns/quic/_trio.py b/venv/Lib/site-packages/dns/quic/_trio.py deleted file mode 100644 index ae79f36..0000000 --- a/venv/Lib/site-packages/dns/quic/_trio.py +++ /dev/null @@ -1,246 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import socket -import ssl -import struct -import time - -import aioquic.quic.configuration # type: ignore -import aioquic.quic.connection # type: ignore -import aioquic.quic.events # type: ignore -import trio - -import dns.exception -import dns.inet -from dns._asyncbackend import NullContext -from dns.quic._common import ( - QUIC_MAX_DATAGRAM, - AsyncQuicConnection, - AsyncQuicManager, - BaseQuicStream, - UnexpectedEOF, -) - - -class TrioQuicStream(BaseQuicStream): - def __init__(self, connection, stream_id): - super().__init__(connection, stream_id) - self._wake_up = trio.Condition() - - async def wait_for(self, amount): - while True: - if self._buffer.have(amount): - return - self._expecting = amount - async with self._wake_up: - await self._wake_up.wait() - self._expecting = 0 - - async def wait_for_end(self): - while True: - if self._buffer.seen_end(): - return - async with self._wake_up: - await self._wake_up.wait() - - async def receive(self, timeout=None): - if timeout is None: - context = NullContext(None) - else: - context = trio.move_on_after(timeout) - with context: - if self._connection.is_h3(): - await self.wait_for_end() - return self._buffer.get_all() - else: - await self.wait_for(2) - (size,) = struct.unpack("!H", self._buffer.get(2)) - await self.wait_for(size) - return self._buffer.get(size) - raise dns.exception.Timeout - - async def send(self, datagram, is_end=False): - data = self._encapsulate(datagram) - await self._connection.write(self._stream_id, data, is_end) - - async def _add_input(self, data, is_end): - if self._common_add_input(data, is_end): - async with self._wake_up: - self._wake_up.notify() - - async def close(self): - self._close() - - # Streams are async context managers - - async def __aenter__(self): - return self - - async def __aexit__(self, exc_type, exc_val, exc_tb): - await self.close() - async with self._wake_up: - self._wake_up.notify() - return False - - -class TrioQuicConnection(AsyncQuicConnection): - def __init__(self, connection, address, port, source, source_port, manager=None): - super().__init__(connection, address, port, source, source_port, manager) - self._socket = trio.socket.socket(self._af, socket.SOCK_DGRAM, 0) - self._handshake_complete = trio.Event() - self._run_done = trio.Event() - self._worker_scope = None - self._send_pending = False - - async def _worker(self): - try: - if self._source: - await self._socket.bind( - dns.inet.low_level_address_tuple(self._source, self._af) - ) - await self._socket.connect(self._peer) - while not self._done: - (expiration, interval) = self._get_timer_values(False) - if self._send_pending: - # Do not block forever if sends are pending. Even though we - # have a wake-up mechanism if we've already started the blocking - # read, the possibility of context switching in send means that - # more writes can happen while we have no wake up context, so - # we need self._send_pending to avoid (effectively) a "lost wakeup" - # race. - interval = 0.0 - with trio.CancelScope( - deadline=trio.current_time() + interval - ) as self._worker_scope: - datagram = await self._socket.recv(QUIC_MAX_DATAGRAM) - self._connection.receive_datagram(datagram, self._peer, time.time()) - self._worker_scope = None - self._handle_timer(expiration) - await self._handle_events() - # We clear this now, before sending anything, as sending can cause - # context switches that do more sends. We want to know if that - # happens so we don't block a long time on the recv() above. - self._send_pending = False - datagrams = self._connection.datagrams_to_send(time.time()) - for datagram, _ in datagrams: - await self._socket.send(datagram) - finally: - self._done = True - self._socket.close() - self._handshake_complete.set() - - async def _handle_events(self): - count = 0 - while True: - event = self._connection.next_event() - if event is None: - return - if isinstance(event, aioquic.quic.events.StreamDataReceived): - if self.is_h3(): - h3_events = self._h3_conn.handle_event(event) - for h3_event in h3_events: - if isinstance(h3_event, aioquic.h3.events.HeadersReceived): - stream = self._streams.get(event.stream_id) - if stream: - if stream._headers is None: - stream._headers = h3_event.headers - elif stream._trailers is None: - stream._trailers = h3_event.headers - if h3_event.stream_ended: - await stream._add_input(b"", True) - elif isinstance(h3_event, aioquic.h3.events.DataReceived): - stream = self._streams.get(event.stream_id) - if stream: - await stream._add_input( - h3_event.data, h3_event.stream_ended - ) - else: - stream = self._streams.get(event.stream_id) - if stream: - await stream._add_input(event.data, event.end_stream) - elif isinstance(event, aioquic.quic.events.HandshakeCompleted): - self._handshake_complete.set() - elif isinstance(event, aioquic.quic.events.ConnectionTerminated): - self._done = True - self._socket.close() - elif isinstance(event, aioquic.quic.events.StreamReset): - stream = self._streams.get(event.stream_id) - if stream: - await stream._add_input(b"", True) - count += 1 - if count > 10: - # yield - count = 0 - await trio.sleep(0) - - async def write(self, stream, data, is_end=False): - self._connection.send_stream_data(stream, data, is_end) - self._send_pending = True - if self._worker_scope is not None: - self._worker_scope.cancel() - - async def run(self): - if self._closed: - return - async with trio.open_nursery() as nursery: - nursery.start_soon(self._worker) - self._run_done.set() - - async def make_stream(self, timeout=None): - if timeout is None: - context = NullContext(None) - else: - context = trio.move_on_after(timeout) - with context: - await self._handshake_complete.wait() - if self._done: - raise UnexpectedEOF - stream_id = self._connection.get_next_available_stream_id(False) - stream = TrioQuicStream(self, stream_id) - self._streams[stream_id] = stream - return stream - raise dns.exception.Timeout - - async def close(self): - if not self._closed: - self._manager.closed(self._peer[0], self._peer[1]) - self._closed = True - self._connection.close() - self._send_pending = True - if self._worker_scope is not None: - self._worker_scope.cancel() - await self._run_done.wait() - - -class TrioQuicManager(AsyncQuicManager): - def __init__( - self, - nursery, - conf=None, - verify_mode=ssl.CERT_REQUIRED, - server_name=None, - h3=False, - ): - super().__init__(conf, verify_mode, TrioQuicConnection, server_name, h3) - self._nursery = nursery - - def connect( - self, address, port=853, source=None, source_port=0, want_session_ticket=True - ): - (connection, start) = self._connect( - address, port, source, source_port, want_session_ticket - ) - if start: - self._nursery.start_soon(connection.run) - return connection - - async def __aenter__(self): - return self - - async def __aexit__(self, exc_type, exc_val, exc_tb): - # Copy the iterator into a list as exiting things will mutate the connections - # table. - connections = list(self._connections.values()) - for connection in connections: - await connection.close() - return False diff --git a/venv/Lib/site-packages/dns/rcode.py b/venv/Lib/site-packages/dns/rcode.py deleted file mode 100644 index 8e6386f..0000000 --- a/venv/Lib/site-packages/dns/rcode.py +++ /dev/null @@ -1,168 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Result Codes.""" - -from typing import Tuple - -import dns.enum -import dns.exception - - -class Rcode(dns.enum.IntEnum): - #: No error - NOERROR = 0 - #: Format error - FORMERR = 1 - #: Server failure - SERVFAIL = 2 - #: Name does not exist ("Name Error" in RFC 1025 terminology). - NXDOMAIN = 3 - #: Not implemented - NOTIMP = 4 - #: Refused - REFUSED = 5 - #: Name exists. - YXDOMAIN = 6 - #: RRset exists. - YXRRSET = 7 - #: RRset does not exist. - NXRRSET = 8 - #: Not authoritative. - NOTAUTH = 9 - #: Name not in zone. - NOTZONE = 10 - #: DSO-TYPE Not Implemented - DSOTYPENI = 11 - #: Bad EDNS version. - BADVERS = 16 - #: TSIG Signature Failure - BADSIG = 16 - #: Key not recognized. - BADKEY = 17 - #: Signature out of time window. - BADTIME = 18 - #: Bad TKEY Mode. - BADMODE = 19 - #: Duplicate key name. - BADNAME = 20 - #: Algorithm not supported. - BADALG = 21 - #: Bad Truncation - BADTRUNC = 22 - #: Bad/missing Server Cookie - BADCOOKIE = 23 - - @classmethod - def _maximum(cls): - return 4095 - - @classmethod - def _unknown_exception_class(cls): - return UnknownRcode - - -class UnknownRcode(dns.exception.DNSException): - """A DNS rcode is unknown.""" - - -def from_text(text: str) -> Rcode: - """Convert text into an rcode. - - *text*, a ``str``, the textual rcode or an integer in textual form. - - Raises ``dns.rcode.UnknownRcode`` if the rcode mnemonic is unknown. - - Returns a ``dns.rcode.Rcode``. - """ - - return Rcode.from_text(text) - - -def from_flags(flags: int, ednsflags: int) -> Rcode: - """Return the rcode value encoded by flags and ednsflags. - - *flags*, an ``int``, the DNS flags field. - - *ednsflags*, an ``int``, the EDNS flags field. - - Raises ``ValueError`` if rcode is < 0 or > 4095 - - Returns a ``dns.rcode.Rcode``. - """ - - value = (flags & 0x000F) | ((ednsflags >> 20) & 0xFF0) - return Rcode.make(value) - - -def to_flags(value: Rcode) -> Tuple[int, int]: - """Return a (flags, ednsflags) tuple which encodes the rcode. - - *value*, a ``dns.rcode.Rcode``, the rcode. - - Raises ``ValueError`` if rcode is < 0 or > 4095. - - Returns an ``(int, int)`` tuple. - """ - - if value < 0 or value > 4095: - raise ValueError("rcode must be >= 0 and <= 4095") - v = value & 0xF - ev = (value & 0xFF0) << 20 - return (v, ev) - - -def to_text(value: Rcode, tsig: bool = False) -> str: - """Convert rcode into text. - - *value*, a ``dns.rcode.Rcode``, the rcode. - - Raises ``ValueError`` if rcode is < 0 or > 4095. - - Returns a ``str``. - """ - - if tsig and value == Rcode.BADVERS: - return "BADSIG" - return Rcode.to_text(value) - - -### BEGIN generated Rcode constants - -NOERROR = Rcode.NOERROR -FORMERR = Rcode.FORMERR -SERVFAIL = Rcode.SERVFAIL -NXDOMAIN = Rcode.NXDOMAIN -NOTIMP = Rcode.NOTIMP -REFUSED = Rcode.REFUSED -YXDOMAIN = Rcode.YXDOMAIN -YXRRSET = Rcode.YXRRSET -NXRRSET = Rcode.NXRRSET -NOTAUTH = Rcode.NOTAUTH -NOTZONE = Rcode.NOTZONE -DSOTYPENI = Rcode.DSOTYPENI -BADVERS = Rcode.BADVERS -BADSIG = Rcode.BADSIG -BADKEY = Rcode.BADKEY -BADTIME = Rcode.BADTIME -BADMODE = Rcode.BADMODE -BADNAME = Rcode.BADNAME -BADALG = Rcode.BADALG -BADTRUNC = Rcode.BADTRUNC -BADCOOKIE = Rcode.BADCOOKIE - -### END generated Rcode constants diff --git a/venv/Lib/site-packages/dns/rdata.py b/venv/Lib/site-packages/dns/rdata.py deleted file mode 100644 index 8099c26..0000000 --- a/venv/Lib/site-packages/dns/rdata.py +++ /dev/null @@ -1,911 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS rdata.""" - -import base64 -import binascii -import inspect -import io -import itertools -import random -from importlib import import_module -from typing import Any, Dict, Optional, Tuple, Union - -import dns.exception -import dns.immutable -import dns.ipv4 -import dns.ipv6 -import dns.name -import dns.rdataclass -import dns.rdatatype -import dns.tokenizer -import dns.ttl -import dns.wire - -_chunksize = 32 - -# We currently allow comparisons for rdata with relative names for backwards -# compatibility, but in the future we will not, as these kinds of comparisons -# can lead to subtle bugs if code is not carefully written. -# -# This switch allows the future behavior to be turned on so code can be -# tested with it. -_allow_relative_comparisons = True - - -class NoRelativeRdataOrdering(dns.exception.DNSException): - """An attempt was made to do an ordered comparison of one or more - rdata with relative names. The only reliable way of sorting rdata - is to use non-relativized rdata. - - """ - - -def _wordbreak(data, chunksize=_chunksize, separator=b" "): - """Break a binary string into chunks of chunksize characters separated by - a space. - """ - - if not chunksize: - return data.decode() - return separator.join( - [data[i : i + chunksize] for i in range(0, len(data), chunksize)] - ).decode() - - -# pylint: disable=unused-argument - - -def _hexify(data, chunksize=_chunksize, separator=b" ", **kw): - """Convert a binary string into its hex encoding, broken up into chunks - of chunksize characters separated by a separator. - """ - - return _wordbreak(binascii.hexlify(data), chunksize, separator) - - -def _base64ify(data, chunksize=_chunksize, separator=b" ", **kw): - """Convert a binary string into its base64 encoding, broken up into chunks - of chunksize characters separated by a separator. - """ - - return _wordbreak(base64.b64encode(data), chunksize, separator) - - -# pylint: enable=unused-argument - -__escaped = b'"\\' - - -def _escapify(qstring): - """Escape the characters in a quoted string which need it.""" - - if isinstance(qstring, str): - qstring = qstring.encode() - if not isinstance(qstring, bytearray): - qstring = bytearray(qstring) - - text = "" - for c in qstring: - if c in __escaped: - text += "\\" + chr(c) - elif c >= 0x20 and c < 0x7F: - text += chr(c) - else: - text += "\\%03d" % c - return text - - -def _truncate_bitmap(what): - """Determine the index of greatest byte that isn't all zeros, and - return the bitmap that contains all the bytes less than that index. - """ - - for i in range(len(what) - 1, -1, -1): - if what[i] != 0: - return what[0 : i + 1] - return what[0:1] - - -# So we don't have to edit all the rdata classes... -_constify = dns.immutable.constify - - -@dns.immutable.immutable -class Rdata: - """Base class for all DNS rdata types.""" - - __slots__ = ["rdclass", "rdtype", "rdcomment"] - - def __init__(self, rdclass, rdtype): - """Initialize an rdata. - - *rdclass*, an ``int`` is the rdataclass of the Rdata. - - *rdtype*, an ``int`` is the rdatatype of the Rdata. - """ - - self.rdclass = self._as_rdataclass(rdclass) - self.rdtype = self._as_rdatatype(rdtype) - self.rdcomment = None - - def _get_all_slots(self): - return itertools.chain.from_iterable( - getattr(cls, "__slots__", []) for cls in self.__class__.__mro__ - ) - - def __getstate__(self): - # We used to try to do a tuple of all slots here, but it - # doesn't work as self._all_slots isn't available at - # __setstate__() time. Before that we tried to store a tuple - # of __slots__, but that didn't work as it didn't store the - # slots defined by ancestors. This older way didn't fail - # outright, but ended up with partially broken objects, e.g. - # if you unpickled an A RR it wouldn't have rdclass and rdtype - # attributes, and would compare badly. - state = {} - for slot in self._get_all_slots(): - state[slot] = getattr(self, slot) - return state - - def __setstate__(self, state): - for slot, val in state.items(): - object.__setattr__(self, slot, val) - if not hasattr(self, "rdcomment"): - # Pickled rdata from 2.0.x might not have a rdcomment, so add - # it if needed. - object.__setattr__(self, "rdcomment", None) - - def covers(self) -> dns.rdatatype.RdataType: - """Return the type a Rdata covers. - - DNS SIG/RRSIG rdatas apply to a specific type; this type is - returned by the covers() function. If the rdata type is not - SIG or RRSIG, dns.rdatatype.NONE is returned. This is useful when - creating rdatasets, allowing the rdataset to contain only RRSIGs - of a particular type, e.g. RRSIG(NS). - - Returns a ``dns.rdatatype.RdataType``. - """ - - return dns.rdatatype.NONE - - def extended_rdatatype(self) -> int: - """Return a 32-bit type value, the least significant 16 bits of - which are the ordinary DNS type, and the upper 16 bits of which are - the "covered" type, if any. - - Returns an ``int``. - """ - - return self.covers() << 16 | self.rdtype - - def to_text( - self, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - **kw: Dict[str, Any], - ) -> str: - """Convert an rdata to text format. - - Returns a ``str``. - """ - - raise NotImplementedError # pragma: no cover - - def _to_wire( - self, - file: Optional[Any], - compress: Optional[dns.name.CompressType] = None, - origin: Optional[dns.name.Name] = None, - canonicalize: bool = False, - ) -> None: - raise NotImplementedError # pragma: no cover - - def to_wire( - self, - file: Optional[Any] = None, - compress: Optional[dns.name.CompressType] = None, - origin: Optional[dns.name.Name] = None, - canonicalize: bool = False, - ) -> Optional[bytes]: - """Convert an rdata to wire format. - - Returns a ``bytes`` if no output file was specified, or ``None`` otherwise. - """ - - if file: - # We call _to_wire() and then return None explicitly instead of - # of just returning the None from _to_wire() as mypy's func-returns-value - # unhelpfully errors out with "error: "_to_wire" of "Rdata" does not return - # a value (it only ever returns None)" - self._to_wire(file, compress, origin, canonicalize) - return None - else: - f = io.BytesIO() - self._to_wire(f, compress, origin, canonicalize) - return f.getvalue() - - def to_generic( - self, origin: Optional[dns.name.Name] = None - ) -> "dns.rdata.GenericRdata": - """Creates a dns.rdata.GenericRdata equivalent of this rdata. - - Returns a ``dns.rdata.GenericRdata``. - """ - return dns.rdata.GenericRdata( - self.rdclass, self.rdtype, self.to_wire(origin=origin) - ) - - def to_digestable(self, origin: Optional[dns.name.Name] = None) -> bytes: - """Convert rdata to a format suitable for digesting in hashes. This - is also the DNSSEC canonical form. - - Returns a ``bytes``. - """ - wire = self.to_wire(origin=origin, canonicalize=True) - assert wire is not None # for mypy - return wire - - def __repr__(self): - covers = self.covers() - if covers == dns.rdatatype.NONE: - ctext = "" - else: - ctext = "(" + dns.rdatatype.to_text(covers) + ")" - return ( - "" - ) - - def __str__(self): - return self.to_text() - - def _cmp(self, other): - """Compare an rdata with another rdata of the same rdtype and - rdclass. - - For rdata with only absolute names: - Return < 0 if self < other in the DNSSEC ordering, 0 if self - == other, and > 0 if self > other. - For rdata with at least one relative names: - The rdata sorts before any rdata with only absolute names. - When compared with another relative rdata, all names are - made absolute as if they were relative to the root, as the - proper origin is not available. While this creates a stable - ordering, it is NOT guaranteed to be the DNSSEC ordering. - In the future, all ordering comparisons for rdata with - relative names will be disallowed. - """ - try: - our = self.to_digestable() - our_relative = False - except dns.name.NeedAbsoluteNameOrOrigin: - if _allow_relative_comparisons: - our = self.to_digestable(dns.name.root) - our_relative = True - try: - their = other.to_digestable() - their_relative = False - except dns.name.NeedAbsoluteNameOrOrigin: - if _allow_relative_comparisons: - their = other.to_digestable(dns.name.root) - their_relative = True - if _allow_relative_comparisons: - if our_relative != their_relative: - # For the purpose of comparison, all rdata with at least one - # relative name is less than an rdata with only absolute names. - if our_relative: - return -1 - else: - return 1 - elif our_relative or their_relative: - raise NoRelativeRdataOrdering - if our == their: - return 0 - elif our > their: - return 1 - else: - return -1 - - def __eq__(self, other): - if not isinstance(other, Rdata): - return False - if self.rdclass != other.rdclass or self.rdtype != other.rdtype: - return False - our_relative = False - their_relative = False - try: - our = self.to_digestable() - except dns.name.NeedAbsoluteNameOrOrigin: - our = self.to_digestable(dns.name.root) - our_relative = True - try: - their = other.to_digestable() - except dns.name.NeedAbsoluteNameOrOrigin: - their = other.to_digestable(dns.name.root) - their_relative = True - if our_relative != their_relative: - return False - return our == their - - def __ne__(self, other): - if not isinstance(other, Rdata): - return True - if self.rdclass != other.rdclass or self.rdtype != other.rdtype: - return True - return not self.__eq__(other) - - def __lt__(self, other): - if ( - not isinstance(other, Rdata) - or self.rdclass != other.rdclass - or self.rdtype != other.rdtype - ): - return NotImplemented - return self._cmp(other) < 0 - - def __le__(self, other): - if ( - not isinstance(other, Rdata) - or self.rdclass != other.rdclass - or self.rdtype != other.rdtype - ): - return NotImplemented - return self._cmp(other) <= 0 - - def __ge__(self, other): - if ( - not isinstance(other, Rdata) - or self.rdclass != other.rdclass - or self.rdtype != other.rdtype - ): - return NotImplemented - return self._cmp(other) >= 0 - - def __gt__(self, other): - if ( - not isinstance(other, Rdata) - or self.rdclass != other.rdclass - or self.rdtype != other.rdtype - ): - return NotImplemented - return self._cmp(other) > 0 - - def __hash__(self): - return hash(self.to_digestable(dns.name.root)) - - @classmethod - def from_text( - cls, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - tok: dns.tokenizer.Tokenizer, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - relativize_to: Optional[dns.name.Name] = None, - ) -> "Rdata": - raise NotImplementedError # pragma: no cover - - @classmethod - def from_wire_parser( - cls, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - parser: dns.wire.Parser, - origin: Optional[dns.name.Name] = None, - ) -> "Rdata": - raise NotImplementedError # pragma: no cover - - def replace(self, **kwargs: Any) -> "Rdata": - """ - Create a new Rdata instance based on the instance replace was - invoked on. It is possible to pass different parameters to - override the corresponding properties of the base Rdata. - - Any field specific to the Rdata type can be replaced, but the - *rdtype* and *rdclass* fields cannot. - - Returns an instance of the same Rdata subclass as *self*. - """ - - # Get the constructor parameters. - parameters = inspect.signature(self.__init__).parameters # type: ignore - - # Ensure that all of the arguments correspond to valid fields. - # Don't allow rdclass or rdtype to be changed, though. - for key in kwargs: - if key == "rdcomment": - continue - if key not in parameters: - raise AttributeError( - f"'{self.__class__.__name__}' object has no attribute '{key}'" - ) - if key in ("rdclass", "rdtype"): - raise AttributeError( - f"Cannot overwrite '{self.__class__.__name__}' attribute '{key}'" - ) - - # Construct the parameter list. For each field, use the value in - # kwargs if present, and the current value otherwise. - args = (kwargs.get(key, getattr(self, key)) for key in parameters) - - # Create, validate, and return the new object. - rd = self.__class__(*args) - # The comment is not set in the constructor, so give it special - # handling. - rdcomment = kwargs.get("rdcomment", self.rdcomment) - if rdcomment is not None: - object.__setattr__(rd, "rdcomment", rdcomment) - return rd - - # Type checking and conversion helpers. These are class methods as - # they don't touch object state and may be useful to others. - - @classmethod - def _as_rdataclass(cls, value): - return dns.rdataclass.RdataClass.make(value) - - @classmethod - def _as_rdatatype(cls, value): - return dns.rdatatype.RdataType.make(value) - - @classmethod - def _as_bytes( - cls, - value: Any, - encode: bool = False, - max_length: Optional[int] = None, - empty_ok: bool = True, - ) -> bytes: - if encode and isinstance(value, str): - bvalue = value.encode() - elif isinstance(value, bytearray): - bvalue = bytes(value) - elif isinstance(value, bytes): - bvalue = value - else: - raise ValueError("not bytes") - if max_length is not None and len(bvalue) > max_length: - raise ValueError("too long") - if not empty_ok and len(bvalue) == 0: - raise ValueError("empty bytes not allowed") - return bvalue - - @classmethod - def _as_name(cls, value): - # Note that proper name conversion (e.g. with origin and IDNA - # awareness) is expected to be done via from_text. This is just - # a simple thing for people invoking the constructor directly. - if isinstance(value, str): - return dns.name.from_text(value) - elif not isinstance(value, dns.name.Name): - raise ValueError("not a name") - return value - - @classmethod - def _as_uint8(cls, value): - if not isinstance(value, int): - raise ValueError("not an integer") - if value < 0 or value > 255: - raise ValueError("not a uint8") - return value - - @classmethod - def _as_uint16(cls, value): - if not isinstance(value, int): - raise ValueError("not an integer") - if value < 0 or value > 65535: - raise ValueError("not a uint16") - return value - - @classmethod - def _as_uint32(cls, value): - if not isinstance(value, int): - raise ValueError("not an integer") - if value < 0 or value > 4294967295: - raise ValueError("not a uint32") - return value - - @classmethod - def _as_uint48(cls, value): - if not isinstance(value, int): - raise ValueError("not an integer") - if value < 0 or value > 281474976710655: - raise ValueError("not a uint48") - return value - - @classmethod - def _as_int(cls, value, low=None, high=None): - if not isinstance(value, int): - raise ValueError("not an integer") - if low is not None and value < low: - raise ValueError("value too small") - if high is not None and value > high: - raise ValueError("value too large") - return value - - @classmethod - def _as_ipv4_address(cls, value): - if isinstance(value, str): - return dns.ipv4.canonicalize(value) - elif isinstance(value, bytes): - return dns.ipv4.inet_ntoa(value) - else: - raise ValueError("not an IPv4 address") - - @classmethod - def _as_ipv6_address(cls, value): - if isinstance(value, str): - return dns.ipv6.canonicalize(value) - elif isinstance(value, bytes): - return dns.ipv6.inet_ntoa(value) - else: - raise ValueError("not an IPv6 address") - - @classmethod - def _as_bool(cls, value): - if isinstance(value, bool): - return value - else: - raise ValueError("not a boolean") - - @classmethod - def _as_ttl(cls, value): - if isinstance(value, int): - return cls._as_int(value, 0, dns.ttl.MAX_TTL) - elif isinstance(value, str): - return dns.ttl.from_text(value) - else: - raise ValueError("not a TTL") - - @classmethod - def _as_tuple(cls, value, as_value): - try: - # For user convenience, if value is a singleton of the list - # element type, wrap it in a tuple. - return (as_value(value),) - except Exception: - # Otherwise, check each element of the iterable *value* - # against *as_value*. - return tuple(as_value(v) for v in value) - - # Processing order - - @classmethod - def _processing_order(cls, iterable): - items = list(iterable) - random.shuffle(items) - return items - - -@dns.immutable.immutable -class GenericRdata(Rdata): - """Generic Rdata Class - - This class is used for rdata types for which we have no better - implementation. It implements the DNS "unknown RRs" scheme. - """ - - __slots__ = ["data"] - - def __init__(self, rdclass, rdtype, data): - super().__init__(rdclass, rdtype) - self.data = data - - def to_text( - self, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - **kw: Dict[str, Any], - ) -> str: - return r"\# %d " % len(self.data) + _hexify(self.data, **kw) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - token = tok.get() - if not token.is_identifier() or token.value != r"\#": - raise dns.exception.SyntaxError(r"generic rdata does not start with \#") - length = tok.get_int() - hex = tok.concatenate_remaining_identifiers(True).encode() - data = binascii.unhexlify(hex) - if len(data) != length: - raise dns.exception.SyntaxError("generic rdata hex data has wrong length") - return cls(rdclass, rdtype, data) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(self.data) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - return cls(rdclass, rdtype, parser.get_remaining()) - - -_rdata_classes: Dict[Tuple[dns.rdataclass.RdataClass, dns.rdatatype.RdataType], Any] = ( - {} -) -_module_prefix = "dns.rdtypes" -_dynamic_load_allowed = True - - -def get_rdata_class(rdclass, rdtype, use_generic=True): - cls = _rdata_classes.get((rdclass, rdtype)) - if not cls: - cls = _rdata_classes.get((dns.rdatatype.ANY, rdtype)) - if not cls and _dynamic_load_allowed: - rdclass_text = dns.rdataclass.to_text(rdclass) - rdtype_text = dns.rdatatype.to_text(rdtype) - rdtype_text = rdtype_text.replace("-", "_") - try: - mod = import_module( - ".".join([_module_prefix, rdclass_text, rdtype_text]) - ) - cls = getattr(mod, rdtype_text) - _rdata_classes[(rdclass, rdtype)] = cls - except ImportError: - try: - mod = import_module(".".join([_module_prefix, "ANY", rdtype_text])) - cls = getattr(mod, rdtype_text) - _rdata_classes[(dns.rdataclass.ANY, rdtype)] = cls - _rdata_classes[(rdclass, rdtype)] = cls - except ImportError: - pass - if not cls and use_generic: - cls = GenericRdata - _rdata_classes[(rdclass, rdtype)] = cls - return cls - - -def load_all_types(disable_dynamic_load=True): - """Load all rdata types for which dnspython has a non-generic implementation. - - Normally dnspython loads DNS rdatatype implementations on demand, but in some - specialized cases loading all types at an application-controlled time is preferred. - - If *disable_dynamic_load*, a ``bool``, is ``True`` then dnspython will not attempt - to use its dynamic loading mechanism if an unknown type is subsequently encountered, - and will simply use the ``GenericRdata`` class. - """ - # Load class IN and ANY types. - for rdtype in dns.rdatatype.RdataType: - get_rdata_class(dns.rdataclass.IN, rdtype, False) - # Load the one non-ANY implementation we have in CH. Everything - # else in CH is an ANY type, and we'll discover those on demand but won't - # have to import anything. - get_rdata_class(dns.rdataclass.CH, dns.rdatatype.A, False) - if disable_dynamic_load: - # Now disable dynamic loading so any subsequent unknown type immediately becomes - # GenericRdata without a load attempt. - global _dynamic_load_allowed - _dynamic_load_allowed = False - - -def from_text( - rdclass: Union[dns.rdataclass.RdataClass, str], - rdtype: Union[dns.rdatatype.RdataType, str], - tok: Union[dns.tokenizer.Tokenizer, str], - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - relativize_to: Optional[dns.name.Name] = None, - idna_codec: Optional[dns.name.IDNACodec] = None, -) -> Rdata: - """Build an rdata object from text format. - - This function attempts to dynamically load a class which - implements the specified rdata class and type. If there is no - class-and-type-specific implementation, the GenericRdata class - is used. - - Once a class is chosen, its from_text() class method is called - with the parameters to this function. - - If *tok* is a ``str``, then a tokenizer is created and the string - is used as its input. - - *rdclass*, a ``dns.rdataclass.RdataClass`` or ``str``, the rdataclass. - - *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdatatype. - - *tok*, a ``dns.tokenizer.Tokenizer`` or a ``str``. - - *origin*, a ``dns.name.Name`` (or ``None``), the - origin to use for relative names. - - *relativize*, a ``bool``. If true, name will be relativized. - - *relativize_to*, a ``dns.name.Name`` (or ``None``), the origin to use - when relativizing names. If not set, the *origin* value will be used. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder to use if a tokenizer needs to be created. If - ``None``, the default IDNA 2003 encoder/decoder is used. If a - tokenizer is not created, then the codec associated with the tokenizer - is the one that is used. - - Returns an instance of the chosen Rdata subclass. - - """ - if isinstance(tok, str): - tok = dns.tokenizer.Tokenizer(tok, idna_codec=idna_codec) - rdclass = dns.rdataclass.RdataClass.make(rdclass) - rdtype = dns.rdatatype.RdataType.make(rdtype) - cls = get_rdata_class(rdclass, rdtype) - with dns.exception.ExceptionWrapper(dns.exception.SyntaxError): - rdata = None - if cls != GenericRdata: - # peek at first token - token = tok.get() - tok.unget(token) - if token.is_identifier() and token.value == r"\#": - # - # Known type using the generic syntax. Extract the - # wire form from the generic syntax, and then run - # from_wire on it. - # - grdata = GenericRdata.from_text( - rdclass, rdtype, tok, origin, relativize, relativize_to - ) - rdata = from_wire( - rdclass, rdtype, grdata.data, 0, len(grdata.data), origin - ) - # - # If this comparison isn't equal, then there must have been - # compressed names in the wire format, which is an error, - # there being no reasonable context to decompress with. - # - rwire = rdata.to_wire() - if rwire != grdata.data: - raise dns.exception.SyntaxError( - "compressed data in " - "generic syntax form " - "of known rdatatype" - ) - if rdata is None: - rdata = cls.from_text( - rdclass, rdtype, tok, origin, relativize, relativize_to - ) - token = tok.get_eol_as_token() - if token.comment is not None: - object.__setattr__(rdata, "rdcomment", token.comment) - return rdata - - -def from_wire_parser( - rdclass: Union[dns.rdataclass.RdataClass, str], - rdtype: Union[dns.rdatatype.RdataType, str], - parser: dns.wire.Parser, - origin: Optional[dns.name.Name] = None, -) -> Rdata: - """Build an rdata object from wire format - - This function attempts to dynamically load a class which - implements the specified rdata class and type. If there is no - class-and-type-specific implementation, the GenericRdata class - is used. - - Once a class is chosen, its from_wire() class method is called - with the parameters to this function. - - *rdclass*, a ``dns.rdataclass.RdataClass`` or ``str``, the rdataclass. - - *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdatatype. - - *parser*, a ``dns.wire.Parser``, the parser, which should be - restricted to the rdata length. - - *origin*, a ``dns.name.Name`` (or ``None``). If not ``None``, - then names will be relativized to this origin. - - Returns an instance of the chosen Rdata subclass. - """ - - rdclass = dns.rdataclass.RdataClass.make(rdclass) - rdtype = dns.rdatatype.RdataType.make(rdtype) - cls = get_rdata_class(rdclass, rdtype) - with dns.exception.ExceptionWrapper(dns.exception.FormError): - return cls.from_wire_parser(rdclass, rdtype, parser, origin) - - -def from_wire( - rdclass: Union[dns.rdataclass.RdataClass, str], - rdtype: Union[dns.rdatatype.RdataType, str], - wire: bytes, - current: int, - rdlen: int, - origin: Optional[dns.name.Name] = None, -) -> Rdata: - """Build an rdata object from wire format - - This function attempts to dynamically load a class which - implements the specified rdata class and type. If there is no - class-and-type-specific implementation, the GenericRdata class - is used. - - Once a class is chosen, its from_wire() class method is called - with the parameters to this function. - - *rdclass*, an ``int``, the rdataclass. - - *rdtype*, an ``int``, the rdatatype. - - *wire*, a ``bytes``, the wire-format message. - - *current*, an ``int``, the offset in wire of the beginning of - the rdata. - - *rdlen*, an ``int``, the length of the wire-format rdata - - *origin*, a ``dns.name.Name`` (or ``None``). If not ``None``, - then names will be relativized to this origin. - - Returns an instance of the chosen Rdata subclass. - """ - parser = dns.wire.Parser(wire, current) - with parser.restrict_to(rdlen): - return from_wire_parser(rdclass, rdtype, parser, origin) - - -class RdatatypeExists(dns.exception.DNSException): - """DNS rdatatype already exists.""" - - supp_kwargs = {"rdclass", "rdtype"} - fmt = ( - "The rdata type with class {rdclass:d} and rdtype {rdtype:d} " - + "already exists." - ) - - -def register_type( - implementation: Any, - rdtype: int, - rdtype_text: str, - is_singleton: bool = False, - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, -) -> None: - """Dynamically register a module to handle an rdatatype. - - *implementation*, a module implementing the type in the usual dnspython - way. - - *rdtype*, an ``int``, the rdatatype to register. - - *rdtype_text*, a ``str``, the textual form of the rdatatype. - - *is_singleton*, a ``bool``, indicating if the type is a singleton (i.e. - RRsets of the type can have only one member.) - - *rdclass*, the rdataclass of the type, or ``dns.rdataclass.ANY`` if - it applies to all classes. - """ - - rdtype = dns.rdatatype.RdataType.make(rdtype) - existing_cls = get_rdata_class(rdclass, rdtype) - if existing_cls != GenericRdata or dns.rdatatype.is_metatype(rdtype): - raise RdatatypeExists(rdclass=rdclass, rdtype=rdtype) - _rdata_classes[(rdclass, rdtype)] = getattr( - implementation, rdtype_text.replace("-", "_") - ) - dns.rdatatype.register_type(rdtype, rdtype_text, is_singleton) diff --git a/venv/Lib/site-packages/dns/rdataclass.py b/venv/Lib/site-packages/dns/rdataclass.py deleted file mode 100644 index 89b85a7..0000000 --- a/venv/Lib/site-packages/dns/rdataclass.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Rdata Classes.""" - -import dns.enum -import dns.exception - - -class RdataClass(dns.enum.IntEnum): - """DNS Rdata Class""" - - RESERVED0 = 0 - IN = 1 - INTERNET = IN - CH = 3 - CHAOS = CH - HS = 4 - HESIOD = HS - NONE = 254 - ANY = 255 - - @classmethod - def _maximum(cls): - return 65535 - - @classmethod - def _short_name(cls): - return "class" - - @classmethod - def _prefix(cls): - return "CLASS" - - @classmethod - def _unknown_exception_class(cls): - return UnknownRdataclass - - -_metaclasses = {RdataClass.NONE, RdataClass.ANY} - - -class UnknownRdataclass(dns.exception.DNSException): - """A DNS class is unknown.""" - - -def from_text(text: str) -> RdataClass: - """Convert text into a DNS rdata class value. - - The input text can be a defined DNS RR class mnemonic or - instance of the DNS generic class syntax. - - For example, "IN" and "CLASS1" will both result in a value of 1. - - Raises ``dns.rdatatype.UnknownRdataclass`` if the class is unknown. - - Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535. - - Returns a ``dns.rdataclass.RdataClass``. - """ - - return RdataClass.from_text(text) - - -def to_text(value: RdataClass) -> str: - """Convert a DNS rdata class value to text. - - If the value has a known mnemonic, it will be used, otherwise the - DNS generic class syntax will be used. - - Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535. - - Returns a ``str``. - """ - - return RdataClass.to_text(value) - - -def is_metaclass(rdclass: RdataClass) -> bool: - """True if the specified class is a metaclass. - - The currently defined metaclasses are ANY and NONE. - - *rdclass* is a ``dns.rdataclass.RdataClass``. - """ - - if rdclass in _metaclasses: - return True - return False - - -### BEGIN generated RdataClass constants - -RESERVED0 = RdataClass.RESERVED0 -IN = RdataClass.IN -INTERNET = RdataClass.INTERNET -CH = RdataClass.CH -CHAOS = RdataClass.CHAOS -HS = RdataClass.HS -HESIOD = RdataClass.HESIOD -NONE = RdataClass.NONE -ANY = RdataClass.ANY - -### END generated RdataClass constants diff --git a/venv/Lib/site-packages/dns/rdataset.py b/venv/Lib/site-packages/dns/rdataset.py deleted file mode 100644 index 39cab23..0000000 --- a/venv/Lib/site-packages/dns/rdataset.py +++ /dev/null @@ -1,512 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS rdatasets (an rdataset is a set of rdatas of a given type and class)""" - -import io -import random -import struct -from typing import Any, Collection, Dict, List, Optional, Union, cast - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdataclass -import dns.rdatatype -import dns.renderer -import dns.set -import dns.ttl - -# define SimpleSet here for backwards compatibility -SimpleSet = dns.set.Set - - -class DifferingCovers(dns.exception.DNSException): - """An attempt was made to add a DNS SIG/RRSIG whose covered type - is not the same as that of the other rdatas in the rdataset.""" - - -class IncompatibleTypes(dns.exception.DNSException): - """An attempt was made to add DNS RR data of an incompatible type.""" - - -class Rdataset(dns.set.Set): - """A DNS rdataset.""" - - __slots__ = ["rdclass", "rdtype", "covers", "ttl"] - - def __init__( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - ttl: int = 0, - ): - """Create a new rdataset of the specified class and type. - - *rdclass*, a ``dns.rdataclass.RdataClass``, the rdataclass. - - *rdtype*, an ``dns.rdatatype.RdataType``, the rdatatype. - - *covers*, an ``dns.rdatatype.RdataType``, the covered rdatatype. - - *ttl*, an ``int``, the TTL. - """ - - super().__init__() - self.rdclass = rdclass - self.rdtype: dns.rdatatype.RdataType = rdtype - self.covers: dns.rdatatype.RdataType = covers - self.ttl = ttl - - def _clone(self): - obj = super()._clone() - obj.rdclass = self.rdclass - obj.rdtype = self.rdtype - obj.covers = self.covers - obj.ttl = self.ttl - return obj - - def update_ttl(self, ttl: int) -> None: - """Perform TTL minimization. - - Set the TTL of the rdataset to be the lesser of the set's current - TTL or the specified TTL. If the set contains no rdatas, set the TTL - to the specified TTL. - - *ttl*, an ``int`` or ``str``. - """ - ttl = dns.ttl.make(ttl) - if len(self) == 0: - self.ttl = ttl - elif ttl < self.ttl: - self.ttl = ttl - - def add( # pylint: disable=arguments-differ,arguments-renamed - self, rd: dns.rdata.Rdata, ttl: Optional[int] = None - ) -> None: - """Add the specified rdata to the rdataset. - - If the optional *ttl* parameter is supplied, then - ``self.update_ttl(ttl)`` will be called prior to adding the rdata. - - *rd*, a ``dns.rdata.Rdata``, the rdata - - *ttl*, an ``int``, the TTL. - - Raises ``dns.rdataset.IncompatibleTypes`` if the type and class - do not match the type and class of the rdataset. - - Raises ``dns.rdataset.DifferingCovers`` if the type is a signature - type and the covered type does not match that of the rdataset. - """ - - # - # If we're adding a signature, do some special handling to - # check that the signature covers the same type as the - # other rdatas in this rdataset. If this is the first rdata - # in the set, initialize the covers field. - # - if self.rdclass != rd.rdclass or self.rdtype != rd.rdtype: - raise IncompatibleTypes - if ttl is not None: - self.update_ttl(ttl) - if self.rdtype == dns.rdatatype.RRSIG or self.rdtype == dns.rdatatype.SIG: - covers = rd.covers() - if len(self) == 0 and self.covers == dns.rdatatype.NONE: - self.covers = covers - elif self.covers != covers: - raise DifferingCovers - if dns.rdatatype.is_singleton(rd.rdtype) and len(self) > 0: - self.clear() - super().add(rd) - - def union_update(self, other): - self.update_ttl(other.ttl) - super().union_update(other) - - def intersection_update(self, other): - self.update_ttl(other.ttl) - super().intersection_update(other) - - def update(self, other): - """Add all rdatas in other to self. - - *other*, a ``dns.rdataset.Rdataset``, the rdataset from which - to update. - """ - - self.update_ttl(other.ttl) - super().update(other) - - def _rdata_repr(self): - def maybe_truncate(s): - if len(s) > 100: - return s[:100] + "..." - return s - - return "[" + ", ".join(f"<{maybe_truncate(str(rr))}>" for rr in self) + "]" - - def __repr__(self): - if self.covers == 0: - ctext = "" - else: - ctext = "(" + dns.rdatatype.to_text(self.covers) + ")" - return ( - "" - ) - - def __str__(self): - return self.to_text() - - def __eq__(self, other): - if not isinstance(other, Rdataset): - return False - if ( - self.rdclass != other.rdclass - or self.rdtype != other.rdtype - or self.covers != other.covers - ): - return False - return super().__eq__(other) - - def __ne__(self, other): - return not self.__eq__(other) - - def to_text( - self, - name: Optional[dns.name.Name] = None, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - override_rdclass: Optional[dns.rdataclass.RdataClass] = None, - want_comments: bool = False, - **kw: Dict[str, Any], - ) -> str: - """Convert the rdataset into DNS zone file format. - - See ``dns.name.Name.choose_relativity`` for more information - on how *origin* and *relativize* determine the way names - are emitted. - - Any additional keyword arguments are passed on to the rdata - ``to_text()`` method. - - *name*, a ``dns.name.Name``. If name is not ``None``, emit RRs with - *name* as the owner name. - - *origin*, a ``dns.name.Name`` or ``None``, the origin for relative - names. - - *relativize*, a ``bool``. If ``True``, names will be relativized - to *origin*. - - *override_rdclass*, a ``dns.rdataclass.RdataClass`` or ``None``. - If not ``None``, use this class instead of the Rdataset's class. - - *want_comments*, a ``bool``. If ``True``, emit comments for rdata - which have them. The default is ``False``. - """ - - if name is not None: - name = name.choose_relativity(origin, relativize) - ntext = str(name) - pad = " " - else: - ntext = "" - pad = "" - s = io.StringIO() - if override_rdclass is not None: - rdclass = override_rdclass - else: - rdclass = self.rdclass - if len(self) == 0: - # - # Empty rdatasets are used for the question section, and in - # some dynamic updates, so we don't need to print out the TTL - # (which is meaningless anyway). - # - s.write( - f"{ntext}{pad}{dns.rdataclass.to_text(rdclass)} " - f"{dns.rdatatype.to_text(self.rdtype)}\n" - ) - else: - for rd in self: - extra = "" - if want_comments: - if rd.rdcomment: - extra = f" ;{rd.rdcomment}" - s.write( - "%s%s%d %s %s %s%s\n" - % ( - ntext, - pad, - self.ttl, - dns.rdataclass.to_text(rdclass), - dns.rdatatype.to_text(self.rdtype), - rd.to_text(origin=origin, relativize=relativize, **kw), - extra, - ) - ) - # - # We strip off the final \n for the caller's convenience in printing - # - return s.getvalue()[:-1] - - def to_wire( - self, - name: dns.name.Name, - file: Any, - compress: Optional[dns.name.CompressType] = None, - origin: Optional[dns.name.Name] = None, - override_rdclass: Optional[dns.rdataclass.RdataClass] = None, - want_shuffle: bool = True, - ) -> int: - """Convert the rdataset to wire format. - - *name*, a ``dns.name.Name`` is the owner name to use. - - *file* is the file where the name is emitted (typically a - BytesIO file). - - *compress*, a ``dict``, is the compression table to use. If - ``None`` (the default), names will not be compressed. - - *origin* is a ``dns.name.Name`` or ``None``. If the name is - relative and origin is not ``None``, then *origin* will be appended - to it. - - *override_rdclass*, an ``int``, is used as the class instead of the - class of the rdataset. This is useful when rendering rdatasets - associated with dynamic updates. - - *want_shuffle*, a ``bool``. If ``True``, then the order of the - Rdatas within the Rdataset will be shuffled before rendering. - - Returns an ``int``, the number of records emitted. - """ - - if override_rdclass is not None: - rdclass = override_rdclass - want_shuffle = False - else: - rdclass = self.rdclass - if len(self) == 0: - name.to_wire(file, compress, origin) - file.write(struct.pack("!HHIH", self.rdtype, rdclass, 0, 0)) - return 1 - else: - l: Union[Rdataset, List[dns.rdata.Rdata]] - if want_shuffle: - l = list(self) - random.shuffle(l) - else: - l = self - for rd in l: - name.to_wire(file, compress, origin) - file.write(struct.pack("!HHI", self.rdtype, rdclass, self.ttl)) - with dns.renderer.prefixed_length(file, 2): - rd.to_wire(file, compress, origin) - return len(self) - - def match( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType, - ) -> bool: - """Returns ``True`` if this rdataset matches the specified class, - type, and covers. - """ - if self.rdclass == rdclass and self.rdtype == rdtype and self.covers == covers: - return True - return False - - def processing_order(self) -> List[dns.rdata.Rdata]: - """Return rdatas in a valid processing order according to the type's - specification. For example, MX records are in preference order from - lowest to highest preferences, with items of the same preference - shuffled. - - For types that do not define a processing order, the rdatas are - simply shuffled. - """ - if len(self) == 0: - return [] - else: - return self[0]._processing_order(iter(self)) - - -@dns.immutable.immutable -class ImmutableRdataset(Rdataset): # lgtm[py/missing-equals] - """An immutable DNS rdataset.""" - - _clone_class = Rdataset - - def __init__(self, rdataset: Rdataset): - """Create an immutable rdataset from the specified rdataset.""" - - super().__init__( - rdataset.rdclass, rdataset.rdtype, rdataset.covers, rdataset.ttl - ) - self.items = dns.immutable.Dict(rdataset.items) - - def update_ttl(self, ttl): - raise TypeError("immutable") - - def add(self, rd, ttl=None): - raise TypeError("immutable") - - def union_update(self, other): - raise TypeError("immutable") - - def intersection_update(self, other): - raise TypeError("immutable") - - def update(self, other): - raise TypeError("immutable") - - def __delitem__(self, i): - raise TypeError("immutable") - - # lgtm complains about these not raising ArithmeticError, but there is - # precedent for overrides of these methods in other classes to raise - # TypeError, and it seems like the better exception. - - def __ior__(self, other): # lgtm[py/unexpected-raise-in-special-method] - raise TypeError("immutable") - - def __iand__(self, other): # lgtm[py/unexpected-raise-in-special-method] - raise TypeError("immutable") - - def __iadd__(self, other): # lgtm[py/unexpected-raise-in-special-method] - raise TypeError("immutable") - - def __isub__(self, other): # lgtm[py/unexpected-raise-in-special-method] - raise TypeError("immutable") - - def clear(self): - raise TypeError("immutable") - - def __copy__(self): - return ImmutableRdataset(super().copy()) - - def copy(self): - return ImmutableRdataset(super().copy()) - - def union(self, other): - return ImmutableRdataset(super().union(other)) - - def intersection(self, other): - return ImmutableRdataset(super().intersection(other)) - - def difference(self, other): - return ImmutableRdataset(super().difference(other)) - - def symmetric_difference(self, other): - return ImmutableRdataset(super().symmetric_difference(other)) - - -def from_text_list( - rdclass: Union[dns.rdataclass.RdataClass, str], - rdtype: Union[dns.rdatatype.RdataType, str], - ttl: int, - text_rdatas: Collection[str], - idna_codec: Optional[dns.name.IDNACodec] = None, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - relativize_to: Optional[dns.name.Name] = None, -) -> Rdataset: - """Create an rdataset with the specified class, type, and TTL, and with - the specified list of rdatas in text format. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder to use; if ``None``, the default IDNA 2003 - encoder/decoder is used. - - *origin*, a ``dns.name.Name`` (or ``None``), the - origin to use for relative names. - - *relativize*, a ``bool``. If true, name will be relativized. - - *relativize_to*, a ``dns.name.Name`` (or ``None``), the origin to use - when relativizing names. If not set, the *origin* value will be used. - - Returns a ``dns.rdataset.Rdataset`` object. - """ - - rdclass = dns.rdataclass.RdataClass.make(rdclass) - rdtype = dns.rdatatype.RdataType.make(rdtype) - r = Rdataset(rdclass, rdtype) - r.update_ttl(ttl) - for t in text_rdatas: - rd = dns.rdata.from_text( - r.rdclass, r.rdtype, t, origin, relativize, relativize_to, idna_codec - ) - r.add(rd) - return r - - -def from_text( - rdclass: Union[dns.rdataclass.RdataClass, str], - rdtype: Union[dns.rdatatype.RdataType, str], - ttl: int, - *text_rdatas: Any, -) -> Rdataset: - """Create an rdataset with the specified class, type, and TTL, and with - the specified rdatas in text format. - - Returns a ``dns.rdataset.Rdataset`` object. - """ - - return from_text_list(rdclass, rdtype, ttl, cast(Collection[str], text_rdatas)) - - -def from_rdata_list(ttl: int, rdatas: Collection[dns.rdata.Rdata]) -> Rdataset: - """Create an rdataset with the specified TTL, and with - the specified list of rdata objects. - - Returns a ``dns.rdataset.Rdataset`` object. - """ - - if len(rdatas) == 0: - raise ValueError("rdata list must not be empty") - r = None - for rd in rdatas: - if r is None: - r = Rdataset(rd.rdclass, rd.rdtype) - r.update_ttl(ttl) - r.add(rd) - assert r is not None - return r - - -def from_rdata(ttl: int, *rdatas: Any) -> Rdataset: - """Create an rdataset with the specified TTL, and with - the specified rdata objects. - - Returns a ``dns.rdataset.Rdataset`` object. - """ - - return from_rdata_list(ttl, cast(Collection[dns.rdata.Rdata], rdatas)) diff --git a/venv/Lib/site-packages/dns/rdatatype.py b/venv/Lib/site-packages/dns/rdatatype.py deleted file mode 100644 index aa9e561..0000000 --- a/venv/Lib/site-packages/dns/rdatatype.py +++ /dev/null @@ -1,336 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Rdata Types.""" - -from typing import Dict - -import dns.enum -import dns.exception - - -class RdataType(dns.enum.IntEnum): - """DNS Rdata Type""" - - TYPE0 = 0 - NONE = 0 - A = 1 - NS = 2 - MD = 3 - MF = 4 - CNAME = 5 - SOA = 6 - MB = 7 - MG = 8 - MR = 9 - NULL = 10 - WKS = 11 - PTR = 12 - HINFO = 13 - MINFO = 14 - MX = 15 - TXT = 16 - RP = 17 - AFSDB = 18 - X25 = 19 - ISDN = 20 - RT = 21 - NSAP = 22 - NSAP_PTR = 23 - SIG = 24 - KEY = 25 - PX = 26 - GPOS = 27 - AAAA = 28 - LOC = 29 - NXT = 30 - SRV = 33 - NAPTR = 35 - KX = 36 - CERT = 37 - A6 = 38 - DNAME = 39 - OPT = 41 - APL = 42 - DS = 43 - SSHFP = 44 - IPSECKEY = 45 - RRSIG = 46 - NSEC = 47 - DNSKEY = 48 - DHCID = 49 - NSEC3 = 50 - NSEC3PARAM = 51 - TLSA = 52 - SMIMEA = 53 - HIP = 55 - NINFO = 56 - CDS = 59 - CDNSKEY = 60 - OPENPGPKEY = 61 - CSYNC = 62 - ZONEMD = 63 - SVCB = 64 - HTTPS = 65 - SPF = 99 - UNSPEC = 103 - NID = 104 - L32 = 105 - L64 = 106 - LP = 107 - EUI48 = 108 - EUI64 = 109 - TKEY = 249 - TSIG = 250 - IXFR = 251 - AXFR = 252 - MAILB = 253 - MAILA = 254 - ANY = 255 - URI = 256 - CAA = 257 - AVC = 258 - AMTRELAY = 260 - RESINFO = 261 - WALLET = 262 - TA = 32768 - DLV = 32769 - - @classmethod - def _maximum(cls): - return 65535 - - @classmethod - def _short_name(cls): - return "type" - - @classmethod - def _prefix(cls): - return "TYPE" - - @classmethod - def _extra_from_text(cls, text): - if text.find("-") >= 0: - try: - return cls[text.replace("-", "_")] - except KeyError: # pragma: no cover - pass - return _registered_by_text.get(text) - - @classmethod - def _extra_to_text(cls, value, current_text): - if current_text is None: - return _registered_by_value.get(value) - if current_text.find("_") >= 0: - return current_text.replace("_", "-") - return current_text - - @classmethod - def _unknown_exception_class(cls): - return UnknownRdatatype - - -_registered_by_text: Dict[str, RdataType] = {} -_registered_by_value: Dict[RdataType, str] = {} - -_metatypes = {RdataType.OPT} - -_singletons = { - RdataType.SOA, - RdataType.NXT, - RdataType.DNAME, - RdataType.NSEC, - RdataType.CNAME, -} - - -class UnknownRdatatype(dns.exception.DNSException): - """DNS resource record type is unknown.""" - - -def from_text(text: str) -> RdataType: - """Convert text into a DNS rdata type value. - - The input text can be a defined DNS RR type mnemonic or - instance of the DNS generic type syntax. - - For example, "NS" and "TYPE2" will both result in a value of 2. - - Raises ``dns.rdatatype.UnknownRdatatype`` if the type is unknown. - - Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535. - - Returns a ``dns.rdatatype.RdataType``. - """ - - return RdataType.from_text(text) - - -def to_text(value: RdataType) -> str: - """Convert a DNS rdata type value to text. - - If the value has a known mnemonic, it will be used, otherwise the - DNS generic type syntax will be used. - - Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535. - - Returns a ``str``. - """ - - return RdataType.to_text(value) - - -def is_metatype(rdtype: RdataType) -> bool: - """True if the specified type is a metatype. - - *rdtype* is a ``dns.rdatatype.RdataType``. - - The currently defined metatypes are TKEY, TSIG, IXFR, AXFR, MAILA, - MAILB, ANY, and OPT. - - Returns a ``bool``. - """ - - return (256 > rdtype >= 128) or rdtype in _metatypes - - -def is_singleton(rdtype: RdataType) -> bool: - """Is the specified type a singleton type? - - Singleton types can only have a single rdata in an rdataset, or a single - RR in an RRset. - - The currently defined singleton types are CNAME, DNAME, NSEC, NXT, and - SOA. - - *rdtype* is an ``int``. - - Returns a ``bool``. - """ - - if rdtype in _singletons: - return True - return False - - -# pylint: disable=redefined-outer-name -def register_type( - rdtype: RdataType, rdtype_text: str, is_singleton: bool = False -) -> None: - """Dynamically register an rdatatype. - - *rdtype*, a ``dns.rdatatype.RdataType``, the rdatatype to register. - - *rdtype_text*, a ``str``, the textual form of the rdatatype. - - *is_singleton*, a ``bool``, indicating if the type is a singleton (i.e. - RRsets of the type can have only one member.) - """ - - _registered_by_text[rdtype_text] = rdtype - _registered_by_value[rdtype] = rdtype_text - if is_singleton: - _singletons.add(rdtype) - - -### BEGIN generated RdataType constants - -TYPE0 = RdataType.TYPE0 -NONE = RdataType.NONE -A = RdataType.A -NS = RdataType.NS -MD = RdataType.MD -MF = RdataType.MF -CNAME = RdataType.CNAME -SOA = RdataType.SOA -MB = RdataType.MB -MG = RdataType.MG -MR = RdataType.MR -NULL = RdataType.NULL -WKS = RdataType.WKS -PTR = RdataType.PTR -HINFO = RdataType.HINFO -MINFO = RdataType.MINFO -MX = RdataType.MX -TXT = RdataType.TXT -RP = RdataType.RP -AFSDB = RdataType.AFSDB -X25 = RdataType.X25 -ISDN = RdataType.ISDN -RT = RdataType.RT -NSAP = RdataType.NSAP -NSAP_PTR = RdataType.NSAP_PTR -SIG = RdataType.SIG -KEY = RdataType.KEY -PX = RdataType.PX -GPOS = RdataType.GPOS -AAAA = RdataType.AAAA -LOC = RdataType.LOC -NXT = RdataType.NXT -SRV = RdataType.SRV -NAPTR = RdataType.NAPTR -KX = RdataType.KX -CERT = RdataType.CERT -A6 = RdataType.A6 -DNAME = RdataType.DNAME -OPT = RdataType.OPT -APL = RdataType.APL -DS = RdataType.DS -SSHFP = RdataType.SSHFP -IPSECKEY = RdataType.IPSECKEY -RRSIG = RdataType.RRSIG -NSEC = RdataType.NSEC -DNSKEY = RdataType.DNSKEY -DHCID = RdataType.DHCID -NSEC3 = RdataType.NSEC3 -NSEC3PARAM = RdataType.NSEC3PARAM -TLSA = RdataType.TLSA -SMIMEA = RdataType.SMIMEA -HIP = RdataType.HIP -NINFO = RdataType.NINFO -CDS = RdataType.CDS -CDNSKEY = RdataType.CDNSKEY -OPENPGPKEY = RdataType.OPENPGPKEY -CSYNC = RdataType.CSYNC -ZONEMD = RdataType.ZONEMD -SVCB = RdataType.SVCB -HTTPS = RdataType.HTTPS -SPF = RdataType.SPF -UNSPEC = RdataType.UNSPEC -NID = RdataType.NID -L32 = RdataType.L32 -L64 = RdataType.L64 -LP = RdataType.LP -EUI48 = RdataType.EUI48 -EUI64 = RdataType.EUI64 -TKEY = RdataType.TKEY -TSIG = RdataType.TSIG -IXFR = RdataType.IXFR -AXFR = RdataType.AXFR -MAILB = RdataType.MAILB -MAILA = RdataType.MAILA -ANY = RdataType.ANY -URI = RdataType.URI -CAA = RdataType.CAA -AVC = RdataType.AVC -AMTRELAY = RdataType.AMTRELAY -RESINFO = RdataType.RESINFO -WALLET = RdataType.WALLET -TA = RdataType.TA -DLV = RdataType.DLV - -### END generated RdataType constants diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/AFSDB.py b/venv/Lib/site-packages/dns/rdtypes/ANY/AFSDB.py deleted file mode 100644 index 06a3b97..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/AFSDB.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.mxbase - - -@dns.immutable.immutable -class AFSDB(dns.rdtypes.mxbase.UncompressedDowncasingMX): - """AFSDB record""" - - # Use the property mechanism to make "subtype" an alias for the - # "preference" attribute, and "hostname" an alias for the "exchange" - # attribute. - # - # This lets us inherit the UncompressedMX implementation but lets - # the caller use appropriate attribute names for the rdata type. - # - # We probably lose some performance vs. a cut-and-paste - # implementation, but this way we don't copy code, and that's - # good. - - @property - def subtype(self): - "the AFSDB subtype" - return self.preference - - @property - def hostname(self): - "the AFSDB hostname" - return self.exchange diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/AMTRELAY.py b/venv/Lib/site-packages/dns/rdtypes/ANY/AMTRELAY.py deleted file mode 100644 index ed2b072..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/AMTRELAY.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.rdtypes.util - - -class Relay(dns.rdtypes.util.Gateway): - name = "AMTRELAY relay" - - @property - def relay(self): - return self.gateway - - -@dns.immutable.immutable -class AMTRELAY(dns.rdata.Rdata): - """AMTRELAY record""" - - # see: RFC 8777 - - __slots__ = ["precedence", "discovery_optional", "relay_type", "relay"] - - def __init__( - self, rdclass, rdtype, precedence, discovery_optional, relay_type, relay - ): - super().__init__(rdclass, rdtype) - relay = Relay(relay_type, relay) - self.precedence = self._as_uint8(precedence) - self.discovery_optional = self._as_bool(discovery_optional) - self.relay_type = relay.type - self.relay = relay.relay - - def to_text(self, origin=None, relativize=True, **kw): - relay = Relay(self.relay_type, self.relay).to_text(origin, relativize) - return "%d %d %d %s" % ( - self.precedence, - self.discovery_optional, - self.relay_type, - relay, - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - precedence = tok.get_uint8() - discovery_optional = tok.get_uint8() - if discovery_optional > 1: - raise dns.exception.SyntaxError("expecting 0 or 1") - discovery_optional = bool(discovery_optional) - relay_type = tok.get_uint8() - if relay_type > 0x7F: - raise dns.exception.SyntaxError("expecting an integer <= 127") - relay = Relay.from_text(relay_type, tok, origin, relativize, relativize_to) - return cls( - rdclass, rdtype, precedence, discovery_optional, relay_type, relay.relay - ) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - relay_type = self.relay_type | (self.discovery_optional << 7) - header = struct.pack("!BB", self.precedence, relay_type) - file.write(header) - Relay(self.relay_type, self.relay).to_wire(file, compress, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (precedence, relay_type) = parser.get_struct("!BB") - discovery_optional = bool(relay_type >> 7) - relay_type &= 0x7F - relay = Relay.from_wire_parser(relay_type, parser, origin) - return cls( - rdclass, rdtype, precedence, discovery_optional, relay_type, relay.relay - ) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/AVC.py b/venv/Lib/site-packages/dns/rdtypes/ANY/AVC.py deleted file mode 100644 index a27ae2d..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/AVC.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2016 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.txtbase - - -@dns.immutable.immutable -class AVC(dns.rdtypes.txtbase.TXTBase): - """AVC record""" - - # See: IANA dns parameters for AVC diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/CAA.py b/venv/Lib/site-packages/dns/rdtypes/ANY/CAA.py deleted file mode 100644 index 2e6a7e7..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/CAA.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class CAA(dns.rdata.Rdata): - """CAA (Certification Authority Authorization) record""" - - # see: RFC 6844 - - __slots__ = ["flags", "tag", "value"] - - def __init__(self, rdclass, rdtype, flags, tag, value): - super().__init__(rdclass, rdtype) - self.flags = self._as_uint8(flags) - self.tag = self._as_bytes(tag, True, 255) - if not tag.isalnum(): - raise ValueError("tag is not alphanumeric") - self.value = self._as_bytes(value) - - def to_text(self, origin=None, relativize=True, **kw): - return '%u %s "%s"' % ( - self.flags, - dns.rdata._escapify(self.tag), - dns.rdata._escapify(self.value), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - flags = tok.get_uint8() - tag = tok.get_string().encode() - value = tok.get_string().encode() - return cls(rdclass, rdtype, flags, tag, value) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(struct.pack("!B", self.flags)) - l = len(self.tag) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.tag) - file.write(self.value) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - flags = parser.get_uint8() - tag = parser.get_counted_bytes() - value = parser.get_remaining() - return cls(rdclass, rdtype, flags, tag, value) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/CDNSKEY.py b/venv/Lib/site-packages/dns/rdtypes/ANY/CDNSKEY.py deleted file mode 100644 index b613409..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/CDNSKEY.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.dnskeybase # lgtm[py/import-and-import-from] - -# pylint: disable=unused-import -from dns.rdtypes.dnskeybase import ( # noqa: F401 lgtm[py/unused-import] - REVOKE, - SEP, - ZONE, -) - -# pylint: enable=unused-import - - -@dns.immutable.immutable -class CDNSKEY(dns.rdtypes.dnskeybase.DNSKEYBase): - """CDNSKEY record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/CDS.py b/venv/Lib/site-packages/dns/rdtypes/ANY/CDS.py deleted file mode 100644 index 8312b97..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/CDS.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.dsbase - - -@dns.immutable.immutable -class CDS(dns.rdtypes.dsbase.DSBase): - """CDS record""" - - _digest_length_by_type = { - **dns.rdtypes.dsbase.DSBase._digest_length_by_type, - 0: 1, # delete, RFC 8078 Sec. 4 (including Errata ID 5049) - } diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/CERT.py b/venv/Lib/site-packages/dns/rdtypes/ANY/CERT.py deleted file mode 100644 index f369cc8..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/CERT.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import struct - -import dns.dnssectypes -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - -_ctype_by_value = { - 1: "PKIX", - 2: "SPKI", - 3: "PGP", - 4: "IPKIX", - 5: "ISPKI", - 6: "IPGP", - 7: "ACPKIX", - 8: "IACPKIX", - 253: "URI", - 254: "OID", -} - -_ctype_by_name = { - "PKIX": 1, - "SPKI": 2, - "PGP": 3, - "IPKIX": 4, - "ISPKI": 5, - "IPGP": 6, - "ACPKIX": 7, - "IACPKIX": 8, - "URI": 253, - "OID": 254, -} - - -def _ctype_from_text(what): - v = _ctype_by_name.get(what) - if v is not None: - return v - return int(what) - - -def _ctype_to_text(what): - v = _ctype_by_value.get(what) - if v is not None: - return v - return str(what) - - -@dns.immutable.immutable -class CERT(dns.rdata.Rdata): - """CERT record""" - - # see RFC 4398 - - __slots__ = ["certificate_type", "key_tag", "algorithm", "certificate"] - - def __init__( - self, rdclass, rdtype, certificate_type, key_tag, algorithm, certificate - ): - super().__init__(rdclass, rdtype) - self.certificate_type = self._as_uint16(certificate_type) - self.key_tag = self._as_uint16(key_tag) - self.algorithm = self._as_uint8(algorithm) - self.certificate = self._as_bytes(certificate) - - def to_text(self, origin=None, relativize=True, **kw): - certificate_type = _ctype_to_text(self.certificate_type) - return "%s %d %s %s" % ( - certificate_type, - self.key_tag, - dns.dnssectypes.Algorithm.to_text(self.algorithm), - dns.rdata._base64ify(self.certificate, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - certificate_type = _ctype_from_text(tok.get_string()) - key_tag = tok.get_uint16() - algorithm = dns.dnssectypes.Algorithm.from_text(tok.get_string()) - b64 = tok.concatenate_remaining_identifiers().encode() - certificate = base64.b64decode(b64) - return cls(rdclass, rdtype, certificate_type, key_tag, algorithm, certificate) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - prefix = struct.pack( - "!HHB", self.certificate_type, self.key_tag, self.algorithm - ) - file.write(prefix) - file.write(self.certificate) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (certificate_type, key_tag, algorithm) = parser.get_struct("!HHB") - certificate = parser.get_remaining() - return cls(rdclass, rdtype, certificate_type, key_tag, algorithm, certificate) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/CNAME.py b/venv/Lib/site-packages/dns/rdtypes/ANY/CNAME.py deleted file mode 100644 index 665e407..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/CNAME.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.nsbase - - -@dns.immutable.immutable -class CNAME(dns.rdtypes.nsbase.NSBase): - """CNAME record - - Note: although CNAME is officially a singleton type, dnspython allows - non-singleton CNAME rdatasets because such sets have been commonly - used by BIND and other nameservers for load balancing.""" diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/CSYNC.py b/venv/Lib/site-packages/dns/rdtypes/ANY/CSYNC.py deleted file mode 100644 index 2f972f6..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/CSYNC.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011, 2016 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdatatype -import dns.rdtypes.util - - -@dns.immutable.immutable -class Bitmap(dns.rdtypes.util.Bitmap): - type_name = "CSYNC" - - -@dns.immutable.immutable -class CSYNC(dns.rdata.Rdata): - """CSYNC record""" - - __slots__ = ["serial", "flags", "windows"] - - def __init__(self, rdclass, rdtype, serial, flags, windows): - super().__init__(rdclass, rdtype) - self.serial = self._as_uint32(serial) - self.flags = self._as_uint16(flags) - if not isinstance(windows, Bitmap): - windows = Bitmap(windows) - self.windows = tuple(windows.windows) - - def to_text(self, origin=None, relativize=True, **kw): - text = Bitmap(self.windows).to_text() - return "%d %d%s" % (self.serial, self.flags, text) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - serial = tok.get_uint32() - flags = tok.get_uint16() - bitmap = Bitmap.from_text(tok) - return cls(rdclass, rdtype, serial, flags, bitmap) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(struct.pack("!IH", self.serial, self.flags)) - Bitmap(self.windows).to_wire(file) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (serial, flags) = parser.get_struct("!IH") - bitmap = Bitmap.from_wire_parser(parser) - return cls(rdclass, rdtype, serial, flags, bitmap) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/DLV.py b/venv/Lib/site-packages/dns/rdtypes/ANY/DLV.py deleted file mode 100644 index 6c134f1..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/DLV.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.dsbase - - -@dns.immutable.immutable -class DLV(dns.rdtypes.dsbase.DSBase): - """DLV record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/DNAME.py b/venv/Lib/site-packages/dns/rdtypes/ANY/DNAME.py deleted file mode 100644 index bbf9186..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/DNAME.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.nsbase - - -@dns.immutable.immutable -class DNAME(dns.rdtypes.nsbase.UncompressedNS): - """DNAME record""" - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.target.to_wire(file, None, origin, canonicalize) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/DNSKEY.py b/venv/Lib/site-packages/dns/rdtypes/ANY/DNSKEY.py deleted file mode 100644 index 6d961a9..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/DNSKEY.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.dnskeybase # lgtm[py/import-and-import-from] - -# pylint: disable=unused-import -from dns.rdtypes.dnskeybase import ( # noqa: F401 lgtm[py/unused-import] - REVOKE, - SEP, - ZONE, -) - -# pylint: enable=unused-import - - -@dns.immutable.immutable -class DNSKEY(dns.rdtypes.dnskeybase.DNSKEYBase): - """DNSKEY record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/DS.py b/venv/Lib/site-packages/dns/rdtypes/ANY/DS.py deleted file mode 100644 index 58b3108..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/DS.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.dsbase - - -@dns.immutable.immutable -class DS(dns.rdtypes.dsbase.DSBase): - """DS record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/EUI48.py b/venv/Lib/site-packages/dns/rdtypes/ANY/EUI48.py deleted file mode 100644 index c843be5..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/EUI48.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2015 Red Hat, Inc. -# Author: Petr Spacek -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.euibase - - -@dns.immutable.immutable -class EUI48(dns.rdtypes.euibase.EUIBase): - """EUI48 record""" - - # see: rfc7043.txt - - byte_len = 6 # 0123456789ab (in hex) - text_len = byte_len * 3 - 1 # 01-23-45-67-89-ab diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/EUI64.py b/venv/Lib/site-packages/dns/rdtypes/ANY/EUI64.py deleted file mode 100644 index f6d7e25..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/EUI64.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2015 Red Hat, Inc. -# Author: Petr Spacek -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.euibase - - -@dns.immutable.immutable -class EUI64(dns.rdtypes.euibase.EUIBase): - """EUI64 record""" - - # see: rfc7043.txt - - byte_len = 8 # 0123456789abcdef (in hex) - text_len = byte_len * 3 - 1 # 01-23-45-67-89-ab-cd-ef diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/GPOS.py b/venv/Lib/site-packages/dns/rdtypes/ANY/GPOS.py deleted file mode 100644 index d79f4a0..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/GPOS.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - - -def _validate_float_string(what): - if len(what) == 0: - raise dns.exception.FormError - if what[0] == b"-"[0] or what[0] == b"+"[0]: - what = what[1:] - if what.isdigit(): - return - try: - (left, right) = what.split(b".") - except ValueError: - raise dns.exception.FormError - if left == b"" and right == b"": - raise dns.exception.FormError - if not left == b"" and not left.decode().isdigit(): - raise dns.exception.FormError - if not right == b"" and not right.decode().isdigit(): - raise dns.exception.FormError - - -@dns.immutable.immutable -class GPOS(dns.rdata.Rdata): - """GPOS record""" - - # see: RFC 1712 - - __slots__ = ["latitude", "longitude", "altitude"] - - def __init__(self, rdclass, rdtype, latitude, longitude, altitude): - super().__init__(rdclass, rdtype) - if isinstance(latitude, float) or isinstance(latitude, int): - latitude = str(latitude) - if isinstance(longitude, float) or isinstance(longitude, int): - longitude = str(longitude) - if isinstance(altitude, float) or isinstance(altitude, int): - altitude = str(altitude) - latitude = self._as_bytes(latitude, True, 255) - longitude = self._as_bytes(longitude, True, 255) - altitude = self._as_bytes(altitude, True, 255) - _validate_float_string(latitude) - _validate_float_string(longitude) - _validate_float_string(altitude) - self.latitude = latitude - self.longitude = longitude - self.altitude = altitude - flat = self.float_latitude - if flat < -90.0 or flat > 90.0: - raise dns.exception.FormError("bad latitude") - flong = self.float_longitude - if flong < -180.0 or flong > 180.0: - raise dns.exception.FormError("bad longitude") - - def to_text(self, origin=None, relativize=True, **kw): - return ( - f"{self.latitude.decode()} {self.longitude.decode()} " - f"{self.altitude.decode()}" - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - latitude = tok.get_string() - longitude = tok.get_string() - altitude = tok.get_string() - return cls(rdclass, rdtype, latitude, longitude, altitude) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - l = len(self.latitude) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.latitude) - l = len(self.longitude) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.longitude) - l = len(self.altitude) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.altitude) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - latitude = parser.get_counted_bytes() - longitude = parser.get_counted_bytes() - altitude = parser.get_counted_bytes() - return cls(rdclass, rdtype, latitude, longitude, altitude) - - @property - def float_latitude(self): - "latitude as a floating point value" - return float(self.latitude) - - @property - def float_longitude(self): - "longitude as a floating point value" - return float(self.longitude) - - @property - def float_altitude(self): - "altitude as a floating point value" - return float(self.altitude) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/HINFO.py b/venv/Lib/site-packages/dns/rdtypes/ANY/HINFO.py deleted file mode 100644 index 06ad348..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/HINFO.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class HINFO(dns.rdata.Rdata): - """HINFO record""" - - # see: RFC 1035 - - __slots__ = ["cpu", "os"] - - def __init__(self, rdclass, rdtype, cpu, os): - super().__init__(rdclass, rdtype) - self.cpu = self._as_bytes(cpu, True, 255) - self.os = self._as_bytes(os, True, 255) - - def to_text(self, origin=None, relativize=True, **kw): - return f'"{dns.rdata._escapify(self.cpu)}" "{dns.rdata._escapify(self.os)}"' - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - cpu = tok.get_string(max_length=255) - os = tok.get_string(max_length=255) - return cls(rdclass, rdtype, cpu, os) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - l = len(self.cpu) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.cpu) - l = len(self.os) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.os) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - cpu = parser.get_counted_bytes() - os = parser.get_counted_bytes() - return cls(rdclass, rdtype, cpu, os) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/HIP.py b/venv/Lib/site-packages/dns/rdtypes/ANY/HIP.py deleted file mode 100644 index f3157da..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/HIP.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2010, 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import binascii -import struct - -import dns.exception -import dns.immutable -import dns.rdata -import dns.rdatatype - - -@dns.immutable.immutable -class HIP(dns.rdata.Rdata): - """HIP record""" - - # see: RFC 5205 - - __slots__ = ["hit", "algorithm", "key", "servers"] - - def __init__(self, rdclass, rdtype, hit, algorithm, key, servers): - super().__init__(rdclass, rdtype) - self.hit = self._as_bytes(hit, True, 255) - self.algorithm = self._as_uint8(algorithm) - self.key = self._as_bytes(key, True) - self.servers = self._as_tuple(servers, self._as_name) - - def to_text(self, origin=None, relativize=True, **kw): - hit = binascii.hexlify(self.hit).decode() - key = base64.b64encode(self.key).replace(b"\n", b"").decode() - text = "" - servers = [] - for server in self.servers: - servers.append(server.choose_relativity(origin, relativize)) - if len(servers) > 0: - text += " " + " ".join(x.to_unicode() for x in servers) - return "%u %s %s%s" % (self.algorithm, hit, key, text) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - algorithm = tok.get_uint8() - hit = binascii.unhexlify(tok.get_string().encode()) - key = base64.b64decode(tok.get_string().encode()) - servers = [] - for token in tok.get_remaining(): - server = tok.as_name(token, origin, relativize, relativize_to) - servers.append(server) - return cls(rdclass, rdtype, hit, algorithm, key, servers) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - lh = len(self.hit) - lk = len(self.key) - file.write(struct.pack("!BBH", lh, self.algorithm, lk)) - file.write(self.hit) - file.write(self.key) - for server in self.servers: - server.to_wire(file, None, origin, False) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (lh, algorithm, lk) = parser.get_struct("!BBH") - hit = parser.get_bytes(lh) - key = parser.get_bytes(lk) - servers = [] - while parser.remaining() > 0: - server = parser.get_name(origin) - servers.append(server) - return cls(rdclass, rdtype, hit, algorithm, key, servers) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/ISDN.py b/venv/Lib/site-packages/dns/rdtypes/ANY/ISDN.py deleted file mode 100644 index 6428a0a..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/ISDN.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class ISDN(dns.rdata.Rdata): - """ISDN record""" - - # see: RFC 1183 - - __slots__ = ["address", "subaddress"] - - def __init__(self, rdclass, rdtype, address, subaddress): - super().__init__(rdclass, rdtype) - self.address = self._as_bytes(address, True, 255) - self.subaddress = self._as_bytes(subaddress, True, 255) - - def to_text(self, origin=None, relativize=True, **kw): - if self.subaddress: - return ( - f'"{dns.rdata._escapify(self.address)}" ' - f'"{dns.rdata._escapify(self.subaddress)}"' - ) - else: - return f'"{dns.rdata._escapify(self.address)}"' - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - address = tok.get_string() - tokens = tok.get_remaining(max_tokens=1) - if len(tokens) >= 1: - subaddress = tokens[0].unescape().value - else: - subaddress = "" - return cls(rdclass, rdtype, address, subaddress) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - l = len(self.address) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.address) - l = len(self.subaddress) - if l > 0: - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.subaddress) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - address = parser.get_counted_bytes() - if parser.remaining() > 0: - subaddress = parser.get_counted_bytes() - else: - subaddress = b"" - return cls(rdclass, rdtype, address, subaddress) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/L32.py b/venv/Lib/site-packages/dns/rdtypes/ANY/L32.py deleted file mode 100644 index 09804c2..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/L32.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import struct - -import dns.immutable -import dns.rdata - - -@dns.immutable.immutable -class L32(dns.rdata.Rdata): - """L32 record""" - - # see: rfc6742.txt - - __slots__ = ["preference", "locator32"] - - def __init__(self, rdclass, rdtype, preference, locator32): - super().__init__(rdclass, rdtype) - self.preference = self._as_uint16(preference) - self.locator32 = self._as_ipv4_address(locator32) - - def to_text(self, origin=None, relativize=True, **kw): - return f"{self.preference} {self.locator32}" - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - preference = tok.get_uint16() - nodeid = tok.get_identifier() - return cls(rdclass, rdtype, preference, nodeid) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(struct.pack("!H", self.preference)) - file.write(dns.ipv4.inet_aton(self.locator32)) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - preference = parser.get_uint16() - locator32 = parser.get_remaining() - return cls(rdclass, rdtype, preference, locator32) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/L64.py b/venv/Lib/site-packages/dns/rdtypes/ANY/L64.py deleted file mode 100644 index fb76808..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/L64.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import struct - -import dns.immutable -import dns.rdtypes.util - - -@dns.immutable.immutable -class L64(dns.rdata.Rdata): - """L64 record""" - - # see: rfc6742.txt - - __slots__ = ["preference", "locator64"] - - def __init__(self, rdclass, rdtype, preference, locator64): - super().__init__(rdclass, rdtype) - self.preference = self._as_uint16(preference) - if isinstance(locator64, bytes): - if len(locator64) != 8: - raise ValueError("invalid locator64") - self.locator64 = dns.rdata._hexify(locator64, 4, b":") - else: - dns.rdtypes.util.parse_formatted_hex(locator64, 4, 4, ":") - self.locator64 = locator64 - - def to_text(self, origin=None, relativize=True, **kw): - return f"{self.preference} {self.locator64}" - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - preference = tok.get_uint16() - locator64 = tok.get_identifier() - return cls(rdclass, rdtype, preference, locator64) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(struct.pack("!H", self.preference)) - file.write(dns.rdtypes.util.parse_formatted_hex(self.locator64, 4, 4, ":")) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - preference = parser.get_uint16() - locator64 = parser.get_remaining() - return cls(rdclass, rdtype, preference, locator64) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/LOC.py b/venv/Lib/site-packages/dns/rdtypes/ANY/LOC.py deleted file mode 100644 index 1153cf0..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/LOC.py +++ /dev/null @@ -1,353 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.rdata - -_pows = tuple(10**i for i in range(0, 11)) - -# default values are in centimeters -_default_size = 100.0 -_default_hprec = 1000000.0 -_default_vprec = 1000.0 - -# for use by from_wire() -_MAX_LATITUDE = 0x80000000 + 90 * 3600000 -_MIN_LATITUDE = 0x80000000 - 90 * 3600000 -_MAX_LONGITUDE = 0x80000000 + 180 * 3600000 -_MIN_LONGITUDE = 0x80000000 - 180 * 3600000 - - -def _exponent_of(what, desc): - if what == 0: - return 0 - exp = None - for i, pow in enumerate(_pows): - if what < pow: - exp = i - 1 - break - if exp is None or exp < 0: - raise dns.exception.SyntaxError(f"{desc} value out of bounds") - return exp - - -def _float_to_tuple(what): - if what < 0: - sign = -1 - what *= -1 - else: - sign = 1 - what = round(what * 3600000) - degrees = int(what // 3600000) - what -= degrees * 3600000 - minutes = int(what // 60000) - what -= minutes * 60000 - seconds = int(what // 1000) - what -= int(seconds * 1000) - what = int(what) - return (degrees, minutes, seconds, what, sign) - - -def _tuple_to_float(what): - value = float(what[0]) - value += float(what[1]) / 60.0 - value += float(what[2]) / 3600.0 - value += float(what[3]) / 3600000.0 - return float(what[4]) * value - - -def _encode_size(what, desc): - what = int(what) - exponent = _exponent_of(what, desc) & 0xF - base = what // pow(10, exponent) & 0xF - return base * 16 + exponent - - -def _decode_size(what, desc): - exponent = what & 0x0F - if exponent > 9: - raise dns.exception.FormError(f"bad {desc} exponent") - base = (what & 0xF0) >> 4 - if base > 9: - raise dns.exception.FormError(f"bad {desc} base") - return base * pow(10, exponent) - - -def _check_coordinate_list(value, low, high): - if value[0] < low or value[0] > high: - raise ValueError(f"not in range [{low}, {high}]") - if value[1] < 0 or value[1] > 59: - raise ValueError("bad minutes value") - if value[2] < 0 or value[2] > 59: - raise ValueError("bad seconds value") - if value[3] < 0 or value[3] > 999: - raise ValueError("bad milliseconds value") - if value[4] != 1 and value[4] != -1: - raise ValueError("bad hemisphere value") - - -@dns.immutable.immutable -class LOC(dns.rdata.Rdata): - """LOC record""" - - # see: RFC 1876 - - __slots__ = [ - "latitude", - "longitude", - "altitude", - "size", - "horizontal_precision", - "vertical_precision", - ] - - def __init__( - self, - rdclass, - rdtype, - latitude, - longitude, - altitude, - size=_default_size, - hprec=_default_hprec, - vprec=_default_vprec, - ): - """Initialize a LOC record instance. - - The parameters I{latitude} and I{longitude} may be either a 4-tuple - of integers specifying (degrees, minutes, seconds, milliseconds), - or they may be floating point values specifying the number of - degrees. The other parameters are floats. Size, horizontal precision, - and vertical precision are specified in centimeters.""" - - super().__init__(rdclass, rdtype) - if isinstance(latitude, int): - latitude = float(latitude) - if isinstance(latitude, float): - latitude = _float_to_tuple(latitude) - _check_coordinate_list(latitude, -90, 90) - self.latitude = tuple(latitude) - if isinstance(longitude, int): - longitude = float(longitude) - if isinstance(longitude, float): - longitude = _float_to_tuple(longitude) - _check_coordinate_list(longitude, -180, 180) - self.longitude = tuple(longitude) - self.altitude = float(altitude) - self.size = float(size) - self.horizontal_precision = float(hprec) - self.vertical_precision = float(vprec) - - def to_text(self, origin=None, relativize=True, **kw): - if self.latitude[4] > 0: - lat_hemisphere = "N" - else: - lat_hemisphere = "S" - if self.longitude[4] > 0: - long_hemisphere = "E" - else: - long_hemisphere = "W" - text = "%d %d %d.%03d %s %d %d %d.%03d %s %0.2fm" % ( - self.latitude[0], - self.latitude[1], - self.latitude[2], - self.latitude[3], - lat_hemisphere, - self.longitude[0], - self.longitude[1], - self.longitude[2], - self.longitude[3], - long_hemisphere, - self.altitude / 100.0, - ) - - # do not print default values - if ( - self.size != _default_size - or self.horizontal_precision != _default_hprec - or self.vertical_precision != _default_vprec - ): - text += ( - f" {self.size / 100.0:0.2f}m {self.horizontal_precision / 100.0:0.2f}m" - f" {self.vertical_precision / 100.0:0.2f}m" - ) - return text - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - latitude = [0, 0, 0, 0, 1] - longitude = [0, 0, 0, 0, 1] - size = _default_size - hprec = _default_hprec - vprec = _default_vprec - - latitude[0] = tok.get_int() - t = tok.get_string() - if t.isdigit(): - latitude[1] = int(t) - t = tok.get_string() - if "." in t: - (seconds, milliseconds) = t.split(".") - if not seconds.isdigit(): - raise dns.exception.SyntaxError("bad latitude seconds value") - latitude[2] = int(seconds) - l = len(milliseconds) - if l == 0 or l > 3 or not milliseconds.isdigit(): - raise dns.exception.SyntaxError("bad latitude milliseconds value") - if l == 1: - m = 100 - elif l == 2: - m = 10 - else: - m = 1 - latitude[3] = m * int(milliseconds) - t = tok.get_string() - elif t.isdigit(): - latitude[2] = int(t) - t = tok.get_string() - if t == "S": - latitude[4] = -1 - elif t != "N": - raise dns.exception.SyntaxError("bad latitude hemisphere value") - - longitude[0] = tok.get_int() - t = tok.get_string() - if t.isdigit(): - longitude[1] = int(t) - t = tok.get_string() - if "." in t: - (seconds, milliseconds) = t.split(".") - if not seconds.isdigit(): - raise dns.exception.SyntaxError("bad longitude seconds value") - longitude[2] = int(seconds) - l = len(milliseconds) - if l == 0 or l > 3 or not milliseconds.isdigit(): - raise dns.exception.SyntaxError("bad longitude milliseconds value") - if l == 1: - m = 100 - elif l == 2: - m = 10 - else: - m = 1 - longitude[3] = m * int(milliseconds) - t = tok.get_string() - elif t.isdigit(): - longitude[2] = int(t) - t = tok.get_string() - if t == "W": - longitude[4] = -1 - elif t != "E": - raise dns.exception.SyntaxError("bad longitude hemisphere value") - - t = tok.get_string() - if t[-1] == "m": - t = t[0:-1] - altitude = float(t) * 100.0 # m -> cm - - tokens = tok.get_remaining(max_tokens=3) - if len(tokens) >= 1: - value = tokens[0].unescape().value - if value[-1] == "m": - value = value[0:-1] - size = float(value) * 100.0 # m -> cm - if len(tokens) >= 2: - value = tokens[1].unescape().value - if value[-1] == "m": - value = value[0:-1] - hprec = float(value) * 100.0 # m -> cm - if len(tokens) >= 3: - value = tokens[2].unescape().value - if value[-1] == "m": - value = value[0:-1] - vprec = float(value) * 100.0 # m -> cm - - # Try encoding these now so we raise if they are bad - _encode_size(size, "size") - _encode_size(hprec, "horizontal precision") - _encode_size(vprec, "vertical precision") - - return cls(rdclass, rdtype, latitude, longitude, altitude, size, hprec, vprec) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - milliseconds = ( - self.latitude[0] * 3600000 - + self.latitude[1] * 60000 - + self.latitude[2] * 1000 - + self.latitude[3] - ) * self.latitude[4] - latitude = 0x80000000 + milliseconds - milliseconds = ( - self.longitude[0] * 3600000 - + self.longitude[1] * 60000 - + self.longitude[2] * 1000 - + self.longitude[3] - ) * self.longitude[4] - longitude = 0x80000000 + milliseconds - altitude = int(self.altitude) + 10000000 - size = _encode_size(self.size, "size") - hprec = _encode_size(self.horizontal_precision, "horizontal precision") - vprec = _encode_size(self.vertical_precision, "vertical precision") - wire = struct.pack( - "!BBBBIII", 0, size, hprec, vprec, latitude, longitude, altitude - ) - file.write(wire) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - ( - version, - size, - hprec, - vprec, - latitude, - longitude, - altitude, - ) = parser.get_struct("!BBBBIII") - if version != 0: - raise dns.exception.FormError("LOC version not zero") - if latitude < _MIN_LATITUDE or latitude > _MAX_LATITUDE: - raise dns.exception.FormError("bad latitude") - if latitude > 0x80000000: - latitude = (latitude - 0x80000000) / 3600000 - else: - latitude = -1 * (0x80000000 - latitude) / 3600000 - if longitude < _MIN_LONGITUDE or longitude > _MAX_LONGITUDE: - raise dns.exception.FormError("bad longitude") - if longitude > 0x80000000: - longitude = (longitude - 0x80000000) / 3600000 - else: - longitude = -1 * (0x80000000 - longitude) / 3600000 - altitude = float(altitude) - 10000000.0 - size = _decode_size(size, "size") - hprec = _decode_size(hprec, "horizontal precision") - vprec = _decode_size(vprec, "vertical precision") - return cls(rdclass, rdtype, latitude, longitude, altitude, size, hprec, vprec) - - @property - def float_latitude(self): - "latitude as a floating point value" - return _tuple_to_float(self.latitude) - - @property - def float_longitude(self): - "longitude as a floating point value" - return _tuple_to_float(self.longitude) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/LP.py b/venv/Lib/site-packages/dns/rdtypes/ANY/LP.py deleted file mode 100644 index 312663f..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/LP.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import struct - -import dns.immutable -import dns.rdata - - -@dns.immutable.immutable -class LP(dns.rdata.Rdata): - """LP record""" - - # see: rfc6742.txt - - __slots__ = ["preference", "fqdn"] - - def __init__(self, rdclass, rdtype, preference, fqdn): - super().__init__(rdclass, rdtype) - self.preference = self._as_uint16(preference) - self.fqdn = self._as_name(fqdn) - - def to_text(self, origin=None, relativize=True, **kw): - fqdn = self.fqdn.choose_relativity(origin, relativize) - return "%d %s" % (self.preference, fqdn) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - preference = tok.get_uint16() - fqdn = tok.get_name(origin, relativize, relativize_to) - return cls(rdclass, rdtype, preference, fqdn) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(struct.pack("!H", self.preference)) - self.fqdn.to_wire(file, compress, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - preference = parser.get_uint16() - fqdn = parser.get_name(origin) - return cls(rdclass, rdtype, preference, fqdn) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/MX.py b/venv/Lib/site-packages/dns/rdtypes/ANY/MX.py deleted file mode 100644 index 0c300c5..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/MX.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.mxbase - - -@dns.immutable.immutable -class MX(dns.rdtypes.mxbase.MXBase): - """MX record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/NID.py b/venv/Lib/site-packages/dns/rdtypes/ANY/NID.py deleted file mode 100644 index 2f64917..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/NID.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import struct - -import dns.immutable -import dns.rdtypes.util - - -@dns.immutable.immutable -class NID(dns.rdata.Rdata): - """NID record""" - - # see: rfc6742.txt - - __slots__ = ["preference", "nodeid"] - - def __init__(self, rdclass, rdtype, preference, nodeid): - super().__init__(rdclass, rdtype) - self.preference = self._as_uint16(preference) - if isinstance(nodeid, bytes): - if len(nodeid) != 8: - raise ValueError("invalid nodeid") - self.nodeid = dns.rdata._hexify(nodeid, 4, b":") - else: - dns.rdtypes.util.parse_formatted_hex(nodeid, 4, 4, ":") - self.nodeid = nodeid - - def to_text(self, origin=None, relativize=True, **kw): - return f"{self.preference} {self.nodeid}" - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - preference = tok.get_uint16() - nodeid = tok.get_identifier() - return cls(rdclass, rdtype, preference, nodeid) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(struct.pack("!H", self.preference)) - file.write(dns.rdtypes.util.parse_formatted_hex(self.nodeid, 4, 4, ":")) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - preference = parser.get_uint16() - nodeid = parser.get_remaining() - return cls(rdclass, rdtype, preference, nodeid) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/NINFO.py b/venv/Lib/site-packages/dns/rdtypes/ANY/NINFO.py deleted file mode 100644 index b177bdd..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/NINFO.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.txtbase - - -@dns.immutable.immutable -class NINFO(dns.rdtypes.txtbase.TXTBase): - """NINFO record""" - - # see: draft-reid-dnsext-zs-01 diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/NS.py b/venv/Lib/site-packages/dns/rdtypes/ANY/NS.py deleted file mode 100644 index c3f34ce..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/NS.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.nsbase - - -@dns.immutable.immutable -class NS(dns.rdtypes.nsbase.NSBase): - """NS record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/NSEC.py b/venv/Lib/site-packages/dns/rdtypes/ANY/NSEC.py deleted file mode 100644 index 3c78b72..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/NSEC.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdatatype -import dns.rdtypes.util - - -@dns.immutable.immutable -class Bitmap(dns.rdtypes.util.Bitmap): - type_name = "NSEC" - - -@dns.immutable.immutable -class NSEC(dns.rdata.Rdata): - """NSEC record""" - - __slots__ = ["next", "windows"] - - def __init__(self, rdclass, rdtype, next, windows): - super().__init__(rdclass, rdtype) - self.next = self._as_name(next) - if not isinstance(windows, Bitmap): - windows = Bitmap(windows) - self.windows = tuple(windows.windows) - - def to_text(self, origin=None, relativize=True, **kw): - next = self.next.choose_relativity(origin, relativize) - text = Bitmap(self.windows).to_text() - return f"{next}{text}" - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - next = tok.get_name(origin, relativize, relativize_to) - windows = Bitmap.from_text(tok) - return cls(rdclass, rdtype, next, windows) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - # Note that NSEC downcasing, originally mandated by RFC 4034 - # section 6.2 was removed by RFC 6840 section 5.1. - self.next.to_wire(file, None, origin, False) - Bitmap(self.windows).to_wire(file) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - next = parser.get_name(origin) - bitmap = Bitmap.from_wire_parser(parser) - return cls(rdclass, rdtype, next, bitmap) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/NSEC3.py b/venv/Lib/site-packages/dns/rdtypes/ANY/NSEC3.py deleted file mode 100644 index d71302b..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/NSEC3.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import binascii -import struct - -import dns.exception -import dns.immutable -import dns.rdata -import dns.rdatatype -import dns.rdtypes.util - -b32_hex_to_normal = bytes.maketrans( - b"0123456789ABCDEFGHIJKLMNOPQRSTUV", b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" -) -b32_normal_to_hex = bytes.maketrans( - b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", b"0123456789ABCDEFGHIJKLMNOPQRSTUV" -) - -# hash algorithm constants -SHA1 = 1 - -# flag constants -OPTOUT = 1 - - -@dns.immutable.immutable -class Bitmap(dns.rdtypes.util.Bitmap): - type_name = "NSEC3" - - -@dns.immutable.immutable -class NSEC3(dns.rdata.Rdata): - """NSEC3 record""" - - __slots__ = ["algorithm", "flags", "iterations", "salt", "next", "windows"] - - def __init__( - self, rdclass, rdtype, algorithm, flags, iterations, salt, next, windows - ): - super().__init__(rdclass, rdtype) - self.algorithm = self._as_uint8(algorithm) - self.flags = self._as_uint8(flags) - self.iterations = self._as_uint16(iterations) - self.salt = self._as_bytes(salt, True, 255) - self.next = self._as_bytes(next, True, 255) - if not isinstance(windows, Bitmap): - windows = Bitmap(windows) - self.windows = tuple(windows.windows) - - def _next_text(self): - next = base64.b32encode(self.next).translate(b32_normal_to_hex).lower().decode() - next = next.rstrip("=") - return next - - def to_text(self, origin=None, relativize=True, **kw): - next = self._next_text() - if self.salt == b"": - salt = "-" - else: - salt = binascii.hexlify(self.salt).decode() - text = Bitmap(self.windows).to_text() - return "%u %u %u %s %s%s" % ( - self.algorithm, - self.flags, - self.iterations, - salt, - next, - text, - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - algorithm = tok.get_uint8() - flags = tok.get_uint8() - iterations = tok.get_uint16() - salt = tok.get_string() - if salt == "-": - salt = b"" - else: - salt = binascii.unhexlify(salt.encode("ascii")) - next = tok.get_string().encode("ascii").upper().translate(b32_hex_to_normal) - if next.endswith(b"="): - raise binascii.Error("Incorrect padding") - if len(next) % 8 != 0: - next += b"=" * (8 - len(next) % 8) - next = base64.b32decode(next) - bitmap = Bitmap.from_text(tok) - return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, bitmap) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - l = len(self.salt) - file.write(struct.pack("!BBHB", self.algorithm, self.flags, self.iterations, l)) - file.write(self.salt) - l = len(self.next) - file.write(struct.pack("!B", l)) - file.write(self.next) - Bitmap(self.windows).to_wire(file) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (algorithm, flags, iterations) = parser.get_struct("!BBH") - salt = parser.get_counted_bytes() - next = parser.get_counted_bytes() - bitmap = Bitmap.from_wire_parser(parser) - return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, bitmap) - - def next_name(self, origin=None): - return dns.name.from_text(self._next_text(), origin) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py b/venv/Lib/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py deleted file mode 100644 index d1e62eb..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import binascii -import struct - -import dns.exception -import dns.immutable -import dns.rdata - - -@dns.immutable.immutable -class NSEC3PARAM(dns.rdata.Rdata): - """NSEC3PARAM record""" - - __slots__ = ["algorithm", "flags", "iterations", "salt"] - - def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt): - super().__init__(rdclass, rdtype) - self.algorithm = self._as_uint8(algorithm) - self.flags = self._as_uint8(flags) - self.iterations = self._as_uint16(iterations) - self.salt = self._as_bytes(salt, True, 255) - - def to_text(self, origin=None, relativize=True, **kw): - if self.salt == b"": - salt = "-" - else: - salt = binascii.hexlify(self.salt).decode() - return "%u %u %u %s" % (self.algorithm, self.flags, self.iterations, salt) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - algorithm = tok.get_uint8() - flags = tok.get_uint8() - iterations = tok.get_uint16() - salt = tok.get_string() - if salt == "-": - salt = "" - else: - salt = binascii.unhexlify(salt.encode()) - return cls(rdclass, rdtype, algorithm, flags, iterations, salt) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - l = len(self.salt) - file.write(struct.pack("!BBHB", self.algorithm, self.flags, self.iterations, l)) - file.write(self.salt) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (algorithm, flags, iterations) = parser.get_struct("!BBH") - salt = parser.get_counted_bytes() - return cls(rdclass, rdtype, algorithm, flags, iterations, salt) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py b/venv/Lib/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py deleted file mode 100644 index 4d7a4b6..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2016 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 - -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class OPENPGPKEY(dns.rdata.Rdata): - """OPENPGPKEY record""" - - # see: RFC 7929 - - def __init__(self, rdclass, rdtype, key): - super().__init__(rdclass, rdtype) - self.key = self._as_bytes(key) - - def to_text(self, origin=None, relativize=True, **kw): - return dns.rdata._base64ify(self.key, chunksize=None, **kw) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - b64 = tok.concatenate_remaining_identifiers().encode() - key = base64.b64decode(b64) - return cls(rdclass, rdtype, key) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(self.key) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - key = parser.get_remaining() - return cls(rdclass, rdtype, key) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/OPT.py b/venv/Lib/site-packages/dns/rdtypes/ANY/OPT.py deleted file mode 100644 index d343dfa..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/OPT.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.edns -import dns.exception -import dns.immutable -import dns.rdata - -# We don't implement from_text, and that's ok. -# pylint: disable=abstract-method - - -@dns.immutable.immutable -class OPT(dns.rdata.Rdata): - """OPT record""" - - __slots__ = ["options"] - - def __init__(self, rdclass, rdtype, options): - """Initialize an OPT rdata. - - *rdclass*, an ``int`` is the rdataclass of the Rdata, - which is also the payload size. - - *rdtype*, an ``int`` is the rdatatype of the Rdata. - - *options*, a tuple of ``bytes`` - """ - - super().__init__(rdclass, rdtype) - - def as_option(option): - if not isinstance(option, dns.edns.Option): - raise ValueError("option is not a dns.edns.option") - return option - - self.options = self._as_tuple(options, as_option) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - for opt in self.options: - owire = opt.to_wire() - file.write(struct.pack("!HH", opt.otype, len(owire))) - file.write(owire) - - def to_text(self, origin=None, relativize=True, **kw): - return " ".join(opt.to_text() for opt in self.options) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - options = [] - while parser.remaining() > 0: - (otype, olen) = parser.get_struct("!HH") - with parser.restrict_to(olen): - opt = dns.edns.option_from_wire_parser(otype, parser) - options.append(opt) - return cls(rdclass, rdtype, options) - - @property - def payload(self): - "payload size" - return self.rdclass diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/PTR.py b/venv/Lib/site-packages/dns/rdtypes/ANY/PTR.py deleted file mode 100644 index 98c3616..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/PTR.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.nsbase - - -@dns.immutable.immutable -class PTR(dns.rdtypes.nsbase.NSBase): - """PTR record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/RESINFO.py b/venv/Lib/site-packages/dns/rdtypes/ANY/RESINFO.py deleted file mode 100644 index 76c8ea2..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/RESINFO.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.txtbase - - -@dns.immutable.immutable -class RESINFO(dns.rdtypes.txtbase.TXTBase): - """RESINFO record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/RP.py b/venv/Lib/site-packages/dns/rdtypes/ANY/RP.py deleted file mode 100644 index a66cfc5..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/RP.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata - - -@dns.immutable.immutable -class RP(dns.rdata.Rdata): - """RP record""" - - # see: RFC 1183 - - __slots__ = ["mbox", "txt"] - - def __init__(self, rdclass, rdtype, mbox, txt): - super().__init__(rdclass, rdtype) - self.mbox = self._as_name(mbox) - self.txt = self._as_name(txt) - - def to_text(self, origin=None, relativize=True, **kw): - mbox = self.mbox.choose_relativity(origin, relativize) - txt = self.txt.choose_relativity(origin, relativize) - return f"{str(mbox)} {str(txt)}" - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - mbox = tok.get_name(origin, relativize, relativize_to) - txt = tok.get_name(origin, relativize, relativize_to) - return cls(rdclass, rdtype, mbox, txt) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.mbox.to_wire(file, None, origin, canonicalize) - self.txt.to_wire(file, None, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - mbox = parser.get_name(origin) - txt = parser.get_name(origin) - return cls(rdclass, rdtype, mbox, txt) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/RRSIG.py b/venv/Lib/site-packages/dns/rdtypes/ANY/RRSIG.py deleted file mode 100644 index 8beb423..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/RRSIG.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import calendar -import struct -import time - -import dns.dnssectypes -import dns.exception -import dns.immutable -import dns.rdata -import dns.rdatatype - - -class BadSigTime(dns.exception.DNSException): - """Time in DNS SIG or RRSIG resource record cannot be parsed.""" - - -def sigtime_to_posixtime(what): - if len(what) <= 10 and what.isdigit(): - return int(what) - if len(what) != 14: - raise BadSigTime - year = int(what[0:4]) - month = int(what[4:6]) - day = int(what[6:8]) - hour = int(what[8:10]) - minute = int(what[10:12]) - second = int(what[12:14]) - return calendar.timegm((year, month, day, hour, minute, second, 0, 0, 0)) - - -def posixtime_to_sigtime(what): - return time.strftime("%Y%m%d%H%M%S", time.gmtime(what)) - - -@dns.immutable.immutable -class RRSIG(dns.rdata.Rdata): - """RRSIG record""" - - __slots__ = [ - "type_covered", - "algorithm", - "labels", - "original_ttl", - "expiration", - "inception", - "key_tag", - "signer", - "signature", - ] - - def __init__( - self, - rdclass, - rdtype, - type_covered, - algorithm, - labels, - original_ttl, - expiration, - inception, - key_tag, - signer, - signature, - ): - super().__init__(rdclass, rdtype) - self.type_covered = self._as_rdatatype(type_covered) - self.algorithm = dns.dnssectypes.Algorithm.make(algorithm) - self.labels = self._as_uint8(labels) - self.original_ttl = self._as_ttl(original_ttl) - self.expiration = self._as_uint32(expiration) - self.inception = self._as_uint32(inception) - self.key_tag = self._as_uint16(key_tag) - self.signer = self._as_name(signer) - self.signature = self._as_bytes(signature) - - def covers(self): - return self.type_covered - - def to_text(self, origin=None, relativize=True, **kw): - return "%s %d %d %d %s %s %d %s %s" % ( - dns.rdatatype.to_text(self.type_covered), - self.algorithm, - self.labels, - self.original_ttl, - posixtime_to_sigtime(self.expiration), - posixtime_to_sigtime(self.inception), - self.key_tag, - self.signer.choose_relativity(origin, relativize), - dns.rdata._base64ify(self.signature, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - type_covered = dns.rdatatype.from_text(tok.get_string()) - algorithm = dns.dnssectypes.Algorithm.from_text(tok.get_string()) - labels = tok.get_int() - original_ttl = tok.get_ttl() - expiration = sigtime_to_posixtime(tok.get_string()) - inception = sigtime_to_posixtime(tok.get_string()) - key_tag = tok.get_int() - signer = tok.get_name(origin, relativize, relativize_to) - b64 = tok.concatenate_remaining_identifiers().encode() - signature = base64.b64decode(b64) - return cls( - rdclass, - rdtype, - type_covered, - algorithm, - labels, - original_ttl, - expiration, - inception, - key_tag, - signer, - signature, - ) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - header = struct.pack( - "!HBBIIIH", - self.type_covered, - self.algorithm, - self.labels, - self.original_ttl, - self.expiration, - self.inception, - self.key_tag, - ) - file.write(header) - self.signer.to_wire(file, None, origin, canonicalize) - file.write(self.signature) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - header = parser.get_struct("!HBBIIIH") - signer = parser.get_name(origin) - signature = parser.get_remaining() - return cls(rdclass, rdtype, *header, signer, signature) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/RT.py b/venv/Lib/site-packages/dns/rdtypes/ANY/RT.py deleted file mode 100644 index 5a4d45c..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/RT.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.mxbase - - -@dns.immutable.immutable -class RT(dns.rdtypes.mxbase.UncompressedDowncasingMX): - """RT record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/SMIMEA.py b/venv/Lib/site-packages/dns/rdtypes/ANY/SMIMEA.py deleted file mode 100644 index 55d87bf..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/SMIMEA.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import dns.immutable -import dns.rdtypes.tlsabase - - -@dns.immutable.immutable -class SMIMEA(dns.rdtypes.tlsabase.TLSABase): - """SMIMEA record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/SOA.py b/venv/Lib/site-packages/dns/rdtypes/ANY/SOA.py deleted file mode 100644 index 09aa832..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/SOA.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata - - -@dns.immutable.immutable -class SOA(dns.rdata.Rdata): - """SOA record""" - - # see: RFC 1035 - - __slots__ = ["mname", "rname", "serial", "refresh", "retry", "expire", "minimum"] - - def __init__( - self, rdclass, rdtype, mname, rname, serial, refresh, retry, expire, minimum - ): - super().__init__(rdclass, rdtype) - self.mname = self._as_name(mname) - self.rname = self._as_name(rname) - self.serial = self._as_uint32(serial) - self.refresh = self._as_ttl(refresh) - self.retry = self._as_ttl(retry) - self.expire = self._as_ttl(expire) - self.minimum = self._as_ttl(minimum) - - def to_text(self, origin=None, relativize=True, **kw): - mname = self.mname.choose_relativity(origin, relativize) - rname = self.rname.choose_relativity(origin, relativize) - return "%s %s %d %d %d %d %d" % ( - mname, - rname, - self.serial, - self.refresh, - self.retry, - self.expire, - self.minimum, - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - mname = tok.get_name(origin, relativize, relativize_to) - rname = tok.get_name(origin, relativize, relativize_to) - serial = tok.get_uint32() - refresh = tok.get_ttl() - retry = tok.get_ttl() - expire = tok.get_ttl() - minimum = tok.get_ttl() - return cls( - rdclass, rdtype, mname, rname, serial, refresh, retry, expire, minimum - ) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.mname.to_wire(file, compress, origin, canonicalize) - self.rname.to_wire(file, compress, origin, canonicalize) - five_ints = struct.pack( - "!IIIII", self.serial, self.refresh, self.retry, self.expire, self.minimum - ) - file.write(five_ints) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - mname = parser.get_name(origin) - rname = parser.get_name(origin) - return cls(rdclass, rdtype, mname, rname, *parser.get_struct("!IIIII")) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/SPF.py b/venv/Lib/site-packages/dns/rdtypes/ANY/SPF.py deleted file mode 100644 index 1df3b70..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/SPF.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.txtbase - - -@dns.immutable.immutable -class SPF(dns.rdtypes.txtbase.TXTBase): - """SPF record""" - - # see: RFC 4408 diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/SSHFP.py b/venv/Lib/site-packages/dns/rdtypes/ANY/SSHFP.py deleted file mode 100644 index d2c4b07..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/SSHFP.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import binascii -import struct - -import dns.immutable -import dns.rdata -import dns.rdatatype - - -@dns.immutable.immutable -class SSHFP(dns.rdata.Rdata): - """SSHFP record""" - - # See RFC 4255 - - __slots__ = ["algorithm", "fp_type", "fingerprint"] - - def __init__(self, rdclass, rdtype, algorithm, fp_type, fingerprint): - super().__init__(rdclass, rdtype) - self.algorithm = self._as_uint8(algorithm) - self.fp_type = self._as_uint8(fp_type) - self.fingerprint = self._as_bytes(fingerprint, True) - - def to_text(self, origin=None, relativize=True, **kw): - kw = kw.copy() - chunksize = kw.pop("chunksize", 128) - return "%d %d %s" % ( - self.algorithm, - self.fp_type, - dns.rdata._hexify(self.fingerprint, chunksize=chunksize, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - algorithm = tok.get_uint8() - fp_type = tok.get_uint8() - fingerprint = tok.concatenate_remaining_identifiers().encode() - fingerprint = binascii.unhexlify(fingerprint) - return cls(rdclass, rdtype, algorithm, fp_type, fingerprint) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - header = struct.pack("!BB", self.algorithm, self.fp_type) - file.write(header) - file.write(self.fingerprint) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - header = parser.get_struct("BB") - fingerprint = parser.get_remaining() - return cls(rdclass, rdtype, header[0], header[1], fingerprint) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/TKEY.py b/venv/Lib/site-packages/dns/rdtypes/ANY/TKEY.py deleted file mode 100644 index 75f6224..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/TKEY.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import struct - -import dns.exception -import dns.immutable -import dns.rdata - - -@dns.immutable.immutable -class TKEY(dns.rdata.Rdata): - """TKEY Record""" - - __slots__ = [ - "algorithm", - "inception", - "expiration", - "mode", - "error", - "key", - "other", - ] - - def __init__( - self, - rdclass, - rdtype, - algorithm, - inception, - expiration, - mode, - error, - key, - other=b"", - ): - super().__init__(rdclass, rdtype) - self.algorithm = self._as_name(algorithm) - self.inception = self._as_uint32(inception) - self.expiration = self._as_uint32(expiration) - self.mode = self._as_uint16(mode) - self.error = self._as_uint16(error) - self.key = self._as_bytes(key) - self.other = self._as_bytes(other) - - def to_text(self, origin=None, relativize=True, **kw): - _algorithm = self.algorithm.choose_relativity(origin, relativize) - text = "%s %u %u %u %u %s" % ( - str(_algorithm), - self.inception, - self.expiration, - self.mode, - self.error, - dns.rdata._base64ify(self.key, 0), - ) - if len(self.other) > 0: - text += f" {dns.rdata._base64ify(self.other, 0)}" - - return text - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - algorithm = tok.get_name(relativize=False) - inception = tok.get_uint32() - expiration = tok.get_uint32() - mode = tok.get_uint16() - error = tok.get_uint16() - key_b64 = tok.get_string().encode() - key = base64.b64decode(key_b64) - other_b64 = tok.concatenate_remaining_identifiers(True).encode() - other = base64.b64decode(other_b64) - - return cls( - rdclass, rdtype, algorithm, inception, expiration, mode, error, key, other - ) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.algorithm.to_wire(file, compress, origin) - file.write( - struct.pack("!IIHH", self.inception, self.expiration, self.mode, self.error) - ) - file.write(struct.pack("!H", len(self.key))) - file.write(self.key) - file.write(struct.pack("!H", len(self.other))) - if len(self.other) > 0: - file.write(self.other) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - algorithm = parser.get_name(origin) - inception, expiration, mode, error = parser.get_struct("!IIHH") - key = parser.get_counted_bytes(2) - other = parser.get_counted_bytes(2) - - return cls( - rdclass, rdtype, algorithm, inception, expiration, mode, error, key, other - ) - - # Constants for the mode field - from RFC 2930: - # 2.5 The Mode Field - # - # The mode field specifies the general scheme for key agreement or - # the purpose of the TKEY DNS message. Servers and resolvers - # supporting this specification MUST implement the Diffie-Hellman key - # agreement mode and the key deletion mode for queries. All other - # modes are OPTIONAL. A server supporting TKEY that receives a TKEY - # request with a mode it does not support returns the BADMODE error. - # The following values of the Mode octet are defined, available, or - # reserved: - # - # Value Description - # ----- ----------- - # 0 - reserved, see section 7 - # 1 server assignment - # 2 Diffie-Hellman exchange - # 3 GSS-API negotiation - # 4 resolver assignment - # 5 key deletion - # 6-65534 - available, see section 7 - # 65535 - reserved, see section 7 - SERVER_ASSIGNMENT = 1 - DIFFIE_HELLMAN_EXCHANGE = 2 - GSSAPI_NEGOTIATION = 3 - RESOLVER_ASSIGNMENT = 4 - KEY_DELETION = 5 diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/TLSA.py b/venv/Lib/site-packages/dns/rdtypes/ANY/TLSA.py deleted file mode 100644 index 4dffc55..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/TLSA.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import dns.immutable -import dns.rdtypes.tlsabase - - -@dns.immutable.immutable -class TLSA(dns.rdtypes.tlsabase.TLSABase): - """TLSA record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/TSIG.py b/venv/Lib/site-packages/dns/rdtypes/ANY/TSIG.py deleted file mode 100644 index 7942382..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/TSIG.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import struct - -import dns.exception -import dns.immutable -import dns.rcode -import dns.rdata - - -@dns.immutable.immutable -class TSIG(dns.rdata.Rdata): - """TSIG record""" - - __slots__ = [ - "algorithm", - "time_signed", - "fudge", - "mac", - "original_id", - "error", - "other", - ] - - def __init__( - self, - rdclass, - rdtype, - algorithm, - time_signed, - fudge, - mac, - original_id, - error, - other, - ): - """Initialize a TSIG rdata. - - *rdclass*, an ``int`` is the rdataclass of the Rdata. - - *rdtype*, an ``int`` is the rdatatype of the Rdata. - - *algorithm*, a ``dns.name.Name``. - - *time_signed*, an ``int``. - - *fudge*, an ``int`. - - *mac*, a ``bytes`` - - *original_id*, an ``int`` - - *error*, an ``int`` - - *other*, a ``bytes`` - """ - - super().__init__(rdclass, rdtype) - self.algorithm = self._as_name(algorithm) - self.time_signed = self._as_uint48(time_signed) - self.fudge = self._as_uint16(fudge) - self.mac = self._as_bytes(mac) - self.original_id = self._as_uint16(original_id) - self.error = dns.rcode.Rcode.make(error) - self.other = self._as_bytes(other) - - def to_text(self, origin=None, relativize=True, **kw): - algorithm = self.algorithm.choose_relativity(origin, relativize) - error = dns.rcode.to_text(self.error, True) - text = ( - f"{algorithm} {self.time_signed} {self.fudge} " - + f"{len(self.mac)} {dns.rdata._base64ify(self.mac, 0)} " - + f"{self.original_id} {error} {len(self.other)}" - ) - if self.other: - text += f" {dns.rdata._base64ify(self.other, 0)}" - return text - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - algorithm = tok.get_name(relativize=False) - time_signed = tok.get_uint48() - fudge = tok.get_uint16() - mac_len = tok.get_uint16() - mac = base64.b64decode(tok.get_string()) - if len(mac) != mac_len: - raise SyntaxError("invalid MAC") - original_id = tok.get_uint16() - error = dns.rcode.from_text(tok.get_string()) - other_len = tok.get_uint16() - if other_len > 0: - other = base64.b64decode(tok.get_string()) - if len(other) != other_len: - raise SyntaxError("invalid other data") - else: - other = b"" - return cls( - rdclass, - rdtype, - algorithm, - time_signed, - fudge, - mac, - original_id, - error, - other, - ) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.algorithm.to_wire(file, None, origin, False) - file.write( - struct.pack( - "!HIHH", - (self.time_signed >> 32) & 0xFFFF, - self.time_signed & 0xFFFFFFFF, - self.fudge, - len(self.mac), - ) - ) - file.write(self.mac) - file.write(struct.pack("!HHH", self.original_id, self.error, len(self.other))) - file.write(self.other) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - algorithm = parser.get_name() - time_signed = parser.get_uint48() - fudge = parser.get_uint16() - mac = parser.get_counted_bytes(2) - (original_id, error) = parser.get_struct("!HH") - other = parser.get_counted_bytes(2) - return cls( - rdclass, - rdtype, - algorithm, - time_signed, - fudge, - mac, - original_id, - error, - other, - ) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/TXT.py b/venv/Lib/site-packages/dns/rdtypes/ANY/TXT.py deleted file mode 100644 index 6d4dae2..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/TXT.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.txtbase - - -@dns.immutable.immutable -class TXT(dns.rdtypes.txtbase.TXTBase): - """TXT record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/URI.py b/venv/Lib/site-packages/dns/rdtypes/ANY/URI.py deleted file mode 100644 index 2efbb30..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/URI.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# Copyright (C) 2015 Red Hat, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdtypes.util - - -@dns.immutable.immutable -class URI(dns.rdata.Rdata): - """URI record""" - - # see RFC 7553 - - __slots__ = ["priority", "weight", "target"] - - def __init__(self, rdclass, rdtype, priority, weight, target): - super().__init__(rdclass, rdtype) - self.priority = self._as_uint16(priority) - self.weight = self._as_uint16(weight) - self.target = self._as_bytes(target, True) - if len(self.target) == 0: - raise dns.exception.SyntaxError("URI target cannot be empty") - - def to_text(self, origin=None, relativize=True, **kw): - return '%d %d "%s"' % (self.priority, self.weight, self.target.decode()) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - priority = tok.get_uint16() - weight = tok.get_uint16() - target = tok.get().unescape() - if not (target.is_quoted_string() or target.is_identifier()): - raise dns.exception.SyntaxError("URI target must be a string") - return cls(rdclass, rdtype, priority, weight, target.value) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - two_ints = struct.pack("!HH", self.priority, self.weight) - file.write(two_ints) - file.write(self.target) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (priority, weight) = parser.get_struct("!HH") - target = parser.get_remaining() - if len(target) == 0: - raise dns.exception.FormError("URI target may not be empty") - return cls(rdclass, rdtype, priority, weight, target) - - def _processing_priority(self): - return self.priority - - def _processing_weight(self): - return self.weight - - @classmethod - def _processing_order(cls, iterable): - return dns.rdtypes.util.weighted_processing_order(iterable) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/WALLET.py b/venv/Lib/site-packages/dns/rdtypes/ANY/WALLET.py deleted file mode 100644 index ff46476..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/WALLET.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import dns.immutable -import dns.rdtypes.txtbase - - -@dns.immutable.immutable -class WALLET(dns.rdtypes.txtbase.TXTBase): - """WALLET record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/X25.py b/venv/Lib/site-packages/dns/rdtypes/ANY/X25.py deleted file mode 100644 index 2436ddb..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/X25.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class X25(dns.rdata.Rdata): - """X25 record""" - - # see RFC 1183 - - __slots__ = ["address"] - - def __init__(self, rdclass, rdtype, address): - super().__init__(rdclass, rdtype) - self.address = self._as_bytes(address, True, 255) - - def to_text(self, origin=None, relativize=True, **kw): - return f'"{dns.rdata._escapify(self.address)}"' - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - address = tok.get_string() - return cls(rdclass, rdtype, address) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - l = len(self.address) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.address) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - address = parser.get_counted_bytes() - return cls(rdclass, rdtype, address) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/ZONEMD.py b/venv/Lib/site-packages/dns/rdtypes/ANY/ZONEMD.py deleted file mode 100644 index c90e3ee..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/ZONEMD.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import binascii -import struct - -import dns.immutable -import dns.rdata -import dns.rdatatype -import dns.zonetypes - - -@dns.immutable.immutable -class ZONEMD(dns.rdata.Rdata): - """ZONEMD record""" - - # See RFC 8976 - - __slots__ = ["serial", "scheme", "hash_algorithm", "digest"] - - def __init__(self, rdclass, rdtype, serial, scheme, hash_algorithm, digest): - super().__init__(rdclass, rdtype) - self.serial = self._as_uint32(serial) - self.scheme = dns.zonetypes.DigestScheme.make(scheme) - self.hash_algorithm = dns.zonetypes.DigestHashAlgorithm.make(hash_algorithm) - self.digest = self._as_bytes(digest) - - if self.scheme == 0: # reserved, RFC 8976 Sec. 5.2 - raise ValueError("scheme 0 is reserved") - if self.hash_algorithm == 0: # reserved, RFC 8976 Sec. 5.3 - raise ValueError("hash_algorithm 0 is reserved") - - hasher = dns.zonetypes._digest_hashers.get(self.hash_algorithm) - if hasher and hasher().digest_size != len(self.digest): - raise ValueError("digest length inconsistent with hash algorithm") - - def to_text(self, origin=None, relativize=True, **kw): - kw = kw.copy() - chunksize = kw.pop("chunksize", 128) - return "%d %d %d %s" % ( - self.serial, - self.scheme, - self.hash_algorithm, - dns.rdata._hexify(self.digest, chunksize=chunksize, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - serial = tok.get_uint32() - scheme = tok.get_uint8() - hash_algorithm = tok.get_uint8() - digest = tok.concatenate_remaining_identifiers().encode() - digest = binascii.unhexlify(digest) - return cls(rdclass, rdtype, serial, scheme, hash_algorithm, digest) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - header = struct.pack("!IBB", self.serial, self.scheme, self.hash_algorithm) - file.write(header) - file.write(self.digest) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - header = parser.get_struct("!IBB") - digest = parser.get_remaining() - return cls(rdclass, rdtype, header[0], header[1], header[2], digest) diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__init__.py b/venv/Lib/site-packages/dns/rdtypes/ANY/__init__.py deleted file mode 100644 index 647b215..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/ANY/__init__.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Class ANY (generic) rdata type classes.""" - -__all__ = [ - "AFSDB", - "AMTRELAY", - "AVC", - "CAA", - "CDNSKEY", - "CDS", - "CERT", - "CNAME", - "CSYNC", - "DLV", - "DNAME", - "DNSKEY", - "DS", - "EUI48", - "EUI64", - "GPOS", - "HINFO", - "HIP", - "ISDN", - "L32", - "L64", - "LOC", - "LP", - "MX", - "NID", - "NINFO", - "NS", - "NSEC", - "NSEC3", - "NSEC3PARAM", - "OPENPGPKEY", - "OPT", - "PTR", - "RESINFO", - "RP", - "RRSIG", - "RT", - "SMIMEA", - "SOA", - "SPF", - "SSHFP", - "TKEY", - "TLSA", - "TSIG", - "TXT", - "URI", - "WALLET", - "X25", - "ZONEMD", -] diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/AFSDB.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/AFSDB.cpython-310.pyc deleted file mode 100644 index 50befc1..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/AFSDB.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/AMTRELAY.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/AMTRELAY.cpython-310.pyc deleted file mode 100644 index c25baa0..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/AMTRELAY.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/AVC.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/AVC.cpython-310.pyc deleted file mode 100644 index 79e4ec5..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/AVC.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CAA.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CAA.cpython-310.pyc deleted file mode 100644 index 2a02bef..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CAA.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CDNSKEY.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CDNSKEY.cpython-310.pyc deleted file mode 100644 index 0bd98f8..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CDNSKEY.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CDS.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CDS.cpython-310.pyc deleted file mode 100644 index 7ee1f80..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CDS.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CERT.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CERT.cpython-310.pyc deleted file mode 100644 index 23dad0e..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CERT.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CNAME.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CNAME.cpython-310.pyc deleted file mode 100644 index 5cab356..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CNAME.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CSYNC.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CSYNC.cpython-310.pyc deleted file mode 100644 index 207964d..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/CSYNC.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/DLV.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/DLV.cpython-310.pyc deleted file mode 100644 index 30f673d..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/DLV.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/DNAME.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/DNAME.cpython-310.pyc deleted file mode 100644 index e492358..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/DNAME.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/DNSKEY.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/DNSKEY.cpython-310.pyc deleted file mode 100644 index c293f9e..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/DNSKEY.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/DS.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/DS.cpython-310.pyc deleted file mode 100644 index 52c069b..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/DS.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/EUI48.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/EUI48.cpython-310.pyc deleted file mode 100644 index b562e4e..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/EUI48.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/EUI64.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/EUI64.cpython-310.pyc deleted file mode 100644 index fa6f558..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/EUI64.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/GPOS.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/GPOS.cpython-310.pyc deleted file mode 100644 index 7800e7f..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/GPOS.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/HINFO.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/HINFO.cpython-310.pyc deleted file mode 100644 index cc94366..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/HINFO.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/HIP.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/HIP.cpython-310.pyc deleted file mode 100644 index 5fad0c5..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/HIP.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/ISDN.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/ISDN.cpython-310.pyc deleted file mode 100644 index 40053b1..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/ISDN.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/L32.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/L32.cpython-310.pyc deleted file mode 100644 index a68c6b6..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/L32.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/L64.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/L64.cpython-310.pyc deleted file mode 100644 index 9d277b9..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/L64.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/LOC.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/LOC.cpython-310.pyc deleted file mode 100644 index 56c9d40..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/LOC.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/LP.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/LP.cpython-310.pyc deleted file mode 100644 index 9c5716a..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/LP.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/MX.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/MX.cpython-310.pyc deleted file mode 100644 index e1b6604..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/MX.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NID.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NID.cpython-310.pyc deleted file mode 100644 index 1f9aa95..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NID.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NINFO.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NINFO.cpython-310.pyc deleted file mode 100644 index 3ad1de9..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NINFO.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NS.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NS.cpython-310.pyc deleted file mode 100644 index a239491..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NS.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NSEC.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NSEC.cpython-310.pyc deleted file mode 100644 index e86e23e..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NSEC.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NSEC3.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NSEC3.cpython-310.pyc deleted file mode 100644 index c62c6d6..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NSEC3.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NSEC3PARAM.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NSEC3PARAM.cpython-310.pyc deleted file mode 100644 index 8fdc05f..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/NSEC3PARAM.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/OPENPGPKEY.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/OPENPGPKEY.cpython-310.pyc deleted file mode 100644 index 702d5ef..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/OPENPGPKEY.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/OPT.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/OPT.cpython-310.pyc deleted file mode 100644 index 8e72602..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/OPT.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/PTR.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/PTR.cpython-310.pyc deleted file mode 100644 index c33d396..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/PTR.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/RESINFO.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/RESINFO.cpython-310.pyc deleted file mode 100644 index aeddb85..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/RESINFO.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/RP.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/RP.cpython-310.pyc deleted file mode 100644 index eb34285..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/RP.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/RRSIG.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/RRSIG.cpython-310.pyc deleted file mode 100644 index 4436be8..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/RRSIG.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/RT.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/RT.cpython-310.pyc deleted file mode 100644 index bd808d8..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/RT.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/SMIMEA.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/SMIMEA.cpython-310.pyc deleted file mode 100644 index aac8e34..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/SMIMEA.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/SOA.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/SOA.cpython-310.pyc deleted file mode 100644 index 3de4e0a..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/SOA.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/SPF.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/SPF.cpython-310.pyc deleted file mode 100644 index f803f24..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/SPF.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/SSHFP.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/SSHFP.cpython-310.pyc deleted file mode 100644 index b4439b5..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/SSHFP.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/TKEY.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/TKEY.cpython-310.pyc deleted file mode 100644 index 2abd2d6..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/TKEY.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/TLSA.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/TLSA.cpython-310.pyc deleted file mode 100644 index 884d874..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/TLSA.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/TSIG.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/TSIG.cpython-310.pyc deleted file mode 100644 index 70929c5..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/TSIG.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/TXT.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/TXT.cpython-310.pyc deleted file mode 100644 index 5c71bc7..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/TXT.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/URI.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/URI.cpython-310.pyc deleted file mode 100644 index 885b65a..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/URI.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/WALLET.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/WALLET.cpython-310.pyc deleted file mode 100644 index af47795..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/WALLET.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/X25.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/X25.cpython-310.pyc deleted file mode 100644 index cb06cf6..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/X25.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/ZONEMD.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/ZONEMD.cpython-310.pyc deleted file mode 100644 index 2d12809..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/ZONEMD.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 07ff20b..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/ANY/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/CH/A.py b/venv/Lib/site-packages/dns/rdtypes/CH/A.py deleted file mode 100644 index 832e8d3..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/CH/A.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.immutable -import dns.rdtypes.mxbase - - -@dns.immutable.immutable -class A(dns.rdata.Rdata): - """A record for Chaosnet""" - - # domain: the domain of the address - # address: the 16-bit address - - __slots__ = ["domain", "address"] - - def __init__(self, rdclass, rdtype, domain, address): - super().__init__(rdclass, rdtype) - self.domain = self._as_name(domain) - self.address = self._as_uint16(address) - - def to_text(self, origin=None, relativize=True, **kw): - domain = self.domain.choose_relativity(origin, relativize) - return f"{domain} {self.address:o}" - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - domain = tok.get_name(origin, relativize, relativize_to) - address = tok.get_uint16(base=8) - return cls(rdclass, rdtype, domain, address) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.domain.to_wire(file, compress, origin, canonicalize) - pref = struct.pack("!H", self.address) - file.write(pref) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - domain = parser.get_name(origin) - address = parser.get_uint16() - return cls(rdclass, rdtype, domain, address) diff --git a/venv/Lib/site-packages/dns/rdtypes/CH/__init__.py b/venv/Lib/site-packages/dns/rdtypes/CH/__init__.py deleted file mode 100644 index 0760c26..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/CH/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Class CH rdata type classes.""" - -__all__ = [ - "A", -] diff --git a/venv/Lib/site-packages/dns/rdtypes/CH/__pycache__/A.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/CH/__pycache__/A.cpython-310.pyc deleted file mode 100644 index 319e6a0..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/CH/__pycache__/A.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/CH/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/CH/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index fa16762..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/CH/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/A.py b/venv/Lib/site-packages/dns/rdtypes/IN/A.py deleted file mode 100644 index e09d611..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/A.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.immutable -import dns.ipv4 -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class A(dns.rdata.Rdata): - """A record.""" - - __slots__ = ["address"] - - def __init__(self, rdclass, rdtype, address): - super().__init__(rdclass, rdtype) - self.address = self._as_ipv4_address(address) - - def to_text(self, origin=None, relativize=True, **kw): - return self.address - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - address = tok.get_identifier() - return cls(rdclass, rdtype, address) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(dns.ipv4.inet_aton(self.address)) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - address = parser.get_remaining() - return cls(rdclass, rdtype, address) diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/AAAA.py b/venv/Lib/site-packages/dns/rdtypes/IN/AAAA.py deleted file mode 100644 index 0cd139e..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/AAAA.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.immutable -import dns.ipv6 -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class AAAA(dns.rdata.Rdata): - """AAAA record.""" - - __slots__ = ["address"] - - def __init__(self, rdclass, rdtype, address): - super().__init__(rdclass, rdtype) - self.address = self._as_ipv6_address(address) - - def to_text(self, origin=None, relativize=True, **kw): - return self.address - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - address = tok.get_identifier() - return cls(rdclass, rdtype, address) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(dns.ipv6.inet_aton(self.address)) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - address = parser.get_remaining() - return cls(rdclass, rdtype, address) diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/APL.py b/venv/Lib/site-packages/dns/rdtypes/IN/APL.py deleted file mode 100644 index 44cb3fe..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/APL.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import binascii -import codecs -import struct - -import dns.exception -import dns.immutable -import dns.ipv4 -import dns.ipv6 -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class APLItem: - """An APL list item.""" - - __slots__ = ["family", "negation", "address", "prefix"] - - def __init__(self, family, negation, address, prefix): - self.family = dns.rdata.Rdata._as_uint16(family) - self.negation = dns.rdata.Rdata._as_bool(negation) - if self.family == 1: - self.address = dns.rdata.Rdata._as_ipv4_address(address) - self.prefix = dns.rdata.Rdata._as_int(prefix, 0, 32) - elif self.family == 2: - self.address = dns.rdata.Rdata._as_ipv6_address(address) - self.prefix = dns.rdata.Rdata._as_int(prefix, 0, 128) - else: - self.address = dns.rdata.Rdata._as_bytes(address, max_length=127) - self.prefix = dns.rdata.Rdata._as_uint8(prefix) - - def __str__(self): - if self.negation: - return "!%d:%s/%s" % (self.family, self.address, self.prefix) - else: - return "%d:%s/%s" % (self.family, self.address, self.prefix) - - def to_wire(self, file): - if self.family == 1: - address = dns.ipv4.inet_aton(self.address) - elif self.family == 2: - address = dns.ipv6.inet_aton(self.address) - else: - address = binascii.unhexlify(self.address) - # - # Truncate least significant zero bytes. - # - last = 0 - for i in range(len(address) - 1, -1, -1): - if address[i] != 0: - last = i + 1 - break - address = address[0:last] - l = len(address) - assert l < 128 - if self.negation: - l |= 0x80 - header = struct.pack("!HBB", self.family, self.prefix, l) - file.write(header) - file.write(address) - - -@dns.immutable.immutable -class APL(dns.rdata.Rdata): - """APL record.""" - - # see: RFC 3123 - - __slots__ = ["items"] - - def __init__(self, rdclass, rdtype, items): - super().__init__(rdclass, rdtype) - for item in items: - if not isinstance(item, APLItem): - raise ValueError("item not an APLItem") - self.items = tuple(items) - - def to_text(self, origin=None, relativize=True, **kw): - return " ".join(map(str, self.items)) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - items = [] - for token in tok.get_remaining(): - item = token.unescape().value - if item[0] == "!": - negation = True - item = item[1:] - else: - negation = False - (family, rest) = item.split(":", 1) - family = int(family) - (address, prefix) = rest.split("/", 1) - prefix = int(prefix) - item = APLItem(family, negation, address, prefix) - items.append(item) - - return cls(rdclass, rdtype, items) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - for item in self.items: - item.to_wire(file) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - items = [] - while parser.remaining() > 0: - header = parser.get_struct("!HBB") - afdlen = header[2] - if afdlen > 127: - negation = True - afdlen -= 128 - else: - negation = False - address = parser.get_bytes(afdlen) - l = len(address) - if header[0] == 1: - if l < 4: - address += b"\x00" * (4 - l) - elif header[0] == 2: - if l < 16: - address += b"\x00" * (16 - l) - else: - # - # This isn't really right according to the RFC, but it - # seems better than throwing an exception - # - address = codecs.encode(address, "hex_codec") - item = APLItem(header[0], negation, address, header[1]) - items.append(item) - return cls(rdclass, rdtype, items) diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/DHCID.py b/venv/Lib/site-packages/dns/rdtypes/IN/DHCID.py deleted file mode 100644 index 723492f..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/DHCID.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 - -import dns.exception -import dns.immutable -import dns.rdata - - -@dns.immutable.immutable -class DHCID(dns.rdata.Rdata): - """DHCID record""" - - # see: RFC 4701 - - __slots__ = ["data"] - - def __init__(self, rdclass, rdtype, data): - super().__init__(rdclass, rdtype) - self.data = self._as_bytes(data) - - def to_text(self, origin=None, relativize=True, **kw): - return dns.rdata._base64ify(self.data, **kw) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - b64 = tok.concatenate_remaining_identifiers().encode() - data = base64.b64decode(b64) - return cls(rdclass, rdtype, data) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(self.data) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - data = parser.get_remaining() - return cls(rdclass, rdtype, data) diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/HTTPS.py b/venv/Lib/site-packages/dns/rdtypes/IN/HTTPS.py deleted file mode 100644 index 15464cb..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/HTTPS.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import dns.immutable -import dns.rdtypes.svcbbase - - -@dns.immutable.immutable -class HTTPS(dns.rdtypes.svcbbase.SVCBBase): - """HTTPS record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/IPSECKEY.py b/venv/Lib/site-packages/dns/rdtypes/IN/IPSECKEY.py deleted file mode 100644 index e3a6615..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/IPSECKEY.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import struct - -import dns.exception -import dns.immutable -import dns.rdtypes.util - - -class Gateway(dns.rdtypes.util.Gateway): - name = "IPSECKEY gateway" - - -@dns.immutable.immutable -class IPSECKEY(dns.rdata.Rdata): - """IPSECKEY record""" - - # see: RFC 4025 - - __slots__ = ["precedence", "gateway_type", "algorithm", "gateway", "key"] - - def __init__( - self, rdclass, rdtype, precedence, gateway_type, algorithm, gateway, key - ): - super().__init__(rdclass, rdtype) - gateway = Gateway(gateway_type, gateway) - self.precedence = self._as_uint8(precedence) - self.gateway_type = gateway.type - self.algorithm = self._as_uint8(algorithm) - self.gateway = gateway.gateway - self.key = self._as_bytes(key) - - def to_text(self, origin=None, relativize=True, **kw): - gateway = Gateway(self.gateway_type, self.gateway).to_text(origin, relativize) - return "%d %d %d %s %s" % ( - self.precedence, - self.gateway_type, - self.algorithm, - gateway, - dns.rdata._base64ify(self.key, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - precedence = tok.get_uint8() - gateway_type = tok.get_uint8() - algorithm = tok.get_uint8() - gateway = Gateway.from_text( - gateway_type, tok, origin, relativize, relativize_to - ) - b64 = tok.concatenate_remaining_identifiers().encode() - key = base64.b64decode(b64) - return cls( - rdclass, rdtype, precedence, gateway_type, algorithm, gateway.gateway, key - ) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - header = struct.pack("!BBB", self.precedence, self.gateway_type, self.algorithm) - file.write(header) - Gateway(self.gateway_type, self.gateway).to_wire( - file, compress, origin, canonicalize - ) - file.write(self.key) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - header = parser.get_struct("!BBB") - gateway_type = header[1] - gateway = Gateway.from_wire_parser(gateway_type, parser, origin) - key = parser.get_remaining() - return cls( - rdclass, rdtype, header[0], gateway_type, header[2], gateway.gateway, key - ) diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/KX.py b/venv/Lib/site-packages/dns/rdtypes/IN/KX.py deleted file mode 100644 index 6073df4..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/KX.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.mxbase - - -@dns.immutable.immutable -class KX(dns.rdtypes.mxbase.UncompressedDowncasingMX): - """KX record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/NAPTR.py b/venv/Lib/site-packages/dns/rdtypes/IN/NAPTR.py deleted file mode 100644 index 195d1cb..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/NAPTR.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdtypes.util - - -def _write_string(file, s): - l = len(s) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(s) - - -@dns.immutable.immutable -class NAPTR(dns.rdata.Rdata): - """NAPTR record""" - - # see: RFC 3403 - - __slots__ = ["order", "preference", "flags", "service", "regexp", "replacement"] - - def __init__( - self, rdclass, rdtype, order, preference, flags, service, regexp, replacement - ): - super().__init__(rdclass, rdtype) - self.flags = self._as_bytes(flags, True, 255) - self.service = self._as_bytes(service, True, 255) - self.regexp = self._as_bytes(regexp, True, 255) - self.order = self._as_uint16(order) - self.preference = self._as_uint16(preference) - self.replacement = self._as_name(replacement) - - def to_text(self, origin=None, relativize=True, **kw): - replacement = self.replacement.choose_relativity(origin, relativize) - return '%d %d "%s" "%s" "%s" %s' % ( - self.order, - self.preference, - dns.rdata._escapify(self.flags), - dns.rdata._escapify(self.service), - dns.rdata._escapify(self.regexp), - replacement, - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - order = tok.get_uint16() - preference = tok.get_uint16() - flags = tok.get_string() - service = tok.get_string() - regexp = tok.get_string() - replacement = tok.get_name(origin, relativize, relativize_to) - return cls( - rdclass, rdtype, order, preference, flags, service, regexp, replacement - ) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - two_ints = struct.pack("!HH", self.order, self.preference) - file.write(two_ints) - _write_string(file, self.flags) - _write_string(file, self.service) - _write_string(file, self.regexp) - self.replacement.to_wire(file, compress, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (order, preference) = parser.get_struct("!HH") - strings = [] - for _ in range(3): - s = parser.get_counted_bytes() - strings.append(s) - replacement = parser.get_name(origin) - return cls( - rdclass, - rdtype, - order, - preference, - strings[0], - strings[1], - strings[2], - replacement, - ) - - def _processing_priority(self): - return (self.order, self.preference) - - @classmethod - def _processing_order(cls, iterable): - return dns.rdtypes.util.priority_processing_order(iterable) diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/NSAP.py b/venv/Lib/site-packages/dns/rdtypes/IN/NSAP.py deleted file mode 100644 index d55edb7..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/NSAP.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import binascii - -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class NSAP(dns.rdata.Rdata): - """NSAP record.""" - - # see: RFC 1706 - - __slots__ = ["address"] - - def __init__(self, rdclass, rdtype, address): - super().__init__(rdclass, rdtype) - self.address = self._as_bytes(address) - - def to_text(self, origin=None, relativize=True, **kw): - return f"0x{binascii.hexlify(self.address).decode()}" - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - address = tok.get_string() - if address[0:2] != "0x": - raise dns.exception.SyntaxError("string does not start with 0x") - address = address[2:].replace(".", "") - if len(address) % 2 != 0: - raise dns.exception.SyntaxError("hexstring has odd length") - address = binascii.unhexlify(address.encode()) - return cls(rdclass, rdtype, address) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(self.address) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - address = parser.get_remaining() - return cls(rdclass, rdtype, address) diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/NSAP_PTR.py b/venv/Lib/site-packages/dns/rdtypes/IN/NSAP_PTR.py deleted file mode 100644 index ce1c663..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/NSAP_PTR.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.nsbase - - -@dns.immutable.immutable -class NSAP_PTR(dns.rdtypes.nsbase.UncompressedNS): - """NSAP-PTR record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/PX.py b/venv/Lib/site-packages/dns/rdtypes/IN/PX.py deleted file mode 100644 index cdca153..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/PX.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdtypes.util - - -@dns.immutable.immutable -class PX(dns.rdata.Rdata): - """PX record.""" - - # see: RFC 2163 - - __slots__ = ["preference", "map822", "mapx400"] - - def __init__(self, rdclass, rdtype, preference, map822, mapx400): - super().__init__(rdclass, rdtype) - self.preference = self._as_uint16(preference) - self.map822 = self._as_name(map822) - self.mapx400 = self._as_name(mapx400) - - def to_text(self, origin=None, relativize=True, **kw): - map822 = self.map822.choose_relativity(origin, relativize) - mapx400 = self.mapx400.choose_relativity(origin, relativize) - return "%d %s %s" % (self.preference, map822, mapx400) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - preference = tok.get_uint16() - map822 = tok.get_name(origin, relativize, relativize_to) - mapx400 = tok.get_name(origin, relativize, relativize_to) - return cls(rdclass, rdtype, preference, map822, mapx400) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - pref = struct.pack("!H", self.preference) - file.write(pref) - self.map822.to_wire(file, None, origin, canonicalize) - self.mapx400.to_wire(file, None, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - preference = parser.get_uint16() - map822 = parser.get_name(origin) - mapx400 = parser.get_name(origin) - return cls(rdclass, rdtype, preference, map822, mapx400) - - def _processing_priority(self): - return self.preference - - @classmethod - def _processing_order(cls, iterable): - return dns.rdtypes.util.priority_processing_order(iterable) diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/SRV.py b/venv/Lib/site-packages/dns/rdtypes/IN/SRV.py deleted file mode 100644 index 5adef98..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/SRV.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdtypes.util - - -@dns.immutable.immutable -class SRV(dns.rdata.Rdata): - """SRV record""" - - # see: RFC 2782 - - __slots__ = ["priority", "weight", "port", "target"] - - def __init__(self, rdclass, rdtype, priority, weight, port, target): - super().__init__(rdclass, rdtype) - self.priority = self._as_uint16(priority) - self.weight = self._as_uint16(weight) - self.port = self._as_uint16(port) - self.target = self._as_name(target) - - def to_text(self, origin=None, relativize=True, **kw): - target = self.target.choose_relativity(origin, relativize) - return "%d %d %d %s" % (self.priority, self.weight, self.port, target) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - priority = tok.get_uint16() - weight = tok.get_uint16() - port = tok.get_uint16() - target = tok.get_name(origin, relativize, relativize_to) - return cls(rdclass, rdtype, priority, weight, port, target) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - three_ints = struct.pack("!HHH", self.priority, self.weight, self.port) - file.write(three_ints) - self.target.to_wire(file, compress, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (priority, weight, port) = parser.get_struct("!HHH") - target = parser.get_name(origin) - return cls(rdclass, rdtype, priority, weight, port, target) - - def _processing_priority(self): - return self.priority - - def _processing_weight(self): - return self.weight - - @classmethod - def _processing_order(cls, iterable): - return dns.rdtypes.util.weighted_processing_order(iterable) diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/SVCB.py b/venv/Lib/site-packages/dns/rdtypes/IN/SVCB.py deleted file mode 100644 index ff3e932..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/SVCB.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import dns.immutable -import dns.rdtypes.svcbbase - - -@dns.immutable.immutable -class SVCB(dns.rdtypes.svcbbase.SVCBBase): - """SVCB record""" diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/WKS.py b/venv/Lib/site-packages/dns/rdtypes/IN/WKS.py deleted file mode 100644 index 881a784..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/WKS.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import socket -import struct - -import dns.immutable -import dns.ipv4 -import dns.rdata - -try: - _proto_tcp = socket.getprotobyname("tcp") - _proto_udp = socket.getprotobyname("udp") -except OSError: - # Fall back to defaults in case /etc/protocols is unavailable. - _proto_tcp = 6 - _proto_udp = 17 - - -@dns.immutable.immutable -class WKS(dns.rdata.Rdata): - """WKS record""" - - # see: RFC 1035 - - __slots__ = ["address", "protocol", "bitmap"] - - def __init__(self, rdclass, rdtype, address, protocol, bitmap): - super().__init__(rdclass, rdtype) - self.address = self._as_ipv4_address(address) - self.protocol = self._as_uint8(protocol) - self.bitmap = self._as_bytes(bitmap) - - def to_text(self, origin=None, relativize=True, **kw): - bits = [] - for i, byte in enumerate(self.bitmap): - for j in range(0, 8): - if byte & (0x80 >> j): - bits.append(str(i * 8 + j)) - text = " ".join(bits) - return "%s %d %s" % (self.address, self.protocol, text) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - address = tok.get_string() - protocol = tok.get_string() - if protocol.isdigit(): - protocol = int(protocol) - else: - protocol = socket.getprotobyname(protocol) - bitmap = bytearray() - for token in tok.get_remaining(): - value = token.unescape().value - if value.isdigit(): - serv = int(value) - else: - if protocol != _proto_udp and protocol != _proto_tcp: - raise NotImplementedError("protocol must be TCP or UDP") - if protocol == _proto_udp: - protocol_text = "udp" - else: - protocol_text = "tcp" - serv = socket.getservbyname(value, protocol_text) - i = serv // 8 - l = len(bitmap) - if l < i + 1: - for _ in range(l, i + 1): - bitmap.append(0) - bitmap[i] = bitmap[i] | (0x80 >> (serv % 8)) - bitmap = dns.rdata._truncate_bitmap(bitmap) - return cls(rdclass, rdtype, address, protocol, bitmap) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(dns.ipv4.inet_aton(self.address)) - protocol = struct.pack("!B", self.protocol) - file.write(protocol) - file.write(self.bitmap) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - address = parser.get_bytes(4) - protocol = parser.get_uint8() - bitmap = parser.get_remaining() - return cls(rdclass, rdtype, address, protocol, bitmap) diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__init__.py b/venv/Lib/site-packages/dns/rdtypes/IN/__init__.py deleted file mode 100644 index dcec4dd..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/IN/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Class IN rdata type classes.""" - -__all__ = [ - "A", - "AAAA", - "APL", - "DHCID", - "HTTPS", - "IPSECKEY", - "KX", - "NAPTR", - "NSAP", - "NSAP_PTR", - "PX", - "SRV", - "SVCB", - "WKS", -] diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/A.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/A.cpython-310.pyc deleted file mode 100644 index f575756..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/A.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/AAAA.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/AAAA.cpython-310.pyc deleted file mode 100644 index 9382a61..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/AAAA.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/APL.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/APL.cpython-310.pyc deleted file mode 100644 index 01fb083..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/APL.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/DHCID.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/DHCID.cpython-310.pyc deleted file mode 100644 index 7d3ad8f..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/DHCID.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/HTTPS.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/HTTPS.cpython-310.pyc deleted file mode 100644 index 1d4e3a9..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/HTTPS.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/IPSECKEY.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/IPSECKEY.cpython-310.pyc deleted file mode 100644 index 10865d3..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/IPSECKEY.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/KX.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/KX.cpython-310.pyc deleted file mode 100644 index b7eb948..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/KX.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/NAPTR.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/NAPTR.cpython-310.pyc deleted file mode 100644 index 8c86c2f..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/NAPTR.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/NSAP.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/NSAP.cpython-310.pyc deleted file mode 100644 index cc1ae2b..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/NSAP.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/NSAP_PTR.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/NSAP_PTR.cpython-310.pyc deleted file mode 100644 index 69b0003..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/NSAP_PTR.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/PX.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/PX.cpython-310.pyc deleted file mode 100644 index d8e6025..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/PX.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/SRV.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/SRV.cpython-310.pyc deleted file mode 100644 index d59cebd..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/SRV.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/SVCB.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/SVCB.cpython-310.pyc deleted file mode 100644 index 9efde75..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/SVCB.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/WKS.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/WKS.cpython-310.pyc deleted file mode 100644 index cd30aa2..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/WKS.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index f3856e7..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/IN/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/__init__.py b/venv/Lib/site-packages/dns/rdtypes/__init__.py deleted file mode 100644 index 3997f84..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS rdata type classes""" - -__all__ = [ - "ANY", - "IN", - "CH", - "dnskeybase", - "dsbase", - "euibase", - "mxbase", - "nsbase", - "svcbbase", - "tlsabase", - "txtbase", - "util", -] diff --git a/venv/Lib/site-packages/dns/rdtypes/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 386fb1b..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/__pycache__/dnskeybase.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/__pycache__/dnskeybase.cpython-310.pyc deleted file mode 100644 index 4342eb2..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/__pycache__/dnskeybase.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/__pycache__/dsbase.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/__pycache__/dsbase.cpython-310.pyc deleted file mode 100644 index 67d4efd..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/__pycache__/dsbase.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/__pycache__/euibase.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/__pycache__/euibase.cpython-310.pyc deleted file mode 100644 index 0d6ba69..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/__pycache__/euibase.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/__pycache__/mxbase.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/__pycache__/mxbase.cpython-310.pyc deleted file mode 100644 index 91ae69e..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/__pycache__/mxbase.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/__pycache__/nsbase.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/__pycache__/nsbase.cpython-310.pyc deleted file mode 100644 index 37df187..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/__pycache__/nsbase.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/__pycache__/svcbbase.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/__pycache__/svcbbase.cpython-310.pyc deleted file mode 100644 index f0e2082..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/__pycache__/svcbbase.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/__pycache__/tlsabase.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/__pycache__/tlsabase.cpython-310.pyc deleted file mode 100644 index 053c054..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/__pycache__/tlsabase.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/__pycache__/txtbase.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/__pycache__/txtbase.cpython-310.pyc deleted file mode 100644 index 2e093b0..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/__pycache__/txtbase.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/__pycache__/util.cpython-310.pyc b/venv/Lib/site-packages/dns/rdtypes/__pycache__/util.cpython-310.pyc deleted file mode 100644 index e40393d..0000000 Binary files a/venv/Lib/site-packages/dns/rdtypes/__pycache__/util.cpython-310.pyc and /dev/null differ diff --git a/venv/Lib/site-packages/dns/rdtypes/dnskeybase.py b/venv/Lib/site-packages/dns/rdtypes/dnskeybase.py deleted file mode 100644 index db300f8..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/dnskeybase.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import enum -import struct - -import dns.dnssectypes -import dns.exception -import dns.immutable -import dns.rdata - -# wildcard import -__all__ = ["SEP", "REVOKE", "ZONE"] # noqa: F822 - - -class Flag(enum.IntFlag): - SEP = 0x0001 - REVOKE = 0x0080 - ZONE = 0x0100 - - -@dns.immutable.immutable -class DNSKEYBase(dns.rdata.Rdata): - """Base class for rdata that is like a DNSKEY record""" - - __slots__ = ["flags", "protocol", "algorithm", "key"] - - def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key): - super().__init__(rdclass, rdtype) - self.flags = Flag(self._as_uint16(flags)) - self.protocol = self._as_uint8(protocol) - self.algorithm = dns.dnssectypes.Algorithm.make(algorithm) - self.key = self._as_bytes(key) - - def to_text(self, origin=None, relativize=True, **kw): - return "%d %d %d %s" % ( - self.flags, - self.protocol, - self.algorithm, - dns.rdata._base64ify(self.key, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - flags = tok.get_uint16() - protocol = tok.get_uint8() - algorithm = tok.get_string() - b64 = tok.concatenate_remaining_identifiers().encode() - key = base64.b64decode(b64) - return cls(rdclass, rdtype, flags, protocol, algorithm, key) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - header = struct.pack("!HBB", self.flags, self.protocol, self.algorithm) - file.write(header) - file.write(self.key) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - header = parser.get_struct("!HBB") - key = parser.get_remaining() - return cls(rdclass, rdtype, header[0], header[1], header[2], key) - - -### BEGIN generated Flag constants - -SEP = Flag.SEP -REVOKE = Flag.REVOKE -ZONE = Flag.ZONE - -### END generated Flag constants diff --git a/venv/Lib/site-packages/dns/rdtypes/dsbase.py b/venv/Lib/site-packages/dns/rdtypes/dsbase.py deleted file mode 100644 index cd21f02..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/dsbase.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2010, 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import binascii -import struct - -import dns.dnssectypes -import dns.immutable -import dns.rdata -import dns.rdatatype - - -@dns.immutable.immutable -class DSBase(dns.rdata.Rdata): - """Base class for rdata that is like a DS record""" - - __slots__ = ["key_tag", "algorithm", "digest_type", "digest"] - - # Digest types registry: - # https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml - _digest_length_by_type = { - 1: 20, # SHA-1, RFC 3658 Sec. 2.4 - 2: 32, # SHA-256, RFC 4509 Sec. 2.2 - 3: 32, # GOST R 34.11-94, RFC 5933 Sec. 4 in conjunction with RFC 4490 Sec. 2.1 - 4: 48, # SHA-384, RFC 6605 Sec. 2 - } - - def __init__(self, rdclass, rdtype, key_tag, algorithm, digest_type, digest): - super().__init__(rdclass, rdtype) - self.key_tag = self._as_uint16(key_tag) - self.algorithm = dns.dnssectypes.Algorithm.make(algorithm) - self.digest_type = dns.dnssectypes.DSDigest.make(self._as_uint8(digest_type)) - self.digest = self._as_bytes(digest) - try: - if len(self.digest) != self._digest_length_by_type[self.digest_type]: - raise ValueError("digest length inconsistent with digest type") - except KeyError: - if self.digest_type == 0: # reserved, RFC 3658 Sec. 2.4 - raise ValueError("digest type 0 is reserved") - - def to_text(self, origin=None, relativize=True, **kw): - kw = kw.copy() - chunksize = kw.pop("chunksize", 128) - return "%d %d %d %s" % ( - self.key_tag, - self.algorithm, - self.digest_type, - dns.rdata._hexify(self.digest, chunksize=chunksize, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - key_tag = tok.get_uint16() - algorithm = tok.get_string() - digest_type = tok.get_uint8() - digest = tok.concatenate_remaining_identifiers().encode() - digest = binascii.unhexlify(digest) - return cls(rdclass, rdtype, key_tag, algorithm, digest_type, digest) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - header = struct.pack("!HBB", self.key_tag, self.algorithm, self.digest_type) - file.write(header) - file.write(self.digest) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - header = parser.get_struct("!HBB") - digest = parser.get_remaining() - return cls(rdclass, rdtype, header[0], header[1], header[2], digest) diff --git a/venv/Lib/site-packages/dns/rdtypes/euibase.py b/venv/Lib/site-packages/dns/rdtypes/euibase.py deleted file mode 100644 index a39c166..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/euibase.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (C) 2015 Red Hat, Inc. -# Author: Petr Spacek -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import binascii - -import dns.immutable -import dns.rdata - - -@dns.immutable.immutable -class EUIBase(dns.rdata.Rdata): - """EUIxx record""" - - # see: rfc7043.txt - - __slots__ = ["eui"] - # define these in subclasses - # byte_len = 6 # 0123456789ab (in hex) - # text_len = byte_len * 3 - 1 # 01-23-45-67-89-ab - - def __init__(self, rdclass, rdtype, eui): - super().__init__(rdclass, rdtype) - self.eui = self._as_bytes(eui) - if len(self.eui) != self.byte_len: - raise dns.exception.FormError( - f"EUI{self.byte_len * 8} rdata has to have {self.byte_len} bytes" - ) - - def to_text(self, origin=None, relativize=True, **kw): - return dns.rdata._hexify(self.eui, chunksize=2, separator=b"-", **kw) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - text = tok.get_string() - if len(text) != cls.text_len: - raise dns.exception.SyntaxError( - f"Input text must have {cls.text_len} characters" - ) - for i in range(2, cls.byte_len * 3 - 1, 3): - if text[i] != "-": - raise dns.exception.SyntaxError(f"Dash expected at position {i}") - text = text.replace("-", "") - try: - data = binascii.unhexlify(text.encode()) - except (ValueError, TypeError) as ex: - raise dns.exception.SyntaxError(f"Hex decoding error: {str(ex)}") - return cls(rdclass, rdtype, data) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(self.eui) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - eui = parser.get_bytes(cls.byte_len) - return cls(rdclass, rdtype, eui) diff --git a/venv/Lib/site-packages/dns/rdtypes/mxbase.py b/venv/Lib/site-packages/dns/rdtypes/mxbase.py deleted file mode 100644 index 6d5e3d8..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/mxbase.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""MX-like base classes.""" - -import struct - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdtypes.util - - -@dns.immutable.immutable -class MXBase(dns.rdata.Rdata): - """Base class for rdata that is like an MX record.""" - - __slots__ = ["preference", "exchange"] - - def __init__(self, rdclass, rdtype, preference, exchange): - super().__init__(rdclass, rdtype) - self.preference = self._as_uint16(preference) - self.exchange = self._as_name(exchange) - - def to_text(self, origin=None, relativize=True, **kw): - exchange = self.exchange.choose_relativity(origin, relativize) - return "%d %s" % (self.preference, exchange) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - preference = tok.get_uint16() - exchange = tok.get_name(origin, relativize, relativize_to) - return cls(rdclass, rdtype, preference, exchange) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - pref = struct.pack("!H", self.preference) - file.write(pref) - self.exchange.to_wire(file, compress, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - preference = parser.get_uint16() - exchange = parser.get_name(origin) - return cls(rdclass, rdtype, preference, exchange) - - def _processing_priority(self): - return self.preference - - @classmethod - def _processing_order(cls, iterable): - return dns.rdtypes.util.priority_processing_order(iterable) - - -@dns.immutable.immutable -class UncompressedMX(MXBase): - """Base class for rdata that is like an MX record, but whose name - is not compressed when converted to DNS wire format, and whose - digestable form is not downcased.""" - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - super()._to_wire(file, None, origin, False) - - -@dns.immutable.immutable -class UncompressedDowncasingMX(MXBase): - """Base class for rdata that is like an MX record, but whose name - is not compressed when convert to DNS wire format.""" - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - super()._to_wire(file, None, origin, canonicalize) diff --git a/venv/Lib/site-packages/dns/rdtypes/nsbase.py b/venv/Lib/site-packages/dns/rdtypes/nsbase.py deleted file mode 100644 index 904224f..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/nsbase.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""NS-like base classes.""" - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata - - -@dns.immutable.immutable -class NSBase(dns.rdata.Rdata): - """Base class for rdata that is like an NS record.""" - - __slots__ = ["target"] - - def __init__(self, rdclass, rdtype, target): - super().__init__(rdclass, rdtype) - self.target = self._as_name(target) - - def to_text(self, origin=None, relativize=True, **kw): - target = self.target.choose_relativity(origin, relativize) - return str(target) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - target = tok.get_name(origin, relativize, relativize_to) - return cls(rdclass, rdtype, target) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.target.to_wire(file, compress, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - target = parser.get_name(origin) - return cls(rdclass, rdtype, target) - - -@dns.immutable.immutable -class UncompressedNS(NSBase): - """Base class for rdata that is like an NS record, but whose name - is not compressed when convert to DNS wire format, and whose - digestable form is not downcased.""" - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.target.to_wire(file, None, origin, False) diff --git a/venv/Lib/site-packages/dns/rdtypes/svcbbase.py b/venv/Lib/site-packages/dns/rdtypes/svcbbase.py deleted file mode 100644 index a2b15b9..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/svcbbase.py +++ /dev/null @@ -1,585 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import base64 -import enum -import struct - -import dns.enum -import dns.exception -import dns.immutable -import dns.ipv4 -import dns.ipv6 -import dns.name -import dns.rdata -import dns.rdtypes.util -import dns.renderer -import dns.tokenizer -import dns.wire - -# Until there is an RFC, this module is experimental and may be changed in -# incompatible ways. - - -class UnknownParamKey(dns.exception.DNSException): - """Unknown SVCB ParamKey""" - - -class ParamKey(dns.enum.IntEnum): - """SVCB ParamKey""" - - MANDATORY = 0 - ALPN = 1 - NO_DEFAULT_ALPN = 2 - PORT = 3 - IPV4HINT = 4 - ECH = 5 - IPV6HINT = 6 - DOHPATH = 7 - OHTTP = 8 - - @classmethod - def _maximum(cls): - return 65535 - - @classmethod - def _short_name(cls): - return "SVCBParamKey" - - @classmethod - def _prefix(cls): - return "KEY" - - @classmethod - def _unknown_exception_class(cls): - return UnknownParamKey - - -class Emptiness(enum.IntEnum): - NEVER = 0 - ALWAYS = 1 - ALLOWED = 2 - - -def _validate_key(key): - force_generic = False - if isinstance(key, bytes): - # We decode to latin-1 so we get 0-255 as valid and do NOT interpret - # UTF-8 sequences - key = key.decode("latin-1") - if isinstance(key, str): - if key.lower().startswith("key"): - force_generic = True - if key[3:].startswith("0") and len(key) != 4: - # key has leading zeros - raise ValueError("leading zeros in key") - key = key.replace("-", "_") - return (ParamKey.make(key), force_generic) - - -def key_to_text(key): - return ParamKey.to_text(key).replace("_", "-").lower() - - -# Like rdata escapify, but escapes ',' too. - -_escaped = b'",\\' - - -def _escapify(qstring): - text = "" - for c in qstring: - if c in _escaped: - text += "\\" + chr(c) - elif c >= 0x20 and c < 0x7F: - text += chr(c) - else: - text += "\\%03d" % c - return text - - -def _unescape(value): - if value == "": - return value - unescaped = b"" - l = len(value) - i = 0 - while i < l: - c = value[i] - i += 1 - if c == "\\": - if i >= l: # pragma: no cover (can't happen via tokenizer get()) - raise dns.exception.UnexpectedEnd - c = value[i] - i += 1 - if c.isdigit(): - if i >= l: - raise dns.exception.UnexpectedEnd - c2 = value[i] - i += 1 - if i >= l: - raise dns.exception.UnexpectedEnd - c3 = value[i] - i += 1 - if not (c2.isdigit() and c3.isdigit()): - raise dns.exception.SyntaxError - codepoint = int(c) * 100 + int(c2) * 10 + int(c3) - if codepoint > 255: - raise dns.exception.SyntaxError - unescaped += b"%c" % (codepoint) - continue - unescaped += c.encode() - return unescaped - - -def _split(value): - l = len(value) - i = 0 - items = [] - unescaped = b"" - while i < l: - c = value[i] - i += 1 - if c == ord("\\"): - if i >= l: # pragma: no cover (can't happen via tokenizer get()) - raise dns.exception.UnexpectedEnd - c = value[i] - i += 1 - unescaped += b"%c" % (c) - elif c == ord(","): - items.append(unescaped) - unescaped = b"" - else: - unescaped += b"%c" % (c) - items.append(unescaped) - return items - - -@dns.immutable.immutable -class Param: - """Abstract base class for SVCB parameters""" - - @classmethod - def emptiness(cls): - return Emptiness.NEVER - - -@dns.immutable.immutable -class GenericParam(Param): - """Generic SVCB parameter""" - - def __init__(self, value): - self.value = dns.rdata.Rdata._as_bytes(value, True) - - @classmethod - def emptiness(cls): - return Emptiness.ALLOWED - - @classmethod - def from_value(cls, value): - if value is None or len(value) == 0: - return None - else: - return cls(_unescape(value)) - - def to_text(self): - return '"' + dns.rdata._escapify(self.value) + '"' - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - value = parser.get_bytes(parser.remaining()) - if len(value) == 0: - return None - else: - return cls(value) - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - file.write(self.value) - - -@dns.immutable.immutable -class MandatoryParam(Param): - def __init__(self, keys): - # check for duplicates - keys = sorted([_validate_key(key)[0] for key in keys]) - prior_k = None - for k in keys: - if k == prior_k: - raise ValueError(f"duplicate key {k:d}") - prior_k = k - if k == ParamKey.MANDATORY: - raise ValueError("listed the mandatory key as mandatory") - self.keys = tuple(keys) - - @classmethod - def from_value(cls, value): - keys = [k.encode() for k in value.split(",")] - return cls(keys) - - def to_text(self): - return '"' + ",".join([key_to_text(key) for key in self.keys]) + '"' - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - keys = [] - last_key = -1 - while parser.remaining() > 0: - key = parser.get_uint16() - if key < last_key: - raise dns.exception.FormError("manadatory keys not ascending") - last_key = key - keys.append(key) - return cls(keys) - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - for key in self.keys: - file.write(struct.pack("!H", key)) - - -@dns.immutable.immutable -class ALPNParam(Param): - def __init__(self, ids): - self.ids = dns.rdata.Rdata._as_tuple( - ids, lambda x: dns.rdata.Rdata._as_bytes(x, True, 255, False) - ) - - @classmethod - def from_value(cls, value): - return cls(_split(_unescape(value))) - - def to_text(self): - value = ",".join([_escapify(id) for id in self.ids]) - return '"' + dns.rdata._escapify(value.encode()) + '"' - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - ids = [] - while parser.remaining() > 0: - id = parser.get_counted_bytes() - ids.append(id) - return cls(ids) - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - for id in self.ids: - file.write(struct.pack("!B", len(id))) - file.write(id) - - -@dns.immutable.immutable -class NoDefaultALPNParam(Param): - # We don't ever expect to instantiate this class, but we need - # a from_value() and a from_wire_parser(), so we just return None - # from the class methods when things are OK. - - @classmethod - def emptiness(cls): - return Emptiness.ALWAYS - - @classmethod - def from_value(cls, value): - if value is None or value == "": - return None - else: - raise ValueError("no-default-alpn with non-empty value") - - def to_text(self): - raise NotImplementedError # pragma: no cover - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - if parser.remaining() != 0: - raise dns.exception.FormError - return None - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - raise NotImplementedError # pragma: no cover - - -@dns.immutable.immutable -class PortParam(Param): - def __init__(self, port): - self.port = dns.rdata.Rdata._as_uint16(port) - - @classmethod - def from_value(cls, value): - value = int(value) - return cls(value) - - def to_text(self): - return f'"{self.port}"' - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - port = parser.get_uint16() - return cls(port) - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - file.write(struct.pack("!H", self.port)) - - -@dns.immutable.immutable -class IPv4HintParam(Param): - def __init__(self, addresses): - self.addresses = dns.rdata.Rdata._as_tuple( - addresses, dns.rdata.Rdata._as_ipv4_address - ) - - @classmethod - def from_value(cls, value): - addresses = value.split(",") - return cls(addresses) - - def to_text(self): - return '"' + ",".join(self.addresses) + '"' - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - addresses = [] - while parser.remaining() > 0: - ip = parser.get_bytes(4) - addresses.append(dns.ipv4.inet_ntoa(ip)) - return cls(addresses) - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - for address in self.addresses: - file.write(dns.ipv4.inet_aton(address)) - - -@dns.immutable.immutable -class IPv6HintParam(Param): - def __init__(self, addresses): - self.addresses = dns.rdata.Rdata._as_tuple( - addresses, dns.rdata.Rdata._as_ipv6_address - ) - - @classmethod - def from_value(cls, value): - addresses = value.split(",") - return cls(addresses) - - def to_text(self): - return '"' + ",".join(self.addresses) + '"' - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - addresses = [] - while parser.remaining() > 0: - ip = parser.get_bytes(16) - addresses.append(dns.ipv6.inet_ntoa(ip)) - return cls(addresses) - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - for address in self.addresses: - file.write(dns.ipv6.inet_aton(address)) - - -@dns.immutable.immutable -class ECHParam(Param): - def __init__(self, ech): - self.ech = dns.rdata.Rdata._as_bytes(ech, True) - - @classmethod - def from_value(cls, value): - if "\\" in value: - raise ValueError("escape in ECH value") - value = base64.b64decode(value.encode()) - return cls(value) - - def to_text(self): - b64 = base64.b64encode(self.ech).decode("ascii") - return f'"{b64}"' - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - value = parser.get_bytes(parser.remaining()) - return cls(value) - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - file.write(self.ech) - - -@dns.immutable.immutable -class OHTTPParam(Param): - # We don't ever expect to instantiate this class, but we need - # a from_value() and a from_wire_parser(), so we just return None - # from the class methods when things are OK. - - @classmethod - def emptiness(cls): - return Emptiness.ALWAYS - - @classmethod - def from_value(cls, value): - if value is None or value == "": - return None - else: - raise ValueError("ohttp with non-empty value") - - def to_text(self): - raise NotImplementedError # pragma: no cover - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - if parser.remaining() != 0: - raise dns.exception.FormError - return None - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - raise NotImplementedError # pragma: no cover - - -_class_for_key = { - ParamKey.MANDATORY: MandatoryParam, - ParamKey.ALPN: ALPNParam, - ParamKey.NO_DEFAULT_ALPN: NoDefaultALPNParam, - ParamKey.PORT: PortParam, - ParamKey.IPV4HINT: IPv4HintParam, - ParamKey.ECH: ECHParam, - ParamKey.IPV6HINT: IPv6HintParam, - ParamKey.OHTTP: OHTTPParam, -} - - -def _validate_and_define(params, key, value): - (key, force_generic) = _validate_key(_unescape(key)) - if key in params: - raise SyntaxError(f'duplicate key "{key:d}"') - cls = _class_for_key.get(key, GenericParam) - emptiness = cls.emptiness() - if value is None: - if emptiness == Emptiness.NEVER: - raise SyntaxError("value cannot be empty") - value = cls.from_value(value) - else: - if force_generic: - value = cls.from_wire_parser(dns.wire.Parser(_unescape(value))) - else: - value = cls.from_value(value) - params[key] = value - - -@dns.immutable.immutable -class SVCBBase(dns.rdata.Rdata): - """Base class for SVCB-like records""" - - # see: draft-ietf-dnsop-svcb-https-11 - - __slots__ = ["priority", "target", "params"] - - def __init__(self, rdclass, rdtype, priority, target, params): - super().__init__(rdclass, rdtype) - self.priority = self._as_uint16(priority) - self.target = self._as_name(target) - for k, v in params.items(): - k = ParamKey.make(k) - if not isinstance(v, Param) and v is not None: - raise ValueError(f"{k:d} not a Param") - self.params = dns.immutable.Dict(params) - # Make sure any parameter listed as mandatory is present in the - # record. - mandatory = params.get(ParamKey.MANDATORY) - if mandatory: - for key in mandatory.keys: - # Note we have to say "not in" as we have None as a value - # so a get() and a not None test would be wrong. - if key not in params: - raise ValueError(f"key {key:d} declared mandatory but not present") - # The no-default-alpn parameter requires the alpn parameter. - if ParamKey.NO_DEFAULT_ALPN in params: - if ParamKey.ALPN not in params: - raise ValueError("no-default-alpn present, but alpn missing") - - def to_text(self, origin=None, relativize=True, **kw): - target = self.target.choose_relativity(origin, relativize) - params = [] - for key in sorted(self.params.keys()): - value = self.params[key] - if value is None: - params.append(key_to_text(key)) - else: - kv = key_to_text(key) + "=" + value.to_text() - params.append(kv) - if len(params) > 0: - space = " " - else: - space = "" - return "%d %s%s%s" % (self.priority, target, space, " ".join(params)) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - priority = tok.get_uint16() - target = tok.get_name(origin, relativize, relativize_to) - if priority == 0: - token = tok.get() - if not token.is_eol_or_eof(): - raise SyntaxError("parameters in AliasMode") - tok.unget(token) - params = {} - while True: - token = tok.get() - if token.is_eol_or_eof(): - tok.unget(token) - break - if token.ttype != dns.tokenizer.IDENTIFIER: - raise SyntaxError("parameter is not an identifier") - equals = token.value.find("=") - if equals == len(token.value) - 1: - # 'key=', so next token should be a quoted string without - # any intervening whitespace. - key = token.value[:-1] - token = tok.get(want_leading=True) - if token.ttype != dns.tokenizer.QUOTED_STRING: - raise SyntaxError("whitespace after =") - value = token.value - elif equals > 0: - # key=value - key = token.value[:equals] - value = token.value[equals + 1 :] - elif equals == 0: - # =key - raise SyntaxError('parameter cannot start with "="') - else: - # key - key = token.value - value = None - _validate_and_define(params, key, value) - return cls(rdclass, rdtype, priority, target, params) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(struct.pack("!H", self.priority)) - self.target.to_wire(file, None, origin, False) - for key in sorted(self.params): - file.write(struct.pack("!H", key)) - value = self.params[key] - with dns.renderer.prefixed_length(file, 2): - # Note that we're still writing a length of zero if the value is None - if value is not None: - value.to_wire(file, origin) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - priority = parser.get_uint16() - target = parser.get_name(origin) - if priority == 0 and parser.remaining() != 0: - raise dns.exception.FormError("parameters in AliasMode") - params = {} - prior_key = -1 - while parser.remaining() > 0: - key = parser.get_uint16() - if key < prior_key: - raise dns.exception.FormError("keys not in order") - prior_key = key - vlen = parser.get_uint16() - pcls = _class_for_key.get(key, GenericParam) - with parser.restrict_to(vlen): - value = pcls.from_wire_parser(parser, origin) - params[key] = value - return cls(rdclass, rdtype, priority, target, params) - - def _processing_priority(self): - return self.priority - - @classmethod - def _processing_order(cls, iterable): - return dns.rdtypes.util.priority_processing_order(iterable) diff --git a/venv/Lib/site-packages/dns/rdtypes/tlsabase.py b/venv/Lib/site-packages/dns/rdtypes/tlsabase.py deleted file mode 100644 index a059d2c..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/tlsabase.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import binascii -import struct - -import dns.immutable -import dns.rdata -import dns.rdatatype - - -@dns.immutable.immutable -class TLSABase(dns.rdata.Rdata): - """Base class for TLSA and SMIMEA records""" - - # see: RFC 6698 - - __slots__ = ["usage", "selector", "mtype", "cert"] - - def __init__(self, rdclass, rdtype, usage, selector, mtype, cert): - super().__init__(rdclass, rdtype) - self.usage = self._as_uint8(usage) - self.selector = self._as_uint8(selector) - self.mtype = self._as_uint8(mtype) - self.cert = self._as_bytes(cert) - - def to_text(self, origin=None, relativize=True, **kw): - kw = kw.copy() - chunksize = kw.pop("chunksize", 128) - return "%d %d %d %s" % ( - self.usage, - self.selector, - self.mtype, - dns.rdata._hexify(self.cert, chunksize=chunksize, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - usage = tok.get_uint8() - selector = tok.get_uint8() - mtype = tok.get_uint8() - cert = tok.concatenate_remaining_identifiers().encode() - cert = binascii.unhexlify(cert) - return cls(rdclass, rdtype, usage, selector, mtype, cert) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - header = struct.pack("!BBB", self.usage, self.selector, self.mtype) - file.write(header) - file.write(self.cert) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - header = parser.get_struct("BBB") - cert = parser.get_remaining() - return cls(rdclass, rdtype, header[0], header[1], header[2], cert) diff --git a/venv/Lib/site-packages/dns/rdtypes/txtbase.py b/venv/Lib/site-packages/dns/rdtypes/txtbase.py deleted file mode 100644 index 73db6d9..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/txtbase.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""TXT-like base class.""" - -from typing import Any, Dict, Iterable, Optional, Tuple, Union - -import dns.exception -import dns.immutable -import dns.rdata -import dns.renderer -import dns.tokenizer - - -@dns.immutable.immutable -class TXTBase(dns.rdata.Rdata): - """Base class for rdata that is like a TXT record (see RFC 1035).""" - - __slots__ = ["strings"] - - def __init__( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - strings: Iterable[Union[bytes, str]], - ): - """Initialize a TXT-like rdata. - - *rdclass*, an ``int`` is the rdataclass of the Rdata. - - *rdtype*, an ``int`` is the rdatatype of the Rdata. - - *strings*, a tuple of ``bytes`` - """ - super().__init__(rdclass, rdtype) - self.strings: Tuple[bytes] = self._as_tuple( - strings, lambda x: self._as_bytes(x, True, 255) - ) - if len(self.strings) == 0: - raise ValueError("the list of strings must not be empty") - - def to_text( - self, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - **kw: Dict[str, Any], - ) -> str: - txt = "" - prefix = "" - for s in self.strings: - txt += f'{prefix}"{dns.rdata._escapify(s)}"' - prefix = " " - return txt - - @classmethod - def from_text( - cls, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - tok: dns.tokenizer.Tokenizer, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - relativize_to: Optional[dns.name.Name] = None, - ) -> dns.rdata.Rdata: - strings = [] - for token in tok.get_remaining(): - token = token.unescape_to_bytes() - # The 'if' below is always true in the current code, but we - # are leaving this check in in case things change some day. - if not ( - token.is_quoted_string() or token.is_identifier() - ): # pragma: no cover - raise dns.exception.SyntaxError("expected a string") - if len(token.value) > 255: - raise dns.exception.SyntaxError("string too long") - strings.append(token.value) - if len(strings) == 0: - raise dns.exception.UnexpectedEnd - return cls(rdclass, rdtype, strings) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - for s in self.strings: - with dns.renderer.prefixed_length(file, 1): - file.write(s) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - strings = [] - while parser.remaining() > 0: - s = parser.get_counted_bytes() - strings.append(s) - return cls(rdclass, rdtype, strings) diff --git a/venv/Lib/site-packages/dns/rdtypes/util.py b/venv/Lib/site-packages/dns/rdtypes/util.py deleted file mode 100644 index 653a0bf..0000000 --- a/venv/Lib/site-packages/dns/rdtypes/util.py +++ /dev/null @@ -1,257 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import collections -import random -import struct -from typing import Any, List - -import dns.exception -import dns.ipv4 -import dns.ipv6 -import dns.name -import dns.rdata - - -class Gateway: - """A helper class for the IPSECKEY gateway and AMTRELAY relay fields""" - - name = "" - - def __init__(self, type, gateway=None): - self.type = dns.rdata.Rdata._as_uint8(type) - self.gateway = gateway - self._check() - - @classmethod - def _invalid_type(cls, gateway_type): - return f"invalid {cls.name} type: {gateway_type}" - - def _check(self): - if self.type == 0: - if self.gateway not in (".", None): - raise SyntaxError(f"invalid {self.name} for type 0") - self.gateway = None - elif self.type == 1: - # check that it's OK - dns.ipv4.inet_aton(self.gateway) - elif self.type == 2: - # check that it's OK - dns.ipv6.inet_aton(self.gateway) - elif self.type == 3: - if not isinstance(self.gateway, dns.name.Name): - raise SyntaxError(f"invalid {self.name}; not a name") - else: - raise SyntaxError(self._invalid_type(self.type)) - - def to_text(self, origin=None, relativize=True): - if self.type == 0: - return "." - elif self.type in (1, 2): - return self.gateway - elif self.type == 3: - return str(self.gateway.choose_relativity(origin, relativize)) - else: - raise ValueError(self._invalid_type(self.type)) # pragma: no cover - - @classmethod - def from_text( - cls, gateway_type, tok, origin=None, relativize=True, relativize_to=None - ): - if gateway_type in (0, 1, 2): - gateway = tok.get_string() - elif gateway_type == 3: - gateway = tok.get_name(origin, relativize, relativize_to) - else: - raise dns.exception.SyntaxError( - cls._invalid_type(gateway_type) - ) # pragma: no cover - return cls(gateway_type, gateway) - - # pylint: disable=unused-argument - def to_wire(self, file, compress=None, origin=None, canonicalize=False): - if self.type == 0: - pass - elif self.type == 1: - file.write(dns.ipv4.inet_aton(self.gateway)) - elif self.type == 2: - file.write(dns.ipv6.inet_aton(self.gateway)) - elif self.type == 3: - self.gateway.to_wire(file, None, origin, False) - else: - raise ValueError(self._invalid_type(self.type)) # pragma: no cover - - # pylint: enable=unused-argument - - @classmethod - def from_wire_parser(cls, gateway_type, parser, origin=None): - if gateway_type == 0: - gateway = None - elif gateway_type == 1: - gateway = dns.ipv4.inet_ntoa(parser.get_bytes(4)) - elif gateway_type == 2: - gateway = dns.ipv6.inet_ntoa(parser.get_bytes(16)) - elif gateway_type == 3: - gateway = parser.get_name(origin) - else: - raise dns.exception.FormError(cls._invalid_type(gateway_type)) - return cls(gateway_type, gateway) - - -class Bitmap: - """A helper class for the NSEC/NSEC3/CSYNC type bitmaps""" - - type_name = "" - - def __init__(self, windows=None): - last_window = -1 - self.windows = windows - for window, bitmap in self.windows: - if not isinstance(window, int): - raise ValueError(f"bad {self.type_name} window type") - if window <= last_window: - raise ValueError(f"bad {self.type_name} window order") - if window > 256: - raise ValueError(f"bad {self.type_name} window number") - last_window = window - if not isinstance(bitmap, bytes): - raise ValueError(f"bad {self.type_name} octets type") - if len(bitmap) == 0 or len(bitmap) > 32: - raise ValueError(f"bad {self.type_name} octets") - - def to_text(self) -> str: - text = "" - for window, bitmap in self.windows: - bits = [] - for i, byte in enumerate(bitmap): - for j in range(0, 8): - if byte & (0x80 >> j): - rdtype = window * 256 + i * 8 + j - bits.append(dns.rdatatype.to_text(rdtype)) - text += " " + " ".join(bits) - return text - - @classmethod - def from_text(cls, tok: "dns.tokenizer.Tokenizer") -> "Bitmap": - rdtypes = [] - for token in tok.get_remaining(): - rdtype = dns.rdatatype.from_text(token.unescape().value) - if rdtype == 0: - raise dns.exception.SyntaxError(f"{cls.type_name} with bit 0") - rdtypes.append(rdtype) - return cls.from_rdtypes(rdtypes) - - @classmethod - def from_rdtypes(cls, rdtypes: List[dns.rdatatype.RdataType]) -> "Bitmap": - rdtypes = sorted(rdtypes) - window = 0 - octets = 0 - prior_rdtype = 0 - bitmap = bytearray(b"\0" * 32) - windows = [] - for rdtype in rdtypes: - if rdtype == prior_rdtype: - continue - prior_rdtype = rdtype - new_window = rdtype // 256 - if new_window != window: - if octets != 0: - windows.append((window, bytes(bitmap[0:octets]))) - bitmap = bytearray(b"\0" * 32) - window = new_window - offset = rdtype % 256 - byte = offset // 8 - bit = offset % 8 - octets = byte + 1 - bitmap[byte] = bitmap[byte] | (0x80 >> bit) - if octets != 0: - windows.append((window, bytes(bitmap[0:octets]))) - return cls(windows) - - def to_wire(self, file: Any) -> None: - for window, bitmap in self.windows: - file.write(struct.pack("!BB", window, len(bitmap))) - file.write(bitmap) - - @classmethod - def from_wire_parser(cls, parser: "dns.wire.Parser") -> "Bitmap": - windows = [] - while parser.remaining() > 0: - window = parser.get_uint8() - bitmap = parser.get_counted_bytes() - windows.append((window, bitmap)) - return cls(windows) - - -def _priority_table(items): - by_priority = collections.defaultdict(list) - for rdata in items: - by_priority[rdata._processing_priority()].append(rdata) - return by_priority - - -def priority_processing_order(iterable): - items = list(iterable) - if len(items) == 1: - return items - by_priority = _priority_table(items) - ordered = [] - for k in sorted(by_priority.keys()): - rdatas = by_priority[k] - random.shuffle(rdatas) - ordered.extend(rdatas) - return ordered - - -_no_weight = 0.1 - - -def weighted_processing_order(iterable): - items = list(iterable) - if len(items) == 1: - return items - by_priority = _priority_table(items) - ordered = [] - for k in sorted(by_priority.keys()): - rdatas = by_priority[k] - total = sum(rdata._processing_weight() or _no_weight for rdata in rdatas) - while len(rdatas) > 1: - r = random.uniform(0, total) - for n, rdata in enumerate(rdatas): # noqa: B007 - weight = rdata._processing_weight() or _no_weight - if weight > r: - break - r -= weight - total -= weight - ordered.append(rdata) # pylint: disable=undefined-loop-variable - del rdatas[n] # pylint: disable=undefined-loop-variable - ordered.append(rdatas[0]) - return ordered - - -def parse_formatted_hex(formatted, num_chunks, chunk_size, separator): - if len(formatted) != num_chunks * (chunk_size + 1) - 1: - raise ValueError("invalid formatted hex string") - value = b"" - for _ in range(num_chunks): - chunk = formatted[0:chunk_size] - value += int(chunk, 16).to_bytes(chunk_size // 2, "big") - formatted = formatted[chunk_size:] - if len(formatted) > 0 and formatted[0] != separator: - raise ValueError("invalid formatted hex string") - formatted = formatted[1:] - return value diff --git a/venv/Lib/site-packages/dns/renderer.py b/venv/Lib/site-packages/dns/renderer.py deleted file mode 100644 index a77481f..0000000 --- a/venv/Lib/site-packages/dns/renderer.py +++ /dev/null @@ -1,346 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Help for building DNS wire format messages""" - -import contextlib -import io -import random -import struct -import time - -import dns.exception -import dns.tsig - -QUESTION = 0 -ANSWER = 1 -AUTHORITY = 2 -ADDITIONAL = 3 - - -@contextlib.contextmanager -def prefixed_length(output, length_length): - output.write(b"\00" * length_length) - start = output.tell() - yield - end = output.tell() - length = end - start - if length > 0: - try: - output.seek(start - length_length) - try: - output.write(length.to_bytes(length_length, "big")) - except OverflowError: - raise dns.exception.FormError - finally: - output.seek(end) - - -class Renderer: - """Helper class for building DNS wire-format messages. - - Most applications can use the higher-level L{dns.message.Message} - class and its to_wire() method to generate wire-format messages. - This class is for those applications which need finer control - over the generation of messages. - - Typical use:: - - r = dns.renderer.Renderer(id=1, flags=0x80, max_size=512) - r.add_question(qname, qtype, qclass) - r.add_rrset(dns.renderer.ANSWER, rrset_1) - r.add_rrset(dns.renderer.ANSWER, rrset_2) - r.add_rrset(dns.renderer.AUTHORITY, ns_rrset) - r.add_rrset(dns.renderer.ADDITIONAL, ad_rrset_1) - r.add_rrset(dns.renderer.ADDITIONAL, ad_rrset_2) - r.add_edns(0, 0, 4096) - r.write_header() - r.add_tsig(keyname, secret, 300, 1, 0, '', request_mac) - wire = r.get_wire() - - If padding is going to be used, then the OPT record MUST be - written after everything else in the additional section except for - the TSIG (if any). - - output, an io.BytesIO, where rendering is written - - id: the message id - - flags: the message flags - - max_size: the maximum size of the message - - origin: the origin to use when rendering relative names - - compress: the compression table - - section: an int, the section currently being rendered - - counts: list of the number of RRs in each section - - mac: the MAC of the rendered message (if TSIG was used) - """ - - def __init__(self, id=None, flags=0, max_size=65535, origin=None): - """Initialize a new renderer.""" - - self.output = io.BytesIO() - if id is None: - self.id = random.randint(0, 65535) - else: - self.id = id - self.flags = flags - self.max_size = max_size - self.origin = origin - self.compress = {} - self.section = QUESTION - self.counts = [0, 0, 0, 0] - self.output.write(b"\x00" * 12) - self.mac = "" - self.reserved = 0 - self.was_padded = False - - def _rollback(self, where): - """Truncate the output buffer at offset *where*, and remove any - compression table entries that pointed beyond the truncation - point. - """ - - self.output.seek(where) - self.output.truncate() - keys_to_delete = [] - for k, v in self.compress.items(): - if v >= where: - keys_to_delete.append(k) - for k in keys_to_delete: - del self.compress[k] - - def _set_section(self, section): - """Set the renderer's current section. - - Sections must be rendered order: QUESTION, ANSWER, AUTHORITY, - ADDITIONAL. Sections may be empty. - - Raises dns.exception.FormError if an attempt was made to set - a section value less than the current section. - """ - - if self.section != section: - if self.section > section: - raise dns.exception.FormError - self.section = section - - @contextlib.contextmanager - def _track_size(self): - start = self.output.tell() - yield start - if self.output.tell() > self.max_size: - self._rollback(start) - raise dns.exception.TooBig - - @contextlib.contextmanager - def _temporarily_seek_to(self, where): - current = self.output.tell() - try: - self.output.seek(where) - yield - finally: - self.output.seek(current) - - def add_question(self, qname, rdtype, rdclass=dns.rdataclass.IN): - """Add a question to the message.""" - - self._set_section(QUESTION) - with self._track_size(): - qname.to_wire(self.output, self.compress, self.origin) - self.output.write(struct.pack("!HH", rdtype, rdclass)) - self.counts[QUESTION] += 1 - - def add_rrset(self, section, rrset, **kw): - """Add the rrset to the specified section. - - Any keyword arguments are passed on to the rdataset's to_wire() - routine. - """ - - self._set_section(section) - with self._track_size(): - n = rrset.to_wire(self.output, self.compress, self.origin, **kw) - self.counts[section] += n - - def add_rdataset(self, section, name, rdataset, **kw): - """Add the rdataset to the specified section, using the specified - name as the owner name. - - Any keyword arguments are passed on to the rdataset's to_wire() - routine. - """ - - self._set_section(section) - with self._track_size(): - n = rdataset.to_wire(name, self.output, self.compress, self.origin, **kw) - self.counts[section] += n - - def add_opt(self, opt, pad=0, opt_size=0, tsig_size=0): - """Add *opt* to the additional section, applying padding if desired. The - padding will take the specified precomputed OPT size and TSIG size into - account. - - Note that we don't have reliable way of knowing how big a GSS-TSIG digest - might be, so we we might not get an even multiple of the pad in that case.""" - if pad: - ttl = opt.ttl - assert opt_size >= 11 - opt_rdata = opt[0] - size_without_padding = self.output.tell() + opt_size + tsig_size - remainder = size_without_padding % pad - if remainder: - pad = b"\x00" * (pad - remainder) - else: - pad = b"" - options = list(opt_rdata.options) - options.append(dns.edns.GenericOption(dns.edns.OptionType.PADDING, pad)) - opt = dns.message.Message._make_opt(ttl, opt_rdata.rdclass, options) - self.was_padded = True - self.add_rrset(ADDITIONAL, opt) - - def add_edns(self, edns, ednsflags, payload, options=None): - """Add an EDNS OPT record to the message.""" - - # make sure the EDNS version in ednsflags agrees with edns - ednsflags &= 0xFF00FFFF - ednsflags |= edns << 16 - opt = dns.message.Message._make_opt(ednsflags, payload, options) - self.add_opt(opt) - - def add_tsig( - self, - keyname, - secret, - fudge, - id, - tsig_error, - other_data, - request_mac, - algorithm=dns.tsig.default_algorithm, - ): - """Add a TSIG signature to the message.""" - - s = self.output.getvalue() - - if isinstance(secret, dns.tsig.Key): - key = secret - else: - key = dns.tsig.Key(keyname, secret, algorithm) - tsig = dns.message.Message._make_tsig( - keyname, algorithm, 0, fudge, b"", id, tsig_error, other_data - ) - (tsig, _) = dns.tsig.sign(s, key, tsig[0], int(time.time()), request_mac) - self._write_tsig(tsig, keyname) - - def add_multi_tsig( - self, - ctx, - keyname, - secret, - fudge, - id, - tsig_error, - other_data, - request_mac, - algorithm=dns.tsig.default_algorithm, - ): - """Add a TSIG signature to the message. Unlike add_tsig(), this can be - used for a series of consecutive DNS envelopes, e.g. for a zone - transfer over TCP [RFC2845, 4.4]. - - For the first message in the sequence, give ctx=None. For each - subsequent message, give the ctx that was returned from the - add_multi_tsig() call for the previous message.""" - - s = self.output.getvalue() - - if isinstance(secret, dns.tsig.Key): - key = secret - else: - key = dns.tsig.Key(keyname, secret, algorithm) - tsig = dns.message.Message._make_tsig( - keyname, algorithm, 0, fudge, b"", id, tsig_error, other_data - ) - (tsig, ctx) = dns.tsig.sign( - s, key, tsig[0], int(time.time()), request_mac, ctx, True - ) - self._write_tsig(tsig, keyname) - return ctx - - def _write_tsig(self, tsig, keyname): - if self.was_padded: - compress = None - else: - compress = self.compress - self._set_section(ADDITIONAL) - with self._track_size(): - keyname.to_wire(self.output, compress, self.origin) - self.output.write( - struct.pack("!HHI", dns.rdatatype.TSIG, dns.rdataclass.ANY, 0) - ) - with prefixed_length(self.output, 2): - tsig.to_wire(self.output) - - self.counts[ADDITIONAL] += 1 - with self._temporarily_seek_to(10): - self.output.write(struct.pack("!H", self.counts[ADDITIONAL])) - - def write_header(self): - """Write the DNS message header. - - Writing the DNS message header is done after all sections - have been rendered, but before the optional TSIG signature - is added. - """ - - with self._temporarily_seek_to(0): - self.output.write( - struct.pack( - "!HHHHHH", - self.id, - self.flags, - self.counts[0], - self.counts[1], - self.counts[2], - self.counts[3], - ) - ) - - def get_wire(self): - """Return the wire format message.""" - - return self.output.getvalue() - - def reserve(self, size: int) -> None: - """Reserve *size* bytes.""" - if size < 0: - raise ValueError("reserved amount must be non-negative") - if size > self.max_size: - raise ValueError("cannot reserve more than the maximum size") - self.reserved += size - self.max_size -= size - - def release_reserved(self) -> None: - """Release the reserved bytes.""" - self.max_size += self.reserved - self.reserved = 0 diff --git a/venv/Lib/site-packages/dns/resolver.py b/venv/Lib/site-packages/dns/resolver.py deleted file mode 100644 index 3ba76e3..0000000 --- a/venv/Lib/site-packages/dns/resolver.py +++ /dev/null @@ -1,2053 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS stub resolver.""" - -import contextlib -import random -import socket -import sys -import threading -import time -import warnings -from typing import Any, Dict, Iterator, List, Optional, Sequence, Tuple, Union -from urllib.parse import urlparse - -import dns._ddr -import dns.edns -import dns.exception -import dns.flags -import dns.inet -import dns.ipv4 -import dns.ipv6 -import dns.message -import dns.name -import dns.rdata -import dns.nameserver -import dns.query -import dns.rcode -import dns.rdataclass -import dns.rdatatype -import dns.rdtypes.svcbbase -import dns.reversename -import dns.tsig - -if sys.platform == "win32": # pragma: no cover - import dns.win32util - - -class NXDOMAIN(dns.exception.DNSException): - """The DNS query name does not exist.""" - - supp_kwargs = {"qnames", "responses"} - fmt = None # we have our own __str__ implementation - - # pylint: disable=arguments-differ - - # We do this as otherwise mypy complains about unexpected keyword argument - # idna_exception - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def _check_kwargs(self, qnames, responses=None): - if not isinstance(qnames, (list, tuple, set)): - raise AttributeError("qnames must be a list, tuple or set") - if len(qnames) == 0: - raise AttributeError("qnames must contain at least one element") - if responses is None: - responses = {} - elif not isinstance(responses, dict): - raise AttributeError("responses must be a dict(qname=response)") - kwargs = dict(qnames=qnames, responses=responses) - return kwargs - - def __str__(self) -> str: - if "qnames" not in self.kwargs: - return super().__str__() - qnames = self.kwargs["qnames"] - if len(qnames) > 1: - msg = "None of DNS query names exist" - else: - msg = "The DNS query name does not exist" - qnames = ", ".join(map(str, qnames)) - return f"{msg}: {qnames}" - - @property - def canonical_name(self): - """Return the unresolved canonical name.""" - if "qnames" not in self.kwargs: - raise TypeError("parametrized exception required") - for qname in self.kwargs["qnames"]: - response = self.kwargs["responses"][qname] - try: - cname = response.canonical_name() - if cname != qname: - return cname - except Exception: # pragma: no cover - # We can just eat this exception as it means there was - # something wrong with the response. - pass - return self.kwargs["qnames"][0] - - def __add__(self, e_nx): - """Augment by results from another NXDOMAIN exception.""" - qnames0 = list(self.kwargs.get("qnames", [])) - responses0 = dict(self.kwargs.get("responses", {})) - responses1 = e_nx.kwargs.get("responses", {}) - for qname1 in e_nx.kwargs.get("qnames", []): - if qname1 not in qnames0: - qnames0.append(qname1) - if qname1 in responses1: - responses0[qname1] = responses1[qname1] - return NXDOMAIN(qnames=qnames0, responses=responses0) - - def qnames(self): - """All of the names that were tried. - - Returns a list of ``dns.name.Name``. - """ - return self.kwargs["qnames"] - - def responses(self): - """A map from queried names to their NXDOMAIN responses. - - Returns a dict mapping a ``dns.name.Name`` to a - ``dns.message.Message``. - """ - return self.kwargs["responses"] - - def response(self, qname): - """The response for query *qname*. - - Returns a ``dns.message.Message``. - """ - return self.kwargs["responses"][qname] - - -class YXDOMAIN(dns.exception.DNSException): - """The DNS query name is too long after DNAME substitution.""" - - -ErrorTuple = Tuple[ - Optional[str], - bool, - int, - Union[Exception, str], - Optional[dns.message.Message], -] - - -def _errors_to_text(errors: List[ErrorTuple]) -> List[str]: - """Turn a resolution errors trace into a list of text.""" - texts = [] - for err in errors: - texts.append(f"Server {err[0]} answered {err[3]}") - return texts - - -class LifetimeTimeout(dns.exception.Timeout): - """The resolution lifetime expired.""" - - msg = "The resolution lifetime expired." - fmt = f"{msg[:-1]} after {{timeout:.3f}} seconds: {{errors}}" - supp_kwargs = {"timeout", "errors"} - - # We do this as otherwise mypy complains about unexpected keyword argument - # idna_exception - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def _fmt_kwargs(self, **kwargs): - srv_msgs = _errors_to_text(kwargs["errors"]) - return super()._fmt_kwargs( - timeout=kwargs["timeout"], errors="; ".join(srv_msgs) - ) - - -# We added more detail to resolution timeouts, but they are still -# subclasses of dns.exception.Timeout for backwards compatibility. We also -# keep dns.resolver.Timeout defined for backwards compatibility. -Timeout = LifetimeTimeout - - -class NoAnswer(dns.exception.DNSException): - """The DNS response does not contain an answer to the question.""" - - fmt = "The DNS response does not contain an answer to the question: {query}" - supp_kwargs = {"response"} - - # We do this as otherwise mypy complains about unexpected keyword argument - # idna_exception - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def _fmt_kwargs(self, **kwargs): - return super()._fmt_kwargs(query=kwargs["response"].question) - - def response(self): - return self.kwargs["response"] - - -class NoNameservers(dns.exception.DNSException): - """All nameservers failed to answer the query. - - errors: list of servers and respective errors - The type of errors is - [(server IP address, any object convertible to string)]. - Non-empty errors list will add explanatory message () - """ - - msg = "All nameservers failed to answer the query." - fmt = f"{msg[:-1]} {{query}}: {{errors}}" - supp_kwargs = {"request", "errors"} - - # We do this as otherwise mypy complains about unexpected keyword argument - # idna_exception - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def _fmt_kwargs(self, **kwargs): - srv_msgs = _errors_to_text(kwargs["errors"]) - return super()._fmt_kwargs( - query=kwargs["request"].question, errors="; ".join(srv_msgs) - ) - - -class NotAbsolute(dns.exception.DNSException): - """An absolute domain name is required but a relative name was provided.""" - - -class NoRootSOA(dns.exception.DNSException): - """There is no SOA RR at the DNS root name. This should never happen!""" - - -class NoMetaqueries(dns.exception.DNSException): - """DNS metaqueries are not allowed.""" - - -class NoResolverConfiguration(dns.exception.DNSException): - """Resolver configuration could not be read or specified no nameservers.""" - - -class Answer: - """DNS stub resolver answer. - - Instances of this class bundle up the result of a successful DNS - resolution. - - For convenience, the answer object implements much of the sequence - protocol, forwarding to its ``rrset`` attribute. E.g. - ``for a in answer`` is equivalent to ``for a in answer.rrset``. - ``answer[i]`` is equivalent to ``answer.rrset[i]``, and - ``answer[i:j]`` is equivalent to ``answer.rrset[i:j]``. - - Note that CNAMEs or DNAMEs in the response may mean that answer - RRset's name might not be the query name. - """ - - def __init__( - self, - qname: dns.name.Name, - rdtype: dns.rdatatype.RdataType, - rdclass: dns.rdataclass.RdataClass, - response: dns.message.QueryMessage, - nameserver: Optional[str] = None, - port: Optional[int] = None, - ) -> None: - self.qname = qname - self.rdtype = rdtype - self.rdclass = rdclass - self.response = response - self.nameserver = nameserver - self.port = port - self.chaining_result = response.resolve_chaining() - # Copy some attributes out of chaining_result for backwards - # compatibility and convenience. - self.canonical_name = self.chaining_result.canonical_name - self.rrset = self.chaining_result.answer - self.expiration = time.time() + self.chaining_result.minimum_ttl - - def __getattr__(self, attr): # pragma: no cover - if attr == "name": - return self.rrset.name - elif attr == "ttl": - return self.rrset.ttl - elif attr == "covers": - return self.rrset.covers - elif attr == "rdclass": - return self.rrset.rdclass - elif attr == "rdtype": - return self.rrset.rdtype - else: - raise AttributeError(attr) - - def __len__(self) -> int: - return self.rrset and len(self.rrset) or 0 - - def __iter__(self) -> Iterator[dns.rdata.Rdata]: - return self.rrset and iter(self.rrset) or iter(tuple()) - - def __getitem__(self, i): - if self.rrset is None: - raise IndexError - return self.rrset[i] - - def __delitem__(self, i): - if self.rrset is None: - raise IndexError - del self.rrset[i] - - -class Answers(dict): - """A dict of DNS stub resolver answers, indexed by type.""" - - -class HostAnswers(Answers): - """A dict of DNS stub resolver answers to a host name lookup, indexed by - type. - """ - - @classmethod - def make( - cls, - v6: Optional[Answer] = None, - v4: Optional[Answer] = None, - add_empty: bool = True, - ) -> "HostAnswers": - answers = HostAnswers() - if v6 is not None and (add_empty or v6.rrset): - answers[dns.rdatatype.AAAA] = v6 - if v4 is not None and (add_empty or v4.rrset): - answers[dns.rdatatype.A] = v4 - return answers - - # Returns pairs of (address, family) from this result, potentially - # filtering by address family. - def addresses_and_families( - self, family: int = socket.AF_UNSPEC - ) -> Iterator[Tuple[str, int]]: - if family == socket.AF_UNSPEC: - yield from self.addresses_and_families(socket.AF_INET6) - yield from self.addresses_and_families(socket.AF_INET) - return - elif family == socket.AF_INET6: - answer = self.get(dns.rdatatype.AAAA) - elif family == socket.AF_INET: - answer = self.get(dns.rdatatype.A) - else: # pragma: no cover - raise NotImplementedError(f"unknown address family {family}") - if answer: - for rdata in answer: - yield (rdata.address, family) - - # Returns addresses from this result, potentially filtering by - # address family. - def addresses(self, family: int = socket.AF_UNSPEC) -> Iterator[str]: - return (pair[0] for pair in self.addresses_and_families(family)) - - # Returns the canonical name from this result. - def canonical_name(self) -> dns.name.Name: - answer = self.get(dns.rdatatype.AAAA, self.get(dns.rdatatype.A)) - return answer.canonical_name - - -class CacheStatistics: - """Cache Statistics""" - - def __init__(self, hits: int = 0, misses: int = 0) -> None: - self.hits = hits - self.misses = misses - - def reset(self) -> None: - self.hits = 0 - self.misses = 0 - - def clone(self) -> "CacheStatistics": - return CacheStatistics(self.hits, self.misses) - - -class CacheBase: - def __init__(self) -> None: - self.lock = threading.Lock() - self.statistics = CacheStatistics() - - def reset_statistics(self) -> None: - """Reset all statistics to zero.""" - with self.lock: - self.statistics.reset() - - def hits(self) -> int: - """How many hits has the cache had?""" - with self.lock: - return self.statistics.hits - - def misses(self) -> int: - """How many misses has the cache had?""" - with self.lock: - return self.statistics.misses - - def get_statistics_snapshot(self) -> CacheStatistics: - """Return a consistent snapshot of all the statistics. - - If running with multiple threads, it's better to take a - snapshot than to call statistics methods such as hits() and - misses() individually. - """ - with self.lock: - return self.statistics.clone() - - -CacheKey = Tuple[dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass] - - -class Cache(CacheBase): - """Simple thread-safe DNS answer cache.""" - - def __init__(self, cleaning_interval: float = 300.0) -> None: - """*cleaning_interval*, a ``float`` is the number of seconds between - periodic cleanings. - """ - - super().__init__() - self.data: Dict[CacheKey, Answer] = {} - self.cleaning_interval = cleaning_interval - self.next_cleaning: float = time.time() + self.cleaning_interval - - def _maybe_clean(self) -> None: - """Clean the cache if it's time to do so.""" - - now = time.time() - if self.next_cleaning <= now: - keys_to_delete = [] - for k, v in self.data.items(): - if v.expiration <= now: - keys_to_delete.append(k) - for k in keys_to_delete: - del self.data[k] - now = time.time() - self.next_cleaning = now + self.cleaning_interval - - def get(self, key: CacheKey) -> Optional[Answer]: - """Get the answer associated with *key*. - - Returns None if no answer is cached for the key. - - *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` - tuple whose values are the query name, rdtype, and rdclass respectively. - - Returns a ``dns.resolver.Answer`` or ``None``. - """ - - with self.lock: - self._maybe_clean() - v = self.data.get(key) - if v is None or v.expiration <= time.time(): - self.statistics.misses += 1 - return None - self.statistics.hits += 1 - return v - - def put(self, key: CacheKey, value: Answer) -> None: - """Associate key and value in the cache. - - *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` - tuple whose values are the query name, rdtype, and rdclass respectively. - - *value*, a ``dns.resolver.Answer``, the answer. - """ - - with self.lock: - self._maybe_clean() - self.data[key] = value - - def flush(self, key: Optional[CacheKey] = None) -> None: - """Flush the cache. - - If *key* is not ``None``, only that item is flushed. Otherwise the entire cache - is flushed. - - *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` - tuple whose values are the query name, rdtype, and rdclass respectively. - """ - - with self.lock: - if key is not None: - if key in self.data: - del self.data[key] - else: - self.data = {} - self.next_cleaning = time.time() + self.cleaning_interval - - -class LRUCacheNode: - """LRUCache node.""" - - def __init__(self, key, value): - self.key = key - self.value = value - self.hits = 0 - self.prev = self - self.next = self - - def link_after(self, node: "LRUCacheNode") -> None: - self.prev = node - self.next = node.next - node.next.prev = self - node.next = self - - def unlink(self) -> None: - self.next.prev = self.prev - self.prev.next = self.next - - -class LRUCache(CacheBase): - """Thread-safe, bounded, least-recently-used DNS answer cache. - - This cache is better than the simple cache (above) if you're - running a web crawler or other process that does a lot of - resolutions. The LRUCache has a maximum number of nodes, and when - it is full, the least-recently used node is removed to make space - for a new one. - """ - - def __init__(self, max_size: int = 100000) -> None: - """*max_size*, an ``int``, is the maximum number of nodes to cache; - it must be greater than 0. - """ - - super().__init__() - self.data: Dict[CacheKey, LRUCacheNode] = {} - self.set_max_size(max_size) - self.sentinel: LRUCacheNode = LRUCacheNode(None, None) - self.sentinel.prev = self.sentinel - self.sentinel.next = self.sentinel - - def set_max_size(self, max_size: int) -> None: - if max_size < 1: - max_size = 1 - self.max_size = max_size - - def get(self, key: CacheKey) -> Optional[Answer]: - """Get the answer associated with *key*. - - Returns None if no answer is cached for the key. - - *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` - tuple whose values are the query name, rdtype, and rdclass respectively. - - Returns a ``dns.resolver.Answer`` or ``None``. - """ - - with self.lock: - node = self.data.get(key) - if node is None: - self.statistics.misses += 1 - return None - # Unlink because we're either going to move the node to the front - # of the LRU list or we're going to free it. - node.unlink() - if node.value.expiration <= time.time(): - del self.data[node.key] - self.statistics.misses += 1 - return None - node.link_after(self.sentinel) - self.statistics.hits += 1 - node.hits += 1 - return node.value - - def get_hits_for_key(self, key: CacheKey) -> int: - """Return the number of cache hits associated with the specified key.""" - with self.lock: - node = self.data.get(key) - if node is None or node.value.expiration <= time.time(): - return 0 - else: - return node.hits - - def put(self, key: CacheKey, value: Answer) -> None: - """Associate key and value in the cache. - - *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` - tuple whose values are the query name, rdtype, and rdclass respectively. - - *value*, a ``dns.resolver.Answer``, the answer. - """ - - with self.lock: - node = self.data.get(key) - if node is not None: - node.unlink() - del self.data[node.key] - while len(self.data) >= self.max_size: - gnode = self.sentinel.prev - gnode.unlink() - del self.data[gnode.key] - node = LRUCacheNode(key, value) - node.link_after(self.sentinel) - self.data[key] = node - - def flush(self, key: Optional[CacheKey] = None) -> None: - """Flush the cache. - - If *key* is not ``None``, only that item is flushed. Otherwise the entire cache - is flushed. - - *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` - tuple whose values are the query name, rdtype, and rdclass respectively. - """ - - with self.lock: - if key is not None: - node = self.data.get(key) - if node is not None: - node.unlink() - del self.data[node.key] - else: - gnode = self.sentinel.next - while gnode != self.sentinel: - next = gnode.next - gnode.unlink() - gnode = next - self.data = {} - - -class _Resolution: - """Helper class for dns.resolver.Resolver.resolve(). - - All of the "business logic" of resolution is encapsulated in this - class, allowing us to have multiple resolve() implementations - using different I/O schemes without copying all of the - complicated logic. - - This class is a "friend" to dns.resolver.Resolver and manipulates - resolver data structures directly. - """ - - def __init__( - self, - resolver: "BaseResolver", - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - rdclass: Union[dns.rdataclass.RdataClass, str], - tcp: bool, - raise_on_no_answer: bool, - search: Optional[bool], - ) -> None: - if isinstance(qname, str): - qname = dns.name.from_text(qname, None) - rdtype = dns.rdatatype.RdataType.make(rdtype) - if dns.rdatatype.is_metatype(rdtype): - raise NoMetaqueries - rdclass = dns.rdataclass.RdataClass.make(rdclass) - if dns.rdataclass.is_metaclass(rdclass): - raise NoMetaqueries - self.resolver = resolver - self.qnames_to_try = resolver._get_qnames_to_try(qname, search) - self.qnames = self.qnames_to_try[:] - self.rdtype = rdtype - self.rdclass = rdclass - self.tcp = tcp - self.raise_on_no_answer = raise_on_no_answer - self.nxdomain_responses: Dict[dns.name.Name, dns.message.QueryMessage] = {} - # Initialize other things to help analysis tools - self.qname = dns.name.empty - self.nameservers: List[dns.nameserver.Nameserver] = [] - self.current_nameservers: List[dns.nameserver.Nameserver] = [] - self.errors: List[ErrorTuple] = [] - self.nameserver: Optional[dns.nameserver.Nameserver] = None - self.tcp_attempt = False - self.retry_with_tcp = False - self.request: Optional[dns.message.QueryMessage] = None - self.backoff = 0.0 - - def next_request( - self, - ) -> Tuple[Optional[dns.message.QueryMessage], Optional[Answer]]: - """Get the next request to send, and check the cache. - - Returns a (request, answer) tuple. At most one of request or - answer will not be None. - """ - - # We return a tuple instead of Union[Message,Answer] as it lets - # the caller avoid isinstance(). - - while len(self.qnames) > 0: - self.qname = self.qnames.pop(0) - - # Do we know the answer? - if self.resolver.cache: - answer = self.resolver.cache.get( - (self.qname, self.rdtype, self.rdclass) - ) - if answer is not None: - if answer.rrset is None and self.raise_on_no_answer: - raise NoAnswer(response=answer.response) - else: - return (None, answer) - answer = self.resolver.cache.get( - (self.qname, dns.rdatatype.ANY, self.rdclass) - ) - if answer is not None and answer.response.rcode() == dns.rcode.NXDOMAIN: - # cached NXDOMAIN; record it and continue to next - # name. - self.nxdomain_responses[self.qname] = answer.response - continue - - # Build the request - request = dns.message.make_query(self.qname, self.rdtype, self.rdclass) - if self.resolver.keyname is not None: - request.use_tsig( - self.resolver.keyring, - self.resolver.keyname, - algorithm=self.resolver.keyalgorithm, - ) - request.use_edns( - self.resolver.edns, - self.resolver.ednsflags, - self.resolver.payload, - options=self.resolver.ednsoptions, - ) - if self.resolver.flags is not None: - request.flags = self.resolver.flags - - self.nameservers = self.resolver._enrich_nameservers( - self.resolver._nameservers, - self.resolver.nameserver_ports, - self.resolver.port, - ) - if self.resolver.rotate: - random.shuffle(self.nameservers) - self.current_nameservers = self.nameservers[:] - self.errors = [] - self.nameserver = None - self.tcp_attempt = False - self.retry_with_tcp = False - self.request = request - self.backoff = 0.10 - - return (request, None) - - # - # We've tried everything and only gotten NXDOMAINs. (We know - # it's only NXDOMAINs as anything else would have returned - # before now.) - # - raise NXDOMAIN(qnames=self.qnames_to_try, responses=self.nxdomain_responses) - - def next_nameserver(self) -> Tuple[dns.nameserver.Nameserver, bool, float]: - if self.retry_with_tcp: - assert self.nameserver is not None - assert not self.nameserver.is_always_max_size() - self.tcp_attempt = True - self.retry_with_tcp = False - return (self.nameserver, True, 0) - - backoff = 0.0 - if not self.current_nameservers: - if len(self.nameservers) == 0: - # Out of things to try! - raise NoNameservers(request=self.request, errors=self.errors) - self.current_nameservers = self.nameservers[:] - backoff = self.backoff - self.backoff = min(self.backoff * 2, 2) - - self.nameserver = self.current_nameservers.pop(0) - self.tcp_attempt = self.tcp or self.nameserver.is_always_max_size() - return (self.nameserver, self.tcp_attempt, backoff) - - def query_result( - self, response: Optional[dns.message.Message], ex: Optional[Exception] - ) -> Tuple[Optional[Answer], bool]: - # - # returns an (answer: Answer, end_loop: bool) tuple. - # - assert self.nameserver is not None - if ex: - # Exception during I/O or from_wire() - assert response is None - self.errors.append( - ( - str(self.nameserver), - self.tcp_attempt, - self.nameserver.answer_port(), - ex, - response, - ) - ) - if ( - isinstance(ex, dns.exception.FormError) - or isinstance(ex, EOFError) - or isinstance(ex, OSError) - or isinstance(ex, NotImplementedError) - ): - # This nameserver is no good, take it out of the mix. - self.nameservers.remove(self.nameserver) - elif isinstance(ex, dns.message.Truncated): - if self.tcp_attempt: - # Truncation with TCP is no good! - self.nameservers.remove(self.nameserver) - else: - self.retry_with_tcp = True - return (None, False) - # We got an answer! - assert response is not None - assert isinstance(response, dns.message.QueryMessage) - rcode = response.rcode() - if rcode == dns.rcode.NOERROR: - try: - answer = Answer( - self.qname, - self.rdtype, - self.rdclass, - response, - self.nameserver.answer_nameserver(), - self.nameserver.answer_port(), - ) - except Exception as e: - self.errors.append( - ( - str(self.nameserver), - self.tcp_attempt, - self.nameserver.answer_port(), - e, - response, - ) - ) - # The nameserver is no good, take it out of the mix. - self.nameservers.remove(self.nameserver) - return (None, False) - if self.resolver.cache: - self.resolver.cache.put((self.qname, self.rdtype, self.rdclass), answer) - if answer.rrset is None and self.raise_on_no_answer: - raise NoAnswer(response=answer.response) - return (answer, True) - elif rcode == dns.rcode.NXDOMAIN: - # Further validate the response by making an Answer, even - # if we aren't going to cache it. - try: - answer = Answer( - self.qname, dns.rdatatype.ANY, dns.rdataclass.IN, response - ) - except Exception as e: - self.errors.append( - ( - str(self.nameserver), - self.tcp_attempt, - self.nameserver.answer_port(), - e, - response, - ) - ) - # The nameserver is no good, take it out of the mix. - self.nameservers.remove(self.nameserver) - return (None, False) - self.nxdomain_responses[self.qname] = response - if self.resolver.cache: - self.resolver.cache.put( - (self.qname, dns.rdatatype.ANY, self.rdclass), answer - ) - # Make next_nameserver() return None, so caller breaks its - # inner loop and calls next_request(). - return (None, True) - elif rcode == dns.rcode.YXDOMAIN: - yex = YXDOMAIN() - self.errors.append( - ( - str(self.nameserver), - self.tcp_attempt, - self.nameserver.answer_port(), - yex, - response, - ) - ) - raise yex - else: - # - # We got a response, but we're not happy with the - # rcode in it. - # - if rcode != dns.rcode.SERVFAIL or not self.resolver.retry_servfail: - self.nameservers.remove(self.nameserver) - self.errors.append( - ( - str(self.nameserver), - self.tcp_attempt, - self.nameserver.answer_port(), - dns.rcode.to_text(rcode), - response, - ) - ) - return (None, False) - - -class BaseResolver: - """DNS stub resolver.""" - - # We initialize in reset() - # - # pylint: disable=attribute-defined-outside-init - - domain: dns.name.Name - nameserver_ports: Dict[str, int] - port: int - search: List[dns.name.Name] - use_search_by_default: bool - timeout: float - lifetime: float - keyring: Optional[Any] - keyname: Optional[Union[dns.name.Name, str]] - keyalgorithm: Union[dns.name.Name, str] - edns: int - ednsflags: int - ednsoptions: Optional[List[dns.edns.Option]] - payload: int - cache: Any - flags: Optional[int] - retry_servfail: bool - rotate: bool - ndots: Optional[int] - _nameservers: Sequence[Union[str, dns.nameserver.Nameserver]] - - def __init__( - self, filename: str = "/etc/resolv.conf", configure: bool = True - ) -> None: - """*filename*, a ``str`` or file object, specifying a file - in standard /etc/resolv.conf format. This parameter is meaningful - only when *configure* is true and the platform is POSIX. - - *configure*, a ``bool``. If True (the default), the resolver - instance is configured in the normal fashion for the operating - system the resolver is running on. (I.e. by reading a - /etc/resolv.conf file on POSIX systems and from the registry - on Windows systems.) - """ - - self.reset() - if configure: - if sys.platform == "win32": # pragma: no cover - self.read_registry() - elif filename: - self.read_resolv_conf(filename) - - def reset(self) -> None: - """Reset all resolver configuration to the defaults.""" - - self.domain = dns.name.Name(dns.name.from_text(socket.gethostname())[1:]) - if len(self.domain) == 0: # pragma: no cover - self.domain = dns.name.root - self._nameservers = [] - self.nameserver_ports = {} - self.port = 53 - self.search = [] - self.use_search_by_default = False - self.timeout = 2.0 - self.lifetime = 5.0 - self.keyring = None - self.keyname = None - self.keyalgorithm = dns.tsig.default_algorithm - self.edns = -1 - self.ednsflags = 0 - self.ednsoptions = None - self.payload = 0 - self.cache = None - self.flags = None - self.retry_servfail = False - self.rotate = False - self.ndots = None - - def read_resolv_conf(self, f: Any) -> None: - """Process *f* as a file in the /etc/resolv.conf format. If f is - a ``str``, it is used as the name of the file to open; otherwise it - is treated as the file itself. - - Interprets the following items: - - - nameserver - name server IP address - - - domain - local domain name - - - search - search list for host-name lookup - - - options - supported options are rotate, timeout, edns0, and ndots - - """ - - nameservers = [] - if isinstance(f, str): - try: - cm: contextlib.AbstractContextManager = open(f) - except OSError: - # /etc/resolv.conf doesn't exist, can't be read, etc. - raise NoResolverConfiguration(f"cannot open {f}") - else: - cm = contextlib.nullcontext(f) - with cm as f: - for l in f: - if len(l) == 0 or l[0] == "#" or l[0] == ";": - continue - tokens = l.split() - - # Any line containing less than 2 tokens is malformed - if len(tokens) < 2: - continue - - if tokens[0] == "nameserver": - nameservers.append(tokens[1]) - elif tokens[0] == "domain": - self.domain = dns.name.from_text(tokens[1]) - # domain and search are exclusive - self.search = [] - elif tokens[0] == "search": - # the last search wins - self.search = [] - for suffix in tokens[1:]: - self.search.append(dns.name.from_text(suffix)) - # We don't set domain as it is not used if - # len(self.search) > 0 - elif tokens[0] == "options": - for opt in tokens[1:]: - if opt == "rotate": - self.rotate = True - elif opt == "edns0": - self.use_edns() - elif "timeout" in opt: - try: - self.timeout = int(opt.split(":")[1]) - except (ValueError, IndexError): - pass - elif "ndots" in opt: - try: - self.ndots = int(opt.split(":")[1]) - except (ValueError, IndexError): - pass - if len(nameservers) == 0: - raise NoResolverConfiguration("no nameservers") - # Assigning directly instead of appending means we invoke the - # setter logic, with additonal checking and enrichment. - self.nameservers = nameservers - - def read_registry(self) -> None: # pragma: no cover - """Extract resolver configuration from the Windows registry.""" - try: - info = dns.win32util.get_dns_info() # type: ignore - if info.domain is not None: - self.domain = info.domain - self.nameservers = info.nameservers - self.search = info.search - except AttributeError: - raise NotImplementedError - - def _compute_timeout( - self, - start: float, - lifetime: Optional[float] = None, - errors: Optional[List[ErrorTuple]] = None, - ) -> float: - lifetime = self.lifetime if lifetime is None else lifetime - now = time.time() - duration = now - start - if errors is None: - errors = [] - if duration < 0: - if duration < -1: - # Time going backwards is bad. Just give up. - raise LifetimeTimeout(timeout=duration, errors=errors) - else: - # Time went backwards, but only a little. This can - # happen, e.g. under vmware with older linux kernels. - # Pretend it didn't happen. - duration = 0 - if duration >= lifetime: - raise LifetimeTimeout(timeout=duration, errors=errors) - return min(lifetime - duration, self.timeout) - - def _get_qnames_to_try( - self, qname: dns.name.Name, search: Optional[bool] - ) -> List[dns.name.Name]: - # This is a separate method so we can unit test the search - # rules without requiring the Internet. - if search is None: - search = self.use_search_by_default - qnames_to_try = [] - if qname.is_absolute(): - qnames_to_try.append(qname) - else: - abs_qname = qname.concatenate(dns.name.root) - if search: - if len(self.search) > 0: - # There is a search list, so use it exclusively - search_list = self.search[:] - elif self.domain != dns.name.root and self.domain is not None: - # We have some notion of a domain that isn't the root, so - # use it as the search list. - search_list = [self.domain] - else: - search_list = [] - # Figure out the effective ndots (default is 1) - if self.ndots is None: - ndots = 1 - else: - ndots = self.ndots - for suffix in search_list: - qnames_to_try.append(qname + suffix) - if len(qname) > ndots: - # The name has at least ndots dots, so we should try an - # absolute query first. - qnames_to_try.insert(0, abs_qname) - else: - # The name has less than ndots dots, so we should search - # first, then try the absolute name. - qnames_to_try.append(abs_qname) - else: - qnames_to_try.append(abs_qname) - return qnames_to_try - - def use_tsig( - self, - keyring: Any, - keyname: Optional[Union[dns.name.Name, str]] = None, - algorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, - ) -> None: - """Add a TSIG signature to each query. - - The parameters are passed to ``dns.message.Message.use_tsig()``; - see its documentation for details. - """ - - self.keyring = keyring - self.keyname = keyname - self.keyalgorithm = algorithm - - def use_edns( - self, - edns: Optional[Union[int, bool]] = 0, - ednsflags: int = 0, - payload: int = dns.message.DEFAULT_EDNS_PAYLOAD, - options: Optional[List[dns.edns.Option]] = None, - ) -> None: - """Configure EDNS behavior. - - *edns*, an ``int``, is the EDNS level to use. Specifying - ``None``, ``False``, or ``-1`` means "do not use EDNS", and in this case - the other parameters are ignored. Specifying ``True`` is - equivalent to specifying 0, i.e. "use EDNS0". - - *ednsflags*, an ``int``, the EDNS flag values. - - *payload*, an ``int``, is the EDNS sender's payload field, which is the - maximum size of UDP datagram the sender can handle. I.e. how big - a response to this message can be. - - *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS - options. - """ - - if edns is None or edns is False: - edns = -1 - elif edns is True: - edns = 0 - self.edns = edns - self.ednsflags = ednsflags - self.payload = payload - self.ednsoptions = options - - def set_flags(self, flags: int) -> None: - """Overrides the default flags with your own. - - *flags*, an ``int``, the message flags to use. - """ - - self.flags = flags - - @classmethod - def _enrich_nameservers( - cls, - nameservers: Sequence[Union[str, dns.nameserver.Nameserver]], - nameserver_ports: Dict[str, int], - default_port: int, - ) -> List[dns.nameserver.Nameserver]: - enriched_nameservers = [] - if isinstance(nameservers, list): - for nameserver in nameservers: - enriched_nameserver: dns.nameserver.Nameserver - if isinstance(nameserver, dns.nameserver.Nameserver): - enriched_nameserver = nameserver - elif dns.inet.is_address(nameserver): - port = nameserver_ports.get(nameserver, default_port) - enriched_nameserver = dns.nameserver.Do53Nameserver( - nameserver, port - ) - else: - try: - if urlparse(nameserver).scheme != "https": - raise NotImplementedError - except Exception: - raise ValueError( - f"nameserver {nameserver} is not a " - "dns.nameserver.Nameserver instance or text form, " - "IP address, nor a valid https URL" - ) - enriched_nameserver = dns.nameserver.DoHNameserver(nameserver) - enriched_nameservers.append(enriched_nameserver) - else: - raise ValueError( - f"nameservers must be a list or tuple (not a {type(nameservers)})" - ) - return enriched_nameservers - - @property - def nameservers( - self, - ) -> Sequence[Union[str, dns.nameserver.Nameserver]]: - return self._nameservers - - @nameservers.setter - def nameservers( - self, nameservers: Sequence[Union[str, dns.nameserver.Nameserver]] - ) -> None: - """ - *nameservers*, a ``list`` of nameservers, where a nameserver is either - a string interpretable as a nameserver, or a ``dns.nameserver.Nameserver`` - instance. - - Raises ``ValueError`` if *nameservers* is not a list of nameservers. - """ - # We just call _enrich_nameservers() for checking - self._enrich_nameservers(nameservers, self.nameserver_ports, self.port) - self._nameservers = nameservers - - -class Resolver(BaseResolver): - """DNS stub resolver.""" - - def resolve( - self, - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, - search: Optional[bool] = None, - ) -> Answer: # pylint: disable=arguments-differ - """Query nameservers to find the answer to the question. - - The *qname*, *rdtype*, and *rdclass* parameters may be objects - of the appropriate type, or strings that can be converted into objects - of the appropriate type. - - *qname*, a ``dns.name.Name`` or ``str``, the query name. - - *rdtype*, an ``int`` or ``str``, the query type. - - *rdclass*, an ``int`` or ``str``, the query class. - - *tcp*, a ``bool``. If ``True``, use TCP to make the query. - - *source*, a ``str`` or ``None``. If not ``None``, bind to this IP - address when making queries. - - *raise_on_no_answer*, a ``bool``. If ``True``, raise - ``dns.resolver.NoAnswer`` if there's no answer to the question. - - *source_port*, an ``int``, the port from which to send the message. - - *lifetime*, a ``float``, how many seconds a query should run - before timing out. - - *search*, a ``bool`` or ``None``, determines whether the - search list configured in the system's resolver configuration - are used for relative names, and whether the resolver's domain - may be added to relative names. The default is ``None``, - which causes the value of the resolver's - ``use_search_by_default`` attribute to be used. - - Raises ``dns.resolver.LifetimeTimeout`` if no answers could be found - in the specified lifetime. - - Raises ``dns.resolver.NXDOMAIN`` if the query name does not exist. - - Raises ``dns.resolver.YXDOMAIN`` if the query name is too long after - DNAME substitution. - - Raises ``dns.resolver.NoAnswer`` if *raise_on_no_answer* is - ``True`` and the query name exists but has no RRset of the - desired type and class. - - Raises ``dns.resolver.NoNameservers`` if no non-broken - nameservers are available to answer the question. - - Returns a ``dns.resolver.Answer`` instance. - - """ - - resolution = _Resolution( - self, qname, rdtype, rdclass, tcp, raise_on_no_answer, search - ) - start = time.time() - while True: - (request, answer) = resolution.next_request() - # Note we need to say "if answer is not None" and not just - # "if answer" because answer implements __len__, and python - # will call that. We want to return if we have an answer - # object, including in cases where its length is 0. - if answer is not None: - # cache hit! - return answer - assert request is not None # needed for type checking - done = False - while not done: - (nameserver, tcp, backoff) = resolution.next_nameserver() - if backoff: - time.sleep(backoff) - timeout = self._compute_timeout(start, lifetime, resolution.errors) - try: - response = nameserver.query( - request, - timeout=timeout, - source=source, - source_port=source_port, - max_size=tcp, - ) - except Exception as ex: - (_, done) = resolution.query_result(None, ex) - continue - (answer, done) = resolution.query_result(response, None) - # Note we need to say "if answer is not None" and not just - # "if answer" because answer implements __len__, and python - # will call that. We want to return if we have an answer - # object, including in cases where its length is 0. - if answer is not None: - return answer - - def query( - self, - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, - ) -> Answer: # pragma: no cover - """Query nameservers to find the answer to the question. - - This method calls resolve() with ``search=True``, and is - provided for backwards compatibility with prior versions of - dnspython. See the documentation for the resolve() method for - further details. - """ - warnings.warn( - "please use dns.resolver.Resolver.resolve() instead", - DeprecationWarning, - stacklevel=2, - ) - return self.resolve( - qname, - rdtype, - rdclass, - tcp, - source, - raise_on_no_answer, - source_port, - lifetime, - True, - ) - - def resolve_address(self, ipaddr: str, *args: Any, **kwargs: Any) -> Answer: - """Use a resolver to run a reverse query for PTR records. - - This utilizes the resolve() method to perform a PTR lookup on the - specified IP address. - - *ipaddr*, a ``str``, the IPv4 or IPv6 address you want to get - the PTR record for. - - All other arguments that can be passed to the resolve() function - except for rdtype and rdclass are also supported by this - function. - """ - # We make a modified kwargs for type checking happiness, as otherwise - # we get a legit warning about possibly having rdtype and rdclass - # in the kwargs more than once. - modified_kwargs: Dict[str, Any] = {} - modified_kwargs.update(kwargs) - modified_kwargs["rdtype"] = dns.rdatatype.PTR - modified_kwargs["rdclass"] = dns.rdataclass.IN - return self.resolve( - dns.reversename.from_address(ipaddr), *args, **modified_kwargs - ) - - def resolve_name( - self, - name: Union[dns.name.Name, str], - family: int = socket.AF_UNSPEC, - **kwargs: Any, - ) -> HostAnswers: - """Use a resolver to query for address records. - - This utilizes the resolve() method to perform A and/or AAAA lookups on - the specified name. - - *qname*, a ``dns.name.Name`` or ``str``, the name to resolve. - - *family*, an ``int``, the address family. If socket.AF_UNSPEC - (the default), both A and AAAA records will be retrieved. - - All other arguments that can be passed to the resolve() function - except for rdtype and rdclass are also supported by this - function. - """ - # We make a modified kwargs for type checking happiness, as otherwise - # we get a legit warning about possibly having rdtype and rdclass - # in the kwargs more than once. - modified_kwargs: Dict[str, Any] = {} - modified_kwargs.update(kwargs) - modified_kwargs.pop("rdtype", None) - modified_kwargs["rdclass"] = dns.rdataclass.IN - - if family == socket.AF_INET: - v4 = self.resolve(name, dns.rdatatype.A, **modified_kwargs) - return HostAnswers.make(v4=v4) - elif family == socket.AF_INET6: - v6 = self.resolve(name, dns.rdatatype.AAAA, **modified_kwargs) - return HostAnswers.make(v6=v6) - elif family != socket.AF_UNSPEC: # pragma: no cover - raise NotImplementedError(f"unknown address family {family}") - - raise_on_no_answer = modified_kwargs.pop("raise_on_no_answer", True) - lifetime = modified_kwargs.pop("lifetime", None) - start = time.time() - v6 = self.resolve( - name, - dns.rdatatype.AAAA, - raise_on_no_answer=False, - lifetime=self._compute_timeout(start, lifetime), - **modified_kwargs, - ) - # Note that setting name ensures we query the same name - # for A as we did for AAAA. (This is just in case search lists - # are active by default in the resolver configuration and - # we might be talking to a server that says NXDOMAIN when it - # wants to say NOERROR no data. - name = v6.qname - v4 = self.resolve( - name, - dns.rdatatype.A, - raise_on_no_answer=False, - lifetime=self._compute_timeout(start, lifetime), - **modified_kwargs, - ) - answers = HostAnswers.make(v6=v6, v4=v4, add_empty=not raise_on_no_answer) - if not answers: - raise NoAnswer(response=v6.response) - return answers - - # pylint: disable=redefined-outer-name - - def canonical_name(self, name: Union[dns.name.Name, str]) -> dns.name.Name: - """Determine the canonical name of *name*. - - The canonical name is the name the resolver uses for queries - after all CNAME and DNAME renamings have been applied. - - *name*, a ``dns.name.Name`` or ``str``, the query name. - - This method can raise any exception that ``resolve()`` can - raise, other than ``dns.resolver.NoAnswer`` and - ``dns.resolver.NXDOMAIN``. - - Returns a ``dns.name.Name``. - """ - try: - answer = self.resolve(name, raise_on_no_answer=False) - canonical_name = answer.canonical_name - except dns.resolver.NXDOMAIN as e: - canonical_name = e.canonical_name - return canonical_name - - # pylint: enable=redefined-outer-name - - def try_ddr(self, lifetime: float = 5.0) -> None: - """Try to update the resolver's nameservers using Discovery of Designated - Resolvers (DDR). If successful, the resolver will subsequently use - DNS-over-HTTPS or DNS-over-TLS for future queries. - - *lifetime*, a float, is the maximum time to spend attempting DDR. The default - is 5 seconds. - - If the SVCB query is successful and results in a non-empty list of nameservers, - then the resolver's nameservers are set to the returned servers in priority - order. - - The current implementation does not use any address hints from the SVCB record, - nor does it resolve addresses for the SCVB target name, rather it assumes that - the bootstrap nameserver will always be one of the addresses and uses it. - A future revision to the code may offer fuller support. The code verifies that - the bootstrap nameserver is in the Subject Alternative Name field of the - TLS certficate. - """ - try: - expiration = time.time() + lifetime - answer = self.resolve( - dns._ddr._local_resolver_name, "SVCB", lifetime=lifetime - ) - timeout = dns.query._remaining(expiration) - nameservers = dns._ddr._get_nameservers_sync(answer, timeout) - if len(nameservers) > 0: - self.nameservers = nameservers - except Exception: # pragma: no cover - pass - - -#: The default resolver. -default_resolver: Optional[Resolver] = None - - -def get_default_resolver() -> Resolver: - """Get the default resolver, initializing it if necessary.""" - if default_resolver is None: - reset_default_resolver() - assert default_resolver is not None - return default_resolver - - -def reset_default_resolver() -> None: - """Re-initialize default resolver. - - Note that the resolver configuration (i.e. /etc/resolv.conf on UNIX - systems) will be re-read immediately. - """ - - global default_resolver - default_resolver = Resolver() - - -def resolve( - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, - search: Optional[bool] = None, -) -> Answer: # pragma: no cover - """Query nameservers to find the answer to the question. - - This is a convenience function that uses the default resolver - object to make the query. - - See ``dns.resolver.Resolver.resolve`` for more information on the - parameters. - """ - - return get_default_resolver().resolve( - qname, - rdtype, - rdclass, - tcp, - source, - raise_on_no_answer, - source_port, - lifetime, - search, - ) - - -def query( - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, -) -> Answer: # pragma: no cover - """Query nameservers to find the answer to the question. - - This method calls resolve() with ``search=True``, and is - provided for backwards compatibility with prior versions of - dnspython. See the documentation for the resolve() method for - further details. - """ - warnings.warn( - "please use dns.resolver.resolve() instead", DeprecationWarning, stacklevel=2 - ) - return resolve( - qname, - rdtype, - rdclass, - tcp, - source, - raise_on_no_answer, - source_port, - lifetime, - True, - ) - - -def resolve_address(ipaddr: str, *args: Any, **kwargs: Any) -> Answer: - """Use a resolver to run a reverse query for PTR records. - - See ``dns.resolver.Resolver.resolve_address`` for more information on the - parameters. - """ - - return get_default_resolver().resolve_address(ipaddr, *args, **kwargs) - - -def resolve_name( - name: Union[dns.name.Name, str], family: int = socket.AF_UNSPEC, **kwargs: Any -) -> HostAnswers: - """Use a resolver to query for address records. - - See ``dns.resolver.Resolver.resolve_name`` for more information on the - parameters. - """ - - return get_default_resolver().resolve_name(name, family, **kwargs) - - -def canonical_name(name: Union[dns.name.Name, str]) -> dns.name.Name: - """Determine the canonical name of *name*. - - See ``dns.resolver.Resolver.canonical_name`` for more information on the - parameters and possible exceptions. - """ - - return get_default_resolver().canonical_name(name) - - -def try_ddr(lifetime: float = 5.0) -> None: # pragma: no cover - """Try to update the default resolver's nameservers using Discovery of Designated - Resolvers (DDR). If successful, the resolver will subsequently use - DNS-over-HTTPS or DNS-over-TLS for future queries. - - See :py:func:`dns.resolver.Resolver.try_ddr` for more information. - """ - return get_default_resolver().try_ddr(lifetime) - - -def zone_for_name( - name: Union[dns.name.Name, str], - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - tcp: bool = False, - resolver: Optional[Resolver] = None, - lifetime: Optional[float] = None, -) -> dns.name.Name: - """Find the name of the zone which contains the specified name. - - *name*, an absolute ``dns.name.Name`` or ``str``, the query name. - - *rdclass*, an ``int``, the query class. - - *tcp*, a ``bool``. If ``True``, use TCP to make the query. - - *resolver*, a ``dns.resolver.Resolver`` or ``None``, the resolver to use. - If ``None``, the default, then the default resolver is used. - - *lifetime*, a ``float``, the total time to allow for the queries needed - to determine the zone. If ``None``, the default, then only the individual - query limits of the resolver apply. - - Raises ``dns.resolver.NoRootSOA`` if there is no SOA RR at the DNS - root. (This is only likely to happen if you're using non-default - root servers in your network and they are misconfigured.) - - Raises ``dns.resolver.LifetimeTimeout`` if the answer could not be - found in the allotted lifetime. - - Returns a ``dns.name.Name``. - """ - - if isinstance(name, str): - name = dns.name.from_text(name, dns.name.root) - if resolver is None: - resolver = get_default_resolver() - if not name.is_absolute(): - raise NotAbsolute(name) - start = time.time() - expiration: Optional[float] - if lifetime is not None: - expiration = start + lifetime - else: - expiration = None - while 1: - try: - rlifetime: Optional[float] - if expiration is not None: - rlifetime = expiration - time.time() - if rlifetime <= 0: - rlifetime = 0 - else: - rlifetime = None - answer = resolver.resolve( - name, dns.rdatatype.SOA, rdclass, tcp, lifetime=rlifetime - ) - assert answer.rrset is not None - if answer.rrset.name == name: - return name - # otherwise we were CNAMEd or DNAMEd and need to look higher - except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer) as e: - if isinstance(e, dns.resolver.NXDOMAIN): - response = e.responses().get(name) - else: - response = e.response() # pylint: disable=no-value-for-parameter - if response: - for rrs in response.authority: - if rrs.rdtype == dns.rdatatype.SOA and rrs.rdclass == rdclass: - (nr, _, _) = rrs.name.fullcompare(name) - if nr == dns.name.NAMERELN_SUPERDOMAIN: - # We're doing a proper superdomain check as - # if the name were equal we ought to have gotten - # it in the answer section! We are ignoring the - # possibility that the authority is insane and - # is including multiple SOA RRs for different - # authorities. - return rrs.name - # we couldn't extract anything useful from the response (e.g. it's - # a type 3 NXDOMAIN) - try: - name = name.parent() - except dns.name.NoParent: - raise NoRootSOA - - -def make_resolver_at( - where: Union[dns.name.Name, str], - port: int = 53, - family: int = socket.AF_UNSPEC, - resolver: Optional[Resolver] = None, -) -> Resolver: - """Make a stub resolver using the specified destination as the full resolver. - - *where*, a ``dns.name.Name`` or ``str`` the domain name or IP address of the - full resolver. - - *port*, an ``int``, the port to use. If not specified, the default is 53. - - *family*, an ``int``, the address family to use. This parameter is used if - *where* is not an address. The default is ``socket.AF_UNSPEC`` in which case - the first address returned by ``resolve_name()`` will be used, otherwise the - first address of the specified family will be used. - - *resolver*, a ``dns.resolver.Resolver`` or ``None``, the resolver to use for - resolution of hostnames. If not specified, the default resolver will be used. - - Returns a ``dns.resolver.Resolver`` or raises an exception. - """ - if resolver is None: - resolver = get_default_resolver() - nameservers: List[Union[str, dns.nameserver.Nameserver]] = [] - if isinstance(where, str) and dns.inet.is_address(where): - nameservers.append(dns.nameserver.Do53Nameserver(where, port)) - else: - for address in resolver.resolve_name(where, family).addresses(): - nameservers.append(dns.nameserver.Do53Nameserver(address, port)) - res = dns.resolver.Resolver(configure=False) - res.nameservers = nameservers - return res - - -def resolve_at( - where: Union[dns.name.Name, str], - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, - search: Optional[bool] = None, - port: int = 53, - family: int = socket.AF_UNSPEC, - resolver: Optional[Resolver] = None, -) -> Answer: - """Query nameservers to find the answer to the question. - - This is a convenience function that calls ``dns.resolver.make_resolver_at()`` to - make a resolver, and then uses it to resolve the query. - - See ``dns.resolver.Resolver.resolve`` for more information on the resolution - parameters, and ``dns.resolver.make_resolver_at`` for information about the resolver - parameters *where*, *port*, *family*, and *resolver*. - - If making more than one query, it is more efficient to call - ``dns.resolver.make_resolver_at()`` and then use that resolver for the queries - instead of calling ``resolve_at()`` multiple times. - """ - return make_resolver_at(where, port, family, resolver).resolve( - qname, - rdtype, - rdclass, - tcp, - source, - raise_on_no_answer, - source_port, - lifetime, - search, - ) - - -# -# Support for overriding the system resolver for all python code in the -# running process. -# - -_protocols_for_socktype = { - socket.SOCK_DGRAM: [socket.SOL_UDP], - socket.SOCK_STREAM: [socket.SOL_TCP], -} - -_resolver = None -_original_getaddrinfo = socket.getaddrinfo -_original_getnameinfo = socket.getnameinfo -_original_getfqdn = socket.getfqdn -_original_gethostbyname = socket.gethostbyname -_original_gethostbyname_ex = socket.gethostbyname_ex -_original_gethostbyaddr = socket.gethostbyaddr - - -def _getaddrinfo( - host=None, service=None, family=socket.AF_UNSPEC, socktype=0, proto=0, flags=0 -): - if flags & socket.AI_NUMERICHOST != 0: - # Short circuit directly into the system's getaddrinfo(). We're - # not adding any value in this case, and this avoids infinite loops - # because dns.query.* needs to call getaddrinfo() for IPv6 scoping - # reasons. We will also do this short circuit below if we - # discover that the host is an address literal. - return _original_getaddrinfo(host, service, family, socktype, proto, flags) - if flags & (socket.AI_ADDRCONFIG | socket.AI_V4MAPPED) != 0: - # Not implemented. We raise a gaierror as opposed to a - # NotImplementedError as it helps callers handle errors more - # appropriately. [Issue #316] - # - # We raise EAI_FAIL as opposed to EAI_SYSTEM because there is - # no EAI_SYSTEM on Windows [Issue #416]. We didn't go for - # EAI_BADFLAGS as the flags aren't bad, we just don't - # implement them. - raise socket.gaierror( - socket.EAI_FAIL, "Non-recoverable failure in name resolution" - ) - if host is None and service is None: - raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") - addrs = [] - canonical_name = None # pylint: disable=redefined-outer-name - # Is host None or an address literal? If so, use the system's - # getaddrinfo(). - if host is None: - return _original_getaddrinfo(host, service, family, socktype, proto, flags) - try: - # We don't care about the result of af_for_address(), we're just - # calling it so it raises an exception if host is not an IPv4 or - # IPv6 address. - dns.inet.af_for_address(host) - return _original_getaddrinfo(host, service, family, socktype, proto, flags) - except Exception: - pass - # Something needs resolution! - try: - answers = _resolver.resolve_name(host, family) - addrs = answers.addresses_and_families() - canonical_name = answers.canonical_name().to_text(True) - except dns.resolver.NXDOMAIN: - raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") - except Exception: - # We raise EAI_AGAIN here as the failure may be temporary - # (e.g. a timeout) and EAI_SYSTEM isn't defined on Windows. - # [Issue #416] - raise socket.gaierror(socket.EAI_AGAIN, "Temporary failure in name resolution") - port = None - try: - # Is it a port literal? - if service is None: - port = 0 - else: - port = int(service) - except Exception: - if flags & socket.AI_NUMERICSERV == 0: - try: - port = socket.getservbyname(service) - except Exception: - pass - if port is None: - raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") - tuples = [] - if socktype == 0: - socktypes = [socket.SOCK_DGRAM, socket.SOCK_STREAM] - else: - socktypes = [socktype] - if flags & socket.AI_CANONNAME != 0: - cname = canonical_name - else: - cname = "" - for addr, af in addrs: - for socktype in socktypes: - for proto in _protocols_for_socktype[socktype]: - addr_tuple = dns.inet.low_level_address_tuple((addr, port), af) - tuples.append((af, socktype, proto, cname, addr_tuple)) - if len(tuples) == 0: - raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") - return tuples - - -def _getnameinfo(sockaddr, flags=0): - host = sockaddr[0] - port = sockaddr[1] - if len(sockaddr) == 4: - scope = sockaddr[3] - family = socket.AF_INET6 - else: - scope = None - family = socket.AF_INET - tuples = _getaddrinfo(host, port, family, socket.SOCK_STREAM, socket.SOL_TCP, 0) - if len(tuples) > 1: - raise OSError("sockaddr resolved to multiple addresses") - addr = tuples[0][4][0] - if flags & socket.NI_DGRAM: - pname = "udp" - else: - pname = "tcp" - qname = dns.reversename.from_address(addr) - if flags & socket.NI_NUMERICHOST == 0: - try: - answer = _resolver.resolve(qname, "PTR") - hostname = answer.rrset[0].target.to_text(True) - except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): - if flags & socket.NI_NAMEREQD: - raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") - hostname = addr - if scope is not None: - hostname += "%" + str(scope) - else: - hostname = addr - if scope is not None: - hostname += "%" + str(scope) - if flags & socket.NI_NUMERICSERV: - service = str(port) - else: - service = socket.getservbyport(port, pname) - return (hostname, service) - - -def _getfqdn(name=None): - if name is None: - name = socket.gethostname() - try: - (name, _, _) = _gethostbyaddr(name) - # Python's version checks aliases too, but our gethostbyname - # ignores them, so we do so here as well. - except Exception: # pragma: no cover - pass - return name - - -def _gethostbyname(name): - return _gethostbyname_ex(name)[2][0] - - -def _gethostbyname_ex(name): - aliases = [] - addresses = [] - tuples = _getaddrinfo( - name, 0, socket.AF_INET, socket.SOCK_STREAM, socket.SOL_TCP, socket.AI_CANONNAME - ) - canonical = tuples[0][3] - for item in tuples: - addresses.append(item[4][0]) - # XXX we just ignore aliases - return (canonical, aliases, addresses) - - -def _gethostbyaddr(ip): - try: - dns.ipv6.inet_aton(ip) - sockaddr = (ip, 80, 0, 0) - family = socket.AF_INET6 - except Exception: - try: - dns.ipv4.inet_aton(ip) - except Exception: - raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") - sockaddr = (ip, 80) - family = socket.AF_INET - (name, _) = _getnameinfo(sockaddr, socket.NI_NAMEREQD) - aliases = [] - addresses = [] - tuples = _getaddrinfo( - name, 0, family, socket.SOCK_STREAM, socket.SOL_TCP, socket.AI_CANONNAME - ) - canonical = tuples[0][3] - # We only want to include an address from the tuples if it's the - # same as the one we asked about. We do this comparison in binary - # to avoid any differences in text representations. - bin_ip = dns.inet.inet_pton(family, ip) - for item in tuples: - addr = item[4][0] - bin_addr = dns.inet.inet_pton(family, addr) - if bin_ip == bin_addr: - addresses.append(addr) - # XXX we just ignore aliases - return (canonical, aliases, addresses) - - -def override_system_resolver(resolver: Optional[Resolver] = None) -> None: - """Override the system resolver routines in the socket module with - versions which use dnspython's resolver. - - This can be useful in testing situations where you want to control - the resolution behavior of python code without having to change - the system's resolver settings (e.g. /etc/resolv.conf). - - The resolver to use may be specified; if it's not, the default - resolver will be used. - - resolver, a ``dns.resolver.Resolver`` or ``None``, the resolver to use. - """ - - if resolver is None: - resolver = get_default_resolver() - global _resolver - _resolver = resolver - socket.getaddrinfo = _getaddrinfo - socket.getnameinfo = _getnameinfo - socket.getfqdn = _getfqdn - socket.gethostbyname = _gethostbyname - socket.gethostbyname_ex = _gethostbyname_ex - socket.gethostbyaddr = _gethostbyaddr - - -def restore_system_resolver() -> None: - """Undo the effects of prior override_system_resolver().""" - - global _resolver - _resolver = None - socket.getaddrinfo = _original_getaddrinfo - socket.getnameinfo = _original_getnameinfo - socket.getfqdn = _original_getfqdn - socket.gethostbyname = _original_gethostbyname - socket.gethostbyname_ex = _original_gethostbyname_ex - socket.gethostbyaddr = _original_gethostbyaddr diff --git a/venv/Lib/site-packages/dns/reversename.py b/venv/Lib/site-packages/dns/reversename.py deleted file mode 100644 index 8236c71..0000000 --- a/venv/Lib/site-packages/dns/reversename.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Reverse Map Names.""" - -import binascii - -import dns.ipv4 -import dns.ipv6 -import dns.name - -ipv4_reverse_domain = dns.name.from_text("in-addr.arpa.") -ipv6_reverse_domain = dns.name.from_text("ip6.arpa.") - - -def from_address( - text: str, - v4_origin: dns.name.Name = ipv4_reverse_domain, - v6_origin: dns.name.Name = ipv6_reverse_domain, -) -> dns.name.Name: - """Convert an IPv4 or IPv6 address in textual form into a Name object whose - value is the reverse-map domain name of the address. - - *text*, a ``str``, is an IPv4 or IPv6 address in textual form - (e.g. '127.0.0.1', '::1') - - *v4_origin*, a ``dns.name.Name`` to append to the labels corresponding to - the address if the address is an IPv4 address, instead of the default - (in-addr.arpa.) - - *v6_origin*, a ``dns.name.Name`` to append to the labels corresponding to - the address if the address is an IPv6 address, instead of the default - (ip6.arpa.) - - Raises ``dns.exception.SyntaxError`` if the address is badly formed. - - Returns a ``dns.name.Name``. - """ - - try: - v6 = dns.ipv6.inet_aton(text) - if dns.ipv6.is_mapped(v6): - parts = ["%d" % byte for byte in v6[12:]] - origin = v4_origin - else: - parts = [x for x in str(binascii.hexlify(v6).decode())] - origin = v6_origin - except Exception: - parts = ["%d" % byte for byte in dns.ipv4.inet_aton(text)] - origin = v4_origin - return dns.name.from_text(".".join(reversed(parts)), origin=origin) - - -def to_address( - name: dns.name.Name, - v4_origin: dns.name.Name = ipv4_reverse_domain, - v6_origin: dns.name.Name = ipv6_reverse_domain, -) -> str: - """Convert a reverse map domain name into textual address form. - - *name*, a ``dns.name.Name``, an IPv4 or IPv6 address in reverse-map name - form. - - *v4_origin*, a ``dns.name.Name`` representing the top-level domain for - IPv4 addresses, instead of the default (in-addr.arpa.) - - *v6_origin*, a ``dns.name.Name`` representing the top-level domain for - IPv4 addresses, instead of the default (ip6.arpa.) - - Raises ``dns.exception.SyntaxError`` if the name does not have a - reverse-map form. - - Returns a ``str``. - """ - - if name.is_subdomain(v4_origin): - name = name.relativize(v4_origin) - text = b".".join(reversed(name.labels)) - # run through inet_ntoa() to check syntax and make pretty. - return dns.ipv4.inet_ntoa(dns.ipv4.inet_aton(text)) - elif name.is_subdomain(v6_origin): - name = name.relativize(v6_origin) - labels = list(reversed(name.labels)) - parts = [] - for i in range(0, len(labels), 4): - parts.append(b"".join(labels[i : i + 4])) - text = b":".join(parts) - # run through inet_ntoa() to check syntax and make pretty. - return dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(text)) - else: - raise dns.exception.SyntaxError("unknown reverse-map address family") diff --git a/venv/Lib/site-packages/dns/rrset.py b/venv/Lib/site-packages/dns/rrset.py deleted file mode 100644 index 6f39b10..0000000 --- a/venv/Lib/site-packages/dns/rrset.py +++ /dev/null @@ -1,285 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS RRsets (an RRset is a named rdataset)""" - -from typing import Any, Collection, Dict, Optional, Union, cast - -import dns.name -import dns.rdataclass -import dns.rdataset -import dns.renderer - - -class RRset(dns.rdataset.Rdataset): - """A DNS RRset (named rdataset). - - RRset inherits from Rdataset, and RRsets can be treated as - Rdatasets in most cases. There are, however, a few notable - exceptions. RRsets have different to_wire() and to_text() method - arguments, reflecting the fact that RRsets always have an owner - name. - """ - - __slots__ = ["name", "deleting"] - - def __init__( - self, - name: dns.name.Name, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - deleting: Optional[dns.rdataclass.RdataClass] = None, - ): - """Create a new RRset.""" - - super().__init__(rdclass, rdtype, covers) - self.name = name - self.deleting = deleting - - def _clone(self): - obj = super()._clone() - obj.name = self.name - obj.deleting = self.deleting - return obj - - def __repr__(self): - if self.covers == 0: - ctext = "" - else: - ctext = "(" + dns.rdatatype.to_text(self.covers) + ")" - if self.deleting is not None: - dtext = " delete=" + dns.rdataclass.to_text(self.deleting) - else: - dtext = "" - return ( - "" - ) - - def __str__(self): - return self.to_text() - - def __eq__(self, other): - if isinstance(other, RRset): - if self.name != other.name: - return False - elif not isinstance(other, dns.rdataset.Rdataset): - return False - return super().__eq__(other) - - def match(self, *args: Any, **kwargs: Any) -> bool: # type: ignore[override] - """Does this rrset match the specified attributes? - - Behaves as :py:func:`full_match()` if the first argument is a - ``dns.name.Name``, and as :py:func:`dns.rdataset.Rdataset.match()` - otherwise. - - (This behavior fixes a design mistake where the signature of this - method became incompatible with that of its superclass. The fix - makes RRsets matchable as Rdatasets while preserving backwards - compatibility.) - """ - if isinstance(args[0], dns.name.Name): - return self.full_match(*args, **kwargs) # type: ignore[arg-type] - else: - return super().match(*args, **kwargs) # type: ignore[arg-type] - - def full_match( - self, - name: dns.name.Name, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType, - deleting: Optional[dns.rdataclass.RdataClass] = None, - ) -> bool: - """Returns ``True`` if this rrset matches the specified name, class, - type, covers, and deletion state. - """ - if not super().match(rdclass, rdtype, covers): - return False - if self.name != name or self.deleting != deleting: - return False - return True - - # pylint: disable=arguments-differ - - def to_text( # type: ignore[override] - self, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - **kw: Dict[str, Any], - ) -> str: - """Convert the RRset into DNS zone file format. - - See ``dns.name.Name.choose_relativity`` for more information - on how *origin* and *relativize* determine the way names - are emitted. - - Any additional keyword arguments are passed on to the rdata - ``to_text()`` method. - - *origin*, a ``dns.name.Name`` or ``None``, the origin for relative - names. - - *relativize*, a ``bool``. If ``True``, names will be relativized - to *origin*. - """ - - return super().to_text( - self.name, origin, relativize, self.deleting, **kw # type: ignore - ) - - def to_wire( # type: ignore[override] - self, - file: Any, - compress: Optional[dns.name.CompressType] = None, # type: ignore - origin: Optional[dns.name.Name] = None, - **kw: Dict[str, Any], - ) -> int: - """Convert the RRset to wire format. - - All keyword arguments are passed to ``dns.rdataset.to_wire()``; see - that function for details. - - Returns an ``int``, the number of records emitted. - """ - - return super().to_wire( - self.name, file, compress, origin, self.deleting, **kw # type:ignore - ) - - # pylint: enable=arguments-differ - - def to_rdataset(self) -> dns.rdataset.Rdataset: - """Convert an RRset into an Rdataset. - - Returns a ``dns.rdataset.Rdataset``. - """ - return dns.rdataset.from_rdata_list(self.ttl, list(self)) - - -def from_text_list( - name: Union[dns.name.Name, str], - ttl: int, - rdclass: Union[dns.rdataclass.RdataClass, str], - rdtype: Union[dns.rdatatype.RdataType, str], - text_rdatas: Collection[str], - idna_codec: Optional[dns.name.IDNACodec] = None, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - relativize_to: Optional[dns.name.Name] = None, -) -> RRset: - """Create an RRset with the specified name, TTL, class, and type, and with - the specified list of rdatas in text format. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder to use; if ``None``, the default IDNA 2003 - encoder/decoder is used. - - *origin*, a ``dns.name.Name`` (or ``None``), the - origin to use for relative names. - - *relativize*, a ``bool``. If true, name will be relativized. - - *relativize_to*, a ``dns.name.Name`` (or ``None``), the origin to use - when relativizing names. If not set, the *origin* value will be used. - - Returns a ``dns.rrset.RRset`` object. - """ - - if isinstance(name, str): - name = dns.name.from_text(name, None, idna_codec=idna_codec) - rdclass = dns.rdataclass.RdataClass.make(rdclass) - rdtype = dns.rdatatype.RdataType.make(rdtype) - r = RRset(name, rdclass, rdtype) - r.update_ttl(ttl) - for t in text_rdatas: - rd = dns.rdata.from_text( - r.rdclass, r.rdtype, t, origin, relativize, relativize_to, idna_codec - ) - r.add(rd) - return r - - -def from_text( - name: Union[dns.name.Name, str], - ttl: int, - rdclass: Union[dns.rdataclass.RdataClass, str], - rdtype: Union[dns.rdatatype.RdataType, str], - *text_rdatas: Any, -) -> RRset: - """Create an RRset with the specified name, TTL, class, and type and with - the specified rdatas in text format. - - Returns a ``dns.rrset.RRset`` object. - """ - - return from_text_list( - name, ttl, rdclass, rdtype, cast(Collection[str], text_rdatas) - ) - - -def from_rdata_list( - name: Union[dns.name.Name, str], - ttl: int, - rdatas: Collection[dns.rdata.Rdata], - idna_codec: Optional[dns.name.IDNACodec] = None, -) -> RRset: - """Create an RRset with the specified name and TTL, and with - the specified list of rdata objects. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder to use; if ``None``, the default IDNA 2003 - encoder/decoder is used. - - Returns a ``dns.rrset.RRset`` object. - - """ - - if isinstance(name, str): - name = dns.name.from_text(name, None, idna_codec=idna_codec) - - if len(rdatas) == 0: - raise ValueError("rdata list must not be empty") - r = None - for rd in rdatas: - if r is None: - r = RRset(name, rd.rdclass, rd.rdtype) - r.update_ttl(ttl) - r.add(rd) - assert r is not None - return r - - -def from_rdata(name: Union[dns.name.Name, str], ttl: int, *rdatas: Any) -> RRset: - """Create an RRset with the specified name and TTL, and with - the specified rdata objects. - - Returns a ``dns.rrset.RRset`` object. - """ - - return from_rdata_list(name, ttl, cast(Collection[dns.rdata.Rdata], rdatas)) diff --git a/venv/Lib/site-packages/dns/serial.py b/venv/Lib/site-packages/dns/serial.py deleted file mode 100644 index 3417299..0000000 --- a/venv/Lib/site-packages/dns/serial.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -"""Serial Number Arthimetic from RFC 1982""" - - -class Serial: - def __init__(self, value: int, bits: int = 32): - self.value = value % 2**bits - self.bits = bits - - def __repr__(self): - return f"dns.serial.Serial({self.value}, {self.bits})" - - def __eq__(self, other): - if isinstance(other, int): - other = Serial(other, self.bits) - elif not isinstance(other, Serial) or other.bits != self.bits: - return NotImplemented - return self.value == other.value - - def __ne__(self, other): - if isinstance(other, int): - other = Serial(other, self.bits) - elif not isinstance(other, Serial) or other.bits != self.bits: - return NotImplemented - return self.value != other.value - - def __lt__(self, other): - if isinstance(other, int): - other = Serial(other, self.bits) - elif not isinstance(other, Serial) or other.bits != self.bits: - return NotImplemented - if self.value < other.value and other.value - self.value < 2 ** (self.bits - 1): - return True - elif self.value > other.value and self.value - other.value > 2 ** ( - self.bits - 1 - ): - return True - else: - return False - - def __le__(self, other): - return self == other or self < other - - def __gt__(self, other): - if isinstance(other, int): - other = Serial(other, self.bits) - elif not isinstance(other, Serial) or other.bits != self.bits: - return NotImplemented - if self.value < other.value and other.value - self.value > 2 ** (self.bits - 1): - return True - elif self.value > other.value and self.value - other.value < 2 ** ( - self.bits - 1 - ): - return True - else: - return False - - def __ge__(self, other): - return self == other or self > other - - def __add__(self, other): - v = self.value - if isinstance(other, Serial): - delta = other.value - elif isinstance(other, int): - delta = other - else: - raise ValueError - if abs(delta) > (2 ** (self.bits - 1) - 1): - raise ValueError - v += delta - v = v % 2**self.bits - return Serial(v, self.bits) - - def __iadd__(self, other): - v = self.value - if isinstance(other, Serial): - delta = other.value - elif isinstance(other, int): - delta = other - else: - raise ValueError - if abs(delta) > (2 ** (self.bits - 1) - 1): - raise ValueError - v += delta - v = v % 2**self.bits - self.value = v - return self - - def __sub__(self, other): - v = self.value - if isinstance(other, Serial): - delta = other.value - elif isinstance(other, int): - delta = other - else: - raise ValueError - if abs(delta) > (2 ** (self.bits - 1) - 1): - raise ValueError - v -= delta - v = v % 2**self.bits - return Serial(v, self.bits) - - def __isub__(self, other): - v = self.value - if isinstance(other, Serial): - delta = other.value - elif isinstance(other, int): - delta = other - else: - raise ValueError - if abs(delta) > (2 ** (self.bits - 1) - 1): - raise ValueError - v -= delta - v = v % 2**self.bits - self.value = v - return self diff --git a/venv/Lib/site-packages/dns/set.py b/venv/Lib/site-packages/dns/set.py deleted file mode 100644 index ae8f0dd..0000000 --- a/venv/Lib/site-packages/dns/set.py +++ /dev/null @@ -1,308 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import itertools - - -class Set: - """A simple set class. - - This class was originally used to deal with python not having a set class, and - originally the class used lists in its implementation. The ordered and indexable - nature of RRsets and Rdatasets is unfortunately widely used in dnspython - applications, so for backwards compatibility sets continue to be a custom class, now - based on an ordered dictionary. - """ - - __slots__ = ["items"] - - def __init__(self, items=None): - """Initialize the set. - - *items*, an iterable or ``None``, the initial set of items. - """ - - self.items = dict() - if items is not None: - for item in items: - # This is safe for how we use set, but if other code - # subclasses it could be a legitimate issue. - self.add(item) # lgtm[py/init-calls-subclass] - - def __repr__(self): - return f"dns.set.Set({repr(list(self.items.keys()))})" # pragma: no cover - - def add(self, item): - """Add an item to the set.""" - - if item not in self.items: - self.items[item] = None - - def remove(self, item): - """Remove an item from the set.""" - - try: - del self.items[item] - except KeyError: - raise ValueError - - def discard(self, item): - """Remove an item from the set if present.""" - - self.items.pop(item, None) - - def pop(self): - """Remove an arbitrary item from the set.""" - (k, _) = self.items.popitem() - return k - - def _clone(self) -> "Set": - """Make a (shallow) copy of the set. - - There is a 'clone protocol' that subclasses of this class - should use. To make a copy, first call your super's _clone() - method, and use the object returned as the new instance. Then - make shallow copies of the attributes defined in the subclass. - - This protocol allows us to write the set algorithms that - return new instances (e.g. union) once, and keep using them in - subclasses. - """ - - if hasattr(self, "_clone_class"): - cls = self._clone_class # type: ignore - else: - cls = self.__class__ - obj = cls.__new__(cls) - obj.items = dict() - obj.items.update(self.items) - return obj - - def __copy__(self): - """Make a (shallow) copy of the set.""" - - return self._clone() - - def copy(self): - """Make a (shallow) copy of the set.""" - - return self._clone() - - def union_update(self, other): - """Update the set, adding any elements from other which are not - already in the set. - """ - - if not isinstance(other, Set): - raise ValueError("other must be a Set instance") - if self is other: # lgtm[py/comparison-using-is] - return - for item in other.items: - self.add(item) - - def intersection_update(self, other): - """Update the set, removing any elements from other which are not - in both sets. - """ - - if not isinstance(other, Set): - raise ValueError("other must be a Set instance") - if self is other: # lgtm[py/comparison-using-is] - return - # we make a copy of the list so that we can remove items from - # the list without breaking the iterator. - for item in list(self.items): - if item not in other.items: - del self.items[item] - - def difference_update(self, other): - """Update the set, removing any elements from other which are in - the set. - """ - - if not isinstance(other, Set): - raise ValueError("other must be a Set instance") - if self is other: # lgtm[py/comparison-using-is] - self.items.clear() - else: - for item in other.items: - self.discard(item) - - def symmetric_difference_update(self, other): - """Update the set, retaining only elements unique to both sets.""" - - if not isinstance(other, Set): - raise ValueError("other must be a Set instance") - if self is other: # lgtm[py/comparison-using-is] - self.items.clear() - else: - overlap = self.intersection(other) - self.union_update(other) - self.difference_update(overlap) - - def union(self, other): - """Return a new set which is the union of ``self`` and ``other``. - - Returns the same Set type as this set. - """ - - obj = self._clone() - obj.union_update(other) - return obj - - def intersection(self, other): - """Return a new set which is the intersection of ``self`` and - ``other``. - - Returns the same Set type as this set. - """ - - obj = self._clone() - obj.intersection_update(other) - return obj - - def difference(self, other): - """Return a new set which ``self`` - ``other``, i.e. the items - in ``self`` which are not also in ``other``. - - Returns the same Set type as this set. - """ - - obj = self._clone() - obj.difference_update(other) - return obj - - def symmetric_difference(self, other): - """Return a new set which (``self`` - ``other``) | (``other`` - - ``self), ie: the items in either ``self`` or ``other`` which - are not contained in their intersection. - - Returns the same Set type as this set. - """ - - obj = self._clone() - obj.symmetric_difference_update(other) - return obj - - def __or__(self, other): - return self.union(other) - - def __and__(self, other): - return self.intersection(other) - - def __add__(self, other): - return self.union(other) - - def __sub__(self, other): - return self.difference(other) - - def __xor__(self, other): - return self.symmetric_difference(other) - - def __ior__(self, other): - self.union_update(other) - return self - - def __iand__(self, other): - self.intersection_update(other) - return self - - def __iadd__(self, other): - self.union_update(other) - return self - - def __isub__(self, other): - self.difference_update(other) - return self - - def __ixor__(self, other): - self.symmetric_difference_update(other) - return self - - def update(self, other): - """Update the set, adding any elements from other which are not - already in the set. - - *other*, the collection of items with which to update the set, which - may be any iterable type. - """ - - for item in other: - self.add(item) - - def clear(self): - """Make the set empty.""" - self.items.clear() - - def __eq__(self, other): - return self.items == other.items - - def __ne__(self, other): - return not self.__eq__(other) - - def __len__(self): - return len(self.items) - - def __iter__(self): - return iter(self.items) - - def __getitem__(self, i): - if isinstance(i, slice): - return list(itertools.islice(self.items, i.start, i.stop, i.step)) - else: - return next(itertools.islice(self.items, i, i + 1)) - - def __delitem__(self, i): - if isinstance(i, slice): - for elt in list(self[i]): - del self.items[elt] - else: - del self.items[self[i]] - - def issubset(self, other): - """Is this set a subset of *other*? - - Returns a ``bool``. - """ - - if not isinstance(other, Set): - raise ValueError("other must be a Set instance") - for item in self.items: - if item not in other.items: - return False - return True - - def issuperset(self, other): - """Is this set a superset of *other*? - - Returns a ``bool``. - """ - - if not isinstance(other, Set): - raise ValueError("other must be a Set instance") - for item in other.items: - if item not in self.items: - return False - return True - - def isdisjoint(self, other): - if not isinstance(other, Set): - raise ValueError("other must be a Set instance") - for item in other.items: - if item in self.items: - return False - return True diff --git a/venv/Lib/site-packages/dns/tokenizer.py b/venv/Lib/site-packages/dns/tokenizer.py deleted file mode 100644 index ab205bc..0000000 --- a/venv/Lib/site-packages/dns/tokenizer.py +++ /dev/null @@ -1,708 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Tokenize DNS zone file format""" - -import io -import sys -from typing import Any, List, Optional, Tuple - -import dns.exception -import dns.name -import dns.ttl - -_DELIMITERS = {" ", "\t", "\n", ";", "(", ")", '"'} -_QUOTING_DELIMITERS = {'"'} - -EOF = 0 -EOL = 1 -WHITESPACE = 2 -IDENTIFIER = 3 -QUOTED_STRING = 4 -COMMENT = 5 -DELIMITER = 6 - - -class UngetBufferFull(dns.exception.DNSException): - """An attempt was made to unget a token when the unget buffer was full.""" - - -class Token: - """A DNS zone file format token. - - ttype: The token type - value: The token value - has_escape: Does the token value contain escapes? - """ - - def __init__( - self, - ttype: int, - value: Any = "", - has_escape: bool = False, - comment: Optional[str] = None, - ): - """Initialize a token instance.""" - - self.ttype = ttype - self.value = value - self.has_escape = has_escape - self.comment = comment - - def is_eof(self) -> bool: - return self.ttype == EOF - - def is_eol(self) -> bool: - return self.ttype == EOL - - def is_whitespace(self) -> bool: - return self.ttype == WHITESPACE - - def is_identifier(self) -> bool: - return self.ttype == IDENTIFIER - - def is_quoted_string(self) -> bool: - return self.ttype == QUOTED_STRING - - def is_comment(self) -> bool: - return self.ttype == COMMENT - - def is_delimiter(self) -> bool: # pragma: no cover (we don't return delimiters yet) - return self.ttype == DELIMITER - - def is_eol_or_eof(self) -> bool: - return self.ttype == EOL or self.ttype == EOF - - def __eq__(self, other): - if not isinstance(other, Token): - return False - return self.ttype == other.ttype and self.value == other.value - - def __ne__(self, other): - if not isinstance(other, Token): - return True - return self.ttype != other.ttype or self.value != other.value - - def __str__(self): - return '%d "%s"' % (self.ttype, self.value) - - def unescape(self) -> "Token": - if not self.has_escape: - return self - unescaped = "" - l = len(self.value) - i = 0 - while i < l: - c = self.value[i] - i += 1 - if c == "\\": - if i >= l: # pragma: no cover (can't happen via get()) - raise dns.exception.UnexpectedEnd - c = self.value[i] - i += 1 - if c.isdigit(): - if i >= l: - raise dns.exception.UnexpectedEnd - c2 = self.value[i] - i += 1 - if i >= l: - raise dns.exception.UnexpectedEnd - c3 = self.value[i] - i += 1 - if not (c2.isdigit() and c3.isdigit()): - raise dns.exception.SyntaxError - codepoint = int(c) * 100 + int(c2) * 10 + int(c3) - if codepoint > 255: - raise dns.exception.SyntaxError - c = chr(codepoint) - unescaped += c - return Token(self.ttype, unescaped) - - def unescape_to_bytes(self) -> "Token": - # We used to use unescape() for TXT-like records, but this - # caused problems as we'd process DNS escapes into Unicode code - # points instead of byte values, and then a to_text() of the - # processed data would not equal the original input. For - # example, \226 in the TXT record would have a to_text() of - # \195\162 because we applied UTF-8 encoding to Unicode code - # point 226. - # - # We now apply escapes while converting directly to bytes, - # avoiding this double encoding. - # - # This code also handles cases where the unicode input has - # non-ASCII code-points in it by converting it to UTF-8. TXT - # records aren't defined for Unicode, but this is the best we - # can do to preserve meaning. For example, - # - # foo\u200bbar - # - # (where \u200b is Unicode code point 0x200b) will be treated - # as if the input had been the UTF-8 encoding of that string, - # namely: - # - # foo\226\128\139bar - # - unescaped = b"" - l = len(self.value) - i = 0 - while i < l: - c = self.value[i] - i += 1 - if c == "\\": - if i >= l: # pragma: no cover (can't happen via get()) - raise dns.exception.UnexpectedEnd - c = self.value[i] - i += 1 - if c.isdigit(): - if i >= l: - raise dns.exception.UnexpectedEnd - c2 = self.value[i] - i += 1 - if i >= l: - raise dns.exception.UnexpectedEnd - c3 = self.value[i] - i += 1 - if not (c2.isdigit() and c3.isdigit()): - raise dns.exception.SyntaxError - codepoint = int(c) * 100 + int(c2) * 10 + int(c3) - if codepoint > 255: - raise dns.exception.SyntaxError - unescaped += b"%c" % (codepoint) - else: - # Note that as mentioned above, if c is a Unicode - # code point outside of the ASCII range, then this - # += is converting that code point to its UTF-8 - # encoding and appending multiple bytes to - # unescaped. - unescaped += c.encode() - else: - unescaped += c.encode() - return Token(self.ttype, bytes(unescaped)) - - -class Tokenizer: - """A DNS zone file format tokenizer. - - A token object is basically a (type, value) tuple. The valid - types are EOF, EOL, WHITESPACE, IDENTIFIER, QUOTED_STRING, - COMMENT, and DELIMITER. - - file: The file to tokenize - - ungotten_char: The most recently ungotten character, or None. - - ungotten_token: The most recently ungotten token, or None. - - multiline: The current multiline level. This value is increased - by one every time a '(' delimiter is read, and decreased by one every time - a ')' delimiter is read. - - quoting: This variable is true if the tokenizer is currently - reading a quoted string. - - eof: This variable is true if the tokenizer has encountered EOF. - - delimiters: The current delimiter dictionary. - - line_number: The current line number - - filename: A filename that will be returned by the where() method. - - idna_codec: A dns.name.IDNACodec, specifies the IDNA - encoder/decoder. If None, the default IDNA 2003 - encoder/decoder is used. - """ - - def __init__( - self, - f: Any = sys.stdin, - filename: Optional[str] = None, - idna_codec: Optional[dns.name.IDNACodec] = None, - ): - """Initialize a tokenizer instance. - - f: The file to tokenize. The default is sys.stdin. - This parameter may also be a string, in which case the tokenizer - will take its input from the contents of the string. - - filename: the name of the filename that the where() method - will return. - - idna_codec: A dns.name.IDNACodec, specifies the IDNA - encoder/decoder. If None, the default IDNA 2003 - encoder/decoder is used. - """ - - if isinstance(f, str): - f = io.StringIO(f) - if filename is None: - filename = "" - elif isinstance(f, bytes): - f = io.StringIO(f.decode()) - if filename is None: - filename = "" - else: - if filename is None: - if f is sys.stdin: - filename = "" - else: - filename = "" - self.file = f - self.ungotten_char: Optional[str] = None - self.ungotten_token: Optional[Token] = None - self.multiline = 0 - self.quoting = False - self.eof = False - self.delimiters = _DELIMITERS - self.line_number = 1 - assert filename is not None - self.filename = filename - if idna_codec is None: - self.idna_codec: dns.name.IDNACodec = dns.name.IDNA_2003 - else: - self.idna_codec = idna_codec - - def _get_char(self) -> str: - """Read a character from input.""" - - if self.ungotten_char is None: - if self.eof: - c = "" - else: - c = self.file.read(1) - if c == "": - self.eof = True - elif c == "\n": - self.line_number += 1 - else: - c = self.ungotten_char - self.ungotten_char = None - return c - - def where(self) -> Tuple[str, int]: - """Return the current location in the input. - - Returns a (string, int) tuple. The first item is the filename of - the input, the second is the current line number. - """ - - return (self.filename, self.line_number) - - def _unget_char(self, c: str) -> None: - """Unget a character. - - The unget buffer for characters is only one character large; it is - an error to try to unget a character when the unget buffer is not - empty. - - c: the character to unget - raises UngetBufferFull: there is already an ungotten char - """ - - if self.ungotten_char is not None: - # this should never happen! - raise UngetBufferFull # pragma: no cover - self.ungotten_char = c - - def skip_whitespace(self) -> int: - """Consume input until a non-whitespace character is encountered. - - The non-whitespace character is then ungotten, and the number of - whitespace characters consumed is returned. - - If the tokenizer is in multiline mode, then newlines are whitespace. - - Returns the number of characters skipped. - """ - - skipped = 0 - while True: - c = self._get_char() - if c != " " and c != "\t": - if (c != "\n") or not self.multiline: - self._unget_char(c) - return skipped - skipped += 1 - - def get(self, want_leading: bool = False, want_comment: bool = False) -> Token: - """Get the next token. - - want_leading: If True, return a WHITESPACE token if the - first character read is whitespace. The default is False. - - want_comment: If True, return a COMMENT token if the - first token read is a comment. The default is False. - - Raises dns.exception.UnexpectedEnd: input ended prematurely - - Raises dns.exception.SyntaxError: input was badly formed - - Returns a Token. - """ - - if self.ungotten_token is not None: - utoken = self.ungotten_token - self.ungotten_token = None - if utoken.is_whitespace(): - if want_leading: - return utoken - elif utoken.is_comment(): - if want_comment: - return utoken - else: - return utoken - skipped = self.skip_whitespace() - if want_leading and skipped > 0: - return Token(WHITESPACE, " ") - token = "" - ttype = IDENTIFIER - has_escape = False - while True: - c = self._get_char() - if c == "" or c in self.delimiters: - if c == "" and self.quoting: - raise dns.exception.UnexpectedEnd - if token == "" and ttype != QUOTED_STRING: - if c == "(": - self.multiline += 1 - self.skip_whitespace() - continue - elif c == ")": - if self.multiline <= 0: - raise dns.exception.SyntaxError - self.multiline -= 1 - self.skip_whitespace() - continue - elif c == '"': - if not self.quoting: - self.quoting = True - self.delimiters = _QUOTING_DELIMITERS - ttype = QUOTED_STRING - continue - else: - self.quoting = False - self.delimiters = _DELIMITERS - self.skip_whitespace() - continue - elif c == "\n": - return Token(EOL, "\n") - elif c == ";": - while 1: - c = self._get_char() - if c == "\n" or c == "": - break - token += c - if want_comment: - self._unget_char(c) - return Token(COMMENT, token) - elif c == "": - if self.multiline: - raise dns.exception.SyntaxError( - "unbalanced parentheses" - ) - return Token(EOF, comment=token) - elif self.multiline: - self.skip_whitespace() - token = "" - continue - else: - return Token(EOL, "\n", comment=token) - else: - # This code exists in case we ever want a - # delimiter to be returned. It never produces - # a token currently. - token = c - ttype = DELIMITER - else: - self._unget_char(c) - break - elif self.quoting and c == "\n": - raise dns.exception.SyntaxError("newline in quoted string") - elif c == "\\": - # - # It's an escape. Put it and the next character into - # the token; it will be checked later for goodness. - # - token += c - has_escape = True - c = self._get_char() - if c == "" or (c == "\n" and not self.quoting): - raise dns.exception.UnexpectedEnd - token += c - if token == "" and ttype != QUOTED_STRING: - if self.multiline: - raise dns.exception.SyntaxError("unbalanced parentheses") - ttype = EOF - return Token(ttype, token, has_escape) - - def unget(self, token: Token) -> None: - """Unget a token. - - The unget buffer for tokens is only one token large; it is - an error to try to unget a token when the unget buffer is not - empty. - - token: the token to unget - - Raises UngetBufferFull: there is already an ungotten token - """ - - if self.ungotten_token is not None: - raise UngetBufferFull - self.ungotten_token = token - - def next(self): - """Return the next item in an iteration. - - Returns a Token. - """ - - token = self.get() - if token.is_eof(): - raise StopIteration - return token - - __next__ = next - - def __iter__(self): - return self - - # Helpers - - def get_int(self, base: int = 10) -> int: - """Read the next token and interpret it as an unsigned integer. - - Raises dns.exception.SyntaxError if not an unsigned integer. - - Returns an int. - """ - - token = self.get().unescape() - if not token.is_identifier(): - raise dns.exception.SyntaxError("expecting an identifier") - if not token.value.isdigit(): - raise dns.exception.SyntaxError("expecting an integer") - return int(token.value, base) - - def get_uint8(self) -> int: - """Read the next token and interpret it as an 8-bit unsigned - integer. - - Raises dns.exception.SyntaxError if not an 8-bit unsigned integer. - - Returns an int. - """ - - value = self.get_int() - if value < 0 or value > 255: - raise dns.exception.SyntaxError( - "%d is not an unsigned 8-bit integer" % value - ) - return value - - def get_uint16(self, base: int = 10) -> int: - """Read the next token and interpret it as a 16-bit unsigned - integer. - - Raises dns.exception.SyntaxError if not a 16-bit unsigned integer. - - Returns an int. - """ - - value = self.get_int(base=base) - if value < 0 or value > 65535: - if base == 8: - raise dns.exception.SyntaxError( - f"{value:o} is not an octal unsigned 16-bit integer" - ) - else: - raise dns.exception.SyntaxError( - "%d is not an unsigned 16-bit integer" % value - ) - return value - - def get_uint32(self, base: int = 10) -> int: - """Read the next token and interpret it as a 32-bit unsigned - integer. - - Raises dns.exception.SyntaxError if not a 32-bit unsigned integer. - - Returns an int. - """ - - value = self.get_int(base=base) - if value < 0 or value > 4294967295: - raise dns.exception.SyntaxError( - "%d is not an unsigned 32-bit integer" % value - ) - return value - - def get_uint48(self, base: int = 10) -> int: - """Read the next token and interpret it as a 48-bit unsigned - integer. - - Raises dns.exception.SyntaxError if not a 48-bit unsigned integer. - - Returns an int. - """ - - value = self.get_int(base=base) - if value < 0 or value > 281474976710655: - raise dns.exception.SyntaxError( - "%d is not an unsigned 48-bit integer" % value - ) - return value - - def get_string(self, max_length: Optional[int] = None) -> str: - """Read the next token and interpret it as a string. - - Raises dns.exception.SyntaxError if not a string. - Raises dns.exception.SyntaxError if token value length - exceeds max_length (if specified). - - Returns a string. - """ - - token = self.get().unescape() - if not (token.is_identifier() or token.is_quoted_string()): - raise dns.exception.SyntaxError("expecting a string") - if max_length and len(token.value) > max_length: - raise dns.exception.SyntaxError("string too long") - return token.value - - def get_identifier(self) -> str: - """Read the next token, which should be an identifier. - - Raises dns.exception.SyntaxError if not an identifier. - - Returns a string. - """ - - token = self.get().unescape() - if not token.is_identifier(): - raise dns.exception.SyntaxError("expecting an identifier") - return token.value - - def get_remaining(self, max_tokens: Optional[int] = None) -> List[Token]: - """Return the remaining tokens on the line, until an EOL or EOF is seen. - - max_tokens: If not None, stop after this number of tokens. - - Returns a list of tokens. - """ - - tokens = [] - while True: - token = self.get() - if token.is_eol_or_eof(): - self.unget(token) - break - tokens.append(token) - if len(tokens) == max_tokens: - break - return tokens - - def concatenate_remaining_identifiers(self, allow_empty: bool = False) -> str: - """Read the remaining tokens on the line, which should be identifiers. - - Raises dns.exception.SyntaxError if there are no remaining tokens, - unless `allow_empty=True` is given. - - Raises dns.exception.SyntaxError if a token is seen that is not an - identifier. - - Returns a string containing a concatenation of the remaining - identifiers. - """ - s = "" - while True: - token = self.get().unescape() - if token.is_eol_or_eof(): - self.unget(token) - break - if not token.is_identifier(): - raise dns.exception.SyntaxError - s += token.value - if not (allow_empty or s): - raise dns.exception.SyntaxError("expecting another identifier") - return s - - def as_name( - self, - token: Token, - origin: Optional[dns.name.Name] = None, - relativize: bool = False, - relativize_to: Optional[dns.name.Name] = None, - ) -> dns.name.Name: - """Try to interpret the token as a DNS name. - - Raises dns.exception.SyntaxError if not a name. - - Returns a dns.name.Name. - """ - if not token.is_identifier(): - raise dns.exception.SyntaxError("expecting an identifier") - name = dns.name.from_text(token.value, origin, self.idna_codec) - return name.choose_relativity(relativize_to or origin, relativize) - - def get_name( - self, - origin: Optional[dns.name.Name] = None, - relativize: bool = False, - relativize_to: Optional[dns.name.Name] = None, - ) -> dns.name.Name: - """Read the next token and interpret it as a DNS name. - - Raises dns.exception.SyntaxError if not a name. - - Returns a dns.name.Name. - """ - - token = self.get() - return self.as_name(token, origin, relativize, relativize_to) - - def get_eol_as_token(self) -> Token: - """Read the next token and raise an exception if it isn't EOL or - EOF. - - Returns a string. - """ - - token = self.get() - if not token.is_eol_or_eof(): - raise dns.exception.SyntaxError( - 'expected EOL or EOF, got %d "%s"' % (token.ttype, token.value) - ) - return token - - def get_eol(self) -> str: - return self.get_eol_as_token().value - - def get_ttl(self) -> int: - """Read the next token and interpret it as a DNS TTL. - - Raises dns.exception.SyntaxError or dns.ttl.BadTTL if not an - identifier or badly formed. - - Returns an int. - """ - - token = self.get().unescape() - if not token.is_identifier(): - raise dns.exception.SyntaxError("expecting an identifier") - return dns.ttl.from_text(token.value) diff --git a/venv/Lib/site-packages/dns/transaction.py b/venv/Lib/site-packages/dns/transaction.py deleted file mode 100644 index aa2e116..0000000 --- a/venv/Lib/site-packages/dns/transaction.py +++ /dev/null @@ -1,649 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import collections -from typing import Any, Callable, Iterator, List, Optional, Tuple, Union - -import dns.exception -import dns.name -import dns.node -import dns.rdataclass -import dns.rdataset -import dns.rdatatype -import dns.rrset -import dns.serial -import dns.ttl - - -class TransactionManager: - def reader(self) -> "Transaction": - """Begin a read-only transaction.""" - raise NotImplementedError # pragma: no cover - - def writer(self, replacement: bool = False) -> "Transaction": - """Begin a writable transaction. - - *replacement*, a ``bool``. If `True`, the content of the - transaction completely replaces any prior content. If False, - the default, then the content of the transaction updates the - existing content. - """ - raise NotImplementedError # pragma: no cover - - def origin_information( - self, - ) -> Tuple[Optional[dns.name.Name], bool, Optional[dns.name.Name]]: - """Returns a tuple - - (absolute_origin, relativize, effective_origin) - - giving the absolute name of the default origin for any - relative domain names, the "effective origin", and whether - names should be relativized. The "effective origin" is the - absolute origin if relativize is False, and the empty name if - relativize is true. (The effective origin is provided even - though it can be computed from the absolute_origin and - relativize setting because it avoids a lot of code - duplication.) - - If the returned names are `None`, then no origin information is - available. - - This information is used by code working with transactions to - allow it to coordinate relativization. The transaction code - itself takes what it gets (i.e. does not change name - relativity). - - """ - raise NotImplementedError # pragma: no cover - - def get_class(self) -> dns.rdataclass.RdataClass: - """The class of the transaction manager.""" - raise NotImplementedError # pragma: no cover - - def from_wire_origin(self) -> Optional[dns.name.Name]: - """Origin to use in from_wire() calls.""" - (absolute_origin, relativize, _) = self.origin_information() - if relativize: - return absolute_origin - else: - return None - - -class DeleteNotExact(dns.exception.DNSException): - """Existing data did not match data specified by an exact delete.""" - - -class ReadOnly(dns.exception.DNSException): - """Tried to write to a read-only transaction.""" - - -class AlreadyEnded(dns.exception.DNSException): - """Tried to use an already-ended transaction.""" - - -def _ensure_immutable_rdataset(rdataset): - if rdataset is None or isinstance(rdataset, dns.rdataset.ImmutableRdataset): - return rdataset - return dns.rdataset.ImmutableRdataset(rdataset) - - -def _ensure_immutable_node(node): - if node is None or node.is_immutable(): - return node - return dns.node.ImmutableNode(node) - - -CheckPutRdatasetType = Callable[ - ["Transaction", dns.name.Name, dns.rdataset.Rdataset], None -] -CheckDeleteRdatasetType = Callable[ - ["Transaction", dns.name.Name, dns.rdatatype.RdataType, dns.rdatatype.RdataType], - None, -] -CheckDeleteNameType = Callable[["Transaction", dns.name.Name], None] - - -class Transaction: - def __init__( - self, - manager: TransactionManager, - replacement: bool = False, - read_only: bool = False, - ): - self.manager = manager - self.replacement = replacement - self.read_only = read_only - self._ended = False - self._check_put_rdataset: List[CheckPutRdatasetType] = [] - self._check_delete_rdataset: List[CheckDeleteRdatasetType] = [] - self._check_delete_name: List[CheckDeleteNameType] = [] - - # - # This is the high level API - # - # Note that we currently use non-immutable types in the return type signature to - # avoid covariance problems, e.g. if the caller has a List[Rdataset], mypy will be - # unhappy if we return an ImmutableRdataset. - - def get( - self, - name: Optional[Union[dns.name.Name, str]], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - ) -> dns.rdataset.Rdataset: - """Return the rdataset associated with *name*, *rdtype*, and *covers*, - or `None` if not found. - - Note that the returned rdataset is immutable. - """ - self._check_ended() - if isinstance(name, str): - name = dns.name.from_text(name, None) - rdtype = dns.rdatatype.RdataType.make(rdtype) - covers = dns.rdatatype.RdataType.make(covers) - rdataset = self._get_rdataset(name, rdtype, covers) - return _ensure_immutable_rdataset(rdataset) - - def get_node(self, name: dns.name.Name) -> Optional[dns.node.Node]: - """Return the node at *name*, if any. - - Returns an immutable node or ``None``. - """ - return _ensure_immutable_node(self._get_node(name)) - - def _check_read_only(self) -> None: - if self.read_only: - raise ReadOnly - - def add(self, *args: Any) -> None: - """Add records. - - The arguments may be: - - - rrset - - - name, rdataset... - - - name, ttl, rdata... - """ - self._check_ended() - self._check_read_only() - self._add(False, args) - - def replace(self, *args: Any) -> None: - """Replace the existing rdataset at the name with the specified - rdataset, or add the specified rdataset if there was no existing - rdataset. - - The arguments may be: - - - rrset - - - name, rdataset... - - - name, ttl, rdata... - - Note that if you want to replace the entire node, you should do - a delete of the name followed by one or more calls to add() or - replace(). - """ - self._check_ended() - self._check_read_only() - self._add(True, args) - - def delete(self, *args: Any) -> None: - """Delete records. - - It is not an error if some of the records are not in the existing - set. - - The arguments may be: - - - rrset - - - name - - - name, rdatatype, [covers] - - - name, rdataset... - - - name, rdata... - """ - self._check_ended() - self._check_read_only() - self._delete(False, args) - - def delete_exact(self, *args: Any) -> None: - """Delete records. - - The arguments may be: - - - rrset - - - name - - - name, rdatatype, [covers] - - - name, rdataset... - - - name, rdata... - - Raises dns.transaction.DeleteNotExact if some of the records - are not in the existing set. - - """ - self._check_ended() - self._check_read_only() - self._delete(True, args) - - def name_exists(self, name: Union[dns.name.Name, str]) -> bool: - """Does the specified name exist?""" - self._check_ended() - if isinstance(name, str): - name = dns.name.from_text(name, None) - return self._name_exists(name) - - def update_serial( - self, - value: int = 1, - relative: bool = True, - name: dns.name.Name = dns.name.empty, - ) -> None: - """Update the serial number. - - *value*, an `int`, is an increment if *relative* is `True`, or the - actual value to set if *relative* is `False`. - - Raises `KeyError` if there is no SOA rdataset at *name*. - - Raises `ValueError` if *value* is negative or if the increment is - so large that it would cause the new serial to be less than the - prior value. - """ - self._check_ended() - if value < 0: - raise ValueError("negative update_serial() value") - if isinstance(name, str): - name = dns.name.from_text(name, None) - rdataset = self._get_rdataset(name, dns.rdatatype.SOA, dns.rdatatype.NONE) - if rdataset is None or len(rdataset) == 0: - raise KeyError - if relative: - serial = dns.serial.Serial(rdataset[0].serial) + value - else: - serial = dns.serial.Serial(value) - serial = serial.value # convert back to int - if serial == 0: - serial = 1 - rdata = rdataset[0].replace(serial=serial) - new_rdataset = dns.rdataset.from_rdata(rdataset.ttl, rdata) - self.replace(name, new_rdataset) - - def __iter__(self): - self._check_ended() - return self._iterate_rdatasets() - - def changed(self) -> bool: - """Has this transaction changed anything? - - For read-only transactions, the result is always `False`. - - For writable transactions, the result is `True` if at some time - during the life of the transaction, the content was changed. - """ - self._check_ended() - return self._changed() - - def commit(self) -> None: - """Commit the transaction. - - Normally transactions are used as context managers and commit - or rollback automatically, but it may be done explicitly if needed. - A ``dns.transaction.Ended`` exception will be raised if you try - to use a transaction after it has been committed or rolled back. - - Raises an exception if the commit fails (in which case the transaction - is also rolled back. - """ - self._end(True) - - def rollback(self) -> None: - """Rollback the transaction. - - Normally transactions are used as context managers and commit - or rollback automatically, but it may be done explicitly if needed. - A ``dns.transaction.AlreadyEnded`` exception will be raised if you try - to use a transaction after it has been committed or rolled back. - - Rollback cannot otherwise fail. - """ - self._end(False) - - def check_put_rdataset(self, check: CheckPutRdatasetType) -> None: - """Call *check* before putting (storing) an rdataset. - - The function is called with the transaction, the name, and the rdataset. - - The check function may safely make non-mutating transaction method - calls, but behavior is undefined if mutating transaction methods are - called. The check function should raise an exception if it objects to - the put, and otherwise should return ``None``. - """ - self._check_put_rdataset.append(check) - - def check_delete_rdataset(self, check: CheckDeleteRdatasetType) -> None: - """Call *check* before deleting an rdataset. - - The function is called with the transaction, the name, the rdatatype, - and the covered rdatatype. - - The check function may safely make non-mutating transaction method - calls, but behavior is undefined if mutating transaction methods are - called. The check function should raise an exception if it objects to - the put, and otherwise should return ``None``. - """ - self._check_delete_rdataset.append(check) - - def check_delete_name(self, check: CheckDeleteNameType) -> None: - """Call *check* before putting (storing) an rdataset. - - The function is called with the transaction and the name. - - The check function may safely make non-mutating transaction method - calls, but behavior is undefined if mutating transaction methods are - called. The check function should raise an exception if it objects to - the put, and otherwise should return ``None``. - """ - self._check_delete_name.append(check) - - def iterate_rdatasets( - self, - ) -> Iterator[Tuple[dns.name.Name, dns.rdataset.Rdataset]]: - """Iterate all the rdatasets in the transaction, returning - (`dns.name.Name`, `dns.rdataset.Rdataset`) tuples. - - Note that as is usual with python iterators, adding or removing items - while iterating will invalidate the iterator and may raise `RuntimeError` - or fail to iterate over all entries.""" - self._check_ended() - return self._iterate_rdatasets() - - def iterate_names(self) -> Iterator[dns.name.Name]: - """Iterate all the names in the transaction. - - Note that as is usual with python iterators, adding or removing names - while iterating will invalidate the iterator and may raise `RuntimeError` - or fail to iterate over all entries.""" - self._check_ended() - return self._iterate_names() - - # - # Helper methods - # - - def _raise_if_not_empty(self, method, args): - if len(args) != 0: - raise TypeError(f"extra parameters to {method}") - - def _rdataset_from_args(self, method, deleting, args): - try: - arg = args.popleft() - if isinstance(arg, dns.rrset.RRset): - rdataset = arg.to_rdataset() - elif isinstance(arg, dns.rdataset.Rdataset): - rdataset = arg - else: - if deleting: - ttl = 0 - else: - if isinstance(arg, int): - ttl = arg - if ttl > dns.ttl.MAX_TTL: - raise ValueError(f"{method}: TTL value too big") - else: - raise TypeError(f"{method}: expected a TTL") - arg = args.popleft() - if isinstance(arg, dns.rdata.Rdata): - rdataset = dns.rdataset.from_rdata(ttl, arg) - else: - raise TypeError(f"{method}: expected an Rdata") - return rdataset - except IndexError: - if deleting: - return None - else: - # reraise - raise TypeError(f"{method}: expected more arguments") - - def _add(self, replace, args): - try: - args = collections.deque(args) - if replace: - method = "replace()" - else: - method = "add()" - arg = args.popleft() - if isinstance(arg, str): - arg = dns.name.from_text(arg, None) - if isinstance(arg, dns.name.Name): - name = arg - rdataset = self._rdataset_from_args(method, False, args) - elif isinstance(arg, dns.rrset.RRset): - rrset = arg - name = rrset.name - # rrsets are also rdatasets, but they don't print the - # same and can't be stored in nodes, so convert. - rdataset = rrset.to_rdataset() - else: - raise TypeError( - f"{method} requires a name or RRset as the first argument" - ) - if rdataset.rdclass != self.manager.get_class(): - raise ValueError(f"{method} has objects of wrong RdataClass") - if rdataset.rdtype == dns.rdatatype.SOA: - (_, _, origin) = self._origin_information() - if name != origin: - raise ValueError(f"{method} has non-origin SOA") - self._raise_if_not_empty(method, args) - if not replace: - existing = self._get_rdataset(name, rdataset.rdtype, rdataset.covers) - if existing is not None: - if isinstance(existing, dns.rdataset.ImmutableRdataset): - trds = dns.rdataset.Rdataset( - existing.rdclass, existing.rdtype, existing.covers - ) - trds.update(existing) - existing = trds - rdataset = existing.union(rdataset) - self._checked_put_rdataset(name, rdataset) - except IndexError: - raise TypeError(f"not enough parameters to {method}") - - def _delete(self, exact, args): - try: - args = collections.deque(args) - if exact: - method = "delete_exact()" - else: - method = "delete()" - arg = args.popleft() - if isinstance(arg, str): - arg = dns.name.from_text(arg, None) - if isinstance(arg, dns.name.Name): - name = arg - if len(args) > 0 and ( - isinstance(args[0], int) or isinstance(args[0], str) - ): - # deleting by type and (optionally) covers - rdtype = dns.rdatatype.RdataType.make(args.popleft()) - if len(args) > 0: - covers = dns.rdatatype.RdataType.make(args.popleft()) - else: - covers = dns.rdatatype.NONE - self._raise_if_not_empty(method, args) - existing = self._get_rdataset(name, rdtype, covers) - if existing is None: - if exact: - raise DeleteNotExact(f"{method}: missing rdataset") - else: - self._checked_delete_rdataset(name, rdtype, covers) - return - else: - rdataset = self._rdataset_from_args(method, True, args) - elif isinstance(arg, dns.rrset.RRset): - rdataset = arg # rrsets are also rdatasets - name = rdataset.name - else: - raise TypeError( - f"{method} requires a name or RRset as the first argument" - ) - self._raise_if_not_empty(method, args) - if rdataset: - if rdataset.rdclass != self.manager.get_class(): - raise ValueError(f"{method} has objects of wrong RdataClass") - existing = self._get_rdataset(name, rdataset.rdtype, rdataset.covers) - if existing is not None: - if exact: - intersection = existing.intersection(rdataset) - if intersection != rdataset: - raise DeleteNotExact(f"{method}: missing rdatas") - rdataset = existing.difference(rdataset) - if len(rdataset) == 0: - self._checked_delete_rdataset( - name, rdataset.rdtype, rdataset.covers - ) - else: - self._checked_put_rdataset(name, rdataset) - elif exact: - raise DeleteNotExact(f"{method}: missing rdataset") - else: - if exact and not self._name_exists(name): - raise DeleteNotExact(f"{method}: name not known") - self._checked_delete_name(name) - except IndexError: - raise TypeError(f"not enough parameters to {method}") - - def _check_ended(self): - if self._ended: - raise AlreadyEnded - - def _end(self, commit): - self._check_ended() - try: - self._end_transaction(commit) - finally: - self._ended = True - - def _checked_put_rdataset(self, name, rdataset): - for check in self._check_put_rdataset: - check(self, name, rdataset) - self._put_rdataset(name, rdataset) - - def _checked_delete_rdataset(self, name, rdtype, covers): - for check in self._check_delete_rdataset: - check(self, name, rdtype, covers) - self._delete_rdataset(name, rdtype, covers) - - def _checked_delete_name(self, name): - for check in self._check_delete_name: - check(self, name) - self._delete_name(name) - - # - # Transactions are context managers. - # - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - if not self._ended: - if exc_type is None: - self.commit() - else: - self.rollback() - return False - - # - # This is the low level API, which must be implemented by subclasses - # of Transaction. - # - - def _get_rdataset(self, name, rdtype, covers): - """Return the rdataset associated with *name*, *rdtype*, and *covers*, - or `None` if not found. - """ - raise NotImplementedError # pragma: no cover - - def _put_rdataset(self, name, rdataset): - """Store the rdataset.""" - raise NotImplementedError # pragma: no cover - - def _delete_name(self, name): - """Delete all data associated with *name*. - - It is not an error if the name does not exist. - """ - raise NotImplementedError # pragma: no cover - - def _delete_rdataset(self, name, rdtype, covers): - """Delete all data associated with *name*, *rdtype*, and *covers*. - - It is not an error if the rdataset does not exist. - """ - raise NotImplementedError # pragma: no cover - - def _name_exists(self, name): - """Does name exist? - - Returns a bool. - """ - raise NotImplementedError # pragma: no cover - - def _changed(self): - """Has this transaction changed anything?""" - raise NotImplementedError # pragma: no cover - - def _end_transaction(self, commit): - """End the transaction. - - *commit*, a bool. If ``True``, commit the transaction, otherwise - roll it back. - - If committing and the commit fails, then roll back and raise an - exception. - """ - raise NotImplementedError # pragma: no cover - - def _set_origin(self, origin): - """Set the origin. - - This method is called when reading a possibly relativized - source, and an origin setting operation occurs (e.g. $ORIGIN - in a zone file). - """ - raise NotImplementedError # pragma: no cover - - def _iterate_rdatasets(self): - """Return an iterator that yields (name, rdataset) tuples.""" - raise NotImplementedError # pragma: no cover - - def _iterate_names(self): - """Return an iterator that yields a name.""" - raise NotImplementedError # pragma: no cover - - def _get_node(self, name): - """Return the node at *name*, if any. - - Returns a node or ``None``. - """ - raise NotImplementedError # pragma: no cover - - # - # Low-level API with a default implementation, in case a subclass needs - # to override. - # - - def _origin_information(self): - # This is only used by _add() - return self.manager.origin_information() diff --git a/venv/Lib/site-packages/dns/tsig.py b/venv/Lib/site-packages/dns/tsig.py deleted file mode 100644 index 780852e..0000000 --- a/venv/Lib/site-packages/dns/tsig.py +++ /dev/null @@ -1,352 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS TSIG support.""" - -import base64 -import hashlib -import hmac -import struct - -import dns.exception -import dns.name -import dns.rcode -import dns.rdataclass - - -class BadTime(dns.exception.DNSException): - """The current time is not within the TSIG's validity time.""" - - -class BadSignature(dns.exception.DNSException): - """The TSIG signature fails to verify.""" - - -class BadKey(dns.exception.DNSException): - """The TSIG record owner name does not match the key.""" - - -class BadAlgorithm(dns.exception.DNSException): - """The TSIG algorithm does not match the key.""" - - -class PeerError(dns.exception.DNSException): - """Base class for all TSIG errors generated by the remote peer""" - - -class PeerBadKey(PeerError): - """The peer didn't know the key we used""" - - -class PeerBadSignature(PeerError): - """The peer didn't like the signature we sent""" - - -class PeerBadTime(PeerError): - """The peer didn't like the time we sent""" - - -class PeerBadTruncation(PeerError): - """The peer didn't like amount of truncation in the TSIG we sent""" - - -# TSIG Algorithms - -HMAC_MD5 = dns.name.from_text("HMAC-MD5.SIG-ALG.REG.INT") -HMAC_SHA1 = dns.name.from_text("hmac-sha1") -HMAC_SHA224 = dns.name.from_text("hmac-sha224") -HMAC_SHA256 = dns.name.from_text("hmac-sha256") -HMAC_SHA256_128 = dns.name.from_text("hmac-sha256-128") -HMAC_SHA384 = dns.name.from_text("hmac-sha384") -HMAC_SHA384_192 = dns.name.from_text("hmac-sha384-192") -HMAC_SHA512 = dns.name.from_text("hmac-sha512") -HMAC_SHA512_256 = dns.name.from_text("hmac-sha512-256") -GSS_TSIG = dns.name.from_text("gss-tsig") - -default_algorithm = HMAC_SHA256 - -mac_sizes = { - HMAC_SHA1: 20, - HMAC_SHA224: 28, - HMAC_SHA256: 32, - HMAC_SHA256_128: 16, - HMAC_SHA384: 48, - HMAC_SHA384_192: 24, - HMAC_SHA512: 64, - HMAC_SHA512_256: 32, - HMAC_MD5: 16, - GSS_TSIG: 128, # This is what we assume to be the worst case! -} - - -class GSSTSig: - """ - GSS-TSIG TSIG implementation. This uses the GSS-API context established - in the TKEY message handshake to sign messages using GSS-API message - integrity codes, per the RFC. - - In order to avoid a direct GSSAPI dependency, the keyring holds a ref - to the GSSAPI object required, rather than the key itself. - """ - - def __init__(self, gssapi_context): - self.gssapi_context = gssapi_context - self.data = b"" - self.name = "gss-tsig" - - def update(self, data): - self.data += data - - def sign(self): - # defer to the GSSAPI function to sign - return self.gssapi_context.get_signature(self.data) - - def verify(self, expected): - try: - # defer to the GSSAPI function to verify - return self.gssapi_context.verify_signature(self.data, expected) - except Exception: - # note the usage of a bare exception - raise BadSignature - - -class GSSTSigAdapter: - def __init__(self, keyring): - self.keyring = keyring - - def __call__(self, message, keyname): - if keyname in self.keyring: - key = self.keyring[keyname] - if isinstance(key, Key) and key.algorithm == GSS_TSIG: - if message: - GSSTSigAdapter.parse_tkey_and_step(key, message, keyname) - return key - else: - return None - - @classmethod - def parse_tkey_and_step(cls, key, message, keyname): - # if the message is a TKEY type, absorb the key material - # into the context using step(); this is used to allow the - # client to complete the GSSAPI negotiation before attempting - # to verify the signed response to a TKEY message exchange - try: - rrset = message.find_rrset( - message.answer, keyname, dns.rdataclass.ANY, dns.rdatatype.TKEY - ) - if rrset: - token = rrset[0].key - gssapi_context = key.secret - return gssapi_context.step(token) - except KeyError: - pass - - -class HMACTSig: - """ - HMAC TSIG implementation. This uses the HMAC python module to handle the - sign/verify operations. - """ - - _hashes = { - HMAC_SHA1: hashlib.sha1, - HMAC_SHA224: hashlib.sha224, - HMAC_SHA256: hashlib.sha256, - HMAC_SHA256_128: (hashlib.sha256, 128), - HMAC_SHA384: hashlib.sha384, - HMAC_SHA384_192: (hashlib.sha384, 192), - HMAC_SHA512: hashlib.sha512, - HMAC_SHA512_256: (hashlib.sha512, 256), - HMAC_MD5: hashlib.md5, - } - - def __init__(self, key, algorithm): - try: - hashinfo = self._hashes[algorithm] - except KeyError: - raise NotImplementedError(f"TSIG algorithm {algorithm} is not supported") - - # create the HMAC context - if isinstance(hashinfo, tuple): - self.hmac_context = hmac.new(key, digestmod=hashinfo[0]) - self.size = hashinfo[1] - else: - self.hmac_context = hmac.new(key, digestmod=hashinfo) - self.size = None - self.name = self.hmac_context.name - if self.size: - self.name += f"-{self.size}" - - def update(self, data): - return self.hmac_context.update(data) - - def sign(self): - # defer to the HMAC digest() function for that digestmod - digest = self.hmac_context.digest() - if self.size: - digest = digest[: (self.size // 8)] - return digest - - def verify(self, expected): - # re-digest and compare the results - mac = self.sign() - if not hmac.compare_digest(mac, expected): - raise BadSignature - - -def _digest(wire, key, rdata, time=None, request_mac=None, ctx=None, multi=None): - """Return a context containing the TSIG rdata for the input parameters - @rtype: dns.tsig.HMACTSig or dns.tsig.GSSTSig object - @raises ValueError: I{other_data} is too long - @raises NotImplementedError: I{algorithm} is not supported - """ - - first = not (ctx and multi) - if first: - ctx = get_context(key) - if request_mac: - ctx.update(struct.pack("!H", len(request_mac))) - ctx.update(request_mac) - ctx.update(struct.pack("!H", rdata.original_id)) - ctx.update(wire[2:]) - if first: - ctx.update(key.name.to_digestable()) - ctx.update(struct.pack("!H", dns.rdataclass.ANY)) - ctx.update(struct.pack("!I", 0)) - if time is None: - time = rdata.time_signed - upper_time = (time >> 32) & 0xFFFF - lower_time = time & 0xFFFFFFFF - time_encoded = struct.pack("!HIH", upper_time, lower_time, rdata.fudge) - other_len = len(rdata.other) - if other_len > 65535: - raise ValueError("TSIG Other Data is > 65535 bytes") - if first: - ctx.update(key.algorithm.to_digestable() + time_encoded) - ctx.update(struct.pack("!HH", rdata.error, other_len) + rdata.other) - else: - ctx.update(time_encoded) - return ctx - - -def _maybe_start_digest(key, mac, multi): - """If this is the first message in a multi-message sequence, - start a new context. - @rtype: dns.tsig.HMACTSig or dns.tsig.GSSTSig object - """ - if multi: - ctx = get_context(key) - ctx.update(struct.pack("!H", len(mac))) - ctx.update(mac) - return ctx - else: - return None - - -def sign(wire, key, rdata, time=None, request_mac=None, ctx=None, multi=False): - """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata - for the input parameters, the HMAC MAC calculated by applying the - TSIG signature algorithm, and the TSIG digest context. - @rtype: (string, dns.tsig.HMACTSig or dns.tsig.GSSTSig object) - @raises ValueError: I{other_data} is too long - @raises NotImplementedError: I{algorithm} is not supported - """ - - ctx = _digest(wire, key, rdata, time, request_mac, ctx, multi) - mac = ctx.sign() - tsig = rdata.replace(time_signed=time, mac=mac) - - return (tsig, _maybe_start_digest(key, mac, multi)) - - -def validate( - wire, key, owner, rdata, now, request_mac, tsig_start, ctx=None, multi=False -): - """Validate the specified TSIG rdata against the other input parameters. - - @raises FormError: The TSIG is badly formed. - @raises BadTime: There is too much time skew between the client and the - server. - @raises BadSignature: The TSIG signature did not validate - @rtype: dns.tsig.HMACTSig or dns.tsig.GSSTSig object""" - - (adcount,) = struct.unpack("!H", wire[10:12]) - if adcount == 0: - raise dns.exception.FormError - adcount -= 1 - new_wire = wire[0:10] + struct.pack("!H", adcount) + wire[12:tsig_start] - if rdata.error != 0: - if rdata.error == dns.rcode.BADSIG: - raise PeerBadSignature - elif rdata.error == dns.rcode.BADKEY: - raise PeerBadKey - elif rdata.error == dns.rcode.BADTIME: - raise PeerBadTime - elif rdata.error == dns.rcode.BADTRUNC: - raise PeerBadTruncation - else: - raise PeerError("unknown TSIG error code %d" % rdata.error) - if abs(rdata.time_signed - now) > rdata.fudge: - raise BadTime - if key.name != owner: - raise BadKey - if key.algorithm != rdata.algorithm: - raise BadAlgorithm - ctx = _digest(new_wire, key, rdata, None, request_mac, ctx, multi) - ctx.verify(rdata.mac) - return _maybe_start_digest(key, rdata.mac, multi) - - -def get_context(key): - """Returns an HMAC context for the specified key. - - @rtype: HMAC context - @raises NotImplementedError: I{algorithm} is not supported - """ - - if key.algorithm == GSS_TSIG: - return GSSTSig(key.secret) - else: - return HMACTSig(key.secret, key.algorithm) - - -class Key: - def __init__(self, name, secret, algorithm=default_algorithm): - if isinstance(name, str): - name = dns.name.from_text(name) - self.name = name - if isinstance(secret, str): - secret = base64.decodebytes(secret.encode()) - self.secret = secret - if isinstance(algorithm, str): - algorithm = dns.name.from_text(algorithm) - self.algorithm = algorithm - - def __eq__(self, other): - return ( - isinstance(other, Key) - and self.name == other.name - and self.secret == other.secret - and self.algorithm == other.algorithm - ) - - def __repr__(self): - r = f" Dict[dns.name.Name, dns.tsig.Key]: - """Convert a dictionary containing (textual DNS name, base64 secret) - pairs into a binary keyring which has (dns.name.Name, bytes) pairs, or - a dictionary containing (textual DNS name, (algorithm, base64 secret)) - pairs into a binary keyring which has (dns.name.Name, dns.tsig.Key) pairs. - @rtype: dict""" - - keyring = {} - for name, value in textring.items(): - kname = dns.name.from_text(name) - if isinstance(value, str): - keyring[kname] = dns.tsig.Key(kname, value).secret - else: - (algorithm, secret) = value - keyring[kname] = dns.tsig.Key(kname, secret, algorithm) - return keyring - - -def to_text(keyring: Dict[dns.name.Name, Any]) -> Dict[str, Any]: - """Convert a dictionary containing (dns.name.Name, dns.tsig.Key) pairs - into a text keyring which has (textual DNS name, (textual algorithm, - base64 secret)) pairs, or a dictionary containing (dns.name.Name, bytes) - pairs into a text keyring which has (textual DNS name, base64 secret) pairs. - @rtype: dict""" - - textring = {} - - def b64encode(secret): - return base64.encodebytes(secret).decode().rstrip() - - for name, key in keyring.items(): - tname = name.to_text() - if isinstance(key, bytes): - textring[tname] = b64encode(key) - else: - if isinstance(key.secret, bytes): - text_secret = b64encode(key.secret) - else: - text_secret = str(key.secret) - - textring[tname] = (key.algorithm.to_text(), text_secret) - return textring diff --git a/venv/Lib/site-packages/dns/ttl.py b/venv/Lib/site-packages/dns/ttl.py deleted file mode 100644 index b9a99fe..0000000 --- a/venv/Lib/site-packages/dns/ttl.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS TTL conversion.""" - -from typing import Union - -import dns.exception - -# Technically TTLs are supposed to be between 0 and 2**31 - 1, with values -# greater than that interpreted as 0, but we do not impose this policy here -# as values > 2**31 - 1 occur in real world data. -# -# We leave it to applications to impose tighter bounds if desired. -MAX_TTL = 2**32 - 1 - - -class BadTTL(dns.exception.SyntaxError): - """DNS TTL value is not well-formed.""" - - -def from_text(text: str) -> int: - """Convert the text form of a TTL to an integer. - - The BIND 8 units syntax for TTLs (e.g. '1w6d4h3m10s') is supported. - - *text*, a ``str``, the textual TTL. - - Raises ``dns.ttl.BadTTL`` if the TTL is not well-formed. - - Returns an ``int``. - """ - - if text.isdigit(): - total = int(text) - elif len(text) == 0: - raise BadTTL - else: - total = 0 - current = 0 - need_digit = True - for c in text: - if c.isdigit(): - current *= 10 - current += int(c) - need_digit = False - else: - if need_digit: - raise BadTTL - c = c.lower() - if c == "w": - total += current * 604800 - elif c == "d": - total += current * 86400 - elif c == "h": - total += current * 3600 - elif c == "m": - total += current * 60 - elif c == "s": - total += current - else: - raise BadTTL(f"unknown unit '{c}'") - current = 0 - need_digit = True - if not current == 0: - raise BadTTL("trailing integer") - if total < 0 or total > MAX_TTL: - raise BadTTL("TTL should be between 0 and 2**32 - 1 (inclusive)") - return total - - -def make(value: Union[int, str]) -> int: - if isinstance(value, int): - return value - elif isinstance(value, str): - return dns.ttl.from_text(value) - else: - raise ValueError("cannot convert value to TTL") diff --git a/venv/Lib/site-packages/dns/update.py b/venv/Lib/site-packages/dns/update.py deleted file mode 100644 index bf1157a..0000000 --- a/venv/Lib/site-packages/dns/update.py +++ /dev/null @@ -1,386 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Dynamic Update Support""" - -from typing import Any, List, Optional, Union - -import dns.message -import dns.name -import dns.opcode -import dns.rdata -import dns.rdataclass -import dns.rdataset -import dns.rdatatype -import dns.tsig - - -class UpdateSection(dns.enum.IntEnum): - """Update sections""" - - ZONE = 0 - PREREQ = 1 - UPDATE = 2 - ADDITIONAL = 3 - - @classmethod - def _maximum(cls): - return 3 - - -class UpdateMessage(dns.message.Message): # lgtm[py/missing-equals] - # ignore the mypy error here as we mean to use a different enum - _section_enum = UpdateSection # type: ignore - - def __init__( - self, - zone: Optional[Union[dns.name.Name, str]] = None, - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - keyring: Optional[Any] = None, - keyname: Optional[dns.name.Name] = None, - keyalgorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, - id: Optional[int] = None, - ): - """Initialize a new DNS Update object. - - See the documentation of the Message class for a complete - description of the keyring dictionary. - - *zone*, a ``dns.name.Name``, ``str``, or ``None``, the zone - which is being updated. ``None`` should only be used by dnspython's - message constructors, as a zone is required for the convenience - methods like ``add()``, ``replace()``, etc. - - *rdclass*, an ``int`` or ``str``, the class of the zone. - - The *keyring*, *keyname*, and *keyalgorithm* parameters are passed to - ``use_tsig()``; see its documentation for details. - """ - super().__init__(id=id) - self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE) - if isinstance(zone, str): - zone = dns.name.from_text(zone) - self.origin = zone - rdclass = dns.rdataclass.RdataClass.make(rdclass) - self.zone_rdclass = rdclass - if self.origin: - self.find_rrset( - self.zone, - self.origin, - rdclass, - dns.rdatatype.SOA, - create=True, - force_unique=True, - ) - if keyring is not None: - self.use_tsig(keyring, keyname, algorithm=keyalgorithm) - - @property - def zone(self) -> List[dns.rrset.RRset]: - """The zone section.""" - return self.sections[0] - - @zone.setter - def zone(self, v): - self.sections[0] = v - - @property - def prerequisite(self) -> List[dns.rrset.RRset]: - """The prerequisite section.""" - return self.sections[1] - - @prerequisite.setter - def prerequisite(self, v): - self.sections[1] = v - - @property - def update(self) -> List[dns.rrset.RRset]: - """The update section.""" - return self.sections[2] - - @update.setter - def update(self, v): - self.sections[2] = v - - def _add_rr(self, name, ttl, rd, deleting=None, section=None): - """Add a single RR to the update section.""" - - if section is None: - section = self.update - covers = rd.covers() - rrset = self.find_rrset( - section, name, self.zone_rdclass, rd.rdtype, covers, deleting, True, True - ) - rrset.add(rd, ttl) - - def _add(self, replace, section, name, *args): - """Add records. - - *replace* is the replacement mode. If ``False``, - RRs are added to an existing RRset; if ``True``, the RRset - is replaced with the specified contents. The second - argument is the section to add to. The third argument - is always a name. The other arguments can be: - - - rdataset... - - - ttl, rdata... - - - ttl, rdtype, string... - """ - - if isinstance(name, str): - name = dns.name.from_text(name, None) - if isinstance(args[0], dns.rdataset.Rdataset): - for rds in args: - if replace: - self.delete(name, rds.rdtype) - for rd in rds: - self._add_rr(name, rds.ttl, rd, section=section) - else: - args = list(args) - ttl = int(args.pop(0)) - if isinstance(args[0], dns.rdata.Rdata): - if replace: - self.delete(name, args[0].rdtype) - for rd in args: - self._add_rr(name, ttl, rd, section=section) - else: - rdtype = dns.rdatatype.RdataType.make(args.pop(0)) - if replace: - self.delete(name, rdtype) - for s in args: - rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s, self.origin) - self._add_rr(name, ttl, rd, section=section) - - def add(self, name: Union[dns.name.Name, str], *args: Any) -> None: - """Add records. - - The first argument is always a name. The other - arguments can be: - - - rdataset... - - - ttl, rdata... - - - ttl, rdtype, string... - """ - - self._add(False, self.update, name, *args) - - def delete(self, name: Union[dns.name.Name, str], *args: Any) -> None: - """Delete records. - - The first argument is always a name. The other - arguments can be: - - - *empty* - - - rdataset... - - - rdata... - - - rdtype, [string...] - """ - - if isinstance(name, str): - name = dns.name.from_text(name, None) - if len(args) == 0: - self.find_rrset( - self.update, - name, - dns.rdataclass.ANY, - dns.rdatatype.ANY, - dns.rdatatype.NONE, - dns.rdataclass.ANY, - True, - True, - ) - elif isinstance(args[0], dns.rdataset.Rdataset): - for rds in args: - for rd in rds: - self._add_rr(name, 0, rd, dns.rdataclass.NONE) - else: - largs = list(args) - if isinstance(largs[0], dns.rdata.Rdata): - for rd in largs: - self._add_rr(name, 0, rd, dns.rdataclass.NONE) - else: - rdtype = dns.rdatatype.RdataType.make(largs.pop(0)) - if len(largs) == 0: - self.find_rrset( - self.update, - name, - self.zone_rdclass, - rdtype, - dns.rdatatype.NONE, - dns.rdataclass.ANY, - True, - True, - ) - else: - for s in largs: - rd = dns.rdata.from_text( - self.zone_rdclass, - rdtype, - s, # type: ignore[arg-type] - self.origin, - ) - self._add_rr(name, 0, rd, dns.rdataclass.NONE) - - def replace(self, name: Union[dns.name.Name, str], *args: Any) -> None: - """Replace records. - - The first argument is always a name. The other - arguments can be: - - - rdataset... - - - ttl, rdata... - - - ttl, rdtype, string... - - Note that if you want to replace the entire node, you should do - a delete of the name followed by one or more calls to add. - """ - - self._add(True, self.update, name, *args) - - def present(self, name: Union[dns.name.Name, str], *args: Any) -> None: - """Require that an owner name (and optionally an rdata type, - or specific rdataset) exists as a prerequisite to the - execution of the update. - - The first argument is always a name. - The other arguments can be: - - - rdataset... - - - rdata... - - - rdtype, string... - """ - - if isinstance(name, str): - name = dns.name.from_text(name, None) - if len(args) == 0: - self.find_rrset( - self.prerequisite, - name, - dns.rdataclass.ANY, - dns.rdatatype.ANY, - dns.rdatatype.NONE, - None, - True, - True, - ) - elif ( - isinstance(args[0], dns.rdataset.Rdataset) - or isinstance(args[0], dns.rdata.Rdata) - or len(args) > 1 - ): - if not isinstance(args[0], dns.rdataset.Rdataset): - # Add a 0 TTL - largs = list(args) - largs.insert(0, 0) # type: ignore[arg-type] - self._add(False, self.prerequisite, name, *largs) - else: - self._add(False, self.prerequisite, name, *args) - else: - rdtype = dns.rdatatype.RdataType.make(args[0]) - self.find_rrset( - self.prerequisite, - name, - dns.rdataclass.ANY, - rdtype, - dns.rdatatype.NONE, - None, - True, - True, - ) - - def absent( - self, - name: Union[dns.name.Name, str], - rdtype: Optional[Union[dns.rdatatype.RdataType, str]] = None, - ) -> None: - """Require that an owner name (and optionally an rdata type) does - not exist as a prerequisite to the execution of the update.""" - - if isinstance(name, str): - name = dns.name.from_text(name, None) - if rdtype is None: - self.find_rrset( - self.prerequisite, - name, - dns.rdataclass.NONE, - dns.rdatatype.ANY, - dns.rdatatype.NONE, - None, - True, - True, - ) - else: - rdtype = dns.rdatatype.RdataType.make(rdtype) - self.find_rrset( - self.prerequisite, - name, - dns.rdataclass.NONE, - rdtype, - dns.rdatatype.NONE, - None, - True, - True, - ) - - def _get_one_rr_per_rrset(self, value): - # Updates are always one_rr_per_rrset - return True - - def _parse_rr_header(self, section, name, rdclass, rdtype): - deleting = None - empty = False - if section == UpdateSection.ZONE: - if ( - dns.rdataclass.is_metaclass(rdclass) - or rdtype != dns.rdatatype.SOA - or self.zone - ): - raise dns.exception.FormError - else: - if not self.zone: - raise dns.exception.FormError - if rdclass in (dns.rdataclass.ANY, dns.rdataclass.NONE): - deleting = rdclass - rdclass = self.zone[0].rdclass - empty = ( - deleting == dns.rdataclass.ANY or section == UpdateSection.PREREQ - ) - return (rdclass, rdtype, deleting, empty) - - -# backwards compatibility -Update = UpdateMessage - -### BEGIN generated UpdateSection constants - -ZONE = UpdateSection.ZONE -PREREQ = UpdateSection.PREREQ -UPDATE = UpdateSection.UPDATE -ADDITIONAL = UpdateSection.ADDITIONAL - -### END generated UpdateSection constants diff --git a/venv/Lib/site-packages/dns/version.py b/venv/Lib/site-packages/dns/version.py deleted file mode 100644 index 9ed2ce1..0000000 --- a/venv/Lib/site-packages/dns/version.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""dnspython release version information.""" - -#: MAJOR -MAJOR = 2 -#: MINOR -MINOR = 7 -#: MICRO -MICRO = 0 -#: RELEASELEVEL -RELEASELEVEL = 0x0F -#: SERIAL -SERIAL = 0 - -if RELEASELEVEL == 0x0F: # pragma: no cover lgtm[py/unreachable-statement] - #: version - version = "%d.%d.%d" % (MAJOR, MINOR, MICRO) # lgtm[py/unreachable-statement] -elif RELEASELEVEL == 0x00: # pragma: no cover lgtm[py/unreachable-statement] - version = "%d.%d.%ddev%d" % ( - MAJOR, - MINOR, - MICRO, - SERIAL, - ) # lgtm[py/unreachable-statement] -elif RELEASELEVEL == 0x0C: # pragma: no cover lgtm[py/unreachable-statement] - version = "%d.%d.%drc%d" % ( - MAJOR, - MINOR, - MICRO, - SERIAL, - ) # lgtm[py/unreachable-statement] -else: # pragma: no cover lgtm[py/unreachable-statement] - version = "%d.%d.%d%x%d" % ( - MAJOR, - MINOR, - MICRO, - RELEASELEVEL, - SERIAL, - ) # lgtm[py/unreachable-statement] - -#: hexversion -hexversion = MAJOR << 24 | MINOR << 16 | MICRO << 8 | RELEASELEVEL << 4 | SERIAL diff --git a/venv/Lib/site-packages/dns/versioned.py b/venv/Lib/site-packages/dns/versioned.py deleted file mode 100644 index fd78e67..0000000 --- a/venv/Lib/site-packages/dns/versioned.py +++ /dev/null @@ -1,318 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -"""DNS Versioned Zones.""" - -import collections -import threading -from typing import Callable, Deque, Optional, Set, Union - -import dns.exception -import dns.immutable -import dns.name -import dns.node -import dns.rdataclass -import dns.rdataset -import dns.rdatatype -import dns.rdtypes.ANY.SOA -import dns.zone - - -class UseTransaction(dns.exception.DNSException): - """To alter a versioned zone, use a transaction.""" - - -# Backwards compatibility -Node = dns.zone.VersionedNode -ImmutableNode = dns.zone.ImmutableVersionedNode -Version = dns.zone.Version -WritableVersion = dns.zone.WritableVersion -ImmutableVersion = dns.zone.ImmutableVersion -Transaction = dns.zone.Transaction - - -class Zone(dns.zone.Zone): # lgtm[py/missing-equals] - __slots__ = [ - "_versions", - "_versions_lock", - "_write_txn", - "_write_waiters", - "_write_event", - "_pruning_policy", - "_readers", - ] - - node_factory = Node - - def __init__( - self, - origin: Optional[Union[dns.name.Name, str]], - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - relativize: bool = True, - pruning_policy: Optional[Callable[["Zone", Version], Optional[bool]]] = None, - ): - """Initialize a versioned zone object. - - *origin* is the origin of the zone. It may be a ``dns.name.Name``, - a ``str``, or ``None``. If ``None``, then the zone's origin will - be set by the first ``$ORIGIN`` line in a zone file. - - *rdclass*, an ``int``, the zone's rdata class; the default is class IN. - - *relativize*, a ``bool``, determine's whether domain names are - relativized to the zone's origin. The default is ``True``. - - *pruning policy*, a function taking a ``Zone`` and a ``Version`` and returning - a ``bool``, or ``None``. Should the version be pruned? If ``None``, - the default policy, which retains one version is used. - """ - super().__init__(origin, rdclass, relativize) - self._versions: Deque[Version] = collections.deque() - self._version_lock = threading.Lock() - if pruning_policy is None: - self._pruning_policy = self._default_pruning_policy - else: - self._pruning_policy = pruning_policy - self._write_txn: Optional[Transaction] = None - self._write_event: Optional[threading.Event] = None - self._write_waiters: Deque[threading.Event] = collections.deque() - self._readers: Set[Transaction] = set() - self._commit_version_unlocked( - None, WritableVersion(self, replacement=True), origin - ) - - def reader( - self, id: Optional[int] = None, serial: Optional[int] = None - ) -> Transaction: # pylint: disable=arguments-differ - if id is not None and serial is not None: - raise ValueError("cannot specify both id and serial") - with self._version_lock: - if id is not None: - version = None - for v in reversed(self._versions): - if v.id == id: - version = v - break - if version is None: - raise KeyError("version not found") - elif serial is not None: - if self.relativize: - oname = dns.name.empty - else: - assert self.origin is not None - oname = self.origin - version = None - for v in reversed(self._versions): - n = v.nodes.get(oname) - if n: - rds = n.get_rdataset(self.rdclass, dns.rdatatype.SOA) - if rds and rds[0].serial == serial: - version = v - break - if version is None: - raise KeyError("serial not found") - else: - version = self._versions[-1] - txn = Transaction(self, False, version) - self._readers.add(txn) - return txn - - def writer(self, replacement: bool = False) -> Transaction: - event = None - while True: - with self._version_lock: - # Checking event == self._write_event ensures that either - # no one was waiting before we got lucky and found no write - # txn, or we were the one who was waiting and got woken up. - # This prevents "taking cuts" when creating a write txn. - if self._write_txn is None and event == self._write_event: - # Creating the transaction defers version setup - # (i.e. copying the nodes dictionary) until we - # give up the lock, so that we hold the lock as - # short a time as possible. This is why we call - # _setup_version() below. - self._write_txn = Transaction( - self, replacement, make_immutable=True - ) - # give up our exclusive right to make a Transaction - self._write_event = None - break - # Someone else is writing already, so we will have to - # wait, but we want to do the actual wait outside the - # lock. - event = threading.Event() - self._write_waiters.append(event) - # wait (note we gave up the lock!) - # - # We only wake one sleeper at a time, so it's important - # that no event waiter can exit this method (e.g. via - # cancellation) without returning a transaction or waking - # someone else up. - # - # This is not a problem with Threading module threads as - # they cannot be canceled, but could be an issue with trio - # tasks when we do the async version of writer(). - # I.e. we'd need to do something like: - # - # try: - # event.wait() - # except trio.Cancelled: - # with self._version_lock: - # self._maybe_wakeup_one_waiter_unlocked() - # raise - # - event.wait() - # Do the deferred version setup. - self._write_txn._setup_version() - return self._write_txn - - def _maybe_wakeup_one_waiter_unlocked(self): - if len(self._write_waiters) > 0: - self._write_event = self._write_waiters.popleft() - self._write_event.set() - - # pylint: disable=unused-argument - def _default_pruning_policy(self, zone, version): - return True - - # pylint: enable=unused-argument - - def _prune_versions_unlocked(self): - assert len(self._versions) > 0 - # Don't ever prune a version greater than or equal to one that - # a reader has open. This pins versions in memory while the - # reader is open, and importantly lets the reader open a txn on - # a successor version (e.g. if generating an IXFR). - # - # Note our definition of least_kept also ensures we do not try to - # delete the greatest version. - if len(self._readers) > 0: - least_kept = min(txn.version.id for txn in self._readers) - else: - least_kept = self._versions[-1].id - while self._versions[0].id < least_kept and self._pruning_policy( - self, self._versions[0] - ): - self._versions.popleft() - - def set_max_versions(self, max_versions: Optional[int]) -> None: - """Set a pruning policy that retains up to the specified number - of versions - """ - if max_versions is not None and max_versions < 1: - raise ValueError("max versions must be at least 1") - if max_versions is None: - - def policy(zone, _): # pylint: disable=unused-argument - return False - - else: - - def policy(zone, _): - return len(zone._versions) > max_versions - - self.set_pruning_policy(policy) - - def set_pruning_policy( - self, policy: Optional[Callable[["Zone", Version], Optional[bool]]] - ) -> None: - """Set the pruning policy for the zone. - - The *policy* function takes a `Version` and returns `True` if - the version should be pruned, and `False` otherwise. `None` - may also be specified for policy, in which case the default policy - is used. - - Pruning checking proceeds from the least version and the first - time the function returns `False`, the checking stops. I.e. the - retained versions are always a consecutive sequence. - """ - if policy is None: - policy = self._default_pruning_policy - with self._version_lock: - self._pruning_policy = policy - self._prune_versions_unlocked() - - def _end_read(self, txn): - with self._version_lock: - self._readers.remove(txn) - self._prune_versions_unlocked() - - def _end_write_unlocked(self, txn): - assert self._write_txn == txn - self._write_txn = None - self._maybe_wakeup_one_waiter_unlocked() - - def _end_write(self, txn): - with self._version_lock: - self._end_write_unlocked(txn) - - def _commit_version_unlocked(self, txn, version, origin): - self._versions.append(version) - self._prune_versions_unlocked() - self.nodes = version.nodes - if self.origin is None: - self.origin = origin - # txn can be None in __init__ when we make the empty version. - if txn is not None: - self._end_write_unlocked(txn) - - def _commit_version(self, txn, version, origin): - with self._version_lock: - self._commit_version_unlocked(txn, version, origin) - - def _get_next_version_id(self): - if len(self._versions) > 0: - id = self._versions[-1].id + 1 - else: - id = 1 - return id - - def find_node( - self, name: Union[dns.name.Name, str], create: bool = False - ) -> dns.node.Node: - if create: - raise UseTransaction - return super().find_node(name) - - def delete_node(self, name: Union[dns.name.Name, str]) -> None: - raise UseTransaction - - def find_rdataset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - create: bool = False, - ) -> dns.rdataset.Rdataset: - if create: - raise UseTransaction - rdataset = super().find_rdataset(name, rdtype, covers) - return dns.rdataset.ImmutableRdataset(rdataset) - - def get_rdataset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - create: bool = False, - ) -> Optional[dns.rdataset.Rdataset]: - if create: - raise UseTransaction - rdataset = super().get_rdataset(name, rdtype, covers) - if rdataset is not None: - return dns.rdataset.ImmutableRdataset(rdataset) - else: - return None - - def delete_rdataset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - ) -> None: - raise UseTransaction - - def replace_rdataset( - self, name: Union[dns.name.Name, str], replacement: dns.rdataset.Rdataset - ) -> None: - raise UseTransaction diff --git a/venv/Lib/site-packages/dns/win32util.py b/venv/Lib/site-packages/dns/win32util.py deleted file mode 100644 index 9ed3f11..0000000 --- a/venv/Lib/site-packages/dns/win32util.py +++ /dev/null @@ -1,242 +0,0 @@ -import sys - -import dns._features - -if sys.platform == "win32": - from typing import Any - - import dns.name - - _prefer_wmi = True - - import winreg # pylint: disable=import-error - - # Keep pylint quiet on non-windows. - try: - _ = WindowsError # pylint: disable=used-before-assignment - except NameError: - WindowsError = Exception - - if dns._features.have("wmi"): - import threading - - import pythoncom # pylint: disable=import-error - import wmi # pylint: disable=import-error - - _have_wmi = True - else: - _have_wmi = False - - def _config_domain(domain): - # Sometimes DHCP servers add a '.' prefix to the default domain, and - # Windows just stores such values in the registry (see #687). - # Check for this and fix it. - if domain.startswith("."): - domain = domain[1:] - return dns.name.from_text(domain) - - class DnsInfo: - def __init__(self): - self.domain = None - self.nameservers = [] - self.search = [] - - if _have_wmi: - - class _WMIGetter(threading.Thread): - # pylint: disable=possibly-used-before-assignment - def __init__(self): - super().__init__() - self.info = DnsInfo() - - def run(self): - pythoncom.CoInitialize() - try: - system = wmi.WMI() - for interface in system.Win32_NetworkAdapterConfiguration(): - if interface.IPEnabled and interface.DNSServerSearchOrder: - self.info.nameservers = list(interface.DNSServerSearchOrder) - if interface.DNSDomain: - self.info.domain = _config_domain(interface.DNSDomain) - if interface.DNSDomainSuffixSearchOrder: - self.info.search = [ - _config_domain(x) - for x in interface.DNSDomainSuffixSearchOrder - ] - break - finally: - pythoncom.CoUninitialize() - - def get(self): - # We always run in a separate thread to avoid any issues with - # the COM threading model. - self.start() - self.join() - return self.info - - else: - - class _WMIGetter: # type: ignore - pass - - class _RegistryGetter: - def __init__(self): - self.info = DnsInfo() - - def _split(self, text): - # The windows registry has used both " " and "," as a delimiter, and while - # it is currently using "," in Windows 10 and later, updates can seemingly - # leave a space in too, e.g. "a, b". So we just convert all commas to - # spaces, and use split() in its default configuration, which splits on - # all whitespace and ignores empty strings. - return text.replace(",", " ").split() - - def _config_nameservers(self, nameservers): - for ns in self._split(nameservers): - if ns not in self.info.nameservers: - self.info.nameservers.append(ns) - - def _config_search(self, search): - for s in self._split(search): - s = _config_domain(s) - if s not in self.info.search: - self.info.search.append(s) - - def _config_fromkey(self, key, always_try_domain): - try: - servers, _ = winreg.QueryValueEx(key, "NameServer") - except WindowsError: - servers = None - if servers: - self._config_nameservers(servers) - if servers or always_try_domain: - try: - dom, _ = winreg.QueryValueEx(key, "Domain") - if dom: - self.info.domain = _config_domain(dom) - except WindowsError: - pass - else: - try: - servers, _ = winreg.QueryValueEx(key, "DhcpNameServer") - except WindowsError: - servers = None - if servers: - self._config_nameservers(servers) - try: - dom, _ = winreg.QueryValueEx(key, "DhcpDomain") - if dom: - self.info.domain = _config_domain(dom) - except WindowsError: - pass - try: - search, _ = winreg.QueryValueEx(key, "SearchList") - except WindowsError: - search = None - if search is None: - try: - search, _ = winreg.QueryValueEx(key, "DhcpSearchList") - except WindowsError: - search = None - if search: - self._config_search(search) - - def _is_nic_enabled(self, lm, guid): - # Look in the Windows Registry to determine whether the network - # interface corresponding to the given guid is enabled. - # - # (Code contributed by Paul Marks, thanks!) - # - try: - # This hard-coded location seems to be consistent, at least - # from Windows 2000 through Vista. - connection_key = winreg.OpenKey( - lm, - r"SYSTEM\CurrentControlSet\Control\Network" - r"\{4D36E972-E325-11CE-BFC1-08002BE10318}" - rf"\{guid}\Connection", - ) - - try: - # The PnpInstanceID points to a key inside Enum - (pnp_id, ttype) = winreg.QueryValueEx( - connection_key, "PnpInstanceID" - ) - - if ttype != winreg.REG_SZ: - raise ValueError # pragma: no cover - - device_key = winreg.OpenKey( - lm, rf"SYSTEM\CurrentControlSet\Enum\{pnp_id}" - ) - - try: - # Get ConfigFlags for this device - (flags, ttype) = winreg.QueryValueEx(device_key, "ConfigFlags") - - if ttype != winreg.REG_DWORD: - raise ValueError # pragma: no cover - - # Based on experimentation, bit 0x1 indicates that the - # device is disabled. - # - # XXXRTH I suspect we really want to & with 0x03 so - # that CONFIGFLAGS_REMOVED devices are also ignored, - # but we're shifting to WMI as ConfigFlags is not - # supposed to be used. - return not flags & 0x1 - - finally: - device_key.Close() - finally: - connection_key.Close() - except Exception: # pragma: no cover - return False - - def get(self): - """Extract resolver configuration from the Windows registry.""" - - lm = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) - try: - tcp_params = winreg.OpenKey( - lm, r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" - ) - try: - self._config_fromkey(tcp_params, True) - finally: - tcp_params.Close() - interfaces = winreg.OpenKey( - lm, - r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces", - ) - try: - i = 0 - while True: - try: - guid = winreg.EnumKey(interfaces, i) - i += 1 - key = winreg.OpenKey(interfaces, guid) - try: - if not self._is_nic_enabled(lm, guid): - continue - self._config_fromkey(key, False) - finally: - key.Close() - except OSError: - break - finally: - interfaces.Close() - finally: - lm.Close() - return self.info - - _getter_class: Any - if _have_wmi and _prefer_wmi: - _getter_class = _WMIGetter - else: - _getter_class = _RegistryGetter - - def get_dns_info(): - """Extract resolver configuration.""" - getter = _getter_class() - return getter.get() diff --git a/venv/Lib/site-packages/dns/wire.py b/venv/Lib/site-packages/dns/wire.py deleted file mode 100644 index 9f9b157..0000000 --- a/venv/Lib/site-packages/dns/wire.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import contextlib -import struct -from typing import Iterator, Optional, Tuple - -import dns.exception -import dns.name - - -class Parser: - def __init__(self, wire: bytes, current: int = 0): - self.wire = wire - self.current = 0 - self.end = len(self.wire) - if current: - self.seek(current) - self.furthest = current - - def remaining(self) -> int: - return self.end - self.current - - def get_bytes(self, size: int) -> bytes: - assert size >= 0 - if size > self.remaining(): - raise dns.exception.FormError - output = self.wire[self.current : self.current + size] - self.current += size - self.furthest = max(self.furthest, self.current) - return output - - def get_counted_bytes(self, length_size: int = 1) -> bytes: - length = int.from_bytes(self.get_bytes(length_size), "big") - return self.get_bytes(length) - - def get_remaining(self) -> bytes: - return self.get_bytes(self.remaining()) - - def get_uint8(self) -> int: - return struct.unpack("!B", self.get_bytes(1))[0] - - def get_uint16(self) -> int: - return struct.unpack("!H", self.get_bytes(2))[0] - - def get_uint32(self) -> int: - return struct.unpack("!I", self.get_bytes(4))[0] - - def get_uint48(self) -> int: - return int.from_bytes(self.get_bytes(6), "big") - - def get_struct(self, format: str) -> Tuple: - return struct.unpack(format, self.get_bytes(struct.calcsize(format))) - - def get_name(self, origin: Optional["dns.name.Name"] = None) -> "dns.name.Name": - name = dns.name.from_wire_parser(self) - if origin: - name = name.relativize(origin) - return name - - def seek(self, where: int) -> None: - # Note that seeking to the end is OK! (If you try to read - # after such a seek, you'll get an exception as expected.) - if where < 0 or where > self.end: - raise dns.exception.FormError - self.current = where - - @contextlib.contextmanager - def restrict_to(self, size: int) -> Iterator: - assert size >= 0 - if size > self.remaining(): - raise dns.exception.FormError - saved_end = self.end - try: - self.end = self.current + size - yield - # We make this check here and not in the finally as we - # don't want to raise if we're already raising for some - # other reason. - if self.current != self.end: - raise dns.exception.FormError - finally: - self.end = saved_end - - @contextlib.contextmanager - def restore_furthest(self) -> Iterator: - try: - yield None - finally: - self.current = self.furthest diff --git a/venv/Lib/site-packages/dns/xfr.py b/venv/Lib/site-packages/dns/xfr.py deleted file mode 100644 index 520aa32..0000000 --- a/venv/Lib/site-packages/dns/xfr.py +++ /dev/null @@ -1,343 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -from typing import Any, List, Optional, Tuple, Union - -import dns.exception -import dns.message -import dns.name -import dns.rcode -import dns.rdataset -import dns.rdatatype -import dns.serial -import dns.transaction -import dns.tsig -import dns.zone - - -class TransferError(dns.exception.DNSException): - """A zone transfer response got a non-zero rcode.""" - - def __init__(self, rcode): - message = f"Zone transfer error: {dns.rcode.to_text(rcode)}" - super().__init__(message) - self.rcode = rcode - - -class SerialWentBackwards(dns.exception.FormError): - """The current serial number is less than the serial we know.""" - - -class UseTCP(dns.exception.DNSException): - """This IXFR cannot be completed with UDP.""" - - -class Inbound: - """ - State machine for zone transfers. - """ - - def __init__( - self, - txn_manager: dns.transaction.TransactionManager, - rdtype: dns.rdatatype.RdataType = dns.rdatatype.AXFR, - serial: Optional[int] = None, - is_udp: bool = False, - ): - """Initialize an inbound zone transfer. - - *txn_manager* is a :py:class:`dns.transaction.TransactionManager`. - - *rdtype* can be `dns.rdatatype.AXFR` or `dns.rdatatype.IXFR` - - *serial* is the base serial number for IXFRs, and is required in - that case. - - *is_udp*, a ``bool`` indidicates if UDP is being used for this - XFR. - """ - self.txn_manager = txn_manager - self.txn: Optional[dns.transaction.Transaction] = None - self.rdtype = rdtype - if rdtype == dns.rdatatype.IXFR: - if serial is None: - raise ValueError("a starting serial must be supplied for IXFRs") - elif is_udp: - raise ValueError("is_udp specified for AXFR") - self.serial = serial - self.is_udp = is_udp - (_, _, self.origin) = txn_manager.origin_information() - self.soa_rdataset: Optional[dns.rdataset.Rdataset] = None - self.done = False - self.expecting_SOA = False - self.delete_mode = False - - def process_message(self, message: dns.message.Message) -> bool: - """Process one message in the transfer. - - The message should have the same relativization as was specified when - the `dns.xfr.Inbound` was created. The message should also have been - created with `one_rr_per_rrset=True` because order matters. - - Returns `True` if the transfer is complete, and `False` otherwise. - """ - if self.txn is None: - replacement = self.rdtype == dns.rdatatype.AXFR - self.txn = self.txn_manager.writer(replacement) - rcode = message.rcode() - if rcode != dns.rcode.NOERROR: - raise TransferError(rcode) - # - # We don't require a question section, but if it is present is - # should be correct. - # - if len(message.question) > 0: - if message.question[0].name != self.origin: - raise dns.exception.FormError("wrong question name") - if message.question[0].rdtype != self.rdtype: - raise dns.exception.FormError("wrong question rdatatype") - answer_index = 0 - if self.soa_rdataset is None: - # - # This is the first message. We're expecting an SOA at - # the origin. - # - if not message.answer or message.answer[0].name != self.origin: - raise dns.exception.FormError("No answer or RRset not for zone origin") - rrset = message.answer[0] - rdataset = rrset - if rdataset.rdtype != dns.rdatatype.SOA: - raise dns.exception.FormError("first RRset is not an SOA") - answer_index = 1 - self.soa_rdataset = rdataset.copy() - if self.rdtype == dns.rdatatype.IXFR: - if self.soa_rdataset[0].serial == self.serial: - # - # We're already up-to-date. - # - self.done = True - elif dns.serial.Serial(self.soa_rdataset[0].serial) < self.serial: - # It went backwards! - raise SerialWentBackwards - else: - if self.is_udp and len(message.answer[answer_index:]) == 0: - # - # There are no more records, so this is the - # "truncated" response. Say to use TCP - # - raise UseTCP - # - # Note we're expecting another SOA so we can detect - # if this IXFR response is an AXFR-style response. - # - self.expecting_SOA = True - # - # Process the answer section (other than the initial SOA in - # the first message). - # - for rrset in message.answer[answer_index:]: - name = rrset.name - rdataset = rrset - if self.done: - raise dns.exception.FormError("answers after final SOA") - assert self.txn is not None # for mypy - if rdataset.rdtype == dns.rdatatype.SOA and name == self.origin: - # - # Every time we see an origin SOA delete_mode inverts - # - if self.rdtype == dns.rdatatype.IXFR: - self.delete_mode = not self.delete_mode - # - # If this SOA Rdataset is equal to the first we saw - # then we're finished. If this is an IXFR we also - # check that we're seeing the record in the expected - # part of the response. - # - if rdataset == self.soa_rdataset and ( - self.rdtype == dns.rdatatype.AXFR - or (self.rdtype == dns.rdatatype.IXFR and self.delete_mode) - ): - # - # This is the final SOA - # - if self.expecting_SOA: - # We got an empty IXFR sequence! - raise dns.exception.FormError("empty IXFR sequence") - if ( - self.rdtype == dns.rdatatype.IXFR - and self.serial != rdataset[0].serial - ): - raise dns.exception.FormError("unexpected end of IXFR sequence") - self.txn.replace(name, rdataset) - self.txn.commit() - self.txn = None - self.done = True - else: - # - # This is not the final SOA - # - self.expecting_SOA = False - if self.rdtype == dns.rdatatype.IXFR: - if self.delete_mode: - # This is the start of an IXFR deletion set - if rdataset[0].serial != self.serial: - raise dns.exception.FormError( - "IXFR base serial mismatch" - ) - else: - # This is the start of an IXFR addition set - self.serial = rdataset[0].serial - self.txn.replace(name, rdataset) - else: - # We saw a non-final SOA for the origin in an AXFR. - raise dns.exception.FormError("unexpected origin SOA in AXFR") - continue - if self.expecting_SOA: - # - # We made an IXFR request and are expecting another - # SOA RR, but saw something else, so this must be an - # AXFR response. - # - self.rdtype = dns.rdatatype.AXFR - self.expecting_SOA = False - self.delete_mode = False - self.txn.rollback() - self.txn = self.txn_manager.writer(True) - # - # Note we are falling through into the code below - # so whatever rdataset this was gets written. - # - # Add or remove the data - if self.delete_mode: - self.txn.delete_exact(name, rdataset) - else: - self.txn.add(name, rdataset) - if self.is_udp and not self.done: - # - # This is a UDP IXFR and we didn't get to done, and we didn't - # get the proper "truncated" response - # - raise dns.exception.FormError("unexpected end of UDP IXFR") - return self.done - - # - # Inbounds are context managers. - # - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - if self.txn: - self.txn.rollback() - return False - - -def make_query( - txn_manager: dns.transaction.TransactionManager, - serial: Optional[int] = 0, - use_edns: Optional[Union[int, bool]] = None, - ednsflags: Optional[int] = None, - payload: Optional[int] = None, - request_payload: Optional[int] = None, - options: Optional[List[dns.edns.Option]] = None, - keyring: Any = None, - keyname: Optional[dns.name.Name] = None, - keyalgorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, -) -> Tuple[dns.message.QueryMessage, Optional[int]]: - """Make an AXFR or IXFR query. - - *txn_manager* is a ``dns.transaction.TransactionManager``, typically a - ``dns.zone.Zone``. - - *serial* is an ``int`` or ``None``. If 0, then IXFR will be - attempted using the most recent serial number from the - *txn_manager*; it is the caller's responsibility to ensure there - are no write transactions active that could invalidate the - retrieved serial. If a serial cannot be determined, AXFR will be - forced. Other integer values are the starting serial to use. - ``None`` forces an AXFR. - - Please see the documentation for :py:func:`dns.message.make_query` and - :py:func:`dns.message.Message.use_tsig` for details on the other parameters - to this function. - - Returns a `(query, serial)` tuple. - """ - (zone_origin, _, origin) = txn_manager.origin_information() - if zone_origin is None: - raise ValueError("no zone origin") - if serial is None: - rdtype = dns.rdatatype.AXFR - elif not isinstance(serial, int): - raise ValueError("serial is not an integer") - elif serial == 0: - with txn_manager.reader() as txn: - rdataset = txn.get(origin, "SOA") - if rdataset: - serial = rdataset[0].serial - rdtype = dns.rdatatype.IXFR - else: - serial = None - rdtype = dns.rdatatype.AXFR - elif serial > 0 and serial < 4294967296: - rdtype = dns.rdatatype.IXFR - else: - raise ValueError("serial out-of-range") - rdclass = txn_manager.get_class() - q = dns.message.make_query( - zone_origin, - rdtype, - rdclass, - use_edns, - False, - ednsflags, - payload, - request_payload, - options, - ) - if serial is not None: - rdata = dns.rdata.from_text(rdclass, "SOA", f". . {serial} 0 0 0 0") - rrset = q.find_rrset( - q.authority, zone_origin, rdclass, dns.rdatatype.SOA, create=True - ) - rrset.add(rdata, 0) - if keyring is not None: - q.use_tsig(keyring, keyname, algorithm=keyalgorithm) - return (q, serial) - - -def extract_serial_from_query(query: dns.message.Message) -> Optional[int]: - """Extract the SOA serial number from query if it is an IXFR and return - it, otherwise return None. - - *query* is a dns.message.QueryMessage that is an IXFR or AXFR request. - - Raises if the query is not an IXFR or AXFR, or if an IXFR doesn't have - an appropriate SOA RRset in the authority section. - """ - if not isinstance(query, dns.message.QueryMessage): - raise ValueError("query not a QueryMessage") - question = query.question[0] - if question.rdtype == dns.rdatatype.AXFR: - return None - elif question.rdtype != dns.rdatatype.IXFR: - raise ValueError("query is not an AXFR or IXFR") - soa = query.find_rrset( - query.authority, question.name, question.rdclass, dns.rdatatype.SOA - ) - return soa[0].serial diff --git a/venv/Lib/site-packages/dns/zone.py b/venv/Lib/site-packages/dns/zone.py deleted file mode 100644 index 844919e..0000000 --- a/venv/Lib/site-packages/dns/zone.py +++ /dev/null @@ -1,1434 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Zones.""" - -import contextlib -import io -import os -import struct -from typing import ( - Any, - Callable, - Iterable, - Iterator, - List, - MutableMapping, - Optional, - Set, - Tuple, - Union, -) - -import dns.exception -import dns.grange -import dns.immutable -import dns.name -import dns.node -import dns.rdata -import dns.rdataclass -import dns.rdataset -import dns.rdatatype -import dns.rdtypes.ANY.SOA -import dns.rdtypes.ANY.ZONEMD -import dns.rrset -import dns.tokenizer -import dns.transaction -import dns.ttl -import dns.zonefile -from dns.zonetypes import DigestHashAlgorithm, DigestScheme, _digest_hashers - - -class BadZone(dns.exception.DNSException): - """The DNS zone is malformed.""" - - -class NoSOA(BadZone): - """The DNS zone has no SOA RR at its origin.""" - - -class NoNS(BadZone): - """The DNS zone has no NS RRset at its origin.""" - - -class UnknownOrigin(BadZone): - """The DNS zone's origin is unknown.""" - - -class UnsupportedDigestScheme(dns.exception.DNSException): - """The zone digest's scheme is unsupported.""" - - -class UnsupportedDigestHashAlgorithm(dns.exception.DNSException): - """The zone digest's origin is unsupported.""" - - -class NoDigest(dns.exception.DNSException): - """The DNS zone has no ZONEMD RRset at its origin.""" - - -class DigestVerificationFailure(dns.exception.DNSException): - """The ZONEMD digest failed to verify.""" - - -def _validate_name( - name: dns.name.Name, - origin: Optional[dns.name.Name], - relativize: bool, -) -> dns.name.Name: - # This name validation code is shared by Zone and Version - if origin is None: - # This should probably never happen as other code (e.g. - # _rr_line) will notice the lack of an origin before us, but - # we check just in case! - raise KeyError("no zone origin is defined") - if name.is_absolute(): - if not name.is_subdomain(origin): - raise KeyError("name parameter must be a subdomain of the zone origin") - if relativize: - name = name.relativize(origin) - else: - # We have a relative name. Make sure that the derelativized name is - # not too long. - try: - abs_name = name.derelativize(origin) - except dns.name.NameTooLong: - # We map dns.name.NameTooLong to KeyError to be consistent with - # the other exceptions above. - raise KeyError("relative name too long for zone") - if not relativize: - # We have a relative name in a non-relative zone, so use the - # derelativized name. - name = abs_name - return name - - -class Zone(dns.transaction.TransactionManager): - """A DNS zone. - - A ``Zone`` is a mapping from names to nodes. The zone object may be - treated like a Python dictionary, e.g. ``zone[name]`` will retrieve - the node associated with that name. The *name* may be a - ``dns.name.Name object``, or it may be a string. In either case, - if the name is relative it is treated as relative to the origin of - the zone. - """ - - node_factory: Callable[[], dns.node.Node] = dns.node.Node - map_factory: Callable[[], MutableMapping[dns.name.Name, dns.node.Node]] = dict - writable_version_factory: Optional[Callable[[], "WritableVersion"]] = None - immutable_version_factory: Optional[Callable[[], "ImmutableVersion"]] = None - - __slots__ = ["rdclass", "origin", "nodes", "relativize"] - - def __init__( - self, - origin: Optional[Union[dns.name.Name, str]], - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - relativize: bool = True, - ): - """Initialize a zone object. - - *origin* is the origin of the zone. It may be a ``dns.name.Name``, - a ``str``, or ``None``. If ``None``, then the zone's origin will - be set by the first ``$ORIGIN`` line in a zone file. - - *rdclass*, an ``int``, the zone's rdata class; the default is class IN. - - *relativize*, a ``bool``, determine's whether domain names are - relativized to the zone's origin. The default is ``True``. - """ - - if origin is not None: - if isinstance(origin, str): - origin = dns.name.from_text(origin) - elif not isinstance(origin, dns.name.Name): - raise ValueError("origin parameter must be convertible to a DNS name") - if not origin.is_absolute(): - raise ValueError("origin parameter must be an absolute name") - self.origin = origin - self.rdclass = rdclass - self.nodes: MutableMapping[dns.name.Name, dns.node.Node] = self.map_factory() - self.relativize = relativize - - def __eq__(self, other): - """Two zones are equal if they have the same origin, class, and - nodes. - - Returns a ``bool``. - """ - - if not isinstance(other, Zone): - return False - if ( - self.rdclass != other.rdclass - or self.origin != other.origin - or self.nodes != other.nodes - ): - return False - return True - - def __ne__(self, other): - """Are two zones not equal? - - Returns a ``bool``. - """ - - return not self.__eq__(other) - - def _validate_name(self, name: Union[dns.name.Name, str]) -> dns.name.Name: - # Note that any changes in this method should have corresponding changes - # made in the Version _validate_name() method. - if isinstance(name, str): - name = dns.name.from_text(name, None) - elif not isinstance(name, dns.name.Name): - raise KeyError("name parameter must be convertible to a DNS name") - return _validate_name(name, self.origin, self.relativize) - - def __getitem__(self, key): - key = self._validate_name(key) - return self.nodes[key] - - def __setitem__(self, key, value): - key = self._validate_name(key) - self.nodes[key] = value - - def __delitem__(self, key): - key = self._validate_name(key) - del self.nodes[key] - - def __iter__(self): - return self.nodes.__iter__() - - def keys(self): - return self.nodes.keys() - - def values(self): - return self.nodes.values() - - def items(self): - return self.nodes.items() - - def get(self, key): - key = self._validate_name(key) - return self.nodes.get(key) - - def __contains__(self, key): - key = self._validate_name(key) - return key in self.nodes - - def find_node( - self, name: Union[dns.name.Name, str], create: bool = False - ) -> dns.node.Node: - """Find a node in the zone, possibly creating it. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - *create*, a ``bool``. If true, the node will be created if it does - not exist. - - Raises ``KeyError`` if the name is not known and create was - not specified, or if the name was not a subdomain of the origin. - - Returns a ``dns.node.Node``. - """ - - name = self._validate_name(name) - node = self.nodes.get(name) - if node is None: - if not create: - raise KeyError - node = self.node_factory() - self.nodes[name] = node - return node - - def get_node( - self, name: Union[dns.name.Name, str], create: bool = False - ) -> Optional[dns.node.Node]: - """Get a node in the zone, possibly creating it. - - This method is like ``find_node()``, except it returns None instead - of raising an exception if the node does not exist and creation - has not been requested. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - *create*, a ``bool``. If true, the node will be created if it does - not exist. - - Returns a ``dns.node.Node`` or ``None``. - """ - - try: - node = self.find_node(name, create) - except KeyError: - node = None - return node - - def delete_node(self, name: Union[dns.name.Name, str]) -> None: - """Delete the specified node if it exists. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - It is not an error if the node does not exist. - """ - - name = self._validate_name(name) - if name in self.nodes: - del self.nodes[name] - - def find_rdataset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - create: bool = False, - ) -> dns.rdataset.Rdataset: - """Look for an rdataset with the specified name and type in the zone, - and return an rdataset encapsulating it. - - The rdataset returned is not a copy; changes to it will change - the zone. - - KeyError is raised if the name or type are not found. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdata type desired. - - *covers*, a ``dns.rdatatype.RdataType`` or ``str`` the covered type. - Usually this value is ``dns.rdatatype.NONE``, but if the - rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, - then the covers value will be the rdata type the SIG/RRSIG - covers. The library treats the SIG and RRSIG types as if they - were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). - This makes RRSIGs much easier to work with than if RRSIGs - covering different rdata types were aggregated into a single - RRSIG rdataset. - - *create*, a ``bool``. If true, the node will be created if it does - not exist. - - Raises ``KeyError`` if the name is not known and create was - not specified, or if the name was not a subdomain of the origin. - - Returns a ``dns.rdataset.Rdataset``. - """ - - name = self._validate_name(name) - rdtype = dns.rdatatype.RdataType.make(rdtype) - covers = dns.rdatatype.RdataType.make(covers) - node = self.find_node(name, create) - return node.find_rdataset(self.rdclass, rdtype, covers, create) - - def get_rdataset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - create: bool = False, - ) -> Optional[dns.rdataset.Rdataset]: - """Look for an rdataset with the specified name and type in the zone. - - This method is like ``find_rdataset()``, except it returns None instead - of raising an exception if the rdataset does not exist and creation - has not been requested. - - The rdataset returned is not a copy; changes to it will change - the zone. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdata type desired. - - *covers*, a ``dns.rdatatype.RdataType`` or ``str``, the covered type. - Usually this value is ``dns.rdatatype.NONE``, but if the - rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, - then the covers value will be the rdata type the SIG/RRSIG - covers. The library treats the SIG and RRSIG types as if they - were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). - This makes RRSIGs much easier to work with than if RRSIGs - covering different rdata types were aggregated into a single - RRSIG rdataset. - - *create*, a ``bool``. If true, the node will be created if it does - not exist. - - Raises ``KeyError`` if the name is not known and create was - not specified, or if the name was not a subdomain of the origin. - - Returns a ``dns.rdataset.Rdataset`` or ``None``. - """ - - try: - rdataset = self.find_rdataset(name, rdtype, covers, create) - except KeyError: - rdataset = None - return rdataset - - def delete_rdataset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - ) -> None: - """Delete the rdataset matching *rdtype* and *covers*, if it - exists at the node specified by *name*. - - It is not an error if the node does not exist, or if there is no matching - rdataset at the node. - - If the node has no rdatasets after the deletion, it will itself be deleted. - - *name*: the name of the node to find. The value may be a ``dns.name.Name`` or a - ``str``. If absolute, the name must be a subdomain of the zone's origin. If - ``zone.relativize`` is ``True``, then the name will be relativized. - - *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdata type desired. - - *covers*, a ``dns.rdatatype.RdataType`` or ``str`` or ``None``, the covered - type. Usually this value is ``dns.rdatatype.NONE``, but if the rdtype is - ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, then the covers value will be - the rdata type the SIG/RRSIG covers. The library treats the SIG and RRSIG types - as if they were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This - makes RRSIGs much easier to work with than if RRSIGs covering different rdata - types were aggregated into a single RRSIG rdataset. - """ - - name = self._validate_name(name) - rdtype = dns.rdatatype.RdataType.make(rdtype) - covers = dns.rdatatype.RdataType.make(covers) - node = self.get_node(name) - if node is not None: - node.delete_rdataset(self.rdclass, rdtype, covers) - if len(node) == 0: - self.delete_node(name) - - def replace_rdataset( - self, name: Union[dns.name.Name, str], replacement: dns.rdataset.Rdataset - ) -> None: - """Replace an rdataset at name. - - It is not an error if there is no rdataset matching I{replacement}. - - Ownership of the *replacement* object is transferred to the zone; - in other words, this method does not store a copy of *replacement* - at the node, it stores *replacement* itself. - - If the node does not exist, it is created. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - *replacement*, a ``dns.rdataset.Rdataset``, the replacement rdataset. - """ - - if replacement.rdclass != self.rdclass: - raise ValueError("replacement.rdclass != zone.rdclass") - node = self.find_node(name, True) - node.replace_rdataset(replacement) - - def find_rrset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - ) -> dns.rrset.RRset: - """Look for an rdataset with the specified name and type in the zone, - and return an RRset encapsulating it. - - This method is less efficient than the similar - ``find_rdataset()`` because it creates an RRset instead of - returning the matching rdataset. It may be more convenient - for some uses since it returns an object which binds the owner - name to the rdataset. - - This method may not be used to create new nodes or rdatasets; - use ``find_rdataset`` instead. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdata type desired. - - *covers*, a ``dns.rdatatype.RdataType`` or ``str``, the covered type. - Usually this value is ``dns.rdatatype.NONE``, but if the - rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, - then the covers value will be the rdata type the SIG/RRSIG - covers. The library treats the SIG and RRSIG types as if they - were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). - This makes RRSIGs much easier to work with than if RRSIGs - covering different rdata types were aggregated into a single - RRSIG rdataset. - - *create*, a ``bool``. If true, the node will be created if it does - not exist. - - Raises ``KeyError`` if the name is not known and create was - not specified, or if the name was not a subdomain of the origin. - - Returns a ``dns.rrset.RRset`` or ``None``. - """ - - vname = self._validate_name(name) - rdtype = dns.rdatatype.RdataType.make(rdtype) - covers = dns.rdatatype.RdataType.make(covers) - rdataset = self.nodes[vname].find_rdataset(self.rdclass, rdtype, covers) - rrset = dns.rrset.RRset(vname, self.rdclass, rdtype, covers) - rrset.update(rdataset) - return rrset - - def get_rrset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - ) -> Optional[dns.rrset.RRset]: - """Look for an rdataset with the specified name and type in the zone, - and return an RRset encapsulating it. - - This method is less efficient than the similar ``get_rdataset()`` - because it creates an RRset instead of returning the matching - rdataset. It may be more convenient for some uses since it - returns an object which binds the owner name to the rdataset. - - This method may not be used to create new nodes or rdatasets; - use ``get_rdataset()`` instead. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - *rdtype*, a ``dns.rdataset.Rdataset`` or ``str``, the rdata type desired. - - *covers*, a ``dns.rdataset.Rdataset`` or ``str``, the covered type. - Usually this value is ``dns.rdatatype.NONE``, but if the - rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, - then the covers value will be the rdata type the SIG/RRSIG - covers. The library treats the SIG and RRSIG types as if they - were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). - This makes RRSIGs much easier to work with than if RRSIGs - covering different rdata types were aggregated into a single - RRSIG rdataset. - - *create*, a ``bool``. If true, the node will be created if it does - not exist. - - Returns a ``dns.rrset.RRset`` or ``None``. - """ - - try: - rrset = self.find_rrset(name, rdtype, covers) - except KeyError: - rrset = None - return rrset - - def iterate_rdatasets( - self, - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.ANY, - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - ) -> Iterator[Tuple[dns.name.Name, dns.rdataset.Rdataset]]: - """Return a generator which yields (name, rdataset) tuples for - all rdatasets in the zone which have the specified *rdtype* - and *covers*. If *rdtype* is ``dns.rdatatype.ANY``, the default, - then all rdatasets will be matched. - - *rdtype*, a ``dns.rdataset.Rdataset`` or ``str``, the rdata type desired. - - *covers*, a ``dns.rdataset.Rdataset`` or ``str``, the covered type. - Usually this value is ``dns.rdatatype.NONE``, but if the - rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, - then the covers value will be the rdata type the SIG/RRSIG - covers. The library treats the SIG and RRSIG types as if they - were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). - This makes RRSIGs much easier to work with than if RRSIGs - covering different rdata types were aggregated into a single - RRSIG rdataset. - """ - - rdtype = dns.rdatatype.RdataType.make(rdtype) - covers = dns.rdatatype.RdataType.make(covers) - for name, node in self.items(): - for rds in node: - if rdtype == dns.rdatatype.ANY or ( - rds.rdtype == rdtype and rds.covers == covers - ): - yield (name, rds) - - def iterate_rdatas( - self, - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.ANY, - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - ) -> Iterator[Tuple[dns.name.Name, int, dns.rdata.Rdata]]: - """Return a generator which yields (name, ttl, rdata) tuples for - all rdatas in the zone which have the specified *rdtype* - and *covers*. If *rdtype* is ``dns.rdatatype.ANY``, the default, - then all rdatas will be matched. - - *rdtype*, a ``dns.rdataset.Rdataset`` or ``str``, the rdata type desired. - - *covers*, a ``dns.rdataset.Rdataset`` or ``str``, the covered type. - Usually this value is ``dns.rdatatype.NONE``, but if the - rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, - then the covers value will be the rdata type the SIG/RRSIG - covers. The library treats the SIG and RRSIG types as if they - were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). - This makes RRSIGs much easier to work with than if RRSIGs - covering different rdata types were aggregated into a single - RRSIG rdataset. - """ - - rdtype = dns.rdatatype.RdataType.make(rdtype) - covers = dns.rdatatype.RdataType.make(covers) - for name, node in self.items(): - for rds in node: - if rdtype == dns.rdatatype.ANY or ( - rds.rdtype == rdtype and rds.covers == covers - ): - for rdata in rds: - yield (name, rds.ttl, rdata) - - def to_file( - self, - f: Any, - sorted: bool = True, - relativize: bool = True, - nl: Optional[str] = None, - want_comments: bool = False, - want_origin: bool = False, - ) -> None: - """Write a zone to a file. - - *f*, a file or `str`. If *f* is a string, it is treated - as the name of a file to open. - - *sorted*, a ``bool``. If True, the default, then the file - will be written with the names sorted in DNSSEC order from - least to greatest. Otherwise the names will be written in - whatever order they happen to have in the zone's dictionary. - - *relativize*, a ``bool``. If True, the default, then domain - names in the output will be relativized to the zone's origin - if possible. - - *nl*, a ``str`` or None. The end of line string. If not - ``None``, the output will use the platform's native - end-of-line marker (i.e. LF on POSIX, CRLF on Windows). - - *want_comments*, a ``bool``. If ``True``, emit end-of-line comments - as part of writing the file. If ``False``, the default, do not - emit them. - - *want_origin*, a ``bool``. If ``True``, emit a $ORIGIN line at - the start of the file. If ``False``, the default, do not emit - one. - """ - - if isinstance(f, str): - cm: contextlib.AbstractContextManager = open(f, "wb") - else: - cm = contextlib.nullcontext(f) - with cm as f: - # must be in this way, f.encoding may contain None, or even - # attribute may not be there - file_enc = getattr(f, "encoding", None) - if file_enc is None: - file_enc = "utf-8" - - if nl is None: - # binary mode, '\n' is not enough - nl_b = os.linesep.encode(file_enc) - nl = "\n" - elif isinstance(nl, str): - nl_b = nl.encode(file_enc) - else: - nl_b = nl - nl = nl.decode() - - if want_origin: - assert self.origin is not None - l = "$ORIGIN " + self.origin.to_text() - l_b = l.encode(file_enc) - try: - f.write(l_b) - f.write(nl_b) - except TypeError: # textual mode - f.write(l) - f.write(nl) - - if sorted: - names = list(self.keys()) - names.sort() - else: - names = self.keys() - for n in names: - l = self[n].to_text( - n, - origin=self.origin, - relativize=relativize, - want_comments=want_comments, - ) - l_b = l.encode(file_enc) - - try: - f.write(l_b) - f.write(nl_b) - except TypeError: # textual mode - f.write(l) - f.write(nl) - - def to_text( - self, - sorted: bool = True, - relativize: bool = True, - nl: Optional[str] = None, - want_comments: bool = False, - want_origin: bool = False, - ) -> str: - """Return a zone's text as though it were written to a file. - - *sorted*, a ``bool``. If True, the default, then the file - will be written with the names sorted in DNSSEC order from - least to greatest. Otherwise the names will be written in - whatever order they happen to have in the zone's dictionary. - - *relativize*, a ``bool``. If True, the default, then domain - names in the output will be relativized to the zone's origin - if possible. - - *nl*, a ``str`` or None. The end of line string. If not - ``None``, the output will use the platform's native - end-of-line marker (i.e. LF on POSIX, CRLF on Windows). - - *want_comments*, a ``bool``. If ``True``, emit end-of-line comments - as part of writing the file. If ``False``, the default, do not - emit them. - - *want_origin*, a ``bool``. If ``True``, emit a $ORIGIN line at - the start of the output. If ``False``, the default, do not emit - one. - - Returns a ``str``. - """ - temp_buffer = io.StringIO() - self.to_file(temp_buffer, sorted, relativize, nl, want_comments, want_origin) - return_value = temp_buffer.getvalue() - temp_buffer.close() - return return_value - - def check_origin(self) -> None: - """Do some simple checking of the zone's origin. - - Raises ``dns.zone.NoSOA`` if there is no SOA RRset. - - Raises ``dns.zone.NoNS`` if there is no NS RRset. - - Raises ``KeyError`` if there is no origin node. - """ - if self.relativize: - name = dns.name.empty - else: - assert self.origin is not None - name = self.origin - if self.get_rdataset(name, dns.rdatatype.SOA) is None: - raise NoSOA - if self.get_rdataset(name, dns.rdatatype.NS) is None: - raise NoNS - - def get_soa( - self, txn: Optional[dns.transaction.Transaction] = None - ) -> dns.rdtypes.ANY.SOA.SOA: - """Get the zone SOA rdata. - - Raises ``dns.zone.NoSOA`` if there is no SOA RRset. - - Returns a ``dns.rdtypes.ANY.SOA.SOA`` Rdata. - """ - if self.relativize: - origin_name = dns.name.empty - else: - if self.origin is None: - # get_soa() has been called very early, and there must not be - # an SOA if there is no origin. - raise NoSOA - origin_name = self.origin - soa: Optional[dns.rdataset.Rdataset] - if txn: - soa = txn.get(origin_name, dns.rdatatype.SOA) - else: - soa = self.get_rdataset(origin_name, dns.rdatatype.SOA) - if soa is None: - raise NoSOA - return soa[0] - - def _compute_digest( - self, - hash_algorithm: DigestHashAlgorithm, - scheme: DigestScheme = DigestScheme.SIMPLE, - ) -> bytes: - hashinfo = _digest_hashers.get(hash_algorithm) - if not hashinfo: - raise UnsupportedDigestHashAlgorithm - if scheme != DigestScheme.SIMPLE: - raise UnsupportedDigestScheme - - if self.relativize: - origin_name = dns.name.empty - else: - assert self.origin is not None - origin_name = self.origin - hasher = hashinfo() - for name, node in sorted(self.items()): - rrnamebuf = name.to_digestable(self.origin) - for rdataset in sorted(node, key=lambda rds: (rds.rdtype, rds.covers)): - if name == origin_name and dns.rdatatype.ZONEMD in ( - rdataset.rdtype, - rdataset.covers, - ): - continue - rrfixed = struct.pack( - "!HHI", rdataset.rdtype, rdataset.rdclass, rdataset.ttl - ) - rdatas = [rdata.to_digestable(self.origin) for rdata in rdataset] - for rdata in sorted(rdatas): - rrlen = struct.pack("!H", len(rdata)) - hasher.update(rrnamebuf + rrfixed + rrlen + rdata) - return hasher.digest() - - def compute_digest( - self, - hash_algorithm: DigestHashAlgorithm, - scheme: DigestScheme = DigestScheme.SIMPLE, - ) -> dns.rdtypes.ANY.ZONEMD.ZONEMD: - serial = self.get_soa().serial - digest = self._compute_digest(hash_algorithm, scheme) - return dns.rdtypes.ANY.ZONEMD.ZONEMD( - self.rdclass, dns.rdatatype.ZONEMD, serial, scheme, hash_algorithm, digest - ) - - def verify_digest( - self, zonemd: Optional[dns.rdtypes.ANY.ZONEMD.ZONEMD] = None - ) -> None: - digests: Union[dns.rdataset.Rdataset, List[dns.rdtypes.ANY.ZONEMD.ZONEMD]] - if zonemd: - digests = [zonemd] - else: - assert self.origin is not None - rds = self.get_rdataset(self.origin, dns.rdatatype.ZONEMD) - if rds is None: - raise NoDigest - digests = rds - for digest in digests: - try: - computed = self._compute_digest(digest.hash_algorithm, digest.scheme) - if computed == digest.digest: - return - except Exception: - pass - raise DigestVerificationFailure - - # TransactionManager methods - - def reader(self) -> "Transaction": - return Transaction(self, False, Version(self, 1, self.nodes, self.origin)) - - def writer(self, replacement: bool = False) -> "Transaction": - txn = Transaction(self, replacement) - txn._setup_version() - return txn - - def origin_information( - self, - ) -> Tuple[Optional[dns.name.Name], bool, Optional[dns.name.Name]]: - effective: Optional[dns.name.Name] - if self.relativize: - effective = dns.name.empty - else: - effective = self.origin - return (self.origin, self.relativize, effective) - - def get_class(self): - return self.rdclass - - # Transaction methods - - def _end_read(self, txn): - pass - - def _end_write(self, txn): - pass - - def _commit_version(self, _, version, origin): - self.nodes = version.nodes - if self.origin is None: - self.origin = origin - - def _get_next_version_id(self): - # Versions are ephemeral and all have id 1 - return 1 - - -# These classes used to be in dns.versioned, but have moved here so we can use -# the copy-on-write transaction mechanism for both kinds of zones. In a -# regular zone, the version only exists during the transaction, and the nodes -# are regular dns.node.Nodes. - -# A node with a version id. - - -class VersionedNode(dns.node.Node): # lgtm[py/missing-equals] - __slots__ = ["id"] - - def __init__(self): - super().__init__() - # A proper id will get set by the Version - self.id = 0 - - -@dns.immutable.immutable -class ImmutableVersionedNode(VersionedNode): - def __init__(self, node): - super().__init__() - self.id = node.id - self.rdatasets = tuple( - [dns.rdataset.ImmutableRdataset(rds) for rds in node.rdatasets] - ) - - def find_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - create: bool = False, - ) -> dns.rdataset.Rdataset: - if create: - raise TypeError("immutable") - return super().find_rdataset(rdclass, rdtype, covers, False) - - def get_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - create: bool = False, - ) -> Optional[dns.rdataset.Rdataset]: - if create: - raise TypeError("immutable") - return super().get_rdataset(rdclass, rdtype, covers, False) - - def delete_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - ) -> None: - raise TypeError("immutable") - - def replace_rdataset(self, replacement: dns.rdataset.Rdataset) -> None: - raise TypeError("immutable") - - def is_immutable(self) -> bool: - return True - - -class Version: - def __init__( - self, - zone: Zone, - id: int, - nodes: Optional[MutableMapping[dns.name.Name, dns.node.Node]] = None, - origin: Optional[dns.name.Name] = None, - ): - self.zone = zone - self.id = id - if nodes is not None: - self.nodes = nodes - else: - self.nodes = zone.map_factory() - self.origin = origin - - def _validate_name(self, name: dns.name.Name) -> dns.name.Name: - return _validate_name(name, self.origin, self.zone.relativize) - - def get_node(self, name: dns.name.Name) -> Optional[dns.node.Node]: - name = self._validate_name(name) - return self.nodes.get(name) - - def get_rdataset( - self, - name: dns.name.Name, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType, - ) -> Optional[dns.rdataset.Rdataset]: - node = self.get_node(name) - if node is None: - return None - return node.get_rdataset(self.zone.rdclass, rdtype, covers) - - def keys(self): - return self.nodes.keys() - - def items(self): - return self.nodes.items() - - -class WritableVersion(Version): - def __init__(self, zone: Zone, replacement: bool = False): - # The zone._versions_lock must be held by our caller in a versioned - # zone. - id = zone._get_next_version_id() - super().__init__(zone, id) - if not replacement: - # We copy the map, because that gives us a simple and thread-safe - # way of doing versions, and we have a garbage collector to help - # us. We only make new node objects if we actually change the - # node. - self.nodes.update(zone.nodes) - # We have to copy the zone origin as it may be None in the first - # version, and we don't want to mutate the zone until we commit. - self.origin = zone.origin - self.changed: Set[dns.name.Name] = set() - - def _maybe_cow(self, name: dns.name.Name) -> dns.node.Node: - name = self._validate_name(name) - node = self.nodes.get(name) - if node is None or name not in self.changed: - new_node = self.zone.node_factory() - if hasattr(new_node, "id"): - # We keep doing this for backwards compatibility, as earlier - # code used new_node.id != self.id for the "do we need to CoW?" - # test. Now we use the changed set as this works with both - # regular zones and versioned zones. - # - # We ignore the mypy error as this is safe but it doesn't see it. - new_node.id = self.id # type: ignore - if node is not None: - # moo! copy on write! - new_node.rdatasets.extend(node.rdatasets) - self.nodes[name] = new_node - self.changed.add(name) - return new_node - else: - return node - - def delete_node(self, name: dns.name.Name) -> None: - name = self._validate_name(name) - if name in self.nodes: - del self.nodes[name] - self.changed.add(name) - - def put_rdataset( - self, name: dns.name.Name, rdataset: dns.rdataset.Rdataset - ) -> None: - node = self._maybe_cow(name) - node.replace_rdataset(rdataset) - - def delete_rdataset( - self, - name: dns.name.Name, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType, - ) -> None: - node = self._maybe_cow(name) - node.delete_rdataset(self.zone.rdclass, rdtype, covers) - if len(node) == 0: - del self.nodes[name] - - -@dns.immutable.immutable -class ImmutableVersion(Version): - def __init__(self, version: WritableVersion): - # We tell super() that it's a replacement as we don't want it - # to copy the nodes, as we're about to do that with an - # immutable Dict. - super().__init__(version.zone, True) - # set the right id! - self.id = version.id - # keep the origin - self.origin = version.origin - # Make changed nodes immutable - for name in version.changed: - node = version.nodes.get(name) - # it might not exist if we deleted it in the version - if node: - version.nodes[name] = ImmutableVersionedNode(node) - # We're changing the type of the nodes dictionary here on purpose, so - # we ignore the mypy error. - self.nodes = dns.immutable.Dict( - version.nodes, True, self.zone.map_factory - ) # type: ignore - - -class Transaction(dns.transaction.Transaction): - def __init__(self, zone, replacement, version=None, make_immutable=False): - read_only = version is not None - super().__init__(zone, replacement, read_only) - self.version = version - self.make_immutable = make_immutable - - @property - def zone(self): - return self.manager - - def _setup_version(self): - assert self.version is None - factory = self.manager.writable_version_factory - if factory is None: - factory = WritableVersion - self.version = factory(self.zone, self.replacement) - - def _get_rdataset(self, name, rdtype, covers): - return self.version.get_rdataset(name, rdtype, covers) - - def _put_rdataset(self, name, rdataset): - assert not self.read_only - self.version.put_rdataset(name, rdataset) - - def _delete_name(self, name): - assert not self.read_only - self.version.delete_node(name) - - def _delete_rdataset(self, name, rdtype, covers): - assert not self.read_only - self.version.delete_rdataset(name, rdtype, covers) - - def _name_exists(self, name): - return self.version.get_node(name) is not None - - def _changed(self): - if self.read_only: - return False - else: - return len(self.version.changed) > 0 - - def _end_transaction(self, commit): - if self.read_only: - self.zone._end_read(self) - elif commit and len(self.version.changed) > 0: - if self.make_immutable: - factory = self.manager.immutable_version_factory - if factory is None: - factory = ImmutableVersion - version = factory(self.version) - else: - version = self.version - self.zone._commit_version(self, version, self.version.origin) - else: - # rollback - self.zone._end_write(self) - - def _set_origin(self, origin): - if self.version.origin is None: - self.version.origin = origin - - def _iterate_rdatasets(self): - for name, node in self.version.items(): - for rdataset in node: - yield (name, rdataset) - - def _iterate_names(self): - return self.version.keys() - - def _get_node(self, name): - return self.version.get_node(name) - - def _origin_information(self): - (absolute, relativize, effective) = self.manager.origin_information() - if absolute is None and self.version.origin is not None: - # No origin has been committed yet, but we've learned one as part of - # this txn. Use it. - absolute = self.version.origin - if relativize: - effective = dns.name.empty - else: - effective = absolute - return (absolute, relativize, effective) - - -def _from_text( - text: Any, - origin: Optional[Union[dns.name.Name, str]] = None, - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - relativize: bool = True, - zone_factory: Any = Zone, - filename: Optional[str] = None, - allow_include: bool = False, - check_origin: bool = True, - idna_codec: Optional[dns.name.IDNACodec] = None, - allow_directives: Union[bool, Iterable[str]] = True, -) -> Zone: - # See the comments for the public APIs from_text() and from_file() for - # details. - - # 'text' can also be a file, but we don't publish that fact - # since it's an implementation detail. The official file - # interface is from_file(). - - if filename is None: - filename = "" - zone = zone_factory(origin, rdclass, relativize=relativize) - with zone.writer(True) as txn: - tok = dns.tokenizer.Tokenizer(text, filename, idna_codec=idna_codec) - reader = dns.zonefile.Reader( - tok, - rdclass, - txn, - allow_include=allow_include, - allow_directives=allow_directives, - ) - try: - reader.read() - except dns.zonefile.UnknownOrigin: - # for backwards compatibility - raise dns.zone.UnknownOrigin - # Now that we're done reading, do some basic checking of the zone. - if check_origin: - zone.check_origin() - return zone - - -def from_text( - text: str, - origin: Optional[Union[dns.name.Name, str]] = None, - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - relativize: bool = True, - zone_factory: Any = Zone, - filename: Optional[str] = None, - allow_include: bool = False, - check_origin: bool = True, - idna_codec: Optional[dns.name.IDNACodec] = None, - allow_directives: Union[bool, Iterable[str]] = True, -) -> Zone: - """Build a zone object from a zone file format string. - - *text*, a ``str``, the zone file format input. - - *origin*, a ``dns.name.Name``, a ``str``, or ``None``. The origin - of the zone; if not specified, the first ``$ORIGIN`` statement in the - zone file will determine the origin of the zone. - - *rdclass*, a ``dns.rdataclass.RdataClass``, the zone's rdata class; the default is - class IN. - - *relativize*, a ``bool``, determine's whether domain names are - relativized to the zone's origin. The default is ``True``. - - *zone_factory*, the zone factory to use or ``None``. If ``None``, then - ``dns.zone.Zone`` will be used. The value may be any class or callable - that returns a subclass of ``dns.zone.Zone``. - - *filename*, a ``str`` or ``None``, the filename to emit when - describing where an error occurred; the default is ``''``. - - *allow_include*, a ``bool``. If ``True``, the default, then ``$INCLUDE`` - directives are permitted. If ``False``, then encoutering a ``$INCLUDE`` - will raise a ``SyntaxError`` exception. - - *check_origin*, a ``bool``. If ``True``, the default, then sanity - checks of the origin node will be made by calling the zone's - ``check_origin()`` method. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - *allow_directives*, a ``bool`` or an iterable of `str`. If ``True``, the default, - then directives are permitted, and the *allow_include* parameter controls whether - ``$INCLUDE`` is permitted. If ``False`` or an empty iterable, then no directive - processing is done and any directive-like text will be treated as a regular owner - name. If a non-empty iterable, then only the listed directives (including the - ``$``) are allowed. - - Raises ``dns.zone.NoSOA`` if there is no SOA RRset. - - Raises ``dns.zone.NoNS`` if there is no NS RRset. - - Raises ``KeyError`` if there is no origin node. - - Returns a subclass of ``dns.zone.Zone``. - """ - return _from_text( - text, - origin, - rdclass, - relativize, - zone_factory, - filename, - allow_include, - check_origin, - idna_codec, - allow_directives, - ) - - -def from_file( - f: Any, - origin: Optional[Union[dns.name.Name, str]] = None, - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - relativize: bool = True, - zone_factory: Any = Zone, - filename: Optional[str] = None, - allow_include: bool = True, - check_origin: bool = True, - idna_codec: Optional[dns.name.IDNACodec] = None, - allow_directives: Union[bool, Iterable[str]] = True, -) -> Zone: - """Read a zone file and build a zone object. - - *f*, a file or ``str``. If *f* is a string, it is treated - as the name of a file to open. - - *origin*, a ``dns.name.Name``, a ``str``, or ``None``. The origin - of the zone; if not specified, the first ``$ORIGIN`` statement in the - zone file will determine the origin of the zone. - - *rdclass*, an ``int``, the zone's rdata class; the default is class IN. - - *relativize*, a ``bool``, determine's whether domain names are - relativized to the zone's origin. The default is ``True``. - - *zone_factory*, the zone factory to use or ``None``. If ``None``, then - ``dns.zone.Zone`` will be used. The value may be any class or callable - that returns a subclass of ``dns.zone.Zone``. - - *filename*, a ``str`` or ``None``, the filename to emit when - describing where an error occurred; the default is ``''``. - - *allow_include*, a ``bool``. If ``True``, the default, then ``$INCLUDE`` - directives are permitted. If ``False``, then encoutering a ``$INCLUDE`` - will raise a ``SyntaxError`` exception. - - *check_origin*, a ``bool``. If ``True``, the default, then sanity - checks of the origin node will be made by calling the zone's - ``check_origin()`` method. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - *allow_directives*, a ``bool`` or an iterable of `str`. If ``True``, the default, - then directives are permitted, and the *allow_include* parameter controls whether - ``$INCLUDE`` is permitted. If ``False`` or an empty iterable, then no directive - processing is done and any directive-like text will be treated as a regular owner - name. If a non-empty iterable, then only the listed directives (including the - ``$``) are allowed. - - Raises ``dns.zone.NoSOA`` if there is no SOA RRset. - - Raises ``dns.zone.NoNS`` if there is no NS RRset. - - Raises ``KeyError`` if there is no origin node. - - Returns a subclass of ``dns.zone.Zone``. - """ - - if isinstance(f, str): - if filename is None: - filename = f - cm: contextlib.AbstractContextManager = open(f) - else: - cm = contextlib.nullcontext(f) - with cm as f: - return _from_text( - f, - origin, - rdclass, - relativize, - zone_factory, - filename, - allow_include, - check_origin, - idna_codec, - allow_directives, - ) - assert False # make mypy happy lgtm[py/unreachable-statement] - - -def from_xfr( - xfr: Any, - zone_factory: Any = Zone, - relativize: bool = True, - check_origin: bool = True, -) -> Zone: - """Convert the output of a zone transfer generator into a zone object. - - *xfr*, a generator of ``dns.message.Message`` objects, typically - ``dns.query.xfr()``. - - *relativize*, a ``bool``, determine's whether domain names are - relativized to the zone's origin. The default is ``True``. - It is essential that the relativize setting matches the one specified - to the generator. - - *check_origin*, a ``bool``. If ``True``, the default, then sanity - checks of the origin node will be made by calling the zone's - ``check_origin()`` method. - - Raises ``dns.zone.NoSOA`` if there is no SOA RRset. - - Raises ``dns.zone.NoNS`` if there is no NS RRset. - - Raises ``KeyError`` if there is no origin node. - - Raises ``ValueError`` if no messages are yielded by the generator. - - Returns a subclass of ``dns.zone.Zone``. - """ - - z = None - for r in xfr: - if z is None: - if relativize: - origin = r.origin - else: - origin = r.answer[0].name - rdclass = r.answer[0].rdclass - z = zone_factory(origin, rdclass, relativize=relativize) - for rrset in r.answer: - znode = z.nodes.get(rrset.name) - if not znode: - znode = z.node_factory() - z.nodes[rrset.name] = znode - zrds = znode.find_rdataset(rrset.rdclass, rrset.rdtype, rrset.covers, True) - zrds.update_ttl(rrset.ttl) - for rd in rrset: - zrds.add(rd) - if z is None: - raise ValueError("empty transfer") - if check_origin: - z.check_origin() - return z diff --git a/venv/Lib/site-packages/dns/zonefile.py b/venv/Lib/site-packages/dns/zonefile.py deleted file mode 100644 index d74510b..0000000 --- a/venv/Lib/site-packages/dns/zonefile.py +++ /dev/null @@ -1,744 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Zones.""" - -import re -import sys -from typing import Any, Iterable, List, Optional, Set, Tuple, Union - -import dns.exception -import dns.grange -import dns.name -import dns.node -import dns.rdata -import dns.rdataclass -import dns.rdatatype -import dns.rdtypes.ANY.SOA -import dns.rrset -import dns.tokenizer -import dns.transaction -import dns.ttl - - -class UnknownOrigin(dns.exception.DNSException): - """Unknown origin""" - - -class CNAMEAndOtherData(dns.exception.DNSException): - """A node has a CNAME and other data""" - - -def _check_cname_and_other_data(txn, name, rdataset): - rdataset_kind = dns.node.NodeKind.classify_rdataset(rdataset) - node = txn.get_node(name) - if node is None: - # empty nodes are neutral. - return - node_kind = node.classify() - if ( - node_kind == dns.node.NodeKind.CNAME - and rdataset_kind == dns.node.NodeKind.REGULAR - ): - raise CNAMEAndOtherData("rdataset type is not compatible with a CNAME node") - elif ( - node_kind == dns.node.NodeKind.REGULAR - and rdataset_kind == dns.node.NodeKind.CNAME - ): - raise CNAMEAndOtherData( - "CNAME rdataset is not compatible with a regular data node" - ) - # Otherwise at least one of the node and the rdataset is neutral, so - # adding the rdataset is ok - - -SavedStateType = Tuple[ - dns.tokenizer.Tokenizer, - Optional[dns.name.Name], # current_origin - Optional[dns.name.Name], # last_name - Optional[Any], # current_file - int, # last_ttl - bool, # last_ttl_known - int, # default_ttl - bool, -] # default_ttl_known - - -def _upper_dollarize(s): - s = s.upper() - if not s.startswith("$"): - s = "$" + s - return s - - -class Reader: - """Read a DNS zone file into a transaction.""" - - def __init__( - self, - tok: dns.tokenizer.Tokenizer, - rdclass: dns.rdataclass.RdataClass, - txn: dns.transaction.Transaction, - allow_include: bool = False, - allow_directives: Union[bool, Iterable[str]] = True, - force_name: Optional[dns.name.Name] = None, - force_ttl: Optional[int] = None, - force_rdclass: Optional[dns.rdataclass.RdataClass] = None, - force_rdtype: Optional[dns.rdatatype.RdataType] = None, - default_ttl: Optional[int] = None, - ): - self.tok = tok - (self.zone_origin, self.relativize, _) = txn.manager.origin_information() - self.current_origin = self.zone_origin - self.last_ttl = 0 - self.last_ttl_known = False - if force_ttl is not None: - default_ttl = force_ttl - if default_ttl is None: - self.default_ttl = 0 - self.default_ttl_known = False - else: - self.default_ttl = default_ttl - self.default_ttl_known = True - self.last_name = self.current_origin - self.zone_rdclass = rdclass - self.txn = txn - self.saved_state: List[SavedStateType] = [] - self.current_file: Optional[Any] = None - self.allowed_directives: Set[str] - if allow_directives is True: - self.allowed_directives = {"$GENERATE", "$ORIGIN", "$TTL"} - if allow_include: - self.allowed_directives.add("$INCLUDE") - elif allow_directives is False: - # allow_include was ignored in earlier releases if allow_directives was - # False, so we continue that. - self.allowed_directives = set() - else: - # Note that if directives are explicitly specified, then allow_include - # is ignored. - self.allowed_directives = set(_upper_dollarize(d) for d in allow_directives) - self.force_name = force_name - self.force_ttl = force_ttl - self.force_rdclass = force_rdclass - self.force_rdtype = force_rdtype - self.txn.check_put_rdataset(_check_cname_and_other_data) - - def _eat_line(self): - while 1: - token = self.tok.get() - if token.is_eol_or_eof(): - break - - def _get_identifier(self): - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - return token - - def _rr_line(self): - """Process one line from a DNS zone file.""" - token = None - # Name - if self.force_name is not None: - name = self.force_name - else: - if self.current_origin is None: - raise UnknownOrigin - token = self.tok.get(want_leading=True) - if not token.is_whitespace(): - self.last_name = self.tok.as_name(token, self.current_origin) - else: - token = self.tok.get() - if token.is_eol_or_eof(): - # treat leading WS followed by EOL/EOF as if they were EOL/EOF. - return - self.tok.unget(token) - name = self.last_name - if not name.is_subdomain(self.zone_origin): - self._eat_line() - return - if self.relativize: - name = name.relativize(self.zone_origin) - - # TTL - if self.force_ttl is not None: - ttl = self.force_ttl - self.last_ttl = ttl - self.last_ttl_known = True - else: - token = self._get_identifier() - ttl = None - try: - ttl = dns.ttl.from_text(token.value) - self.last_ttl = ttl - self.last_ttl_known = True - token = None - except dns.ttl.BadTTL: - self.tok.unget(token) - - # Class - if self.force_rdclass is not None: - rdclass = self.force_rdclass - else: - token = self._get_identifier() - try: - rdclass = dns.rdataclass.from_text(token.value) - except dns.exception.SyntaxError: - raise - except Exception: - rdclass = self.zone_rdclass - self.tok.unget(token) - if rdclass != self.zone_rdclass: - raise dns.exception.SyntaxError("RR class is not zone's class") - - if ttl is None: - # support for syntax - token = self._get_identifier() - ttl = None - try: - ttl = dns.ttl.from_text(token.value) - self.last_ttl = ttl - self.last_ttl_known = True - token = None - except dns.ttl.BadTTL: - if self.default_ttl_known: - ttl = self.default_ttl - elif self.last_ttl_known: - ttl = self.last_ttl - self.tok.unget(token) - - # Type - if self.force_rdtype is not None: - rdtype = self.force_rdtype - else: - token = self._get_identifier() - try: - rdtype = dns.rdatatype.from_text(token.value) - except Exception: - raise dns.exception.SyntaxError(f"unknown rdatatype '{token.value}'") - - try: - rd = dns.rdata.from_text( - rdclass, - rdtype, - self.tok, - self.current_origin, - self.relativize, - self.zone_origin, - ) - except dns.exception.SyntaxError: - # Catch and reraise. - raise - except Exception: - # All exceptions that occur in the processing of rdata - # are treated as syntax errors. This is not strictly - # correct, but it is correct almost all of the time. - # We convert them to syntax errors so that we can emit - # helpful filename:line info. - (ty, va) = sys.exc_info()[:2] - raise dns.exception.SyntaxError(f"caught exception {str(ty)}: {str(va)}") - - if not self.default_ttl_known and rdtype == dns.rdatatype.SOA: - # The pre-RFC2308 and pre-BIND9 behavior inherits the zone default - # TTL from the SOA minttl if no $TTL statement is present before the - # SOA is parsed. - self.default_ttl = rd.minimum - self.default_ttl_known = True - if ttl is None: - # if we didn't have a TTL on the SOA, set it! - ttl = rd.minimum - - # TTL check. We had to wait until now to do this as the SOA RR's - # own TTL can be inferred from its minimum. - if ttl is None: - raise dns.exception.SyntaxError("Missing default TTL value") - - self.txn.add(name, ttl, rd) - - def _parse_modify(self, side: str) -> Tuple[str, str, int, int, str]: - # Here we catch everything in '{' '}' in a group so we can replace it - # with ''. - is_generate1 = re.compile(r"^.*\$({(\+|-?)(\d+),(\d+),(.)}).*$") - is_generate2 = re.compile(r"^.*\$({(\+|-?)(\d+)}).*$") - is_generate3 = re.compile(r"^.*\$({(\+|-?)(\d+),(\d+)}).*$") - # Sometimes there are modifiers in the hostname. These come after - # the dollar sign. They are in the form: ${offset[,width[,base]]}. - # Make names - mod = "" - sign = "+" - offset = "0" - width = "0" - base = "d" - g1 = is_generate1.match(side) - if g1: - mod, sign, offset, width, base = g1.groups() - if sign == "": - sign = "+" - else: - g2 = is_generate2.match(side) - if g2: - mod, sign, offset = g2.groups() - if sign == "": - sign = "+" - width = "0" - base = "d" - else: - g3 = is_generate3.match(side) - if g3: - mod, sign, offset, width = g3.groups() - if sign == "": - sign = "+" - base = "d" - - ioffset = int(offset) - iwidth = int(width) - - if sign not in ["+", "-"]: - raise dns.exception.SyntaxError(f"invalid offset sign {sign}") - if base not in ["d", "o", "x", "X", "n", "N"]: - raise dns.exception.SyntaxError(f"invalid type {base}") - - return mod, sign, ioffset, iwidth, base - - def _generate_line(self): - # range lhs [ttl] [class] type rhs [ comment ] - """Process one line containing the GENERATE statement from a DNS - zone file.""" - if self.current_origin is None: - raise UnknownOrigin - - token = self.tok.get() - # Range (required) - try: - start, stop, step = dns.grange.from_text(token.value) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except Exception: - raise dns.exception.SyntaxError - - # lhs (required) - try: - lhs = token.value - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except Exception: - raise dns.exception.SyntaxError - - # TTL - try: - ttl = dns.ttl.from_text(token.value) - self.last_ttl = ttl - self.last_ttl_known = True - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except dns.ttl.BadTTL: - if not (self.last_ttl_known or self.default_ttl_known): - raise dns.exception.SyntaxError("Missing default TTL value") - if self.default_ttl_known: - ttl = self.default_ttl - elif self.last_ttl_known: - ttl = self.last_ttl - # Class - try: - rdclass = dns.rdataclass.from_text(token.value) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except dns.exception.SyntaxError: - raise dns.exception.SyntaxError - except Exception: - rdclass = self.zone_rdclass - if rdclass != self.zone_rdclass: - raise dns.exception.SyntaxError("RR class is not zone's class") - # Type - try: - rdtype = dns.rdatatype.from_text(token.value) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except Exception: - raise dns.exception.SyntaxError(f"unknown rdatatype '{token.value}'") - - # rhs (required) - rhs = token.value - - def _calculate_index(counter: int, offset_sign: str, offset: int) -> int: - """Calculate the index from the counter and offset.""" - if offset_sign == "-": - offset *= -1 - return counter + offset - - def _format_index(index: int, base: str, width: int) -> str: - """Format the index with the given base, and zero-fill it - to the given width.""" - if base in ["d", "o", "x", "X"]: - return format(index, base).zfill(width) - - # base can only be n or N here - hexa = _format_index(index, "x", width) - nibbles = ".".join(hexa[::-1])[:width] - if base == "N": - nibbles = nibbles.upper() - return nibbles - - lmod, lsign, loffset, lwidth, lbase = self._parse_modify(lhs) - rmod, rsign, roffset, rwidth, rbase = self._parse_modify(rhs) - for i in range(start, stop + 1, step): - # +1 because bind is inclusive and python is exclusive - - lindex = _calculate_index(i, lsign, loffset) - rindex = _calculate_index(i, rsign, roffset) - - lzfindex = _format_index(lindex, lbase, lwidth) - rzfindex = _format_index(rindex, rbase, rwidth) - - name = lhs.replace(f"${lmod}", lzfindex) - rdata = rhs.replace(f"${rmod}", rzfindex) - - self.last_name = dns.name.from_text( - name, self.current_origin, self.tok.idna_codec - ) - name = self.last_name - if not name.is_subdomain(self.zone_origin): - self._eat_line() - return - if self.relativize: - name = name.relativize(self.zone_origin) - - try: - rd = dns.rdata.from_text( - rdclass, - rdtype, - rdata, - self.current_origin, - self.relativize, - self.zone_origin, - ) - except dns.exception.SyntaxError: - # Catch and reraise. - raise - except Exception: - # All exceptions that occur in the processing of rdata - # are treated as syntax errors. This is not strictly - # correct, but it is correct almost all of the time. - # We convert them to syntax errors so that we can emit - # helpful filename:line info. - (ty, va) = sys.exc_info()[:2] - raise dns.exception.SyntaxError( - f"caught exception {str(ty)}: {str(va)}" - ) - - self.txn.add(name, ttl, rd) - - def read(self) -> None: - """Read a DNS zone file and build a zone object. - - @raises dns.zone.NoSOA: No SOA RR was found at the zone origin - @raises dns.zone.NoNS: No NS RRset was found at the zone origin - """ - - try: - while 1: - token = self.tok.get(True, True) - if token.is_eof(): - if self.current_file is not None: - self.current_file.close() - if len(self.saved_state) > 0: - ( - self.tok, - self.current_origin, - self.last_name, - self.current_file, - self.last_ttl, - self.last_ttl_known, - self.default_ttl, - self.default_ttl_known, - ) = self.saved_state.pop(-1) - continue - break - elif token.is_eol(): - continue - elif token.is_comment(): - self.tok.get_eol() - continue - elif token.value[0] == "$" and len(self.allowed_directives) > 0: - # Note that we only run directive processing code if at least - # one directive is allowed in order to be backwards compatible - c = token.value.upper() - if c not in self.allowed_directives: - raise dns.exception.SyntaxError( - f"zone file directive '{c}' is not allowed" - ) - if c == "$TTL": - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError("bad $TTL") - self.default_ttl = dns.ttl.from_text(token.value) - self.default_ttl_known = True - self.tok.get_eol() - elif c == "$ORIGIN": - self.current_origin = self.tok.get_name() - self.tok.get_eol() - if self.zone_origin is None: - self.zone_origin = self.current_origin - self.txn._set_origin(self.current_origin) - elif c == "$INCLUDE": - token = self.tok.get() - filename = token.value - token = self.tok.get() - new_origin: Optional[dns.name.Name] - if token.is_identifier(): - new_origin = dns.name.from_text( - token.value, self.current_origin, self.tok.idna_codec - ) - self.tok.get_eol() - elif not token.is_eol_or_eof(): - raise dns.exception.SyntaxError("bad origin in $INCLUDE") - else: - new_origin = self.current_origin - self.saved_state.append( - ( - self.tok, - self.current_origin, - self.last_name, - self.current_file, - self.last_ttl, - self.last_ttl_known, - self.default_ttl, - self.default_ttl_known, - ) - ) - self.current_file = open(filename) - self.tok = dns.tokenizer.Tokenizer(self.current_file, filename) - self.current_origin = new_origin - elif c == "$GENERATE": - self._generate_line() - else: - raise dns.exception.SyntaxError( - f"Unknown zone file directive '{c}'" - ) - continue - self.tok.unget(token) - self._rr_line() - except dns.exception.SyntaxError as detail: - (filename, line_number) = self.tok.where() - if detail is None: - detail = "syntax error" - ex = dns.exception.SyntaxError( - "%s:%d: %s" % (filename, line_number, detail) - ) - tb = sys.exc_info()[2] - raise ex.with_traceback(tb) from None - - -class RRsetsReaderTransaction(dns.transaction.Transaction): - def __init__(self, manager, replacement, read_only): - assert not read_only - super().__init__(manager, replacement, read_only) - self.rdatasets = {} - - def _get_rdataset(self, name, rdtype, covers): - return self.rdatasets.get((name, rdtype, covers)) - - def _get_node(self, name): - rdatasets = [] - for (rdataset_name, _, _), rdataset in self.rdatasets.items(): - if name == rdataset_name: - rdatasets.append(rdataset) - if len(rdatasets) == 0: - return None - node = dns.node.Node() - node.rdatasets = rdatasets - return node - - def _put_rdataset(self, name, rdataset): - self.rdatasets[(name, rdataset.rdtype, rdataset.covers)] = rdataset - - def _delete_name(self, name): - # First remove any changes involving the name - remove = [] - for key in self.rdatasets: - if key[0] == name: - remove.append(key) - if len(remove) > 0: - for key in remove: - del self.rdatasets[key] - - def _delete_rdataset(self, name, rdtype, covers): - try: - del self.rdatasets[(name, rdtype, covers)] - except KeyError: - pass - - def _name_exists(self, name): - for n, _, _ in self.rdatasets: - if n == name: - return True - return False - - def _changed(self): - return len(self.rdatasets) > 0 - - def _end_transaction(self, commit): - if commit and self._changed(): - rrsets = [] - for (name, _, _), rdataset in self.rdatasets.items(): - rrset = dns.rrset.RRset( - name, rdataset.rdclass, rdataset.rdtype, rdataset.covers - ) - rrset.update(rdataset) - rrsets.append(rrset) - self.manager.set_rrsets(rrsets) - - def _set_origin(self, origin): - pass - - def _iterate_rdatasets(self): - raise NotImplementedError # pragma: no cover - - def _iterate_names(self): - raise NotImplementedError # pragma: no cover - - -class RRSetsReaderManager(dns.transaction.TransactionManager): - def __init__( - self, origin=dns.name.root, relativize=False, rdclass=dns.rdataclass.IN - ): - self.origin = origin - self.relativize = relativize - self.rdclass = rdclass - self.rrsets = [] - - def reader(self): # pragma: no cover - raise NotImplementedError - - def writer(self, replacement=False): - assert replacement is True - return RRsetsReaderTransaction(self, True, False) - - def get_class(self): - return self.rdclass - - def origin_information(self): - if self.relativize: - effective = dns.name.empty - else: - effective = self.origin - return (self.origin, self.relativize, effective) - - def set_rrsets(self, rrsets): - self.rrsets = rrsets - - -def read_rrsets( - text: Any, - name: Optional[Union[dns.name.Name, str]] = None, - ttl: Optional[int] = None, - rdclass: Optional[Union[dns.rdataclass.RdataClass, str]] = dns.rdataclass.IN, - default_rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - rdtype: Optional[Union[dns.rdatatype.RdataType, str]] = None, - default_ttl: Optional[Union[int, str]] = None, - idna_codec: Optional[dns.name.IDNACodec] = None, - origin: Optional[Union[dns.name.Name, str]] = dns.name.root, - relativize: bool = False, -) -> List[dns.rrset.RRset]: - """Read one or more rrsets from the specified text, possibly subject - to restrictions. - - *text*, a file object or a string, is the input to process. - - *name*, a string, ``dns.name.Name``, or ``None``, is the owner name of - the rrset. If not ``None``, then the owner name is "forced", and the - input must not specify an owner name. If ``None``, then any owner names - are allowed and must be present in the input. - - *ttl*, an ``int``, string, or None. If not ``None``, the the TTL is - forced to be the specified value and the input must not specify a TTL. - If ``None``, then a TTL may be specified in the input. If it is not - specified, then the *default_ttl* will be used. - - *rdclass*, a ``dns.rdataclass.RdataClass``, string, or ``None``. If - not ``None``, then the class is forced to the specified value, and the - input must not specify a class. If ``None``, then the input may specify - a class that matches *default_rdclass*. Note that it is not possible to - return rrsets with differing classes; specifying ``None`` for the class - simply allows the user to optionally type a class as that may be convenient - when cutting and pasting. - - *default_rdclass*, a ``dns.rdataclass.RdataClass`` or string. The class - of the returned rrsets. - - *rdtype*, a ``dns.rdatatype.RdataType``, string, or ``None``. If not - ``None``, then the type is forced to the specified value, and the - input must not specify a type. If ``None``, then a type must be present - for each RR. - - *default_ttl*, an ``int``, string, or ``None``. If not ``None``, then if - the TTL is not forced and is not specified, then this value will be used. - if ``None``, then if the TTL is not forced an error will occur if the TTL - is not specified. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. Note that codecs only apply to the owner name; dnspython does - not do IDNA for names in rdata, as there is no IDNA zonefile format. - - *origin*, a string, ``dns.name.Name``, or ``None``, is the origin for any - relative names in the input, and also the origin to relativize to if - *relativize* is ``True``. - - *relativize*, a bool. If ``True``, names are relativized to the *origin*; - if ``False`` then any relative names in the input are made absolute by - appending the *origin*. - """ - if isinstance(origin, str): - origin = dns.name.from_text(origin, dns.name.root, idna_codec) - if isinstance(name, str): - name = dns.name.from_text(name, origin, idna_codec) - if isinstance(ttl, str): - ttl = dns.ttl.from_text(ttl) - if isinstance(default_ttl, str): - default_ttl = dns.ttl.from_text(default_ttl) - if rdclass is not None: - rdclass = dns.rdataclass.RdataClass.make(rdclass) - else: - rdclass = None - default_rdclass = dns.rdataclass.RdataClass.make(default_rdclass) - if rdtype is not None: - rdtype = dns.rdatatype.RdataType.make(rdtype) - else: - rdtype = None - manager = RRSetsReaderManager(origin, relativize, default_rdclass) - with manager.writer(True) as txn: - tok = dns.tokenizer.Tokenizer(text, "", idna_codec=idna_codec) - reader = Reader( - tok, - default_rdclass, - txn, - allow_directives=False, - force_name=name, - force_ttl=ttl, - force_rdclass=rdclass, - force_rdtype=rdtype, - default_ttl=default_ttl, - ) - reader.read() - return manager.rrsets diff --git a/venv/Lib/site-packages/dns/zonetypes.py b/venv/Lib/site-packages/dns/zonetypes.py deleted file mode 100644 index 195ee2e..0000000 --- a/venv/Lib/site-packages/dns/zonetypes.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -"""Common zone-related types.""" - -# This is a separate file to avoid import circularity between dns.zone and -# the implementation of the ZONEMD type. - -import hashlib - -import dns.enum - - -class DigestScheme(dns.enum.IntEnum): - """ZONEMD Scheme""" - - SIMPLE = 1 - - @classmethod - def _maximum(cls): - return 255 - - -class DigestHashAlgorithm(dns.enum.IntEnum): - """ZONEMD Hash Algorithm""" - - SHA384 = 1 - SHA512 = 2 - - @classmethod - def _maximum(cls): - return 255 - - -_digest_hashers = { - DigestHashAlgorithm.SHA384: hashlib.sha384, - DigestHashAlgorithm.SHA512: hashlib.sha512, -} diff --git a/venv/Lib/site-packages/dnspython-2.7.0.dist-info/INSTALLER b/venv/Lib/site-packages/dnspython-2.7.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/Lib/site-packages/dnspython-2.7.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/Lib/site-packages/dnspython-2.7.0.dist-info/METADATA b/venv/Lib/site-packages/dnspython-2.7.0.dist-info/METADATA deleted file mode 100644 index ca4a4f4..0000000 --- a/venv/Lib/site-packages/dnspython-2.7.0.dist-info/METADATA +++ /dev/null @@ -1,149 +0,0 @@ -Metadata-Version: 2.3 -Name: dnspython -Version: 2.7.0 -Summary: DNS toolkit -Project-URL: homepage, https://www.dnspython.org -Project-URL: repository, https://github.com/rthalley/dnspython.git -Project-URL: documentation, https://dnspython.readthedocs.io/en/stable/ -Project-URL: issues, https://github.com/rthalley/dnspython/issues -Author-email: Bob Halley -License: ISC -License-File: LICENSE -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: System Administrators -Classifier: License :: OSI Approved :: ISC License (ISCL) -Classifier: Operating System :: Microsoft :: Windows -Classifier: Operating System :: POSIX -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Topic :: Internet :: Name Service (DNS) -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Requires-Python: >=3.9 -Provides-Extra: dev -Requires-Dist: black>=23.1.0; extra == 'dev' -Requires-Dist: coverage>=7.0; extra == 'dev' -Requires-Dist: flake8>=7; extra == 'dev' -Requires-Dist: hypercorn>=0.16.0; extra == 'dev' -Requires-Dist: mypy>=1.8; extra == 'dev' -Requires-Dist: pylint>=3; extra == 'dev' -Requires-Dist: pytest-cov>=4.1.0; extra == 'dev' -Requires-Dist: pytest>=7.4; extra == 'dev' -Requires-Dist: quart-trio>=0.11.0; extra == 'dev' -Requires-Dist: sphinx-rtd-theme>=2.0.0; extra == 'dev' -Requires-Dist: sphinx>=7.2.0; extra == 'dev' -Requires-Dist: twine>=4.0.0; extra == 'dev' -Requires-Dist: wheel>=0.42.0; extra == 'dev' -Provides-Extra: dnssec -Requires-Dist: cryptography>=43; extra == 'dnssec' -Provides-Extra: doh -Requires-Dist: h2>=4.1.0; extra == 'doh' -Requires-Dist: httpcore>=1.0.0; extra == 'doh' -Requires-Dist: httpx>=0.26.0; extra == 'doh' -Provides-Extra: doq -Requires-Dist: aioquic>=1.0.0; extra == 'doq' -Provides-Extra: idna -Requires-Dist: idna>=3.7; extra == 'idna' -Provides-Extra: trio -Requires-Dist: trio>=0.23; extra == 'trio' -Provides-Extra: wmi -Requires-Dist: wmi>=1.5.1; extra == 'wmi' -Description-Content-Type: text/markdown - -# dnspython - -[![Build Status](https://github.com/rthalley/dnspython/actions/workflows/ci.yml/badge.svg)](https://github.com/rthalley/dnspython/actions/) -[![Documentation Status](https://readthedocs.org/projects/dnspython/badge/?version=latest)](https://dnspython.readthedocs.io/en/latest/?badge=latest) -[![PyPI version](https://badge.fury.io/py/dnspython.svg)](https://badge.fury.io/py/dnspython) -[![License: ISC](https://img.shields.io/badge/License-ISC-brightgreen.svg)](https://opensource.org/licenses/ISC) -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) - -## INTRODUCTION - -dnspython is a DNS toolkit for Python. It supports almost all record types. It -can be used for queries, zone transfers, and dynamic updates. It supports TSIG -authenticated messages and EDNS0. - -dnspython provides both high and low level access to DNS. The high level classes -perform queries for data of a given name, type, and class, and return an answer -set. The low level classes allow direct manipulation of DNS zones, messages, -names, and records. - -To see a few of the ways dnspython can be used, look in the `examples/` -directory. - -dnspython is a utility to work with DNS, `/etc/hosts` is thus not used. For -simple forward DNS lookups, it's better to use `socket.getaddrinfo()` or -`socket.gethostbyname()`. - -dnspython originated at Nominum where it was developed -to facilitate the testing of DNS software. - -## ABOUT THIS RELEASE - -This is dnspython 2.7.0. -Please read -[What's New](https://dnspython.readthedocs.io/en/stable/whatsnew.html) for -information about the changes in this release. - -## INSTALLATION - -* Many distributions have dnspython packaged for you, so you should - check there first. -* To use a wheel downloaded from PyPi, run: - - pip install dnspython - -* To install from the source code, go into the top-level of the source code - and run: - -``` - pip install --upgrade pip build - python -m build - pip install dist/*.whl -``` - -* To install the latest from the main branch, run `pip install git+https://github.com/rthalley/dnspython.git` - -Dnspython's default installation does not depend on any modules other than -those in the Python standard library. To use some features, additional modules -must be installed. For convenience, pip options are defined for the -requirements. - -If you want to use DNS-over-HTTPS, run -`pip install dnspython[doh]`. - -If you want to use DNSSEC functionality, run -`pip install dnspython[dnssec]`. - -If you want to use internationalized domain names (IDNA) -functionality, you must run -`pip install dnspython[idna]` - -If you want to use the Trio asynchronous I/O package, run -`pip install dnspython[trio]`. - -If you want to use WMI on Windows to determine the active DNS settings -instead of the default registry scanning method, run -`pip install dnspython[wmi]`. - -If you want to try the experimental DNS-over-QUIC code, run -`pip install dnspython[doq]`. - -Note that you can install any combination of the above, e.g.: -`pip install dnspython[doh,dnssec,idna]` - -### Notices - -Python 2.x support ended with the release of 1.16.0. Dnspython 2.6.x supports -Python 3.8 and later, though support for 3.8 ends on October 14, 2024. -Dnspython 2.7.x supports Python 3.9 and later. Future support is aligned with the -lifetime of the Python 3 versions. - -Documentation has moved to -[dnspython.readthedocs.io](https://dnspython.readthedocs.io). diff --git a/venv/Lib/site-packages/dnspython-2.7.0.dist-info/RECORD b/venv/Lib/site-packages/dnspython-2.7.0.dist-info/RECORD deleted file mode 100644 index 6794b95..0000000 --- a/venv/Lib/site-packages/dnspython-2.7.0.dist-info/RECORD +++ /dev/null @@ -1,294 +0,0 @@ -dns/__init__.py,sha256=YJZtDG14Idw5ui3h1nWooSwPM9gsxQgB8M0GBZ3aly0,1663 -dns/__pycache__/__init__.cpython-310.pyc,, -dns/__pycache__/_asyncbackend.cpython-310.pyc,, -dns/__pycache__/_asyncio_backend.cpython-310.pyc,, -dns/__pycache__/_ddr.cpython-310.pyc,, -dns/__pycache__/_features.cpython-310.pyc,, -dns/__pycache__/_immutable_ctx.cpython-310.pyc,, -dns/__pycache__/_trio_backend.cpython-310.pyc,, -dns/__pycache__/asyncbackend.cpython-310.pyc,, -dns/__pycache__/asyncquery.cpython-310.pyc,, -dns/__pycache__/asyncresolver.cpython-310.pyc,, -dns/__pycache__/dnssec.cpython-310.pyc,, -dns/__pycache__/dnssectypes.cpython-310.pyc,, -dns/__pycache__/e164.cpython-310.pyc,, -dns/__pycache__/edns.cpython-310.pyc,, -dns/__pycache__/entropy.cpython-310.pyc,, -dns/__pycache__/enum.cpython-310.pyc,, -dns/__pycache__/exception.cpython-310.pyc,, -dns/__pycache__/flags.cpython-310.pyc,, -dns/__pycache__/grange.cpython-310.pyc,, -dns/__pycache__/immutable.cpython-310.pyc,, -dns/__pycache__/inet.cpython-310.pyc,, -dns/__pycache__/ipv4.cpython-310.pyc,, -dns/__pycache__/ipv6.cpython-310.pyc,, -dns/__pycache__/message.cpython-310.pyc,, -dns/__pycache__/name.cpython-310.pyc,, -dns/__pycache__/namedict.cpython-310.pyc,, -dns/__pycache__/nameserver.cpython-310.pyc,, -dns/__pycache__/node.cpython-310.pyc,, -dns/__pycache__/opcode.cpython-310.pyc,, -dns/__pycache__/query.cpython-310.pyc,, -dns/__pycache__/rcode.cpython-310.pyc,, -dns/__pycache__/rdata.cpython-310.pyc,, -dns/__pycache__/rdataclass.cpython-310.pyc,, -dns/__pycache__/rdataset.cpython-310.pyc,, -dns/__pycache__/rdatatype.cpython-310.pyc,, -dns/__pycache__/renderer.cpython-310.pyc,, -dns/__pycache__/resolver.cpython-310.pyc,, -dns/__pycache__/reversename.cpython-310.pyc,, -dns/__pycache__/rrset.cpython-310.pyc,, -dns/__pycache__/serial.cpython-310.pyc,, -dns/__pycache__/set.cpython-310.pyc,, -dns/__pycache__/tokenizer.cpython-310.pyc,, -dns/__pycache__/transaction.cpython-310.pyc,, -dns/__pycache__/tsig.cpython-310.pyc,, -dns/__pycache__/tsigkeyring.cpython-310.pyc,, -dns/__pycache__/ttl.cpython-310.pyc,, -dns/__pycache__/update.cpython-310.pyc,, -dns/__pycache__/version.cpython-310.pyc,, -dns/__pycache__/versioned.cpython-310.pyc,, -dns/__pycache__/win32util.cpython-310.pyc,, -dns/__pycache__/wire.cpython-310.pyc,, -dns/__pycache__/xfr.cpython-310.pyc,, -dns/__pycache__/zone.cpython-310.pyc,, -dns/__pycache__/zonefile.cpython-310.pyc,, -dns/__pycache__/zonetypes.cpython-310.pyc,, -dns/_asyncbackend.py,sha256=pamIAWJ73e7ic2u7Q3RJyG6_6L8t78ccttvi65682MM,2396 -dns/_asyncio_backend.py,sha256=iLqhcUXqnFWC_2tcAp9U00NOGxT5GKPn4qeXS4iKaro,9051 -dns/_ddr.py,sha256=rHXKC8kncCTT9N4KBh1flicl79nyDjQ-DDvq30MJ3B8,5247 -dns/_features.py,sha256=Ig_leAKUT9RDiOVOfA0nXmmqpiPfnOnP9TcxlISUGSk,2492 -dns/_immutable_ctx.py,sha256=gtoCLMmdHXI23zt5lRSIS3A4Ca3jZJngebdoFFOtiwU,2459 -dns/_trio_backend.py,sha256=IXNdUP1MUBPyZRgAFhGH71KHtUCh3Rm5dM8SX4bMj2U,8473 -dns/asyncbackend.py,sha256=82fXTFls_m7F_ekQbgUGOkoBbs4BI-GBLDZAWNGUvJ0,2796 -dns/asyncquery.py,sha256=PMZ_D4Z8vgSioWHftyxNw7eax1IqrPleqY5FIi40hd8,30821 -dns/asyncresolver.py,sha256=GD86dCyW9YGKs6SggWXwBKEXifW7Qdx4cEAGFKY6fA4,17852 -dns/dnssec.py,sha256=gXmIrbKK1t1hE8ht-WlhUc0giy1PpLYj07r6o0pVATY,41717 -dns/dnssecalgs/__init__.py,sha256=OWvTadxZ3oF5PxVGodNimxBt_-3YUNTOSV62HrIb4PQ,4331 -dns/dnssecalgs/__pycache__/__init__.cpython-310.pyc,, -dns/dnssecalgs/__pycache__/base.cpython-310.pyc,, -dns/dnssecalgs/__pycache__/cryptography.cpython-310.pyc,, -dns/dnssecalgs/__pycache__/dsa.cpython-310.pyc,, -dns/dnssecalgs/__pycache__/ecdsa.cpython-310.pyc,, -dns/dnssecalgs/__pycache__/eddsa.cpython-310.pyc,, -dns/dnssecalgs/__pycache__/rsa.cpython-310.pyc,, -dns/dnssecalgs/base.py,sha256=jlTV_nd1Nqkvqyf-FVHIccXKFrE2LL6GVu6AW8QUh2E,2513 -dns/dnssecalgs/cryptography.py,sha256=3uqMfRm-zCkJPOrxUqlu9CmdxIMy71dVor9eAHi0wZM,2425 -dns/dnssecalgs/dsa.py,sha256=DNO68g_lbG7_oKcDN8c2xuzYRPbLaZc9Ns7oQoa0Vbc,3564 -dns/dnssecalgs/ecdsa.py,sha256=RfvFKRNExsYgd5SoXXRxMHkoBeF2Gktkz2rOwObEYAY,3172 -dns/dnssecalgs/eddsa.py,sha256=7VGARpVUzIYRjPh0gFapTPFzmsK8WJDqDZDLw2KLc8w,1981 -dns/dnssecalgs/rsa.py,sha256=_tNABpr6iwd8STBEHYIXfyLrgBpRNCj8K0UQj32_kOU,3622 -dns/dnssectypes.py,sha256=CyeuGTS_rM3zXr8wD9qMT9jkzvVfTY2JWckUcogG83E,1799 -dns/e164.py,sha256=EsK8cnOtOx7kQ0DmSwibcwkzp6efMWjbRiTyHZO8Q-M,3978 -dns/edns.py,sha256=-XDhC2jr7BRLsJrpCAWShxLn-3eG1oI0HhduWhLxdMw,17089 -dns/entropy.py,sha256=qkG8hXDLzrJS6R5My26iA59c0RhPwJNzuOhOCAZU5Bw,4242 -dns/enum.py,sha256=EepaunPKixTSrascy7iAe9UQEXXxP_MB5Gx4jUpHIhg,3691 -dns/exception.py,sha256=8vjxLf4T3T77vfANe_iKVeButAEhSJve6UrPjiBzht4,5953 -dns/flags.py,sha256=cQ3kTFyvcKiWHAxI5AwchNqxVOrsIrgJ6brgrH42Wq8,2750 -dns/grange.py,sha256=D016OrOv3i44G3mb_CzPFjDk61uZ6BMRib3yJnDQvbw,2144 -dns/immutable.py,sha256=InrtpKvPxl-74oYbzsyneZwAuX78hUqeG22f2aniZbk,2017 -dns/inet.py,sha256=j6jQs3K_ehVhDv-i4jwCKePr5HpEiSzvOXQ4uhgn1sU,5772 -dns/ipv4.py,sha256=qEUXtlqWDH_blicj6VMvyQhfX7-BF0gB_lWJliV-2FI,2552 -dns/ipv6.py,sha256=Ww8ayshM6FxtQsRYdXXuKkPFTad5ZcGbBd9lr1nFct4,6554 -dns/message.py,sha256=QOtdFBEAORhTKN0uQg86uSNvthdxJx40HhMQXYCBHng,68185 -dns/name.py,sha256=Bf3170QHhLFLDnMsWeJyik4i9ucBDbIY6Bydcz8H-2o,42778 -dns/namedict.py,sha256=hJRYpKeQv6Bd2LaUOPV0L_a0eXEIuqgggPXaH4c3Tow,4000 -dns/nameserver.py,sha256=hH4LLOkB4jeyO3VDUWK0lNpMJNNt_cFYf23-HdhpSmE,10115 -dns/node.py,sha256=NGZa0AUMq-CNledJ6wn1Rx6TFYc703cH2OraLysoNWM,12663 -dns/opcode.py,sha256=I6JyuFUL0msja_BYm6bzXHfbbfqUod_69Ss4xcv8xWQ,2730 -dns/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -dns/query.py,sha256=_Ev7EivZNEpgrUiPIn4BVnDRFCizcayHHcBXt0Ju3As,56298 -dns/quic/__init__.py,sha256=S5_2UuYzSU_LLtrLAf8DHh3KqNF2YHeKJ_-Wv991WlI,2272 -dns/quic/__pycache__/__init__.cpython-310.pyc,, -dns/quic/__pycache__/_asyncio.cpython-310.pyc,, -dns/quic/__pycache__/_common.cpython-310.pyc,, -dns/quic/__pycache__/_sync.cpython-310.pyc,, -dns/quic/__pycache__/_trio.cpython-310.pyc,, -dns/quic/_asyncio.py,sha256=dnABPz5f-JOJsA7D_BdPfuyzpkL_87AaY4CUcmgNj-g,9870 -dns/quic/_common.py,sha256=koWf6rq9_gUorIOV60QZKAHwZF5MuSgQvBznzA5rGzk,10857 -dns/quic/_sync.py,sha256=QF-dW19NwiDW_BDoJZkSQmHz2uEpfgedsUKTPc0JAio,10436 -dns/quic/_trio.py,sha256=01HH4_hU1VRx-BWXl8bQo4-LZem_eKRBNy6RolTZZXY,9248 -dns/rcode.py,sha256=N6JjrIQjCdJy0boKIp8Hcky5tm__LSDscpDz3rE_sgU,4156 -dns/rdata.py,sha256=uk82eldqpWR8L2zp_CB8JG6wWWfK7zdYowWISfMC2XE,31022 -dns/rdataclass.py,sha256=TK4W4ywB1L_X7EZqk2Gmwnu7vdQpolQF5DtQWyNk5xo,2984 -dns/rdataset.py,sha256=BMNvGAzE4HfYHA-pnhsKwELfpr-saz73BzYwMucoKj0,16664 -dns/rdatatype.py,sha256=wgKWnu4mAbXnmG8wKHpV8dZHkhMqNeSsWWlWFo5HcDY,7448 -dns/rdtypes/ANY/AFSDB.py,sha256=k75wMwreF1DAfDymu4lHh16BUx7ulVP3PLeQBZnkurY,1661 -dns/rdtypes/ANY/AMTRELAY.py,sha256=19jfS61mT1CQT-8vf67ZylhDS9JVRVp4WCbFE-7l0jM,3381 -dns/rdtypes/ANY/AVC.py,sha256=SpsXYzlBirRWN0mGnQe0MdN6H8fvlgXPJX5PjOHnEak,1024 -dns/rdtypes/ANY/CAA.py,sha256=AHh59Is-4WiVWd26yovnPM3hXqKS-yx7IWfXSS0NZhE,2511 -dns/rdtypes/ANY/CDNSKEY.py,sha256=bJAdrBMsFHIJz8TF1AxZoNbdxVWBCRTG-bR_uR_r_G4,1225 -dns/rdtypes/ANY/CDS.py,sha256=Y9nIRUCAabztVLbxm2SXAdYapFemCOUuGh5JqroCDUs,1163 -dns/rdtypes/ANY/CERT.py,sha256=2Cu2LQM6-K4darqhHv1EM_blmpYpnrBIIX1GnL_rxKE,3533 -dns/rdtypes/ANY/CNAME.py,sha256=IHGGq2BDpeKUahTr1pvyBQgm0NGBI_vQ3Vs5mKTXO4w,1206 -dns/rdtypes/ANY/CSYNC.py,sha256=KkZ_rG6PfeL14il97nmJGWWmUGGS5o9nd2EqbJqOuYo,2439 -dns/rdtypes/ANY/DLV.py,sha256=J-pOrw5xXsDoaB9G0r6znlYXJtqtcqhsl1OXs6CPRU4,986 -dns/rdtypes/ANY/DNAME.py,sha256=yqXRtx4dAWwB4YCCv-qW6uaxeGhg2LPQ2uyKwWaMdXs,1150 -dns/rdtypes/ANY/DNSKEY.py,sha256=MD8HUVH5XXeAGOnFWg5aVz_w-2tXYwCeVXmzExhiIeQ,1223 -dns/rdtypes/ANY/DS.py,sha256=_gf8vk1O_uY8QXFjsfUw-bny-fm6e-QpCk3PT0JCyoM,995 -dns/rdtypes/ANY/EUI48.py,sha256=x0BkK0sY_tgzuCwfDYpw6tyuChHjjtbRpAgYhO0Y44o,1151 -dns/rdtypes/ANY/EUI64.py,sha256=1jCff2-SXHJLDnNDnMW8Cd_o-ok0P3x6zKy_bcCU5h4,1161 -dns/rdtypes/ANY/GPOS.py,sha256=u4qwiDBVoC7bsKfxDKGbPjnOKddpdjy2p1AhziDWcPw,4439 -dns/rdtypes/ANY/HINFO.py,sha256=D2WvjTsvD_XqT8BepBIyjPL2iYGMgYqb1VQa9ApO0qE,2217 -dns/rdtypes/ANY/HIP.py,sha256=c32Ewlk88schJ1nPOmT5BVR60ttIM-uH8I8LaRAkFOA,3226 -dns/rdtypes/ANY/ISDN.py,sha256=L4C2Rxrr4JJN17lmJRbZN8RhM_ujjwIskY_4V4Gd3r4,2723 -dns/rdtypes/ANY/L32.py,sha256=TMz2kdGCd0siiQZyiocVDCSnvkOdjhUuYRFyf8o622M,1286 -dns/rdtypes/ANY/L64.py,sha256=sb2BjuPA0PQt67nEyT9rBt759C9e6lH71d3EJHGGnww,1592 -dns/rdtypes/ANY/LOC.py,sha256=NZKIUJULZ3BcK1-gnb2Mk76Pc4UUZry47C5n9VBvhnk,11995 -dns/rdtypes/ANY/LP.py,sha256=wTsKIjtK6vh66qZRLSsiE0k54GO8ieVBGZH8dzVvFnE,1338 -dns/rdtypes/ANY/MX.py,sha256=qQk83idY0-SbRMDmB15JOpJi7cSyiheF-ALUD0Ev19E,995 -dns/rdtypes/ANY/NID.py,sha256=N7Xx4kXf3yVAocTlCXQeJ3BtiQNPFPQVdL1iMuyl5W4,1544 -dns/rdtypes/ANY/NINFO.py,sha256=bdL_-6Bejb2EH-xwR1rfSr_9E3SDXLTAnov7x2924FI,1041 -dns/rdtypes/ANY/NS.py,sha256=ThfaPalUlhbyZyNyvBM3k-7onl3eJKq5wCORrOGtkMM,995 -dns/rdtypes/ANY/NSEC.py,sha256=kicEYxcKaLBpV6C_M8cHdDaqBoiYl6EYtPvjyR6kExI,2465 -dns/rdtypes/ANY/NSEC3.py,sha256=696h-Zz30bmcT0n1rqoEtS5wqE6jIgsVGzaw5TfdGJo,4331 -dns/rdtypes/ANY/NSEC3PARAM.py,sha256=08p6NWS4DiLav1wOuPbxUxB9MtY2IPjfOMCtJwzzMuA,2635 -dns/rdtypes/ANY/OPENPGPKEY.py,sha256=Va0FGo_8vm1OeX62N5iDTWukAdLwrjTXIZeQ6oanE78,1851 -dns/rdtypes/ANY/OPT.py,sha256=W36RslT_Psp95OPUC70knumOYjKpaRHvGT27I-NV2qc,2561 -dns/rdtypes/ANY/PTR.py,sha256=5HcR1D77Otyk91vVY4tmqrfZfSxSXWyWvwIW-rIH5gc,997 -dns/rdtypes/ANY/RESINFO.py,sha256=Kf2NcKbkeI5gFE1bJfQNqQCaitYyXfV_9nQYl1luUZ0,1008 -dns/rdtypes/ANY/RP.py,sha256=8doJlhjYDYiAT6KNF1mAaemJ20YJFUPvit8LOx4-I-U,2174 -dns/rdtypes/ANY/RRSIG.py,sha256=O8vwzS7ldfaj_x8DypvEGFsDSb7al-D7OEnprA3QQoo,4922 -dns/rdtypes/ANY/RT.py,sha256=2t9q3FZQ28iEyceeU25KU2Ur0T5JxELAu8BTwfOUgVw,1013 -dns/rdtypes/ANY/SMIMEA.py,sha256=6yjHuVDfIEodBU9wxbCGCDZ5cWYwyY6FCk-aq2VNU0s,222 -dns/rdtypes/ANY/SOA.py,sha256=Cn8yrag1YvrvwivQgWg-KXmOCaVQVdFHSkFF77w-CE0,3145 -dns/rdtypes/ANY/SPF.py,sha256=rA3Srs9ECQx-37lqm7Zf7aYmMpp_asv4tGS8_fSQ-CU,1022 -dns/rdtypes/ANY/SSHFP.py,sha256=l6TZH2R0kytiZGWez_g-Lq94o5a2xMuwLKwUwsPMx5w,2530 -dns/rdtypes/ANY/TKEY.py,sha256=1ecTuBse2b4QPH2qmx3vn-gfPK0INcKXfxrIyAJxFHA,4927 -dns/rdtypes/ANY/TLSA.py,sha256=cytzebS3W7FFr9qeJ9gFSHq_bOwUk9aRVlXWHfnVrRs,218 -dns/rdtypes/ANY/TSIG.py,sha256=4fNQJSNWZXUKZejCciwQuUJtTw2g-YbPmqHrEj_pitg,4750 -dns/rdtypes/ANY/TXT.py,sha256=F1U9gIAhwXIV4UVT7CwOCEn_su6G1nJIdgWJsLktk20,1000 -dns/rdtypes/ANY/URI.py,sha256=dpcS8KwcJ2WJ7BkOp4CZYaUyRuw7U2S9GzvVwKUihQg,2921 -dns/rdtypes/ANY/WALLET.py,sha256=IaP2g7Nq26jWGKa8MVxvJjWXLQ0wrNR1IWJVyyMG8oU,219 -dns/rdtypes/ANY/X25.py,sha256=BzEM7uOY7CMAm7QN-dSLj-_LvgnnohwJDUjMstzwqYo,1942 -dns/rdtypes/ANY/ZONEMD.py,sha256=JQicv69EvUxh4FCT7eZSLzzU5L5brw_dSM65Um2t5lQ,2393 -dns/rdtypes/ANY/__init__.py,sha256=My5jT8T5bA66zBydmRSxkmDCFxwI81B4DBRA_S36IL8,1526 -dns/rdtypes/ANY/__pycache__/AFSDB.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/AMTRELAY.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/AVC.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/CAA.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/CDNSKEY.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/CDS.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/CERT.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/CNAME.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/CSYNC.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/DLV.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/DNAME.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/DNSKEY.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/DS.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/EUI48.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/EUI64.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/GPOS.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/HINFO.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/HIP.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/ISDN.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/L32.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/L64.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/LOC.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/LP.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/MX.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/NID.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/NINFO.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/NS.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/NSEC.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/NSEC3.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/NSEC3PARAM.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/OPENPGPKEY.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/OPT.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/PTR.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/RESINFO.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/RP.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/RRSIG.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/RT.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/SMIMEA.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/SOA.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/SPF.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/SSHFP.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/TKEY.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/TLSA.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/TSIG.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/TXT.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/URI.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/WALLET.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/X25.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/ZONEMD.cpython-310.pyc,, -dns/rdtypes/ANY/__pycache__/__init__.cpython-310.pyc,, -dns/rdtypes/CH/A.py,sha256=-4G3ASZGj7oUlPfDxADibAB1WfTsZBavUO8ghDWarJ8,2212 -dns/rdtypes/CH/__init__.py,sha256=GD9YeDKb9VBDo-J5rrChX1MWEGyQXuR9Htnbhg_iYLc,923 -dns/rdtypes/CH/__pycache__/A.cpython-310.pyc,, -dns/rdtypes/CH/__pycache__/__init__.cpython-310.pyc,, -dns/rdtypes/IN/A.py,sha256=FfFn3SqbpneL9Ky63COP50V2ZFxqS1ldCKJh39Enwug,1814 -dns/rdtypes/IN/AAAA.py,sha256=AxrOlYy-1TTTWeQypDKeXrDCrdHGor0EKCE4fxzSQGo,1820 -dns/rdtypes/IN/APL.py,sha256=ppyFwn0KYMdyDzphxd0BUhgTmZv0QnDMRLjzQQM793U,5097 -dns/rdtypes/IN/DHCID.py,sha256=zRUh_EOxUPVpJjWY5m7taX8q4Oz5K70785ZtKv5OTCU,1856 -dns/rdtypes/IN/HTTPS.py,sha256=P-IjwcvDQMmtoBgsDHglXF7KgLX73G6jEDqCKsnaGpQ,220 -dns/rdtypes/IN/IPSECKEY.py,sha256=RyIy9K0Yt0uJRjdr6cj5S95ELHHbl--0xV-Qq9O3QQk,3290 -dns/rdtypes/IN/KX.py,sha256=K1JwItL0n5G-YGFCjWeh0C9DyDD8G8VzicsBeQiNAv0,1013 -dns/rdtypes/IN/NAPTR.py,sha256=SaOK-0hIYImwLtb5Hqewi-e49ykJaQiLNvk8ZzNoG7Q,3750 -dns/rdtypes/IN/NSAP.py,sha256=6YfWCVSIPTTBmRAzG8nVBj3LnohncXUhSFJHgp-TRdc,2163 -dns/rdtypes/IN/NSAP_PTR.py,sha256=iTxlV6fr_Y9lqivLLncSHxEhmFqz5UEElDW3HMBtuCU,1015 -dns/rdtypes/IN/PX.py,sha256=vHDNN2rfLObuUKwpYDIvpPB482BqXlHA-ZQpQn9Sb_E,2756 -dns/rdtypes/IN/SRV.py,sha256=a0zGaUwzvih_a4Q9BViUTFs7NZaCqgl7mls3-KRVHm8,2769 -dns/rdtypes/IN/SVCB.py,sha256=HeFmi2v01F00Hott8FlvQ4R7aPxFmT7RF-gt45R5K_M,218 -dns/rdtypes/IN/WKS.py,sha256=kErSG5AO2qIuot_hkMHnQuZB1_uUzUirNdqBoCp97rk,3652 -dns/rdtypes/IN/__init__.py,sha256=HbI8aw9HWroI6SgEvl8Sx6FdkDswCCXMbSRuJy5o8LQ,1083 -dns/rdtypes/IN/__pycache__/A.cpython-310.pyc,, -dns/rdtypes/IN/__pycache__/AAAA.cpython-310.pyc,, -dns/rdtypes/IN/__pycache__/APL.cpython-310.pyc,, -dns/rdtypes/IN/__pycache__/DHCID.cpython-310.pyc,, -dns/rdtypes/IN/__pycache__/HTTPS.cpython-310.pyc,, -dns/rdtypes/IN/__pycache__/IPSECKEY.cpython-310.pyc,, -dns/rdtypes/IN/__pycache__/KX.cpython-310.pyc,, -dns/rdtypes/IN/__pycache__/NAPTR.cpython-310.pyc,, -dns/rdtypes/IN/__pycache__/NSAP.cpython-310.pyc,, -dns/rdtypes/IN/__pycache__/NSAP_PTR.cpython-310.pyc,, -dns/rdtypes/IN/__pycache__/PX.cpython-310.pyc,, -dns/rdtypes/IN/__pycache__/SRV.cpython-310.pyc,, -dns/rdtypes/IN/__pycache__/SVCB.cpython-310.pyc,, -dns/rdtypes/IN/__pycache__/WKS.cpython-310.pyc,, -dns/rdtypes/IN/__pycache__/__init__.cpython-310.pyc,, -dns/rdtypes/__init__.py,sha256=NYizfGglJfhqt_GMtSSXf7YQXIEHHCiJ_Y_qaLVeiOI,1073 -dns/rdtypes/__pycache__/__init__.cpython-310.pyc,, -dns/rdtypes/__pycache__/dnskeybase.cpython-310.pyc,, -dns/rdtypes/__pycache__/dsbase.cpython-310.pyc,, -dns/rdtypes/__pycache__/euibase.cpython-310.pyc,, -dns/rdtypes/__pycache__/mxbase.cpython-310.pyc,, -dns/rdtypes/__pycache__/nsbase.cpython-310.pyc,, -dns/rdtypes/__pycache__/svcbbase.cpython-310.pyc,, -dns/rdtypes/__pycache__/tlsabase.cpython-310.pyc,, -dns/rdtypes/__pycache__/txtbase.cpython-310.pyc,, -dns/rdtypes/__pycache__/util.cpython-310.pyc,, -dns/rdtypes/dnskeybase.py,sha256=FoDllfa9Pz2j2rf45VyUUYUsIt3kjjrwDy6LxrlPb5s,2856 -dns/rdtypes/dsbase.py,sha256=I85Aps1lBsiItdqGpsNY1O8icosfPtkWjiUn1J1lLUQ,3427 -dns/rdtypes/euibase.py,sha256=1yKWOM4xBwLLIFFfEj7M9JMankO2l8ljxhG-5OOxXDg,2618 -dns/rdtypes/mxbase.py,sha256=DzjbiKoAAgpqbhwMBIFGA081jR5_doqGAq-kLvy2mns,3196 -dns/rdtypes/nsbase.py,sha256=tueXVV6E8lelebOmrmoOPq47eeRvOpsxHVXH4cOFxcs,2323 -dns/rdtypes/svcbbase.py,sha256=YOH3Wz3fp5GQjdTF7hU-1ys9iDkYEC5p4d0F32ivv5g,17612 -dns/rdtypes/tlsabase.py,sha256=pIiWem6sF4IwyyKmyqx5xg55IG0w3K9r502Yx8PdziA,2596 -dns/rdtypes/txtbase.py,sha256=Dt9ptWSWtnq0Qwlni6IT6YUz_DCixQDDUl5d4P_AfqY,3696 -dns/rdtypes/util.py,sha256=c3eLaucwuxXZjXWuNyCGKzwltgub4AjT4uLVytEuxSk,9017 -dns/renderer.py,sha256=5THf1iKql2JPL2sKZt2-b4zqHKfk_vlx0FEfPtMJysY,11254 -dns/resolver.py,sha256=FH_hiMeCdVYonIYmE3QqEWJKgHOOxlTcHS0dwd_loGY,73730 -dns/reversename.py,sha256=zoqXEbMZXm6R13nXbJHgTsf6L2C6uReODj6mqSHrTiE,3828 -dns/rrset.py,sha256=J-oQPEPJuKueLLiz1FN08P-ys9fjHhPWuwpDdrL4UTQ,9170 -dns/serial.py,sha256=-t5rPW-TcJwzBMfIJo7Tl-uDtaYtpqOfCVYx9dMaDCY,3606 -dns/set.py,sha256=hublMKCIhd9zp5Hz_fvQTwF-Ze28jn7mjqei6vTGWfs,9213 -dns/tokenizer.py,sha256=65vVkEeTuml3l2AT-NePE6Gt6ucDQNvSpeIVgMpP6G0,23583 -dns/transaction.py,sha256=UhwD6CLQI51dguuz__dxJS8V91vKAoqHdQDCBErJWxE,22589 -dns/tsig.py,sha256=I-Y-c3WMBX11bVioy5puFly2BhlpptUz82ikahxuh1c,11413 -dns/tsigkeyring.py,sha256=Z0xZemcU3XjZ9HlxBYv2E2PSuIhaFreqLDlD7HcmZDA,2633 -dns/ttl.py,sha256=Y4inc4bvkfKpogZn5i1n-tpg1CAjDJxH4_HvfeVjVsM,2977 -dns/update.py,sha256=y9d6LOO8xrUaH2UrZhy3ssnx8bJEsxqTArw5V8XqBRs,12243 -dns/version.py,sha256=GTecBDFJx8cKnGiCmxJhSVjk1EkqnuNVt4xailIi3sk,1926 -dns/versioned.py,sha256=3YQj8mzGmZEsjnuVJJjcWopVmDKYLhEj4hEGTLEwzco,11765 -dns/win32util.py,sha256=r9dOvC0Tq288vwPk-ngugsVpwB5YnfW22DaRv6TTPcU,8874 -dns/wire.py,sha256=vy0SolgECbO1UXB4dnhXhDeFKOJT29nQxXvSfKOgA5s,2830 -dns/xfr.py,sha256=aoW0UtvweaE0NV8cmzgMKLYQOa3hwJ3NudRuqjit4SU,13271 -dns/zone.py,sha256=lLAarSxPtpx4Sw29OQ0ifPshD4QauGu8RnPh2dEropA,52086 -dns/zonefile.py,sha256=Y9lm6I7n4eRS35CyclooiQ_jxiOs3pSyH_0uD4FQyag,27926 -dns/zonetypes.py,sha256=HrQNZxZ_gWLWI9dskix71msi9wkYK5pgrBBbPb1T74Y,690 -dnspython-2.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -dnspython-2.7.0.dist-info/METADATA,sha256=1lF6uqZwb6RAQFYVtBkLic6pBCe9t14TQWtkK9U5eyY,5763 -dnspython-2.7.0.dist-info/RECORD,, -dnspython-2.7.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87 -dnspython-2.7.0.dist-info/licenses/LICENSE,sha256=w-o_9WVLMpwZ07xfdIGvYjw93tSmFFWFSZ-EOtPXQc0,1526 diff --git a/venv/Lib/site-packages/dnspython-2.7.0.dist-info/WHEEL b/venv/Lib/site-packages/dnspython-2.7.0.dist-info/WHEEL deleted file mode 100644 index cdd68a4..0000000 --- a/venv/Lib/site-packages/dnspython-2.7.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: hatchling 1.25.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/venv/Lib/site-packages/dnspython-2.7.0.dist-info/licenses/LICENSE b/venv/Lib/site-packages/dnspython-2.7.0.dist-info/licenses/LICENSE deleted file mode 100644 index 390a726..0000000 --- a/venv/Lib/site-packages/dnspython-2.7.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,35 +0,0 @@ -ISC License - -Copyright (C) Dnspython Contributors - -Permission to use, copy, modify, and/or distribute this software for -any purpose with or without fee is hereby granted, provided that the -above copyright notice and this permission notice appear in all -copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL -WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE -AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL -DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR -PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. - - - -Copyright (C) 2001-2017 Nominum, Inc. -Copyright (C) Google Inc. - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose with or without fee is hereby granted, -provided that the above copyright notice and this permission notice -appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/venv/Lib/site-packages/docutils-0.21.2.dist-info/COPYING.txt b/venv/Lib/site-packages/docutils-0.21.2.dist-info/COPYING.txt deleted file mode 100644 index 333583c..0000000 --- a/venv/Lib/site-packages/docutils-0.21.2.dist-info/COPYING.txt +++ /dev/null @@ -1,159 +0,0 @@ -.. include:: docs/header0.txt - -================== - Copying Docutils -================== - -:Author: David Goodger -:Contact: goodger@python.org -:Date: $Date: 2023-06-22 17:34:37 +0200 (Do, 22. Jun 2023) $ -:Web site: https://docutils.sourceforge.io/ -:Copyright: This document has been placed in the public domain. - -Most of the files included in this project have been placed in the -public domain, and therefore have no license requirements and no -restrictions on copying or usage; see the `Public Domain Dedication`_ -below. There are exceptions_, listed below. -Files in the Sandbox_ are not distributed with Docutils releases and -may have different license terms. - - -Public Domain Dedication -======================== - -The persons who have associated their work with this project (the -"Dedicator": David Goodger and the many contributors to the Docutils -project) hereby dedicate the entire copyright, less the exceptions_ -listed below, in the work of authorship known as "Docutils" identified -below (the "Work") to the public domain. - -The primary repository for the Work is the Internet World Wide Web -site . The Work consists of the -files within the "docutils" module of the Docutils project Subversion -repository (http://svn.code.sf.net/p/docutils/code/), -whose Internet web interface is located at -. Files dedicated to the -public domain may be identified by the inclusion, near the beginning -of each file, of a declaration of the form:: - - Copyright: This document/module/DTD/stylesheet/file/etc. has been - placed in the public domain. - -Dedicator makes this dedication for the benefit of the public at large -and to the detriment of Dedicator's heirs and successors. Dedicator -intends this dedication to be an overt act of relinquishment in -perpetuity of all present and future rights under copyright law, -whether vested or contingent, in the Work. Dedicator understands that -such relinquishment of all rights includes the relinquishment of all -rights to enforce (by lawsuit or otherwise) those copyrights in the -Work. - -Dedicator recognizes that, once placed in the public domain, the Work -may be freely reproduced, distributed, transmitted, used, modified, -built upon, or otherwise exploited by anyone for any purpose, -commercial or non-commercial, and in any way, including by methods -that have not yet been invented or conceived. - -(This dedication is derived from the text of the `Creative Commons -Public Domain Dedication`. [#]_) - -.. [#] Creative Commons has `retired this legal tool`__ and does not - recommend that it be applied to works: This tool is based on United - States law and may not be applicable outside the US. For dedicating new - works to the public domain, Creative Commons recommend the replacement - Public Domain Dedication CC0_ (CC zero, "No Rights Reserved"). So does - the Free Software Foundation in its license-list_. - - __ http://creativecommons.org/retiredlicenses - .. _CC0: http://creativecommons.org/about/cc0 - -Exceptions -========== - -The exceptions to the `Public Domain Dedication`_ above are: - -* docutils/utils/smartquotes.py - - Copyright © 2011 Günter Milde, - based on `SmartyPants`_ © 2003 John Gruber - (released under a "revised" `BSD 3-Clause License`_ included in the file) - and smartypants.py © 2004, 2007 Chad Miller. - Released under the terms of the `BSD 2-Clause License`_ - (`local copy `__). - - .. _SmartyPants: http://daringfireball.net/projects/smartypants/ - -* docutils/utils/math/latex2mathml.py - - Copyright © Jens Jørgen Mortensen, Günter Milde. - Released under the terms of the `BSD 2-Clause License`_ - (`local copy `__). - -* | docutils/utils/math/math2html.py, - | docutils/writers/html5_polyglot/math.css - - Copyright © 2009,2010 Alex Fernández; 2021 Günter Milde - - These files were part of eLyXer_, released under the `GNU - General Public License`_ version 3 or later. The author relicensed - them for Docutils under the terms of the `BSD 2-Clause License`_ - (`local copy `__). - - .. _eLyXer: https://github.com/alexfernandez/elyxer - -* | docutils/__main__.py, - | docutils/parsers/commonmark_wrapper.py, - | docutils/parsers/recommonmark_wrapper.py, - | docutils/utils/error_reporting.py, - | docutils/utils/math/__init__.py, - | docutils/utils/math/latex2mathml.py, - | docutils/utils/math/tex2mathml_extern.py, - | docutils/utils/punctuation_chars.py, - | docutils/utils/smartquotes.py, - | docutils/writers/html5_polyglot/__init__.py, - | docutils/writers/html5_polyglot/\*.css, - | docutils/writers/latex2e/docutils.sty, - | docutils/writers/xetex/__init__.py, - | test/test_parsers/test_recommonmark/\*.py, - | test/test_parsers/test_rst/test_directives/test__init__.py, - | test/test_parsers/test_rst/test_directives/test_code_parsing.py, - | test/test_parsers/test_rst/test_line_length_limit_default.py, - | test/test_parsers/test_rst/test_line_length_limit.py, - | test/test_writers/test_latex2e_misc.py, - | test/transforms/test_smartquotes.py, - | tools/docutils-cli.py, - | tools/rst2html5.py - - Copyright © Günter Milde. - Released under the terms of the `BSD 2-Clause License`_ - (`local copy `__). - -* docutils/utils/roman.py - - copyright by Mark Pilgrim, released under the - `Zope Public License Version 2.1`_ (`local copy`__). - - __ licenses/ZPL-2-1.txt - -* tools/editors/emacs/rst.el - - copyright by Free Software Foundation, Inc., - released under the `GNU General Public License`_ version 3 or later - (`local copy`__). - - __ licenses/gpl-3-0.txt - -All used licenses are OSI-approved_ and GPL-compatible_. - -Plaintext versions of all the linked-to licenses are provided in the -licenses_ directory. - -.. _sandbox: https://docutils.sourceforge.io/sandbox/README.html -.. _licenses: licenses/ -.. _GNU General Public License: https://www.gnu.org/copyleft/gpl.html -.. _BSD 2-Clause License: http://opensource.org/licenses/BSD-2-Clause -.. _BSD 3-Clause License: https://opensource.org/licenses/BSD-3-Clause -.. _Zope Public License Version 2.1: https://opensource.org/license/zpl-2-1/ -.. _OSI-approved: http://opensource.org/licenses/ -.. _license-list: -.. _GPL-compatible: https://www.gnu.org/licenses/license-list.html diff --git a/venv/Lib/site-packages/docutils-0.21.2.dist-info/INSTALLER b/venv/Lib/site-packages/docutils-0.21.2.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/Lib/site-packages/docutils-0.21.2.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/Lib/site-packages/docutils-0.21.2.dist-info/METADATA b/venv/Lib/site-packages/docutils-0.21.2.dist-info/METADATA deleted file mode 100644 index bec0da0..0000000 --- a/venv/Lib/site-packages/docutils-0.21.2.dist-info/METADATA +++ /dev/null @@ -1,63 +0,0 @@ -Metadata-Version: 2.1 -Name: docutils -Version: 0.21.2 -Summary: Docutils -- Python Documentation Utilities -Author-email: David Goodger -Maintainer-email: docutils-develop list -Requires-Python: >=3.9 -Description-Content-Type: text/plain -Classifier: Development Status :: 4 - Beta -Classifier: Environment :: Console -Classifier: Intended Audience :: End Users/Desktop -Classifier: Intended Audience :: Other Audience -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: System Administrators -Classifier: License :: Public Domain -Classifier: License :: OSI Approved :: Python Software Foundation License -Classifier: License :: OSI Approved :: BSD License -Classifier: License :: OSI Approved :: GNU General Public License (GPL) -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Topic :: Documentation -Classifier: Topic :: Software Development :: Documentation -Classifier: Topic :: Text Processing -Classifier: Natural Language :: English -Classifier: Natural Language :: Afrikaans -Classifier: Natural Language :: Arabic -Classifier: Natural Language :: Catalan -Classifier: Natural Language :: Catalan (Valencian) -Classifier: Natural Language :: Chinese (Simplified) -Classifier: Natural Language :: Chinese (Traditional) -Classifier: Natural Language :: Czech -Classifier: Natural Language :: Danish -Classifier: Natural Language :: Dutch -Classifier: Natural Language :: Esperanto -Classifier: Natural Language :: Finnish -Classifier: Natural Language :: French -Classifier: Natural Language :: Galician -Classifier: Natural Language :: Georgian -Classifier: Natural Language :: German -Classifier: Natural Language :: Hebrew -Classifier: Natural Language :: Italian -Classifier: Natural Language :: Japanese -Classifier: Natural Language :: Korean -Classifier: Natural Language :: Latvian -Classifier: Natural Language :: Lithuanian -Classifier: Natural Language :: Persian -Classifier: Natural Language :: Polish -Classifier: Natural Language :: Portuguese (Brazilian) -Classifier: Natural Language :: Russian -Classifier: Natural Language :: Slovak -Classifier: Natural Language :: Spanish -Classifier: Natural Language :: Swedish -Classifier: Natural Language :: Ukrainian -Project-URL: Homepage, https://docutils.sourceforge.io - -Docutils is a modular system for processing documentation -into useful formats, such as HTML, XML, and LaTeX. For -input Docutils supports reStructuredText, an easy-to-read, -what-you-see-is-what-you-get plaintext markup syntax. diff --git a/venv/Lib/site-packages/docutils-0.21.2.dist-info/RECORD b/venv/Lib/site-packages/docutils-0.21.2.dist-info/RECORD deleted file mode 100644 index c4ac135..0000000 --- a/venv/Lib/site-packages/docutils-0.21.2.dist-info/RECORD +++ /dev/null @@ -1,347 +0,0 @@ -../../Scripts/docutils.exe,sha256=n6SPmXlvnoXMhoTtt3mBFTGhn_Vuctn4s_GvlbhxMqs,107895 -../../Scripts/rst2html.exe,sha256=7Dao_wECqCodl6Q5UygpUiTi5liZ1lWB1ss5ylf7-KY,107899 -../../Scripts/rst2html4.exe,sha256=wLdBhMEuc7drugGlZIEKTx_-GGfjrz0Bb1wB3Fr1pHQ,107901 -../../Scripts/rst2html5.exe,sha256=G0WdcHdsNBBpn5GkEs8QL7UNx_SxGoW-TvY6H_1PC7I,107901 -../../Scripts/rst2latex.exe,sha256=gmPXM_vm26TqW-tupqiVHa8Kvuzb9-MpAF4I9KFAk5Q,107901 -../../Scripts/rst2man.exe,sha256=KgQWizWiIFrArL-TYFLOXgamsS4k9UiPzmaRUNDh16Y,107897 -../../Scripts/rst2odt.exe,sha256=giyUQtjNnL6DYKY_7_qQg_p7FYrKoc3Y0PXv0_5w0pY,107897 -../../Scripts/rst2pseudoxml.exe,sha256=k-sm2jv_TX9OCSWUDjVfsc_90cXFl1mlkmGm4NYxIhI,107909 -../../Scripts/rst2s5.exe,sha256=Zgw9d5xPDOU4vRho0gHQC7zpZ_ttyWOZrsG3sXkGuFI,107895 -../../Scripts/rst2xetex.exe,sha256=dtoJEKod0Qq51eeJLU8huvUvwhqn48mzqV3OZKkb8Ec,107901 -../../Scripts/rst2xml.exe,sha256=hRXp5cKqCags4iuJxi5itredfoD8RwDOHwum7NjLrCY,107897 -docutils-0.21.2.dist-info/COPYING.txt,sha256=U07fdkGr3mn8UA8ijAl1GBLAWJgV7vDAgrxroxfWRqw,6310 -docutils-0.21.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -docutils-0.21.2.dist-info/METADATA,sha256=3oOygKJQ4otFhgmtONEmLPiLjUgZUQ5Fu1ZFHxTP4C8,2776 -docutils-0.21.2.dist-info/RECORD,, -docutils-0.21.2.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 -docutils-0.21.2.dist-info/entry_points.txt,sha256=9xC35dFk4tSRrSMOKqq6kYlAZDEERdBM7fmwDLBMifY,379 -docutils/__init__.py,sha256=gmP9fnbribb7oZWoe4PTw8Obh_B_bt3e_CXKi45Xzus,10293 -docutils/__main__.py,sha256=HviLRJb_t90M1fOQ8s8J2A4XZXSvw4cYHkrAtZjR2XA,3625 -docutils/__pycache__/__init__.cpython-310.pyc,, -docutils/__pycache__/__main__.cpython-310.pyc,, -docutils/__pycache__/core.cpython-310.pyc,, -docutils/__pycache__/examples.cpython-310.pyc,, -docutils/__pycache__/frontend.cpython-310.pyc,, -docutils/__pycache__/io.cpython-310.pyc,, -docutils/__pycache__/nodes.cpython-310.pyc,, -docutils/__pycache__/statemachine.cpython-310.pyc,, -docutils/core.py,sha256=Lm2qwSppidgDBjoZQpOLiodDKtEbkT_CsLH93Iq4M6U,33045 -docutils/docutils.conf,sha256=F2yH40kC3N5nnjGIRdT7DHHGXbDKbd5YsbhmQxF2CNY,151 -docutils/examples.py,sha256=nP7S8EnZk4w3PYi2Ymeu9_UUcDIi39lP049Pe6iBECU,3961 -docutils/frontend.py,sha256=2oRkks_lIJHhlk537ejUki5dyHodR0SMuoZLXYRcUtI,44396 -docutils/io.py,sha256=VrAIuTlsf93O3wAYMK_PC0DqINMvRxuZZC5W_-6NEOg,22583 -docutils/languages/__init__.py,sha256=I8j0eJD3Jj3Q1C5ALqhYa-f8kCCQDPxmgn2VdPeTusI,2921 -docutils/languages/__pycache__/__init__.cpython-310.pyc,, -docutils/languages/__pycache__/af.cpython-310.pyc,, -docutils/languages/__pycache__/ar.cpython-310.pyc,, -docutils/languages/__pycache__/ca.cpython-310.pyc,, -docutils/languages/__pycache__/cs.cpython-310.pyc,, -docutils/languages/__pycache__/da.cpython-310.pyc,, -docutils/languages/__pycache__/de.cpython-310.pyc,, -docutils/languages/__pycache__/en.cpython-310.pyc,, -docutils/languages/__pycache__/eo.cpython-310.pyc,, -docutils/languages/__pycache__/es.cpython-310.pyc,, -docutils/languages/__pycache__/fa.cpython-310.pyc,, -docutils/languages/__pycache__/fi.cpython-310.pyc,, -docutils/languages/__pycache__/fr.cpython-310.pyc,, -docutils/languages/__pycache__/gl.cpython-310.pyc,, -docutils/languages/__pycache__/he.cpython-310.pyc,, -docutils/languages/__pycache__/it.cpython-310.pyc,, -docutils/languages/__pycache__/ja.cpython-310.pyc,, -docutils/languages/__pycache__/ka.cpython-310.pyc,, -docutils/languages/__pycache__/ko.cpython-310.pyc,, -docutils/languages/__pycache__/lt.cpython-310.pyc,, -docutils/languages/__pycache__/lv.cpython-310.pyc,, -docutils/languages/__pycache__/nl.cpython-310.pyc,, -docutils/languages/__pycache__/pl.cpython-310.pyc,, -docutils/languages/__pycache__/pt_br.cpython-310.pyc,, -docutils/languages/__pycache__/ru.cpython-310.pyc,, -docutils/languages/__pycache__/sk.cpython-310.pyc,, -docutils/languages/__pycache__/sv.cpython-310.pyc,, -docutils/languages/__pycache__/uk.cpython-310.pyc,, -docutils/languages/__pycache__/zh_cn.cpython-310.pyc,, -docutils/languages/__pycache__/zh_tw.cpython-310.pyc,, -docutils/languages/af.py,sha256=bjIWD_cNZAyZ9XIqqEoQaSljWlvP0vrQi8p8Fu9Nj2o,1831 -docutils/languages/ar.py,sha256=3FRbb0CSpXPHSQru96bqPDGGEdcnWhQIUG_ndpmacfg,1943 -docutils/languages/ca.py,sha256=b98Y79DEl88Jt2IZsKaH1OaJlC9W5khQcOV71Tvwpj0,2085 -docutils/languages/cs.py,sha256=qkEwPMKaPgw3D4qKCJoGXfDczIA3Wju8joBobn5hMvg,1832 -docutils/languages/da.py,sha256=1db0GiWS2YYOIqWru9BGeun7E3c-lwAgCWA6epn6KH4,1856 -docutils/languages/de.py,sha256=oaGzlhKsE4yFKTnCKrZt8nMnwZGqQ-PDT92UgUwRYzs,1728 -docutils/languages/en.py,sha256=MOTXOluYEnUwSHRlkINnBXMI8sbIfGahXF8RLws4NSk,1854 -docutils/languages/eo.py,sha256=p-wQOrAdL9tQ-89OT8A1VS8sO0OjogmPXRBAoihaAc0,1895 -docutils/languages/es.py,sha256=Jf81tKn_FJ7Kieozten6hn8WjVnYpZ9vsu4N6ER4O1s,1854 -docutils/languages/fa.py,sha256=dXhN7qZnzv1ksnR0Yb5LrSgNLwXy387ILj90v5iHHHM,1958 -docutils/languages/fi.py,sha256=34cPWxDFZXBnOw5LWENSIKaFqFLxZOc6vIioLLnS2do,1892 -docutils/languages/fr.py,sha256=zIPQD7UdOx3FPIBgIAT_mWNk4dped04jK8No2kI1Zic,1799 -docutils/languages/gl.py,sha256=Hz30Wfc-CMuGxKoHzPtY2Wdn-A6tFyvirnnFGZdZ_04,1958 -docutils/languages/he.py,sha256=eHyDSLwp6Y55GyHlgr8yMOkR-gxSgk7Ge8gfsm5n_Io,1878 -docutils/languages/it.py,sha256=aqmXdit3DYMhnke5MmyQIAVfCcrFuKzWmv0L5H06AI8,1814 -docutils/languages/ja.py,sha256=8C-hjqB7fa_Asa-uKKKkHRqT6NTjO3IJiiks6JADJNU,1890 -docutils/languages/ka.py,sha256=aqqIgkJyhLg3FOYWuhBhYDgzAj_nos9WwEQqI-eUcwg,2429 -docutils/languages/ko.py,sha256=eqKib7kW6h8DKg6inoXAtJa3f8FygdT37eGqiMifzZU,1832 -docutils/languages/lt.py,sha256=ixPG61Q2xB6J0uk0TXTXDwfKcEQgqIYC9Tc4VrBMQEY,1919 -docutils/languages/lv.py,sha256=pSZ7y94j6YWKLz4U16uBVwZsgKpd4jWkvcAgwmkDjv0,1851 -docutils/languages/nl.py,sha256=LKV2Hkuh0kH17w_ukZoAtTIlvWSo5iC3g7nPFDHoDDY,1871 -docutils/languages/pl.py,sha256=zyW9iwwriTX8YIGbT54DKgvkdyfiPxm49R0e2eIBxKE,1830 -docutils/languages/pt_br.py,sha256=navcDOoMajIE3yqibn7NPEIf-_oF15ECBWk4beMY3Zw,1865 -docutils/languages/ru.py,sha256=BAQA-1TZEeHpK44L_Os9V-WKUBt9lPApRl1nFM1zOhw,2070 -docutils/languages/sk.py,sha256=r-vrXCNGbDqFXAxM9WpRaH-ws1KumaIb1yramTTlmlI,1788 -docutils/languages/sv.py,sha256=bmJkUSdVn_j8s_P_Nu4KGBM2F42Og7sFzMNH238eKtQ,1908 -docutils/languages/uk.py,sha256=abLYoA0w2pJWlGLBglvUvEnGrhAVgbRyDEmlwV5ntxQ,2062 -docutils/languages/zh_cn.py,sha256=7nOmSfLgrb3Zq-xImQEN4-My6NqHZVat7GoMbpijVn4,1852 -docutils/languages/zh_tw.py,sha256=KJS8-gHwJVUdW7NOjgJQPb6iWkXtBWTT0JrRBV7BYTU,2112 -docutils/nodes.py,sha256=psy0zIro734Gsk7PGIqWqx0Ax_5AB0mBHC0J4KYduHo,80628 -docutils/parsers/__init__.py,sha256=NenSsWynQ-HU858BTDg0dMIX7ErS_qrAJot8KdtHyiU,3724 -docutils/parsers/__pycache__/__init__.cpython-310.pyc,, -docutils/parsers/__pycache__/commonmark_wrapper.cpython-310.pyc,, -docutils/parsers/__pycache__/null.cpython-310.pyc,, -docutils/parsers/__pycache__/recommonmark_wrapper.cpython-310.pyc,, -docutils/parsers/commonmark_wrapper.py,sha256=UcBtp5AcOSQRq-0-UsSZcnFMWa14TULD8kayhp4A0eY,1762 -docutils/parsers/null.py,sha256=LtO7n-E6lNOs4mLXZ2SiShB3C630SoJ80ugd9fh1vXI,445 -docutils/parsers/recommonmark_wrapper.py,sha256=SbFSCvBeQRH-fDQczHgK2qF_HwZ3gQsJx_CY2txX43s,5426 -docutils/parsers/rst/__init__.py,sha256=EydGQTcJeAmJQbCwPEFk77kA7Nio-Vqs7R0NMNkcb_s,15954 -docutils/parsers/rst/__pycache__/__init__.cpython-310.pyc,, -docutils/parsers/rst/__pycache__/roles.cpython-310.pyc,, -docutils/parsers/rst/__pycache__/states.cpython-310.pyc,, -docutils/parsers/rst/__pycache__/tableparser.cpython-310.pyc,, -docutils/parsers/rst/directives/__init__.py,sha256=82wwJQW7spmBVtko7E-a62LUXYfATJg3hSl6CZNrUus,14812 -docutils/parsers/rst/directives/__pycache__/__init__.cpython-310.pyc,, -docutils/parsers/rst/directives/__pycache__/admonitions.cpython-310.pyc,, -docutils/parsers/rst/directives/__pycache__/body.cpython-310.pyc,, -docutils/parsers/rst/directives/__pycache__/html.cpython-310.pyc,, -docutils/parsers/rst/directives/__pycache__/images.cpython-310.pyc,, -docutils/parsers/rst/directives/__pycache__/misc.cpython-310.pyc,, -docutils/parsers/rst/directives/__pycache__/parts.cpython-310.pyc,, -docutils/parsers/rst/directives/__pycache__/references.cpython-310.pyc,, -docutils/parsers/rst/directives/__pycache__/tables.cpython-310.pyc,, -docutils/parsers/rst/directives/admonitions.py,sha256=44OwQdPrDKD5VnCd-pPFtEzJs3U1uF-MeS3iWMmCWwU,2526 -docutils/parsers/rst/directives/body.py,sha256=lRClR4ljNiXntJfojN0HcaNpqH1m2GpwsOMS3JVNJK8,9939 -docutils/parsers/rst/directives/html.py,sha256=adxIFdnOHpqH0QSeXT7utBSy7FKWV0x18zbnXtmmuhs,695 -docutils/parsers/rst/directives/images.py,sha256=amQeXFb31hHlyjf15vgF2Sfe3OGCGzLaZAHxfsWSmOw,7265 -docutils/parsers/rst/directives/misc.py,sha256=9mc7erT0KvKHWR4_i5Uz2FhBk2pLd36fMCabyF2ClLA,26700 -docutils/parsers/rst/directives/parts.py,sha256=m5YOwZoPawR6I-Y3Q7CN9wGUzWmXa9v-Hvglh2E5ffQ,4247 -docutils/parsers/rst/directives/references.py,sha256=1Y1yhe_O2PqLtQUSly-ny291nrQKJgQiO4Hu7Xew9Zo,831 -docutils/parsers/rst/directives/tables.py,sha256=PRwO-lA1I93JAXR7qGDQ6n2cT5UYA8JQHdymHOdLFdg,23470 -docutils/parsers/rst/include/README.txt,sha256=R3Y-9wDzYQ0jOhj9FAlwG6hRRhHcEWOZcn2hMF1DeVg,670 -docutils/parsers/rst/include/isoamsa.txt,sha256=ZqGuK-R-yIxa2YDSREt48DFxc8fpF-HX51eiCKXCPp4,10925 -docutils/parsers/rst/include/isoamsb.txt,sha256=3CK8um9WjhPMVgEAbeI16rk91IzWqWXFbRJC44InP3A,7242 -docutils/parsers/rst/include/isoamsc.txt,sha256=XCI2ubAKaO-eOQj87hbBMeYpkqHvc2b2daUCS9ekUzU,1723 -docutils/parsers/rst/include/isoamsn.txt,sha256=Wx54SjZGeYVEB3oNnRi7eGYHEjROZiBUFhQAQDxcVMQ,6721 -docutils/parsers/rst/include/isoamso.txt,sha256=RFxHs5s8DtMgvDaeArmwnSZP_QN20KssvW5f6KMohYA,3825 -docutils/parsers/rst/include/isoamsr.txt,sha256=TLH3gNugqSX3-tH6gDNcgIgbGiKe_GukwZ8U1MIeJCQ,11763 -docutils/parsers/rst/include/isobox.txt,sha256=NORZqqDIewr0-CPoVWqVfTbCVrGZOqY87Crn8O4OUoo,3101 -docutils/parsers/rst/include/isocyr1.txt,sha256=B2DWWIEZ8aJ-scOBP9pbrsKYEmnNF8VZ9e9Mut2MZzU,4241 -docutils/parsers/rst/include/isocyr2.txt,sha256=t52cY0R-9bnkWiQPXW1NYDzO4ueE6ogUF9Ho4ARHg7Q,1882 -docutils/parsers/rst/include/isodia.txt,sha256=VMg8jI2IQogISrpiTS3L88TntxiMfS0cElsrpxZ1FAI,869 -docutils/parsers/rst/include/isogrk1.txt,sha256=DkJc-K_nTh-WDhfOQIRMdQ4aUnsYKb_etyEEJfU8SG4,3010 -docutils/parsers/rst/include/isogrk2.txt,sha256=0x8w_DgroVISgsTLUOuyLZNzDLThcnti27T7T7DxL7g,1705 -docutils/parsers/rst/include/isogrk3.txt,sha256=8b7gQSKtw4yhLEVMZ6vH8VaToiZMM2_kD2snlqfeIQE,2880 -docutils/parsers/rst/include/isogrk4-wide.txt,sha256=RAdw43c5ZAUBWu8MO_lJDmXVV0cGHhVrky0H_bq1eEs,3035 -docutils/parsers/rst/include/isogrk4.txt,sha256=FQxEZAJu2d_RX3G3PfHm6JlC_1osHQoheFA886MLeQk,372 -docutils/parsers/rst/include/isolat1.txt,sha256=d4dBGSPosghudIhZfMPNzsBJKvH1nyVyi6m5p0oW_HY,4397 -docutils/parsers/rst/include/isolat2.txt,sha256=2RMWwHB9djHvsdnKSv2dSNHlBc50P0JYF1DRcK3HW8s,8466 -docutils/parsers/rst/include/isomfrk-wide.txt,sha256=RsYrcq3mX-CMuV6oijCIfUTnUe8Z6w0PG0ephU9isBM,3334 -docutils/parsers/rst/include/isomfrk.txt,sha256=Y40ZXO1GLLzHezKEJJ8w8OBFtHGwtdjErI_062abwC4,519 -docutils/parsers/rst/include/isomopf-wide.txt,sha256=nrhNkzw15HdEA-Gf8T7yatCbs5b7z7q7T6SCTNLByJw,1931 -docutils/parsers/rst/include/isomopf.txt,sha256=l9rTXrdZWf2RchhPr4Oi2M-4yZYLtVH-7kdOmCPzY_M,639 -docutils/parsers/rst/include/isomscr-wide.txt,sha256=VNfKzET1n08k2PUeK9UeDamgP0j8iKpaorregQbaP3w,3231 -docutils/parsers/rst/include/isomscr.txt,sha256=EBWiVvLZYhm9e2c5i7T4Ur6i1WODl_BLiHjcWA_C45g,776 -docutils/parsers/rst/include/isonum.txt,sha256=yg4P9UxBM-72JRGkB4KVdjmRPyBWLvkOlkelh8quDzc,4066 -docutils/parsers/rst/include/isopub.txt,sha256=BFkr5rRRFuYM7a19WPP7lfBpUKftkYKZjnM2SHLtzwY,4613 -docutils/parsers/rst/include/isotech.txt,sha256=2WGt7TSBeRMr2m2DBlY-xiVjxeiNXdTZJb1DJdtgYKg,9726 -docutils/parsers/rst/include/mmlalias.txt,sha256=jQ4IbZwZAJ9rXmb7De77DYgdIlMsWA-s8uZf42EYtFU,45428 -docutils/parsers/rst/include/mmlextra-wide.txt,sha256=Myj4APWltVYohIK7f1v8urJuDVT2_I_U7rasnBCTsYY,9010 -docutils/parsers/rst/include/mmlextra.txt,sha256=DfWtgBA6Bn4TzlZokxTu5vp7zIf1hqUeaeGpYbfHSAg,6800 -docutils/parsers/rst/include/s5defs.txt,sha256=_5JOMpDtaufiZbdxh6QKpICqLvGpB9cypHM-SEt3sKA,1036 -docutils/parsers/rst/include/xhtml1-lat1.txt,sha256=ht_IZrejaCfgG95sfLNfCu1WAzU4LwpkWgzRbZ_6OA4,6112 -docutils/parsers/rst/include/xhtml1-special.txt,sha256=u4YARKjTrICRTtqlMDDOmpYR8xe-DKDRiNjzmXQs7gc,1945 -docutils/parsers/rst/include/xhtml1-symbol.txt,sha256=e6GP5rkmSNcXusRBJkKf2LSbSEyd1oFXJG_WBCYBKE8,7028 -docutils/parsers/rst/languages/__init__.py,sha256=bAE-YQUQ95QwYJVPnWcjz5bw6LOfW5Pgbhwmwp01-OY,1222 -docutils/parsers/rst/languages/__pycache__/__init__.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/af.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/ar.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/ca.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/cs.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/da.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/de.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/en.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/eo.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/es.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/fa.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/fi.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/fr.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/gl.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/he.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/it.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/ja.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/ka.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/ko.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/lt.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/lv.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/nl.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/pl.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/pt_br.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/ru.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/sk.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/sv.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/uk.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/zh_cn.cpython-310.pyc,, -docutils/parsers/rst/languages/__pycache__/zh_tw.cpython-310.pyc,, -docutils/parsers/rst/languages/af.py,sha256=MYGuM5yX_bx83imYLMJreUelpx3c1fcMmLhEu5tyQQI,3760 -docutils/parsers/rst/languages/ar.py,sha256=XpIY3SM10ccvFrO_j__REi_ITvLxSmGuf_y0Dc-Z_-g,3051 -docutils/parsers/rst/languages/ca.py,sha256=atmy4uqx3eVNvnKJwD0HuY0JSRG7uJfo7-AXrRgWg9g,4318 -docutils/parsers/rst/languages/cs.py,sha256=UXKr7zpl6UY31mBtCaNF-FBSsGVvWC59dx7KkwnIbTk,4760 -docutils/parsers/rst/languages/da.py,sha256=tQfxUwhlJohtnfKscjY0PUnwDYF2M-6Tu08EcXy7c6Q,3752 -docutils/parsers/rst/languages/de.py,sha256=nTIYDpKgOf28OJkcJbnuRvTOeLG6nDkfXDwW-MJV3Ls,3564 -docutils/parsers/rst/languages/en.py,sha256=9dmxPb_sYYsY4vEg8B_IBGQhEytcRvXtp5iOrzwmaHI,3516 -docutils/parsers/rst/languages/eo.py,sha256=ozUCNH5zKhnmYXxd5nZRu3TmF_aVeCAVzSLleRJWDf8,3751 -docutils/parsers/rst/languages/es.py,sha256=gFFm2EwAtAZOJUQLk03QUQdV0E7DKnct0amy6gpaNs4,4034 -docutils/parsers/rst/languages/fa.py,sha256=fap5ifAVKvqHGo3S2_G4mMrA8fvOiQ3BPi4z8Zlf94M,3171 -docutils/parsers/rst/languages/fi.py,sha256=xsxxUTOqVj1P1XkCQsPPkI-p1-ELE0QwMA9Oy-QPDHg,3536 -docutils/parsers/rst/languages/fr.py,sha256=V9_k_lbLkBSWuAbNeW2l4_Bv4EzaK4Dbwih0fJYIi0U,3816 -docutils/parsers/rst/languages/gl.py,sha256=v-zXIwcRYxgu8mZJmlexv73pMtwWlSB4yGVcYeENQP4,3389 -docutils/parsers/rst/languages/he.py,sha256=_UHQHJQx7EAGvMlURHJ8kDk_tcG1uv-7eTl7a4XEGgU,3293 -docutils/parsers/rst/languages/it.py,sha256=ZR4TsyM3611ASRQelyA7rx0zjUgMVM6gzrym6Bqrj40,3353 -docutils/parsers/rst/languages/ja.py,sha256=_PlI9H5u300_VlYvW52UvSy_MJ5OLlMmoNo1Pc6Pib8,3776 -docutils/parsers/rst/languages/ka.py,sha256=Xxv37fZnLqIbo1vmT1--Kj7etRxlKPgHcgfgiBvn9Xk,4141 -docutils/parsers/rst/languages/ko.py,sha256=IpSyXjTkoij0C7859i1p0iSgz9BQYpdF3fNTS6qcQ1A,3377 -docutils/parsers/rst/languages/lt.py,sha256=PHzrQAP-ZAcboCyc1jvHI5l0CMQnrbBEZ285MWJVy9w,3519 -docutils/parsers/rst/languages/lv.py,sha256=pfTCSDtQkOWYVTnTFjWhUZpnJJW7yuNzVW9xvBgP_u4,3376 -docutils/parsers/rst/languages/nl.py,sha256=dQ2Hex8oQS8f075B6nWZjeSQ0Rzjnl87oS8LauqBe1Y,3794 -docutils/parsers/rst/languages/pl.py,sha256=TRNum6wVR2XQY_Y_uhKqXNHdTBeaYgXwWaqokBaO-N0,3240 -docutils/parsers/rst/languages/pt_br.py,sha256=-cFMXIp8_6cryE7Q7smlElf4QegPqjmDRya8VxM1CQQ,3870 -docutils/parsers/rst/languages/ru.py,sha256=t5K4rlNKIcoOchVeC68sjJ8mr0Cox_BeyCvb1MDm9tQ,3398 -docutils/parsers/rst/languages/sk.py,sha256=HSdFwU6j9rZShkXyGPBc749TrUUB7tBSz-aw0f4UAzs,3943 -docutils/parsers/rst/languages/sv.py,sha256=HxyE3k93qgJw-aD6Ea3Tmfqz3QX1Q7AJIaU65MrkJxc,3261 -docutils/parsers/rst/languages/uk.py,sha256=LBUHWeKwFdEjfif4CG7LtMhBidl4ln6u4oStJ2omVsQ,3441 -docutils/parsers/rst/languages/zh_cn.py,sha256=YEC3UmY43NaI7s_6DatL_VQugiHvSUbBrHubgnMfYbI,3925 -docutils/parsers/rst/languages/zh_tw.py,sha256=icmm58W9wO69L6p5dwTQwjj45uxer2ZrJCqvBbERnkQ,5160 -docutils/parsers/rst/roles.py,sha256=GLJVffa-S_Fn3u6RTxdXJm1MXd1AQ7IxbCq99MmxIzQ,16119 -docutils/parsers/rst/states.py,sha256=ziKtByqhX2TAjRg4cOgGyQ6729W6gmVuaUUMKwKtf1k,133123 -docutils/parsers/rst/tableparser.py,sha256=D2jtx00mTdHsn20TKk9GJmCilwISeKHe9VoSy8PYELs,20912 -docutils/readers/__init__.py,sha256=rgBQZvedeYA8UZb2pYKoKqYWAjvUi8ZrzG1kANjnwC4,3520 -docutils/readers/__pycache__/__init__.cpython-310.pyc,, -docutils/readers/__pycache__/doctree.cpython-310.pyc,, -docutils/readers/__pycache__/pep.cpython-310.pyc,, -docutils/readers/__pycache__/standalone.cpython-310.pyc,, -docutils/readers/doctree.py,sha256=9QNTk_8x46sDkcSjzQiyFZxN-m9CBO3XA5bLar7OA0Q,1607 -docutils/readers/pep.py,sha256=m5RnOjKfMnmwHXU8vsr0HfZlqcqH2Yn4MXspjLO0GYE,1523 -docutils/readers/standalone.py,sha256=qwVRPiBto5CvE3m-2sZ2a2btlVl8aSbT4J_z7uIr3yM,2334 -docutils/statemachine.py,sha256=fKzVodfw6BEZQf4HquXfpBfNR8N5E8uzVeDoJA5eFRw,56956 -docutils/transforms/__init__.py,sha256=LTftDJ01XJReLdKMyuJrB_ImFr0fHOaZGqzE1VnXg9c,6968 -docutils/transforms/__pycache__/__init__.cpython-310.pyc,, -docutils/transforms/__pycache__/components.cpython-310.pyc,, -docutils/transforms/__pycache__/frontmatter.cpython-310.pyc,, -docutils/transforms/__pycache__/misc.cpython-310.pyc,, -docutils/transforms/__pycache__/parts.cpython-310.pyc,, -docutils/transforms/__pycache__/peps.cpython-310.pyc,, -docutils/transforms/__pycache__/references.cpython-310.pyc,, -docutils/transforms/__pycache__/universal.cpython-310.pyc,, -docutils/transforms/__pycache__/writer_aux.cpython-310.pyc,, -docutils/transforms/components.py,sha256=4qO1txFE98PJa4tqKCpnlnQi_UIEQDU4mMVuzwgqizY,2151 -docutils/transforms/frontmatter.py,sha256=zgWHDRBnv1Flv1Ddq5xEK6KmmGSgRyBwOzOHT8TfOAk,20809 -docutils/transforms/misc.py,sha256=BhyjLyE8j5QRELg7CcT0hEwdvnUJugUqQYyckzXDlh0,4873 -docutils/transforms/parts.py,sha256=Z_72Wf2oqchCcTIPTOgBVS1auAtA7POwJww1EaoeQPk,6912 -docutils/transforms/peps.py,sha256=7TMhgwqyopkuUetITpsByN-t0ZT8bXJOqaClUwliF0U,11111 -docutils/transforms/references.py,sha256=fhgJ6KDifnAFtPZHw6srdH76A5cepjfejzSKNF_2McA,36821 -docutils/transforms/universal.py,sha256=mhJkmCIhYQFqBAzitGyskSjGTYCwAyvSQzld6p5aLEE,12381 -docutils/transforms/writer_aux.py,sha256=G_XXqiAcSqWnFJKWRhQ8oE0jWcZPjLjyW2QBThE1Xes,3057 -docutils/utils/__init__.py,sha256=8G-da95eq3Ka72X1U0vAgNR4UZJYzS2t4o_MpfnN5c0,30381 -docutils/utils/__pycache__/__init__.cpython-310.pyc,, -docutils/utils/__pycache__/code_analyzer.cpython-310.pyc,, -docutils/utils/__pycache__/error_reporting.cpython-310.pyc,, -docutils/utils/__pycache__/punctuation_chars.cpython-310.pyc,, -docutils/utils/__pycache__/roman.cpython-310.pyc,, -docutils/utils/__pycache__/smartquotes.cpython-310.pyc,, -docutils/utils/__pycache__/urischemes.cpython-310.pyc,, -docutils/utils/code_analyzer.py,sha256=O6eUnWPYyZFXtTtk-NuNVv4k7L9sb5BJGIjacSZT4Tw,4920 -docutils/utils/error_reporting.py,sha256=vjHvpHu0BSoE2Ltm4FyYfLUsV5VWAjBbRoniWrJ7CR8,8105 -docutils/utils/math/__init__.py,sha256=DgTt07qT_uN2-9Tz8HNKkRqw5P3KIj81VurrJ2J_Omw,2553 -docutils/utils/math/__pycache__/__init__.cpython-310.pyc,, -docutils/utils/math/__pycache__/latex2mathml.cpython-310.pyc,, -docutils/utils/math/__pycache__/math2html.cpython-310.pyc,, -docutils/utils/math/__pycache__/mathalphabet2unichar.cpython-310.pyc,, -docutils/utils/math/__pycache__/mathml_elements.cpython-310.pyc,, -docutils/utils/math/__pycache__/tex2mathml_extern.cpython-310.pyc,, -docutils/utils/math/__pycache__/tex2unichar.cpython-310.pyc,, -docutils/utils/math/__pycache__/unichar2tex.cpython-310.pyc,, -docutils/utils/math/latex2mathml.py,sha256=ELwAGaYg4kx2E7_AE_rSSSZFID4QMIV8sPAIQ2PsdVE,46961 -docutils/utils/math/math2html.py,sha256=VfZ1ceFmNKu4yvF72j4xsgH_cjtOkeCevPC3UYw4enQ,107808 -docutils/utils/math/mathalphabet2unichar.py,sha256=bsgWbE09bIyXfME-TyCms-0fesWVoxXMErQVVeYOoSo,56217 -docutils/utils/math/mathml_elements.py,sha256=zMoCC2KnjAxmSGu4ZUtCZAEuyWme80pCPTn66E44gcY,14564 -docutils/utils/math/tex2mathml_extern.py,sha256=lo4n8GFXWcpe-yw8TPPahzHfhXU9_jv2oo-Sm_TasE4,9436 -docutils/utils/math/tex2unichar.py,sha256=_A2K7ap6o6cb1LY9qCYtsfP-zk5eeu_Hu6LIl6j7LnA,37497 -docutils/utils/math/unichar2tex.py,sha256=Q1gvqUGWprAjMfpGfujAu4ldjJevoFG2moM46SWy6WU,18393 -docutils/utils/punctuation_chars.py,sha256=OMY7gEwy_TIo1MnjZ9cnT3wA9LmSApldJ3lSJZbEtuE,5747 -docutils/utils/roman.py,sha256=JCF_zBcbauEuVvDPuNTR1ZBxFgyNEUr6jZ2JwZwnQ9A,4280 -docutils/utils/smartquotes.py,sha256=KW27gHCkAhgDCdKisPbAu85yJjc0j2FcssSwzvPlCCI,39136 -docutils/utils/urischemes.py,sha256=5dcLKn-Xo5ldDCcdTtO7l8UM_Y3KZrcjyR3A8RWp91U,6260 -docutils/writers/__init__.py,sha256=sJuZewwKkiJHl0VkAtx-geKluXhd_34H9o3sg4Yp4AU,4945 -docutils/writers/__pycache__/__init__.cpython-310.pyc,, -docutils/writers/__pycache__/_html_base.cpython-310.pyc,, -docutils/writers/__pycache__/docutils_xml.cpython-310.pyc,, -docutils/writers/__pycache__/manpage.cpython-310.pyc,, -docutils/writers/__pycache__/null.cpython-310.pyc,, -docutils/writers/__pycache__/pseudoxml.cpython-310.pyc,, -docutils/writers/_html_base.py,sha256=pQqROc1bBMw_ozqT7T8jNtkHh7F7EWcxZUAbLDIvMrY,75401 -docutils/writers/docutils_xml.py,sha256=wi786IvTZqbojdiIxx-vZWS76BE0cUYJQ5RSqfJnkXc,6851 -docutils/writers/html4css1/__init__.py,sha256=KkCwD339fBkAsvUpO448_i2YcSyZ04bloCTr932wTJ8,38125 -docutils/writers/html4css1/__pycache__/__init__.cpython-310.pyc,, -docutils/writers/html4css1/html4css1.css,sha256=-Uk0s5gu8-k8f4f34R-bSIpcSzqkVGa5JN6FGSh7QbY,7300 -docutils/writers/html4css1/template.txt,sha256=HDzUUyAv7gT4ewGQTqfOE2_9HOVyGu9-wCRgsmoCmjQ,114 -docutils/writers/html5_polyglot/__init__.py,sha256=9wqpd5R89Ewug52mD3g_9tGhyIr5dSEv0vLC0oA8ZOw,15635 -docutils/writers/html5_polyglot/__pycache__/__init__.cpython-310.pyc,, -docutils/writers/html5_polyglot/italic-field-names.css,sha256=R9vxBFOn5NiQXWOLpghskCrn0RzMQEXy6w4EDDSNPXs,1145 -docutils/writers/html5_polyglot/math.css,sha256=eKRtxtJvYDzuQybRk6Ln_k9tr6TCoFhNqek40C9ErdM,6219 -docutils/writers/html5_polyglot/minimal.css,sha256=w78WllgDmLFNc-PwZRiD5_fhYB0zxAq7mGSh3c94TNM,8213 -docutils/writers/html5_polyglot/plain.css,sha256=-UeuH9qUxiAlETSiNjEm4Q1vAtpxD0QWwtN81pVo31Y,7552 -docutils/writers/html5_polyglot/responsive.css,sha256=PYhXgdnYR9hfGobvUMxi8OSPGr-KQUd2ucubuBRm5qg,11739 -docutils/writers/html5_polyglot/template.txt,sha256=HDzUUyAv7gT4ewGQTqfOE2_9HOVyGu9-wCRgsmoCmjQ,114 -docutils/writers/html5_polyglot/tuftig.css,sha256=ACotB-KbCWzGsanW1GncNwgMxa21vapRaAtv-NKdh8M,12023 -docutils/writers/latex2e/__init__.py,sha256=xs566hs7lq1MEteKh3PhCVj7jGdW3ivB_WNlrQ5pHVY,138165 -docutils/writers/latex2e/__pycache__/__init__.cpython-310.pyc,, -docutils/writers/latex2e/default.tex,sha256=JcaJnrdmKE9vqwATl9dSHOsnGnjF2dLX4vsiu7kvWEI,422 -docutils/writers/latex2e/docutils.sty,sha256=dgnu97-E5w_rOrdaKBYAw_PwnMKASOFLXV08k17DABQ,5472 -docutils/writers/latex2e/titlepage.tex,sha256=ampRiXY22vtJ22UPMv61mmPubJPItdrSkl9MSAOftpQ,480 -docutils/writers/latex2e/titlingpage.tex,sha256=Pa9ixIf9Yy6RLljbTPUEgmrQwRzYsyzrxl4KSrHA37E,424 -docutils/writers/latex2e/xelatex.tex,sha256=NbrtTphygnEaTmyJEz5HwkNuWCbV1ijlh_1M7_TXLu0,672 -docutils/writers/manpage.py,sha256=REpVzDREXjLJ8ChcBpzbk3Wv8MJ45QWJuPRLB7s5nt8,38128 -docutils/writers/null.py,sha256=Ue7kizk6_1GGrCmYiltg9MrYNetMyHM-yvQvNRrclN8,568 -docutils/writers/odf_odt/__init__.py,sha256=uW_W6F_zMdbcqEmo4swfou2elk4XG-ShZusngP1acS4,132081 -docutils/writers/odf_odt/__pycache__/__init__.cpython-310.pyc,, -docutils/writers/odf_odt/__pycache__/prepstyles.cpython-310.pyc,, -docutils/writers/odf_odt/__pycache__/pygmentsformatter.cpython-310.pyc,, -docutils/writers/odf_odt/prepstyles.py,sha256=XwC29yEYGKpNATr5yuGJH_WYkPBBUAkVr8fLaMEo6Yo,2142 -docutils/writers/odf_odt/pygmentsformatter.py,sha256=j1fMQPdv5fdczPkSKbyYjoh66G8Z_MZhTN52_XfRhHc,4681 -docutils/writers/odf_odt/styles.odt,sha256=xKv9z2sd1qNxAH28X-5st5JuDZeTw6jyDOxXohsFrKY,16500 -docutils/writers/pep_html/__init__.py,sha256=mrApLw1JROL32XM-eXrDjYnQq_DTS3_KqTNOEoiD55k,3503 -docutils/writers/pep_html/__pycache__/__init__.cpython-310.pyc,, -docutils/writers/pep_html/pep.css,sha256=AyHZfudmKKTu-ZmyoLaihM_e5bD3_gCO51hG_NPEDA8,6367 -docutils/writers/pep_html/template.txt,sha256=SPc44ICSNgps08fDtVaRlu6glPPwcCcx9mqybx5u6W4,1001 -docutils/writers/pseudoxml.py,sha256=gjnBxBIXYNdnQYt_XTXljV3fcp9dW1Ek0LE5VkGIvJk,1032 -docutils/writers/s5_html/__init__.py,sha256=UahNZB0U6WSRb3jR6x1MXy4A4ud2bd8lduqG9Nn5Vao,14712 -docutils/writers/s5_html/__pycache__/__init__.cpython-310.pyc,, -docutils/writers/s5_html/themes/README.txt,sha256=wYnu3iomgGD6odpZOtWTzOynI1dfIGE6AVF1MDR0FVY,278 -docutils/writers/s5_html/themes/big-black/__base__,sha256=WeKnChXCPkrXDs7Xr-Qnf1i-bgFjkeaKJ-ilXV0R5lM,38 -docutils/writers/s5_html/themes/big-black/framing.css,sha256=DtEo7Fti9JARMLmcCx0NIfir7QRR24_WN3UbG-EyH64,910 -docutils/writers/s5_html/themes/big-black/pretty.css,sha256=UP9r7eGX0qEFCIDyKcT5bcazMxCw43O2KSrs2ebBPwI,3605 -docutils/writers/s5_html/themes/big-white/framing.css,sha256=meBByeaKIduudfFCDxVw4uzSOj8q_ZJArnwp8oZ1S8g,905 -docutils/writers/s5_html/themes/big-white/pretty.css,sha256=RlQ7CZuN-WMrR8CmCeQ-U8WVmZj769z2zx2FfLwTS48,3565 -docutils/writers/s5_html/themes/default/framing.css,sha256=Sbh5wryeioxDMZ-kJFwzKNziO-3CRvLBMG7rcJjTLmU,1002 -docutils/writers/s5_html/themes/default/opera.css,sha256=guPZOg_BINv-LjV9_IAM7ILFQ-fKALNjlP1i06e5dmA,261 -docutils/writers/s5_html/themes/default/outline.css,sha256=z3ACJiW3_gnG8XFvX602PMTYvKhbRybqCeoWl3O_pA0,648 -docutils/writers/s5_html/themes/default/pretty.css,sha256=iT_51bIPLTk1hFFs3hCarnyJqtbB4I86BNrxlT1r3eo,4383 -docutils/writers/s5_html/themes/default/print.css,sha256=INhYRMsY7y2wd9p7tqjcDWBREXHUMO-2ApAWvITyetI,818 -docutils/writers/s5_html/themes/default/s5-core.css,sha256=D4WDPb581O-_G5jhzpAIwI88B1Zi8y3nWBB8rCxgzlg,450 -docutils/writers/s5_html/themes/default/slides.css,sha256=VKYQ1Oe8lZ8LHxzPqJiU79J0z295nkmIbzsXL-N_dfQ,283 -docutils/writers/s5_html/themes/default/slides.js,sha256=5BXUM5jSWu9hUQSVhGZhMTEvkdCYgqrOJO3ljwDgxWI,15801 -docutils/writers/s5_html/themes/medium-black/__base__,sha256=822LJG-LrdBZY6CA7wsLFCFzsYfxbyz2mr1j6rpb1UA,41 -docutils/writers/s5_html/themes/medium-black/pretty.css,sha256=OdL1xJ9f_FE1pmS7X0s0yxyIl1n2vUBQaGOcJrT2svg,4029 -docutils/writers/s5_html/themes/medium-white/framing.css,sha256=BF5YnRLGRhobO06xDet-0KZYpR10IgRjRbULPVm3PMM,943 -docutils/writers/s5_html/themes/medium-white/pretty.css,sha256=Zm-Pgk3SLAGmGTRF27nrqvpBb_LH2yQ5FIpDPM3p0Y0,3989 -docutils/writers/s5_html/themes/small-black/__base__,sha256=WmiB80z49RfMsy_7tFI042AfUgyztL5OXI3tap9EfQM,40 -docutils/writers/s5_html/themes/small-black/pretty.css,sha256=fmc73kx-zOp0jbiy4GAmpw2Xdz9Q_-WzebsgDJWUJos,4028 -docutils/writers/s5_html/themes/small-white/framing.css,sha256=qwNUgzqnrXgoX47SddbVIKEZwQDjGnTGA468jHHIXqc,940 -docutils/writers/s5_html/themes/small-white/pretty.css,sha256=qU8WOhY8TT6ZY6cXKXABb7T7JgpJQORzTZJhuAm0gGg,3999 -docutils/writers/xetex/__init__.py,sha256=o25hpaSPL9erEUe18GIzaVbP2QuYtjZ0SwSkDeXfZ6k,5736 -docutils/writers/xetex/__pycache__/__init__.cpython-310.pyc,, diff --git a/venv/Lib/site-packages/docutils-0.21.2.dist-info/WHEEL b/venv/Lib/site-packages/docutils-0.21.2.dist-info/WHEEL deleted file mode 100644 index 3b5e64b..0000000 --- a/venv/Lib/site-packages/docutils-0.21.2.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: flit 3.9.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/venv/Lib/site-packages/docutils-0.21.2.dist-info/entry_points.txt b/venv/Lib/site-packages/docutils-0.21.2.dist-info/entry_points.txt deleted file mode 100644 index 3e32d4e..0000000 --- a/venv/Lib/site-packages/docutils-0.21.2.dist-info/entry_points.txt +++ /dev/null @@ -1,13 +0,0 @@ -[console_scripts] -docutils=docutils.__main__:main -rst2html=docutils.core:rst2html -rst2html4=docutils.core:rst2html4 -rst2html5=docutils.core:rst2html5 -rst2latex=docutils.core:rst2latex -rst2man=docutils.core:rst2man -rst2odt=docutils.core:rst2odt -rst2pseudoxml=docutils.core:rst2pseudoxml -rst2s5=docutils.core:rst2s5 -rst2xetex=docutils.core:rst2xetex -rst2xml=docutils.core:rst2xml - diff --git a/venv/Lib/site-packages/docutils/__init__.py b/venv/Lib/site-packages/docutils/__init__.py deleted file mode 100644 index 16af410..0000000 --- a/venv/Lib/site-packages/docutils/__init__.py +++ /dev/null @@ -1,291 +0,0 @@ -# $Id: __init__.py 9649 2024-04-23 18:54:26Z grubert $ -# Author: David Goodger -# Copyright: This module has been placed in the public domain. - -""" -This is the Docutils (Python Documentation Utilities) package. - -Package Structure -================= - -Modules: - -- __init__.py: Contains component base classes, exception classes, and - Docutils version information. - -- core.py: Contains the ``Publisher`` class and ``publish_*()`` convenience - functions. - -- frontend.py: Runtime settings (command-line interface, configuration files) - processing, for Docutils front-ends. - -- io.py: Provides a uniform API for low-level input and output. - -- nodes.py: Docutils document tree (doctree) node class library. - -- statemachine.py: A finite state machine specialized for - regular-expression-based text filters. - -Subpackages: - -- languages: Language-specific mappings of terms. - -- parsers: Syntax-specific input parser modules or packages. - -- readers: Context-specific input handlers which understand the data - source and manage a parser. - -- transforms: Modules used by readers and writers to modify - the Docutils document tree. - -- utils: Contains the ``Reporter`` system warning class and miscellaneous - utilities used by readers, writers, and transforms. - - utils/urischemes.py: Contains a complete mapping of known URI addressing - scheme names to descriptions. - -- utils/math: Contains functions for conversion of mathematical notation - between different formats (LaTeX, MathML, text, ...). - -- writers: Format-specific output translators. -""" - -from collections import namedtuple - -__docformat__ = 'reStructuredText' - -__version__ = '0.21.2' -"""Docutils version identifier (complies with PEP 440):: - - major.minor[.micro][releaselevel[serial]][.dev] - -For version comparison operations, use `__version_info__` (see, below) -rather than parsing the text of `__version__`. - -https://docutils.sourceforge.io/docs/dev/policies.html#version-identification -""" - -__version_details__ = '' -"""Optional extra version details (e.g. 'snapshot 2005-05-29, r3410'). - -For development and release status, use `__version__ and `__version_info__`. -""" - - -class VersionInfo(namedtuple('VersionInfo', - 'major minor micro releaselevel serial release')): - - def __new__(cls, major=0, minor=0, micro=0, - releaselevel='final', serial=0, release=True): - releaselevels = ('alpha', 'beta', 'candidate', 'final') - if releaselevel not in releaselevels: - raise ValueError('releaselevel must be one of %r.' - % (releaselevels, )) - if releaselevel == 'final': - if not release: - raise ValueError('releaselevel "final" must not be used ' - 'with development versions (leads to wrong ' - 'version ordering of the related __version__') - # cf. https://peps.python.org/pep-0440/#summary-of-permitted-suffixes-and-relative-ordering # noqa - if serial != 0: - raise ValueError('"serial" must be 0 for final releases') - - return super().__new__(cls, major, minor, micro, - releaselevel, serial, release) - - def __lt__(self, other): - if isinstance(other, tuple): - other = VersionInfo(*other) - return tuple.__lt__(self, other) - - def __gt__(self, other): - if isinstance(other, tuple): - other = VersionInfo(*other) - return tuple.__gt__(self, other) - - def __le__(self, other): - if isinstance(other, tuple): - other = VersionInfo(*other) - return tuple.__le__(self, other) - - def __ge__(self, other): - if isinstance(other, tuple): - other = VersionInfo(*other) - return tuple.__ge__(self, other) - - -__version_info__ = VersionInfo( - major=0, - minor=21, - micro=2, - releaselevel='final', # one of 'alpha', 'beta', 'candidate', 'final' - serial=0, # pre-release number (0 for final releases and snapshots) - release=True # True for official releases and pre-releases - ) -"""Comprehensive version information tuple. - -https://docutils.sourceforge.io/docs/dev/policies.html#version-identification -""" - - -class ApplicationError(Exception): pass -class DataError(ApplicationError): pass - - -class SettingsSpec: - - """ - Runtime setting specification base class. - - SettingsSpec subclass objects used by `docutils.frontend.OptionParser`. - """ - - # TODO: replace settings_specs with a new data structure - # Backwards compatiblity: - # Drop-in components: - # Sphinx supplies settings_spec in the current format in some places - # Myst parser provides a settings_spec tuple - # - # Sphinx reads a settings_spec in order to set a default value - # in writers/html.py:59 - # https://github.com/sphinx-doc/sphinx/blob/4.x/sphinx/writers/html.py - # This should be changed (before retiring the old format) - # to use `settings_default_overrides` instead. - settings_spec = () - """Runtime settings specification. Override in subclasses. - - Defines runtime settings and associated command-line options, as used by - `docutils.frontend.OptionParser`. This is a tuple of: - - - Option group title (string or `None` which implies no group, just a list - of single options). - - - Description (string or `None`). - - - A sequence of option tuples. Each consists of: - - - Help text (string) - - - List of option strings (e.g. ``['-Q', '--quux']``). - - - Dictionary of keyword arguments sent to the OptionParser/OptionGroup - ``add_option`` method. - - Runtime setting names are derived implicitly from long option names - ('--a-setting' becomes ``settings.a_setting``) or explicitly from the - 'dest' keyword argument. - - Most settings will also have a 'validator' keyword & function. The - validator function validates setting values (from configuration files - and command-line option arguments) and converts them to appropriate - types. For example, the ``docutils.frontend.validate_boolean`` - function, **required by all boolean settings**, converts true values - ('1', 'on', 'yes', and 'true') to 1 and false values ('0', 'off', - 'no', 'false', and '') to 0. Validators need only be set once per - setting. See the `docutils.frontend.validate_*` functions. - - See the optparse docs for more details. - - - More triples of group title, description, options, as many times as - needed. Thus, `settings_spec` tuples can be simply concatenated. - """ - - settings_defaults = None - """A dictionary of defaults for settings not in `settings_spec` (internal - settings, intended to be inaccessible by command-line and config file). - Override in subclasses.""" - - settings_default_overrides = None - """A dictionary of auxiliary defaults, to override defaults for settings - defined in other components' `setting_specs`. Override in subclasses.""" - - relative_path_settings = () - """Settings containing filesystem paths. Override in subclasses. - Settings listed here are to be interpreted relative to the current working - directory.""" - - config_section = None - """The name of the config file section specific to this component - (lowercase, no brackets). Override in subclasses.""" - - config_section_dependencies = None - """A list of names of config file sections that are to be applied before - `config_section`, in order (from general to specific). In other words, - the settings in `config_section` are to be overlaid on top of the settings - from these sections. The "general" section is assumed implicitly. - Override in subclasses.""" - - -class TransformSpec: - """ - Runtime transform specification base class. - - Provides the interface to register "transforms" and helper functions - to resolve references with a `docutils.transforms.Transformer`. - - https://docutils.sourceforge.io/docs/ref/transforms.html - """ - - def get_transforms(self): - """Transforms required by this class. Override in subclasses.""" - if self.default_transforms != (): - import warnings - warnings.warn('TransformSpec: the "default_transforms" attribute ' - 'will be removed in Docutils 2.0.\n' - 'Use get_transforms() method instead.', - DeprecationWarning) - return list(self.default_transforms) - return [] - - # Deprecated; for compatibility. - default_transforms = () - - unknown_reference_resolvers = () - """List of functions to try to resolve unknown references. - - Unknown references have a 'refname' attribute which doesn't correspond - to any target in the document. Called when the transforms in - `docutils.transforms.references` are unable to find a correct target. - - The list should contain functions which will try to resolve unknown - references, with the following signature:: - - def reference_resolver(node): - '''Returns boolean: true if resolved, false if not.''' - - If the function is able to resolve the reference, it should also remove - the 'refname' attribute and mark the node as resolved:: - - del node['refname'] - node.resolved = 1 - - Each function must have a "priority" attribute which will affect the order - the unknown_reference_resolvers are run:: - - reference_resolver.priority = 100 - - This hook is provided for 3rd party extensions. - Example use case: the `MoinMoin - ReStructured Text Parser` - in ``sandbox/mmgilbe/rst.py``. - """ - - -class Component(SettingsSpec, TransformSpec): - - """Base class for Docutils components.""" - - component_type = None - """Name of the component type ('reader', 'parser', 'writer'). Override in - subclasses.""" - - supported = () - """Name and aliases for this component. Override in subclasses.""" - - def supports(self, format): - """ - Is `format` supported by this component? - - To be used by transforms to ask the dependent component if it supports - a certain input context or output format. - """ - return format in self.supported diff --git a/venv/Lib/site-packages/docutils/__main__.py b/venv/Lib/site-packages/docutils/__main__.py deleted file mode 100644 index ce61489..0000000 --- a/venv/Lib/site-packages/docutils/__main__.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python3 -# :Copyright: © 2020, 2022 Günter Milde. -# :License: Released under the terms of the `2-Clause BSD license`_, in short: -# -# Copying and distribution of this file, with or without modification, -# are permitted in any medium without royalty provided the copyright -# notice and this notice are preserved. -# This file is offered as-is, without any warranty. -# -# .. _2-Clause BSD license: https://opensource.org/licenses/BSD-2-Clause -# -# Revision: $Revision: 9107 $ -# Date: $Date: 2022-07-06 15:59:57 +0200 (Mi, 06. Jul 2022) $ - -"""Generic command line interface for the `docutils` package. - -See also -https://docs.python.org/3/library/__main__.html#main-py-in-python-packages -""" - -import argparse -import locale -import sys - -import docutils -from docutils.core import Publisher, publish_cmdline, default_description - - -class CliSettingsSpec(docutils.SettingsSpec): - """Runtime settings & command-line options for the generic CLI. - - Configurable reader, parser, and writer components. - - The "--writer" default will change to 'html' in Docutils 2.0 - when 'html' becomes an alias for the current value 'html5'. - """ - - settings_spec = ( - 'Docutils Application Options', - 'Reader, writer, and parser settings influence the available options. ' - ' Example: use `--help --writer=latex` to see LaTeX writer options. ', - # options: ('help text', [